diff --git a/product_secondary_unit/README.rst b/product_secondary_unit/README.rst new file mode 100644 index 00000000000..74804d27088 --- /dev/null +++ b/product_secondary_unit/README.rst @@ -0,0 +1,94 @@ +====================== +Product Secondary Unit +====================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fproduct--attribute-lightgray.png?logo=github + :target: https://github.com/OCA/product-attribute/tree/15.0/product_secondary_unit + :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-15-0/product-attribute-15-0-product_secondary_unit + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/135/15.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module extends the functionality of product module to allow define other units with their conversion factor. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To use this module you need to: + +#. Go to a *Product > General Information tab*. +#. Create any record in "Secondary unit of measure". +#. Set the conversion factor. + +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 smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Tecnativa + +Contributors +~~~~~~~~~~~~ + +* Carlos Dauden +* Sergio Teruel +* Kitti Upariphutthiphong +* Pimolnat Suntian +* Alan Ramos + +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-sergio-teruel| image:: https://github.com/sergio-teruel.png?size=40px + :target: https://github.com/sergio-teruel + :alt: sergio-teruel + +Current `maintainer `__: + +|maintainer-sergio-teruel| + +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_secondary_unit/__init__.py b/product_secondary_unit/__init__.py new file mode 100644 index 00000000000..3275ac2adf3 --- /dev/null +++ b/product_secondary_unit/__init__.py @@ -0,0 +1,2 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from . import models diff --git a/product_secondary_unit/__manifest__.py b/product_secondary_unit/__manifest__.py new file mode 100644 index 00000000000..2e4dbc1adca --- /dev/null +++ b/product_secondary_unit/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright 2018 Tecnativa - Sergio Teruel +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Product Secondary Unit", + "summary": "Set a secondary unit per product", + "version": "16.0.1.0.0", + "development_status": "Production/Stable", + "category": "Product", + "website": "https://github.com/OCA/product-attribute", + "author": "Tecnativa, Odoo Community Association (OCA)", + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": ["product"], + "data": ["security/ir.model.access.csv", "views/product_views.xml"], + "maintainers": ["sergio-teruel"], +} diff --git a/product_secondary_unit/i18n/es.po b/product_secondary_unit/i18n/es.po new file mode 100644 index 00000000000..909e019097c --- /dev/null +++ b/product_secondary_unit/i18n/es.po @@ -0,0 +1,162 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_secondary_unit +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-01-11 04:38+0000\n" +"PO-Revision-Date: 2023-01-11 05:40+0100\n" +"Last-Translator: Sergio Teruel \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: Poedit 3.0.1\n" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__active +msgid "Active" +msgstr "Activo" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__code +msgid "Code" +msgstr "Código" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__create_date +msgid "Created on" +msgstr "Creado en" + +#. module: product_secondary_unit +#: model:ir.model.fields,help:product_secondary_unit.field_product_product__secondary_uom_ids +#: model:ir.model.fields,help:product_secondary_unit.field_product_secondary_unit__uom_id +#: model:ir.model.fields,help:product_secondary_unit.field_product_template__secondary_uom_ids +msgid "Default Secondary Unit of Measure." +msgstr "Segunda unidad de medida por defecto." + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__dependency_type +msgid "Dependency Type" +msgstr "Tipo de dependencia" + +#. module: product_secondary_unit +#: model:ir.model.fields.selection,name:product_secondary_unit.selection__product_secondary_unit__dependency_type__dependent +msgid "Dependent" +msgstr "Dependiente" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__display_name +msgid "Display Name" +msgstr "Mostrar nombre" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__id +msgid "ID" +msgstr "ID (Identificador)" + +#. module: product_secondary_unit +#: model:ir.model.fields,help:product_secondary_unit.field_product_secondary_unit__dependency_type +msgid "" +"If dependency type is 'dependent' the factor is used to compute quantity in " +"primary unit,otherwise primary and secondary unit are independent. For " +"example if you sell serviceby package (1 unit for example) and you want to " +"put the real time (ex : 4 hours) to allows employee scheduling" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields.selection,name:product_secondary_unit.selection__product_secondary_unit__dependency_type__independent +msgid "Independent" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit____last_update +msgid "Last Modified on" +msgstr "Última modificación en" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__write_uid +msgid "Last Updated by" +msgstr "Última actualización de" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__write_date +msgid "Last Updated on" +msgstr "Última actualización en" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__name +msgid "Name" +msgstr "Nombre" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_product +msgid "Product" +msgstr "Producto" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_secondary_unit +msgid "Product Secondary Unit" +msgstr "Unidad secundaria del producto" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_secondary_unit_mixin +msgid "Product Secondary Unit Mixin" +msgstr "Producto Unidad secundaria Mixin" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_template +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__product_tmpl_id +msgid "Product Template" +msgstr "Plantilla de producto" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__product_id +msgid "Product Variant" +msgstr "Variante de producto" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit_mixin__secondary_uom_id +msgid "Second unit" +msgstr "Unidad secundaria" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit_mixin__secondary_uom_qty +msgid "Secondary Qty" +msgstr "Cta. Ud. secundaria" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__factor +msgid "Secondary Unit Factor" +msgstr "Factor segunda unidad" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_product__secondary_uom_ids +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__uom_id +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_template__secondary_uom_ids +#: model_terms:ir.ui.view,arch_db:product_secondary_unit.product_template_form_view +msgid "Secondary Unit of Measure" +msgstr "Segunda unidad de medida" + +#~ msgid "secondary Unit" +#~ msgstr "Unidad Secundaria" + +#~ msgid "secondary unit of measure" +#~ msgstr "Segunda unidad de medida" + +#~ msgid "Default unit sale" +#~ msgstr "Unidad de venta por defecto" + +#, fuzzy +#~| msgid "Default unit sale" +#~ msgid "Default unit purchase" +#~ msgstr "Unidad de venta por defecto" diff --git a/product_secondary_unit/i18n/es_MX.po b/product_secondary_unit/i18n/es_MX.po new file mode 100644 index 00000000000..fb94cdd6a20 --- /dev/null +++ b/product_secondary_unit/i18n/es_MX.po @@ -0,0 +1,153 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_secondary_unit +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2020-03-11 15:57+0000\n" +"Last-Translator: Jesús Alan Ramos Rodríguez \n" +"Language-Team: none\n" +"Language: es_MX\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 3.10\n" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__active +msgid "Active" +msgstr "Activo" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__code +msgid "Code" +msgstr "Código" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__create_date +msgid "Created on" +msgstr "Creado en" + +#. module: product_secondary_unit +#: model:ir.model.fields,help:product_secondary_unit.field_product_product__secondary_uom_ids +#: model:ir.model.fields,help:product_secondary_unit.field_product_secondary_unit__uom_id +#: model:ir.model.fields,help:product_secondary_unit.field_product_template__secondary_uom_ids +msgid "Default Secondary Unit of Measure." +msgstr "Unidad de Medida Secundaria por Defecto." + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__dependency_type +msgid "Dependency Type" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields.selection,name:product_secondary_unit.selection__product_secondary_unit__dependency_type__dependent +msgid "Dependent" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__display_name +msgid "Display Name" +msgstr "Nombre para Mostrar" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__id +msgid "ID" +msgstr "ID" + +#. module: product_secondary_unit +#: model:ir.model.fields,help:product_secondary_unit.field_product_secondary_unit__dependency_type +msgid "" +"If dependency type is 'dependent' the factor is used to compute quantity in " +"primary unit,otherwise primary and secondary unit are independent. For " +"example if you sell serviceby package (1 unit for example) and you want to " +"put the real time (ex : 4 hours) to allows employee scheduling" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields.selection,name:product_secondary_unit.selection__product_secondary_unit__dependency_type__independent +msgid "Independent" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit____last_update +msgid "Last Modified on" +msgstr "Última Actualización en" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__write_uid +msgid "Last Updated by" +msgstr "Última Actualización por" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__write_date +msgid "Last Updated on" +msgstr "Última Actualización en" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__name +msgid "Name" +msgstr "Nombre" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_product +msgid "Product" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_secondary_unit +msgid "Product Secondary Unit" +msgstr "Unidad de Medida Secundaria del Producto" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_secondary_unit_mixin +msgid "Product Secondary Unit Mixin" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_template +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__product_tmpl_id +msgid "Product Template" +msgstr "Plantilla de Producto" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__product_id +msgid "Product Variant" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit_mixin__secondary_uom_id +msgid "Second unit" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit_mixin__secondary_uom_qty +msgid "Secondary Qty" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__factor +msgid "Secondary Unit Factor" +msgstr "Factor de Unidad Secundaria" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_product__secondary_uom_ids +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__uom_id +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_template__secondary_uom_ids +#: model_terms:ir.ui.view,arch_db:product_secondary_unit.product_template_form_view +msgid "Secondary Unit of Measure" +msgstr "Unidad de Medida Secundaria" + +#~ msgid "secondary Unit" +#~ msgstr "Unidad Secundaria" + +#~ msgid "secondary unit of measure" +#~ msgstr "unidad de medida secundaria" diff --git a/product_secondary_unit/i18n/fr.po b/product_secondary_unit/i18n/fr.po new file mode 100644 index 00000000000..65c8c112e32 --- /dev/null +++ b/product_secondary_unit/i18n/fr.po @@ -0,0 +1,156 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_secondary_unit +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2020-07-22 11:19+0000\n" +"Last-Translator: c2cdidier \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 3.10\n" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__active +msgid "Active" +msgstr "Actif" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__code +msgid "Code" +msgstr "Code" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__create_uid +msgid "Created by" +msgstr "Créé par" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__create_date +msgid "Created on" +msgstr "Créé le" + +#. module: product_secondary_unit +#: model:ir.model.fields,help:product_secondary_unit.field_product_product__secondary_uom_ids +#: model:ir.model.fields,help:product_secondary_unit.field_product_secondary_unit__uom_id +#: model:ir.model.fields,help:product_secondary_unit.field_product_template__secondary_uom_ids +msgid "Default Secondary Unit of Measure." +msgstr "Unité de mesure secondaire par défaut." + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__dependency_type +msgid "Dependency Type" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields.selection,name:product_secondary_unit.selection__product_secondary_unit__dependency_type__dependent +msgid "Dependent" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__display_name +msgid "Display Name" +msgstr "Nom affiché" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__id +msgid "ID" +msgstr "ID" + +#. module: product_secondary_unit +#: model:ir.model.fields,help:product_secondary_unit.field_product_secondary_unit__dependency_type +msgid "" +"If dependency type is 'dependent' the factor is used to compute quantity in " +"primary unit,otherwise primary and secondary unit are independent. For " +"example if you sell serviceby package (1 unit for example) and you want to " +"put the real time (ex : 4 hours) to allows employee scheduling" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields.selection,name:product_secondary_unit.selection__product_secondary_unit__dependency_type__independent +msgid "Independent" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit____last_update +msgid "Last Modified on" +msgstr "Dernière Modification le" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__write_uid +msgid "Last Updated by" +msgstr "Dernière mise à jour par" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__write_date +msgid "Last Updated on" +msgstr "Dernière mise à jour le" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__name +msgid "Name" +msgstr "Nom" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_product +msgid "Product" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_secondary_unit +msgid "Product Secondary Unit" +msgstr "Unité produit secondaire" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_secondary_unit_mixin +msgid "Product Secondary Unit Mixin" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_template +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__product_tmpl_id +msgid "Product Template" +msgstr "Modèle de produit" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__product_id +msgid "Product Variant" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit_mixin__secondary_uom_id +msgid "Second unit" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit_mixin__secondary_uom_qty +msgid "Secondary Qty" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__factor +msgid "Secondary Unit Factor" +msgstr "Facteur de l'unité secondaire" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_product__secondary_uom_ids +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__uom_id +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_template__secondary_uom_ids +#: model_terms:ir.ui.view,arch_db:product_secondary_unit.product_template_form_view +msgid "Secondary Unit of Measure" +msgstr "Unité de mesure secondaire" + +#~ msgid "secondary Unit" +#~ msgstr "Unité secondaire" + +#~ msgid "secondary unit of measure" +#~ msgstr "Unité de mesure secondaire" + +#~ msgid "Default unit sale" +#~ msgstr "Unité de vente par défaut" diff --git a/product_secondary_unit/i18n/product_secondary_unit.pot b/product_secondary_unit/i18n/product_secondary_unit.pot new file mode 100644 index 00000000000..63178a96989 --- /dev/null +++ b/product_secondary_unit/i18n/product_secondary_unit.pot @@ -0,0 +1,144 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_secondary_unit +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.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_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__active +msgid "Active" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__code +msgid "Code" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__create_uid +msgid "Created by" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__create_date +msgid "Created on" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,help:product_secondary_unit.field_product_product__secondary_uom_ids +#: model:ir.model.fields,help:product_secondary_unit.field_product_secondary_unit__uom_id +#: model:ir.model.fields,help:product_secondary_unit.field_product_template__secondary_uom_ids +msgid "Default Secondary Unit of Measure." +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__dependency_type +msgid "Dependency Type" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields.selection,name:product_secondary_unit.selection__product_secondary_unit__dependency_type__dependent +msgid "Dependent" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__display_name +msgid "Display Name" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__id +msgid "ID" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,help:product_secondary_unit.field_product_secondary_unit__dependency_type +msgid "" +"If dependency type is 'dependent' the factor is used to compute quantity in " +"primary unit,otherwise primary and secondary unit are independent. For " +"example if you sell serviceby package (1 unit for example) and you want to " +"put the real time (ex : 4 hours) to allows employee scheduling" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields.selection,name:product_secondary_unit.selection__product_secondary_unit__dependency_type__independent +msgid "Independent" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit____last_update +msgid "Last Modified on" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__write_date +msgid "Last Updated on" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__name +msgid "Name" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_product +msgid "Product" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_secondary_unit +msgid "Product Secondary Unit" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_secondary_unit_mixin +msgid "Product Secondary Unit Mixin" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_template +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__product_tmpl_id +msgid "Product Template" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__product_id +msgid "Product Variant" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit_mixin__secondary_uom_id +msgid "Second unit" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit_mixin__secondary_uom_qty +msgid "Secondary Qty" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__factor +msgid "Secondary Unit Factor" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_product__secondary_uom_ids +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__uom_id +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_template__secondary_uom_ids +#: model_terms:ir.ui.view,arch_db:product_secondary_unit.product_template_form_view +msgid "Secondary Unit of Measure" +msgstr "" diff --git a/product_secondary_unit/i18n/zh_CN.po b/product_secondary_unit/i18n/zh_CN.po new file mode 100644 index 00000000000..a1b83146ab1 --- /dev/null +++ b/product_secondary_unit/i18n/zh_CN.po @@ -0,0 +1,153 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_secondary_unit +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2019-10-21 13:32+0000\n" +"Last-Translator: Tony Gu \n" +"Language-Team: none\n" +"Language: zh_CN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 3.8\n" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__active +msgid "Active" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__code +msgid "Code" +msgstr "编码" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__create_uid +msgid "Created by" +msgstr "创建者" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__create_date +msgid "Created on" +msgstr "创建于" + +#. module: product_secondary_unit +#: model:ir.model.fields,help:product_secondary_unit.field_product_product__secondary_uom_ids +#: model:ir.model.fields,help:product_secondary_unit.field_product_secondary_unit__uom_id +#: model:ir.model.fields,help:product_secondary_unit.field_product_template__secondary_uom_ids +msgid "Default Secondary Unit of Measure." +msgstr "默认辅助计量单位。" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__dependency_type +msgid "Dependency Type" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields.selection,name:product_secondary_unit.selection__product_secondary_unit__dependency_type__dependent +msgid "Dependent" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__display_name +msgid "Display Name" +msgstr "显示名称" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__id +msgid "ID" +msgstr "ID" + +#. module: product_secondary_unit +#: model:ir.model.fields,help:product_secondary_unit.field_product_secondary_unit__dependency_type +msgid "" +"If dependency type is 'dependent' the factor is used to compute quantity in " +"primary unit,otherwise primary and secondary unit are independent. For " +"example if you sell serviceby package (1 unit for example) and you want to " +"put the real time (ex : 4 hours) to allows employee scheduling" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields.selection,name:product_secondary_unit.selection__product_secondary_unit__dependency_type__independent +msgid "Independent" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit____last_update +msgid "Last Modified on" +msgstr "最后修改于" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__write_uid +msgid "Last Updated by" +msgstr "最后更新者" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__write_date +msgid "Last Updated on" +msgstr "最后更新于" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__name +msgid "Name" +msgstr "名称" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_product +msgid "Product" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_secondary_unit +msgid "Product Secondary Unit" +msgstr "产品辅助计量单位" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_secondary_unit_mixin +msgid "Product Secondary Unit Mixin" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model,name:product_secondary_unit.model_product_template +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__product_tmpl_id +msgid "Product Template" +msgstr "产品模版" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__product_id +msgid "Product Variant" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit_mixin__secondary_uom_id +msgid "Second unit" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit_mixin__secondary_uom_qty +msgid "Secondary Qty" +msgstr "" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__factor +msgid "Secondary Unit Factor" +msgstr "转换率" + +#. module: product_secondary_unit +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_product__secondary_uom_ids +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_secondary_unit__uom_id +#: model:ir.model.fields,field_description:product_secondary_unit.field_product_template__secondary_uom_ids +#: model_terms:ir.ui.view,arch_db:product_secondary_unit.product_template_form_view +msgid "Secondary Unit of Measure" +msgstr "辅助计量单位" + +#~ msgid "secondary Unit" +#~ msgstr "辅助计量单位" + +#~ msgid "secondary unit of measure" +#~ msgstr "辅助计量单位" diff --git a/product_secondary_unit/models/__init__.py b/product_secondary_unit/models/__init__.py new file mode 100644 index 00000000000..0f06812747f --- /dev/null +++ b/product_secondary_unit/models/__init__.py @@ -0,0 +1,5 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from . import product_product +from . import product_second_unit +from . import product_secondary_unit_mixin +from . import product_template diff --git a/product_secondary_unit/models/product_product.py b/product_secondary_unit/models/product_product.py new file mode 100644 index 00000000000..5e6e67d55db --- /dev/null +++ b/product_secondary_unit/models/product_product.py @@ -0,0 +1,35 @@ +# Copyright 2023 Tecnativa - Sergio Teruel +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import api, fields, models + + +class ProductProduct(models.Model): + _inherit = "product.product" + + secondary_uom_ids = fields.One2many( + comodel_name="product.secondary.unit", + inverse_name="product_id", + string="Secondary Unit of Measure", + help="Default Secondary Unit of Measure.", + context={"active_test": False}, + compute="_compute_secondary_uom_ids", + inverse="_inverse_secondary_uom_ids", + ) + + @api.depends("product_tmpl_id") + def _compute_secondary_uom_ids(self): + for variant in self: + variant.secondary_uom_ids = ( + variant.product_tmpl_id.secondary_uom_ids.filtered( + lambda s: s.product_id == variant or not s.product_id + ) + ) + + def _inverse_secondary_uom_ids(self): + for variant in self: + variant.product_tmpl_id.secondary_uom_ids = ( + variant.product_tmpl_id.secondary_uom_ids.filtered( + lambda s: s.product_id != variant + ) + + variant.secondary_uom_ids + ) diff --git a/product_secondary_unit/models/product_second_unit.py b/product_secondary_unit/models/product_second_unit.py new file mode 100644 index 00000000000..47a758b7dc5 --- /dev/null +++ b/product_secondary_unit/models/product_second_unit.py @@ -0,0 +1,67 @@ +# Copyright 2018 Tecnativa - Sergio Teruel +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import api, fields, models + + +class ProductSecondaryUnit(models.Model): + _name = "product.secondary.unit" + _description = "Product Secondary Unit" + + name = fields.Char(required=True, translate=True) + code = fields.Char() + product_tmpl_id = fields.Many2one( + comodel_name="product.template", + string="Product Template", + required=True, + ondelete="cascade", + ) + product_id = fields.Many2one( + comodel_name="product.product", + string="Product Variant", + ondelete="cascade", + ) + uom_id = fields.Many2one( + comodel_name="uom.uom", + string="Secondary Unit of Measure", + required=True, + help="Default Secondary Unit of Measure.", + ) + dependency_type = fields.Selection( + selection=[ + ("dependent", "Dependent"), + ("independent", "Independent"), + ], + default="dependent", + help="If dependency type is 'dependent' the factor is used " + "to compute quantity in primary unit," + "otherwise primary and secondary unit are independent. " + "For example if you sell service" + "by package (1 unit for example) and you want to put the " + "real time (ex : 4 hours) to allows employee scheduling", + ) + factor = fields.Float(string="Secondary Unit Factor", default=1.0, required=True) + active = fields.Boolean(default=True) + + def name_get(self): + result = [] + for unit in self: + result.append( + ( + unit.id, + "{unit_name}-{factor}".format( + unit_name=unit.name, factor=unit.factor + ), + ) + ) + return result + + @api.model + def name_search(self, name="", args=None, operator="ilike", limit=100): + if args is None: + args = [] + units = self.search([("code", "=", name)] + args, limit=1) + if not units: + return super(ProductSecondaryUnit, self).name_search( + name=name, args=args, operator=operator, limit=limit + ) + return units.name_get() diff --git a/product_secondary_unit/models/product_secondary_unit_mixin.py b/product_secondary_unit/models/product_secondary_unit_mixin.py new file mode 100644 index 00000000000..e2feac544d3 --- /dev/null +++ b/product_secondary_unit/models/product_secondary_unit_mixin.py @@ -0,0 +1,141 @@ +# Copyright 2021 Tecnativa - Sergio Teruel +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import api, fields, models +from odoo.tools.float_utils import float_round + + +class ProductSecondaryUnitMixin(models.AbstractModel): + """ + Mixin model that allows to compute a field from a secondary unit helper + An example is to extend any model in which you want to compute quantities + based on secondary units. You must add a dictionary `_secondary_unit_fields` + as class variable with the following content: + _secondary_unit_fields = { + "qty_field": "product_uom_qty", + "uom_field": "product_uom" + } + + To compute ``qty_field`` on target model, you must convert the field to computed + writable (computed, stored and readonly=False), and you have to define the + compute method adding ``secondary_uom_id`` and ``secondary_uom_qty`` fields + as dependencies and calling inside to ``self._compute_helper_target_field_qty()``. + + To compute secondary units when user changes the uom field on target model, + you must add an onchange method on uom field and call to + ``self._onchange_helper_product_uom_for_secondary()`` + + You can see an example in ``purchase_order_secondary_unit`` on purchase-workflow + repository. + """ + + _name = "product.secondary.unit.mixin" + _description = "Product Secondary Unit Mixin" + _secondary_unit_fields = {} + + @api.model + def _get_default_secondary_uom(self): + return self.env["product.template"]._get_default_secondary_uom() + + secondary_uom_qty = fields.Float( + string="Secondary Qty", + digits="Product Unit of Measure", + store=True, + readonly=False, + compute="_compute_secondary_uom_qty", + ) + secondary_uom_id = fields.Many2one( + comodel_name="product.secondary.unit", + string="Second unit", + ondelete="restrict", + default=_get_default_secondary_uom, + ) + + def _get_uom_line(self): + return self[self._secondary_unit_fields["uom_field"]] + + def _get_factor_line(self): + return self.secondary_uom_id.factor * self._get_uom_line().factor + + def _get_quantity_from_line(self): + return self[self._secondary_unit_fields["qty_field"]] + + @api.model + def _get_secondary_uom_qty_depends(self): + if not self._secondary_unit_fields: + return [] + return [self._secondary_unit_fields["qty_field"]] + + @api.depends(lambda x: x._get_secondary_uom_qty_depends()) + def _compute_secondary_uom_qty(self): + for line in self: + if not line.secondary_uom_id: + line.secondary_uom_qty = 0.0 + continue + elif line.secondary_uom_id.dependency_type == "independent": + continue + factor = line._get_factor_line() + qty_line = line._get_quantity_from_line() + qty = float_round( + qty_line / (factor or 1.0), + precision_rounding=line.secondary_uom_id.uom_id.rounding, + ) + line.secondary_uom_qty = qty + + def _get_default_value_for_qty_field(self): + return self.default_get([self._secondary_unit_fields["qty_field"]]).get( + self._secondary_unit_fields["qty_field"] + ) + + def _compute_helper_target_field_qty(self): + """Set the target qty field defined in model""" + default_qty_field_value = self._get_default_value_for_qty_field() + for rec in self: + if not rec.secondary_uom_id: + rec[rec._secondary_unit_fields["qty_field"]] = ( + rec._origin[rec._secondary_unit_fields["qty_field"]] + or default_qty_field_value + ) + continue + if rec.secondary_uom_id.dependency_type == "independent": + if rec[rec._secondary_unit_fields["qty_field"]] == 0.0: + rec[ + rec._secondary_unit_fields["qty_field"] + ] = default_qty_field_value + continue + # To avoid recompute secondary_uom_qty field when + # secondary_uom_id changes. + rec.env.remove_to_compute( + field=rec._fields["secondary_uom_qty"], records=rec + ) + factor = rec._get_factor_line() + qty = float_round( + rec.secondary_uom_qty * factor, + precision_rounding=rec._get_uom_line().rounding, + ) + rec[rec._secondary_unit_fields["qty_field"]] = qty + + def _onchange_helper_product_uom_for_secondary(self): + """Helper method to be called from onchange method of uom field in + target model. + """ + if not self.secondary_uom_id: + self.secondary_uom_qty = 0.0 + return + elif self.secondary_uom_id.dependency_type == "independent": + return + factor = self._get_factor_line() + line_qty = self._get_quantity_from_line() + qty = float_round( + line_qty / (factor or 1.0), + precision_rounding=self.secondary_uom_id.uom_id.rounding, + ) + self.secondary_uom_qty = qty + + @api.model + def default_get(self, fields_list): + defaults = super().default_get(fields_list) + if self.secondary_uom_id and not self.env.context.get( + "skip_default_secondary_uom_qty", False + ): + defaults["secondary_uom_qty"] = 1.0 + return defaults diff --git a/product_secondary_unit/models/product_template.py b/product_secondary_unit/models/product_template.py new file mode 100644 index 00000000000..e6d7725e133 --- /dev/null +++ b/product_secondary_unit/models/product_template.py @@ -0,0 +1,23 @@ +# Copyright 2018 Tecnativa - Sergio Teruel +# 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" + + secondary_uom_ids = fields.One2many( + comodel_name="product.secondary.unit", + inverse_name="product_tmpl_id", + string="Secondary Unit of Measure", + help="Default Secondary Unit of Measure.", + context={"active_test": False}, + ) + + @api.model + def _get_default_secondary_uom(self): + return ( + self.secondary_uom_ids + and self.secondary_uom_ids[0] + or self.secondary_uom_ids + ) diff --git a/product_secondary_unit/readme/CONTRIBUTORS.rst b/product_secondary_unit/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..7b075b8a691 --- /dev/null +++ b/product_secondary_unit/readme/CONTRIBUTORS.rst @@ -0,0 +1,5 @@ +* Carlos Dauden +* Sergio Teruel +* Kitti Upariphutthiphong +* Pimolnat Suntian +* Alan Ramos diff --git a/product_secondary_unit/readme/DESCRIPTION.rst b/product_secondary_unit/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..1b02bbb452e --- /dev/null +++ b/product_secondary_unit/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module extends the functionality of product module to allow define other units with their conversion factor. diff --git a/product_secondary_unit/readme/USAGE.rst b/product_secondary_unit/readme/USAGE.rst new file mode 100644 index 00000000000..3f837c39383 --- /dev/null +++ b/product_secondary_unit/readme/USAGE.rst @@ -0,0 +1,5 @@ +To use this module you need to: + +#. Go to a *Product > General Information tab*. +#. Create any record in "Secondary unit of measure". +#. Set the conversion factor. diff --git a/product_secondary_unit/security/ir.model.access.csv b/product_secondary_unit/security/ir.model.access.csv new file mode 100644 index 00000000000..5ca549da6e5 --- /dev/null +++ b/product_secondary_unit/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_product_secondary_unit_user,access_product_second_unit_user,model_product_secondary_unit,base.group_user,1,1,1,1 diff --git a/product_secondary_unit/static/description/icon.png b/product_secondary_unit/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/product_secondary_unit/static/description/icon.png differ diff --git a/product_secondary_unit/static/description/index.html b/product_secondary_unit/static/description/index.html new file mode 100644 index 00000000000..818f4040f1d --- /dev/null +++ b/product_secondary_unit/static/description/index.html @@ -0,0 +1,435 @@ + + + + + + +Product Secondary Unit + + + +
+

Product Secondary Unit

+ + +

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

+

This module extends the functionality of product module to allow define other units with their conversion factor.

+

Table of contents

+ +
+

Usage

+

To use this module you need to:

+
    +
  1. Go to a Product > General Information tab.
  2. +
  3. Create any record in “Secondary unit of measure”.
  4. +
  5. Set the conversion factor.
  6. +
+
+
+

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

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Tecnativa
  • +
+
+
+

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:

+

sergio-teruel

+

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_secondary_unit/tests/__init__.py b/product_secondary_unit/tests/__init__.py new file mode 100644 index 00000000000..c10322abd6a --- /dev/null +++ b/product_secondary_unit/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from . import test_product_second_unit +from . import test_secondary_unit_mixin diff --git a/product_secondary_unit/tests/models.py b/product_secondary_unit/tests/models.py new file mode 100644 index 00000000000..c801b407394 --- /dev/null +++ b/product_secondary_unit/tests/models.py @@ -0,0 +1,28 @@ +# Copyright 2021 Tecnativa - Sergio Teruel +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import api, fields, models + + +class SecondaryUnitFake(models.Model): + _name = "secondary.unit.fake" + _inherit = "product.secondary.unit.mixin" + _description = "Secondary unit fake model for tests" + _secondary_unit_fields = { + "qty_field": "product_uom_qty", + "uom_field": "product_uom_id", + } + + name = fields.Char() + product_id = fields.Many2one("product.product", "Product", readonly=True) + product_uom_qty = fields.Float( + store=True, readonly=False, compute="_compute_product_uom_qty" + ) + product_uom_id = fields.Many2one("uom.uom", string="Product Unit of Measure") + + @api.depends("secondary_uom_qty", "secondary_uom_id") + def _compute_product_uom_qty(self): + self._compute_helper_target_field_qty() + + @api.onchange("product_uom_id") + def _onchange_product_uom(self): + self._onchange_helper_product_uom_for_secondary() diff --git a/product_secondary_unit/tests/test_product_second_unit.py b/product_secondary_unit/tests/test_product_second_unit.py new file mode 100644 index 00000000000..cb7b070e93b --- /dev/null +++ b/product_secondary_unit/tests/test_product_second_unit.py @@ -0,0 +1,60 @@ +# Copyright 2018 Tecnativa - Sergio Teruel +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo.tests import TransactionCase, tagged + + +@tagged("post_install", "-at_install") +class TestProductSecondaryUnit(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.product_uom_kg = cls.env.ref("uom.product_uom_kgm") + cls.product_uom_unit = cls.env.ref("uom.product_uom_unit") + cls.product = cls.env["product.template"].create( + { + "name": "test", + "uom_id": cls.product_uom_kg.id, + "uom_po_id": cls.product_uom_kg.id, + "secondary_uom_ids": [ + ( + 0, + 0, + { + "code": "A", + "name": "unit-700", + "uom_id": cls.product_uom_unit.id, + "factor": 0.7, + }, + ), + ( + 0, + 0, + { + "code": "B", + "name": "unit-900", + "uom_id": cls.product_uom_unit.id, + "factor": 0.9, + }, + ), + ], + } + ) + cls.secondary_unit = cls.env["product.secondary.unit"].search( + [("product_tmpl_id", "=", cls.product.id)], limit=1 + ) + + def test_product_secondary_unit_name(self): + self.assertEqual(self.secondary_unit.name_get()[0][1], "unit-700-0.7") + + def test_product_secondary_unit_search(self): + args = [ + ( + "product_tmpl_id.product_variant_ids", + "in", + self.product.product_variant_ids.ids, + ) + ] + name_get = self.env["product.secondary.unit"].name_search(name="A", args=args) + self.assertEqual(len(name_get), 1) + name_get = self.env["product.secondary.unit"].name_search(name="X", args=args) + self.assertEqual(len(name_get), 0) diff --git a/product_secondary_unit/tests/test_secondary_unit_mixin.py b/product_secondary_unit/tests/test_secondary_unit_mixin.py new file mode 100644 index 00000000000..e0ff1b4dcdd --- /dev/null +++ b/product_secondary_unit/tests/test_secondary_unit_mixin.py @@ -0,0 +1,131 @@ +# Copyright 2021 Tecnativa - Sergio Teruel +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo_test_helper import FakeModelLoader + +from odoo.tests import TransactionCase + + +class TestProductSecondaryUnitMixin(TransactionCase, FakeModelLoader): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.loader = FakeModelLoader(cls.env, cls.__module__) + cls.loader.backup_registry() + from .models import SecondaryUnitFake + + cls.loader.update_registry((SecondaryUnitFake,)) + cls.product_uom_kg = cls.env.ref("uom.product_uom_kgm") + cls.product_uom_unit = cls.env.ref("uom.product_uom_unit") + cls.product_uom_dozen = cls.env.ref("uom.product_uom_dozen") + cls.product_template = cls.env["product.template"].create( + { + "name": "test", + "uom_id": cls.product_uom_kg.id, + "uom_po_id": cls.product_uom_kg.id, + "secondary_uom_ids": [ + ( + 0, + 0, + { + "code": "C5", + "name": "box 5", + "uom_id": cls.product_uom_unit.id, + "factor": 5, + }, + ), + ( + 0, + 0, + { + "code": "C10", + "name": "box 10", + "uom_id": cls.product_uom_unit.id, + "factor": 10, + }, + ), + ], + } + ) + cls.secondary_unit_box_5 = cls.product_template.secondary_uom_ids[0] + cls.secondary_unit_box_10 = cls.product_template.secondary_uom_ids[1] + # Fake model which inherit from + cls.secondary_unit_fake = cls.env["secondary.unit.fake"].create( + { + "name": "Secondary unit fake", + "product_id": cls.product_template.product_variant_ids.id, + "product_uom_id": cls.product_uom_unit.id, + } + ) + + @classmethod + def tearDownClass(cls): + cls.loader.restore_registry() + return super(TestProductSecondaryUnitMixin, cls).tearDownClass() + + def test_product_secondary_unit_mixin(self): + fake_model = self.secondary_unit_fake + + fake_model.write( + {"secondary_uom_qty": 2, "secondary_uom_id": self.secondary_unit_box_5.id} + ) + self.assertEqual(fake_model.product_uom_qty, 10.0) + + fake_model.write( + {"secondary_uom_qty": 2, "secondary_uom_id": self.secondary_unit_box_10.id} + ) + self.assertEqual(fake_model.product_uom_qty, 20.0) + + fake_model.write({"product_uom_qty": 40.0}) + self.assertEqual(fake_model.secondary_uom_qty, 4) + + # Test onchange helper method + fake_model.write( + {"secondary_uom_qty": 1, "secondary_uom_id": self.secondary_unit_box_10.id} + ) + fake_model.flush() + fake_model.product_uom_id = self.product_uom_dozen + fake_model._onchange_helper_product_uom_for_secondary() + self.assertEqual(fake_model.secondary_uom_qty, 12) + + def test_product_secondary_unit_mixin_no_uom(self): + # If secondary_uom_id is not informed product_qty on target model is + # not computed. + fake_model = self.secondary_unit_fake + fake_model.secondary_uom_qty = 23 + self.assertEqual(fake_model.product_uom_qty, 0) + + def test_product_secondary_unit_mixin_no_uom_onchange(self): + # If secondary_uom_id is not informed secondary_uom_qty on source + # model is not computed. + fake_model = self.secondary_unit_fake + # import pdb ; pdb.set_trace() + fake_model._onchange_helper_product_uom_for_secondary() + self.assertEqual(fake_model.secondary_uom_qty, 0) + + def test_chained_compute_field(self): + """Secondary_uom_qty has not been computed when secondary_uom_id changes""" + fake_model = self.secondary_unit_fake + fake_model.secondary_uom_qty = 2.0 + fake_model.secondary_uom_id = self.secondary_unit_box_5 + self.assertEqual(fake_model.product_uom_qty, 10.0) + self.assertEqual(fake_model.secondary_uom_qty, 2.0) + fake_model.secondary_uom_id = self.secondary_unit_box_10 + self.assertEqual(fake_model.product_uom_qty, 20.0) + self.assertEqual(fake_model.secondary_uom_qty, 2.0) + + def test_independent_type(self): + # dependent type is already tested as dependency_type by default + fake_model = self.secondary_unit_fake + fake_model.secondary_uom_id = self.secondary_unit_box_5 + fake_model.secondary_uom_id.write({"dependency_type": "independent"}) + fake_model.write({"secondary_uom_qty": 2}) + self.assertEqual(fake_model.product_uom_qty, 0.0) + self.assertEqual(fake_model.secondary_uom_qty, 2) + + fake_model.write({"product_uom_qty": 17}) + self.assertEqual(fake_model.product_uom_qty, 17) + self.assertEqual(fake_model.secondary_uom_qty, 2) + + fake_model.write({"secondary_uom_qty": 4}) + self.assertEqual(fake_model.product_uom_qty, 17) + self.assertEqual(fake_model.secondary_uom_qty, 4) diff --git a/product_secondary_unit/views/product_views.xml b/product_secondary_unit/views/product_views.xml new file mode 100644 index 00000000000..4c628165624 --- /dev/null +++ b/product_secondary_unit/views/product_views.xml @@ -0,0 +1,56 @@ + + + + + Product template Secondary Unit + product.template + + + + + + + + + + + + + + + + + + + + + + + Product Secondary Unit + product.product + + + + [('id', '=', parent.id)] + + + + + diff --git a/setup/product_secondary_unit/odoo/addons/product_secondary_unit b/setup/product_secondary_unit/odoo/addons/product_secondary_unit new file mode 120000 index 00000000000..243d462314d --- /dev/null +++ b/setup/product_secondary_unit/odoo/addons/product_secondary_unit @@ -0,0 +1 @@ +../../../../product_secondary_unit \ No newline at end of file diff --git a/setup/product_secondary_unit/setup.py b/setup/product_secondary_unit/setup.py new file mode 100644 index 00000000000..28c57bb6403 --- /dev/null +++ b/setup/product_secondary_unit/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 00000000000..66bc2cbae3f --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1 @@ +odoo_test_helper