diff --git a/stock_picking_partner_brand/README.rst b/stock_picking_partner_brand/README.rst new file mode 100644 index 000000000..3cb25da77 --- /dev/null +++ b/stock_picking_partner_brand/README.rst @@ -0,0 +1,191 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +================================ +Stock Picking Partner Brand Sync +================================ + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:21d109d64c9a3f8ac534b11caa8c03e70210a7617ec997d55d51c69b7b024887 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |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%2Fbrand-lightgray.png?logo=github + :target: https://github.com/OCA/brand/tree/19.0/stock_picking_partner_brand + :alt: OCA/brand +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/brand-19-0/brand-19-0-stock_picking_partner_brand + :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/brand&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module is a glue module between ``sale_brand`` and +``partner_brand``. + +If the customer has a brand assigned, it will set that brand on the +Sales Order. If the customer has no brand, or if the customer is cleared +from the SO, the brand on the SO will also be cleared. + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Use Cases / Context +=================== + +This module was developed because it is a common use case for +(sub)contractors and other types of businesses to sell products under +different brands. + +Installation +============ + +To install this module, you need to: + +1. Place the ``sale_brand`` module in your Odoo addons path. +2. Install the ``partner_brand`` module in Odoo. + +It will auto install if all the dependencies are available. + +Configuration +============= + +1. Configure Brands on Partners: Navigate to Contacts. Open the customer + records for which you want to assign a brand. + +2. On the customer form, locate the "Brand" field (provided by + partner_brand) and select the appropriate brand. + +3. Save the changes. + +Usage +===== + +Automatic Brand Setting +----------------------- + +When you are on a Stock Picking form: + +Selecting a Customer with a Brand: + +If you select a customer in the "Customer" (partner_id) field, and that +customer has a brand assigned to them (via the brand_id field on their +contact record), the "Brand" field on the Stock picking will +automatically be populated with the customer's brand. + +Selecting a Customer without a Brand: + +If you select a customer who does not have a brand assigned to them, the +"Brand" field on the Stock Picking will be automatically cleared (set to +empty/None). + +*Changing Customer*: + +If you change the customer on an existing picking, the brand on the +picking will update according to the newly selected customer's brand (or +be cleared if the new customer has no brand). + +Clearing Customer: + +If you clear the "Customer" field on the stock picking (remove the +selected customer), the "Brand" field on the stock picking will also be +automatically cleared. + +This behavior is triggered by an onchange mechanism on the partner_id +field of the stock.picking model. + +*Manual Override* The automatic setting of the brand based on the +customer is a default behavior. After the brand is automatically +populated (or cleared), you can still manually change or set the brand +on the Stock Picking directly if needed. The onchange mechanism only +triggers when the partner_id field itself is modified. + +Known issues / Roadmap +====================== + + + +Changelog +========= + +18.0.1.0.0 (2025-05-20) +----------------------- + +- [ADD] First Release of module. + +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 +------- + +* O.B.S. Solutions + +Contributors +------------ + +- bosd +- `Heliconia Solutions Pvt. Ltd. `__ + + - Bhavesh Heliconia + +Other credits +------------- + +The development of this module has been financially supported by: + +- OBS Solutions B.V. + +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. + +.. |maintainer-bosd| image:: https://github.com/bosd.png?size=40px + :target: https://github.com/bosd + :alt: bosd + +Current `maintainer `__: + +|maintainer-bosd| + +This module is part of the `OCA/brand `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/stock_picking_partner_brand/__init__.py b/stock_picking_partner_brand/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/stock_picking_partner_brand/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/stock_picking_partner_brand/__manifest__.py b/stock_picking_partner_brand/__manifest__.py new file mode 100644 index 000000000..d47eb09a7 --- /dev/null +++ b/stock_picking_partner_brand/__manifest__.py @@ -0,0 +1,30 @@ +# Copyright 2025 OBS Solutions B.V. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl-3.0) + +{ + "name": "Stock Picking Partner Brand Sync", + "summary": """ + Automatically sets the brand on a Stock Picking based on the + selected partner's brand. + """, + "version": "19.0.1.0.0", + "development_status": "Alpha", + "category": "Inventory", + "website": "https://github.com/OCA/brand", + "author": "O.B.S. Solutions, Odoo Community Association (OCA)", + "maintainers": ["bosd"], + "license": "AGPL-3", + "application": False, + "installable": True, + "auto_install": True, + "external_dependencies": { + "python": [], + "bin": [], + }, + "depends": [ + "stock", + "partner_brand", + "stock_brand", + ], + "data": [], +} diff --git a/stock_picking_partner_brand/i18n/it.po b/stock_picking_partner_brand/i18n/it.po new file mode 100644 index 000000000..d512c2de9 --- /dev/null +++ b/stock_picking_partner_brand/i18n/it.po @@ -0,0 +1,22 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_picking_partner_brand +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-09-26 18:43+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.10.4\n" + +#. module: stock_picking_partner_brand +#: model:ir.model,name:stock_picking_partner_brand.model_stock_picking +msgid "Transfer" +msgstr "Trasferimento" diff --git a/stock_picking_partner_brand/i18n/stock_picking_partner_brand.pot b/stock_picking_partner_brand/i18n/stock_picking_partner_brand.pot new file mode 100644 index 000000000..33b073547 --- /dev/null +++ b/stock_picking_partner_brand/i18n/stock_picking_partner_brand.pot @@ -0,0 +1,19 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_picking_partner_brand +# +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: stock_picking_partner_brand +#: model:ir.model,name:stock_picking_partner_brand.model_stock_picking +msgid "Transfer" +msgstr "" diff --git a/stock_picking_partner_brand/models/__init__.py b/stock_picking_partner_brand/models/__init__.py new file mode 100644 index 000000000..ae4c27227 --- /dev/null +++ b/stock_picking_partner_brand/models/__init__.py @@ -0,0 +1 @@ +from . import stock_picking diff --git a/stock_picking_partner_brand/models/stock_picking.py b/stock_picking_partner_brand/models/stock_picking.py new file mode 100644 index 000000000..d9f344afe --- /dev/null +++ b/stock_picking_partner_brand/models/stock_picking.py @@ -0,0 +1,117 @@ +import logging + +from odoo import api, models + +_logger = logging.getLogger(__name__) + + +class StockPicking(models.Model): + _inherit = "stock.picking" + + @api.onchange("partner_id") + def _onchange_partner_id_set_brand(self): + """ + Onchange method to set the brand_id on the Stock Picking + based on the selected partner's brand or its parent company's brand. + """ + brand_to_set = False + + if self.partner_id: + # Determine the partner whose brand should be checked + # Prefer the commercial entity (parent company) if it exists + partner_for_brand_check = ( + self.partner_id.commercial_partner_id or self.partner_id + ) # noqa + + if ( + hasattr(partner_for_brand_check, "brand_id") + and partner_for_brand_check.brand_id + ): # noqa + brand_to_set = partner_for_brand_check.brand_id + _logger.info( + f"Stock Picking (virtual ID " + "{self._origin.id if self._origin else 'New'}): " + f"Partner '{self.partner_id.name}' selected. " + f"Using brand from '{partner_for_brand_check.name}': " + "'{brand_to_set.name}' (ID: {brand_to_set.id})." + ) + else: + _logger.info( + f"Stock Picking (virtual ID " + "{self._origin.id if self._origin else 'New'}): " + f"Partner '{self.partner_id.name}' selected. " + f"Neither partner nor its commercial entity " + "'{partner_for_brand_check.name}' has a brand. Clearing brand." + ) + else: + _logger.info( + "Stock Picking (virtual ID " + "{self._origin.id if self._origin else 'New'}): " + "Partner cleared. Clearing brand." + ) + + # Set or clear the brand_id on the picking + if hasattr(self, "brand_id"): + self.brand_id = brand_to_set + else: + # This case should ideally not happen if stock_brand is correctly installed + if self.partner_id: + _logger.warning( + "Stock Picking model does not have a 'brand_id' field. " + "Is 'stock_brand' module installed and loaded correctly?" + ) + + @api.model_create_multi + def create(self, vals_list): + """ + Override create to ensure brand_id is set from partner_id + (or its commercial entity) + if provided during direct creation, and not already set in vals. + """ + for vals in vals_list: + # Only attempt to set brand_id if partner_id is provided and brand_id is not + # already in vals + if vals.get("partner_id") and "brand_id" not in vals: + partner = self.env["res.partner"].browse(vals.get("partner_id")) + if partner.exists(): + # Determine the partner whose brand should be checked + partner_for_brand_check = partner.commercial_partner_id or partner + + if ( + hasattr(partner_for_brand_check, "brand_id") + and partner_for_brand_check.brand_id + ): # noqa + # Check if the stock.picking model (self) has brand_id field + if "brand_id" in self._fields: + vals["brand_id"] = partner_for_brand_check.brand_id.id + _logger.info( + f"Create Stock Picking: Partner" + " '{partner.name}' provided. " + f"Using brand from '{partner_for_brand_check.name}': " + "'{partner_for_brand_check.brand_id.name}'. " + f"Setting brand_id in creation vals." + ) + else: + _logger.warning( + "Stock Picking model does not have a 'brand_id' " + "field during create. " + "Is 'stock_brand' module installed?" + ) + else: + _logger.info( + f"Create Stock Picking: Partner '{partner.name}' provided. " + f"Neither partner nor its commercial entity " + "'{partner_for_brand_check.name}' has a brand. " + f"brand_id not set from partner." + ) + + records = super().create(vals_list) + + # The onchange logic might also be triggered after creation if + # partner_id is set, + # but explicitly handling it here ensures it's + # set during the initial create call + # if possible. Re-triggering onchange or a direct write post-create is generally + # less efficient if it can be handled in the initial vals. + + return records diff --git a/stock_picking_partner_brand/pyproject.toml b/stock_picking_partner_brand/pyproject.toml new file mode 100644 index 000000000..4231d0ccc --- /dev/null +++ b/stock_picking_partner_brand/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/stock_picking_partner_brand/readme/CONFIGURE.md b/stock_picking_partner_brand/readme/CONFIGURE.md new file mode 100644 index 000000000..d3be9ccd1 --- /dev/null +++ b/stock_picking_partner_brand/readme/CONFIGURE.md @@ -0,0 +1,7 @@ +1. Configure Brands on Partners: +Navigate to Contacts. +Open the customer records for which you want to assign a brand. + +2. On the customer form, locate the "Brand" field (provided by partner_brand) and select the appropriate brand. + +3. Save the changes. diff --git a/stock_picking_partner_brand/readme/CONTEXT.md b/stock_picking_partner_brand/readme/CONTEXT.md new file mode 100644 index 000000000..447167025 --- /dev/null +++ b/stock_picking_partner_brand/readme/CONTEXT.md @@ -0,0 +1 @@ +This module was developed because it is a common use case for (sub)contractors and other types of businesses to sell products under different brands. diff --git a/stock_picking_partner_brand/readme/CONTRIBUTORS.md b/stock_picking_partner_brand/readme/CONTRIBUTORS.md new file mode 100644 index 000000000..29e910277 --- /dev/null +++ b/stock_picking_partner_brand/readme/CONTRIBUTORS.md @@ -0,0 +1,4 @@ + +- bosd \ +- [Heliconia Solutions Pvt. Ltd.](https://www.heliconia.io) + - Bhavesh Heliconia diff --git a/stock_picking_partner_brand/readme/CREDITS.md b/stock_picking_partner_brand/readme/CREDITS.md new file mode 100644 index 000000000..ded0986d1 --- /dev/null +++ b/stock_picking_partner_brand/readme/CREDITS.md @@ -0,0 +1,4 @@ + +The development of this module has been financially supported by: + +- OBS Solutions B.V. diff --git a/stock_picking_partner_brand/readme/DESCRIPTION.md b/stock_picking_partner_brand/readme/DESCRIPTION.md new file mode 100644 index 000000000..7e87ae55c --- /dev/null +++ b/stock_picking_partner_brand/readme/DESCRIPTION.md @@ -0,0 +1,3 @@ +This module is a glue module between `sale_brand` and `partner_brand`. + +If the customer has a brand assigned, it will set that brand on the Sales Order. If the customer has no brand, or if the customer is cleared from the SO, the brand on the SO will also be cleared. diff --git a/stock_picking_partner_brand/readme/HISTORY.md b/stock_picking_partner_brand/readme/HISTORY.md new file mode 100644 index 000000000..f2a3ae6af --- /dev/null +++ b/stock_picking_partner_brand/readme/HISTORY.md @@ -0,0 +1,3 @@ +## 18.0.1.0.0 (2025-05-20) + +- [ADD] First Release of module. diff --git a/stock_picking_partner_brand/readme/INSTALL.md b/stock_picking_partner_brand/readme/INSTALL.md new file mode 100644 index 000000000..d8a2a70f4 --- /dev/null +++ b/stock_picking_partner_brand/readme/INSTALL.md @@ -0,0 +1,7 @@ + +To install this module, you need to: + +1. Place the `sale_brand` module in your Odoo addons path. +2. Install the `partner_brand` module in Odoo. + +It will auto install if all the dependencies are available. diff --git a/stock_picking_partner_brand/readme/ROADMAP.md b/stock_picking_partner_brand/readme/ROADMAP.md new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/stock_picking_partner_brand/readme/ROADMAP.md @@ -0,0 +1 @@ + diff --git a/stock_picking_partner_brand/readme/USAGE.md b/stock_picking_partner_brand/readme/USAGE.md new file mode 100644 index 000000000..382a1692f --- /dev/null +++ b/stock_picking_partner_brand/readme/USAGE.md @@ -0,0 +1,23 @@ +## Automatic Brand Setting +When you are on a Stock Picking form: + +Selecting a Customer with a Brand: + +If you select a customer in the "Customer" (partner_id) field, and that customer has a brand assigned to them (via the brand_id field on their contact record), the "Brand" field on the Stock picking will automatically be populated with the customer's brand. + +Selecting a Customer without a Brand: + +If you select a customer who does not have a brand assigned to them, the "Brand" field on the Stock Picking will be automatically cleared (set to empty/None). + +*Changing Customer*: + +If you change the customer on an existing picking, the brand on the picking will update according to the newly selected customer's brand (or be cleared if the new customer has no brand). + +Clearing Customer: + +If you clear the "Customer" field on the stock picking (remove the selected customer), the "Brand" field on the stock picking will also be automatically cleared. + +This behavior is triggered by an onchange mechanism on the partner_id field of the stock.picking model. + +*Manual Override* +The automatic setting of the brand based on the customer is a default behavior. After the brand is automatically populated (or cleared), you can still manually change or set the brand on the Stock Picking directly if needed. The onchange mechanism only triggers when the partner_id field itself is modified. diff --git a/stock_picking_partner_brand/static/description/icon.png b/stock_picking_partner_brand/static/description/icon.png new file mode 100644 index 000000000..1dcc49c24 Binary files /dev/null and b/stock_picking_partner_brand/static/description/icon.png differ diff --git a/stock_picking_partner_brand/static/description/icon.svg b/stock_picking_partner_brand/static/description/icon.svg new file mode 100644 index 000000000..ed6aaa04e --- /dev/null +++ b/stock_picking_partner_brand/static/description/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/stock_picking_partner_brand/static/description/index.html b/stock_picking_partner_brand/static/description/index.html new file mode 100644 index 000000000..f98ab32a9 --- /dev/null +++ b/stock_picking_partner_brand/static/description/index.html @@ -0,0 +1,533 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Stock Picking Partner Brand Sync

+ +

Alpha License: AGPL-3 OCA/brand Translate me on Weblate Try me on Runboat

+

This module is a glue module between sale_brand and +partner_brand.

+

If the customer has a brand assigned, it will set that brand on the +Sales Order. If the customer has no brand, or if the customer is cleared +from the SO, the brand on the SO will also be cleared.

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Use Cases / Context

+

This module was developed because it is a common use case for +(sub)contractors and other types of businesses to sell products under +different brands.

+
+
+

Installation

+

To install this module, you need to:

+
    +
  1. Place the sale_brand module in your Odoo addons path.
  2. +
  3. Install the partner_brand module in Odoo.
  4. +
+

It will auto install if all the dependencies are available.

+
+
+

Configuration

+
    +
  1. Configure Brands on Partners: Navigate to Contacts. Open the customer +records for which you want to assign a brand.
  2. +
  3. On the customer form, locate the “Brand” field (provided by +partner_brand) and select the appropriate brand.
  4. +
  5. Save the changes.
  6. +
+
+
+

Usage

+
+

Automatic Brand Setting

+

When you are on a Stock Picking form:

+

Selecting a Customer with a Brand:

+

If you select a customer in the “Customer” (partner_id) field, and that +customer has a brand assigned to them (via the brand_id field on their +contact record), the “Brand” field on the Stock picking will +automatically be populated with the customer’s brand.

+

Selecting a Customer without a Brand:

+

If you select a customer who does not have a brand assigned to them, the +“Brand” field on the Stock Picking will be automatically cleared (set to +empty/None).

+

Changing Customer:

+

If you change the customer on an existing picking, the brand on the +picking will update according to the newly selected customer’s brand (or +be cleared if the new customer has no brand).

+

Clearing Customer:

+

If you clear the “Customer” field on the stock picking (remove the +selected customer), the “Brand” field on the stock picking will also be +automatically cleared.

+

This behavior is triggered by an onchange mechanism on the partner_id +field of the stock.picking model.

+

Manual Override The automatic setting of the brand based on the +customer is a default behavior. After the brand is automatically +populated (or cleared), you can still manually change or set the brand +on the Stock Picking directly if needed. The onchange mechanism only +triggers when the partner_id field itself is modified.

+
+
+ +
+

Changelog

+
+

18.0.1.0.0 (2025-05-20)

+
    +
  • [ADD] First Release of module.
  • +
+
+
+
+

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

+
    +
  • O.B.S. Solutions
  • +
+
+ +
+

Other credits

+

The development of this module has been financially supported by:

+
    +
  • OBS Solutions B.V.
  • +
+
+
+

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.

+

Current maintainer:

+

bosd

+

This module is part of the OCA/brand project on GitHub.

+

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

+
+
+
+
+ + diff --git a/stock_picking_partner_brand/tests/__init__.py b/stock_picking_partner_brand/tests/__init__.py new file mode 100644 index 000000000..e7ecb708e --- /dev/null +++ b/stock_picking_partner_brand/tests/__init__.py @@ -0,0 +1 @@ +from . import test_stock_picking_partner_brand diff --git a/stock_picking_partner_brand/tests/test_stock_picking_partner_brand.py b/stock_picking_partner_brand/tests/test_stock_picking_partner_brand.py new file mode 100644 index 000000000..01364283e --- /dev/null +++ b/stock_picking_partner_brand/tests/test_stock_picking_partner_brand.py @@ -0,0 +1,256 @@ +from odoo.tests import tagged + +from odoo.addons.base.tests.common import BaseCommon + + +@tagged("post_install", "-at_install", "stock_picking_partner_brand") +class TestStockPickingPartnerBrand(BaseCommon): + """ + Test cases for the stock_picking_partner_brand module. + Verifies that the brand_id on a Stock Picking is correctly updated + when the partner_id changes, based on the partner's or commercial partner's + assigned brand. + """ + + @classmethod + def setUpClass(cls): + super().setUpClass() + # Models + cls.ResPartner = cls.env["res.partner"] + cls.ResBrand = cls.env["res.brand"] + cls.StockPicking = cls.env["stock.picking"] + cls.StockPickingType = cls.env["stock.picking.type"] + cls.ProductProduct = cls.env["product.product"] + + # Create Brands + cls.brand_alpha = cls.ResBrand.create({"name": "Test Brand Alpha"}) + cls.brand_beta = cls.ResBrand.create({"name": "Test Brand Beta"}) + + # Create Partners + cls.parent_company_with_brand_alpha = cls.ResPartner.create( + { + "name": "Parent Co Alpha", + "is_company": True, + "brand_id": cls.brand_alpha.id, + } + ) + cls.child_contact_of_alpha = cls.ResPartner.create( + { + "name": "Child Contact of Alpha", + "parent_id": cls.parent_company_with_brand_alpha.id, + "type": "contact", + # This child contact intentionally does not have its own brand_id + } + ) + cls.standalone_partner_with_brand_beta = cls.ResPartner.create( + { + "name": "Standalone Customer Beta", + "is_company": True, + "brand_id": cls.brand_beta.id, + } + ) + cls.partner_without_brand = cls.ResPartner.create( + { + "name": "Customer Without Any Brand", + "is_company": True, + } + ) + cls.child_contact_no_parent_brand = cls.ResPartner.create( + { + "name": "Child Contact, Parent No Brand", + "parent_id": cls.partner_without_brand.id, + "type": "contact", + } + ) + + cls.picking_type_out = cls.env["stock.picking.type"].search( + [ + ("code", "=", "outgoing"), + ("warehouse_id.company_id", "=", cls.env.company.id), + ], + limit=1, + ) + if not cls.picking_type_out: + warehouse = cls.env["stock.warehouse"].search( + [("company_id", "=", cls.env.company.id)], limit=1 + ) + if not warehouse: + warehouse = cls.env["stock.warehouse"].create( + { + "name": "Test Warehouse", + "code": "TSTWH", + "company_id": cls.env.company.id, + } + ) + cls.picking_type_out = cls.StockPickingType.create( + { + "name": "Test Pickings Out", + "code": "outgoing", + "warehouse_id": warehouse.id, + "sequence_code": "TSTOUT", + } + ) + + cls.product_generic = cls.ProductProduct.create( + {"name": "Generic Product", "type": "consu"} + ) + + def _create_picking_form(self, partner_id=None): + """ + Helper to simulate opening a new stock picking form with an optional partner. + """ + vals = {"picking_type_id": self.picking_type_out.id} + if partner_id: + vals["partner_id"] = partner_id + return self.StockPicking.new(vals) + + def test_01_onchange_child_contact_gets_parent_brand(self): + """Test brand is set from parent company when a child contact is selected.""" + picking_form = self._create_picking_form( + partner_id=self.child_contact_of_alpha.id + ) + picking_form._onchange_partner_id_set_brand() + + self.assertTrue(picking_form.brand_id, "Brand should be set on the picking.") + self.assertEqual( + picking_form.brand_id, + self.parent_company_with_brand_alpha.brand_id, + "Picking brand should match the parent company's brand.", + ) + + def test_02_onchange_standalone_partner_with_brand(self): + """Test brand is set from a standalone partner who has a brand.""" + picking_form = self._create_picking_form( + partner_id=self.standalone_partner_with_brand_beta.id + ) + picking_form._onchange_partner_id_set_brand() + + self.assertTrue(picking_form.brand_id, "Brand should be set on the picking.") + self.assertEqual( + picking_form.brand_id, + self.standalone_partner_with_brand_beta.brand_id, + "Picking brand should match the standalone partner's brand.", + ) + + def test_03_onchange_partner_without_brand_and_no_parent_brand(self): + """Test brand is cleared if partner and its parent (if any) have no brand.""" + # First, test with a standalone partner without a brand + picking_form_no_brand_standalone = self._create_picking_form( + partner_id=self.partner_without_brand.id + ) + picking_form_no_brand_standalone._onchange_partner_id_set_brand() + self.assertFalse( + picking_form_no_brand_standalone.brand_id, + "Brand should be cleared for standalone partner without brand.", + ) + + # Second, test with a child contact whose parent has no brand + picking_form_no_brand_child = self._create_picking_form( + partner_id=self.child_contact_no_parent_brand.id + ) + picking_form_no_brand_child._onchange_partner_id_set_brand() + self.assertFalse( + picking_form_no_brand_child.brand_id, + "Brand should be cleared for child contact whose parent has no brand.", + ) + + def test_04_onchange_partner_cleared(self): + """Test brand is cleared when the partner is removed from the picking.""" + picking_form = self._create_picking_form( + partner_id=self.parent_company_with_brand_alpha.id + ) + picking_form._onchange_partner_id_set_brand() # Set initial brand + self.assertEqual(picking_form.brand_id, self.brand_alpha) + + # Clear the partner + picking_form.partner_id = False + picking_form._onchange_partner_id_set_brand() + + self.assertFalse( + picking_form.brand_id, "Brand should be cleared when partner is removed." + ) + + def test_05_create_picking_with_child_partner_gets_parent_brand(self): + """Test brand is set from parent during direct creation with a child partner.""" + picking = self.StockPicking.create( + { + "partner_id": self.child_contact_of_alpha.id, + "picking_type_id": self.picking_type_out.id, + "location_id": self.picking_type_out.default_location_src_id.id, + "location_dest_id": self.picking_type_out.default_location_dest_id.id, + } + ) + self.assertTrue(picking.brand_id, "Brand should be set on picking creation.") + self.assertEqual( + picking.brand_id, + self.parent_company_with_brand_alpha.brand_id, + "Picking brand should match parent company's brand on creation.", + ) + + def test_06_create_picking_with_standalone_partner_with_brand(self): + """Test brand is set during direct creation with a standalone partner having a " + "brand.""" + picking = self.StockPicking.create( + { + "partner_id": self.standalone_partner_with_brand_beta.id, + "picking_type_id": self.picking_type_out.id, + "location_id": self.picking_type_out.default_location_src_id.id, + "location_dest_id": self.picking_type_out.default_location_dest_id.id, + } + ) + self.assertTrue(picking.brand_id, "Brand should be set on picking creation.") + self.assertEqual( + picking.brand_id, + self.standalone_partner_with_brand_beta.brand_id, + "Picking brand should match standalone partner's brand on creation.", + ) + + def test_07_create_picking_with_partner_without_brand(self): + """ + Test brand is not set during direct creation if partner (and parent) + has no brand. + """ + picking = self.StockPicking.create( + { + "partner_id": self.partner_without_brand.id, + "picking_type_id": self.picking_type_out.id, + "location_id": self.picking_type_out.default_location_src_id.id, + "location_dest_id": self.picking_type_out.default_location_dest_id.id, + } + ) + self.assertFalse( + picking.brand_id, + "Brand should not be set if partner (and parent) has no brand.", + ) + + def test_08_create_picking_with_explicit_brand_overrides_partner_brand(self): + """Test that an explicitly provided brand_id in create vals is respected.""" + picking = self.StockPicking.create( + { + "partner_id": self.parent_company_with_brand_alpha.id, + "brand_id": self.brand_beta.id, + "picking_type_id": self.picking_type_out.id, + "location_id": self.picking_type_out.default_location_src_id.id, + "location_dest_id": self.picking_type_out.default_location_dest_id.id, + } + ) + self.assertTrue(picking.brand_id, "Brand should be set.") + self.assertEqual( + picking.brand_id, + self.brand_beta, + "Explicitly provided brand_id in create vals should override " + "partner's brand.", + ) + + def test_09_create_picking_without_partner(self): + """Test creating a picking without a partner_id.""" + picking = self.StockPicking.create( + { + "picking_type_id": self.picking_type_out.id, + "location_id": self.picking_type_out.default_location_src_id.id, + "location_dest_id": self.picking_type_out.default_location_dest_id.id, + } + ) + self.assertFalse( + picking.brand_id, "Brand should not be set if no partner is provided." + ) diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 000000000..ad5002c49 --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,4 @@ +odoo-addon-partner_brand @ git+https://github.com/OCA/brand.git@refs/pull/302/head#subdirectory=partner_brand +odoo-addon-account_brand @ git+https://github.com/OCA/brand.git@refs/pull/303/head#subdirectory=account_brand +odoo-addon-sale_brand @ git+https://github.com/OCA/brand.git@refs/pull/305/head#subdirectory=sale_brand +odoo-addon-stock_brand @ git+https://github.com/OCA/brand.git@refs/pull/308/head#subdirectory=stock_brand