diff --git a/website_sale_carrier_auto_assign/README.rst b/website_sale_carrier_auto_assign/README.rst new file mode 100644 index 0000000000..1d11985f6a --- /dev/null +++ b/website_sale_carrier_auto_assign/README.rst @@ -0,0 +1,90 @@ +================================ +Website Sale Carrier Auto Assign +================================ + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:a9b46f72bc3da4b07f6f3b421bb0d4aa16cec7073c5f7059c3f8313a6a024168 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fe--commerce-lightgray.png?logo=github + :target: https://github.com/OCA/e-commerce/tree/19.0/website_sale_carrier_auto_assign + :alt: OCA/e-commerce +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/e-commerce-19-0/e-commerce-19-0-website_sale_carrier_auto_assign + :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/e-commerce&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module is a glue between +`website_sale `__ +and +`sale_order_carrier_auto_assign `__. + +When ``sale_order_carrier_auto_assign`` is installed with +``carrier_on_create`` enabled and a shipping partner has +``property_delivery_carrier_id`` set, its ``create``/``write`` overrides +automatically add a delivery line from the partner's default carrier. On +website orders, ``website_sale`` already handles carrier selection via +``_get_preferred_delivery_method`` (which reads +``property_delivery_carrier_id``) and ``_set_delivery_method`` (which +removes any existing delivery line before adding the new one). Letting +both mechanisms run in parallel creates two delivery lines, causing a +``ValueError: Expected singleton`` crash in ``order_2_return_dict`` at +checkout confirmation. + +This module prevents the OCA auto-assign from running on website orders +(``website_id`` is set) so that ``website_sale`` remains the sole owner +of delivery-line management for e-commerce carts. + +**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 to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* ADHOC SA + +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/e-commerce `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/website_sale_carrier_auto_assign/__init__.py b/website_sale_carrier_auto_assign/__init__.py new file mode 100644 index 0000000000..31660d6a96 --- /dev/null +++ b/website_sale_carrier_auto_assign/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import models diff --git a/website_sale_carrier_auto_assign/__manifest__.py b/website_sale_carrier_auto_assign/__manifest__.py new file mode 100644 index 0000000000..d339402f3d --- /dev/null +++ b/website_sale_carrier_auto_assign/__manifest__.py @@ -0,0 +1,15 @@ +# Copyright 2026 ADHOC SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Website Sale Carrier Auto Assign", + "summary": "Prevent duplicate delivery lines on website orders when " + "sale_order_carrier_auto_assign is installed.", + "version": "19.0.1.0.0", + "development_status": "Beta", + "category": "Website", + "website": "https://github.com/OCA/e-commerce", + "author": "ADHOC SA, Odoo Community Association (OCA)", + "license": "AGPL-3", + "depends": ["website_sale", "sale_order_carrier_auto_assign"], + "auto_install": True, +} diff --git a/website_sale_carrier_auto_assign/models/__init__.py b/website_sale_carrier_auto_assign/models/__init__.py new file mode 100644 index 0000000000..cbd69320b0 --- /dev/null +++ b/website_sale_carrier_auto_assign/models/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import sale_order diff --git a/website_sale_carrier_auto_assign/models/sale_order.py b/website_sale_carrier_auto_assign/models/sale_order.py new file mode 100644 index 0000000000..87b775aa0f --- /dev/null +++ b/website_sale_carrier_auto_assign/models/sale_order.py @@ -0,0 +1,14 @@ +# Copyright 2026 ADHOC SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + def _is_auto_set_carrier_on_create(self): + self.ensure_one() + if self.website_id: + return False + return super()._is_auto_set_carrier_on_create() diff --git a/website_sale_carrier_auto_assign/pyproject.toml b/website_sale_carrier_auto_assign/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/website_sale_carrier_auto_assign/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/website_sale_carrier_auto_assign/readme/DESCRIPTION.md b/website_sale_carrier_auto_assign/readme/DESCRIPTION.md new file mode 100644 index 0000000000..d39c15f021 --- /dev/null +++ b/website_sale_carrier_auto_assign/readme/DESCRIPTION.md @@ -0,0 +1,19 @@ +This module is a glue between +[website_sale](https://github.com/odoo/odoo/tree/19.0/addons/website_sale) +and +[sale_order_carrier_auto_assign](https://github.com/OCA/sale-workflow/tree/19.0/sale_order_carrier_auto_assign). + +When `sale_order_carrier_auto_assign` is installed with `carrier_on_create` +enabled and a shipping partner has `property_delivery_carrier_id` set, its +`create`/`write` overrides automatically add a delivery line from the partner's +default carrier. On website orders, `website_sale` already handles carrier +selection via `_get_preferred_delivery_method` (which reads +`property_delivery_carrier_id`) and `_set_delivery_method` (which removes any +existing delivery line before adding the new one). Letting both mechanisms run +in parallel creates two delivery lines, causing a +`ValueError: Expected singleton` crash in `order_2_return_dict` at checkout +confirmation. + +This module prevents the OCA auto-assign from running on website orders +(`website_id` is set) so that `website_sale` remains the sole owner of +delivery-line management for e-commerce carts. diff --git a/website_sale_carrier_auto_assign/static/description/index.html b/website_sale_carrier_auto_assign/static/description/index.html new file mode 100644 index 0000000000..df8d93e8ee --- /dev/null +++ b/website_sale_carrier_auto_assign/static/description/index.html @@ -0,0 +1,433 @@ + + + + + +Website Sale Carrier Auto Assign + + + +
+

Website Sale Carrier Auto Assign

+ + +

Beta License: AGPL-3 OCA/e-commerce Translate me on Weblate Try me on Runboat

+

This module is a glue between +website_sale +and +sale_order_carrier_auto_assign.

+

When sale_order_carrier_auto_assign is installed with +carrier_on_create enabled and a shipping partner has +property_delivery_carrier_id set, its create/write overrides +automatically add a delivery line from the partner’s default carrier. On +website orders, website_sale already handles carrier selection via +_get_preferred_delivery_method (which reads +property_delivery_carrier_id) and _set_delivery_method (which +removes any existing delivery line before adding the new one). Letting +both mechanisms run in parallel creates two delivery lines, causing a +ValueError: Expected singleton crash in order_2_return_dict at +checkout confirmation.

+

This module prevents the OCA auto-assign from running on website orders +(website_id is set) so that website_sale remains the sole owner +of delivery-line management for e-commerce carts.

+

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

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • ADHOC SA
  • +
+
+
+

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/e-commerce project on GitHub.

+

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

+
+
+
+ + diff --git a/website_sale_carrier_auto_assign/tests/__init__.py b/website_sale_carrier_auto_assign/tests/__init__.py new file mode 100644 index 0000000000..90cb995175 --- /dev/null +++ b/website_sale_carrier_auto_assign/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import test_website_sale_carrier_auto_assign diff --git a/website_sale_carrier_auto_assign/tests/test_website_sale_carrier_auto_assign.py b/website_sale_carrier_auto_assign/tests/test_website_sale_carrier_auto_assign.py new file mode 100644 index 0000000000..315f09bd77 --- /dev/null +++ b/website_sale_carrier_auto_assign/tests/test_website_sale_carrier_auto_assign.py @@ -0,0 +1,92 @@ +# Copyright 2026 ADHOC SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests import tagged + +from odoo.addons.website_sale.tests.common import WebsiteSaleCommon + + +@tagged("post_install", "-at_install") +class TestWebsiteSaleCarrierAutoAssign(WebsiteSaleCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + + cls.env.company.carrier_on_create = True + + cls.partner_carrier = cls._prepare_carrier( + cls._create_product(name="Partner Default Carrier"), + name="Partner Default Carrier", + delivery_type="fixed", + fixed_price=1000.0, + ) + + cls.web_carrier = cls._prepare_carrier( + cls._create_product(name="Web Selected Carrier"), + name="Web Selected Carrier", + delivery_type="fixed", + fixed_price=0.0, + ) + + cls.partner.property_delivery_carrier_id = cls.partner_carrier + + def test_is_auto_set_carrier_disabled_for_website_order(self): + """_is_auto_set_carrier_on_create must return False for website orders + so the OCA auto-assign is never triggered during create/write.""" + order = self._create_so() + self.assertFalse( + order._is_auto_set_carrier_on_create(), + "OCA carrier auto-assign should be disabled for website orders.", + ) + + def test_is_auto_set_carrier_enabled_for_backend_order(self): + """For orders without website_id the OCA logic must still run normally. + + The order must have at least one non-service product: the OCA gate + checks `not is_all_service`, which is True for empty orders (vacuous + truth of `all([])`) and would mask the website_id difference. + """ + order = self.env["sale.order"].create( + { + "partner_id": self.partner.id, + "order_line": [ + ( + 0, + 0, + { + "product_id": self.product.id, + "product_uom_qty": 1, + }, + ) + ], + } + ) + self.assertTrue( + order._is_auto_set_carrier_on_create(), + "OCA carrier auto-assign should remain active for backend orders.", + ) + + def test_no_duplicate_delivery_line_after_set_delivery_method(self): + """Creating a website order with a product and then setting a delivery + method must result in exactly one delivery line (not two). + + Without the glue module, OCA's write override would add a line for + partner_carrier while _set_delivery_method adds one for web_carrier, + giving two lines and a singleton error in order_2_return_dict. + """ + order = self._create_so() + + order._set_delivery_method(self.web_carrier) + + delivery_lines = order.order_line.filtered("is_delivery") + self.assertEqual( + len(delivery_lines), + 1, + "There must be exactly one delivery line after _set_delivery_method. " + f"Got {len(delivery_lines)}: {delivery_lines.mapped('name')}", + ) + self.assertEqual( + delivery_lines.product_id, + self.web_carrier.product_id, + "The single delivery line must correspond to the web-selected carrier.", + )