diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dc948785fce..59880d025e2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,7 +43,7 @@ repos: hooks: - id: whool-init - repo: https://github.com/oca/maintainer-tools - rev: f9b919b9868143135a9c9cb03021089cabba8223 + rev: f70373fca6830e6536c3967ba5794311602b4bd4 hooks: # update the NOT INSTALLABLE ADDONS section above - id: oca-update-pre-commit-excluded-addons @@ -117,13 +117,13 @@ repos: - id: mixed-line-ending args: ["--fix=lf"] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.13.0 + rev: v0.13.2 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - id: ruff-format - repo: https://github.com/OCA/pylint-odoo - rev: v9.3.15 + rev: v9.3.18 hooks: - id: pylint_odoo name: pylint with optional checks diff --git a/README.md b/README.md index c5a40503160..6b6c4e7545f 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,14 @@ product-attribute [//]: # (addons) -This part will be replaced when running the oca-gen-addons-table script from OCA/maintainer-tools. +Available addons +---------------- +addon | version | maintainers | summary +--- | --- | --- | --- +[product_category_code](product_category_code/) | 19.0.1.0.0 | rousseldenis | Allows to define a code on product categories +[product_code_unique](product_code_unique/) | 19.0.1.0.0 | | Set Product Internal Reference as Unique +[product_company_default](product_company_default/) | 19.0.1.0.0 | | Product Company Default +[product_net_weight](product_net_weight/) | 19.0.1.0.1 | legalsylvain | Add 'Net Weight' on product models [//]: # (end addons) diff --git a/product_assortment/README.rst b/product_assortment/README.rst new file mode 100644 index 00000000000..b4b81824572 --- /dev/null +++ b/product_assortment/README.rst @@ -0,0 +1,137 @@ +.. 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 + +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 +------------ + +- 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..67e093d8458 --- /dev/null +++ b/product_assortment/demo/assortments.xml @@ -0,0 +1,37 @@ + + + + + + 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..e0d9c8a8ac5 --- /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 + + +
+ +
+
+
+
diff --git a/product_category_code/README.rst b/product_category_code/README.rst new file mode 100644 index 00000000000..64f969014fe --- /dev/null +++ b/product_category_code/README.rst @@ -0,0 +1,88 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +===================== +Product Category Code +===================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:7b359f8034ed5ebc6eb877ddf938838ca8c05356d4bc841170bbb57d466ae41a + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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 + :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_category_code + :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_category_code + :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 module adds a field 'code' on product category level. + +**Table of contents** + +.. contents:: + :local: + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* ACSONE SA/NV + +Contributors +------------ + +- Denis Roussel + +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-rousseldenis| image:: https://github.com/rousseldenis.png?size=40px + :target: https://github.com/rousseldenis + :alt: rousseldenis + +Current `maintainer `__: + +|maintainer-rousseldenis| + +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_category_code/__init__.py b/product_category_code/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/product_category_code/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/product_category_code/__manifest__.py b/product_category_code/__manifest__.py new file mode 100644 index 00000000000..a5b0297eb62 --- /dev/null +++ b/product_category_code/__manifest__.py @@ -0,0 +1,19 @@ +# Copyright 2020 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Product Category Code", + "summary": """ + Allows to define a code on product categories""", + "version": "19.0.1.0.0", + "license": "AGPL-3", + "author": "ACSONE SA/NV,Odoo Community Association (OCA)", + "maintainers": ["rousseldenis"], + "website": "https://github.com/OCA/product-attribute", + "depends": [ + "product", + ], + "data": [ + "views/product_category.xml", + ], +} diff --git a/product_category_code/i18n/es.po b/product_category_code/i18n/es.po new file mode 100644 index 00000000000..1d46fc63c40 --- /dev/null +++ b/product_category_code/i18n/es.po @@ -0,0 +1,33 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_category_code +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-03-26 22:36+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: none\n" +"Language: es\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.17\n" + +#. module: product_category_code +#. odoo-python +#: code:addons/product_category_code/models/product_category.py:0 +msgid "-copy" +msgstr "-copia" + +#. module: product_category_code +#: model:ir.model.fields,field_description:product_category_code.field_product_category__code +msgid "Code" +msgstr "Código" + +#. module: product_category_code +#: model:ir.model,name:product_category_code.model_product_category +msgid "Product Category" +msgstr "Categoría de Producto" diff --git a/product_category_code/i18n/it.po b/product_category_code/i18n/it.po new file mode 100644 index 00000000000..98d63ffecec --- /dev/null +++ b/product_category_code/i18n/it.po @@ -0,0 +1,33 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_category_code +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-04-27 10:06+0000\n" +"Last-Translator: Sebastiano Picchi \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 4.14.1\n" + +#. module: product_category_code +#. odoo-python +#: code:addons/product_category_code/models/product_category.py:0 +msgid "-copy" +msgstr "-copia" + +#. module: product_category_code +#: model:ir.model.fields,field_description:product_category_code.field_product_category__code +msgid "Code" +msgstr "Codice" + +#. module: product_category_code +#: model:ir.model,name:product_category_code.model_product_category +msgid "Product Category" +msgstr "Categoria prodotto" diff --git a/product_category_code/i18n/product_category_code.pot b/product_category_code/i18n/product_category_code.pot new file mode 100644 index 00000000000..7189fbe899d --- /dev/null +++ b/product_category_code/i18n/product_category_code.pot @@ -0,0 +1,40 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_category_code +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.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_category_code +#. odoo-python +#: code:addons/product_category_code/models/product_category.py:0 +msgid "-copy" +msgstr "" + +#. module: product_category_code +#: model:ir.model.fields,field_description:product_category_code.field_product_category__code +msgid "Code" +msgstr "" + +#. module: product_category_code +#: model:ir.model.fields,field_description:product_category_code.field_product_category__display_name +msgid "Display Name" +msgstr "" + +#. module: product_category_code +#: model:ir.model.fields,field_description:product_category_code.field_product_category__id +msgid "ID" +msgstr "" + +#. module: product_category_code +#: model:ir.model,name:product_category_code.model_product_category +msgid "Product Category" +msgstr "" diff --git a/product_category_code/models/__init__.py b/product_category_code/models/__init__.py new file mode 100644 index 00000000000..53553f3f2de --- /dev/null +++ b/product_category_code/models/__init__.py @@ -0,0 +1 @@ +from . import product_category diff --git a/product_category_code/models/product_category.py b/product_category_code/models/product_category.py new file mode 100644 index 00000000000..e513b51b9b9 --- /dev/null +++ b/product_category_code/models/product_category.py @@ -0,0 +1,18 @@ +# Copyright 2021 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ProductCategory(models.Model): + _inherit = "product.category" + + code = fields.Char( + default="/", + index=True, + ) + + def copy(self, default=None): + default = default or {} + default.setdefault("code", self.code + self.env._("-copy")) + return super().copy(default) diff --git a/product_category_code/pyproject.toml b/product_category_code/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/product_category_code/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/product_category_code/readme/CONTRIBUTORS.md b/product_category_code/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..89c79f8e430 --- /dev/null +++ b/product_category_code/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Denis Roussel \<\> diff --git a/product_category_code/readme/DESCRIPTION.md b/product_category_code/readme/DESCRIPTION.md new file mode 100644 index 00000000000..464f90a6ee8 --- /dev/null +++ b/product_category_code/readme/DESCRIPTION.md @@ -0,0 +1 @@ +This module adds a field 'code' on product category level. diff --git a/product_category_code/static/description/icon.png b/product_category_code/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/product_category_code/static/description/icon.png differ diff --git a/product_category_code/static/description/index.html b/product_category_code/static/description/index.html new file mode 100644 index 00000000000..768c4e04a4e --- /dev/null +++ b/product_category_code/static/description/index.html @@ -0,0 +1,431 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Product Category Code

+ +

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

+

This module adds a field ‘code’ on product category level.

+

Table of contents

+ +
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • 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.

+

Current maintainer:

+

rousseldenis

+

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_category_code/tests/__init__.py b/product_category_code/tests/__init__.py new file mode 100644 index 00000000000..c7952bfdb4f --- /dev/null +++ b/product_category_code/tests/__init__.py @@ -0,0 +1 @@ +from . import test_category_code diff --git a/product_category_code/tests/test_category_code.py b/product_category_code/tests/test_category_code.py new file mode 100644 index 00000000000..4d5cf411df5 --- /dev/null +++ b/product_category_code/tests/test_category_code.py @@ -0,0 +1,23 @@ +# Copyright 2021 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import odoo.tests.common as common + + +class TestProductCategoryCode(common.TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + vals = { + "name": "Category Test", + "code": "TEST", + } + cls.category = cls.env["product.category"].create(vals) + + def test_category(self): + new_category = self.category.copy() + + self.assertEqual( + "TEST-copy", + new_category.code, + ) diff --git a/product_category_code/views/product_category.xml b/product_category_code/views/product_category.xml new file mode 100644 index 00000000000..a68368dd05e --- /dev/null +++ b/product_category_code/views/product_category.xml @@ -0,0 +1,37 @@ + + + + + product.category.form (in product_category_code) + product.category + + + + + + + + + + product.category.search (in product_category_code) + product.category + + + + + + + + + + product.category.list (in product_category_code) + product.category + + + + + + + + diff --git a/product_code_unique/README.rst b/product_code_unique/README.rst new file mode 100644 index 00000000000..4c74252b5bb --- /dev/null +++ b/product_code_unique/README.rst @@ -0,0 +1,101 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +================================= +Unique Product Internal Reference +================================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:90cf542b3aa17d2e63af1129f939979af06497a6b2e5685f276a36ef0a2ce203 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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 + :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_code_unique + :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_code_unique + :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 module adds a constraint on the internal reference of the product +to make it unique across the database. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +- Unable to save a product when a new internal reference or default_code + value is the same with an existing record. +- A pre_init_hook process is initiated when there exist records without + an internal reference(default_code). A default value is generated to + populate empty field as a temporary value. + +Known issues / Roadmap +====================== + +- Avoid duplicate warnings. Odoo has a warning for duplicate "Internal + Reference" of its own (it doesn't block from saving). Now both + warnings are displayed when trying to save a duplicate "Internal + Reference". + +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 +------- + +* Open Source Integrators + +Contributors +------------ + +- Antonio Yamuta +- Raf Ven +- Watthanun Khorchai +- Nedas Žilinskas + +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_code_unique/__init__.py b/product_code_unique/__init__.py new file mode 100644 index 00000000000..798718f389a --- /dev/null +++ b/product_code_unique/__init__.py @@ -0,0 +1,5 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models +from .hook import pre_init_product_code diff --git a/product_code_unique/__manifest__.py b/product_code_unique/__manifest__.py new file mode 100644 index 00000000000..cc4603b76ec --- /dev/null +++ b/product_code_unique/__manifest__.py @@ -0,0 +1,15 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Unique Product Internal Reference", + "summary": "Set Product Internal Reference as Unique", + "version": "19.0.1.0.0", + "license": "AGPL-3", + "author": "Open Source Integrators, Odoo Community Association (OCA)", + "category": "Product", + "website": "https://github.com/OCA/product-attribute", + "depends": ["product"], + "pre_init_hook": "pre_init_product_code", + "installable": True, +} diff --git a/product_code_unique/hook.py b/product_code_unique/hook.py new file mode 100644 index 00000000000..7396424779e --- /dev/null +++ b/product_code_unique/hook.py @@ -0,0 +1,19 @@ +# Copyright (C) 2019 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +def pre_init_product_code(env): + env.cr.execute( + """UPDATE product_product + SET default_code = 'DEFAULT' || nextval('ir_default_id_seq') + WHERE id in (SELECT distinct(pp.id) + FROM product_product pp + INNER JOIN (SELECT default_code, COUNT(*) + FROM product_product + GROUP BY default_code + HAVING COUNT(*)>1 + )pp1 on pp.default_code=pp1.default_code + or pp.default_code is NULL + or LENGTH(pp.default_code) = 0)""" + ) + return True diff --git a/product_code_unique/i18n/ca.po b/product_code_unique/i18n/ca.po new file mode 100644 index 00000000000..137b1919407 --- /dev/null +++ b/product_code_unique/i18n/ca.po @@ -0,0 +1,30 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_code_unique +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2022-04-22 10:05+0000\n" +"Last-Translator: pablontura \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_code_unique +#: model:ir.model.constraint,message:product_code_unique.constraint_product_product_default_code_uniq +msgid "Internal Reference must be unique across the database!" +msgstr "La referència interna ha de ser única a tota la base de dades!" + +#. module: product_code_unique +#: model:ir.model,name:product_code_unique.model_product_product +msgid "Product Variant" +msgstr "" + +#~ msgid "Product" +#~ msgstr "Producte" diff --git a/product_code_unique/i18n/de.po b/product_code_unique/i18n/de.po new file mode 100644 index 00000000000..ff5acb7de06 --- /dev/null +++ b/product_code_unique/i18n/de.po @@ -0,0 +1,31 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_code_unique +# +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_code_unique +#: model:ir.model.constraint,message:product_code_unique.constraint_product_product_default_code_uniq +msgid "Internal Reference must be unique across the database!" +msgstr "" +"Die Interne Referenz muss eindeutig sein, Duplikate sind nicht zugelassen!" + +#. module: product_code_unique +#: model:ir.model,name:product_code_unique.model_product_product +msgid "Product Variant" +msgstr "Produktvariante" + +#~ msgid "Product" +#~ msgstr "Produkt" diff --git a/product_code_unique/i18n/es.po b/product_code_unique/i18n/es.po new file mode 100644 index 00000000000..9ea401a6267 --- /dev/null +++ b/product_code_unique/i18n/es.po @@ -0,0 +1,27 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_code_unique +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-12-29 11:34+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: none\n" +"Language: es\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.17\n" + +#. module: product_code_unique +#: model:ir.model.constraint,message:product_code_unique.constraint_product_product_default_code_uniq +msgid "Internal Reference must be unique across the database!" +msgstr "¡La Referencia Interna debe ser única en toda la base de datos!" + +#. module: product_code_unique +#: model:ir.model,name:product_code_unique.model_product_product +msgid "Product Variant" +msgstr "Variante del Producto" diff --git a/product_code_unique/i18n/fr.po b/product_code_unique/i18n/fr.po new file mode 100644 index 00000000000..f4e089a464a --- /dev/null +++ b/product_code_unique/i18n/fr.po @@ -0,0 +1,36 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_code_unique +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2021-04-10 11:46+0000\n" +"Last-Translator: Yves Le Doeuff \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 4.3.2\n" + +#. module: product_code_unique +#: model:ir.model.constraint,message:product_code_unique.constraint_product_product_default_code_uniq +msgid "Internal Reference must be unique across the database!" +msgstr "La référence interne doit être unique !" + +#. module: product_code_unique +#: model:ir.model,name:product_code_unique.model_product_product +msgid "Product Variant" +msgstr "" + +#~ msgid "Display Name" +#~ msgstr "Nom affiché" + +#~ msgid "Last Modified on" +#~ msgstr "Dernière modification" + +#~ msgid "Product" +#~ msgstr "Article" diff --git a/product_code_unique/i18n/fr_FR.po b/product_code_unique/i18n/fr_FR.po new file mode 100644 index 00000000000..a80b46051d4 --- /dev/null +++ b/product_code_unique/i18n/fr_FR.po @@ -0,0 +1,36 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_code_unique +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2021-04-10 15:46+0000\n" +"Last-Translator: Yves Le Doeuff \n" +"Language-Team: none\n" +"Language: fr_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 4.3.2\n" + +#. module: product_code_unique +#: model:ir.model.constraint,message:product_code_unique.constraint_product_product_default_code_uniq +msgid "Internal Reference must be unique across the database!" +msgstr "La référence interne doit être unique !" + +#. module: product_code_unique +#: model:ir.model,name:product_code_unique.model_product_product +msgid "Product Variant" +msgstr "" + +#~ msgid "Display Name" +#~ msgstr "Nom affiché" + +#~ msgid "Last Modified on" +#~ msgstr "Dernière modification" + +#~ msgid "Product" +#~ msgstr "Article" diff --git a/product_code_unique/i18n/it.po b/product_code_unique/i18n/it.po new file mode 100644 index 00000000000..9f9337e612c --- /dev/null +++ b/product_code_unique/i18n/it.po @@ -0,0 +1,27 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_code_unique +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-11-01 12:37+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 4.17\n" + +#. module: product_code_unique +#: model:ir.model.constraint,message:product_code_unique.constraint_product_product_default_code_uniq +msgid "Internal Reference must be unique across the database!" +msgstr "Il riferimento interno deve essere unico all'interno del database!" + +#. module: product_code_unique +#: model:ir.model,name:product_code_unique.model_product_product +msgid "Product Variant" +msgstr "Variante prodotto" diff --git a/product_code_unique/i18n/product_code_unique.pot b/product_code_unique/i18n/product_code_unique.pot new file mode 100644 index 00000000000..2f04b3bdb68 --- /dev/null +++ b/product_code_unique/i18n/product_code_unique.pot @@ -0,0 +1,34 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_code_unique +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.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_code_unique +#: model:ir.model.fields,field_description:product_code_unique.field_product_product__display_name +msgid "Display Name" +msgstr "" + +#. module: product_code_unique +#: model:ir.model.fields,field_description:product_code_unique.field_product_product__id +msgid "ID" +msgstr "" + +#. module: product_code_unique +#: model:ir.model.constraint,message:product_code_unique.constraint_product_product_default_code_uniq +msgid "Internal Reference must be unique across the database!" +msgstr "" + +#. module: product_code_unique +#: model:ir.model,name:product_code_unique.model_product_product +msgid "Product Variant" +msgstr "" diff --git a/product_code_unique/i18n/pt.po b/product_code_unique/i18n/pt.po new file mode 100644 index 00000000000..8099bbc77e9 --- /dev/null +++ b/product_code_unique/i18n/pt.po @@ -0,0 +1,27 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_code_unique +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-07-13 00:11+0000\n" +"Last-Translator: Pedro Castro Silva \n" +"Language-Team: none\n" +"Language: pt\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.17\n" + +#. module: product_code_unique +#: model:ir.model.constraint,message:product_code_unique.constraint_product_product_default_code_uniq +msgid "Internal Reference must be unique across the database!" +msgstr "A Referência Interna tem que ser única na base de dados!" + +#. module: product_code_unique +#: model:ir.model,name:product_code_unique.model_product_product +msgid "Product Variant" +msgstr "Variante de Produto" diff --git a/product_code_unique/i18n/sk.po b/product_code_unique/i18n/sk.po new file mode 100644 index 00000000000..1e10acc1b6d --- /dev/null +++ b/product_code_unique/i18n/sk.po @@ -0,0 +1,27 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_code_unique +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-09-02 09:12+0000\n" +"Last-Translator: Jan Prokop \n" +"Language-Team: none\n" +"Language: sk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" +"X-Generator: Weblate 5.10.4\n" + +#. module: product_code_unique +#: model:ir.model.constraint,message:product_code_unique.constraint_product_product_default_code_uniq +msgid "Internal Reference must be unique across the database!" +msgstr "Interná referencia musí byť jednoznačná v celej databáze!" + +#. module: product_code_unique +#: model:ir.model,name:product_code_unique.model_product_product +msgid "Product Variant" +msgstr "Variant produktu" diff --git a/product_code_unique/models/__init__.py b/product_code_unique/models/__init__.py new file mode 100644 index 00000000000..2c3319d8df1 --- /dev/null +++ b/product_code_unique/models/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import product diff --git a/product_code_unique/models/product.py b/product_code_unique/models/product.py new file mode 100644 index 00000000000..d97ed1e3add --- /dev/null +++ b/product_code_unique/models/product.py @@ -0,0 +1,13 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class ProductProduct(models.Model): + _inherit = "product.product" + + _default_code_uniq = models.Constraint( + "unique(default_code)", + "Internal Reference must be unique across the database!", + ) diff --git a/product_code_unique/pyproject.toml b/product_code_unique/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/product_code_unique/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/product_code_unique/readme/CONTRIBUTORS.md b/product_code_unique/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..76a80836d7f --- /dev/null +++ b/product_code_unique/readme/CONTRIBUTORS.md @@ -0,0 +1,4 @@ +- Antonio Yamuta \<\> +- Raf Ven \<\> +- Watthanun Khorchai \<\> +- Nedas Žilinskas \<\> diff --git a/product_code_unique/readme/DESCRIPTION.md b/product_code_unique/readme/DESCRIPTION.md new file mode 100644 index 00000000000..a9cbc2adc0d --- /dev/null +++ b/product_code_unique/readme/DESCRIPTION.md @@ -0,0 +1,2 @@ +This module adds a constraint on the internal reference of the product +to make it unique across the database. diff --git a/product_code_unique/readme/ROADMAP.md b/product_code_unique/readme/ROADMAP.md new file mode 100644 index 00000000000..b06fa45223c --- /dev/null +++ b/product_code_unique/readme/ROADMAP.md @@ -0,0 +1,4 @@ +- Avoid duplicate warnings. Odoo has a warning for duplicate "Internal + Reference" of its own (it doesn't block from saving). Now both + warnings are displayed when trying to save a duplicate "Internal + Reference". diff --git a/product_code_unique/readme/USAGE.md b/product_code_unique/readme/USAGE.md new file mode 100644 index 00000000000..2aad2441583 --- /dev/null +++ b/product_code_unique/readme/USAGE.md @@ -0,0 +1,5 @@ +- Unable to save a product when a new internal reference or default_code + value is the same with an existing record. +- A pre_init_hook process is initiated when there exist records without + an internal reference(default_code). A default value is generated to + populate empty field as a temporary value. diff --git a/product_code_unique/static/description/icon.png b/product_code_unique/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/product_code_unique/static/description/icon.png differ diff --git a/product_code_unique/static/description/index.html b/product_code_unique/static/description/index.html new file mode 100644 index 00000000000..fa3b44887da --- /dev/null +++ b/product_code_unique/static/description/index.html @@ -0,0 +1,454 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Unique Product Internal Reference

+ +

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

+

This module adds a constraint on the internal reference of the product +to make it unique across the database.

+

Table of contents

+ +
+

Usage

+
    +
  • Unable to save a product when a new internal reference or default_code +value is the same with an existing record.
  • +
  • A pre_init_hook process is initiated when there exist records without +an internal reference(default_code). A default value is generated to +populate empty field as a temporary value.
  • +
+
+
+

Known issues / Roadmap

+
    +
  • Avoid duplicate warnings. Odoo has a warning for duplicate “Internal +Reference” of its own (it doesn’t block from saving). Now both +warnings are displayed when trying to save a duplicate “Internal +Reference”.
  • +
+
+
+

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

+
    +
  • Open Source Integrators
  • +
+
+
+

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_code_unique/tests/__init__.py b/product_code_unique/tests/__init__.py new file mode 100644 index 00000000000..424cd39e07a --- /dev/null +++ b/product_code_unique/tests/__init__.py @@ -0,0 +1 @@ +from . import test_code_unique diff --git a/product_code_unique/tests/test_code_unique.py b/product_code_unique/tests/test_code_unique.py new file mode 100644 index 00000000000..11c4343be1d --- /dev/null +++ b/product_code_unique/tests/test_code_unique.py @@ -0,0 +1,31 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import psycopg2 + +from odoo.tests.common import TransactionCase +from odoo.tools.misc import mute_logger + +from odoo.addons.base.tests.common import DISABLED_MAIL_CONTEXT + + +class TestCodeUnique(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, **DISABLED_MAIL_CONTEXT)) + cls.product_obj = cls.env["product.product"] + cls.product1 = cls.product_obj.create( + {"name": "Test Product 1", "default_code": "TSTP1"} + ) + + def test_01_check_code_other(self): + self.product2 = self.product_obj.create( + {"name": "Test Product 2", "default_code": "TSTP2"} + ) + + def test_02_check_code_unique(self): + with self.assertRaises(psycopg2.IntegrityError): + with mute_logger("odoo.sql_db"), self.cr.savepoint(): + self.product2 = self.product_obj.create( + {"name": "Test Product 2", "default_code": "TSTP1"} + ) diff --git a/product_company_default/README.rst b/product_company_default/README.rst new file mode 100644 index 00000000000..766323dc6a4 --- /dev/null +++ b/product_company_default/README.rst @@ -0,0 +1,76 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +======================= +Product Company Default +======================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:b862939b839d32b8d5f756be1dc6d90acf9c6dc3eaaf883817075f19a659fbca + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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 + :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_company_default + :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_company_default + :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 module populates the company field with user's company as the +default value when creating new products. + +**Table of contents** + +.. contents:: + :local: + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Quartile Limited + +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_company_default/__init__.py b/product_company_default/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/product_company_default/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/product_company_default/__manifest__.py b/product_company_default/__manifest__.py new file mode 100644 index 00000000000..4df00a5bae9 --- /dev/null +++ b/product_company_default/__manifest__.py @@ -0,0 +1,13 @@ +# Copyright 2023 Quartile Limited +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Product Company Default", + "version": "19.0.1.0.0", + "author": "Quartile Limited, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/product-attribute", + "depends": ["product"], + "license": "AGPL-3", + "data": ["data/ir_config_parameter.xml"], + "demo": ["demo/demo_ir_config_parameter.xml"], + "installable": True, +} diff --git a/product_company_default/data/ir_config_parameter.xml b/product_company_default/data/ir_config_parameter.xml new file mode 100644 index 00000000000..066fb1c58dc --- /dev/null +++ b/product_company_default/data/ir_config_parameter.xml @@ -0,0 +1,9 @@ + + + + + product_company_default.default_company_enable + 1 + + + diff --git a/product_company_default/demo/demo_ir_config_parameter.xml b/product_company_default/demo/demo_ir_config_parameter.xml new file mode 100644 index 00000000000..1705c80091f --- /dev/null +++ b/product_company_default/demo/demo_ir_config_parameter.xml @@ -0,0 +1,9 @@ + + + + + product_company_default.default_company_enable + 0 + + + diff --git a/product_company_default/i18n/it.po b/product_company_default/i18n/it.po new file mode 100644 index 00000000000..d27c025c79d --- /dev/null +++ b/product_company_default/i18n/it.po @@ -0,0 +1,28 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_company_default +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-11-07 09:39+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 4.17\n" + +#. module: product_company_default +#: model:ir.model.fields,field_description:product_company_default.field_product_product__company_id +#: model:ir.model.fields,field_description:product_company_default.field_product_template__company_id +msgid "Company" +msgstr "Azienda" + +#. module: product_company_default +#: model:ir.model,name:product_company_default.model_product_template +msgid "Product" +msgstr "Prodotto" diff --git a/product_company_default/i18n/product_company_default.pot b/product_company_default/i18n/product_company_default.pot new file mode 100644 index 00000000000..5081df6c3aa --- /dev/null +++ b/product_company_default/i18n/product_company_default.pot @@ -0,0 +1,35 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_company_default +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.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_company_default +#: model:ir.model.fields,field_description:product_company_default.field_product_product__company_id +#: model:ir.model.fields,field_description:product_company_default.field_product_template__company_id +msgid "Company" +msgstr "" + +#. module: product_company_default +#: model:ir.model.fields,field_description:product_company_default.field_product_template__display_name +msgid "Display Name" +msgstr "" + +#. module: product_company_default +#: model:ir.model.fields,field_description:product_company_default.field_product_template__id +msgid "ID" +msgstr "" + +#. module: product_company_default +#: model:ir.model,name:product_company_default.model_product_template +msgid "Product" +msgstr "" diff --git a/product_company_default/models/__init__.py b/product_company_default/models/__init__.py new file mode 100644 index 00000000000..e8fa8f6bf1e --- /dev/null +++ b/product_company_default/models/__init__.py @@ -0,0 +1 @@ +from . import product_template diff --git a/product_company_default/models/product_template.py b/product_company_default/models/product_template.py new file mode 100644 index 00000000000..fab5f479189 --- /dev/null +++ b/product_company_default/models/product_template.py @@ -0,0 +1,22 @@ +# Copyright 2023 Quartile Limited +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + company_id = fields.Many2one(default=lambda self: self._default_company_id()) + + @api.model + def _default_company_id(self): + # Get the system parameter configuration + param = ( + self.env["ir.config_parameter"] + .sudo() + .get_param("product_company_default.default_company_enable") + ) + if param == "1": + return self.env.company + return False diff --git a/product_company_default/pyproject.toml b/product_company_default/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/product_company_default/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/product_company_default/readme/DESCRIPTION.md b/product_company_default/readme/DESCRIPTION.md new file mode 100644 index 00000000000..08837b27c19 --- /dev/null +++ b/product_company_default/readme/DESCRIPTION.md @@ -0,0 +1,2 @@ +This module populates the company field with user's company as the +default value when creating new products. diff --git a/product_company_default/static/description/icon.png b/product_company_default/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/product_company_default/static/description/icon.png differ diff --git a/product_company_default/static/description/index.html b/product_company_default/static/description/index.html new file mode 100644 index 00000000000..cca526a2fab --- /dev/null +++ b/product_company_default/static/description/index.html @@ -0,0 +1,423 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Product Company Default

+ +

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

+

This module populates the company field with user’s company as the +default value when creating new products.

+

Table of contents

+ +
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Quartile Limited
  • +
+
+
+

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_company_default/tests/__init__.py b/product_company_default/tests/__init__.py new file mode 100644 index 00000000000..c1bcf9f65ae --- /dev/null +++ b/product_company_default/tests/__init__.py @@ -0,0 +1 @@ +from . import test_product_company_default diff --git a/product_company_default/tests/test_product_company_default.py b/product_company_default/tests/test_product_company_default.py new file mode 100644 index 00000000000..807d72ef31f --- /dev/null +++ b/product_company_default/tests/test_product_company_default.py @@ -0,0 +1,16 @@ +from odoo.addons.base.tests.common import BaseCommon + + +class TestProductCompanyDefault(BaseCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + # Enable the configuration using ir.config_parameter + cls.env["ir.config_parameter"].sudo().set_param( + "product_company_default.default_company_enable", "1" + ) + + def test_product_company(self): + # Create a sample product + product = self.env["product.product"].create({"name": "Test Product"}) + self.assertEqual(product.company_id, self.env.company) diff --git a/product_net_weight/README.rst b/product_net_weight/README.rst new file mode 100644 index 00000000000..882e50f7668 --- /dev/null +++ b/product_net_weight/README.rst @@ -0,0 +1,118 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +===================== +Products - Net Weight +===================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:c3f19cb07b960ac9c212abcfc24c98bfd63994b5f92649817c42a47e3f1d9237 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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 + :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_net_weight + :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_net_weight + :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 module extends the functionality of stock module to support net +weight. (container excluded) + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +- Go to 'Inventory > Master Data > Product' and edit items. +- Go to 'Inventory' tab, and fill the "Net Weight" value. + +|image1| + +**Note**: The original weight field is renamed to "Gross Weight", to be +more explicit. + +.. |image1| image:: https://raw.githubusercontent.com/OCA/product-attribute/19.0/product_net_weight/static/description/product_form.png + +Known issues / Roadmap +====================== + +In futur version (> 12.0), allow to compute Weight Price (Net weight / +Price). For that purpose, refactor with ``product_logistics_uom``. + +Ref : +https://github.com/OCA/product-attribute/pull/894#issuecomment-895930887 + +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 +------- + +* GRAP + +Contributors +------------ + +- Sylvain LE GAL (https://www.twitter.com/legalsylvain) +- `Greenice `__: + + - Fernando La Chica + +- `Tecnativa `__: + + - 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. + +.. |maintainer-legalsylvain| image:: https://github.com/legalsylvain.png?size=40px + :target: https://github.com/legalsylvain + :alt: legalsylvain + +Current `maintainer `__: + +|maintainer-legalsylvain| + +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_net_weight/__init__.py b/product_net_weight/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/product_net_weight/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/product_net_weight/__manifest__.py b/product_net_weight/__manifest__.py new file mode 100644 index 00000000000..d2681753895 --- /dev/null +++ b/product_net_weight/__manifest__.py @@ -0,0 +1,25 @@ +# Copyright (C) 2021 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Products - Net Weight", + "summary": "Add 'Net Weight' on product models", + "version": "19.0.1.0.1", + "category": "Product", + "author": "GRAP,Odoo Community Association (OCA)", + "maintainers": ["legalsylvain"], + "website": "https://github.com/OCA/product-attribute", + "license": "AGPL-3", + "depends": ["stock"], + "data": [ + "views/view_product_product.xml", + "views/view_product_template.xml", + ], + "demo": [ + "demo/product_product.xml", + ], + "images": [ + "static/description/product_product_form.png", + ], + "installable": True, +} diff --git a/product_net_weight/demo/product_product.xml b/product_net_weight/demo/product_product.xml new file mode 100644 index 00000000000..d6c3a94c53e --- /dev/null +++ b/product_net_weight/demo/product_product.xml @@ -0,0 +1,17 @@ + + + + + Strawberry Jam (Jar of 250 grams) + + + 0.250 + 0.410 + 1.97 + 2.70 + + diff --git a/product_net_weight/i18n/es.po b/product_net_weight/i18n/es.po new file mode 100644 index 00000000000..f8e444cde2b --- /dev/null +++ b/product_net_weight/i18n/es.po @@ -0,0 +1,58 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_net_weight +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-03-02 08:36+0000\n" +"PO-Revision-Date: 2023-08-29 08:30+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: \n" +"Language: es\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.17\n" + +#. module: product_net_weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_product__weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_template__weight +msgid "Gross Weight" +msgstr "Peso bruto" + +#. module: product_net_weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_product__net_weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_template__net_weight +msgid "Net Weight" +msgstr "Peso neto" + +#. module: product_net_weight +#: model:ir.model.fields,help:product_net_weight.field_product_product__net_weight +#: model:ir.model.fields,help:product_net_weight.field_product_template__net_weight +msgid "Net Weight of the product, container excluded." +msgstr "Peso neto del producto, contenedor excluido." + +#. module: product_net_weight +#: model:ir.model,name:product_net_weight.model_product_template +msgid "Product" +msgstr "Producto" + +#. module: product_net_weight +#: model:ir.model,name:product_net_weight.model_product_product +msgid "Product Variant" +msgstr "Variantes de producto" + +#. module: product_net_weight +#: model:product.template,name:product_net_weight.product_strawberry_jam_product_template +msgid "Strawberry Jam (Jar of 250 grams)" +msgstr "Mermelada de fresa (tarro de 250 gramos)" + +#. module: product_net_weight +#. odoo-python +#: code:addons/product_net_weight/models/product_product.py:0 +#, python-format +msgid "The net weight of product must be lower than gross weight." +msgstr "El peso neto tiene que ser menor que el peso bruto." diff --git a/product_net_weight/i18n/fr.po b/product_net_weight/i18n/fr.po new file mode 100644 index 00000000000..4f2d5913f77 --- /dev/null +++ b/product_net_weight/i18n/fr.po @@ -0,0 +1,75 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_net_weight +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-08-13 18:38+0000\n" +"PO-Revision-Date: 2021-08-13 18:38+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: product_net_weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_product__weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_template__weight +msgid "Gross Weight" +msgstr "Poids brut" + +#. module: product_net_weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_product__net_weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_template__net_weight +msgid "Net Weight" +msgstr "Poids net" + +#. module: product_net_weight +#: model:ir.model.fields,help:product_net_weight.field_product_product__net_weight +#: model:ir.model.fields,help:product_net_weight.field_product_template__net_weight +msgid "Net Weight of the product, container excluded." +msgstr "Poids net du produit, contenant exclus." + +#. module: product_net_weight +#: model:ir.model,name:product_net_weight.model_product_template +msgid "Product" +msgstr "Article" + +#. module: product_net_weight +#: model:ir.model,name:product_net_weight.model_product_product +msgid "Product Variant" +msgstr "" + +#. module: product_net_weight +#: model:product.template,name:product_net_weight.product_strawberry_jam_product_template +msgid "Strawberry Jam (Jar of 250 grams)" +msgstr "Confiture de fraise (Pot de 250 grammes)" + +#. module: product_net_weight +#. odoo-python +#: code:addons/product_net_weight/models/product_product.py:0 +#, python-format +msgid "The net weight of product must be lower than gross weight." +msgstr "" + +#~ msgid "Product Template" +#~ msgstr "Modèle d'article" + +#, fuzzy +#~ msgid "The weight of the contents, not including any packaging, etc." +#~ msgstr "" +#~ "Le poids du contenu en kilogrammes, sans compter tout emballage, etc." + +#~ msgid "Unit(s)" +#~ msgstr "Unité(s)" + +#~ msgid "" +#~ "Weight of the product, packaging not included. The unit of measure can be " +#~ "changed in the general settings" +#~ msgstr "" +#~ "Poids du produit, sans le conditionnement. L'unité de mesure peut être " +#~ "changée dans les paramètres généraux." diff --git a/product_net_weight/i18n/it.po b/product_net_weight/i18n/it.po new file mode 100644 index 00000000000..2dd591c532f --- /dev/null +++ b/product_net_weight/i18n/it.po @@ -0,0 +1,57 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_net_weight +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-10-29 15:40+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 4.17\n" + +#. module: product_net_weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_product__weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_template__weight +msgid "Gross Weight" +msgstr "Peso lordo" + +#. module: product_net_weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_product__net_weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_template__net_weight +msgid "Net Weight" +msgstr "Peso netto" + +#. module: product_net_weight +#: model:ir.model.fields,help:product_net_weight.field_product_product__net_weight +#: model:ir.model.fields,help:product_net_weight.field_product_template__net_weight +msgid "Net Weight of the product, container excluded." +msgstr "Peso netto del prodotto, escluso contenitore." + +#. module: product_net_weight +#: model:ir.model,name:product_net_weight.model_product_template +msgid "Product" +msgstr "Prodotto" + +#. module: product_net_weight +#: model:ir.model,name:product_net_weight.model_product_product +msgid "Product Variant" +msgstr "Variante prodotto" + +#. module: product_net_weight +#: model:product.template,name:product_net_weight.product_strawberry_jam_product_template +msgid "Strawberry Jam (Jar of 250 grams)" +msgstr "Marmellata di fragole (vasetto da 250 grammi)" + +#. module: product_net_weight +#. odoo-python +#: code:addons/product_net_weight/models/product_product.py:0 +#, python-format +msgid "The net weight of product must be lower than gross weight." +msgstr "Il peso netto del prodotto deve essere inferiore al peso lordo." diff --git a/product_net_weight/i18n/product_net_weight.pot b/product_net_weight/i18n/product_net_weight.pot new file mode 100644 index 00000000000..5ee66ec09e7 --- /dev/null +++ b/product_net_weight/i18n/product_net_weight.pot @@ -0,0 +1,66 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_net_weight +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.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_net_weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_product__display_name +#: model:ir.model.fields,field_description:product_net_weight.field_product_template__display_name +msgid "Display Name" +msgstr "" + +#. module: product_net_weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_product__weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_template__weight +msgid "Gross Weight" +msgstr "" + +#. module: product_net_weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_product__id +#: model:ir.model.fields,field_description:product_net_weight.field_product_template__id +msgid "ID" +msgstr "" + +#. module: product_net_weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_product__net_weight +#: model:ir.model.fields,field_description:product_net_weight.field_product_template__net_weight +msgid "Net Weight" +msgstr "" + +#. module: product_net_weight +#: model:ir.model,name:product_net_weight.model_product_template +msgid "Product" +msgstr "" + +#. module: product_net_weight +#: model:ir.model,name:product_net_weight.model_product_product +msgid "Product Variant" +msgstr "" + +#. module: product_net_weight +#. odoo-python +#: code:addons/product_net_weight/models/product_product.py:0 +msgid "The net weight of product '%s' must be lower than gross weight." +msgstr "" + +#. module: product_net_weight +#: model:ir.model.fields,help:product_net_weight.field_product_product__weight +#: model:ir.model.fields,help:product_net_weight.field_product_template__weight +msgid "Weight of the product with its container and packaging." +msgstr "" + +#. module: product_net_weight +#: model:ir.model.fields,help:product_net_weight.field_product_product__net_weight +#: model:ir.model.fields,help:product_net_weight.field_product_template__net_weight +msgid "Weight of the product without container nor packaging." +msgstr "" diff --git a/product_net_weight/models/__init__.py b/product_net_weight/models/__init__.py new file mode 100644 index 00000000000..18b37e85320 --- /dev/null +++ b/product_net_weight/models/__init__.py @@ -0,0 +1,2 @@ +from . import product_product +from . import product_template diff --git a/product_net_weight/models/product_product.py b/product_net_weight/models/product_product.py new file mode 100644 index 00000000000..eb89bc687b5 --- /dev/null +++ b/product_net_weight/models/product_product.py @@ -0,0 +1,41 @@ +# Copyright (C) 2021 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models +from odoo.exceptions import ValidationError +from odoo.tools import float_compare, float_is_zero + + +class ProductProduct(models.Model): + _inherit = "product.product" + + net_weight = fields.Float( + digits="Stock Weight", + help="Weight of the product without container nor packaging.", + ) + + # Explicit field, renaming it + weight = fields.Float( + string="Gross Weight", + help="Weight of the product with its container and packaging.", + ) + + @api.constrains("net_weight", "weight") + def _check_net_weight(self): + prec = self.env["decimal.precision"].precision_get("Stock Weight") + for product in self: + if ( + not float_is_zero(product.weight, precision_digits=prec) + and float_compare( + product.net_weight, product.weight, precision_digits=prec + ) + > 0 + ): + raise ValidationError( + self.env._( + "The net weight of product '%s' must be lower than " + "gross weight.", + product.display_name, + ) + ) diff --git a/product_net_weight/models/product_template.py b/product_net_weight/models/product_template.py new file mode 100644 index 00000000000..81a284636d0 --- /dev/null +++ b/product_net_weight/models/product_template.py @@ -0,0 +1,40 @@ +# Copyright (C) 2021 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# Copyright 2023 Tecnativa - Sergio Teruel +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + net_weight = fields.Float( + compute="_compute_net_weight", + inverse="_inverse_net_weight", + digits="Stock Weight", + help="Weight of the product without container nor packaging.", + store=True, + ) + + # Explicit field, renaming it + weight = fields.Float( + string="Gross Weight", + help="Weight of the product with its container and packaging.", + ) + + @api.depends("product_variant_ids.net_weight") + def _compute_net_weight(self): + self._compute_template_field_from_variant_field("net_weight") + + def _inverse_net_weight(self): + self._set_product_variant_field("net_weight") + + @api.model_create_multi + def create(self, vals_list): + templates = super().create(vals_list) + # This is needed to set given values to first variant after creation + for template, vals in zip(templates, vals_list, strict=True): + if vals.get("net_weight"): + template.write({"net_weight": vals["net_weight"]}) + return templates diff --git a/product_net_weight/pyproject.toml b/product_net_weight/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/product_net_weight/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/product_net_weight/readme/CONTRIBUTORS.md b/product_net_weight/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..857c72b4a9e --- /dev/null +++ b/product_net_weight/readme/CONTRIBUTORS.md @@ -0,0 +1,5 @@ +- Sylvain LE GAL () +- [Greenice](https://www.greenice.com): + - Fernando La Chica \<\> +- [Tecnativa](https://www.tecnativa.com): + - Sergio Teruel diff --git a/product_net_weight/readme/DESCRIPTION.md b/product_net_weight/readme/DESCRIPTION.md new file mode 100644 index 00000000000..d9e1fde5cc4 --- /dev/null +++ b/product_net_weight/readme/DESCRIPTION.md @@ -0,0 +1,2 @@ +This module extends the functionality of stock module to support net +weight. (container excluded) diff --git a/product_net_weight/readme/ROADMAP.md b/product_net_weight/readme/ROADMAP.md new file mode 100644 index 00000000000..71662ba1a9e --- /dev/null +++ b/product_net_weight/readme/ROADMAP.md @@ -0,0 +1,5 @@ +In futur version (\> 12.0), allow to compute Weight Price (Net weight / +Price). For that purpose, refactor with `product_logistics_uom`. + +Ref : + diff --git a/product_net_weight/readme/USAGE.md b/product_net_weight/readme/USAGE.md new file mode 100644 index 00000000000..a81cea018de --- /dev/null +++ b/product_net_weight/readme/USAGE.md @@ -0,0 +1,7 @@ +- Go to 'Inventory \> Master Data \> Product' and edit items. +- Go to 'Inventory' tab, and fill the "Net Weight" value. + +![](../static/description/product_form.png) + +**Note**: The original weight field is renamed to "Gross Weight", to be +more explicit. diff --git a/product_net_weight/static/description/icon.png b/product_net_weight/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/product_net_weight/static/description/icon.png differ diff --git a/product_net_weight/static/description/index.html b/product_net_weight/static/description/index.html new file mode 100644 index 00000000000..f71e30bdb8b --- /dev/null +++ b/product_net_weight/static/description/index.html @@ -0,0 +1,459 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Products - Net Weight

+ +

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

+

This module extends the functionality of stock module to support net +weight. (container excluded)

+

Table of contents

+ +
+

Usage

+
    +
  • Go to ‘Inventory > Master Data > Product’ and edit items.
  • +
  • Go to ‘Inventory’ tab, and fill the “Net Weight” value.
  • +
+

image1

+

Note: The original weight field is renamed to “Gross Weight”, to be +more explicit.

+
+
+

Known issues / Roadmap

+

In futur version (> 12.0), allow to compute Weight Price (Net weight / +Price). For that purpose, refactor with product_logistics_uom.

+

Ref : +https://github.com/OCA/product-attribute/pull/894#issuecomment-895930887

+
+
+

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

+
    +
  • GRAP
  • +
+
+
+

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.

+

Current maintainer:

+

legalsylvain

+

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_net_weight/static/description/product_form.png b/product_net_weight/static/description/product_form.png new file mode 100644 index 00000000000..f50a84dac09 Binary files /dev/null and b/product_net_weight/static/description/product_form.png differ diff --git a/product_net_weight/tests/__init__.py b/product_net_weight/tests/__init__.py new file mode 100644 index 00000000000..0804ce88209 --- /dev/null +++ b/product_net_weight/tests/__init__.py @@ -0,0 +1,2 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from . import test_product_net_weight diff --git a/product_net_weight/tests/test_product_net_weight.py b/product_net_weight/tests/test_product_net_weight.py new file mode 100644 index 00000000000..17c554dc441 --- /dev/null +++ b/product_net_weight/tests/test_product_net_weight.py @@ -0,0 +1,72 @@ +# Copyright 2023 Tecnativa - Sergio Teruel +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo.exceptions import ValidationError +from odoo.tests import Form + +from odoo.addons.base.tests.common import BaseCommon + + +class TestProductNetWeight(BaseCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.attribute = cls.env["product.attribute"].create( + { + "name": "test attribute", + "display_type": "select", + } + ) + + def test_create_product_template(self): + product_form = Form(self.env["product.template"]) + product_form.name = "Test net weight" + product_form.net_weight = 25.0 + product = product_form.save() + self.assertEqual(product.net_weight, 25.0) + self.assertEqual(product.product_variant_id.net_weight, 25.0) + product.write( + { + "attribute_line_ids": [ + ( + 0, + 0, + { + "attribute_id": self.attribute.id, + "value_ids": [ + ( + 0, + 0, + { + "attribute_id": self.attribute.id, + "name": "test value 1", + }, + ), + ( + 0, + 0, + { + "attribute_id": self.attribute.id, + "name": "test value 2", + }, + ), + ], + }, + ) + ] + } + ) + self.assertEqual(product.net_weight, 0.0) + + def test_create_product_product(self): + product_form = Form(self.env["product.product"]) + product_form.name = "Test net weight" + product_form.net_weight = 25.0 + product = product_form.save() + self.assertEqual(product.net_weight, 25.0) + self.assertEqual(product.product_variant_id.net_weight, 25.0) + + def test_product_constraint(self): + with self.assertRaises(ValidationError): + self.env["product.product"].create( + {"name": "Test net weight", "net_weight": 25.0, "weight": 22.0} + ) diff --git a/product_net_weight/views/view_product_product.xml b/product_net_weight/views/view_product_product.xml new file mode 100644 index 00000000000..6827c1476fa --- /dev/null +++ b/product_net_weight/views/view_product_product.xml @@ -0,0 +1,34 @@ + + + + + product.product + + + + + + + + product.product + + + + + + + diff --git a/product_net_weight/views/view_product_template.xml b/product_net_weight/views/view_product_template.xml new file mode 100644 index 00000000000..40c2e78573c --- /dev/null +++ b/product_net_weight/views/view_product_template.xml @@ -0,0 +1,35 @@ + + + + + product.template + + + + + + + diff --git a/setup/_metapackage/pyproject.toml b/setup/_metapackage/pyproject.toml new file mode 100644 index 00000000000..145ac390655 --- /dev/null +++ b/setup/_metapackage/pyproject.toml @@ -0,0 +1,14 @@ +[project] +name = "odoo-addons-oca-product-attribute" +version = "19.0.20251130.1" +dependencies = [ + "odoo-addon-product_category_code==19.0.*", + "odoo-addon-product_code_unique==19.0.*", + "odoo-addon-product_company_default==19.0.*", + "odoo-addon-product_net_weight==19.0.*", +] +classifiers=[ + "Programming Language :: Python", + "Framework :: Odoo", + "Framework :: Odoo :: 19.0", +]