diff --git a/.docker_files/main/__manifest__.py b/.docker_files/main/__manifest__.py index f5509e30..9d1f3be6 100644 --- a/.docker_files/main/__manifest__.py +++ b/.docker_files/main/__manifest__.py @@ -15,6 +15,7 @@ "account_bank_menu", "account_closing_journal", "account_invoice_constraint_chronology_forced", + "account_payment_cancel_group", "account_show_full_features", "invoice_refund_not_earlier", "old_accounts", diff --git a/Dockerfile b/Dockerfile index 9b88eade..b242319b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,13 +9,16 @@ RUN pip3 install -r requirements.txt ENV THIRD_PARTY_ADDONS /mnt/third-party-addons RUN mkdir -p "${THIRD_PARTY_ADDONS}" && chown -R odoo "${THIRD_PARTY_ADDONS}" COPY ./gitoo.yml /gitoo.yml -RUN gitoo install-all --conf_file /gitoo.yml --destination "${THIRD_PARTY_ADDONS}" +RUN if [ -s /gitoo.yml ]; then \ + gitoo install-all --conf_file /gitoo.yml --destination "${THIRD_PARTY_ADDONS}"; \ + fi USER odoo 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_payment_cancel_group /mnt/extra-addons/account_payment_cancel_group 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 diff --git a/account_payment_cancel_group/README.rst b/account_payment_cancel_group/README.rst new file mode 100644 index 00000000..fa3a6d38 --- /dev/null +++ b/account_payment_cancel_group/README.rst @@ -0,0 +1,34 @@ +==================== +Payment Cancel Group +==================== + +.. contents:: Table of Contents + +Context +------- +In vanilla Odoo, when the module account_cancel is installed, users with basic accounting access +are able to cancel a payment (if authorized on the journal). + +.. image:: static/description/payment_cancel_button.png + +.. image:: static/description/payment_cancelled_message.png + +Summary +------- +This module adds a user group allowed to cancel payments. + +.. image:: static/description/user_form.png + +The `Cancel` button on payments is only displayed for members of this group. + +Known Issues +~~~~~~~~~~~~ +As of Odoo version 14.0, the cancellation of payments passes through the status ``Draft``. + +In other words, a posted payment can be reset to draft, then it can be cancelled. + +Because of this new behavior, the module now restricts both ``Reset To Draft`` and ``Cancel`` buttons. + +Contributors +------------ +* Numigi (tm) and all its contributors (https://bit.ly/numigiens) diff --git a/account_payment_cancel_group/__init__.py b/account_payment_cancel_group/__init__.py new file mode 100644 index 00000000..63bd6ae3 --- /dev/null +++ b/account_payment_cancel_group/__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/account_payment_cancel_group/__manifest__.py b/account_payment_cancel_group/__manifest__.py new file mode 100644 index 00000000..f299d23d --- /dev/null +++ b/account_payment_cancel_group/__manifest__.py @@ -0,0 +1,21 @@ +# 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": "Payment Cancel Group", + "version": "16.0.1.0.0", + "author": "Numigi", + "maintainer": "Numigi", + "website": "https://www.numigi.com", + "license": "LGPL-3", + "category": "Accounting", + "summary": "Add a user group allowed to cancel payments", + "depends": [ + "account", + ], + "data": [ + "security/res_groups.xml", + "views/account_payment.xml", + ], + "installable": True, +} diff --git a/account_payment_cancel_group/i18n/fr.po b/account_payment_cancel_group/i18n/fr.po new file mode 100644 index 00000000..f3c8e224 --- /dev/null +++ b/account_payment_cancel_group/i18n/fr.po @@ -0,0 +1,52 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_payment_cancel_group +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-06-21 20:40+0000\n" +"PO-Revision-Date: 2024-06-21 15:40-0500\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.3\n" + +#. module: account_payment_cancel_group +#: model:res.groups,name:account_payment_cancel_group.group_cancel_payments +msgid "Cancel Payments" +msgstr "Annulation de paiements" + +#. module: account_payment_cancel_group +#: model:ir.model.fields,field_description:account_payment_cancel_group.field_account_move__display_name +msgid "Display Name" +msgstr "" + +#. module: account_payment_cancel_group +#: model:ir.model.fields,field_description:account_payment_cancel_group.field_account_move__id +msgid "ID" +msgstr "" + +#. module: account_payment_cancel_group +#: model:ir.model,name:account_payment_cancel_group.model_account_move +msgid "Journal Entry" +msgstr "" + +#. module: account_payment_cancel_group +#: model:ir.model.fields,field_description:account_payment_cancel_group.field_account_move____last_update +msgid "Last Modified on" +msgstr "" + +#. module: account_payment_cancel_group +#: code:addons/account_payment_cancel_group/models/account_move.py:0 +#, python-format +msgid "You are not authorized to reset to draft or cancel payments." +msgstr "Vous n'êtes pas autorisé à remettre à brouillon ou annuler des paiements." + +#~ msgid "Payment Cancelled" +#~ msgstr "Paiement annulé" diff --git a/account_payment_cancel_group/models/__init__.py b/account_payment_cancel_group/models/__init__.py new file mode 100644 index 00000000..ff1eb517 --- /dev/null +++ b/account_payment_cancel_group/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/account_payment_cancel_group/models/account_move.py b/account_payment_cancel_group/models/account_move.py new file mode 100644 index 00000000..045ff4d5 --- /dev/null +++ b/account_payment_cancel_group/models/account_move.py @@ -0,0 +1,34 @@ +# 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 AccessError + + +class AccountMove(models.Model): + + _inherit = "account.move" + + def button_draft(self): + self._check_payment_cancel_authorization() + return super().button_draft() + + def button_cancel(self): + self._check_payment_cancel_authorization() + return super().button_cancel() + + def _check_payment_cancel_authorization(self): + if self._contains_payments() and not self._user_can_cancel_payments(): + raise AccessError( + _("You are not authorized to reset to draft or cancel payments.") + ) + + def _user_can_cancel_payments(self): + return self.env.user.has_group( + "account_payment_cancel_group.group_cancel_payments" + ) + + def _contains_payments(self): + return self and bool( + self.env["account.payment"].search([("move_id", "in", self.ids)]) + ) diff --git a/account_payment_cancel_group/security/res_groups.xml b/account_payment_cancel_group/security/res_groups.xml new file mode 100644 index 00000000..2be54a84 --- /dev/null +++ b/account_payment_cancel_group/security/res_groups.xml @@ -0,0 +1,9 @@ + + + + + Cancel Payments + + + + diff --git a/account_payment_cancel_group/static/description/icon.png b/account_payment_cancel_group/static/description/icon.png new file mode 100644 index 00000000..92a86b10 Binary files /dev/null and b/account_payment_cancel_group/static/description/icon.png differ diff --git a/account_payment_cancel_group/static/description/payment_cancel_button.png b/account_payment_cancel_group/static/description/payment_cancel_button.png new file mode 100644 index 00000000..8a37af30 Binary files /dev/null and b/account_payment_cancel_group/static/description/payment_cancel_button.png differ diff --git a/account_payment_cancel_group/static/description/payment_cancelled_message.png b/account_payment_cancel_group/static/description/payment_cancelled_message.png new file mode 100644 index 00000000..d6f5459f Binary files /dev/null and b/account_payment_cancel_group/static/description/payment_cancelled_message.png differ diff --git a/account_payment_cancel_group/static/description/user_form.png b/account_payment_cancel_group/static/description/user_form.png new file mode 100644 index 00000000..9c1722f8 Binary files /dev/null and b/account_payment_cancel_group/static/description/user_form.png differ diff --git a/account_payment_cancel_group/tests/__init__.py b/account_payment_cancel_group/tests/__init__.py new file mode 100644 index 00000000..f8b1d4c0 --- /dev/null +++ b/account_payment_cancel_group/tests/__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 test_payment_cancel diff --git a/account_payment_cancel_group/tests/test_payment_cancel.py b/account_payment_cancel_group/tests/test_payment_cancel.py new file mode 100644 index 00000000..d036b170 --- /dev/null +++ b/account_payment_cancel_group/tests/test_payment_cancel.py @@ -0,0 +1,65 @@ +# Copyright 2024-today Numigi and all its contributors (https://bit.ly/numigiens) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import pytest +from odoo.tests import common +from odoo.exceptions import AccessError + + +class TestPaymentCancel(common.SavepointCase): + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.user = cls.env["res.users"].create( + { + "name": "Test User", + "email": "test@test.com", + "login": "test@test.com", + "groups_id": [ + (4, cls.env.ref("account.group_account_manager").id), + ], + } + ) + + cls.journal = cls.env["account.journal"].create( + { + "name": "Test Bank Journal", + "type": "bank", + "code": "TEST", + } + ) + + cls.supplier = cls.env["res.partner"].create({"name": "Supplier"}) + cls.payment = cls.env["account.payment"].create( + { + "journal_id": cls.journal.id, + "partner_id": cls.supplier.id, + "amount": 100, + "payment_type": "outbound", + "payment_method_id": cls.env.ref( + "account.account_payment_method_manual_out" + ).id, + "partner_type": "supplier", + } + ) + + def test_if_not_member_of_group__action_draft_not_allowed(self): + with pytest.raises(AccessError): + self.payment.with_user(self.user).action_draft() + + def test_if_not_member_of_group__action_cancel_not_allowed(self): + with pytest.raises(AccessError): + self.payment.with_user(self.user).action_cancel() + + def test_if_member_of_group__user_allowed(self): + self.user.groups_id |= self.env.ref( + "account_payment_cancel_group.group_cancel_payments" + ) + self.payment.with_user(self.user).action_draft() + assert self.payment.state == "draft" + self.payment.with_user(self.user).action_cancel() + assert self.payment.state == "cancel" + + def test_call_method_with_empty_recordset(self): + self.env["account.payment"].with_user(self.user).action_draft() diff --git a/account_payment_cancel_group/views/account_payment.xml b/account_payment_cancel_group/views/account_payment.xml new file mode 100644 index 00000000..1009723c --- /dev/null +++ b/account_payment_cancel_group/views/account_payment.xml @@ -0,0 +1,18 @@ + + + + + Payment Form: restrict Cancel button + account.payment + + + + + + + +