diff --git a/product_assortment/README.rst b/product_assortment/README.rst new file mode 100644 index 00000000000..29ace043841 --- /dev/null +++ b/product_assortment/README.rst @@ -0,0 +1,145 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +================== +Product Assortment +================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:eefbb6b1be1750f8aaa8a3d63821b46f0cab7396b595f9e10ce4d2c30e7e18f0 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png + :target: https://odoo-community.org/page/development-status + :alt: Production/Stable +.. |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%2Fproduct--attribute-lightgray.png?logo=github + :target: https://github.com/OCA/product-attribute/tree/19.0/product_assortment + :alt: OCA/product-attribute +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/product-attribute-19-0/product-attribute-19-0-product_assortment + :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/product-attribute&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This addon intends to manage product assortment. In Odoo you can only +define some filters defined by a domain but it can be sometimes really +complicated. With this addon you will be able to define a domain but +also add some products to include or to exclude through a allowed list +and a restricted list. This is done by overriding ir.capability but +without influencing its standard behaviour. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To use this module, you need to: + +1. Enter the menu through the Product Assortment Icon +2. Create a new filter where you can define your domain and add allowed + and restricted products + +Changelog +========= + +10.0.1.0.0 (2018-08-27) +----------------------- + +- [10.0][ADD] productassortment + +.. _section-1: + +12.0.1.0.0 (2019-06-03) +----------------------- + +- [12.0][MIG] productassortment + +.. _section-2: + +14.0.1.0.0 (2019-06-03) +----------------------- + +- [14.0][MIG] productassortment + +.. _section-3: + +16.0.1.0.0 (2022-09-15) +----------------------- + +- [16.0][MIG] product_assortment + +.. _section-4: + +18.0.1.0.0 (2025-03-06) +----------------------- + +- [18.0][MIG] product_assortment +- Forward port demo data +- Forward port Only Show assortments to managers +- Forward port Fix All assortments are applied to original partner when + partner is duplicated +- Adjust test code to new API behavior, for info: odoo/odoo@450f5c9 +- added test for combined black list and whitelisted product +- Fix: Navigating to the product assortment using the smartbutton on + the partner does not show all applicable assortments. (The + assortments with the partner defined as a domain where missing.) + +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 +------- + +* ACSONE SA/NV + +Contributors +------------ + +- Denis Roussel +- Cédric Pigeon +- Xavier Bouquiaux +- `Tecnativa `__: + + - Carlos Roca + - Sergio Teruel + +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/product-attribute `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/product_assortment/__init__.py b/product_assortment/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/product_assortment/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/product_assortment/__manifest__.py b/product_assortment/__manifest__.py new file mode 100644 index 00000000000..e5c6bab5775 --- /dev/null +++ b/product_assortment/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright 2021 ACSONE SA/NV +# Copyright 2023 Tecnativa - Carlos Dauden +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Product Assortment", + "summary": """ + Adds the ability to manage products assortment""", + "version": "19.0.1.0.0", + "license": "AGPL-3", + "development_status": "Production/Stable", + "author": "ACSONE SA/NV,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/product-attribute", + "depends": ["base", "product"], + "data": [ + "data/ir_cron.xml", + "security/product_assortment_security.xml", + "views/product_assortment.xml", + "views/res_partner_view.xml", + ], + "demo": ["demo/assortments.xml"], + "installable": True, +} diff --git a/product_assortment/data/ir_cron.xml b/product_assortment/data/ir_cron.xml new file mode 100644 index 00000000000..f48bdf08430 --- /dev/null +++ b/product_assortment/data/ir_cron.xml @@ -0,0 +1,30 @@ + + + + + Product assortment recompute all partners + + + code + model.search([])._compute_all_partner_ids() + + + + + + 1 + days + + + + diff --git a/product_assortment/demo/assortments.xml b/product_assortment/demo/assortments.xml new file mode 100644 index 00000000000..f1f02a14419 --- /dev/null +++ b/product_assortment/demo/assortments.xml @@ -0,0 +1,40 @@ + + + + + + product.product + + Assortment Desk + + ["|","|",("default_code","ilike","desk"),("name","ilike","desk"),("barcode","ilike","desk")] + + + + product.product + + Assortment Chair + + ["|","|",("default_code","ilike","chair"),("name","ilike","chair"),("barcode","ilike","chair")] + + + + product.product + + Assortment Service + + + + + diff --git a/product_assortment/i18n/ca.po b/product_assortment/i18n/ca.po new file mode 100644 index 00000000000..375f25f8d9a --- /dev/null +++ b/product_assortment/i18n/ca.po @@ -0,0 +1,197 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_assortment +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2022-03-22 13:17+0000\n" +"Last-Translator: Noel estudillo \n" +"Language-Team: none\n" +"Language: ca\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 4.3.2\n" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid ">" +msgstr "" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "" +"\n" +" Product\n" +" " +msgstr "" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.view_partner_form +msgid "Product Assortments" +msgstr "Assortiment de producte" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__all_partner_ids +msgid "All Partner" +msgstr "Tots socis" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Allowed product domain" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__whitelist_product_ids +msgid "Allowed products" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_res_partner__applied_assortment_ids +#: model:ir.model.fields,field_description:product_assortment.field_res_users__applied_assortment_ids +msgid "Applied Assortment" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__apply_black_list_product_domain +msgid "Apply restricted product domain" +msgstr "" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Archived" +msgstr "" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_search +msgid "Assortment Name" +msgstr "Nom de l'assortiment" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_search +msgid "Assortments" +msgstr "Assortiments" + +#. module: product_assortment +#: model:ir.model,name:product_assortment.model_res_partner +msgid "Contact" +msgstr "Contacte" + +#. module: product_assortment +#: model:ir.model,name:product_assortment.model_ir_filters +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Filters" +msgstr "Filtres" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__is_assortment +msgid "Is Assortment" +msgstr "És Assortiment" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__partner_ids +msgid "Partner" +msgstr "Soci" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__partner_domain +msgid "Partner Domain" +msgstr "Domini del soci" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Partner domain" +msgstr "" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Partners to apply" +msgstr "Socis a aplicar" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_product_view_list +msgid "Product" +msgstr "Producte" + +#. module: product_assortment +#: model:ir.ui.menu,name:product_assortment.menu_product_assortments +msgid "Product Assortment" +msgstr "Assortiment de producte" + +#. module: product_assortment +#: model:ir.actions.act_window,name:product_assortment.actions_product_assortment_view +msgid "Product assortment" +msgstr "Assortiment de producte" + +#. module: product_assortment +#: model:ir.actions.server,name:product_assortment.action_product_assortment_recompute_all_partners +#: model:ir.cron,cron_name:product_assortment.ir_cron_product_assortment_recompute_all_partners +msgid "Product assortment recompute all partbers" +msgstr "" + +#. module: product_assortment +#. odoo-python +#: code:addons/product_assortment/models/ir_filters.py:0 +#, python-format +msgid "Products" +msgstr "Productes" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Products to exclude" +msgstr "Productes a excloure" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Products to include" +msgstr "Productes a incloure" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__record_count +msgid "Record Count" +msgstr "Recompte de registres" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__black_list_product_domain +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Restricted product domain" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__blacklist_product_ids +msgid "Restricted products" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,help:product_assortment.field_ir_filters__partner_ids +msgid "This field allow to relate a partner to a domain of products" +msgstr "Aquest camp permet relacionar un soci amb un domini de productes" + +#~ msgid "Products" +#~ msgstr "Productes" + +#~ msgid "Blacklist Product" +#~ msgstr "Producte de la llista negra" + +#~ msgid "Display Name" +#~ msgstr "Nom de visualització" + +#~ msgid "ID" +#~ msgstr "ID" + +#~ msgid "Last Modified on" +#~ msgstr "Última modificació el" + +#~ msgid "Whitelist Product" +#~ msgstr "Producte de la llista blanca" + +#~ msgid "Model" +#~ msgstr "Model" diff --git a/product_assortment/i18n/de.po b/product_assortment/i18n/de.po new file mode 100644 index 00000000000..4cdb4bd86df --- /dev/null +++ b/product_assortment/i18n/de.po @@ -0,0 +1,183 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_assortment +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2022-11-04 14:44+0000\n" +"Last-Translator: Maria Sparenberg \n" +"Language-Team: none\n" +"Language: de\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 4.14.1\n" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid ">" +msgstr ">" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "" +"\n" +" Product\n" +" " +msgstr "" +"\n" +" Produkt\n" +" " + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.view_partner_form +msgid "Product Assortments" +msgstr "Produkt-Sortiment" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__all_partner_ids +msgid "All Partner" +msgstr "Alle Partner" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Allowed product domain" +msgstr "Erlaubte Produkt-Domäne" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__whitelist_product_ids +msgid "Allowed products" +msgstr "Erlaubte Produkte" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_res_partner__applied_assortment_ids +#: model:ir.model.fields,field_description:product_assortment.field_res_users__applied_assortment_ids +msgid "Applied Assortment" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__apply_black_list_product_domain +msgid "Apply restricted product domain" +msgstr "Domäne zum Beschränken der Produkte anwenden" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Archived" +msgstr "Archiviert" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_search +msgid "Assortment Name" +msgstr "Sortimentsname" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_search +msgid "Assortments" +msgstr "Sortimente" + +#. module: product_assortment +#: model:ir.model,name:product_assortment.model_res_partner +msgid "Contact" +msgstr "Kontakt" + +#. module: product_assortment +#: model:ir.model,name:product_assortment.model_ir_filters +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Filters" +msgstr "Filter" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__is_assortment +msgid "Is Assortment" +msgstr "Ist ein Sortiment" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__partner_ids +msgid "Partner" +msgstr "Partner" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__partner_domain +msgid "Partner Domain" +msgstr "Partner-Domäne" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Partner domain" +msgstr "Partner-Domäne" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Partners to apply" +msgstr "" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_product_view_list +msgid "Product" +msgstr "Produkt" + +#. module: product_assortment +#: model:ir.ui.menu,name:product_assortment.menu_product_assortments +msgid "Product Assortment" +msgstr "Produkt-Sortiment" + +#. module: product_assortment +#: model:ir.actions.act_window,name:product_assortment.actions_product_assortment_view +msgid "Product assortment" +msgstr "Produkt-Sortiment" + +#. module: product_assortment +#: model:ir.actions.server,name:product_assortment.action_product_assortment_recompute_all_partners +#: model:ir.cron,cron_name:product_assortment.ir_cron_product_assortment_recompute_all_partners +msgid "Product assortment recompute all partbers" +msgstr "" + +#. module: product_assortment +#. odoo-python +#: code:addons/product_assortment/models/ir_filters.py:0 +#, python-format +msgid "Products" +msgstr "Produkte" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Products to exclude" +msgstr "Auszuschließende Produkte" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Products to include" +msgstr "Einzuschließende Produkte" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__record_count +msgid "Record Count" +msgstr "Anzahl der Objekte" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__black_list_product_domain +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Restricted product domain" +msgstr "Domäne für Produkt-Einschränkung" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__blacklist_product_ids +msgid "Restricted products" +msgstr "Eingeschränkte Produkte" + +#. module: product_assortment +#: model:ir.model.fields,help:product_assortment.field_ir_filters__partner_ids +msgid "This field allow to relate a partner to a domain of products" +msgstr "" diff --git a/product_assortment/i18n/es.po b/product_assortment/i18n/es.po new file mode 100644 index 00000000000..e80bf29645f --- /dev/null +++ b/product_assortment/i18n/es.po @@ -0,0 +1,190 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_assortment +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-03-06 11:12+0000\n" +"PO-Revision-Date: 2023-03-06 12:14+0100\n" +"Last-Translator: Carlos Dauden \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid ">" +msgstr ">" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "" +"\n" +" Product\n" +" " +msgstr "" +"\n" +" Producto\n" +" " + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.view_partner_form +msgid "Product Assortments" +msgstr "Surtido de productos" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__all_partner_ids +msgid "All Partner" +msgstr "Todos los contactos" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Allowed product domain" +msgstr "Dominio de productos permitidos" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__whitelist_product_ids +msgid "Allowed products" +msgstr "Productos permitidos" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_res_partner__applied_assortment_ids +#: model:ir.model.fields,field_description:product_assortment.field_res_users__applied_assortment_ids +msgid "Applied Assortment" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__apply_black_list_product_domain +msgid "Apply restricted product domain" +msgstr "Aplicar dominio de productos restringidos" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Archived" +msgstr "Archivado" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_search +msgid "Assortment Name" +msgstr "Nombre del surtido" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_search +msgid "Assortments" +msgstr "Surtidos" + +#. module: product_assortment +#: model:ir.model,name:product_assortment.model_res_partner +msgid "Contact" +msgstr "Contacto" + +#. module: product_assortment +#: model:ir.model,name:product_assortment.model_ir_filters +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Filters" +msgstr "Filtros" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__is_assortment +msgid "Is Assortment" +msgstr "Es surtido" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__partner_ids +msgid "Partner" +msgstr "Contacto" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__partner_domain +msgid "Partner Domain" +msgstr "Dominio de contactos" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Partner domain" +msgstr "Dominio de contactos" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Partners to apply" +msgstr "Contactos a los que aplica" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_product_view_tree +msgid "Product" +msgstr "Producto" + +#. module: product_assortment +#: model:ir.ui.menu,name:product_assortment.menu_product_assortments +msgid "Product Assortment" +msgstr "Surtido de producto" + +#. module: product_assortment +#: model:ir.actions.act_window,name:product_assortment.actions_product_assortment_view +msgid "Product assortment" +msgstr "Surtido de producto" + +#. module: product_assortment +#: model:ir.actions.server,name:product_assortment.action_product_assortment_recompute_all_partners +#: model:ir.cron,cron_name:product_assortment.ir_cron_product_assortment_recompute_all_partners +msgid "Product assortment recompute all partbers" +msgstr "" + +#. module: product_assortment +#. odoo-python +#: code:addons/product_assortment/models/ir_filters.py:0 +#, python-format +msgid "Products" +msgstr "Productos" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Products to exclude" +msgstr "Productos a excluir" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Products to include" +msgstr "Productos a incluir" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__record_count +msgid "Record Count" +msgstr "Número de registros" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__black_list_product_domain +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Restricted product domain" +msgstr "Dominio de productos restringidos" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__blacklist_product_ids +msgid "Restricted products" +msgstr "Productos restringidos" + +#. module: product_assortment +#: model:ir.model.fields,help:product_assortment.field_ir_filters__partner_ids +msgid "This field allow to relate a partner to a domain of products" +msgstr "Este campo permite relacionar un contacto con un dominio de productos" + +#~ msgid "Product assortment reset cache" +#~ msgstr "Resetear cache de surtido de producto" + +#~ msgid "Products" +#~ msgstr "Productos" diff --git a/product_assortment/i18n/fr.po b/product_assortment/i18n/fr.po new file mode 100644 index 00000000000..7d3686c522e --- /dev/null +++ b/product_assortment/i18n/fr.po @@ -0,0 +1,182 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_assortment +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-11-07 15:06+0000\n" +"Last-Translator: samibc2c \n" +"Language-Team: none\n" +"Language: fr\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.6.2\n" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid ">" +msgstr "" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "" +"\n" +" Product\n" +" " +msgstr "" +"\n" +" Produit\n" +" " + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.view_partner_form +msgid "Product Assortments" +msgstr "Assortiments de Produits" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__all_partner_ids +msgid "All Partner" +msgstr "Tous les partenaires" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Allowed product domain" +msgstr "Domaine de produit autorisé" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__whitelist_product_ids +msgid "Allowed products" +msgstr "Produits autorisés" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_res_partner__applied_assortment_ids +#: model:ir.model.fields,field_description:product_assortment.field_res_users__applied_assortment_ids +msgid "Applied Assortment" +msgstr "Assortiment appliqué" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__apply_black_list_product_domain +msgid "Apply restricted product domain" +msgstr "Appliquer un domaine de produit restreint" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Archived" +msgstr "Archivé" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_search +msgid "Assortment Name" +msgstr "Nome de l'assortiment" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_search +msgid "Assortments" +msgstr "Assortiments" + +#. module: product_assortment +#: model:ir.model,name:product_assortment.model_res_partner +msgid "Contact" +msgstr "Contact" + +#. module: product_assortment +#: model:ir.model,name:product_assortment.model_ir_filters +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Filters" +msgstr "Filtres" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__is_assortment +msgid "Is Assortment" +msgstr "Est un assortiment" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__partner_ids +msgid "Partner" +msgstr "Partenaire" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__partner_domain +msgid "Partner Domain" +msgstr "Domaine du partenaire" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Partner domain" +msgstr "Domaine du partenaire" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Partners to apply" +msgstr "Appliquer aux partenaires" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_product_view_list +msgid "Product" +msgstr "Produit" + +#. module: product_assortment +#: model:ir.ui.menu,name:product_assortment.menu_product_assortments +msgid "Product Assortment" +msgstr "Assortiment de produits" + +#. module: product_assortment +#: model:ir.actions.act_window,name:product_assortment.actions_product_assortment_view +msgid "Product assortment" +msgstr "Assortiement de produits" + +#. module: product_assortment +#: model:ir.actions.server,name:product_assortment.action_product_assortment_recompute_all_partners +#: model:ir.cron,cron_name:product_assortment.ir_cron_product_assortment_recompute_all_partners +msgid "Product assortment recompute all partbers" +msgstr "" + +#. module: product_assortment +#. odoo-python +#: code:addons/product_assortment/models/ir_filters.py:0 +#, python-format +msgid "Products" +msgstr "Produits" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Products to exclude" +msgstr "Produits à exclure" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Products to include" +msgstr "Produits à inclure" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__record_count +msgid "Record Count" +msgstr "Nombre d'enregistrements" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__black_list_product_domain +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Restricted product domain" +msgstr "Domaine de produits restreint" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__blacklist_product_ids +msgid "Restricted products" +msgstr "Produits restreints" + +#. module: product_assortment +#: model:ir.model.fields,help:product_assortment.field_ir_filters__partner_ids +msgid "This field allow to relate a partner to a domain of products" +msgstr "Ce champ permet de relier un partenaire à un domaine de produits" diff --git a/product_assortment/i18n/it.po b/product_assortment/i18n/it.po new file mode 100644 index 00000000000..5af4acf6e87 --- /dev/null +++ b/product_assortment/i18n/it.po @@ -0,0 +1,202 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_assortment +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-07-15 09:58+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.6.2\n" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid ">" +msgstr ">" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "" +"\n" +" Product\n" +" " +msgstr "" +"\n" +" Prodotto\n" +" " + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.view_partner_form +msgid "Product Assortments" +msgstr "Assortimento dei prodotti " + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__all_partner_ids +msgid "All Partner" +msgstr "Tutti i partner" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Allowed product domain" +msgstr "Dominio prodotto consentito" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__whitelist_product_ids +msgid "Allowed products" +msgstr "Prodotti consentiti" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_res_partner__applied_assortment_ids +#: model:ir.model.fields,field_description:product_assortment.field_res_users__applied_assortment_ids +msgid "Applied Assortment" +msgstr "Assortimento applicato" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__apply_black_list_product_domain +msgid "Apply restricted product domain" +msgstr "Applica dominio prodotti limitati" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Archived" +msgstr "In archivio" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_search +msgid "Assortment Name" +msgstr "Nome assortimento" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_search +msgid "Assortments" +msgstr "Assortimenti" + +#. module: product_assortment +#: model:ir.model,name:product_assortment.model_res_partner +msgid "Contact" +msgstr "Contatto" + +#. module: product_assortment +#: model:ir.model,name:product_assortment.model_ir_filters +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Filters" +msgstr "Filtri" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__is_assortment +msgid "Is Assortment" +msgstr "È assortimento" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__partner_ids +msgid "Partner" +msgstr "Partner" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__partner_domain +msgid "Partner Domain" +msgstr "Dominio partner" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Partner domain" +msgstr "Dominio partner" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Partners to apply" +msgstr "Partner da applicare" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_product_view_list +msgid "Product" +msgstr "Prodotto" + +#. module: product_assortment +#: model:ir.ui.menu,name:product_assortment.menu_product_assortments +msgid "Product Assortment" +msgstr "Assortimento prodotto" + +#. module: product_assortment +#: model:ir.actions.act_window,name:product_assortment.actions_product_assortment_view +msgid "Product assortment" +msgstr "Assortimento prodotto" + +#. module: product_assortment +#: model:ir.actions.server,name:product_assortment.action_product_assortment_recompute_all_partners +#: model:ir.cron,cron_name:product_assortment.ir_cron_product_assortment_recompute_all_partners +msgid "Product assortment recompute all partbers" +msgstr "Ricalcolo assortimento prodotto per tutti i partner" + +#. module: product_assortment +#. odoo-python +#: code:addons/product_assortment/models/ir_filters.py:0 +#, python-format +msgid "Products" +msgstr "Prodotti" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Products to exclude" +msgstr "Prodotti da escludere" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Products to include" +msgstr "Prodotti da includere" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__record_count +msgid "Record Count" +msgstr "Conteggio record" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__black_list_product_domain +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Restricted product domain" +msgstr "Dominio prodotto limitato" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__blacklist_product_ids +msgid "Restricted products" +msgstr "Prodotti limitati" + +#. module: product_assortment +#: model:ir.model.fields,help:product_assortment.field_ir_filters__partner_ids +msgid "This field allow to relate a partner to a domain of products" +msgstr "" +"Questo campo consente di relazionare un partner con un dominio di prodotti" + +#~ msgid "Products" +#~ msgstr "Prodotti" + +#~ msgid "Blacklist Product" +#~ msgstr "Prodotti da escludere" + +#~ msgid "Display Name" +#~ msgstr "Nome visualizzato" + +#~ msgid "ID" +#~ msgstr "ID" + +#~ msgid "Last Modified on" +#~ msgstr "Ultima modifica il" + +#~ msgid "Whitelist Product" +#~ msgstr "Prodotti da includere" diff --git a/product_assortment/i18n/nl.po b/product_assortment/i18n/nl.po new file mode 100644 index 00000000000..a82eed0ea15 --- /dev/null +++ b/product_assortment/i18n/nl.po @@ -0,0 +1,184 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_assortment +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-03-08 13:06+0000\n" +"Last-Translator: Bosd \n" +"Language-Team: none\n" +"Language: nl\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.2\n" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid ">" +msgstr ">" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "" +"\n" +" Product\n" +" " +msgstr "" +"\n" +" Product\n" +" " + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.view_partner_form +msgid "Product Assortments" +msgstr "Productassortimenten" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__all_partner_ids +msgid "All Partner" +msgstr "Alle partners" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Allowed product domain" +msgstr "Toegestaan productdomein" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__whitelist_product_ids +msgid "Allowed products" +msgstr "Toegestane producten" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_res_partner__applied_assortment_ids +#: model:ir.model.fields,field_description:product_assortment.field_res_users__applied_assortment_ids +msgid "Applied Assortment" +msgstr "Toegepast assortiment" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__apply_black_list_product_domain +msgid "Apply restricted product domain" +msgstr "Beperkt productdomein toepassen" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Archived" +msgstr "Gearchiveerd" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_search +msgid "Assortment Name" +msgstr "Assortimentnaam" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_search +msgid "Assortments" +msgstr "Assortimenten" + +#. module: product_assortment +#: model:ir.model,name:product_assortment.model_res_partner +msgid "Contact" +msgstr "Contactpersoon" + +#. module: product_assortment +#: model:ir.model,name:product_assortment.model_ir_filters +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Filters" +msgstr "Filters" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__is_assortment +msgid "Is Assortment" +msgstr "Is assortiment" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__partner_ids +msgid "Partner" +msgstr "Partner" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__partner_domain +msgid "Partner Domain" +msgstr "Partnerdomein" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Partner domain" +msgstr "Partnerdomein" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Partners to apply" +msgstr "Partners om toe te passen" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_product_view_list +msgid "Product" +msgstr "Product" + +#. module: product_assortment +#: model:ir.ui.menu,name:product_assortment.menu_product_assortments +msgid "Product Assortment" +msgstr "Productassortiment" + +#. module: product_assortment +#: model:ir.actions.act_window,name:product_assortment.actions_product_assortment_view +msgid "Product assortment" +msgstr "Productassortiment" + +#. module: product_assortment +#: model:ir.actions.server,name:product_assortment.action_product_assortment_recompute_all_partners +#: model:ir.cron,cron_name:product_assortment.ir_cron_product_assortment_recompute_all_partners +msgid "Product assortment recompute all partbers" +msgstr "Productassortiment herbereken alle partners" + +#. module: product_assortment +#. odoo-python +#: code:addons/product_assortment/models/ir_filters.py:0 +#, python-format +msgid "Products" +msgstr "Producten" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Products to exclude" +msgstr "Producten om uit te sluiten" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Products to include" +msgstr "Producten om op te nemen" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__record_count +msgid "Record Count" +msgstr "Aantal records" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__black_list_product_domain +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Restricted product domain" +msgstr "Beperkt productdomein" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__blacklist_product_ids +msgid "Restricted products" +msgstr "Beperkte producten" + +#. module: product_assortment +#: model:ir.model.fields,help:product_assortment.field_ir_filters__partner_ids +msgid "This field allow to relate a partner to a domain of products" +msgstr "" +"Dit veld maakt het mogelijk om een partner te koppelen aan een domein van " +"producten" diff --git a/product_assortment/i18n/product_assortment.pot b/product_assortment/i18n/product_assortment.pot new file mode 100644 index 00000000000..bffde895b67 --- /dev/null +++ b/product_assortment/i18n/product_assortment.pot @@ -0,0 +1,175 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_assortment +# +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: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.view_partner_form +msgid "Product Assortments" +msgstr "" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "" +"\n" +" Products\n" +" \n" +" \n" +" Product\n" +" " +msgstr "" + +#. module: product_assortment +#: model:res.groups,comment:product_assortment.group_product_assortment_manager +msgid "Access to all product assortment" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__all_partner_ids +msgid "All Partner" +msgstr "" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Allowed product domain" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__whitelist_product_ids +msgid "Allowed products" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_res_partner__applied_assortment_ids +#: model:ir.model.fields,field_description:product_assortment.field_res_users__applied_assortment_ids +msgid "Applied Assortment" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__apply_black_list_product_domain +msgid "Apply restricted product domain" +msgstr "" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Archived" +msgstr "" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_search +msgid "Assortment Name" +msgstr "" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_search +msgid "Assortments" +msgstr "" + +#. module: product_assortment +#: model:ir.model,name:product_assortment.model_res_partner +msgid "Contact" +msgstr "" + +#. module: product_assortment +#: model:ir.model,name:product_assortment.model_ir_filters +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Filters" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__is_assortment +msgid "Is Assortment" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__partner_ids +msgid "Partner" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__partner_domain +msgid "Partner Domain" +msgstr "" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Partner domain" +msgstr "" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Partners to apply" +msgstr "" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_product_view_list +msgid "Product" +msgstr "" + +#. module: product_assortment +#: model:ir.ui.menu,name:product_assortment.menu_product_assortments +msgid "Product Assortment" +msgstr "" + +#. module: product_assortment +#: model:res.groups,name:product_assortment.group_product_assortment_manager +msgid "Product Assortment Manager" +msgstr "" + +#. module: product_assortment +#: model:ir.actions.act_window,name:product_assortment.actions_product_assortment_view +msgid "Product assortment" +msgstr "" + +#. module: product_assortment +#: model:ir.actions.server,name:product_assortment.action_product_assortment_recompute_all_partners +msgid "Product assortment recompute all partners" +msgstr "" + +#. module: product_assortment +#. odoo-python +#: code:addons/product_assortment/models/ir_filters.py:0 +msgid "Products" +msgstr "" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Products to exclude" +msgstr "" + +#. module: product_assortment +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Products to include" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__record_count +msgid "Record Count" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__black_list_product_domain +#: model_terms:ir.ui.view,arch_db:product_assortment.product_assortment_view_form +msgid "Restricted product domain" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,field_description:product_assortment.field_ir_filters__blacklist_product_ids +msgid "Restricted products" +msgstr "" + +#. module: product_assortment +#: model:ir.model.fields,help:product_assortment.field_ir_filters__partner_ids +msgid "This field allow to relate a partner to a domain of products" +msgstr "" diff --git a/product_assortment/models/__init__.py b/product_assortment/models/__init__.py new file mode 100644 index 00000000000..40f9898560a --- /dev/null +++ b/product_assortment/models/__init__.py @@ -0,0 +1,3 @@ +from . import ir_filters +from . import ir_rule +from . import res_partner diff --git a/product_assortment/models/ir_filters.py b/product_assortment/models/ir_filters.py new file mode 100644 index 00000000000..315571ffb9b --- /dev/null +++ b/product_assortment/models/ir_filters.py @@ -0,0 +1,174 @@ +# Copyright 2021 ACSONE SA/NV +# Copyright 2023 Tecnativa - Carlos Dauden +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models +from odoo.fields import Domain +from odoo.tools import ormcache +from odoo.tools.safe_eval import datetime, safe_eval + + +class IrFilters(models.Model): + _inherit = "ir.filters" + + partner_ids = fields.Many2many( + comodel_name="res.partner", + help="This field allow to relate a partner to a domain of products", + default=lambda p: p.env.context.get("default_partner_ids"), + ) + + blacklist_product_ids = fields.Many2many( + comodel_name="product.product", + string="Restricted products", + relation="assortment_product_blacklisted", + ) + + whitelist_product_ids = fields.Many2many( + comodel_name="product.product", + string="Allowed products", + relation="assortment_product_whitelisted", + ) + + record_count = fields.Integer(compute="_compute_record_count") + + is_assortment = fields.Boolean(default=lambda x: x._get_default_is_assortment()) + partner_domain = fields.Text(default="[]", required=True) + all_partner_ids = fields.Many2many( + comodel_name="res.partner", + compute="_compute_all_partner_ids", + # Make it store=True because we will need this field to search by involved + # partners + store=True, + relation="ir_filter_all_partner_rel", + column1="filter_id", + column2="partner_id", + ) + apply_black_list_product_domain = fields.Boolean( + string="Apply restricted product domain" + ) + black_list_product_domain = fields.Text( + string="Restricted product domain", default="[]", required=True + ) + + @api.model + def _get_default_is_assortment(self): + return self.env.context.get("product_assortment", False) + + @api.model + def _update_assortment_default_values(self, vals_list): + """ + If we create the filter through the assortment, we need to force + model_id to product.product + """ + product_assortment = self.env.context.get("product_assortment", False) + if not product_assortment: + return vals_list + model = self.env.ref("product.model_product_product") + for vals in vals_list: + if not vals.get("model_id"): + vals.update({"model_id": model.model}) + return vals_list + + @api.model_create_multi + def create(self, vals_list): + self._update_assortment_default_values(vals_list) + return super().create(vals_list) + + @api.model + @ormcache() + def get_partner_domain_fields(self): + field_set = set() + for ir_filter in self.sudo().search([("is_assortment", "=", True)]): + domain = ir_filter._get_eval_partner_domain() + for item in domain: + if isinstance(item, list | tuple) and isinstance(item[0], str): + field_set.add(item[0].split(".")[0]) + return field_set + + @api.depends("partner_ids", "partner_domain") + def _compute_all_partner_ids(self): + """Summarize selected partners and partners from partner domain field""" + for ir_filter in self.sudo(): + if not ir_filter.is_assortment: + ir_filter.all_partner_ids = False + if ir_filter.partner_domain != []: + ir_filter.all_partner_ids = ( + self.env["res.partner"].search(ir_filter._get_eval_partner_domain()) + + ir_filter.partner_ids + ) + else: + ir_filter.all_partner_ids = ir_filter.partner_ids + + def _get_eval_domain(self): + res = super()._get_eval_domain() + if self.apply_black_list_product_domain: + black_list_domain = self._get_eval_black_list_domain() + res = Domain.AND([~Domain(black_list_domain), Domain(res)]) + + if self.whitelist_product_ids: + result_domain = [("id", "in", self.whitelist_product_ids.ids)] + res = Domain.OR([result_domain, Domain(res)]) + + if self.blacklist_product_ids: + result_domain = [("id", "not in", self.blacklist_product_ids.ids)] + res = Domain.AND([result_domain, Domain(res)]) + + return res + + def _get_eval_black_list_domain(self): + self.ensure_one() + return safe_eval( + self.black_list_product_domain, + {"datetime": datetime, "context_today": datetime.datetime.now}, + ) + + def _get_eval_partner_domain(self): + self.ensure_one() + return safe_eval( + self.partner_domain, + {"datetime": datetime, "context_today": datetime.datetime.now}, + ) + + def _compute_record_count(self): + for record in self: + # model_id may be unset on new records, skip count to prevent errors + if not record.is_assortment or not record.model_id: + record.record_count = 0 + continue + domain = record._get_eval_domain() + record.record_count = self.env[record.model_id].search_count(domain) + + @api.model + def _get_action_domain( + self, action_id=None, embedded_action_id=None, embedded_parent_res_id=None + ): + # tricky way to act on get_filter method to prevent returning + # assortment in search view filters + domain = super()._get_action_domain( + action_id=action_id, + embedded_action_id=embedded_action_id, + embedded_parent_res_id=embedded_parent_res_id, + ) + domain = Domain.AND([Domain([("is_assortment", "=", False)]), Domain(domain)]) + + return domain + + def write(self, vals): + res = super().write(vals) + if "partner_ids" in vals or "partner_domain" in vals: + self.env.registry.clear_cache() + return res + + def show_products(self): + self.ensure_one() + xmlid = "product.product_normal_action_sell" + action = self.env["ir.actions.act_window"]._for_xml_id(xmlid) + action.update( + { + "domain": self._get_eval_domain(), + "name": self.env._("Products"), + "context": self.env.context, + "target": "current", + } + ) + return action diff --git a/product_assortment/models/ir_rule.py b/product_assortment/models/ir_rule.py new file mode 100644 index 00000000000..e077978cb42 --- /dev/null +++ b/product_assortment/models/ir_rule.py @@ -0,0 +1,35 @@ +# Copyright 2024 Tecnativa - Sergio Teruel +# License AGPL-3 - See https://www.gnu.org/licenses/agpl-3.0.html + +from odoo import api, models, tools +from odoo.fields import Domain +from odoo.tools import config + + +class IrRule(models.Model): + _inherit = "ir.rule" + + @api.model + @tools.conditional( + "xml" not in config["dev_mode"], + tools.ormcache( + "self.env.uid", + "self.env.su", + "model_name", + "mode", + "tuple(self._compute_domain_context_values())", + ), + ) + def _compute_domain(self, model_name, mode="read"): + """Inject extra domain for restricting filter (Assortments) when the user + has not the group 'Product Assortment Manager'. + """ + res = super()._compute_domain(model_name, mode=mode) + user = self.env.user + if model_name == "ir.filters" and not self.env.su: + if not user.has_group( + "product_assortment.group_product_assortment_manager" + ): + extra_domain = Domain([("is_assortment", "=", False)]) + res = Domain.AND([extra_domain] + [res]) + return res diff --git a/product_assortment/models/res_partner.py b/product_assortment/models/res_partner.py new file mode 100644 index 00000000000..812011ddd2a --- /dev/null +++ b/product_assortment/models/res_partner.py @@ -0,0 +1,60 @@ +# Copyright 2021 Tecnativa - Carlos Roca +# Copyright 2021 Tecnativa - Carlos Dauden +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). +from odoo import api, fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + applied_assortment_ids = fields.Many2many( + comodel_name="ir.filters", + relation="ir_filter_all_partner_rel", + column1="partner_id", + column2="filter_id", + copy=False, + ) + + def action_define_product_assortment(self): + self.ensure_one() + xmlid = "product_assortment.actions_product_assortment_view" + action = self.env["ir.actions.act_window"]._for_xml_id(xmlid) + action["domain"] = [ + ("all_partner_ids", "in", self.ids), + ("is_assortment", "=", True), + ] + ctx = self.env.context.copy() + ctx.update( + { + "default_partner_ids": self.ids, + "default_is_assortment": True, + "product_assortment": True, + } + ) + action["context"] = ctx + return action + + def _update_partner_assortments(self): + # Using sudo to contemplate evaluation of domains with restricted fields + self = self.sudo() + assortments = self.env["ir.filters"].search([("is_assortment", "=", True)]) + for partner in self: + # Use ids instead of record to improve performance (Remove in next versions) + partner_assortment_ids = [] + for assortment in assortments: + if partner in assortment.all_partner_ids: + partner_assortment_ids.append(assortment.id) + partner.applied_assortment_ids = assortments.browse(partner_assortment_ids) + + @api.model_create_multi + def create(self, vals_list): + partners = super().create(vals_list) + self._update_partner_assortments() + return partners + + def write(self, vals): + res = super().write(vals) + IrFilters = self.env["ir.filters"] + if IrFilters.get_partner_domain_fields() & set(vals.keys()): + self._update_partner_assortments() + return res diff --git a/product_assortment/pyproject.toml b/product_assortment/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/product_assortment/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/product_assortment/readme/CONTRIBUTORS.md b/product_assortment/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..78aeddace92 --- /dev/null +++ b/product_assortment/readme/CONTRIBUTORS.md @@ -0,0 +1,6 @@ +- Denis Roussel \<\> +- Cédric Pigeon \<\> +- Xavier Bouquiaux \<\> +- [Tecnativa](https://www.tecnativa.com): + - Carlos Roca + - Sergio Teruel diff --git a/product_assortment/readme/DESCRIPTION.md b/product_assortment/readme/DESCRIPTION.md new file mode 100644 index 00000000000..9c60a45bb2c --- /dev/null +++ b/product_assortment/readme/DESCRIPTION.md @@ -0,0 +1,6 @@ +This addon intends to manage product assortment. In Odoo you can only +define some filters defined by a domain but it can be sometimes really +complicated. With this addon you will be able to define a domain but +also add some products to include or to exclude through a allowed list +and a restricted list. This is done by overriding ir.capability but +without influencing its standard behaviour. diff --git a/product_assortment/readme/HISTORY.md b/product_assortment/readme/HISTORY.md new file mode 100644 index 00000000000..589bbea5c99 --- /dev/null +++ b/product_assortment/readme/HISTORY.md @@ -0,0 +1,27 @@ +## 10.0.1.0.0 (2018-08-27) + +- \[10.0\]\[ADD\] productassortment + +## 12.0.1.0.0 (2019-06-03) + +- \[12.0\]\[MIG\] productassortment + +## 14.0.1.0.0 (2019-06-03) + +- \[14.0\]\[MIG\] productassortment + +## 16.0.1.0.0 (2022-09-15) + +- \[16.0\]\[MIG\] product_assortment + +## 18.0.1.0.0 (2025-03-06) + +- \[18.0\]\[MIG\] product_assortment +- Forward port demo data +- Forward port Only Show assortments to managers +- Forward port Fix All assortments are applied to original partner when partner is duplicated +- Adjust test code to new API behavior, for info: odoo/odoo@450f5c9 +- added test for combined black list and whitelisted product +- Fix: Navigating to the product assortment using the smartbutton on the partner does not show all applicable assortments. + (The assortments with the partner defined as a domain where missing.) + diff --git a/product_assortment/readme/USAGE.md b/product_assortment/readme/USAGE.md new file mode 100644 index 00000000000..dc83a2ad52c --- /dev/null +++ b/product_assortment/readme/USAGE.md @@ -0,0 +1,5 @@ +To use this module, you need to: + +1. Enter the menu through the Product Assortment Icon +2. Create a new filter where you can define your domain and add allowed + and restricted products diff --git a/product_assortment/security/product_assortment_security.xml b/product_assortment/security/product_assortment_security.xml new file mode 100644 index 00000000000..363e69db9c6 --- /dev/null +++ b/product_assortment/security/product_assortment_security.xml @@ -0,0 +1,18 @@ + + + + Product Assortment Manager + Access to all product assortment + + + + ir.filters.product.assortment.manager.rule + + [('is_assortment','=', True)] + + + + diff --git a/product_assortment/static/description/icon.png b/product_assortment/static/description/icon.png new file mode 100644 index 00000000000..809e4c63d63 Binary files /dev/null and b/product_assortment/static/description/icon.png differ diff --git a/product_assortment/static/description/index.html b/product_assortment/static/description/index.html new file mode 100644 index 00000000000..e12b3d47927 --- /dev/null +++ b/product_assortment/static/description/index.html @@ -0,0 +1,501 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Product Assortment

+ +

Production/Stable License: AGPL-3 OCA/product-attribute Translate me on Weblate Try me on Runboat

+

This addon intends to manage product assortment. In Odoo you can only +define some filters defined by a domain but it can be sometimes really +complicated. With this addon you will be able to define a domain but +also add some products to include or to exclude through a allowed list +and a restricted list. This is done by overriding ir.capability but +without influencing its standard behaviour.

+

Table of contents

+ +
+

Usage

+

To use this module, you need to:

+
    +
  1. Enter the menu through the Product Assortment Icon
  2. +
  3. Create a new filter where you can define your domain and add allowed +and restricted products
  4. +
+
+
+

Changelog

+
+

10.0.1.0.0 (2018-08-27)

+
    +
  • [10.0][ADD] productassortment
  • +
+
+
+

12.0.1.0.0 (2019-06-03)

+
    +
  • [12.0][MIG] productassortment
  • +
+
+
+

14.0.1.0.0 (2019-06-03)

+
    +
  • [14.0][MIG] productassortment
  • +
+
+
+

16.0.1.0.0 (2022-09-15)

+
    +
  • [16.0][MIG] product_assortment
  • +
+
+
+

18.0.1.0.0 (2025-03-06)

+
    +
  • [18.0][MIG] product_assortment
  • +
  • Forward port demo data
  • +
  • Forward port Only Show assortments to managers
  • +
  • Forward port Fix All assortments are applied to original partner when +partner is duplicated
  • +
  • Adjust test code to new API behavior, for info: odoo/odoo@450f5c9
  • +
  • added test for combined black list and whitelisted product
  • +
  • Fix: Navigating to the product assortment using the smartbutton on +the partner does not show all applicable assortments. (The +assortments with the partner defined as a domain where missing.)
  • +
+
+
+
+

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

+
    +
  • ACSONE SA/NV
  • +
+
+
+

Contributors

+ +
+
+

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/product-attribute project on GitHub.

+

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

+
+
+
+
+ + diff --git a/product_assortment/tests/__init__.py b/product_assortment/tests/__init__.py new file mode 100644 index 00000000000..7c6e5f94694 --- /dev/null +++ b/product_assortment/tests/__init__.py @@ -0,0 +1 @@ +from . import test_product_assortment diff --git a/product_assortment/tests/test_product_assortment.py b/product_assortment/tests/test_product_assortment.py new file mode 100644 index 00000000000..88df4e9131e --- /dev/null +++ b/product_assortment/tests/test_product_assortment.py @@ -0,0 +1,208 @@ +# Copyright 2021 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from psycopg2 import IntegrityError + +from odoo.tests.common import TransactionCase +from odoo.tools.misc import mute_logger + + +class TestProductAssortment(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.filter_obj = cls.env["ir.filters"] + cls.product_obj = cls.env["product.product"] + cls.product_storage_box = cls.product_obj.create( + { + "name": "Storage Box", + "type": "consu", + "standard_price": 14.0, + "list_price": 15.8, + "default_code": "E-COM08", + } + ) + cls.product_virtual_home_staging = cls.product_obj.create( + { + "name": "Virtual Home Staging", + "type": "service", + "standard_price": 25.5, + "list_price": 38.25, + } + ) + cls.assortment = cls.filter_obj.create( + { + "name": "Test Assortment", + "model_id": "product.product", + "is_assortment": True, + "domain": [], + } + ) + cls.partner = cls.env["res.partner"].create({"name": "Test partner"}) + cls.partner2 = cls.env["res.partner"].create({"name": "Test partner 2"}) + + def test_assortment(self): + products = self.product_obj.search([]) + domain = self.assortment._get_eval_domain() + products_filtered = self.product_obj.search(domain) + self.assertEqual(products.ids, products_filtered.ids) + + # reduce assortment to services products + domain = [("type", "=", "service")] + self.assortment.domain = domain + + products = self.product_obj.search(domain) + domain = self.assortment._get_eval_domain() + products_filtered = self.product_obj.search(domain) + self.assertEqual(products.ids, products_filtered.ids) + + # include one product not in initial filter + included_product = self.product_storage_box + self.assortment.write({"whitelist_product_ids": [(4, included_product.id)]}) + domain = self.assortment._get_eval_domain() + products_filtered = self.product_obj.search(domain) + self.assertIn(included_product.id, products_filtered.ids) + + # exclude one product not in initial filter + excluded_product = self.product_virtual_home_staging + domain = self.assortment._get_eval_domain() + products_filtered = self.product_obj.search(domain) + self.assertIn(excluded_product.id, products_filtered.ids) + self.assortment.write({"blacklist_product_ids": [(4, excluded_product.id)]}) + domain = self.assortment._get_eval_domain() + products_filtered = self.product_obj.search(domain) + self.assertNotIn(excluded_product.id, products_filtered.ids) + + def test_assortment_not_available_search_view(self): + model = self.env.ref("product.model_product_product") + filters = self.filter_obj.get_filters(model.id) + self.assertFalse(filters) + + def test_create_assortment_with_context(self): + assortment = self.filter_obj.with_context(product_assortment=True).create( + {"name": "Test Assortment Context", "domain": []} + ) + self.assertTrue(assortment.is_assortment) + self.assertEqual(assortment.model_id, "product.product") + + @mute_logger("odoo.sql_db") + def test_create_assortment_without_context(self): + with self.assertRaises(IntegrityError), self.env.cr.savepoint(): + self.filter_obj.with_context(product_assortment=False).create( + {"name": "Test Assortment No Context", "domain": []} + ) + + def test_search_assortment_with_partner(self): + self.filter_obj.with_context(product_assortment=True).create( + { + "name": "Test Assortment Partner", + "domain": [], + "partner_ids": [(4, self.partner.id)], + } + ) + search_domain = self.partner.action_define_product_assortment()["domain"] + self.assertEqual( + search_domain, + [ + ("all_partner_ids", "in", [self.partner.id]), + ("is_assortment", "=", True), + ], + ) + + def test_product_assortment_view(self): + included_product = self.product_storage_box + self.assortment.write({"whitelist_product_ids": [(4, included_product.id)]}) + res = self.assortment.show_products() + domain = [tuple(condition) for condition in res["domain"]] + self.assertEqual(domain, [(1, "=", 1)]) + + def test_product_assortment_view_with_black_list(self): + excluded_product = self.product_virtual_home_staging + self.assortment.write( + { + "blacklist_product_ids": [(4, excluded_product.id)], + } + ) + res = self.assortment.show_products() + domain = [tuple(condition) for condition in res["domain"]] + self.assertEqual(domain, [("id", "not in", excluded_product.ids)]) + + def test_product_assortment_mixed_view(self): + included_product = self.product_storage_box + excluded_product = self.product_virtual_home_staging + self.assortment.write( + { + "whitelist_product_ids": [(4, included_product.id)], + "blacklist_product_ids": [(4, excluded_product.id)], + } + ) + res = self.assortment.show_products() + domain = [tuple(condition) for condition in res["domain"]] + self.assertEqual(domain, [("id", "not in", excluded_product.ids)]) + + def test_product_assortment_filter_combination(self): + """Combine a whitelisted and a blacklisted product in order + to validate the combination of both filters. The result should be a + simple domain with the excluded product. + """ + # Add a default no product filter to the assortment + self.assortment.write({"domain": [("id", "=", 0)]}) + included_product = self.product_storage_box + self.assortment.write({"whitelist_product_ids": [(4, included_product.id)]}) + excluded_product = self.product_virtual_home_staging + self.assortment.write({"blacklist_product_ids": [(4, excluded_product.id)]}) + res = self.assortment.show_products() + self.assertIn(("id", "not in", [excluded_product.id]), res["domain"]) + self.assertIn(("id", "in", [included_product.id]), res["domain"]) + + def test_record_count(self): + products = self.product_obj.search([]) + self.assertEqual(self.assortment.record_count, len(products)) + + # reduce assortment to services products + domain = [("type", "=", "service")] + self.assortment.domain = domain + + products = self.product_obj.search(domain) + domain = self.assortment._get_eval_domain() + products_filtered = self.product_obj.search(domain) + self.assortment.invalidate_recordset() + self.assertEqual(self.assortment.record_count, len(products_filtered)) + + def test_assortment_with_partner_domain(self): + assortment = self.filter_obj.with_context(product_assortment=True).create( + { + "name": "Test Assortment Partner domain", + "partner_domain": f"[('id', '=', '{self.partner.id}')]", + "partner_ids": [(4, self.partner2.id)], + } + ) + self.assertEqual(assortment.all_partner_ids, self.partner + self.partner2) + + def test_assortment_update_with_multiple_partner(self): + assortment = self.filter_obj.with_context(product_assortment=True).create( + { + "name": "Test Assortment multiple partner", + "partner_domain": "[('name', '=', 'Test partner updated')]", + "partner_ids": [(4, self.partner.id), (4, self.partner2.id)], + } + ) + self.partner.name = "Test partner updated" + self.assertIn(assortment.id, self.partner.applied_assortment_ids.ids) + self.assertEqual(assortment.all_partner_ids, self.partner + self.partner2) + + def test_assortment_with_black_list_product_domain(self): + excluded_product = self.product_virtual_home_staging + assortment = self.filter_obj.with_context(product_assortment=True).create( + { + "name": "Test Assortment black product domain", + "domain": [], + "partner_ids": [(4, self.partner2.id)], + "apply_black_list_product_domain": True, + "black_list_product_domain": [("id", "=", excluded_product.id)], + } + ) + allowed_products = self.env["product.product"].search( + assortment._get_eval_domain() + ) + self.assertNotIn(excluded_product, allowed_products) diff --git a/product_assortment/views/product_assortment.xml b/product_assortment/views/product_assortment.xml new file mode 100644 index 00000000000..1336c65c5ea --- /dev/null +++ b/product_assortment/views/product_assortment.xml @@ -0,0 +1,151 @@ + + + + + product.assortment.search + ir.filters + + + + + + + + + ir.filters + + + + + + + + + product.assortment.form + ir.filters + +
+
+ +
+ +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + product.product + + + + + + + + Product assortment + ir.actions.act_window + ir.filters + [('is_assortment', '=', True)] + {'product_assortment': True, + 'default_is_assortment': 1} + + + + diff --git a/product_assortment/views/res_partner_view.xml b/product_assortment/views/res_partner_view.xml new file mode 100644 index 00000000000..b9bd5b55e67 --- /dev/null +++ b/product_assortment/views/res_partner_view.xml @@ -0,0 +1,21 @@ + + + + res.partner + form + + +
+ +
+
+
+