diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py index 3f0ae6d9f31..72fe904b39d 100644 --- a/addons/account/models/account_bank_statement.py +++ b/addons/account/models/account_bank_statement.py @@ -292,42 +292,58 @@ class AccountBankStatement(models.Model): st_line.currency_id = self.journal_id.currency_id or self.company_id.currency_id def _check_balance_end_real_same_as_computed(self): - ''' Check the balance_end_real (encoded manually by the user) is equals to the balance_end (computed by odoo). - In case of a cash statement, the different is set automatically to a profit/loss account. - ''' - for stmt in self: - if not stmt.currency_id.is_zero(stmt.difference): - if stmt.journal_type == 'cash': - st_line_vals = { - 'statement_id': stmt.id, - 'journal_id': stmt.journal_id.id, - 'amount': stmt.difference, - 'date': stmt.date, - } + """ Check the balance_end_real (encoded manually by the user) is equals to the balance_end (computed by odoo). """ + return self._check_cash_balance_end_real_same_as_computed() and self._check_bank_balance_end_real_same_as_computed() - if stmt.difference < 0.0: - if not stmt.journal_id.loss_account_id: - raise UserError(_('Please go on the %s journal and define a Loss Account. This account will be used to record cash difference.', stmt.journal_id.name)) + def _check_cash_balance_end_real_same_as_computed(self): + """ Check the balance_end_real (encoded manually by the user) is equals to the balance_end (computed by odoo). + For a cash statement, if there is a difference, the different is set automatically to a profit/loss account. + """ + for statement in self.filtered(lambda stmt: stmt.journal_type == 'cash'): + if not statement.currency_id.is_zero(statement.difference): + st_line_vals = { + 'statement_id': statement.id, + 'journal_id': statement.journal_id.id, + 'amount': statement.difference, + 'date': statement.date, + } - st_line_vals['payment_ref'] = _("Cash difference observed during the counting (Loss)") - st_line_vals['counterpart_account_id'] = stmt.journal_id.loss_account_id.id - else: - # statement.difference > 0.0 - if not stmt.journal_id.profit_account_id: - raise UserError(_('Please go on the %s journal and define a Profit Account. This account will be used to record cash difference.', stmt.journal_id.name)) + if statement.currency_id.compare_amounts(statement.difference, 0.0) < 0.0: + if not statement.journal_id.loss_account_id: + raise UserError(_( + "Please go on the %s journal and define a Loss Account. " + "This account will be used to record cash difference.", + statement.journal_id.name + )) - st_line_vals['payment_ref'] = _("Cash difference observed during the counting (Profit)") - st_line_vals['counterpart_account_id'] = stmt.journal_id.profit_account_id.id - - self.env['account.bank.statement.line'].create(st_line_vals) + st_line_vals['payment_ref'] = _("Cash difference observed during the counting (Loss)") + st_line_vals['counterpart_account_id'] = statement.journal_id.loss_account_id.id else: - balance_end_real = formatLang(self.env, stmt.balance_end_real, currency_obj=stmt.currency_id) - balance_end = formatLang(self.env, stmt.balance_end, currency_obj=stmt.currency_id) - raise UserError(_( - 'The ending balance is incorrect !\nThe expected balance (%(real_balance)s) is different from the computed one (%(computed_balance)s).', - real_balance=balance_end_real, - computed_balance=balance_end - )) + # statement.difference > 0.0 + if not statement.journal_id.profit_account_id: + raise UserError(_( + "Please go on the %s journal and define a Profit Account. " + "This account will be used to record cash difference.", + statement.journal_id.name + )) + + st_line_vals['payment_ref'] = _("Cash difference observed during the counting (Profit)") + st_line_vals['counterpart_account_id'] = statement.journal_id.profit_account_id.id + + self.env['account.bank.statement.line'].create(st_line_vals) + return True + + def _check_bank_balance_end_real_same_as_computed(self): + """ Check the balance_end_real (encoded manually by the user) is equals to the balance_end (computed by odoo). """ + for statement in self.filtered(lambda stmt: stmt.journal_type == 'bank'): + if not statement.currency_id.is_zero(statement.difference): + balance_end_real = formatLang(self.env, statement.balance_end_real, currency_obj=statement.currency_id) + balance_end = formatLang(self.env, statement.balance_end, currency_obj=statement.currency_id) + raise UserError(_( + 'The ending balance is incorrect !\nThe expected balance (%(real_balance)s) is different from the computed one (%(computed_balance)s).', + real_balance=balance_end_real, + computed_balance=balance_end + )) return True @api.ondelete(at_uninstall=False) @@ -398,7 +414,7 @@ class AccountBankStatement(models.Model): if any(statement.state != 'open' for statement in self): raise UserError(_("Only new statements can be posted.")) - self._check_balance_end_real_same_as_computed() + self._check_cash_balance_end_real_same_as_computed() for statement in self: if not statement.name: diff --git a/addons/account/tests/test_account_bank_statement.py b/addons/account/tests/test_account_bank_statement.py index 66ff7715f88..e200c8224d2 100644 --- a/addons/account/tests/test_account_bank_statement.py +++ b/addons/account/tests/test_account_bank_statement.py @@ -292,6 +292,43 @@ class TestAccountBankStatement(TestAccountBankStatementCommon): 'is_reconciled': True, }]) + def test_bank_statement_with_difference(self): + """ Test that a bank statement with difference could be posted but not validated. """ + bank_statement = self.env['account.bank.statement'].create({ + 'name': 'test_statement', + 'date': '2019-01-01', + 'journal_id': self.bank_journal_1.id, + 'balance_start': 10.0, + 'balance_end_real': 100.0, + 'balance_end': 10.0, + 'line_ids': [Command.create({ + 'date': fields.Date.to_date('2019-01-01'), + 'payment_ref': 'transaction_abc', + 'amount': 10, + 'partner_id': self.partner_a.id, + })] + }) + bank_statement.button_post() + # Check if the bank statement is well posted (No check on the balance as the balance could be edited after). + self.assertEqual(bank_statement.state, 'posted') + + # Check that the bank statement couldn't be validated with balance_end != balance_end_real + with self.assertRaises(UserError): + bank_statement.button_validate() + + # Check if we can validate the bank statement if balance_end == balance_end_real + bank_statement.balance_end_real = bank_statement.balance_end + + # Reconcile without the bank reconciliation widget since the widget is in enterprise. + statement_line = bank_statement.line_ids + st_liquidity_lines, st_suspense_lines, _st_other_lines = statement_line \ + .with_context(skip_account_move_synchronization=True) \ + ._seek_for_lines() + st_suspense_lines.account_id = st_liquidity_lines.account_id + (st_suspense_lines + st_liquidity_lines).reconcile() + + bank_statement.button_validate() + self.assertEqual(bank_statement.state, 'confirm') @tagged('post_install', '-at_install') class TestAccountBankStatementLine(TestAccountBankStatementCommon):