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