From 957c7e2ccd68238c1e9b41203ae65286195f6e44 Mon Sep 17 00:00:00 2001 From: Aritz Olea Date: Mon, 23 Oct 2023 16:32:48 +0200 Subject: [PATCH] [IMP] account_mass_reconcile: Add 'oldest move line' option on reconcile option --- .../models/base_reconciliation.py | 5 + .../models/mass_reconcile.py | 6 +- .../models/simple_reconciliation.py | 7 +- account_mass_reconcile/readme/DESCRIPTION.rst | 5 +- .../tests/test_scenario_reconcile.py | 119 ++++++++++++++++++ 5 files changed, 139 insertions(+), 3 deletions(-) diff --git a/account_mass_reconcile/models/base_reconciliation.py b/account_mass_reconcile/models/base_reconciliation.py index d57f57f5..f1375681 100644 --- a/account_mass_reconcile/models/base_reconciliation.py +++ b/account_mass_reconcile/models/base_reconciliation.py @@ -128,6 +128,9 @@ class MassReconcileBase(models.AbstractModel): def last_date(mlines): return max(mlines, key=itemgetter("date")) + def oldest_date(mlines): + return min(mlines, key=itemgetter("date")) + def credit(mlines): return [line for line in mlines if line["credit"] > 0] @@ -136,6 +139,8 @@ class MassReconcileBase(models.AbstractModel): if based_on == "newest": return last_date(lines)["date"] + elif based_on == "oldest": + return oldest_date(lines)["date"] elif based_on == "newest_credit": return last_date(credit(lines))["date"] elif based_on == "newest_debit": diff --git a/account_mass_reconcile/models/mass_reconcile.py b/account_mass_reconcile/models/mass_reconcile.py index c1edb92d..e988fff9 100644 --- a/account_mass_reconcile/models/mass_reconcile.py +++ b/account_mass_reconcile/models/mass_reconcile.py @@ -28,7 +28,11 @@ class MassReconcileOptions(models.AbstractModel): @api.model def _get_rec_base_date(self): - return [("newest", "Most recent move line"), ("actual", "Today")] + return [ + ("newest", "Most recent move line"), + ("actual", "Today"), + ("oldest", "Oldest move line"), + ] write_off = fields.Float("Write off allowed", default=0.0) account_lost_id = fields.Many2one("account.account", string="Account Lost") diff --git a/account_mass_reconcile/models/simple_reconciliation.py b/account_mass_reconcile/models/simple_reconciliation.py index c0dc7b09..28c1c8bf 100644 --- a/account_mass_reconcile/models/simple_reconciliation.py +++ b/account_mass_reconcile/models/simple_reconciliation.py @@ -58,7 +58,12 @@ class MassReconcileSimple(models.AbstractModel): return res def _simple_order(self, *args, **kwargs): - return "ORDER BY account_move_line.%s" % self._key_field + ret = "ORDER BY account_move_line.%s" % self._key_field + if self.date_base_on == "oldest": + ret += ", date" + elif self.date_base_on == "newest": + ret += ", date desc" + return ret def _action_rec(self): """Match only 2 move lines, do not allow partial reconcile""" diff --git a/account_mass_reconcile/readme/DESCRIPTION.rst b/account_mass_reconcile/readme/DESCRIPTION.rst index 1d4b694a..3b8a354d 100644 --- a/account_mass_reconcile/readme/DESCRIPTION.rst +++ b/account_mass_reconcile/readme/DESCRIPTION.rst @@ -15,4 +15,7 @@ in order to provide: in this module, the simple reconciliations works on 2 lines (1 debit / 1 credit) and do not allow partial reconciliation, they also match on 1 key, -partner or Journal item name. +partner or Journal item name. There is also an +option for 'most recent move line' or +'oldest move line' which is used to choose the +move to be reconciled if more than one is found. diff --git a/account_mass_reconcile/tests/test_scenario_reconcile.py b/account_mass_reconcile/tests/test_scenario_reconcile.py index 96fc4ce7..3ad3f43f 100644 --- a/account_mass_reconcile/tests/test_scenario_reconcile.py +++ b/account_mass_reconcile/tests/test_scenario_reconcile.py @@ -1,4 +1,5 @@ # © 2014-2016 Camptocamp SA (Damien Crier) +# © 2023 FactorLibre - Aritz Olea # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from datetime import timedelta @@ -67,6 +68,124 @@ class TestScenarioReconcile(TestAccountReconciliationCommon): mass_rec.run_reconcile() self.assertEqual("paid", invoice.payment_state) + def test_scenario_reconcile_newest(self): + invoice = self.create_invoice() + self.assertEqual("posted", invoice.state) + + receivalble_account_id = invoice.partner_id.property_account_receivable_id.id + # create payments + payment_old = self.env["account.payment"].create( + { + "partner_type": "customer", + "payment_type": "inbound", + "partner_id": invoice.partner_id.id, + "destination_account_id": receivalble_account_id, + "amount": 50.0, + "journal_id": self.bank_journal.id, + "date": fields.Date.from_string("2023-10-01"), + } + ) + payment_new = self.env["account.payment"].create( + { + "partner_type": "customer", + "payment_type": "inbound", + "partner_id": invoice.partner_id.id, + "destination_account_id": receivalble_account_id, + "amount": 50.0, + "journal_id": self.bank_journal.id, + "date": fields.Date.from_string("2023-10-20"), + } + ) + payment_old.action_post() + payment_new.action_post() + + # create the mass reconcile record + mass_rec = self.mass_rec_obj.create( + { + "name": "mass_reconcile_1", + "account": invoice.partner_id.property_account_receivable_id.id, + "reconcile_method": [ + ( + 0, + 0, + { + "name": "mass.reconcile.simple.partner", + "date_base_on": "newest", + }, + ) + ], + } + ) + # call the automatic reconciliation method + mass_rec.run_reconcile() + self.assertEqual("paid", invoice.payment_state) + self.assertTrue(mass_rec.last_history) + payment_new_line = payment_new.move_id.line_ids.filtered(lambda l: l.credit) + payment_old_line = payment_old.move_id.line_ids.filtered(lambda l: l.credit) + self.assertTrue(payment_new_line in mass_rec.last_history.reconcile_line_ids) + self.assertTrue(payment_new_line.reconciled) + self.assertFalse(payment_old_line in mass_rec.last_history.reconcile_line_ids) + self.assertFalse(payment_old_line.reconciled) + + def test_scenario_reconcile_oldest(self): + invoice = self.create_invoice() + self.assertEqual("posted", invoice.state) + + receivalble_account_id = invoice.partner_id.property_account_receivable_id.id + # create payments + payment_old = self.env["account.payment"].create( + { + "partner_type": "customer", + "payment_type": "inbound", + "partner_id": invoice.partner_id.id, + "destination_account_id": receivalble_account_id, + "amount": 50.0, + "journal_id": self.bank_journal.id, + "date": fields.Date.from_string("2023-10-01"), + } + ) + payment_new = self.env["account.payment"].create( + { + "partner_type": "customer", + "payment_type": "inbound", + "partner_id": invoice.partner_id.id, + "destination_account_id": receivalble_account_id, + "amount": 50.0, + "journal_id": self.bank_journal.id, + "date": fields.Date.from_string("2023-10-20"), + } + ) + payment_old.action_post() + payment_new.action_post() + + # create the mass reconcile record + mass_rec = self.mass_rec_obj.create( + { + "name": "mass_reconcile_1", + "account": invoice.partner_id.property_account_receivable_id.id, + "reconcile_method": [ + ( + 0, + 0, + { + "name": "mass.reconcile.simple.partner", + "date_base_on": "oldest", + }, + ) + ], + } + ) + # call the automatic reconciliation method + mass_rec.run_reconcile() + self.assertEqual("paid", invoice.payment_state) + self.assertTrue(mass_rec.last_history) + payment_new_line = payment_new.move_id.line_ids.filtered(lambda l: l.credit) + payment_old_line = payment_old.move_id.line_ids.filtered(lambda l: l.credit) + self.assertFalse(payment_new_line in mass_rec.last_history.reconcile_line_ids) + self.assertFalse(payment_new_line.reconciled) + self.assertTrue(payment_old_line in mass_rec.last_history.reconcile_line_ids) + self.assertTrue(payment_old_line.reconciled) + def test_scenario_reconcile_currency(self): currency_rate = ( self.env["res.currency.rate"]