diff --git a/CHANGELOG b/CHANGELOG
index 2448d8f558..adadf8d999 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,5 @@
+* Create Move by grouped lines (number, date, party)
+
Version 3.4.0 - 2014-10-20
* Bug fixes (see mercurial logs for details)
* Add different validation type on journal
diff --git a/account.py b/account.py
index ebb9ebd92e..ee29faff92 100644
--- a/account.py
+++ b/account.py
@@ -1,6 +1,13 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
-from trytond.pool import PoolMeta
+from itertools import groupby
+from operator import itemgetter
+
+from sql.operators import Concat
+
+from trytond.pool import PoolMeta, Pool
+from trytond.transaction import Transaction
+from trytond.tools import grouped_slice, reduce_ids
__all__ = ['Move']
__metaclass__ = PoolMeta
@@ -9,6 +16,34 @@ __metaclass__ = PoolMeta
class Move:
__name__ = 'account.move'
+ @classmethod
+ def __register__(cls, module_name):
+ pool = Pool()
+ StatementLine = pool.get('account.statement.line')
+ cursor = Transaction().cursor
+ sql_table = cls.__table__()
+
+ super(Move, cls).__register__(module_name)
+
+ # Migration from 3.4:
+ # account.statement.line origin changed to account.statement
+ statement_line = StatementLine.__table__()
+ cursor.execute(*sql_table.join(statement_line,
+ condition=(
+ Concat(StatementLine.__name__ + ',', statement_line.id)
+ == sql_table.origin
+ )
+ ).select(sql_table.id, statement_line.statement,
+ order_by=(sql_table.id, statement_line.statement)))
+ for statement_id, values in groupby(cursor.fetchall(), itemgetter(1)):
+ ids = [x[0] for x in values]
+ for sub_ids in grouped_slice(ids):
+ red_sql = reduce_ids(sql_table.id, sub_ids)
+ cursor.execute(*sql_table.update(
+ columns=[sql_table.origin],
+ values=['account.statement,%s' % statement_id],
+ where=red_sql))
+
@classmethod
def _get_origin(cls):
- return super(Move, cls)._get_origin() + ['account.statement.line']
+ return super(Move, cls)._get_origin() + ['account.statement']
diff --git a/statement.py b/statement.py
index 6e5a7f6fbd..47e53bd5eb 100644
--- a/statement.py
+++ b/statement.py
@@ -119,6 +119,8 @@ class Statement(Workflow, ModelSQL, ModelView):
'deletion.'),
'paid_invoice_draft_statement': ('There are paid invoices on '
'draft statements.'),
+ 'debit_credit_account_statement_journal': ('Please provide '
+ 'debit and credit account on statement journal "%s".'),
})
cls._transitions |= set((
('draft', 'validated'),
@@ -383,8 +385,9 @@ class Statement(Workflow, ModelSQL, ModelView):
for statement in statements:
getattr(statement, 'validate_%s' % statement.validation)()
- for line in statement.lines:
- line.create_move()
+
+ cls.create_move(statements)
+
cls.write(statements, {
'state': 'validated',
})
@@ -399,6 +402,99 @@ class Statement(Workflow, ModelSQL, ModelView):
'invoice': None,
})
+ @classmethod
+ def create_move(cls, statements):
+ 'Create move for the statements and try to reconcile the lines'
+ pool = Pool()
+ Line = pool.get('account.statement.line')
+ Move = pool.get('account.move')
+ MoveLine = pool.get('account.move.line')
+
+ moves = []
+ for statement in statements:
+ for key, lines in groupby(
+ statement.lines, key=statement._group_key):
+ lines = list(lines)
+ key = dict(key)
+ move = statement._get_move(key)
+ moves.append((move, statement, lines))
+
+ Move.save([m for m, _, _ in moves])
+
+ to_write = []
+ for move, _, lines in moves:
+ to_write.append(lines)
+ to_write.append({
+ 'move': move.id,
+ })
+ Line.write(*to_write)
+
+ move_lines = []
+ for move, statement, lines in moves:
+ amount = 0
+ amount_second_currency = 0
+ for line in lines:
+ move_line = line.get_move_line()
+ move_line.move = move
+ amount += move_line.debit - move_line.credit
+ if move_line.amount_second_currency:
+ amount_second_currency += move_line.amount_second_currency
+ move_lines.append((move_line, line))
+
+ move_line = statement._get_move_line(
+ amount, amount_second_currency)
+ move_line.move = move
+ move_lines.append((move_line, None))
+
+ MoveLine.save([l for l, _ in move_lines])
+
+ for move_line, line in move_lines:
+ if line:
+ line.reconcile(move_line)
+
+ def _get_move(self, key):
+ 'Return Move for the grouping key'
+ pool = Pool()
+ Move = pool.get('account.move')
+ Period = pool.get('account.period')
+
+ period_id = Period.find(self.company.id, date=key['date'])
+ return Move(
+ period=period_id,
+ journal=self.journal.journal,
+ date=key['date'],
+ origin=self,
+ company=self.company,
+ )
+
+ def _get_move_line(self, amount, amount_second_currency):
+ 'Return counterpart Move Line for the amount'
+ pool = Pool()
+ MoveLine = pool.get('account.move.line')
+
+ if amount < 0:
+ account = self.journal.journal.debit_account
+ else:
+ account = self.journal.journal.credit_account
+
+ if not account:
+ self.raise_user_error('debit_credit_account_statement_journal',
+ (self.journal.rec_name,))
+
+ if self.journal.currency != self.company.currency:
+ second_currency = self.journal.currency
+ else:
+ second_currency = None
+ amount_second_currency = None
+
+ return MoveLine(
+ debit=abs(amount) if amount < 0 else 0,
+ credit=abs(amount) if amount > 0 else 0,
+ account=account,
+ second_currency=second_currency,
+ amount_second_currency=amount_second_currency,
+ )
+
@classmethod
@ModelView.button
@Workflow.transition('posted')
@@ -454,11 +550,6 @@ class Line(ModelSQL, ModelView):
super(Line, cls).__setup__()
cls._order.insert(0, ('sequence', 'ASC'))
cls._error_messages.update({
- 'debit_credit_account_statement_journal': ('Please provide '
- 'debit and credit account on statement journal "%s".'),
- 'same_debit_credit_account': ('Account "%(account)s" in '
- 'statement line "%(line)s" is the same as the one '
- 'configured as credit or debit on journal "%(journal)s".'),
'amount_greater_invoice_amount_to_pay': ('Amount "%s" is '
'greater than the amount to pay of invoice.'),
})
@@ -551,37 +642,12 @@ class Line(ModelSQL, ModelView):
default.setdefault('invoice', None)
return super(Line, cls).copy(lines, default=default)
- def create_move(self):
- '''
- Create move for the statement line and return move if created.
- '''
+ def reconcile(self, move_line):
pool = Pool()
- Move = pool.get('account.move')
- Period = pool.get('account.period')
- Invoice = pool.get('account.invoice')
Currency = pool.get('currency.currency')
- MoveLine = pool.get('account.move.line')
Lang = pool.get('ir.lang')
-
- if self.move:
- return
-
- period_id = Period.find(self.statement.company.id, date=self.date)
-
- move_lines = self._get_move_lines()
- move = Move(
- period=period_id,
- journal=self.statement.journal.journal,
- date=self.date,
- origin=self,
- company=self.statement.company,
- lines=move_lines,
- )
- move.save()
-
- self.write([self], {
- 'move': move.id,
- })
+ Invoice = pool.get('account.invoice')
+ MoveLine = pool.get('account.move.line')
if self.invoice:
with Transaction().set_context(date=self.invoice.currency_date):
@@ -606,30 +672,28 @@ class Line(ModelSQL, ModelView):
reconcile_lines = self.invoice.get_reconcile_lines_for_amount(
amount)
- for move_line in move.lines:
- if move_line.account == self.invoice.account:
- Invoice.write([self.invoice], {
- 'payment_lines': [('add', [move_line.id])],
- })
- break
+ assert move_line.account == self.invoice.account
+
+ Invoice.write([self.invoice], {
+ 'payment_lines': [('add', [move_line.id])],
+ })
if reconcile_lines[1] == Decimal('0.0'):
lines = reconcile_lines[0] + [move_line]
MoveLine.reconcile(lines)
- return move
@classmethod
def post_move(cls, lines):
Move = Pool().get('account.move')
- Move.post([l.move for l in lines if l.move])
+ Move.post(list({l.move for l in lines if l.move}))
@classmethod
def delete_move(cls, lines):
Move = Pool().get('account.move')
- Move.delete([l.move for l in lines if l.move])
+ Move.delete(list({l.move for l in lines if l.move}))
- def _get_move_lines(self):
+ def get_move_line(self):
'''
- Return the move lines for the statement line
+ Return the move line for the statement line
'''
pool = Pool()
MoveLine = pool.get('account.move.line')
@@ -645,41 +709,15 @@ class Line(ModelSQL, ModelView):
amount_second_currency = None
second_currency = None
- move_lines = []
- move_lines.append(MoveLine(
- description=self.description,
- debit=amount < zero and -amount or zero,
- credit=amount >= zero and amount or zero,
- account=self.account,
- party=self.party if self.account.party_required else None,
- second_currency=second_currency,
- amount_second_currency=amount_second_currency,
- ))
-
- journal = self.statement.journal.journal
- if self.amount >= zero:
- account = journal.debit_account
- else:
- account = journal.credit_account
- if not account:
- self.raise_user_error('debit_credit_account_statement_journal',
- (journal.rec_name,))
- if self.account == account:
- self.raise_user_error('same_debit_credit_account', {
- 'account': self.account.rec_name,
- 'line': self.rec_name,
- 'journal': journal,
- })
- move_lines.append(MoveLine(
- description=self.description,
- debit=amount >= zero and amount or zero,
- credit=amount < zero and -amount or zero,
- account=account,
- party=self.party if account.party_required else None,
- second_currency=second_currency,
- amount_second_currency=amount_second_currency,
- ))
- return move_lines
+ return MoveLine(
+ description=self.description,
+ debit=amount < zero and -amount or zero,
+ credit=amount >= zero and amount or zero,
+ account=self.account,
+ party=self.party if self.account.party_required else None,
+ second_currency=second_currency,
+ amount_second_currency=amount_second_currency,
+ )
class StatementReport(CompanyReport):
diff --git a/statement.xml b/statement.xml
index 1d2780f7f2..8c3ae18870 100644
--- a/statement.xml
+++ b/statement.xml
@@ -116,6 +116,28 @@ this repository contains the full copyright notices and license terms. -->
statement_line_tree_editable
+
+ Statement Lines
+ account.statement.line
+ [('move', 'in', Eval('active_ids'))]
+
+
+ form_relate
+ account.move,-1
+
+
+
+
+ Statement Lines
+ account.statement.line
+ [('move.lines', 'in', Eval('active_ids'))]
+
+
+ form_relate
+ account.move.line,-1
+
+
+
@@ -218,7 +240,7 @@ this repository contains the full copyright notices and license terms. -->
Move Lines
account.move.line
- [('origin.statement.id', 'in', Eval('active_ids'), 'account.statement.line')]
+ [('origin.id', 'in', Eval('active_ids'), 'account.statement')]
form_relate