From 639a7c70d089bbb0022efda8d4b3c667a0809e5f Mon Sep 17 00:00:00 2001 From: SilvioC2C Date: Fri, 17 Oct 2025 22:01:55 +0200 Subject: [PATCH 1/6] [ADD] sale_confirm_group: Add module --- sale_confirm_group/README.rst | 97 ++++ sale_confirm_group/__init__.py | 2 + sale_confirm_group/__manifest__.py | 19 + sale_confirm_group/models/__init__.py | 2 + sale_confirm_group/models/res_company.py | 16 + sale_confirm_group/models/sale_order.py | 88 ++++ sale_confirm_group/pyproject.toml | 3 + sale_confirm_group/readme/CONFIGURE.md | 7 + sale_confirm_group/readme/CONTRIBUTORS.md | 2 + sale_confirm_group/readme/DESCRIPTION.md | 4 + .../static/description/index.html | 448 ++++++++++++++++++ sale_confirm_group/tests/__init__.py | 1 + sale_confirm_group/tests/common.py | 33 ++ .../tests/test_sale_confirm_group.py | 135 ++++++ sale_confirm_group/wizards/__init__.py | 1 + .../wizards/res_config_settings.py | 18 + .../wizards/res_config_settings.xml | 30 ++ 17 files changed, 906 insertions(+) create mode 100644 sale_confirm_group/README.rst create mode 100644 sale_confirm_group/__init__.py create mode 100644 sale_confirm_group/__manifest__.py create mode 100644 sale_confirm_group/models/__init__.py create mode 100644 sale_confirm_group/models/res_company.py create mode 100644 sale_confirm_group/models/sale_order.py create mode 100644 sale_confirm_group/pyproject.toml create mode 100644 sale_confirm_group/readme/CONFIGURE.md create mode 100644 sale_confirm_group/readme/CONTRIBUTORS.md create mode 100644 sale_confirm_group/readme/DESCRIPTION.md create mode 100644 sale_confirm_group/static/description/index.html create mode 100644 sale_confirm_group/tests/__init__.py create mode 100644 sale_confirm_group/tests/common.py create mode 100644 sale_confirm_group/tests/test_sale_confirm_group.py create mode 100644 sale_confirm_group/wizards/__init__.py create mode 100644 sale_confirm_group/wizards/res_config_settings.py create mode 100644 sale_confirm_group/wizards/res_config_settings.xml diff --git a/sale_confirm_group/README.rst b/sale_confirm_group/README.rst new file mode 100644 index 00000000000..37e769271cf --- /dev/null +++ b/sale_confirm_group/README.rst @@ -0,0 +1,97 @@ +======================= +Sale Confirmation Group +======================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:1106c7085c83fb3e552de5ae58762a1f9ac92b65fb98a427e106fc90fa11dd83 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fsale--workflow-lightgray.png?logo=github + :target: https://github.com/OCA/sale-workflow/tree/18.0/sale_confirm_group + :alt: OCA/sale-workflow +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/sale-workflow-18-0/sale-workflow-18-0-sale_confirm_group + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/sale-workflow&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows configuring a list of groups per-company who are +granted permission to confirm sale orders: + +1. button "Confirm" in sale views is always hidden for users not in + those groups +2. if users outside those groups try to confirm a SO, an error is raised + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +- go to Sales / Configuration / Settings +- scroll until you find the "Use SO Confirmation Groups" checkbox +- if you want to restrict SO confirmation permission: + + - activate the checkbox + - add at least 1 security group to the list below the checkbox + +- if you don't want to restrict SO confirmation permission: + + - deactivate the checkbox, or remove all security groups from the list + below the checkbox + +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 to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Camptocamp + +Contributors +------------ + +- Silvio Gregorini +- Simone Orsi + +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/sale-workflow `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_confirm_group/__init__.py b/sale_confirm_group/__init__.py new file mode 100644 index 00000000000..aee8895e7a3 --- /dev/null +++ b/sale_confirm_group/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/sale_confirm_group/__manifest__.py b/sale_confirm_group/__manifest__.py new file mode 100644 index 00000000000..bac3135ffcd --- /dev/null +++ b/sale_confirm_group/__manifest__.py @@ -0,0 +1,19 @@ +# Copyright 2025 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +{ + "name": "Sale Confirmation Group", + "summary": "Allows configuring a list of groups per-company who are granted" + " permission to confirm sale orders", + "version": "18.0.1.0.0", + "author": "Camptocamp, Odoo Community Association (OCA) ", + "website": "https://github.com/OCA/sale-workflow", + "category": "Sale", + "license": "AGPL-3", + "depends": ["sale"], + "data": [ + # Settings view + "wizards/res_config_settings.xml", + ], + "installable": True, +} diff --git a/sale_confirm_group/models/__init__.py b/sale_confirm_group/models/__init__.py new file mode 100644 index 00000000000..eeacbbb6d63 --- /dev/null +++ b/sale_confirm_group/models/__init__.py @@ -0,0 +1,2 @@ +from . import res_company +from . import sale_order diff --git a/sale_confirm_group/models/res_company.py b/sale_confirm_group/models/res_company.py new file mode 100644 index 00000000000..553c28661d6 --- /dev/null +++ b/sale_confirm_group/models/res_company.py @@ -0,0 +1,16 @@ +# Copyright 2025 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +from odoo import fields, models + + +class Company(models.Model): + _inherit = "res.company" + + use_sale_confirmation_groups = fields.Boolean() + sale_confirmation_group_ids = fields.Many2many( + "res.groups", + relation="res_company_2_res_groups_sales_confirm_rel", + column1="company_id", + column2="group_id", + ) diff --git a/sale_confirm_group/models/sale_order.py b/sale_confirm_group/models/sale_order.py new file mode 100644 index 00000000000..1227c203676 --- /dev/null +++ b/sale_confirm_group/models/sale_order.py @@ -0,0 +1,88 @@ +# Copyright 2025 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +from odoo import api, exceptions, fields, models + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + user_can_confirm = fields.Boolean( + compute="_compute_user_can_confirm", compute_sudo=True + ) + + @api.depends( + # Keep these dotted fields as dependencies, because: + # - chances of them being updated very often (leading to lots of records cache + # invalidations and field recomputation) are quite low + # - removing them in favor of ``company_id`` alone (or no deps at all) means + # we'd have to invalidate the records cache manually when we need to check + # whether a user can actually confirm a sale order (to avoid using an outdated + # value for this field, since settings may have been updated in the meanwhile) + "company_id.use_sale_confirmation_groups", + "company_id.sale_confirmation_group_ids.users", + ) + @api.depends_context("uid") + def _compute_user_can_confirm(self): + user = self.env.user + for company, sales in self.grouped("company_id").items(): + if not company.use_sale_confirmation_groups: + sales.user_can_confirm = True + elif not (groups := company.sale_confirmation_group_ids): + sales.user_can_confirm = True + else: + sales.user_can_confirm = user in groups.users + + def action_confirm(self): + # OVERRIDE: prevent unallowed users from confirming a sale order, + # and raise a ``ValidationError`` instead + + # 1- check skipped: exit early + if self._skip_check_user_can_confirm(): + return super().action_confirm() + + # 2- all SO in ``self`` can be confirmed + elif (can_confirm := self._filter_user_can_confirm()) == self: + return super().action_confirm() + + # 3- at least 1 SO cannot be confirmed by the user + raise exceptions.ValidationError( + self.env._( + "User %s cannot confirm Sale(s) %s", + self.env.user.name, + ", ".join((self - can_confirm).mapped(lambda s: f"'{s.display_name}'")), + ) + ) + + def _skip_check_user_can_confirm(self) -> bool: + """Defines whether checks upon user permissions to confirm should be skipped + + Set context key "skip_check_user_can_confirm" as ``True`` to skip the checks. + """ + return bool(self.env.context.get("skip_check_user_can_confirm")) + + def _filter_user_can_confirm(self) -> "SaleOrder": + """Returns the subset of records that the current user can confirm + + Hook method, can be overridden + """ + return self.filtered("user_can_confirm") + + @api.model + def _get_view(self, view_id=None, view_type="form", **options): + # OVERRIDE: hide the SO ``action_confirm`` button to users who shouldn't be + # allowed confirmation + # NB: we override ``sale.order._get_view()``, not ``sale.order.get_view()``, + # because: + # - the result of ``sale.order._get_view()`` is cached + # - the result of ``sale.order._get_view()`` is updated by method + # ``ir.ui.view._add_missing_fields()`` to automatically add fields needed for + # the evaluation of nodes' attributes (required, invisible, etc...), + # so we don't need to do it here + arch, view = super()._get_view(view_id=view_id, view_type=view_type, **options) + for node in arch.xpath("//button[@name='action_confirm']"): + if value := node.get("invisible"): + node.set("invisible", f"not user_can_confirm or ({value})") + else: + node.set("invisible", "not user_can_confirm") + return arch, view diff --git a/sale_confirm_group/pyproject.toml b/sale_confirm_group/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/sale_confirm_group/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/sale_confirm_group/readme/CONFIGURE.md b/sale_confirm_group/readme/CONFIGURE.md new file mode 100644 index 00000000000..017b815eabd --- /dev/null +++ b/sale_confirm_group/readme/CONFIGURE.md @@ -0,0 +1,7 @@ +* go to Sales / Configuration / Settings +* scroll until you find the "Use SO Confirmation Groups" checkbox +* if you want to restrict SO confirmation permission: + * activate the checkbox + * add at least 1 security group to the list below the checkbox +* if you don't want to restrict SO confirmation permission: + * deactivate the checkbox, or remove all security groups from the list below the checkbox diff --git a/sale_confirm_group/readme/CONTRIBUTORS.md b/sale_confirm_group/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..736fa6397ac --- /dev/null +++ b/sale_confirm_group/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- Silvio Gregorini \ +- Simone Orsi \ diff --git a/sale_confirm_group/readme/DESCRIPTION.md b/sale_confirm_group/readme/DESCRIPTION.md new file mode 100644 index 00000000000..7bf212b0ae2 --- /dev/null +++ b/sale_confirm_group/readme/DESCRIPTION.md @@ -0,0 +1,4 @@ +This module allows configuring a list of groups per-company who are granted permission to confirm sale orders: + +1. button "Confirm" in sale views is always hidden for users not in those groups +2. if users outside those groups try to confirm a SO, an error is raised diff --git a/sale_confirm_group/static/description/index.html b/sale_confirm_group/static/description/index.html new file mode 100644 index 00000000000..18a85266218 --- /dev/null +++ b/sale_confirm_group/static/description/index.html @@ -0,0 +1,448 @@ + + + + + +Sale Confirmation Group + + + +
+

Sale Confirmation Group

+ + +

Beta License: AGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runboat

+

This module allows configuring a list of groups per-company who are +granted permission to confirm sale orders:

+
    +
  1. button “Confirm” in sale views is always hidden for users not in +those groups
  2. +
  3. if users outside those groups try to confirm a SO, an error is raised
  4. +
+

Table of contents

+ +
+

Configuration

+
    +
  • go to Sales / Configuration / Settings
  • +
  • scroll until you find the “Use SO Confirmation Groups” checkbox
  • +
  • if you want to restrict SO confirmation permission:
      +
    • activate the checkbox
    • +
    • add at least 1 security group to the list below the checkbox
    • +
    +
  • +
  • if you don’t want to restrict SO confirmation permission:
      +
    • deactivate the checkbox, or remove all security groups from the list +below the checkbox
    • +
    +
  • +
+
+
+

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 to smash it by providing a detailed and welcomed +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Camptocamp
  • +
+
+ +
+

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/sale-workflow project on GitHub.

+

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

+
+
+
+ + diff --git a/sale_confirm_group/tests/__init__.py b/sale_confirm_group/tests/__init__.py new file mode 100644 index 00000000000..1849033b133 --- /dev/null +++ b/sale_confirm_group/tests/__init__.py @@ -0,0 +1 @@ +from . import test_sale_confirm_group diff --git a/sale_confirm_group/tests/common.py b/sale_confirm_group/tests/common.py new file mode 100644 index 00000000000..ab03b75285d --- /dev/null +++ b/sale_confirm_group/tests/common.py @@ -0,0 +1,33 @@ +# Copyright 2025 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +from odoo.tests.common import new_test_user + +from odoo.addons.base.tests.common import DISABLED_MAIL_CONTEXT +from odoo.addons.sale.tests.common import TestSaleCommonBase + + +class TestSaleConfirmGroupCommon(TestSaleCommonBase): + @classmethod + def setUpClass(cls): + super().setUpClass() + # Update context env + cls.env = cls.env(context=dict(cls.env.context, **DISABLED_MAIL_CONTEXT)) + + # Prepare groups + cls.group_sale_user_xmlid = "sales_team.group_sale_salesman" + cls.group_sale_user = cls.env.ref(cls.group_sale_user_xmlid) + cls.group_sale_admin_xmlid = "sales_team.group_sale_manager" + cls.group_sale_admin = cls.env.ref(cls.group_sale_admin_xmlid) + + # Prepare a test user with "Sales / User: Own Documents Only" group + cls.test_user = new_test_user( + cls.env, + login="test-sale-confirm-group-user", + groups=cls.group_sale_user_xmlid, + ) + + # Setup company: activate the feature, add "Sales / Administrator" as the only + # group allowed to confirm sales + cls.env.company.use_sale_confirmation_groups = True + cls.env.company.sale_confirmation_group_ids = cls.group_sale_admin diff --git a/sale_confirm_group/tests/test_sale_confirm_group.py b/sale_confirm_group/tests/test_sale_confirm_group.py new file mode 100644 index 00000000000..e99fe15ff29 --- /dev/null +++ b/sale_confirm_group/tests/test_sale_confirm_group.py @@ -0,0 +1,135 @@ +# Copyright 2025 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +from lxml.etree import fromstring + +from odoo import fields +from odoo.exceptions import ValidationError +from odoo.tests.common import users + +from .common import TestSaleConfirmGroupCommon + + +class TestSaleConfirmGroup(TestSaleConfirmGroupCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + # Dummy customer for SO creation + cls.customer = cls.env["res.partner"].create({"name": "Customer"}) + + def _create_sale(self): + return self.env["sale.order"].create( + { + "partner_id": self.customer.id, + "order_line": [ + fields.Command.create( + { + "name": "Product", + "product_id": self.env.ref("product.consu_delivery_01").id, + "product_uom_qty": 1, + "price_unit": 50.00, + } + ) + ], + } + ) + + @users("test-sale-confirm-group-user") + def test_00_groups_usage_not_active(self): + # Deactivate the usage of confirmation groups: the current user can confirm + sale = self._create_sale() + self.env.company.sudo().write( + { + "use_sale_confirmation_groups": False, + "sale_confirmation_group_ids": [fields.Command.clear()], + } + ) + self.assertTrue(sale.user_can_confirm) + sale.action_confirm() + self.assertEqual(sale.state, "sale") + + @users("test-sale-confirm-group-user") + def test_01_groups_usage_active_no_groups(self): + # Activate the usage of confirmation groups, but remove all groups: the current + # user can confirm + sale = self._create_sale() + self.env.company.sudo().write( + { + "use_sale_confirmation_groups": True, + "sale_confirmation_group_ids": [fields.Command.clear()], + } + ) + self.assertTrue(sale.user_can_confirm) + sale.action_confirm() + self.assertEqual(sale.state, "sale") + + @users("test-sale-confirm-group-user") + def test_02_groups_usage_active_with_groups(self): + # Keep the usage of confirmation groups, keep the sale admins as the only + # allowed group: the current user cannot confirm + sale = self._create_sale() + self.assertFalse(sale.user_can_confirm) + with self.assertRaises(ValidationError) as error: + sale.action_confirm() + self.assertEqual( + error.exception.args[0], + f"User {self.env.user.name} cannot confirm" + f" Sale(s) '{sale.display_name}'", + ) + # Add the sale users as allowed group: the current user can now confirm + self.env.company.sudo().sale_confirmation_group_ids += self.group_sale_user + self.assertTrue(sale.user_can_confirm) + sale.action_confirm() + self.assertEqual(sale.state, "sale") + # Remove the sale users as allowed group: the current user cannot confirm + sale = self._create_sale() + self.env.company.sudo().sale_confirmation_group_ids -= self.group_sale_user + self.assertFalse(sale.user_can_confirm) + with self.assertRaises(ValidationError) as error: + sale.action_confirm() + self.assertEqual( + error.exception.args[0], + f"User {self.env.user.name} cannot confirm" + f" Sale(s) '{sale.display_name}'", + ) + # Add the current user to the "Sales / Administrator" group: the current user + # can now confirm + self.test_user.groups_id += self.group_sale_admin + self.assertTrue(sale.user_can_confirm) + sale.action_confirm() + self.assertEqual(sale.state, "sale") + # Remove the current user from the "Sales / Administrator" group: the current + # user cannot confirm + sale = self._create_sale() + self.test_user.groups_id -= self.group_sale_admin + self.assertFalse(sale.user_can_confirm) + with self.assertRaises(ValidationError) as error: + sale.action_confirm() + self.assertEqual( + error.exception.args[0], + f"User {self.env.user.name} cannot confirm" + f" Sale(s) '{sale.display_name}'", + ) + + @users("test-sale-confirm-group-user") + def test_03_context_key(self): + # Keep the usage of confirmation groups, keep the sale admins as the only + # allowed group: the current user cannot confirm + sale = self._create_sale() + self.assertFalse(sale.user_can_confirm) + with self.assertRaises(ValidationError) as error: + sale.action_confirm() + self.assertEqual( + error.exception.args[0], + f"User {self.env.user.name} cannot confirm" + f" Sale(s) '{sale.display_name}'", + ) + # Add the "skip_check_user_can_confirm" context key to skip user permission + # checks: the curren user can confirm + sale.with_context(skip_check_user_can_confirm=True).action_confirm() + self.assertEqual(sale.state, "sale") + + def test_action_confirm_invisible(self): + arch = fromstring(self.env["sale.order"].get_view(view_type="form")["arch"]) + for node in arch.xpath("//button[@name='action_confirm']"): + self.assertIn("not user_can_confirm", node.get("invisible")) diff --git a/sale_confirm_group/wizards/__init__.py b/sale_confirm_group/wizards/__init__.py new file mode 100644 index 00000000000..0deb68c4680 --- /dev/null +++ b/sale_confirm_group/wizards/__init__.py @@ -0,0 +1 @@ +from . import res_config_settings diff --git a/sale_confirm_group/wizards/res_config_settings.py b/sale_confirm_group/wizards/res_config_settings.py new file mode 100644 index 00000000000..0c227cf3a25 --- /dev/null +++ b/sale_confirm_group/wizards/res_config_settings.py @@ -0,0 +1,18 @@ +# Copyright 2025 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +from odoo import fields, models + + +class Settings(models.TransientModel): + _inherit = "res.config.settings" + + use_sale_confirmation_groups = fields.Boolean( + related="company_id.use_sale_confirmation_groups", + readonly=False, + ) + sale_confirmation_group_ids = fields.Many2many( + "res.groups", + related="company_id.sale_confirmation_group_ids", + readonly=False, + ) diff --git a/sale_confirm_group/wizards/res_config_settings.xml b/sale_confirm_group/wizards/res_config_settings.xml new file mode 100644 index 00000000000..ce4aa8d93ad --- /dev/null +++ b/sale_confirm_group/wizards/res_config_settings.xml @@ -0,0 +1,30 @@ + + + + res.config.settings + + + + + + + + + + + + + + + From 38936192edb6905f12039aa7b0ce54f11f660357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Tue, 28 Oct 2025 16:55:20 +0100 Subject: [PATCH 2/6] [FIX] sale_confirm_group: allow archived users and super-user to confirm --- sale_confirm_group/models/sale_order.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sale_confirm_group/models/sale_order.py b/sale_confirm_group/models/sale_order.py index 1227c203676..697b9473456 100644 --- a/sale_confirm_group/models/sale_order.py +++ b/sale_confirm_group/models/sale_order.py @@ -31,7 +31,9 @@ def _compute_user_can_confirm(self): elif not (groups := company.sale_confirmation_group_ids): sales.user_can_confirm = True else: - sales.user_can_confirm = user in groups.users + sales.user_can_confirm = ( + user in groups.with_context(active_test=False).users + ) def action_confirm(self): # OVERRIDE: prevent unallowed users from confirming a sale order, @@ -59,7 +61,7 @@ def _skip_check_user_can_confirm(self) -> bool: Set context key "skip_check_user_can_confirm" as ``True`` to skip the checks. """ - return bool(self.env.context.get("skip_check_user_can_confirm")) + return bool(self.env.context.get("skip_check_user_can_confirm", self.env.su)) def _filter_user_can_confirm(self) -> "SaleOrder": """Returns the subset of records that the current user can confirm From 19e06d39c6fc19c5e46df41de6dda7640cd03c05 Mon Sep 17 00:00:00 2001 From: oca-ci Date: Fri, 12 Dec 2025 12:54:12 +0000 Subject: [PATCH 3/6] [UPD] Update sale_confirm_group.pot --- .../i18n/sale_confirm_group.pot | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 sale_confirm_group/i18n/sale_confirm_group.pot diff --git a/sale_confirm_group/i18n/sale_confirm_group.pot b/sale_confirm_group/i18n/sale_confirm_group.pot new file mode 100644 index 00000000000..ad3b5cc84a4 --- /dev/null +++ b/sale_confirm_group/i18n/sale_confirm_group.pot @@ -0,0 +1,64 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_confirm_group +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.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: sale_confirm_group +#: model:ir.model,name:sale_confirm_group.model_res_company +msgid "Companies" +msgstr "" + +#. module: sale_confirm_group +#: model:ir.model,name:sale_confirm_group.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: sale_confirm_group +#: model_terms:ir.ui.view,arch_db:sale_confirm_group.sale_res_config_settings_view_form_inherit +msgid "" +"If the checkbox is flagged and at least 1 group is selected, SO confirmation" +" will be allowed to such groups only. This is company-specific." +msgstr "" + +#. module: sale_confirm_group +#: model:ir.model.fields,field_description:sale_confirm_group.field_res_company__sale_confirmation_group_ids +#: model:ir.model.fields,field_description:sale_confirm_group.field_res_config_settings__sale_confirmation_group_ids +msgid "Sale Confirmation Group" +msgstr "" + +#. module: sale_confirm_group +#: model:ir.model,name:sale_confirm_group.model_sale_order +msgid "Sales Order" +msgstr "" + +#. module: sale_confirm_group +#: model_terms:ir.ui.view,arch_db:sale_confirm_group.sale_res_config_settings_view_form_inherit +msgid "Use SO Confirmation Groups" +msgstr "" + +#. module: sale_confirm_group +#: model:ir.model.fields,field_description:sale_confirm_group.field_res_company__use_sale_confirmation_groups +#: model:ir.model.fields,field_description:sale_confirm_group.field_res_config_settings__use_sale_confirmation_groups +msgid "Use Sale Confirmation Groups" +msgstr "" + +#. module: sale_confirm_group +#. odoo-python +#: code:addons/sale_confirm_group/models/sale_order.py:0 +msgid "User %s cannot confirm Sale(s) %s" +msgstr "" + +#. module: sale_confirm_group +#: model:ir.model.fields,field_description:sale_confirm_group.field_sale_order__user_can_confirm +msgid "User Can Confirm" +msgstr "" From b2ff9b7f5e0b8453282c3e008f07e536e814af29 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 12 Dec 2025 13:04:38 +0000 Subject: [PATCH 4/6] [BOT] post-merge updates --- sale_confirm_group/README.rst | 8 +++-- .../static/description/icon.png | Bin 0 -> 10254 bytes .../static/description/index.html | 28 +++++++++++------- 3 files changed, 23 insertions(+), 13 deletions(-) create mode 100644 sale_confirm_group/static/description/icon.png diff --git a/sale_confirm_group/README.rst b/sale_confirm_group/README.rst index 37e769271cf..37ed8b94d14 100644 --- a/sale_confirm_group/README.rst +++ b/sale_confirm_group/README.rst @@ -1,3 +1,7 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + ======================= Sale Confirmation Group ======================= @@ -7,13 +11,13 @@ Sale Confirmation Group !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:1106c7085c83fb3e552de5ae58762a1f9ac92b65fb98a427e106fc90fa11dd83 + !! source digest: sha256:8e2190fe6ab83943e118620c7b3be92f1cc525eabf95b57c350e131544e0f2e6 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |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 +.. |badge2| image:: https://img.shields.io/badge/license-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%2Fsale--workflow-lightgray.png?logo=github diff --git a/sale_confirm_group/static/description/icon.png b/sale_confirm_group/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1dcc49c24f364e9adf0afbc6fc0bac6dbecdeb11 GIT binary patch literal 10254 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 -Sale Confirmation Group +README.rst -
-

Sale Confirmation Group

+
+ + +Odoo Community Association + +
+

Sale Confirmation Group

-

Beta License: AGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runboat

This module allows configuring a list of groups per-company who are granted permission to confirm sale orders:

    @@ -391,7 +396,7 @@

    Sale Confirmation Group

-

Configuration

+

Configuration

  • go to Sales / Configuration / Settings
  • scroll until you find the “Use SO Confirmation Groups” checkbox
  • @@ -408,7 +413,7 @@

    Configuration

-

Bug Tracker

+

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 to smash it by providing a detailed and welcomed @@ -416,22 +421,22 @@

Bug Tracker

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

+
From 740d81fafe31e7418d15fe3fdb1f7e4fa51aa7ee Mon Sep 17 00:00:00 2001 From: JoshuaJan Date: Mon, 26 Jan 2026 23:46:52 +0800 Subject: [PATCH 5/6] [IMP] sale_confirm_group: pre-commit auto fixes --- sale_confirm_group/tests/test_sale_confirm_group.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/sale_confirm_group/tests/test_sale_confirm_group.py b/sale_confirm_group/tests/test_sale_confirm_group.py index e99fe15ff29..3ee0ac6b048 100644 --- a/sale_confirm_group/tests/test_sale_confirm_group.py +++ b/sale_confirm_group/tests/test_sale_confirm_group.py @@ -73,8 +73,7 @@ def test_02_groups_usage_active_with_groups(self): sale.action_confirm() self.assertEqual( error.exception.args[0], - f"User {self.env.user.name} cannot confirm" - f" Sale(s) '{sale.display_name}'", + f"User {self.env.user.name} cannot confirm Sale(s) '{sale.display_name}'", ) # Add the sale users as allowed group: the current user can now confirm self.env.company.sudo().sale_confirmation_group_ids += self.group_sale_user @@ -89,8 +88,7 @@ def test_02_groups_usage_active_with_groups(self): sale.action_confirm() self.assertEqual( error.exception.args[0], - f"User {self.env.user.name} cannot confirm" - f" Sale(s) '{sale.display_name}'", + f"User {self.env.user.name} cannot confirm Sale(s) '{sale.display_name}'", ) # Add the current user to the "Sales / Administrator" group: the current user # can now confirm @@ -107,8 +105,7 @@ def test_02_groups_usage_active_with_groups(self): sale.action_confirm() self.assertEqual( error.exception.args[0], - f"User {self.env.user.name} cannot confirm" - f" Sale(s) '{sale.display_name}'", + f"User {self.env.user.name} cannot confirm Sale(s) '{sale.display_name}'", ) @users("test-sale-confirm-group-user") @@ -121,8 +118,7 @@ def test_03_context_key(self): sale.action_confirm() self.assertEqual( error.exception.args[0], - f"User {self.env.user.name} cannot confirm" - f" Sale(s) '{sale.display_name}'", + f"User {self.env.user.name} cannot confirm Sale(s) '{sale.display_name}'", ) # Add the "skip_check_user_can_confirm" context key to skip user permission # checks: the curren user can confirm From f414c13fe3f4925aec68e9f2cf7cba5104aaa32a Mon Sep 17 00:00:00 2001 From: JoshuaJan Date: Thu, 29 Jan 2026 22:46:06 +0800 Subject: [PATCH 6/6] [MIG] sale_confirm_group: Migration to 19.0 --- sale_confirm_group/README.rst | 11 ++++++----- sale_confirm_group/__manifest__.py | 2 +- sale_confirm_group/models/sale_order.py | 12 +++++++----- sale_confirm_group/readme/CONTRIBUTORS.md | 1 + sale_confirm_group/static/description/index.html | 7 ++++--- sale_confirm_group/tests/common.py | 4 ++-- .../tests/test_sale_confirm_group.py | 14 +++++++++++--- 7 files changed, 32 insertions(+), 19 deletions(-) diff --git a/sale_confirm_group/README.rst b/sale_confirm_group/README.rst index 37ed8b94d14..702e9b704db 100644 --- a/sale_confirm_group/README.rst +++ b/sale_confirm_group/README.rst @@ -21,13 +21,13 @@ Sale Confirmation Group :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsale--workflow-lightgray.png?logo=github - :target: https://github.com/OCA/sale-workflow/tree/18.0/sale_confirm_group + :target: https://github.com/OCA/sale-workflow/tree/19.0/sale_confirm_group :alt: OCA/sale-workflow .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/sale-workflow-18-0/sale-workflow-18-0-sale_confirm_group + :target: https://translation.odoo-community.org/projects/sale-workflow-19-0/sale-workflow-19-0-sale_confirm_group :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png - :target: https://runboat.odoo-community.org/builds?repo=OCA/sale-workflow&target_branch=18.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/sale-workflow&target_branch=19.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| @@ -65,7 +65,7 @@ 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 to smash it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -82,6 +82,7 @@ Contributors - Silvio Gregorini - Simone Orsi +- Joshua Jan Maintainers ----------- @@ -96,6 +97,6 @@ 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/sale-workflow `_ project on GitHub. +This module is part of the `OCA/sale-workflow `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_confirm_group/__manifest__.py b/sale_confirm_group/__manifest__.py index bac3135ffcd..89aacdba1b1 100644 --- a/sale_confirm_group/__manifest__.py +++ b/sale_confirm_group/__manifest__.py @@ -5,7 +5,7 @@ "name": "Sale Confirmation Group", "summary": "Allows configuring a list of groups per-company who are granted" " permission to confirm sale orders", - "version": "18.0.1.0.0", + "version": "19.0.1.0.0", "author": "Camptocamp, Odoo Community Association (OCA) ", "website": "https://github.com/OCA/sale-workflow", "category": "Sale", diff --git a/sale_confirm_group/models/sale_order.py b/sale_confirm_group/models/sale_order.py index 697b9473456..91657a30030 100644 --- a/sale_confirm_group/models/sale_order.py +++ b/sale_confirm_group/models/sale_order.py @@ -20,7 +20,7 @@ class SaleOrder(models.Model): # whether a user can actually confirm a sale order (to avoid using an outdated # value for this field, since settings may have been updated in the meanwhile) "company_id.use_sale_confirmation_groups", - "company_id.sale_confirmation_group_ids.users", + "company_id.sale_confirmation_group_ids.user_ids", ) @api.depends_context("uid") def _compute_user_can_confirm(self): @@ -32,7 +32,7 @@ def _compute_user_can_confirm(self): sales.user_can_confirm = True else: sales.user_can_confirm = ( - user in groups.with_context(active_test=False).users + user in groups.with_context(active_test=False).user_ids ) def action_confirm(self): @@ -50,9 +50,11 @@ def action_confirm(self): # 3- at least 1 SO cannot be confirmed by the user raise exceptions.ValidationError( self.env._( - "User %s cannot confirm Sale(s) %s", - self.env.user.name, - ", ".join((self - can_confirm).mapped(lambda s: f"'{s.display_name}'")), + "User %(user)s cannot confirm Sale(s) %(sales)s", + user=self.env.user.name, + sales=", ".join( + (self - can_confirm).mapped(lambda s: f"'{s.display_name}'") + ), ) ) diff --git a/sale_confirm_group/readme/CONTRIBUTORS.md b/sale_confirm_group/readme/CONTRIBUTORS.md index 736fa6397ac..8bebebda9c9 100644 --- a/sale_confirm_group/readme/CONTRIBUTORS.md +++ b/sale_confirm_group/readme/CONTRIBUTORS.md @@ -1,2 +1,3 @@ - Silvio Gregorini \ - Simone Orsi \ +- Joshua Jan \ \ No newline at end of file diff --git a/sale_confirm_group/static/description/index.html b/sale_confirm_group/static/description/index.html index 012e72d1d53..ddf542ada51 100644 --- a/sale_confirm_group/static/description/index.html +++ b/sale_confirm_group/static/description/index.html @@ -374,7 +374,7 @@

Sale Confirmation Group

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:8e2190fe6ab83943e118620c7b3be92f1cc525eabf95b57c350e131544e0f2e6 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runboat

This module allows configuring a list of groups per-company who are granted permission to confirm sale orders:

    @@ -417,7 +417,7 @@

    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 to smash it by providing a detailed and welcomed -feedback.

    +feedback.

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

    @@ -433,6 +433,7 @@

    Contributors

    @@ -444,7 +445,7 @@

    Maintainers

    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/sale-workflow project on GitHub.

    +

    This module is part of the OCA/sale-workflow project on GitHub.

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

    diff --git a/sale_confirm_group/tests/common.py b/sale_confirm_group/tests/common.py index ab03b75285d..59332752af6 100644 --- a/sale_confirm_group/tests/common.py +++ b/sale_confirm_group/tests/common.py @@ -4,10 +4,10 @@ from odoo.tests.common import new_test_user from odoo.addons.base.tests.common import DISABLED_MAIL_CONTEXT -from odoo.addons.sale.tests.common import TestSaleCommonBase +from odoo.addons.sale.tests.common import TestSaleCommon -class TestSaleConfirmGroupCommon(TestSaleCommonBase): +class TestSaleConfirmGroupCommon(TestSaleCommon): @classmethod def setUpClass(cls): super().setUpClass() diff --git a/sale_confirm_group/tests/test_sale_confirm_group.py b/sale_confirm_group/tests/test_sale_confirm_group.py index 3ee0ac6b048..9ac065ae138 100644 --- a/sale_confirm_group/tests/test_sale_confirm_group.py +++ b/sale_confirm_group/tests/test_sale_confirm_group.py @@ -16,6 +16,14 @@ def setUpClass(cls): super().setUpClass() # Dummy customer for SO creation cls.customer = cls.env["res.partner"].create({"name": "Customer"}) + cls.product = cls.env["product.product"].create( + { + "name": "Test Product", + "type": "consu", + "sale_ok": True, + "standard_price": 50, + } + ) def _create_sale(self): return self.env["sale.order"].create( @@ -25,7 +33,7 @@ def _create_sale(self): fields.Command.create( { "name": "Product", - "product_id": self.env.ref("product.consu_delivery_01").id, + "product_id": self.product.id, "product_uom_qty": 1, "price_unit": 50.00, } @@ -92,14 +100,14 @@ def test_02_groups_usage_active_with_groups(self): ) # Add the current user to the "Sales / Administrator" group: the current user # can now confirm - self.test_user.groups_id += self.group_sale_admin + self.test_user.group_ids += self.group_sale_admin self.assertTrue(sale.user_can_confirm) sale.action_confirm() self.assertEqual(sale.state, "sale") # Remove the current user from the "Sales / Administrator" group: the current # user cannot confirm sale = self._create_sale() - self.test_user.groups_id -= self.group_sale_admin + self.test_user.group_ids -= self.group_sale_admin self.assertFalse(sale.user_can_confirm) with self.assertRaises(ValidationError) as error: sale.action_confirm()