diff --git a/account_reconcile_model_strict_match_amount/__init__.py b/account_reconcile_model_strict_match_amount/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/account_reconcile_model_strict_match_amount/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/account_reconcile_model_strict_match_amount/__manifest__.py b/account_reconcile_model_strict_match_amount/__manifest__.py new file mode 100644 index 00000000..78335557 --- /dev/null +++ b/account_reconcile_model_strict_match_amount/__manifest__.py @@ -0,0 +1,19 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +{ + "name": "Account Reconciliation Model Strict Match Amount", + "summary": "Restrict reconciliation propositions to matching amount parameter", + "version": "12.0.1.0.0", + "category": "Accounting", + "website": "https://github.com/OCA/account-reconcile", + "author": "Camptocamp, Odoo Community Association (OCA)", + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": [ + "account", + ], + "data": [ + "views/account_reconcile_model.xml", + ], +} diff --git a/account_reconcile_model_strict_match_amount/models/__init__.py b/account_reconcile_model_strict_match_amount/models/__init__.py new file mode 100644 index 00000000..e27d0c6c --- /dev/null +++ b/account_reconcile_model_strict_match_amount/models/__init__.py @@ -0,0 +1 @@ +from . import account_reconcile_model diff --git a/account_reconcile_model_strict_match_amount/models/account_reconcile_model.py b/account_reconcile_model_strict_match_amount/models/account_reconcile_model.py new file mode 100644 index 00000000..fcdc395c --- /dev/null +++ b/account_reconcile_model_strict_match_amount/models/account_reconcile_model.py @@ -0,0 +1,42 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from odoo import api, fields, models + + +class AccountReconcileModel(models.Model): + + _inherit = 'account.reconcile.model' + + strict_match_total_amount = fields.Boolean( + string="Strict Amount Matching", + help="Avoid bypassing the Amount Matching parameter in case of a " + "statement line communication matching exactly existing entries." + ) + + @api.multi + def _get_select_communication_flag(self): + if not self.match_total_amount or not self.strict_match_total_amount: + return super()._get_select_communication_flag() + else: + regexp = r"'[^0-9|^\s]', '', 'g'), '\S(?:.*\S)*'), '\s+'" + return r''' + -- Determine a matching or not with the statement line communication using the move.name or move.ref. + -- only digits are considered and reference are split by any space characters + COALESCE( + regexp_split_to_array(substring(REGEXP_REPLACE(move.name, {regexp}) + && regexp_split_to_array(substring(REGEXP_REPLACE(st_line.name, {regexp}) + OR + ( + move.ref IS NOT NULL + AND + regexp_split_to_array(substring(REGEXP_REPLACE(move.ref, {regexp}) + && + regexp_split_to_array(substring(REGEXP_REPLACE(st_line.name, {regexp}) + ), FALSE) + AND + CASE + WHEN abs(st_line.amount) < abs(aml.balance) THEN abs(st_line.amount) / abs(aml.balance) * 100 + WHEN abs(st_line.amount) > abs(aml.balance) THEN abs(aml.balance) / abs(st_line.amount) * 100 + ELSE 100 + END >= {match_total_amount_param} AS communication_flag + '''.format(regexp=regexp, match_total_amount_param=self.match_total_amount_param) diff --git a/account_reconcile_model_strict_match_amount/readme/CONTRIBUTORS.rst b/account_reconcile_model_strict_match_amount/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..e31e2f0c --- /dev/null +++ b/account_reconcile_model_strict_match_amount/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Akim Juillerat diff --git a/account_reconcile_model_strict_match_amount/readme/DESCRIPTION.rst b/account_reconcile_model_strict_match_amount/readme/DESCRIPTION.rst new file mode 100644 index 00000000..0c77d35b --- /dev/null +++ b/account_reconcile_model_strict_match_amount/readme/DESCRIPTION.rst @@ -0,0 +1,4 @@ +This module allows to cancel the bypassing of Amount Matching feature on +Reconciliation models "in case of a statement line communication matching +exactly existing entries", to ensure only statement lines matching the total +amount (or according to its percentage) will be reconciled automatically. diff --git a/account_reconcile_model_strict_match_amount/tests/__init__.py b/account_reconcile_model_strict_match_amount/tests/__init__.py new file mode 100644 index 00000000..97a05422 --- /dev/null +++ b/account_reconcile_model_strict_match_amount/tests/__init__.py @@ -0,0 +1 @@ +from . import test_account_reconcile_model_strict_match_amount diff --git a/account_reconcile_model_strict_match_amount/tests/test_account_reconcile_model_strict_match_amount.py b/account_reconcile_model_strict_match_amount/tests/test_account_reconcile_model_strict_match_amount.py new file mode 100644 index 00000000..e6c8f518 --- /dev/null +++ b/account_reconcile_model_strict_match_amount/tests/test_account_reconcile_model_strict_match_amount.py @@ -0,0 +1,80 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from odoo.addons.account.tests.test_reconciliation_matching_rules import TestReconciliationMatchingRules + + +class TestAccountReconcileModelStrictMatchAmount(TestReconciliationMatchingRules): + + def setUp(self): + super().setUp() + self.partner_3 = self.env['res.partner'].create({'name': 'partner_3'}) + self.partner_4 = self.env['res.partner'].create({'name': 'partner_4'}) + self.invoice_line_5 = self._create_invoice_line( + 150, self.partner_3, 'out_invoice' + ) + self.invoice_line_5.ref = 'ABC001XYZ' + self.invoice_line_6 = self._create_invoice_line( + 300, self.partner_4, 'out_invoice' + ) + self.invoice_line_6.name = 'ABC002XYZ' + + self.bank_st_2 = self.env['account.bank.statement'].create({ + 'name': 'test bank journal 2', 'journal_id': self.bank_journal.id, + }) + + self.bank_line_3 = self.env['account.bank.statement.line'].create({ + 'statement_id': self.bank_st_2.id, + 'name': 'ABC001XYZ', + 'partner_id': self.partner_3.id, + 'amount': 70, + 'sequence': 1, + }) + self.bank_line_4 = self.env['account.bank.statement.line'].create({ + 'statement_id': self.bank_st_2.id, + 'name': 'ABC002XYZ', + 'partner_id': self.partner_4.id, + 'amount': 270, + 'sequence': 1, + }) + + def test_auto_reconcile_strict_match_100(self): + my_rule = self.env['account.reconcile.model'].create({ + 'name': 'Strict Invoice matching amount 100%', + 'rule_type': 'invoice_matching', + 'auto_reconcile': True, + 'match_nature': 'both', + 'match_partner': True, + 'match_same_currency': True, + 'match_total_amount': True, + 'match_total_amount_param': 100.0, + 'strict_match_total_amount': True, + # 'match_partner_ids': [ + # (6, 0, [self.partner_3.id, self.partner_4.id]) + # ], + }) + + self._check_statement_matching(my_rule, { + self.bank_line_3.id: {'aml_ids': []}, + self.bank_line_4.id: {'aml_ids': []}, + }, statements=self.bank_st_2) + + def test_auto_reconcile_strict_match_80(self): + my_rule = self.env['account.reconcile.model'].create({ + 'name': 'Strict Invoice matching amount 80%', + 'rule_type': 'invoice_matching', + 'auto_reconcile': True, + 'match_nature': 'both', + 'match_partner': True, + 'match_same_currency': True, + 'match_total_amount': True, + 'match_total_amount_param': 80.0, + 'strict_match_total_amount': True, + # 'match_partner_ids': [ + # (6, 0, [self.partner_3.id, self.partner_4.id]) + # ], + }) + + self._check_statement_matching(my_rule, { + self.bank_line_3.id: {'aml_ids': []}, + self.bank_line_4.id: {'aml_ids': [self.invoice_line_6.id], 'model': my_rule, 'status': 'reconciled'}, + }, statements=self.bank_st_2) diff --git a/account_reconcile_model_strict_match_amount/views/account_reconcile_model.xml b/account_reconcile_model_strict_match_amount/views/account_reconcile_model.xml new file mode 100644 index 00000000..33fd273b --- /dev/null +++ b/account_reconcile_model_strict_match_amount/views/account_reconcile_model.xml @@ -0,0 +1,16 @@ + + + + account.reconcile.model.form + account.reconcile.model + + + + + + + {'invisible': ['|', '|', ('rule_type', '!=', 'invoice_matching'), ('match_total_amount', '=', False), ('strict_match_total_amount', '=', True)]} + + + +