diff --git a/account_reconcile_restrict_partner_mismatch/README.rst b/account_reconcile_restrict_partner_mismatch/README.rst new file mode 100644 index 0000000000..145d112ac9 --- /dev/null +++ b/account_reconcile_restrict_partner_mismatch/README.rst @@ -0,0 +1,84 @@ +=================================== +Reconcile restrict partner mismatch +=================================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--reconcile-lightgray.png?logo=github + :target: https://github.com/OCA/account-reconcile/tree/13.0/account_reconcile_restrict_partner_mismatch + :alt: OCA/account-reconcile +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/account-reconcile-13-0/account-reconcile-13-0-account_reconcile_restrict_partner_mismatch + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/98/13.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module restricts reconciliation between journal items when: + + - both items have different partners + - one item is with partner and the other without it + +This rule applies only for journal items using receivable and payable account type. + +As at the moment of installation some journal items could have been reconciled +using different partners, you can detect them in menu Accounting > Adviser > +Reconciled items with partner mismatch. + +**Table of contents** + +.. contents:: + :local: + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Camptocamp + +Contributors +~~~~~~~~~~~~ + +* `Tecnativa `_: + + * Ernesto Tejeda + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/account-reconcile `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_reconcile_restrict_partner_mismatch/__init__.py b/account_reconcile_restrict_partner_mismatch/__init__.py new file mode 100644 index 0000000000..bf588bc8b8 --- /dev/null +++ b/account_reconcile_restrict_partner_mismatch/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import report diff --git a/account_reconcile_restrict_partner_mismatch/__manifest__.py b/account_reconcile_restrict_partner_mismatch/__manifest__.py new file mode 100644 index 0000000000..694d288e96 --- /dev/null +++ b/account_reconcile_restrict_partner_mismatch/__manifest__.py @@ -0,0 +1,16 @@ +# Copyright 2019 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "Reconcile restrict partner mismatch", + "summary": "Restrict reconciliation on receivable " + "and payable accounts to the same partner", + "version": "13.0.1.0.0", + "depends": ["account"], + "author": "Camptocamp, Odoo Community Association (OCA)", + "website": "http://www.github.com/OCA/account-reconcile", + "category": "Finance", + "license": "AGPL-3", + "data": ["report/account_move_lines_report.xml", "security/ir.model.access.csv"], + "installable": True, +} diff --git a/account_reconcile_restrict_partner_mismatch/i18n/account_reconcile_restrict_partner_mismatch.pot b/account_reconcile_restrict_partner_mismatch/i18n/account_reconcile_restrict_partner_mismatch.pot new file mode 100644 index 0000000000..8958863273 --- /dev/null +++ b/account_reconcile_restrict_partner_mismatch/i18n/account_reconcile_restrict_partner_mismatch.pot @@ -0,0 +1,104 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_reconcile_restrict_partner_mismatch +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: account_reconcile_restrict_partner_mismatch +#: model:ir.model.fields,field_description:account_reconcile_restrict_partner_mismatch.field_account_reconcile_partner_mismatch_report__account_id +msgid "Account" +msgstr "" + +#. module: account_reconcile_restrict_partner_mismatch +#: model:ir.model,name:account_reconcile_restrict_partner_mismatch.model_account_reconcile_partner_mismatch_report +msgid "Account Reconcile Partner Mismatch Report" +msgstr "" + +#. module: account_reconcile_restrict_partner_mismatch +#: model:ir.model.fields,field_description:account_reconcile_restrict_partner_mismatch.field_account_reconcile_partner_mismatch_report__account_type_id +msgid "Account type" +msgstr "" + +#. module: account_reconcile_restrict_partner_mismatch +#: model:ir.model.fields,field_description:account_reconcile_restrict_partner_mismatch.field_account_reconcile_partner_mismatch_report__credit_amount +msgid "Credit amount" +msgstr "" + +#. module: account_reconcile_restrict_partner_mismatch +#: model:ir.model.fields,field_description:account_reconcile_restrict_partner_mismatch.field_account_reconcile_partner_mismatch_report__credit_move_id +msgid "Credit move" +msgstr "" + +#. module: account_reconcile_restrict_partner_mismatch +#: model:ir.model.fields,field_description:account_reconcile_restrict_partner_mismatch.field_account_reconcile_partner_mismatch_report__credit_partner_id +msgid "Credit partner" +msgstr "" + +#. module: account_reconcile_restrict_partner_mismatch +#: model:ir.model.fields,field_description:account_reconcile_restrict_partner_mismatch.field_account_reconcile_partner_mismatch_report__debit_amount +msgid "Debit amount" +msgstr "" + +#. module: account_reconcile_restrict_partner_mismatch +#: model:ir.model.fields,field_description:account_reconcile_restrict_partner_mismatch.field_account_reconcile_partner_mismatch_report__debit_move_id +msgid "Debit move" +msgstr "" + +#. module: account_reconcile_restrict_partner_mismatch +#: model:ir.model.fields,field_description:account_reconcile_restrict_partner_mismatch.field_account_reconcile_partner_mismatch_report__debit_partner_id +msgid "Debit partner" +msgstr "" + +#. module: account_reconcile_restrict_partner_mismatch +#: model:ir.model.fields,field_description:account_reconcile_restrict_partner_mismatch.field_account_reconcile_partner_mismatch_report__display_name +msgid "Display Name" +msgstr "" + +#. module: account_reconcile_restrict_partner_mismatch +#: model:ir.model.fields,field_description:account_reconcile_restrict_partner_mismatch.field_account_reconcile_partner_mismatch_report__full_reconcile_id +msgid "Full Reconcile" +msgstr "" + +#. module: account_reconcile_restrict_partner_mismatch +#: model:ir.model.fields,field_description:account_reconcile_restrict_partner_mismatch.field_account_reconcile_partner_mismatch_report__id +msgid "ID" +msgstr "" + +#. module: account_reconcile_restrict_partner_mismatch +#: model:ir.model,name:account_reconcile_restrict_partner_mismatch.model_account_move_line +msgid "Journal Item" +msgstr "" + +#. module: account_reconcile_restrict_partner_mismatch +#: model:ir.model.fields,field_description:account_reconcile_restrict_partner_mismatch.field_account_reconcile_partner_mismatch_report____last_update +msgid "Last Modified on" +msgstr "" + +#. module: account_reconcile_restrict_partner_mismatch +#: model:ir.model.fields,field_description:account_reconcile_restrict_partner_mismatch.field_account_reconcile_partner_mismatch_report__partial_reconcile_id +msgid "Partial Reconcile" +msgstr "" + +#. module: account_reconcile_restrict_partner_mismatch +#: model:ir.actions.act_window,name:account_reconcile_restrict_partner_mismatch.action_account_reconcile_partner_mismatch_report +#: model:ir.ui.menu,name:account_reconcile_restrict_partner_mismatch.menu_account_reconcile_partner_mismatch_report +#: model_terms:ir.ui.view,arch_db:account_reconcile_restrict_partner_mismatch.account_reconcile_partner_mismatch_report_view_form +#: model_terms:ir.ui.view,arch_db:account_reconcile_restrict_partner_mismatch.view_account_move_reconciled_report_tree +msgid "Reconciled items with partner mismatch" +msgstr "" + +#. module: account_reconcile_restrict_partner_mismatch +#: code:addons/account_reconcile_restrict_partner_mismatch/models/account_move_line.py:26 +#, python-format +msgid "The partner has to be the same on all lines for receivable and payable accounts!" +msgstr "" + diff --git a/account_reconcile_restrict_partner_mismatch/models/__init__.py b/account_reconcile_restrict_partner_mismatch/models/__init__.py new file mode 100644 index 0000000000..8795b3bea6 --- /dev/null +++ b/account_reconcile_restrict_partner_mismatch/models/__init__.py @@ -0,0 +1 @@ +from . import account_move_line diff --git a/account_reconcile_restrict_partner_mismatch/models/account_move_line.py b/account_reconcile_restrict_partner_mismatch/models/account_move_line.py new file mode 100644 index 0000000000..adafe6e47e --- /dev/null +++ b/account_reconcile_restrict_partner_mismatch/models/account_move_line.py @@ -0,0 +1,30 @@ +# Copyright 2019 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import _, models +from odoo.exceptions import UserError +from odoo.tools import config + + +class AccountMoveLine(models.Model): + _inherit = "account.move.line" + + def reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False): + if config["test_enable"] and not self.env.context.get("test_partner_mismatch"): + return super().reconcile(writeoff_acc_id, writeoff_journal_id) + + # to be consistent with parent method + if not self: + return True + partners = set() + for line in self: + if line.account_id.internal_type in ("receivable", "payable"): + partners.add(line.partner_id.id) + if len(partners) > 1: + raise UserError( + _( + "The partner has to be the same on all" + " lines for receivable and payable accounts!" + ) + ) + return super().reconcile(writeoff_acc_id, writeoff_journal_id) diff --git a/account_reconcile_restrict_partner_mismatch/readme/CONTRIBUTORS.rst b/account_reconcile_restrict_partner_mismatch/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..0b6bee443f --- /dev/null +++ b/account_reconcile_restrict_partner_mismatch/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Tecnativa `_: + + * Ernesto Tejeda diff --git a/account_reconcile_restrict_partner_mismatch/readme/DESCRIPTION.rst b/account_reconcile_restrict_partner_mismatch/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..a546b3fa4d --- /dev/null +++ b/account_reconcile_restrict_partner_mismatch/readme/DESCRIPTION.rst @@ -0,0 +1,10 @@ +This module restricts reconciliation between journal items when: + + - both items have different partners + - one item is with partner and the other without it + +This rule applies only for journal items using receivable and payable account type. + +As at the moment of installation some journal items could have been reconciled +using different partners, you can detect them in menu Accounting > Adviser > +Reconciled items with partner mismatch. diff --git a/account_reconcile_restrict_partner_mismatch/report/__init__.py b/account_reconcile_restrict_partner_mismatch/report/__init__.py new file mode 100644 index 0000000000..f32b29ee8d --- /dev/null +++ b/account_reconcile_restrict_partner_mismatch/report/__init__.py @@ -0,0 +1 @@ +from . import report_reconciled_lines diff --git a/account_reconcile_restrict_partner_mismatch/report/account_move_lines_report.xml b/account_reconcile_restrict_partner_mismatch/report/account_move_lines_report.xml new file mode 100644 index 0000000000..91aef71409 --- /dev/null +++ b/account_reconcile_restrict_partner_mismatch/report/account_move_lines_report.xml @@ -0,0 +1,61 @@ + + + + Reconciled items with partner mismatch + account.reconcile.partner.mismatch.report + + + + + + + + + + + + + + + + + + + account.reconcile.partner.mismatch.report.form + account.reconcile.partner.mismatch.report + +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + Reconciled items with partner mismatch + account.reconcile.partner.mismatch.report + tree,form + + + +
diff --git a/account_reconcile_restrict_partner_mismatch/report/report_reconciled_lines.py b/account_reconcile_restrict_partner_mismatch/report/report_reconciled_lines.py new file mode 100644 index 0000000000..a1ceeab401 --- /dev/null +++ b/account_reconcile_restrict_partner_mismatch/report/report_reconciled_lines.py @@ -0,0 +1,60 @@ +# Copyright 2019 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from odoo import fields, models, tools + + +class AccountReconcilePartnerMismatchReport(models.Model): + _name = "account.reconcile.partner.mismatch.report" + _description = "Account Reconcile Partner Mismatch Report" + _auto = False + + partial_reconcile_id = fields.Many2one( + "account.partial.reconcile", string="Partial Reconcile" + ) + full_reconcile_id = fields.Many2one("account.full.reconcile") + account_id = fields.Many2one("account.account", string="Account") + account_type_id = fields.Many2one("account.account.type", string="Account type") + debit_move_id = fields.Many2one("account.move.line", string="Debit move") + debit_amount = fields.Float("Debit amount") + debit_partner_id = fields.Many2one("res.partner", string="Debit partner") + credit_move_id = fields.Many2one("account.move.line", string="Credit move") + credit_amount = fields.Float("Credit amount") + credit_partner_id = fields.Many2one("res.partner", string="Credit partner") + + def init(self): + """Select lines which violate defined rules""" + tools.drop_view_if_exists(self.env.cr, self._table) + self._cr.execute( + """CREATE OR REPLACE VIEW %s AS ( + SELECT pr.id id + , pr.id partial_reconcile_id + , pr.full_reconcile_id + , pr.debit_move_id + , daml.debit debit_amount + , aat.id account_type_id + , daml.partner_id debit_partner_id + , daml.account_id account_id + , pr.credit_move_id + , caml.credit credit_amount + , caml.partner_id credit_partner_id + FROM account_partial_reconcile pr + LEFT JOIN account_move_line daml + ON daml.id = pr.debit_move_id + LEFT JOIN account_move_line caml + ON caml.id = pr.credit_move_id + LEFT JOIN account_account aa + ON daml.account_id = aa.id + LEFT JOIN account_account_type aat + ON aa.user_type_id = aat.id + WHERE aat.type in ('receivable', 'payable') + AND (daml.partner_id <> caml.partner_id + OR (daml.partner_id IS NULL + AND caml.partner_id IS NOT NULL) + OR (caml.partner_id IS NULL + AND daml.partner_id IS NOT NULL)) + ) + """ + % self._table + ) diff --git a/account_reconcile_restrict_partner_mismatch/security/ir.model.access.csv b/account_reconcile_restrict_partner_mismatch/security/ir.model.access.csv new file mode 100644 index 0000000000..8dea174ac2 --- /dev/null +++ b/account_reconcile_restrict_partner_mismatch/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_account_reconcile_partner_mismatch_report,access_account_reconcile_partner_mismatch_report,model_account_reconcile_partner_mismatch_report,account.group_account_user,1,0,0,0 diff --git a/account_reconcile_restrict_partner_mismatch/static/description/icon.png b/account_reconcile_restrict_partner_mismatch/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/account_reconcile_restrict_partner_mismatch/static/description/icon.png differ diff --git a/account_reconcile_restrict_partner_mismatch/static/description/index.html b/account_reconcile_restrict_partner_mismatch/static/description/index.html new file mode 100644 index 0000000000..3ddd6fd3fa --- /dev/null +++ b/account_reconcile_restrict_partner_mismatch/static/description/index.html @@ -0,0 +1,432 @@ + + + + + + +Reconcile restrict partner mismatch + + + +
+

Reconcile restrict partner mismatch

+ + +

Beta License: AGPL-3 OCA/account-reconcile Translate me on Weblate Try me on Runbot

+

This module restricts reconciliation between journal items when:

+
+
    +
  • both items have different partners
  • +
  • one item is with partner and the other without it
  • +
+
+

This rule applies only for journal items using receivable and payable account type.

+

As at the moment of installation some journal items could have been reconciled +using different partners, you can detect them in menu Accounting > Adviser > +Reconciled items with partner mismatch.

+

Table of contents

+ +
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Camptocamp
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/account-reconcile project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/account_reconcile_restrict_partner_mismatch/tests/__init__.py b/account_reconcile_restrict_partner_mismatch/tests/__init__.py new file mode 100644 index 0000000000..5eaab0190a --- /dev/null +++ b/account_reconcile_restrict_partner_mismatch/tests/__init__.py @@ -0,0 +1 @@ +from . import test_reconciliation diff --git a/account_reconcile_restrict_partner_mismatch/tests/test_reconciliation.py b/account_reconcile_restrict_partner_mismatch/tests/test_reconciliation.py new file mode 100644 index 0000000000..6f1e61f175 --- /dev/null +++ b/account_reconcile_restrict_partner_mismatch/tests/test_reconciliation.py @@ -0,0 +1,116 @@ +# Copyright 2019 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo.exceptions import UserError +from odoo.tests.common import SavepointCase + + +class TestReconciliation(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env( + context=dict( + cls.env.context, tracking_disable=True, test_partner_mismatch=True + ) + ) + cls.partner = cls.env.ref("base.res_partner_2") + cls.partner_id = cls.partner.id + cls.account_rcv = cls.env["account.account"].create( + { + "code": "RA1000", + "name": "Test Receivable Account", + "user_type_id": cls.env.ref("account.data_account_type_receivable").id, + "reconcile": True, + } + ) + cls.account_rsa = cls.env["account.account"].create( + { + "code": "PA1000", + "name": "Test Payable Account", + "user_type_id": cls.env.ref("account.data_account_type_payable").id, + "reconcile": True, + } + ) + cls.bank_journal = cls.env["account.journal"].create( + {"name": "Bank", "type": "bank", "code": "BNK67"} + ) + cls.aml = cls.init_moves() + + @classmethod + def create_move(cls, name, amount): + debit_line_vals = { + "name": name, + "debit": amount > 0 and amount or 0.0, + "credit": amount < 0 and -amount or 0.0, + "account_id": cls.account_rcv.id, + } + credit_line_vals = debit_line_vals.copy() + credit_line_vals["debit"] = debit_line_vals["credit"] + credit_line_vals["credit"] = debit_line_vals["debit"] + credit_line_vals["account_id"] = cls.account_rsa.id + vals = { + "journal_id": cls.bank_journal.id, + "line_ids": [(0, 0, debit_line_vals), (0, 0, credit_line_vals)], + } + return ( + cls.env["account.move"] + .with_context(default_journal_id=cls.bank_journal.id) + .create(vals) + .id + ) + + @classmethod + def init_moves(cls): + move_list_vals = [ + ("1", -1.83), + ("2", 728.35), + ("3", -4.46), + ("4", 0.32), + ("5", 14.72), + ("6", -737.10), + ] + move_ids = [] + for name, amount in move_list_vals: + move_ids.append(cls.create_move(name, amount)) + aml_recs = cls.env["account.move.line"].search( + [("move_id", "in", move_ids), ("account_id", "=", cls.account_rcv.id)] + ) + return aml_recs + + def test_reconcile_no_partner(self): + self.aml.reconcile() + self.assertTrue(all(self.aml.mapped("reconciled"))) + + def test_reconcile_partner_mismatch(self): + self.aml[0].partner_id = self.partner.id + with self.assertRaises(UserError): + self.aml.reconcile() + # all lines with same partner allowed + self.aml.write({"partner_id": self.partner.id}) + self.aml.reconcile() + self.assertTrue(all(self.aml.mapped("reconciled"))) + + def test_reconcile_accounts_excluded(self): + self.aml[0].partner_id = self.partner.id + with self.assertRaises(UserError): + self.aml.reconcile() + # reconciliation forbiden only for certain types of accounts + account = self.env["account.account"].create( + { + "code": "CAA1000", + "name": "Test Current Assets Account", + "user_type_id": self.env.ref( + "account.data_account_type_current_assets" + ).id, + "reconcile": True, + } + ) + self.aml[0].account_id = account.id + with self.assertRaises(UserError): + self.aml.reconcile() + # reconciliation for different partners allowed + # for not forbidden types + self.aml.write({"account_id": account.id}) + self.aml.reconcile() + self.assertTrue(all(self.aml.mapped("reconciled")))