From 279990a2ebfd8ad56e93428501c59c198ebfbc8e Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Tue, 5 Oct 2021 18:23:21 +0200 Subject: [PATCH 1/2] [IMP] account_payment_order: Set orders to done on payment reconciliation --- account_payment_order/__manifest__.py | 2 +- .../migrations/12.0.2.0.0/post-migration.py | 13 +++++ .../models/account_move_line.py | 34 +++++++++++ .../models/account_payment_order.py | 23 ++++++-- .../models/bank_payment_line.py | 2 +- .../tests/test_payment_order_inbound.py | 2 +- .../tests/test_payment_order_outbound.py | 57 ++++++++++++++++++- 7 files changed, 125 insertions(+), 8 deletions(-) create mode 100644 account_payment_order/migrations/12.0.2.0.0/post-migration.py diff --git a/account_payment_order/__manifest__.py b/account_payment_order/__manifest__.py index 994528e6a..ac051e5d1 100644 --- a/account_payment_order/__manifest__.py +++ b/account_payment_order/__manifest__.py @@ -8,7 +8,7 @@ { 'name': 'Account Payment Order', - 'version': '12.0.1.6.3', + 'version': '12.0.2.0.0', 'license': 'AGPL-3', 'author': "ACSONE SA/NV, " "Therp BV, " diff --git a/account_payment_order/migrations/12.0.2.0.0/post-migration.py b/account_payment_order/migrations/12.0.2.0.0/post-migration.py new file mode 100644 index 000000000..3fac9f1f7 --- /dev/null +++ b/account_payment_order/migrations/12.0.2.0.0/post-migration.py @@ -0,0 +1,13 @@ +# Copyright 2021 Hunki Enterprises BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from openupgradelib import openupgrade + + +@openupgrade.migrate() +def migrate(env, version): + for order in env['account.payment.order'].search([ + ('state', '=', 'uploaded'), + ]): + if order._all_lines_reconciled(): + order.action_done() diff --git a/account_payment_order/models/account_move_line.py b/account_payment_order/models/account_move_line.py index f644ec60f..dd3b9a281 100644 --- a/account_payment_order/models/account_move_line.py +++ b/account_payment_order/models/account_move_line.py @@ -148,3 +148,37 @@ class AccountMoveLine(models.Model): ) result.update(arch=arch, fields=fields) return result + + @api.multi + def reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False): + """ Set payment orders with fully reconciled lines to done """ + result = super().reconcile( + writeoff_acc_id=writeoff_acc_id, + writeoff_journal_id=writeoff_journal_id, + ) + if not self.env.context.get('account_payment_order_defer_close'): + self.filtered('full_reconcile_id')._close_payment_orders() + return result + + @api.multi + def _close_payment_orders(self): + """ + Set payment orders linked to move lines in self to done if all + of them are reconciled + """ + for order in self._find_payment_orders(): + if order.state != 'done' and order._all_lines_reconciled(): + order.action_done() + + @api.multi + def _find_payment_orders(self): + """ + Return all payment orders linked (directly by payment_line_ids + or indirectly by reconciliation with a transfer account) to self + """ + return self.mapped( + 'move_id.line_ids.bank_payment_line_id.order_id' + ) | self.mapped( + 'full_reconcile_id.reconciled_line_ids.move_id.line_ids.' + 'bank_payment_line_id.order_id' + ) diff --git a/account_payment_order/models/account_payment_order.py b/account_payment_order/models/account_payment_order.py index e3f57196b..52a21a613 100644 --- a/account_payment_order/models/account_payment_order.py +++ b/account_payment_order/models/account_payment_order.py @@ -114,7 +114,7 @@ class AccountPaymentOrder(models.Model): @api.multi def unlink(self): for order in self: - if order.state == 'uploaded': + if order.state in ('uploaded', 'done'): raise UserError(_( "You cannot delete an uploaded payment order. You can " "cancel it in order to do so.")) @@ -359,13 +359,13 @@ class AccountPaymentOrder(models.Model): @api.multi def generated2uploaded(self): - for order in self: - if order.payment_mode_id.generate_move: - order.generate_move() self.write({ 'state': 'uploaded', 'date_uploaded': fields.Date.context_today(self), }) + for order in self: + if order.payment_mode_id.generate_move: + order.generate_move() return True @api.multi @@ -514,3 +514,18 @@ class AccountPaymentOrder(models.Model): trfmoves = self._prepare_trf_moves() for hashcode, blines in trfmoves.items(): self._create_reconcile_move(hashcode, blines) + + @api.multi + def _all_lines_reconciled(self): + """ + Return true if all payment lines' counterpart lines are reconciled too + Also return true if the counterpart account cannot be reconciled + """ + self.ensure_one() + move_lines = self.env['account.move.line'].search([( + 'bank_payment_line_id', 'in', self.mapped('bank_line_ids').ids + )]).mapped('move_id.line_ids') + return bool(move_lines) and all( + move_line.reconciled for move_line in move_lines + if move_line.account_id.reconcile + ) diff --git a/account_payment_order/models/bank_payment_line.py b/account_payment_order/models/bank_payment_line.py index 573dcba15..984b09341 100644 --- a/account_payment_order/models/bank_payment_line.py +++ b/account_payment_order/models/bank_payment_line.py @@ -163,7 +163,7 @@ class BankPaymentLine(models.Model): def unlink(self): for line in self: order_state = line.order_id.state - if order_state == 'uploaded': + if order_state in ('uploaded', 'done'): raise UserError(_( 'Cannot delete a payment order line whose payment order is' ' in state \'%s\'. You need to cancel it first.') diff --git a/account_payment_order/tests/test_payment_order_inbound.py b/account_payment_order/tests/test_payment_order_inbound.py index f6e1a9ea9..6e74e14ad 100644 --- a/account_payment_order/tests/test_payment_order_inbound.py +++ b/account_payment_order/tests/test_payment_order_inbound.py @@ -109,7 +109,7 @@ class TestPaymentOrderInbound(TestPaymentOrderInboundBase): payment_order.open2generated() payment_order.generated2uploaded() - self.assertEqual(payment_order.state, 'uploaded') + self.assertEqual(payment_order.state, 'done') with self.assertRaises(UserError): payment_order.unlink() diff --git a/account_payment_order/tests/test_payment_order_outbound.py b/account_payment_order/tests/test_payment_order_outbound.py index ee9de9587..71cd91e72 100644 --- a/account_payment_order/tests/test_payment_order_outbound.py +++ b/account_payment_order/tests/test_payment_order_outbound.py @@ -145,6 +145,7 @@ class TestPaymentOrderOutbound(SavepointCase): order.generated2uploaded() order.action_done() self.assertEqual(order.state, 'done') + return order def test_cancel_payment_order(self): # Open invoice @@ -176,7 +177,7 @@ class TestPaymentOrderOutbound(SavepointCase): payment_order.open2generated() payment_order.generated2uploaded() - self.assertEqual(payment_order.state, 'uploaded') + self.assertEqual(payment_order.state, 'done') with self.assertRaises(UserError): payment_order.unlink() @@ -201,3 +202,57 @@ class TestPaymentOrderOutbound(SavepointCase): with self.assertRaises(ValidationError): outbound_order.date_scheduled = date.today() - timedelta( days=1) + + def test_reconciliation_full(self): + self.mode.write({ + 'bank_account_link': 'fixed', + 'default_date_prefered': 'fixed', + 'fixed_journal_id': self.bank_journal.id, + }) + self.bank_journal.default_debit_account_id.reconcile = True + + order_full = self.order_creation('fixed') + order_full.write({'state': 'uploaded'}) + self.reconcile_order_lines(order_full) + self.assertEqual(order_full.state, 'done') + + def test_reconciliation_partial(self): + self.mode.write({ + 'bank_account_link': 'fixed', + 'default_date_prefered': 'fixed', + 'fixed_journal_id': self.bank_journal.id, + }) + self.bank_journal.default_debit_account_id.reconcile = True + + order_partial = self.order_creation('fixed') + order_partial.write({'state': 'uploaded'}) + self.reconcile_order_lines(order_partial, .9) + self.assertEqual(order_partial.state, 'uploaded') + + def reconcile_order_lines(self, order, factor=1): + to_reconcile = self.env['account.move.line'].search([ + ('bank_payment_line_id', 'in', order.mapped('bank_line_ids').ids) + ]).mapped('move_id.line_ids').filtered(lambda x: not x.reconciled) + for move_line in to_reconcile[:]: + move = self.env['account.move'].create({ + 'name': 'payment', + 'date': move_line.date, + 'journal_id': self.bank_journal.id, + 'line_ids': [ + (0, 0, dict( + credit=move_line.credit * factor, + debit=move_line.debit * factor, + account_id=move_line.account_id.id, + )), + (0, 0, dict( + credit=move_line.debit * factor, + debit=move_line.credit * factor, + account_id=move_line.account_id.id, + )), + ], + }) + to_reconcile += move.line_ids.filtered( + lambda x: x.credit != move_line.credit + ) + to_reconcile.reconcile() + return to_reconcile From c5944e66f18596d374e6f9de7b0188c471b4c028 Mon Sep 17 00:00:00 2001 From: AaronHForgeFlow Date: Fri, 12 Feb 2021 16:44:11 +0100 Subject: [PATCH 2/2] [IMP] account_payment_order: done status button --- account_banking_sepa_credit_transfer/tests/test_sct.py | 4 ++-- account_banking_sepa_direct_debit/tests/test_sdd.py | 2 +- account_payment_order/views/account_payment_order.xml | 5 ++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/account_banking_sepa_credit_transfer/tests/test_sct.py b/account_banking_sepa_credit_transfer/tests/test_sct.py index 4a2ee56e1..e6caaa2f6 100644 --- a/account_banking_sepa_credit_transfer/tests/test_sct.py +++ b/account_banking_sepa_credit_transfer/tests/test_sct.py @@ -199,7 +199,7 @@ class TestSCT(common.HttpCase): debtor_acc_xpath[0].text, self.payment_order.company_partner_bank_id.sanitized_acc_number) self.payment_order.generated2uploaded() - self.assertEqual(self.payment_order.state, 'uploaded') + self.assertEqual(self.payment_order.state, 'done') for inv in [invoice1, invoice2, invoice3, invoice4, invoice5]: self.assertEqual(inv.state, 'paid') return @@ -276,7 +276,7 @@ class TestSCT(common.HttpCase): debtor_acc_xpath[0].text, self.payment_order.company_partner_bank_id.sanitized_acc_number) self.payment_order.generated2uploaded() - self.assertEqual(self.payment_order.state, 'uploaded') + self.assertEqual(self.payment_order.state, 'done') for inv in [invoice1, invoice2]: self.assertEqual(inv.state, 'paid') return diff --git a/account_banking_sepa_direct_debit/tests/test_sdd.py b/account_banking_sepa_direct_debit/tests/test_sdd.py index de16fcc65..09d212ff1 100644 --- a/account_banking_sepa_direct_debit/tests/test_sdd.py +++ b/account_banking_sepa_direct_debit/tests/test_sdd.py @@ -209,7 +209,7 @@ class TestSDD(common.HttpCase): debtor_acc_xpath[0].text, payment_order.company_partner_bank_id.sanitized_acc_number) payment_order.generated2uploaded() - self.assertEqual(payment_order.state, 'uploaded') + self.assertEqual(payment_order.state, 'done') for inv in [invoice1, invoice2]: self.assertEqual(inv.state, 'paid') self.assertEqual(self.mandate2.recurrent_sequence_type, 'recurring') diff --git a/account_payment_order/views/account_payment_order.xml b/account_payment_order/views/account_payment_order.xml index 155cbb4a8..ca03f600f 100644 --- a/account_payment_order/views/account_payment_order.xml +++ b/account_payment_order/views/account_payment_order.xml @@ -21,8 +21,11 @@ string="Back to Draft" />