diff --git a/.docker_files/main/__manifest__.py b/.docker_files/main/__manifest__.py index fbb6cc79..f5509e30 100644 --- a/.docker_files/main/__manifest__.py +++ b/.docker_files/main/__manifest__.py @@ -16,6 +16,7 @@ "account_closing_journal", "account_invoice_constraint_chronology_forced", "account_show_full_features", + "invoice_refund_not_earlier", "old_accounts", ], "installable": True, diff --git a/Dockerfile b/Dockerfile index 63588734..9b88eade 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,6 +17,7 @@ COPY account_bank_menu /mnt/extra-addons/account_bank_menu COPY account_closing_journal /mnt/extra-addons/account_closing_journal COPY account_invoice_constraint_chronology_forced /mnt/extra-addons/account_invoice_constraint_chronology_forced COPY account_show_full_features /mnt/extra-addons/account_show_full_features +COPY invoice_refund_not_earlier /mnt/extra-addons/invoice_refund_not_earlier COPY old_accounts /mnt/extra-addons/old_accounts COPY .docker_files/main /mnt/extra-addons/main diff --git a/invoice_refund_not_earlier/README.rst b/invoice_refund_not_earlier/README.rst new file mode 100644 index 00000000..626d5883 --- /dev/null +++ b/invoice_refund_not_earlier/README.rst @@ -0,0 +1,25 @@ +Invoice Refund Not Earlier +========================== +This module prevents to select a date prior to the invoice date when registering a refund. + +This constraint is added on all journal entries, not only invoices. + +Usage +----- +As member of the group `Accounting / Billing`, I go to the form view of an invoice: + +I click to create a refund. + +.. image:: static/description/invoice_form.png + +I select a refund date prior to the invoice date, then I click on ``Reverse``. + +.. image:: static/description/refund_wizard.png + +A blocking message appears: + +.. image:: static/description/error_message.png + +Contributors +------------ +* Numigi (tm) and all its contributors (https://bit.ly/numigiens) diff --git a/invoice_refund_not_earlier/__init__.py b/invoice_refund_not_earlier/__init__.py new file mode 100644 index 00000000..63bd6ae3 --- /dev/null +++ b/invoice_refund_not_earlier/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2024-today Numigi and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import models diff --git a/invoice_refund_not_earlier/__manifest__.py b/invoice_refund_not_earlier/__manifest__.py new file mode 100644 index 00000000..0750e381 --- /dev/null +++ b/invoice_refund_not_earlier/__manifest__.py @@ -0,0 +1,15 @@ +# Copyright 2024-today Numigi and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +{ + "name": "Invoice Refund Not Ealier", + "version": "1.0.0", + "author": "Numigi", + "maintainer": "Numigi", + "website": "https://www.numigi.com", + "license": "LGPL-3", + "category": "Accounting", + "summary": "Prevent refunds prior to the invoice date", + "depends": ["account"], + "installable": True, +} diff --git a/invoice_refund_not_earlier/i18n/fr.po b/invoice_refund_not_earlier/i18n/fr.po new file mode 100644 index 00000000..61df5ff2 --- /dev/null +++ b/invoice_refund_not_earlier/i18n/fr.po @@ -0,0 +1,33 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * invoice_refund_not_earlier +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-04-08 20:53+0000\n" +"PO-Revision-Date: 2019-04-08 16:54-0400\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" +"X-Generator: Poedit 2.0.6\n" + +#. module: invoice_refund_not_earlier +#: model:ir.model,name:invoice_refund_not_earlier.model_account_invoice +msgid "Invoice" +msgstr "" + +#. module: invoice_refund_not_earlier +#: code:addons/invoice_refund_not_earlier/models.py:51 +#, python-format +msgid "" +"The date of the reversal entry ({reversal_date}) can not be prior to the " +"original move date ({move_date})." +msgstr "" +"La date de renversement ({reversal_date}) ne peut pas être inférieure à la " +"date de la pièce comptable d'origine ({move_date})." diff --git a/invoice_refund_not_earlier/models/__init__.py b/invoice_refund_not_earlier/models/__init__.py new file mode 100644 index 00000000..ff1eb517 --- /dev/null +++ b/invoice_refund_not_earlier/models/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2024-today Numigi and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import account_move diff --git a/invoice_refund_not_earlier/models/account_move.py b/invoice_refund_not_earlier/models/account_move.py new file mode 100644 index 00000000..786a024c --- /dev/null +++ b/invoice_refund_not_earlier/models/account_move.py @@ -0,0 +1,20 @@ +# Copyright 2024-today Numigi and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo import models, _ +from odoo.exceptions import ValidationError + + +class AccountMove(models.Model): + + _inherit = "account.move" + + def _reverse_moves(self, default_values_list=None, cancel=False): + for i, move in enumerate(self): + date = default_values_list[i].get("date") + if date and date < move.date: + raise ValidationError(_( + "The date of the reversal entry ({reversal_date}) " + "can not be prior to the original move date ({move_date})." + ).format(reversal_date=date, move_date=move.date)) + return super()._reverse_moves(default_values_list, cancel) diff --git a/invoice_refund_not_earlier/static/description/error_message.png b/invoice_refund_not_earlier/static/description/error_message.png new file mode 100644 index 00000000..b1d54d03 Binary files /dev/null and b/invoice_refund_not_earlier/static/description/error_message.png differ diff --git a/invoice_refund_not_earlier/static/description/icon.png b/invoice_refund_not_earlier/static/description/icon.png new file mode 100644 index 00000000..92a86b10 Binary files /dev/null and b/invoice_refund_not_earlier/static/description/icon.png differ diff --git a/invoice_refund_not_earlier/static/description/invoice_form.png b/invoice_refund_not_earlier/static/description/invoice_form.png new file mode 100644 index 00000000..72c002d9 Binary files /dev/null and b/invoice_refund_not_earlier/static/description/invoice_form.png differ diff --git a/invoice_refund_not_earlier/static/description/refund_wizard.png b/invoice_refund_not_earlier/static/description/refund_wizard.png new file mode 100644 index 00000000..1a45a78d Binary files /dev/null and b/invoice_refund_not_earlier/static/description/refund_wizard.png differ diff --git a/invoice_refund_not_earlier/tests/__init__.py b/invoice_refund_not_earlier/tests/__init__.py new file mode 100644 index 00000000..d2ac44f2 --- /dev/null +++ b/invoice_refund_not_earlier/tests/__init__.py @@ -0,0 +1,2 @@ +# © 2019 Numigi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). diff --git a/invoice_refund_not_earlier/tests/test_journal_entry.py b/invoice_refund_not_earlier/tests/test_journal_entry.py new file mode 100644 index 00000000..d82bf0fc --- /dev/null +++ b/invoice_refund_not_earlier/tests/test_journal_entry.py @@ -0,0 +1,65 @@ +# © 2019 Numigi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import pytest +from datetime import datetime, timedelta +from odoo.tests.common import SavepointCase +from odoo.exceptions import ValidationError + + +class TestAccountMove(SavepointCase): + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.journal = cls.env['account.journal'].create({ + 'name': 'Test', + 'code': 'TEST', + 'type': 'general', + }) + cls.default_plan = cls.env['account.analytic.plan'].create( + {'name': 'Default', 'company_id': False}) + cls.analytic = cls.env['account.analytic.account'].create({ + 'name': 'test', + 'plan_id': cls.default_plan.id + }) + cls.account_1 = cls.env['account.account'].create({ + 'name': 'Account 1', + 'code': '501001', + 'account_type': 'asset_fixed', + }) + cls.account_2 = cls.env['account.account'].create({ + 'name': 'Account 2', + 'code': '101001', + 'account_type': 'expense', + }) + + cls.today = datetime.now().date() + cls.yesterday = cls.today - timedelta(1) + + cls.move = cls.env['account.move'].create({ + 'journal_id': cls.journal.id, + 'date': cls.today, + 'line_ids': [ + (0, 0, { + 'account_id': cls.account_1.id, + 'name': '/', + 'debit': 100, + }), + (0, 0, { + 'account_id': cls.account_2.id, + 'name': '/', + 'credit': 100, + }) + ] + }) + + def test_if_reversed_prior_to_original_move__validation_raised(self): + with pytest.raises(ValidationError): + self._reverse_moves(date=self.yesterday) + + def test_if_reversed_with_same_date__reversal_move_created(self): + assert self._reverse_moves(date=self.today) + + def _reverse_moves(self, date): + return self.move._reverse_moves([{"date": date}])