diff --git a/product_main_seller/README.rst b/product_main_seller/README.rst index 22ed7f8f350..954efacaab1 100644 --- a/product_main_seller/README.rst +++ b/product_main_seller/README.rst @@ -1,7 +1,3 @@ -.. image:: https://odoo-community.org/readme-banner-image - :target: https://odoo-community.org/get-involved?utm_source=readme - :alt: Odoo Community Association - =================== Product Main Vendor =================== @@ -17,7 +13,7 @@ Product Main Vendor .. |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/license-AGPL--3-blue.png +.. |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%2Fpurchase--workflow-lightgray.png?logo=github diff --git a/product_main_seller/__manifest__.py b/product_main_seller/__manifest__.py index 76c768f1b89..f82ecbdeb12 100644 --- a/product_main_seller/__manifest__.py +++ b/product_main_seller/__manifest__.py @@ -13,6 +13,7 @@ "depends": ["purchase"], "maintainers": ["legalsylvain", "quentinDupont"], "data": [ + "data/ir_cron.xml", "views/view_product_product.xml", "views/view_product_template.xml", ], diff --git a/product_main_seller/data/ir_cron.xml b/product_main_seller/data/ir_cron.xml new file mode 100644 index 00000000000..e55c9c7ed1b --- /dev/null +++ b/product_main_seller/data/ir_cron.xml @@ -0,0 +1,12 @@ + + + + Recompute Main Vendor + + code + model._cron_recompute_main_seller_id() + 1 + days + True + + diff --git a/product_main_seller/hooks.py b/product_main_seller/hooks.py index 7643bd665b7..5fe251dd058 100644 --- a/product_main_seller/hooks.py +++ b/product_main_seller/hooks.py @@ -36,3 +36,48 @@ def pre_init_hook(env): WHERE pt.id = first_supplierinfos.product_tmpl_id; """ ) + cr.execute( + """ + ALTER TABLE product_product + ADD COLUMN IF NOT EXISTS main_seller_id integer; + """ + ) + cr.execute( + """ + WITH ranked_supplierinfos AS ( + SELECT + p.id AS product_id, + psi.partner_id, + ROW_NUMBER() OVER ( + PARTITION BY p.id + ORDER BY + CASE + WHEN psi.product_id = p.id THEN 0 + ELSE 1 + END, + psi.sequence, + psi.min_qty DESC, + psi.price, + psi.id + ) AS row_number + FROM product_product p + JOIN product_supplierinfo psi + ON psi.product_tmpl_id = p.product_tmpl_id + AND ( + psi.product_id = p.id + OR psi.product_id IS NULL + ) + JOIN res_partner rp + ON rp.id = psi.partner_id + AND rp.active + WHERE + (psi.date_start IS NULL OR psi.date_start <= CURRENT_DATE) + AND (psi.date_end IS NULL OR psi.date_end >= CURRENT_DATE) + ) + UPDATE product_product p + SET main_seller_id = ranked_supplierinfos.partner_id + FROM ranked_supplierinfos + WHERE ranked_supplierinfos.product_id = p.id + AND ranked_supplierinfos.row_number = 1; + """ + ) diff --git a/product_main_seller/models/__init__.py b/product_main_seller/models/__init__.py index e8fa8f6bf1e..9dfaeffa955 100644 --- a/product_main_seller/models/__init__.py +++ b/product_main_seller/models/__init__.py @@ -1 +1,3 @@ from . import product_template +from . import product_product +from . import product_supplierinfo diff --git a/product_main_seller/models/product_product.py b/product_main_seller/models/product_product.py new file mode 100644 index 00000000000..d385a613d5a --- /dev/null +++ b/product_main_seller/models/product_product.py @@ -0,0 +1,42 @@ +# Copyright 2026 Tecnativa - Andrii Kompaniiets +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class ProductProduct(models.Model): + _inherit = "product.product" + + main_seller_id = fields.Many2one( + comodel_name="res.partner", + string="Main Vendor", + help="Put your supplier info in first position to set as main vendor", + compute="_compute_main_seller_id", + store=True, + ) + + @api.depends( + "variant_seller_ids.sequence", + "variant_seller_ids.partner_id.active", + "variant_seller_ids.date_start", + "variant_seller_ids.date_end", + ) + def _compute_main_seller_id(self): + for product in self: + if product.variant_seller_ids: + product.main_seller_id = fields.first( + product.variant_seller_ids.filtered( + lambda seller, p=product: seller.partner_id.active + and ( + not seller.date_start + or seller.date_start <= fields.Date.today() + ) + and ( + not seller.date_end + or seller.date_end >= fields.Date.today() + ) + and (not seller.product_id or seller.product_id == p) + ) + ).partner_id + else: + product.main_seller_id = False diff --git a/product_main_seller/models/product_supplierinfo.py b/product_main_seller/models/product_supplierinfo.py new file mode 100644 index 00000000000..8af927a9b58 --- /dev/null +++ b/product_main_seller/models/product_supplierinfo.py @@ -0,0 +1,14 @@ +# Copyright 2026 Tecnativa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import api, models + + +class ProductSupplierinfo(models.Model): + _inherit = "product.supplierinfo" + + @api.model + def _cron_recompute_main_seller_id(self): + products = self.env["product.product"].search([]) + templates = self.env["product.template"].search([]) + products._compute_main_seller_id() + templates._compute_main_seller_id() diff --git a/product_main_seller/models/product_template.py b/product_main_seller/models/product_template.py index 1d131dd8fa1..fa51ebea2b8 100644 --- a/product_main_seller/models/product_template.py +++ b/product_main_seller/models/product_template.py @@ -16,13 +16,26 @@ class ProductTemplate(models.Model): store=True, ) - @api.depends("variant_seller_ids.sequence", "variant_seller_ids.partner_id.active") + @api.depends( + "variant_seller_ids.sequence", + "variant_seller_ids.partner_id.active", + "variant_seller_ids.date_start", + "variant_seller_ids.date_end", + ) def _compute_main_seller_id(self): for template in self: if template.variant_seller_ids: template.main_seller_id = fields.first( template.variant_seller_ids.filtered( lambda seller: seller.partner_id.active + and ( + not seller.date_start + or seller.date_start <= fields.Date.today() + ) + and ( + not seller.date_end + or seller.date_end >= fields.Date.today() + ) ) ).partner_id else: diff --git a/product_main_seller/static/description/index.html b/product_main_seller/static/description/index.html index 8c4e691b586..8ee7d63a4e9 100644 --- a/product_main_seller/static/description/index.html +++ b/product_main_seller/static/description/index.html @@ -3,7 +3,7 @@ -README.rst +Product Main Vendor -
+
+

Product Main Vendor

- - -Odoo Community Association - -
-

Product Main Vendor

-

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

+

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

This module extends the Odoo Product module to compute and display the main Vendor of each products. The main vendor is the first vendor in the vendors list.

@@ -392,7 +387,7 @@

Product Main Vendor

-

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 @@ -400,21 +395,21 @@

Bug Tracker

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

-
diff --git a/product_main_seller/views/view_product_product.xml b/product_main_seller/views/view_product_product.xml index 30ee16d09a9..c62dc0fb109 100644 --- a/product_main_seller/views/view_product_product.xml +++ b/product_main_seller/views/view_product_product.xml @@ -1,7 +1,6 @@