diff --git a/product_abc_classification/README.rst b/product_abc_classification/README.rst new file mode 100644 index 00000000000..688c72c15fe --- /dev/null +++ b/product_abc_classification/README.rst @@ -0,0 +1,119 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +========================== +Product Abc Classification +========================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:f602d3cbe4b034e608e4a5b554b3532e87e682d94cb1ee8ba499905258e348c3 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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_abc_classification + :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_abc_classification + :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 modules provides the bases to build ABC analysis (or ABC +classification) addons. These classification are used by inventory +management teams to help identify the most important products in their +portfolio and ensure they prioritize managing them above those less +valuable. + +Managers will create a profile with several levels (percentages) and +then the profiled products will automatically get a corresponding level +using the ABC classification. + +The addon *product_abc_classification_sale_stock* defines a computation +profile based on the number of sale order line delivered by product. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To use this module, you need to: + +#. Go to Inventory menu, then to Configuration/Products/ABC +Classification Profile and create a profile with levels, knowing that +the sum of all levels in the profile should sum 100 and all the levels +should be different. + +#. Later you should go to product categories or product variants, and +assign them a profile. Then the cron classification will proceed to +assign to these products one of the profile's levels. + +NOTE: If you profile (or unprofile) a product category, then all its +child categories and products will be profiled (or unprofiled). + +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 +* ForgeFlow + +Contributors +------------ + +- Miquel Raïch +- Lindsay Marion +- Laurent Mignon +- Denis Roussel +- Hoang Diep + +Other credits +------------- + +The migration of this module from 17.0 to 18.0 was financially supported +by Camptocamp + +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_abc_classification/__init__.py b/product_abc_classification/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/product_abc_classification/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/product_abc_classification/__manifest__.py b/product_abc_classification/__manifest__.py new file mode 100644 index 00000000000..fd5e7c90670 --- /dev/null +++ b/product_abc_classification/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright 2020 ForgeFlow +# Copyright 2021 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Product Abc Classification", + "summary": """ + ABC classification for sales and warehouse management""", + "version": "19.0.1.0.0", + "license": "AGPL-3", + "author": "ACSONE SA/NV, ForgeFlow, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/product-attribute", + "depends": ["product", "stock"], + "data": [ + "views/abc_classification_product_level.xml", + "views/abc_classification_profile.xml", + "views/product_template.xml", + "views/product_product.xml", + "views/product_category.xml", + "security/ir.model.access.csv", + "data/ir_cron.xml", + ], +} diff --git a/product_abc_classification/data/ir_cron.xml b/product_abc_classification/data/ir_cron.xml new file mode 100644 index 00000000000..a17da478cc4 --- /dev/null +++ b/product_abc_classification/data/ir_cron.xml @@ -0,0 +1,12 @@ + + + + Perform the product ABC Classification + + 1 + months + + model._cron_compute_abc_classification() + code + + diff --git a/product_abc_classification/i18n/es.po b/product_abc_classification/i18n/es.po new file mode 100644 index 00000000000..564cf2df8dd --- /dev/null +++ b/product_abc_classification/i18n/es.po @@ -0,0 +1,465 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_abc_classification +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-10-12 15: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_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__percentage +msgid "% Indicator" +msgstr "% Indicador" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__percentage_products +msgid "% Products" +msgstr "% Productos" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.product_template_form_view +msgid "ABC Classification" +msgstr "Clasificación ABC" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_abc_classification_level +msgid "ABC Classification Level" +msgstr "Nivel de clasificación ABC" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_form_view +msgid "ABC Classification Product Level" +msgstr "Clasificación ABC Nivel de producto" + +#. module: product_abc_classification +#: model:ir.actions.act_window,name:product_abc_classification.abc_classification_profile_action +#: model:ir.ui.menu,name:product_abc_classification.menu_abc_classification_profile_config_stock +msgid "ABC Classification profiles" +msgstr "Perfiles de clasificación ABC" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "ABC Profile" +msgstr "Perfil ABC" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_abc_classification_product_level +#: model:ir.model.fields,field_description:product_abc_classification.field_product_product__abc_classification_product_level_ids +#: model:ir.model.fields,field_description:product_abc_classification.field_product_template__abc_classification_product_level_ids +msgid "Abc Classification Product Level" +msgstr "Clasificación Abc Nivel de producto" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_abc_classification_profile +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__allowed_profile_ids +#: model:ir.model.fields,field_description:product_abc_classification.field_product_product__abc_classification_profile_ids +#: model:ir.model.fields,field_description:product_abc_classification.field_product_template__abc_classification_profile_ids +msgid "Abc Classification Profile" +msgstr "Perfil de clasificación Abc" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Abc classification" +msgstr "Clasificación Abc" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_needaction +msgid "Action Needed" +msgstr "Necesita Acción" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "Additional Information" +msgstr "Información Adicional" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_attachment_count +msgid "Attachment Count" +msgstr "Recuento de Archivos Adjuntos" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__auto_apply_computed_value +msgid "Auto Apply Computed Value" +msgstr "Aplicar automáticamente el valor calculado" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_profile__auto_apply_computed_value +msgid "" +"Check this if you want to apply the computed level on each product that has " +"this profile." +msgstr "" +"Marque esta casilla si desea aplicar el nivel calculado a cada producto que " +"tenga este perfil." + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_level__name +msgid "Classification A, B or C" +msgstr "Clasificación A, B o C" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__level_id +msgid "Classification level" +msgstr "Nivel de clasificación" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_product_level.py:0 +#, python-format +msgid "Classification level is mandatory" +msgstr "El nivel de clasificación es obligatorio" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Classification not in sync with computed" +msgstr "Clasificación no sincronizada con el cálculo" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "Computation" +msgstr "Cómputo" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__computed_level_id +msgid "Computed classification level" +msgstr "Nivel de clasificación computado" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_form_view +msgid "Computed level differs from the specified level" +msgstr "El nivel computado difiere del especificado" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_product_level.py:0 +#, python-format +msgid "" +"Computed level must be in the same classifiation profile as the one on the " +"product level" +msgstr "" +"El nivel calculado debe pertenecer al mismo perfil de clasificación que el " +"del producto" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__create_uid +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__create_uid +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__create_date +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__create_date +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__display_name +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__display_name +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__display_name +msgid "Display Name" +msgstr "Mostrar Nombre" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_follower_ids +msgid "Followers" +msgstr "Seguidores" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_partner_ids +msgid "Followers (Partners)" +msgstr "Seguidores/as (Socios)" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Group By" +msgstr "Agrupar Por" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__has_message +msgid "Has Message" +msgstr "Tiene mensaje" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__id +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__id +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__id +msgid "ID" +msgstr "ID (identificación)" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__flag +msgid "" +"If True, this means that the manual classification is different from the " +"computed one" +msgstr "" +"Si es True, significa que la clasificación manual es diferente de la " +"calculada" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__message_needaction +msgid "If checked, new messages require your attention." +msgstr "Si está marcada, nuevos mensajes requieren su atención." + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "si está marcada, algunos mensajes tienen un error de entrega." + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_is_follower +msgid "Is Follower" +msgstr "Es Seguidor/a" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level____last_update +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level____last_update +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile____last_update +msgid "Last Modified on" +msgstr "Última Modificación el" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__write_uid +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__write_uid +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__write_uid +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__write_date +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__write_date +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__write_date +msgid "Last Updated on" +msgstr "Última Actualización el" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__level_ids +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Level" +msgstr "Nivel" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +#: model:ir.model.constraint,message:product_abc_classification.constraint_abc_classification_level_name_uniq +#, python-format +msgid "Level name must be unique by profile" +msgstr "El nombre del nivel debe ser único por perfil" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "Levels" +msgstr "Niveles" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_main_attachment_id +msgid "Main Attachment" +msgstr "Adjunto Principal" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__manual_level_id +msgid "Manual classification level" +msgstr "Nivel de clasificación manual" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_product_level.py:0 +#, python-format +msgid "" +"Manual level must be in the same classifiation profile as the one on the " +"product level" +msgstr "" +"El nivel manual debe pertenecer al mismo perfil de clasificación que el " +"nivel de producto" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_has_error +msgid "Message Delivery error" +msgstr "Error en entrega de mensaje" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_ids +msgid "Messages" +msgstr "Mensajes" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__name +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__name +msgid "Name" +msgstr "Nombre" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_needaction_counter +msgid "Number of Actions" +msgstr "Número de Acciones" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_has_error_counter +msgid "Number of errors" +msgstr "Número de errores" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__message_needaction_counter +msgid "Number of messages requiring action" +msgstr "Número de mensajes que requieren una acción" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "Número de mensajes con error de entrega" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_product_level.py:0 +#: model:ir.model.constraint,message:product_abc_classification.constraint_abc_classification_product_level_product_level_uniq +#, python-format +msgid "Only one level by profile by product allowed" +msgstr "Sólo se permite un nivel por perfil y producto" + +#. module: product_abc_classification +#: model:ir.actions.server,name:product_abc_classification.ir_cron_product_abc_classification_ir_actions_server +#: model:ir.cron,cron_name:product_abc_classification.ir_cron_product_abc_classification +msgid "Perform the product ABC Classification" +msgstr "Realizar la Clasificación ABC del producto" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__period +msgid "Period on which to compute the classification (Days)" +msgstr "Periodo sobre el que se computa la clasificación (Días)" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_product_template +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__product_id +msgid "Product" +msgstr "Producto" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__product_count +msgid "Product Count" +msgstr "Recuento de productos" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_product_product +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__product_variant_ids +msgid "Product Variant" +msgstr "Variante de producto" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__product_tmpl_id +msgid "Product template" +msgstr "Plantilla Producto" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "Products" +msgstr "Productos" + +#. module: product_abc_classification +#: model:ir.actions.act_window,name:product_abc_classification.abc_classification_product_level_action +#: model:ir.ui.menu,name:product_abc_classification.menu_abc_classification_product_level_config_stock +msgid "Products ABC Classification" +msgstr "Clasificación ABC de productos" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__profile_id +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__profile_id +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Profile" +msgstr "Perfil" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "Profile Information" +msgstr "Información del perfil" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_profile.py:0 +#: model:ir.model.constraint,message:product_abc_classification.constraint_abc_classification_profile_name_uniq +#, python-format +msgid "Profile name must be unique" +msgstr "El nombre del perfil debe ser único" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +#, python-format +msgid "The percentage cannot be greater than 100." +msgstr "El porcentaje no puede ser superior a 100." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +#, python-format +msgid "The percentage of products cannot be greater than 100." +msgstr "El porcentaje de productos no puede ser superior a 100." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +#, python-format +msgid "The percentage of products should be a positive number." +msgstr "El porcentaje de productos debe ser un número positivo." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +#, python-format +msgid "The percentage should be a positive number." +msgstr "El porcentaje debe ser un número positivo." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_profile.py:0 +#, python-format +msgid "The percentages of the levels must be unique." +msgstr "Los porcentajes de los niveles deben ser únicos." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_profile.py:0 +#, python-format +msgid "The sum of the percentages of the levels should be 100." +msgstr "La suma de los porcentajes de los niveles debe ser 100." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_profile.py:0 +#, python-format +msgid "The sum of the products percentages of the levels should be 100." +msgstr "" +"La suma de los porcentajes de los productos de los niveles debe ser 100." + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__profile_type +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__profile_type +msgid "Type of ABC classification" +msgstr "Tipo de clasificación ABC" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__website_message_ids +msgid "Website Messages" +msgstr "Mensajes de la Página web" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__website_message_ids +msgid "Website communication history" +msgstr "Historial de la comunicación en la web" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "e.g. Sale Profile" +msgstr "p. ej. Perfil de venta" + +#~ msgid "SMS Delivery error" +#~ msgstr "Error en la entrega del SMS" diff --git a/product_abc_classification/i18n/fr.po b/product_abc_classification/i18n/fr.po new file mode 100644 index 00000000000..55923a743ee --- /dev/null +++ b/product_abc_classification/i18n/fr.po @@ -0,0 +1,487 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_abc_classification +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-02-15 16:46+0000\n" +"PO-Revision-Date: 2021-02-15 16:46+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_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__percentage +msgid "% Indicator" +msgstr "% KPI" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__percentage_products +msgid "% Products" +msgstr "% Articles" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.product_template_form_view +msgid "ABC Classification" +msgstr "Classification ABC" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_abc_classification_level +#, fuzzy +msgid "ABC Classification Level" +msgstr "Classe / Niveau" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_form_view +msgid "ABC Classification Product Level" +msgstr "Niveau de classification ABC des articles" + +#. module: product_abc_classification +#: model:ir.actions.act_window,name:product_abc_classification.abc_classification_profile_action +#: model:ir.ui.menu,name:product_abc_classification.menu_abc_classification_profile_config_stock +msgid "ABC Classification profiles" +msgstr "Profils de classification ABC" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "ABC Profile" +msgstr "Profil ABC" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_abc_classification_product_level +#: model:ir.model.fields,field_description:product_abc_classification.field_product_product__abc_classification_product_level_ids +#: model:ir.model.fields,field_description:product_abc_classification.field_product_template__abc_classification_product_level_ids +msgid "Abc Classification Product Level" +msgstr "Niveau de classification" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_abc_classification_profile +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__allowed_profile_ids +#: model:ir.model.fields,field_description:product_abc_classification.field_product_product__abc_classification_profile_ids +#: model:ir.model.fields,field_description:product_abc_classification.field_product_template__abc_classification_profile_ids +msgid "Abc Classification Profile" +msgstr "Profil de classification ABC" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Abc classification" +msgstr "Classification ABC" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "Additional Information" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__auto_apply_computed_value +#, fuzzy +msgid "Auto Apply Computed Value" +msgstr "Appliquer automatiquement la classification calculée" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_profile__auto_apply_computed_value +msgid "" +"Check this if you want to apply the computed level on each product that has " +"this profile." +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_level__name +msgid "Classification A, B or C" +msgstr "Classification A, B ou C" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__level_id +msgid "Classification level" +msgstr "Classe / Niveau" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_product_level.py:0 +#, python-format +msgid "Classification level is mandatory" +msgstr "La classe / niveau est obligatoire" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Classification not in sync with computed" +msgstr "Classes ABC manuelle et calculée divergentes" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "Computation" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__computed_level_id +msgid "Computed classification level" +msgstr "Classe calculée" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_form_view +msgid "Computed level differs from the specified level" +msgstr "La class calculée diverge de la valeur spécifiée" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_product_level.py:0 +#, python-format +msgid "" +"Computed level must be in the same classifiation profile as the one on the " +"product level" +msgstr "" +"La classe calculée doit utiliser le même profil de classification que celui " +"défini sur le produit" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__create_uid +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__create_uid +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__create_uid +msgid "Created by" +msgstr "Créé par" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__create_date +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__create_date +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__create_date +msgid "Created on" +msgstr "Créé le" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__display_name +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__display_name +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__display_name +msgid "Display Name" +msgstr "Nom affiché" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Group By" +msgstr "Grouper par" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__has_message +msgid "Has Message" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__id +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__id +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__id +msgid "ID" +msgstr "ID" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__flag +msgid "" +"If True, this means that the manual classification is different from the " +"computed one" +msgstr "" +"Si coché, indique que la classe attribuée manuellement au produit diverge de " +"la classe calculée par le système." + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__message_needaction +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level____last_update +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level____last_update +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile____last_update +msgid "Last Modified on" +msgstr "Dernière modification le" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__write_uid +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__write_uid +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__write_uid +msgid "Last Updated by" +msgstr "Dernière mise à jour par" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__write_date +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__write_date +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__write_date +msgid "Last Updated on" +msgstr "Dernière mise à jour le" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__level_ids +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Level" +msgstr "Niveau" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +#: model:ir.model.constraint,message:product_abc_classification.constraint_abc_classification_level_name_uniq +#, python-format +msgid "Level name must be unique by profile" +msgstr "Le nom de la classe doit être unique par profil" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +#, fuzzy +msgid "Levels" +msgstr "Niveau" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_main_attachment_id +msgid "Main Attachment" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__manual_level_id +msgid "Manual classification level" +msgstr "Classe (Valeur à utiliser)" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_product_level.py:0 +#, python-format +msgid "" +"Manual level must be in the same classifiation profile as the one on the " +"product level" +msgstr "" +"La classe à utiliser doit utiliser le même profil de classification que " +"celui défini sur le produit" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_ids +msgid "Messages" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__name +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__name +msgid "Name" +msgstr "Nom" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__message_needaction_counter +msgid "Number of messages requiring action" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_product_level.py:0 +#: model:ir.model.constraint,message:product_abc_classification.constraint_abc_classification_product_level_product_level_uniq +#, python-format +msgid "Only one level by profile by product allowed" +msgstr "Une classe de classification ABC par profil et par produit autorisée." + +#. module: product_abc_classification +#: model:ir.actions.server,name:product_abc_classification.ir_cron_product_abc_classification_ir_actions_server +#: model:ir.cron,cron_name:product_abc_classification.ir_cron_product_abc_classification +#, fuzzy +msgid "Perform the product ABC Classification" +msgstr "Classification ABC des articles" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__period +msgid "Period on which to compute the classification (Days)" +msgstr "Période référence pour le calcul de la classification (Nbr jours)" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_product_template +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__product_id +msgid "Product" +msgstr "Article" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__product_count +#, fuzzy +msgid "Product Count" +msgstr "Article" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_product_product +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__product_variant_ids +#, fuzzy +msgid "Product Variant" +msgstr "Modèle de produit" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__product_tmpl_id +msgid "Product template" +msgstr "Modèle de produit" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +#, fuzzy +msgid "Products" +msgstr "Article" + +#. module: product_abc_classification +#: model:ir.actions.act_window,name:product_abc_classification.abc_classification_product_level_action +#: model:ir.ui.menu,name:product_abc_classification.menu_abc_classification_product_level_config_stock +msgid "Products ABC Classification" +msgstr "Classification ABC des articles" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__profile_id +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__profile_id +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Profile" +msgstr "Profil" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +#, fuzzy +msgid "Profile Information" +msgstr "Profil" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_profile.py:0 +#: model:ir.model.constraint,message:product_abc_classification.constraint_abc_classification_profile_name_uniq +#, python-format +msgid "Profile name must be unique" +msgstr "Le nom du profil doit être unique" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +#, python-format +msgid "The percentage cannot be greater than 100." +msgstr "Le pourcentage ne peut pas dépasser 100." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +#, python-format +msgid "The percentage of products cannot be greater than 100." +msgstr "Le pourcentage d'articles' ne peut pas dépasser 100." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +#, python-format +msgid "The percentage of products should be a positive number." +msgstr "Le pourcentage d'articles' doit être un nombre positif." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +#, python-format +msgid "The percentage should be a positive number." +msgstr "Le pourcentage doit être un nombre positif." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_profile.py:0 +#, python-format +msgid "The percentages of the levels must be unique." +msgstr "" +"Les valeurs de pourcentage des différentes classes doivent être uniques pour " +"un même profil." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_profile.py:0 +#, python-format +msgid "The sum of the percentages of the levels should be 100." +msgstr "La somme des pourcentages ne doit pas dépasser 100." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_profile.py:0 +#, python-format +msgid "The sum of the products percentages of the levels should be 100." +msgstr "La somme des pourcentages d'articles ne doit pas dépasser 100." + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__profile_type +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__profile_type +msgid "Type of ABC classification" +msgstr "Type de classification ABC" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "e.g. Sale Profile" +msgstr "" + +#~ msgid "ABC Profiles" +#~ msgstr "Profils ABC" + +#~ msgid "Abc classification product level ids" +#~ msgstr "Classes ABC" + +#~ msgid "Abc classification profile ids" +#~ msgstr "Profils ABC" + +#~ msgid "Based on the count of delivered sale order line by product" +#~ msgstr "Basé sur le total des lignes de vente par article" + +#~ msgid "Level ids" +#~ msgstr "Classes" + +#~ msgid "abc.classification.level" +#~ msgstr "Classe de classification ABC" diff --git a/product_abc_classification/i18n/it.po b/product_abc_classification/i18n/it.po new file mode 100644 index 00000000000..d40d109c122 --- /dev/null +++ b/product_abc_classification/i18n/it.po @@ -0,0 +1,461 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_abc_classification +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-10-29 18: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_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__percentage +msgid "% Indicator" +msgstr "% indicatore" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__percentage_products +msgid "% Products" +msgstr "% prodotti" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.product_template_form_view +msgid "ABC Classification" +msgstr "Classificazione ABC" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_abc_classification_level +msgid "ABC Classification Level" +msgstr "Livello classificazione ABC" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_form_view +msgid "ABC Classification Product Level" +msgstr "Livello prodotto classificazione ABC" + +#. module: product_abc_classification +#: model:ir.actions.act_window,name:product_abc_classification.abc_classification_profile_action +#: model:ir.ui.menu,name:product_abc_classification.menu_abc_classification_profile_config_stock +msgid "ABC Classification profiles" +msgstr "Profili classificazione ABC" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "ABC Profile" +msgstr "Profilo ABC" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_abc_classification_product_level +#: model:ir.model.fields,field_description:product_abc_classification.field_product_product__abc_classification_product_level_ids +#: model:ir.model.fields,field_description:product_abc_classification.field_product_template__abc_classification_product_level_ids +msgid "Abc Classification Product Level" +msgstr "Livello ABC classificazione prodotto" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_abc_classification_profile +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__allowed_profile_ids +#: model:ir.model.fields,field_description:product_abc_classification.field_product_product__abc_classification_profile_ids +#: model:ir.model.fields,field_description:product_abc_classification.field_product_template__abc_classification_profile_ids +msgid "Abc Classification Profile" +msgstr "Profilo classificazione ABC" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Abc classification" +msgstr "Classificazione ABC" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_needaction +msgid "Action Needed" +msgstr "Azione richiesta" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "Additional Information" +msgstr "Informazioni supplementari" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_attachment_count +msgid "Attachment Count" +msgstr "Conteggio allegati" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__auto_apply_computed_value +msgid "Auto Apply Computed Value" +msgstr "Applica automaticamente valore calcolato" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_profile__auto_apply_computed_value +msgid "" +"Check this if you want to apply the computed level on each product that has " +"this profile." +msgstr "" +"Selezionare se si vuole applicare il livello calcolato ad ogni prodotto che " +"ha questo profilo." + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_level__name +msgid "Classification A, B or C" +msgstr "Classificazione A, B o C" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__level_id +msgid "Classification level" +msgstr "Licello classificazione" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_product_level.py:0 +#, python-format +msgid "Classification level is mandatory" +msgstr "Il livello classificazione è obbligatorio" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Classification not in sync with computed" +msgstr "Classificazione non sincronizzata con il calcolo" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "Computation" +msgstr "Calcolo" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__computed_level_id +msgid "Computed classification level" +msgstr "Livello classificazione calcolato" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_form_view +msgid "Computed level differs from the specified level" +msgstr "Il livello calcolato è diverso dal livello indicato" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_product_level.py:0 +#, python-format +msgid "" +"Computed level must be in the same classifiation profile as the one on the " +"product level" +msgstr "" +"Il livello calcolato deve essere nello stesso profilo di classificazione " +"presente nel livello prodotto" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__create_uid +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__create_uid +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__create_uid +msgid "Created by" +msgstr "Creato da" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__create_date +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__create_date +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__create_date +msgid "Created on" +msgstr "Creato il" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__display_name +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__display_name +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__display_name +msgid "Display Name" +msgstr "Nome visualizzato" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_follower_ids +msgid "Followers" +msgstr "Seguito da" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_partner_ids +msgid "Followers (Partners)" +msgstr "Seguito da (partner)" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Group By" +msgstr "Raggruppa per" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__has_message +msgid "Has Message" +msgstr "Ha un messaggio" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__id +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__id +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__id +msgid "ID" +msgstr "ID" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__flag +msgid "" +"If True, this means that the manual classification is different from the " +"computed one" +msgstr "" +"Se vero, questo significa che la classificazione manuale è diversa da quella " +"calcolata" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__message_needaction +msgid "If checked, new messages require your attention." +msgstr "Se selezionata, nuovi messaggi richiedono attenzione." + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "Se selezionata, alcuni messaggi hanno un errore di consegna." + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_is_follower +msgid "Is Follower" +msgstr "Segue" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level____last_update +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level____last_update +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile____last_update +msgid "Last Modified on" +msgstr "Ultima modifica il" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__write_uid +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__write_uid +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__write_uid +msgid "Last Updated by" +msgstr "Ultimo aggiornamento di" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__write_date +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__write_date +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__write_date +msgid "Last Updated on" +msgstr "Ultimo aggiornamento il" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__level_ids +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Level" +msgstr "Livello" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +#: model:ir.model.constraint,message:product_abc_classification.constraint_abc_classification_level_name_uniq +#, python-format +msgid "Level name must be unique by profile" +msgstr "Il livello deve essere univoco per profilo" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "Levels" +msgstr "Livelli" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_main_attachment_id +msgid "Main Attachment" +msgstr "Allegato principale" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__manual_level_id +msgid "Manual classification level" +msgstr "Livello classificazione manuale" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_product_level.py:0 +#, python-format +msgid "" +"Manual level must be in the same classifiation profile as the one on the " +"product level" +msgstr "" +"Il livello manuale deve essere nello stesso profilo di classificazione " +"presente nel livello prodotto" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_has_error +msgid "Message Delivery error" +msgstr "Errore di consegna messaggio" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_ids +msgid "Messages" +msgstr "Messaggi" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__name +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__name +msgid "Name" +msgstr "Nome" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_needaction_counter +msgid "Number of Actions" +msgstr "Numero di azioni" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_has_error_counter +msgid "Number of errors" +msgstr "Numero di errori" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__message_needaction_counter +msgid "Number of messages requiring action" +msgstr "Numero di messaggi che richiedono un'azione" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "Numero di messaggi con errore di consegna" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_product_level.py:0 +#: model:ir.model.constraint,message:product_abc_classification.constraint_abc_classification_product_level_product_level_uniq +#, python-format +msgid "Only one level by profile by product allowed" +msgstr "Consentto solo un livello per profilo per prodotto" + +#. module: product_abc_classification +#: model:ir.actions.server,name:product_abc_classification.ir_cron_product_abc_classification_ir_actions_server +#: model:ir.cron,cron_name:product_abc_classification.ir_cron_product_abc_classification +msgid "Perform the product ABC Classification" +msgstr "Eseguire la classificazione ABC prodotto" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__period +msgid "Period on which to compute the classification (Days)" +msgstr "Periodo nel quale calcolare la classificazione (giorni)" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_product_template +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__product_id +msgid "Product" +msgstr "Prodotto" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__product_count +msgid "Product Count" +msgstr "Conteggio prodotti" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_product_product +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__product_variant_ids +msgid "Product Variant" +msgstr "Variante prodotto" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__product_tmpl_id +msgid "Product template" +msgstr "Modello prodotto" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "Products" +msgstr "Prodotti" + +#. module: product_abc_classification +#: model:ir.actions.act_window,name:product_abc_classification.abc_classification_product_level_action +#: model:ir.ui.menu,name:product_abc_classification.menu_abc_classification_product_level_config_stock +msgid "Products ABC Classification" +msgstr "Classificazione ABC prodotti" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__profile_id +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__profile_id +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Profile" +msgstr "Profilo" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "Profile Information" +msgstr "Informazioni profilo" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_profile.py:0 +#: model:ir.model.constraint,message:product_abc_classification.constraint_abc_classification_profile_name_uniq +#, python-format +msgid "Profile name must be unique" +msgstr "Il nome profilo deve essere univoco" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +#, python-format +msgid "The percentage cannot be greater than 100." +msgstr "La percentuale non può essere superiore a 100." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +#, python-format +msgid "The percentage of products cannot be greater than 100." +msgstr "La percentuale di prodotti non può essere superiore a 100." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +#, python-format +msgid "The percentage of products should be a positive number." +msgstr "La percentuale di prodotti deve essere un numero positivo." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +#, python-format +msgid "The percentage should be a positive number." +msgstr "La percentuale deve essere un numero positivo." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_profile.py:0 +#, python-format +msgid "The percentages of the levels must be unique." +msgstr "La percentuale dei livelli deve essere univoca." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_profile.py:0 +#, python-format +msgid "The sum of the percentages of the levels should be 100." +msgstr "La somma delle percentuali dei livelli deve essere 100." + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_profile.py:0 +#, python-format +msgid "The sum of the products percentages of the levels should be 100." +msgstr "La somma delle percentuali dei prodotti dei livelli deve essere 100." + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__profile_type +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__profile_type +msgid "Type of ABC classification" +msgstr "Tipo classificazione ABC" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__website_message_ids +msgid "Website Messages" +msgstr "Messaggi sito web" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__website_message_ids +msgid "Website communication history" +msgstr "Cronologia comunicazioni sito web" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "e.g. Sale Profile" +msgstr "es. Profilo vendite" diff --git a/product_abc_classification/i18n/product_abc_classification.pot b/product_abc_classification/i18n/product_abc_classification.pot new file mode 100644 index 00000000000..91a2cce0482 --- /dev/null +++ b/product_abc_classification/i18n/product_abc_classification.pot @@ -0,0 +1,451 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_abc_classification +# +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_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__percentage +msgid "% Indicator" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__percentage_products +msgid "% Products" +msgstr "" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_profile.py:0 +msgid "%s (copy)" +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.view_product_category_form +msgid "-> Apply to Products" +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.product_template_form_view +#: model_terms:ir.ui.view,arch_db:product_abc_classification.view_product_category_form +msgid "ABC Classification" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_abc_classification_level +msgid "ABC Classification Level" +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_form_view +msgid "ABC Classification Product Level" +msgstr "" + +#. module: product_abc_classification +#: model:ir.actions.act_window,name:product_abc_classification.abc_classification_profile_action +#: model:ir.ui.menu,name:product_abc_classification.menu_abc_classification_profile_config_stock +msgid "ABC Classification profiles" +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "ABC Profile" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_abc_classification_product_level +#: model:ir.model.fields,field_description:product_abc_classification.field_product_product__abc_classification_product_level_ids +#: model:ir.model.fields,field_description:product_abc_classification.field_product_template__abc_classification_product_level_ids +msgid "Abc Classification Product Level" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_abc_classification_profile +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__allowed_profile_ids +#: model:ir.model.fields,field_description:product_abc_classification.field_product_category__abc_classification_profile_ids +#: model:ir.model.fields,field_description:product_abc_classification.field_product_product__abc_classification_profile_ids +#: model:ir.model.fields,field_description:product_abc_classification.field_product_template__abc_classification_profile_ids +msgid "Abc Classification Profile" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_product_product__abc_classification_profile_updatable_from_category +msgid "Abc Classification Profile Updatable From Category" +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Abc classification" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "Additional Information" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__auto_apply_computed_value +msgid "Auto Apply Computed Value" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_profile__auto_apply_computed_value +msgid "" +"Check this if you want to apply the computed level on each product that has " +"this profile." +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_level__name +msgid "Classification A, B or C" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__level_id +msgid "Classification level" +msgstr "" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_product_level.py:0 +msgid "Classification level is mandatory" +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Classification not in sync with computed" +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "Computation" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__computed_level_id +msgid "Computed classification level" +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_form_view +msgid "Computed level differs from the specified level" +msgstr "" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_product_level.py:0 +msgid "" +"Computed level must be in the same classifiation profile as the one on the " +"product level" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__create_uid +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__create_uid +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__create_uid +msgid "Created by" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__create_date +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__create_date +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__create_date +msgid "Created on" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__display_name +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__display_name +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__display_name +msgid "Display Name" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Group By" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__has_message +msgid "Has Message" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__id +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__id +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__id +msgid "ID" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__flag +msgid "" +"If True, this means that the manual classification is different from the " +"computed one" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__message_needaction +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__write_uid +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__write_uid +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__write_date +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__write_date +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__write_date +msgid "Last Updated on" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__level_ids +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Level" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.constraint,message:product_abc_classification.constraint_abc_classification_level_name_uniq +msgid "Level name must be unique by profile" +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "Levels" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__manual_level_id +msgid "Manual classification level" +msgstr "" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_product_level.py:0 +msgid "" +"Manual level must be in the same classifiation profile as the one on the " +"product level" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_ids +msgid "Messages" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__name +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__name +msgid "Name" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__message_needaction_counter +msgid "Number of messages requiring action" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.constraint,message:product_abc_classification.constraint_abc_classification_product_level_product_level_uniq +msgid "Only one level by profile by product allowed" +msgstr "" + +#. module: product_abc_classification +#: model:ir.actions.server,name:product_abc_classification.ir_cron_product_abc_classification_ir_actions_server +msgid "Perform the product ABC Classification" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__period +msgid "Period on which to compute the classification (Days)" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_product_template +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__product_id +msgid "Product" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_product_category +msgid "Product Category" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__product_count +msgid "Product Count" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model,name:product_abc_classification.model_product_product +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__product_variant_ids +msgid "Product Variant" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__product_tmpl_id +msgid "Product template" +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "Products" +msgstr "" + +#. module: product_abc_classification +#: model:ir.actions.act_window,name:product_abc_classification.abc_classification_product_level_action +#: model:ir.ui.menu,name:product_abc_classification.menu_abc_classification_product_level_config_stock +msgid "Products ABC Classification" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_level__profile_id +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__profile_id +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_product_level_search_view +msgid "Profile" +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "Profile Information" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.constraint,message:product_abc_classification.constraint_abc_classification_profile_name_uniq +msgid "Profile name must be unique" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__rating_ids +msgid "Ratings" +msgstr "" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +msgid "The percentage cannot be greater than 100." +msgstr "" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +msgid "The percentage of products cannot be greater than 100." +msgstr "" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +msgid "The percentage of products should be a positive number." +msgstr "" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_level.py:0 +msgid "The percentage should be a positive number." +msgstr "" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_profile.py:0 +msgid "The percentages of the levels must be unique." +msgstr "" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_profile.py:0 +msgid "The sum of the percentages of the levels should be 100." +msgstr "" + +#. module: product_abc_classification +#. odoo-python +#: code:addons/product_abc_classification/models/abc_classification_profile.py:0 +msgid "The sum of the products percentages of the levels should be 100." +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.view_product_category_form +msgid "This will apply to all products in the category" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__profile_type +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_profile__profile_type +msgid "Type of ABC classification" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,field_description:product_abc_classification.field_abc_classification_product_level__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: product_abc_classification +#: model:ir.model.fields,help:product_abc_classification.field_abc_classification_product_level__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: product_abc_classification +#: model_terms:ir.ui.view,arch_db:product_abc_classification.abc_classification_profile_form_view +msgid "e.g. Sale Profile" +msgstr "" diff --git a/product_abc_classification/models/__init__.py b/product_abc_classification/models/__init__.py new file mode 100644 index 00000000000..bc793feeb5d --- /dev/null +++ b/product_abc_classification/models/__init__.py @@ -0,0 +1,6 @@ +from . import abc_classification_profile +from . import abc_classification_level +from . import product_category +from . import product_template +from . import product_product +from . import abc_classification_product_level diff --git a/product_abc_classification/models/abc_classification_level.py b/product_abc_classification/models/abc_classification_level.py new file mode 100644 index 00000000000..5052a077e64 --- /dev/null +++ b/product_abc_classification/models/abc_classification_level.py @@ -0,0 +1,50 @@ +# Copyright 2020 ForgeFlow +# Copyright 2021 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models +from odoo.exceptions import ValidationError + + +class AbcClassificationLevel(models.Model): + _name = "abc.classification.level" + _description = "ABC Classification Level" + _order = "percentage desc, id desc" + _rec_name = "name" + + percentage_products = fields.Float(default=0.0, required=True, string="% Products") + percentage = fields.Float(default=0.0, required=True, string="% Indicator") + profile_id = fields.Many2one("abc.classification.profile", ondelete="cascade") + + name = fields.Char(help="Classification A, B or C", required=True) + + _name_uniq = models.Constraint( + "UNIQUE(profile_id, name)", + "Level name must be unique by profile", + ) + + @api.constrains("percentage") + def _check_percentage(self): + for level in self: + if level.percentage > 100.0: + raise ValidationError( + self.env._("The percentage cannot be greater than 100.") + ) + if level.percentage <= 0.0: + raise ValidationError( + self.env._("The percentage should be a positive number.") + ) + + @api.constrains("percentage_products") + def _check_percentage_products(self): + for level in self: + if level.percentage_products > 100.0: + raise ValidationError( + self.env._("The percentage of products cannot be greater than 100.") + ) + if level.percentage_products <= 0.0: + raise ValidationError( + self.env._( + "The percentage of products should be a positive number." + ) + ) diff --git a/product_abc_classification/models/abc_classification_product_level.py b/product_abc_classification/models/abc_classification_product_level.py new file mode 100644 index 00000000000..33559296bc2 --- /dev/null +++ b/product_abc_classification/models/abc_classification_product_level.py @@ -0,0 +1,173 @@ +# Copyright 2021 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models +from odoo.exceptions import ValidationError + + +class AbcClassificationProductLevel(models.Model): + _name = "abc.classification.product.level" + _inherit = "mail.thread" + _description = "Abc Classification Product Level" + _rec_name = "level_id" + + manual_level_id = fields.Many2one( + "abc.classification.level", + string="Manual classification level", + tracking=True, + domain="[('profile_id', '=', profile_id)]", + ) + computed_level_id = fields.Many2one( + "abc.classification.level", + string="Computed classification level", + readonly=True, + ) + level_id = fields.Many2one( + "abc.classification.level", + string="Classification level", + compute="_compute_level_id", + store=True, + domain="[('profile_id', '=', profile_id)]", + ) + flag = fields.Boolean( + default=False, + compute="_compute_flag", + string="If True, this means that the manual classification is " + "different from the computed one", + store=True, + index=True, + ) + product_id = fields.Many2one( + "product.product", + string="Product", + index=True, + required=True, + ondelete="cascade", + ) + product_tmpl_id = fields.Many2one( + "product.template", + string="Product template", + index=True, + readonly=True, + ) + # percentage + profile_id = fields.Many2one( + "abc.classification.profile", + string="Profile", + required=True, + ) + profile_type = fields.Selection( + related="profile_id.profile_type", + readonly=True, + store=True, + ) + allowed_profile_ids = fields.Many2many( + comodel_name="abc.classification.profile", + related="product_id.abc_classification_profile_ids", + ) + + _product_level_uniq = models.Constraint( + "UNIQUE(profile_id, product_id)", + "Only one level by profile by product allowed", + ) + + @api.constrains("computed_level_id", "manual_level_id", "product_id") + def _check_level(self): + for rec in self: + if not rec.computed_level_id and not rec.manual_level_id: + raise ValidationError(self.env._("Classification level is mandatory")) + if ( + rec.computed_level_id + and rec.computed_level_id.profile_id != rec.profile_id + ): + raise ValidationError( + self.env._( + "Computed level must be in the same classifiation " + "profile as the one on the product level" + ) + ) + if rec.manual_level_id and rec.manual_level_id.profile_id != rec.profile_id: + raise ValidationError( + self.env._( + "Manual level must be in the same classifiation " + "profile as the one on the product level" + ) + ) + + @api.onchange("product_tmpl_id") + def _onchange_product_tmpl_id(self): + for rec in self.filtered( + lambda a: a.product_tmpl_id.product_variant_count == 1 + ): + rec.product_id = rec.product_tmpl_id.product_variant_id + + @api.depends("level_id", "profile_id") + def _compute_display_name(self): + for record in self: + record.display_name = f"{record.profile_id.name}: {record.level_id.name}" + + @api.depends("manual_level_id", "computed_level_id") + def _compute_level_id(self): + for rec in self: + if rec.manual_level_id: + rec.level_id = rec.manual_level_id + else: + rec.level_id = rec.computed_level_id + + @api.depends("manual_level_id", "computed_level_id") + def _compute_flag(self): + for rec in self: + rec.flag = ( + rec.computed_level_id and rec.manual_level_id != rec.computed_level_id + ) + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if "manual_level_id" not in vals and "computed_level_id" in vals: + # at creation the manual level is set to the same value as the + # computed one + vals["manual_level_id"] = vals["computed_level_id"] + + if "profile_id" in vals: + profile = self.env["abc.classification.profile"].browse( + vals["profile_id"] + ) + if profile.auto_apply_computed_value and "computed_level_id" in vals: + vals["manual_level_id"] = vals["computed_level_id"] + return super().create(vals_list) + + def write(self, vals): + """ + We apply the manual level to the product level if + computed level is modified and only for profiles with + auto_apply_computed_value = =True + """ + values = vals.copy() + new_self = self + if "computed_level_id" in values: + profile_obj = self.env["abc.classification.profile"] + target_profile_id = ( + profile_obj.browse(values["profile_id"]).filtered( + "auto_apply_computed_value" + ) + if "profile_id" in values + else profile_obj.browse() + ) + if target_profile_id: + # If the profile of levels should be changed at the same time + # and has auto_apply_computed_value True + # So, we can apply change to the whole recordset + values["manual_level_id"] = values["computed_level_id"] + else: + # If profile is not modified, filter levels per profile + # if it has auto_apply_computed_value True and modify only + # those + auto_applied_profiles_levels = self.filtered( + lambda level: level.profile_id.auto_apply_computed_value + ) + new_self = self - auto_applied_profiles_levels + super( + AbcClassificationProductLevel, auto_applied_profiles_levels + ).write(dict(values, manual_level_id=values["computed_level_id"])) + return super(AbcClassificationProductLevel, new_self).write(values) diff --git a/product_abc_classification/models/abc_classification_profile.py b/product_abc_classification/models/abc_classification_profile.py new file mode 100644 index 00000000000..0d43604c1b4 --- /dev/null +++ b/product_abc_classification/models/abc_classification_profile.py @@ -0,0 +1,145 @@ +# Copyright 2020 ForgeFlow +# Copyright 2021 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from psycopg2.extensions import AsIs + +from odoo import api, fields, models +from odoo.exceptions import ValidationError + + +class AbcClassificationProfile(models.Model): + _name = "abc.classification.profile" + _description = "Abc Classification Profile" + _rec_name = "name" + + name = fields.Char(required=True) + level_ids = fields.One2many( + comodel_name="abc.classification.level", inverse_name="profile_id" + ) + profile_type = fields.Selection( + selection=[], + string="Type of ABC classification", + index=True, + required=True, + ) + period = fields.Integer( + default=365, + string="Period on which to compute the classification (Days)", + required=True, + ) + + product_variant_ids = fields.Many2many( + comodel_name="product.product", + relation="abc_classification_profile_product_rel", + column1="profile_id", + column2="product_id", + index=True, + ) + product_count = fields.Integer(compute="_compute_product_count", readonly=True) + + auto_apply_computed_value = fields.Boolean( + default=False, + help="Check this if you want to apply " + "the computed level on each product that has this " + "profile.", + ) + + _name_uniq = models.Constraint("UNIQUE(name)", "Profile name must be unique") + + @api.constrains("level_ids") + def _check_levels(self): + for profile in self: + percentages = profile.level_ids.mapped("percentage") + total = sum(percentages) + if profile.level_ids and total != 100.0: + raise ValidationError( + self.env._( + "The sum of the percentages of the levels should be 100." + ) + ) + if profile.level_ids and len({}.fromkeys(percentages)) != len(percentages): + raise ValidationError( + self.env._("The percentages of the levels must be unique.") + ) + percentage_productss = profile.level_ids.mapped("percentage_products") + total = sum(percentage_productss) + if profile.level_ids and total != 100.0: + raise ValidationError( + self.env._( + "The sum of the products percentages of the levels " + "should be 100." + ) + ) + + def _compute_abc_classification(self): + raise NotImplementedError() + + @api.depends("product_variant_ids") + def _compute_product_count(self): + for profile in self: + profile.product_count = len(profile.product_variant_ids) + + def action_view_products(self): + products = self.mapped("product_variant_ids") + action = self.env["ir.actions.act_window"]._for_xml_id( + "product.product_variant_action" + ) + del action["context"] + if len(products) > 1: + action["domain"] = [("id", "in", products.ids)] + elif len(products) == 1: + form_view = [ + (self.env.ref("product.product_variant_easy_edit_view").id, "form") + ] + if "views" in action: + action["views"] = form_view + [ + (state, view) for state, view in action["views"] if view != "form" + ] + else: + action["views"] = form_view + action["res_id"] = products.id + else: + action = {"type": "ir.actions.act_window_close"} + return action + + @api.model + def _cron_compute_abc_classification(self): + self.search([], limit=False)._compute_abc_classification() + + def write(self, vals): + res = super().write(vals) + if "auto_apply_computed_value" in vals and vals["auto_apply_computed_value"]: + self._auto_apply_computed_value_for_product_levels() + return res + + def _auto_apply_computed_value_for_product_levels(self): + level_ids = [] + for rec in self: + self.env.cr.execute( + """ + UPDATE %(table)s + SET manual_level_id = computed_level_id + WHERE profile_id = %(profile_id)s + RETURNING id + + """, + { + "table": AsIs(self.env["abc.classification.product.level"]._table), + "profile_id": rec.id, + }, + ) + level_ids.extend(r[0] for r in self.env.cr.fetchall()) + modified_levels = self.env["abc.classification.product.level"].browse(level_ids) + modified_levels.invalidate_recordset(fnames=["manual_level_id"]) + # mark field as modified and trigger recompute of dependent fields. + modified_levels.modified(["manual_level_id"]) + modified_levels._recompute_recordset() + + def copy_data(self, default=None): + default = dict(default or {}) + vals_list = super().copy_data(default=default) + for profile, vals in zip(self, vals_list, strict=False): + if "name" not in default: + vals["name"] = self.env._("%s (copy)", profile.name) + return vals_list diff --git a/product_abc_classification/models/product_category.py b/product_abc_classification/models/product_category.py new file mode 100644 index 00000000000..39626a00599 --- /dev/null +++ b/product_abc_classification/models/product_category.py @@ -0,0 +1,34 @@ +# Copyright 2024 ForgeFlow +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ProductCategory(models.Model): + _inherit = "product.category" + + abc_classification_profile_ids = fields.Many2many( + comodel_name="abc.classification.profile", + relation="abc_classification_profile_product_categ_rel", + column1="categ_id", + column2="profile_id", + index=True, + ) + + def update_product_abc_classification_profile(self): + category_products = self.env["product.product"]._read_group( + [ + ("abc_classification_profile_updatable_from_category", "=", True), + ("categ_id", "in", self.ids), + ], + ["categ_id"], + ["id:recordset"], + ) + for categ, products in category_products: + products.write( + { + "abc_classification_profile_ids": [ + (6, 0, categ.abc_classification_profile_ids.ids) + ] + } + ) diff --git a/product_abc_classification/models/product_product.py b/product_abc_classification/models/product_product.py new file mode 100644 index 00000000000..31c79263d73 --- /dev/null +++ b/product_abc_classification/models/product_product.py @@ -0,0 +1,48 @@ +# Copyright 2020 ForgeFlow +# Copyright 2021 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class ProductProduct(models.Model): + _inherit = "product.product" + + abc_classification_product_level_ids = fields.One2many( + "abc.classification.product.level", index=True, inverse_name="product_id" + ) + abc_classification_profile_ids = fields.Many2many( + comodel_name="abc.classification.profile", + relation="abc_classification_profile_product_rel", + column1="product_id", + column2="profile_id", + index=True, + ) + abc_classification_profile_updatable_from_category = fields.Boolean(default=True) + + def _update_abc_classification_profile_from_category(self): + for rec in self: + category = rec.categ_id + if ( + not rec.abc_classification_profile_ids + and category.abc_classification_profile_ids + ): + rec.abc_classification_profile_ids = ( + category.abc_classification_profile_ids + ) + + @api.model_create_multi + def create(self, vals_list): + res = super().create(vals_list) + res._update_abc_classification_profile_from_category() + return res + + def write(self, vals): + res = super().write(vals) + if vals.get("categ_id"): + self._update_abc_classification_profile_from_category() + return res + + @api.onchange("categ_id") + def _onchange_categ_id_abc_classification(self): + self._update_abc_classification_profile_from_category() diff --git a/product_abc_classification/models/product_template.py b/product_abc_classification/models/product_template.py new file mode 100644 index 00000000000..48c01bbc47d --- /dev/null +++ b/product_abc_classification/models/product_template.py @@ -0,0 +1,70 @@ +# Copyright 2020 ForgeFlow +# Copyright 2021 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + abc_classification_profile_ids = fields.Many2many( + "abc.classification.profile", + compute="_compute_abc_classification_profile_ids", + inverse="_inverse_abc_classification_profile_ids", + store=True, + ) + abc_classification_product_level_ids = fields.One2many( + "abc.classification.product.level", + compute="_compute_abc_classification_product_level_ids", + inverse="_inverse_abc_classification_product_level_ids", + inverse_name="product_tmpl_id", + store=True, + ) + + @api.depends( + "product_variant_ids", + "product_variant_ids.abc_classification_profile_ids", + ) + def _compute_abc_classification_profile_ids(self): + unique_variants = self.filtered( + lambda template: len(template.product_variant_ids) == 1 + ) + for template in unique_variants: + template.abc_classification_profile_ids = ( + template.product_variant_ids.abc_classification_profile_ids + ) + for template in self - unique_variants: + template.abc_classification_profile_ids = False + + @api.depends( + "product_variant_ids", + "product_variant_ids.abc_classification_product_level_ids", + ) + def _compute_abc_classification_product_level_ids(self): + unique_variants = self.filtered( + lambda template: len(template.product_variant_ids) == 1 + ) + for template in unique_variants: + variants = template.product_variant_ids + template.abc_classification_product_level_ids = ( + variants.abc_classification_product_level_ids + ) + for template in self - unique_variants: + template.abc_classification_product_level_ids = False + + def _inverse_abc_classification_profile_ids(self): + for template in self: + if len(template.product_variant_ids) == 1: + variants = template.product_variant_ids + variants.abc_classification_profile_ids = ( + template.abc_classification_profile_ids + ) + + def _inverse_abc_classification_product_level_ids(self): + for template in self: + if len(template.product_variant_ids) == 1: + variants = template.product_variant_ids + variants.abc_classification_product_level_ids = ( + template.abc_classification_product_level_ids + ) diff --git a/product_abc_classification/pyproject.toml b/product_abc_classification/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/product_abc_classification/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/product_abc_classification/readme/CONTRIBUTORS.md b/product_abc_classification/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..9b83a45f72f --- /dev/null +++ b/product_abc_classification/readme/CONTRIBUTORS.md @@ -0,0 +1,5 @@ +- Miquel Raïch \<\> +- Lindsay Marion \<\> +- Laurent Mignon \<\> +- Denis Roussel \<\> +- Hoang Diep \<\> \ No newline at end of file diff --git a/product_abc_classification/readme/CREDITS.md b/product_abc_classification/readme/CREDITS.md new file mode 100644 index 00000000000..fd7da320a35 --- /dev/null +++ b/product_abc_classification/readme/CREDITS.md @@ -0,0 +1 @@ +The migration of this module from 17.0 to 18.0 was financially supported by Camptocamp \ No newline at end of file diff --git a/product_abc_classification/readme/DESCRIPTION.md b/product_abc_classification/readme/DESCRIPTION.md new file mode 100644 index 00000000000..c636be2ab6a --- /dev/null +++ b/product_abc_classification/readme/DESCRIPTION.md @@ -0,0 +1,12 @@ +This modules provides the bases to build ABC analysis (or ABC +classification) addons. These classification are used by inventory +management teams to help identify the most important products in their +portfolio and ensure they prioritize managing them above those less +valuable. + +Managers will create a profile with several levels (percentages) and +then the profiled products will automatically get a corresponding level +using the ABC classification. + +The addon *product_abc_classification_sale_stock* defines a computation +profile based on the number of sale order line delivered by product. diff --git a/product_abc_classification/readme/USAGE.md b/product_abc_classification/readme/USAGE.md new file mode 100644 index 00000000000..bdf7aec4a4a --- /dev/null +++ b/product_abc_classification/readme/USAGE.md @@ -0,0 +1,13 @@ +To use this module, you need to: + +#\. Go to Inventory menu, then to Configuration/Products/ABC +Classification Profile and create a profile with levels, knowing that +the sum of all levels in the profile should sum 100 and all the levels +should be different. + +#\. Later you should go to product categories or product variants, and +assign them a profile. Then the cron classification will proceed to +assign to these products one of the profile's levels. + +NOTE: If you profile (or unprofile) a product category, then all its +child categories and products will be profiled (or unprofiled). diff --git a/product_abc_classification/security/ir.model.access.csv b/product_abc_classification/security/ir.model.access.csv new file mode 100644 index 00000000000..9283b353960 --- /dev/null +++ b/product_abc_classification/security/ir.model.access.csv @@ -0,0 +1,7 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_abc_classification_profile_user,abc.classification.profile.user,model_abc_classification_profile,base.group_user,1,0,0,0 +access_abc_classification_profile_manager,abc.classification.profile.manager,model_abc_classification_profile,stock.group_stock_manager,1,1,1,1 +access_abc_classification_level_user,abc.classification.level.user,model_abc_classification_level,base.group_user,1,0,0,0 +access_abc_classification_level_manager,abc.classification.level.manager,model_abc_classification_level,stock.group_stock_manager,1,1,1,1 +access_abc_classification_product_level_user,abc.classification.product.level.user,model_abc_classification_product_level,base.group_user,1,0,0,0 +access_abc_classification_product_level_manager,abc.classification.product.level.manager,model_abc_classification_product_level,stock.group_stock_manager,1,1,0,0 diff --git a/product_abc_classification/static/description/icon.png b/product_abc_classification/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/product_abc_classification/static/description/icon.png differ diff --git a/product_abc_classification/static/description/index.html b/product_abc_classification/static/description/index.html new file mode 100644 index 00000000000..29140cf59a0 --- /dev/null +++ b/product_abc_classification/static/description/index.html @@ -0,0 +1,463 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Product Abc Classification

+ +

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

+

This modules provides the bases to build ABC analysis (or ABC +classification) addons. These classification are used by inventory +management teams to help identify the most important products in their +portfolio and ensure they prioritize managing them above those less +valuable.

+

Managers will create a profile with several levels (percentages) and +then the profiled products will automatically get a corresponding level +using the ABC classification.

+

The addon product_abc_classification_sale_stock defines a computation +profile based on the number of sale order line delivered by product.

+

Table of contents

+ +
+

Usage

+

To use this module, you need to:

+

#. Go to Inventory menu, then to Configuration/Products/ABC +Classification Profile and create a profile with levels, knowing that +the sum of all levels in the profile should sum 100 and all the levels +should be different.

+

#. Later you should go to product categories or product variants, and +assign them a profile. Then the cron classification will proceed to +assign to these products one of the profile’s levels.

+

NOTE: If you profile (or unprofile) a product category, then all its +child categories and products will be profiled (or unprofiled).

+
+
+

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

Contributors

+ +
+
+

Other credits

+

The migration of this module from 17.0 to 18.0 was financially supported +by Camptocamp

+
+
+

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_abc_classification/tests/__init__.py b/product_abc_classification/tests/__init__.py new file mode 100644 index 00000000000..8292c06ca32 --- /dev/null +++ b/product_abc_classification/tests/__init__.py @@ -0,0 +1,3 @@ +from . import test_abc_classification_product_level +from . import test_abc_classification_profile +from . import test_product diff --git a/product_abc_classification/tests/common.py b/product_abc_classification/tests/common.py new file mode 100644 index 00000000000..29ba1a8b461 --- /dev/null +++ b/product_abc_classification/tests/common.py @@ -0,0 +1,141 @@ +# Copyright 2021 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.fields import Command +from odoo.tests.common import TransactionCase + + +class ABCClassificationCase(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + # add a fake profile_type + cls.ABCClassificationProfile = cls.env["abc.classification.profile"] + cls.ABCClassificationProfile._fields["profile_type"].selection = [ + ("test_type", "Test Type") + ] + cls.ABCClassificationProfile._fields["profile_type"]._selection = { + "test_type", + "Test Type", + } + cls.classification_profile = cls.ABCClassificationProfile.create( + {"name": "Profile test", "profile_type": "test_type"} + ) + + +class ABCClassificationLevelCase(ABCClassificationCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.classification_profile.write( + { + "level_ids": [ + ( + 0, + 0, + { + "percentage": 60, + "percentage_products": 40, + "name": "a", + }, + ), + ( + 0, + 0, + { + "percentage": 40, + "percentage_products": 60, + "name": "b", + }, + ), + ] + } + ) + + levels = cls.classification_profile.level_ids + cls.classification_level_a = levels.filtered(lambda level: level.name == "a") + cls.classification_level_b = levels.filtered(lambda level: level.name == "b") + cls.classification_profile_bis = cls.ABCClassificationProfile.create( + { + "name": "Profile test bis", + "profile_type": "test_type", + "level_ids": [ + ( + 0, + 0, + { + "percentage": 80, + "percentage_products": 40, + "name": "a", + }, + ), + ( + 0, + 0, + { + "percentage": 20, + "percentage_products": 60, + "name": "b", + }, + ), + ], + } + ) + levels = cls.classification_profile_bis.level_ids + cls.classification_level_bis_a = levels.filtered( + lambda level: level.name == "a" + ) + + cls.classification_level_bis_b = levels.filtered( + lambda level: level.name == "b" + ) + # create a template with one variant adn declare attributes to create + # another variant on demand + cls.size_attr = cls.env["product.attribute"].create( + { + "name": "Size", + "create_variant": "no_variant", + "value_ids": [(0, 0, {"name": "S"}), (0, 0, {"name": "M"})], + } + ) + cls.size_attr_value_s = cls.size_attr.value_ids[0] + cls.size_attr_value_m = cls.size_attr.value_ids[1] + cls.uom_unit = cls.env.ref("uom.product_uom_unit") + cls.category_all = cls.env["product.category"].create( + { + "name": "All", + } + ) + cls.product_template = cls.env["product.template"].create( + { + "name": "Test sized", + "uom_id": cls.uom_unit.id, + "categ_id": cls.category_all.id, + "attribute_line_ids": [ + ( + 0, + 0, + { + "attribute_id": cls.size_attr.id, + "value_ids": [(6, 0, cls.size_attr.value_ids.ids)], + }, + ) + ], + } + ) + cls.product_product = cls.product_template.product_variant_ids + cls.ProductLevel = cls.env["abc.classification.product.level"] + + @classmethod + def _create_variant(cls, size_value): + return cls.env["product.product"].create( + { + "product_tmpl_id": cls.product_template.id, + "product_template_attribute_value_ids": [ + Command.set( + size_value.pav_attribute_line_ids.product_template_value_ids.ids + ) + ], + } + ) diff --git a/product_abc_classification/tests/test_abc_classification_product_level.py b/product_abc_classification/tests/test_abc_classification_product_level.py new file mode 100644 index 00000000000..18d7786cf71 --- /dev/null +++ b/product_abc_classification/tests/test_abc_classification_product_level.py @@ -0,0 +1,362 @@ +# Copyright 2021 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from psycopg2 import IntegrityError + +from odoo.exceptions import ValidationError +from odoo.tools import mute_logger + +from .common import ABCClassificationLevelCase + + +class TestABCClassificationProductLevel(ABCClassificationLevelCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.product_1 = cls.env["product.product"].create( + { + "name": "Test 1", + "uom_id": cls.uom_unit.id, + } + ) + cls.product_level = cls.ProductLevel.create( + { + "product_id": cls.product_product.id, + "computed_level_id": cls.classification_level_a.id, + "profile_id": cls.classification_profile.id, + } + ) + + @classmethod + def _create_product_levels(cls): + product_2 = cls.env["product.product"].create( + { + "name": "Test 2", + "uom_id": cls.uom_unit.id, + } + ) + + product_3 = cls.env["product.product"].create( + { + "name": "Test 3", + "uom_id": cls.uom_unit.id, + } + ) + cls.ProductLevel.create( + { + "product_id": product_2.id, + "manual_level_id": cls.classification_level_b.id, + "computed_level_id": cls.classification_level_a.id, + "profile_id": cls.classification_profile.id, + } + ) + cls.ProductLevel.create( + { + "product_id": product_3.id, + "manual_level_id": cls.classification_level_b.id, + "computed_level_id": cls.classification_level_a.id, + "profile_id": cls.classification_profile.id, + } + ) + + def test_00(self): + """ + Test case: + Create a classification product level with only a computed_level_id + Expected result: + A instance is created with: + * the manual_level_id and level_id set + * flag is False since manual and computd are the same + + """ + level = self.ProductLevel.create( + { + "product_id": self.product_1.id, + "computed_level_id": self.classification_level_a.id, + "profile_id": self.classification_profile.id, + } + ) + self.assertEqual(level.manual_level_id, self.classification_level_a) + self.assertEqual(level.level_id, self.classification_level_a) + self.assertFalse(level.flag) + + def test_01(self): + """ + Test case: + Create product level with only a manual level + + A creation if a product level is created without computed value + the computed value is never taken into account + Expected result: + A new level is create with: + * computed_level_id = False + * level_id = manual_level_id + * flag = False + """ + level = self.ProductLevel.create( + { + "product_id": self.product_1.id, + "manual_level_id": self.classification_level_a.id, + "profile_id": self.classification_profile.id, + } + ) + self.assertFalse(level.computed_level_id) + self.assertEqual(level.manual_level_id, self.classification_level_a) + self.assertEqual(level.level_id, self.classification_level_a) + self.assertFalse(level.flag) + + def test_02(self): + """ + Data: + An existing classification level with computed = manual + Test case: + 1. Change manual_level_id to an other value than the computed one + 2. Reset manual_level_id to the computed one + Expected result: + 1. level_id === manual =! computed and flag is true + 2 level_id == manual == computed and flag is true + ValidationError + """ + self.assertFalse(self.product_level.flag) + self.assertEqual( + self.product_level.manual_level_id, + self.product_level.computed_level_id, + ) + self.assertEqual( + self.product_level.computed_level_id, self.classification_level_a + ) + self.assertEqual(self.product_level.level_id, self.classification_level_a) + # 1 + self.product_level.manual_level_id = self.classification_level_b + self.assertEqual(self.product_level.level_id, self.classification_level_b) + self.assertTrue(self.product_level.flag) + # 2 + self.product_level.manual_level_id = self.product_level.computed_level_id + self.assertEqual(self.product_level.level_id, self.classification_level_a) + self.assertFalse(self.product_level.flag) + + @mute_logger("odoo.sql_db") + def test_03(self): + """ + Data: + An existing product level + Test case: + Create a new product level for the same product and the same profile + Expected result: + IntegrityError (level name must be unique by profile and product) + """ + with self.assertRaises(IntegrityError): + self.ProductLevel.create( + { + "product_id": self.product_product.id, + "computed_level_id": self.classification_level_a.id, + "profile_id": self.classification_profile.id, + } + ) + + def test_04(self): + """ + Data: + An existing product level + Test case: + 1. Link a manual level from an other profile + 2. Link a computed level from an other profile + Expected result: + 1. and 2. Validation error (All the levels must share the same + profile as the one on the product level) + """ + with self.assertRaises(ValidationError), self.env.cr.savepoint(): + self.product_level.write( + { + "manual_level_id": self.classification_level_b.id, + "computed_level_id": self.classification_level_bis_a.id, + } + ) + with self.assertRaises(ValidationError), self.env.cr.savepoint(): + self.product_level.write( + { + "manual_level_id": self.classification_level_bis_a.id, + "computed_level_id": self.classification_level_a.id, + } + ) + self.product_level.write( + { + "manual_level_id": self.classification_level_bis_a.id, + "computed_level_id": self.classification_level_bis_a.id, + "profile_id": self.classification_profile_bis.id, + } + ) + + def test_05(self): + """ + Test case: + Create a product level without computed nor manual level + Expected result: + Validation error (at least a value for one of these fields is + expected) + """ + with self.assertRaises(ValidationError): + self.ProductLevel.create( + { + "product_id": self.product_1.id, + "profile_id": self.classification_profile.id, + } + ) + + def test_06_update_product_level_with_auto_compute(self): + self.classification_profile_bis.auto_apply_computed_value = True + self.product_level.write( + { + "computed_level_id": self.classification_level_bis_a.id, + "profile_id": self.classification_profile_bis.id, + } + ) + + self.assertEqual( + self.product_level.manual_level_id, + self.product_level.computed_level_id, + ) + self.assertEqual( + self.product_level.computed_level_id, self.classification_level_bis_a + ) + self.assertEqual(self.product_level.level_id, self.classification_level_bis_a) + + self.product_level.write( + { + "computed_level_id": self.classification_level_bis_b.id, + } + ) + self.assertEqual( + self.product_level.manual_level_id, + self.product_level.computed_level_id, + ) + self.assertEqual( + self.product_level.computed_level_id, self.classification_level_bis_b + ) + self.assertEqual(self.product_level.level_id, self.classification_level_bis_b) + + def test_07_update_product_level_without_auto_compute(self): + self.classification_profile.auto_apply_computed_value = False + self.product_level.write( + { + "manual_level_id": self.classification_level_b.id, + "computed_level_id": self.classification_level_a.id, + "profile_id": self.classification_profile.id, + } + ) + + self.assertNotEqual( + self.product_level.manual_level_id, + self.product_level.computed_level_id, + ) + self.assertEqual( + self.product_level.computed_level_id, self.classification_level_a + ) + self.assertEqual( + self.product_level.manual_level_id, self.classification_level_b + ) + self.assertEqual(self.product_level.level_id, self.classification_level_b) + + self.product_level.write( + { + "manual_level_id": self.classification_level_a.id, + "computed_level_id": self.classification_level_b.id, + } + ) + + self.assertNotEqual( + self.product_level.manual_level_id, + self.product_level.computed_level_id, + ) + self.assertEqual( + self.product_level.computed_level_id, self.classification_level_b + ) + self.assertEqual( + self.product_level.manual_level_id, self.classification_level_a + ) + self.assertEqual(self.product_level.level_id, self.classification_level_a) + + def test_08_update_recordset_with__autocompute(self): + self._create_product_levels() + self.classification_profile.auto_apply_computed_value = True + + levels = self.ProductLevel.search( + [("profile_id", "=", self.classification_profile.id)] + ) + levels.write( + { + "manual_level_id": self.classification_level_a.id, + "computed_level_id": self.classification_level_b.id, + } + ) + + for level in levels: + self.assertEqual(level.manual_level_id, level.computed_level_id) + self.assertEqual(level.manual_level_id, self.classification_level_b) + self.assertEqual(level.computed_level_id, self.classification_level_b) + self.assertEqual(level.level_id, self.classification_level_b) + + def test_09_update_recordset_and_change_profile(self): + self._create_product_levels() + self.classification_profile_bis.auto_apply_computed_value = True + + levels = self.ProductLevel.search( + [("profile_id", "=", self.classification_profile.id)] + ) + levels.write( + { + "computed_level_id": self.classification_level_bis_a.id, + "profile_id": self.classification_profile_bis.id, + } + ) + + for level in levels: + self.assertEqual(level.manual_level_id, level.computed_level_id) + self.assertEqual(level.manual_level_id, self.classification_level_bis_a) + self.assertEqual(level.computed_level_id, self.classification_level_bis_a) + self.assertEqual(level.level_id, self.classification_level_bis_a) + + def test_10_create_product_level_for_profile_auto_assign(self): + self.classification_profile.auto_apply_computed_value = True + level = self.ProductLevel.create( + { + "product_id": self.product_1.id, + "manual_level_id": self.classification_level_b.id, + "computed_level_id": self.classification_level_a.id, + "profile_id": self.classification_profile.id, + } + ) + self.assertEqual(level.manual_level_id, level.computed_level_id) + self.assertEqual(level.manual_level_id, self.classification_level_a) + self.assertEqual(level.computed_level_id, self.classification_level_a) + self.assertEqual(level.level_id, self.classification_level_a) + + def test_11_auto_apply_computed_level(self): + self._create_product_levels() + + levels = self.ProductLevel.search( + [("profile_id", "=", self.classification_profile.id)] + ) + level0 = levels[0] + level1 = levels[1] + level2 = levels[2] + self.assertEqual(level0.manual_level_id, level0.computed_level_id) + self.assertEqual(level0.manual_level_id, self.classification_level_a) + self.assertEqual(level0.computed_level_id, self.classification_level_a) + self.assertEqual(level0.level_id, self.classification_level_a) + + self.assertNotEqual(level1.manual_level_id, level1.computed_level_id) + self.assertEqual(level1.manual_level_id, self.classification_level_b) + self.assertEqual(level1.computed_level_id, self.classification_level_a) + self.assertEqual(level1.level_id, self.classification_level_b) + + self.assertNotEqual(level2.manual_level_id, level2.computed_level_id) + self.assertEqual(level2.manual_level_id, self.classification_level_b) + self.assertEqual(level2.computed_level_id, self.classification_level_a) + self.assertEqual(level2.level_id, self.classification_level_b) + + self.classification_profile.auto_apply_computed_value = True + for level in levels: + self.assertEqual(level.manual_level_id, self.classification_level_a) + self.assertEqual(level.computed_level_id, self.classification_level_a) + self.assertEqual(level.level_id, self.classification_level_a) diff --git a/product_abc_classification/tests/test_abc_classification_profile.py b/product_abc_classification/tests/test_abc_classification_profile.py new file mode 100644 index 00000000000..68044ba97bf --- /dev/null +++ b/product_abc_classification/tests/test_abc_classification_profile.py @@ -0,0 +1,303 @@ +# Copyright 2021 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from psycopg2 import IntegrityError + +from odoo.exceptions import ValidationError +from odoo.tools.misc import mute_logger + +from .common import ABCClassificationCase + + +class TestABCClassificationProfile(ABCClassificationCase): + def test_00(self): + """ + Data: + A test profile + Test case: + Assign levels for a total of 100% + Expected result: + OK + """ + self.classification_profile.write( + { + "level_ids": [ + ( + 0, + 0, + { + "percentage": 60, + "percentage_products": 40, + "name": "A", + }, + ), + ( + 0, + 0, + { + "percentage": 40, + "percentage_products": 60, + "name": "B", + }, + ), + ] + } + ) + self.assertEqual(len(self.classification_profile.level_ids), 2) + + def test_01(self): + """ + Data: + A test profile + Test case: + Assign levels for a total < 100% + Expected result: + ValidationError + """ + with self.assertRaises(ValidationError): + self.classification_profile.write( + { + "level_ids": [ + ( + 0, + 0, + { + "percentage": 60, + "percentage_products": 40, + "name": "A", + }, + ), + ( + 0, + 0, + { + "percentage": 30, + "percentage_products": 60, + "name": "B", + }, + ), + ] + } + ) + + def test_02(self): + """ + Data: + A test profile + Test case: + Assign levels for a total > 100% + Expected result: + ValidationError + """ + with self.assertRaises(ValidationError): + self.classification_profile.write( + { + "level_ids": [ + ( + 0, + 0, + { + "percentage": 60, + "percentage_products": 40, + "name": "A", + }, + ), + ( + 0, + 0, + { + "percentage": 50, + "percentage_products": 60, + "name": "B", + }, + ), + ] + } + ) + + def test_03(self): + """ + Data: + A test profile + Test case: + Assign levels for a total = 100% but with same percentage + Expected result: + ValidationError + """ + with self.assertRaises(ValidationError): + self.classification_profile.write( + { + "level_ids": [ + ( + 0, + 0, + { + "percentage": 50, + "percentage_products": 40, + "name": "A", + }, + ), + ( + 0, + 0, + { + "percentage": 50, + "percentage_products": 60, + "name": "B", + }, + ), + ] + } + ) + + def test_04(self): + """ + Data: + A test profile + Test case: + Assign levels for a total = 100% but with one level with negative + percentage and one level exceeding 100% + Expected result: + ValidationError + """ + with self.assertRaises(ValidationError): + self.classification_profile.write( + { + "level_ids": [ + ( + 0, + 0, + { + "percentage": 150, + "percentage_products": 40, + "name": "A", + }, + ), + ( + 0, + 0, + { + "percentage": -50, + "percentage_products": 60, + "name": "B", + }, + ), + ] + } + ) + + @mute_logger("odoo.sql_db") + def test_05(self): + """ + Data: + A test profile + Test case: + Assign levels for a total = 100% but with same name + Expected result: + IntegrityError (level name must be unique by profile) + """ + with self.assertRaises(IntegrityError): + self.classification_profile.write( + { + "level_ids": [ + ( + 0, + 0, + { + "percentage": 60, + "percentage_products": 40, + "name": "A", + }, + ), + ( + 0, + 0, + { + "percentage": 40, + "percentage_products": 60, + "name": "A", + }, + ), + ] + } + ) + + def test_06(self): + """ + Data: + A test profile with 2 levels A and B + Test case: + Create a new profile with the same level name + Expected result: + Profile created without error since the level name is unique by + profile + """ + self.classification_profile.write( + { + "level_ids": [ + ( + 0, + 0, + { + "percentage": 60, + "percentage_products": 40, + "name": "A", + }, + ), + ( + 0, + 0, + { + "percentage": 40, + "percentage_products": 60, + "name": "B", + }, + ), + ] + } + ) + new_profile = self.ABCClassificationProfile.create( + { + "name": "New Profile test", + "profile_type": "test_type", + "level_ids": [ + ( + 0, + 0, + { + "percentage": 60, + "percentage_products": 40, + "name": "A", + }, + ), + ( + 0, + 0, + { + "percentage": 40, + "percentage_products": 60, + "name": "B", + }, + ), + ], + } + ) + self.assertTrue(new_profile) + + @mute_logger("odoo.sql_db") + def test_07(self): + """ + Data: + A test profile + Test case: + Create a new profile with the same name + Expected result: + IntegrityError (profile name must be unique by profile) + """ + with self.assertRaises(IntegrityError): + self.ABCClassificationProfile.create( + { + "name": self.classification_profile.name, + "profile_type": "test_type", + } + ) diff --git a/product_abc_classification/tests/test_product.py b/product_abc_classification/tests/test_product.py new file mode 100644 index 00000000000..350fd2b9e0c --- /dev/null +++ b/product_abc_classification/tests/test_product.py @@ -0,0 +1,179 @@ +# Copyright 2021 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from .common import ABCClassificationLevelCase + + +class TestProduct(ABCClassificationLevelCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + def test_00(self): + """ + Data: + A product template with one variant. + Test Case: + 1. Associate a classification profile to the template + 2. Unset the classifiation profile + Expected: + 1. The classification profile is also associated to the variant + 2. The classification profile no more associated to the variant + """ + self.assertFalse(self.product_template.abc_classification_profile_ids) + self.assertFalse(self.product_product.abc_classification_profile_ids) + # 1 + self.product_template.abc_classification_profile_ids = ( + self.classification_profile + ) + self.assertEqual( + self.product_product.abc_classification_profile_ids, + self.classification_profile, + ) + # 2 + self.product_template.abc_classification_profile_ids = False + self.assertFalse(self.product_product.abc_classification_profile_ids) + + def test_01(self): + """ + Data: + A product template with two variants (without profiles). + Test Case: + 1. Associate a classification profile to the template + Expected: + The classification profile is not associated to the variant + """ + self._create_variant(self.size_attr_value_m) + variants = self.product_template.product_variant_ids + self.assertEqual(len(variants), 2) + self.assertFalse(variants.mapped("abc_classification_profile_ids")) + self.product_template.abc_classification_profile_ids = ( + self.classification_profile + ) + self.assertFalse(variants.mapped("abc_classification_profile_ids")) + + def test_02(self): + """ + Data: + A product template with one variant + Test Case: + 1 Associate a product level to the variant + 2 unlink the level + Expected result: + 1 The product level is also associated to the template + 2 No more level associated to the template + """ + product_level = self.ProductLevel.create( + { + "product_id": self.product_product.id, + "computed_level_id": self.classification_level_a.id, + "profile_id": self.classification_profile.id, + } + ) + self.assertEqual( + self.product_product.abc_classification_product_level_ids, + product_level, + ) + self.assertEqual( + self.product_template.abc_classification_product_level_ids, + product_level, + ) + product_level.unlink() + + self.assertFalse(self.product_product.abc_classification_product_level_ids) + self.assertFalse(self.product_template.abc_classification_product_level_ids) + + def test_03(self): + """ + Data: + A product template with two variants + Test Case: + Associate a product level to one variant + Expected result: + The product level is not associated to the template + """ + new_variant = self._create_variant(self.size_attr_value_m) + variants = self.product_template.product_variant_ids + self.assertEqual(len(variants), 2) + product_level = self.ProductLevel.create( + { + "product_id": new_variant.id, + "computed_level_id": self.classification_level_a.id, + "profile_id": self.classification_profile.id, + } + ) + self.assertEqual( + new_variant.abc_classification_product_level_ids, + product_level, + ) + self.assertFalse(self.product_template.abc_classification_product_level_ids) + + def test_04(self): + """ + Data: + A product template + Test case: + Check if resource id in action is the product variant one + """ + self.product_template.abc_classification_profile_ids = ( + self.classification_profile + ) + action = self.classification_profile.action_view_products() + self.assertEqual(action["res_id"], self.product_template.product_variant_ids.id) + + def test_05(self): + """ + Data: + A product template with two variants + Test case: + Check if doamin in action is the product variants ids + """ + self._create_variant(self.size_attr_value_m) + self.product_template.product_variant_ids.abc_classification_profile_ids = ( + self.classification_profile + ) + action = self.classification_profile.action_view_products() + self.assertEqual( + action["domain"], + [("id", "in", self.product_template.product_variant_ids.ids)], + ) + + def test_06(self): + """ + Data: + A product template with one variant + Test Case: + Associate a classification profile to the category + Expected result: + The variant is associated to the classification profile + """ + self.product_template.categ_id.abc_classification_profile_ids = ( + self.classification_profile + ) + self.product_product._onchange_categ_id_abc_classification() + self.assertEqual( + self.product_product.abc_classification_profile_ids, + self.classification_profile, + ) + + def test_07(self): + """ + Data: + A product template with one variant + Test Case: + 1 Create new category + 2 Associate a classification profile to the category + 3 Create new product + Expected result: + The product is associated to the classification profile + """ + new_category = self.env["product.category"].create( + {"name": "Test Category ABC"} + ) + new_category.abc_classification_profile_ids = self.classification_profile_bis + new_template = self.env["product.template"].create( + {"name": "Test Template ABC", "categ_id": new_category.id} + ) + self.assertEqual( + new_template.abc_classification_profile_ids, self.classification_profile_bis + ) diff --git a/product_abc_classification/views/abc_classification_product_level.xml b/product_abc_classification/views/abc_classification_product_level.xml new file mode 100644 index 00000000000..c131a985596 --- /dev/null +++ b/product_abc_classification/views/abc_classification_product_level.xml @@ -0,0 +1,98 @@ + + + + + abc.classification.product.level.form (in product_abc_classification) + abc.classification.product.level + +
+ + + + + + + + + + + + + + + + + +
+
+ + abc.classification.product.level.tree (in product_abc_classification) + abc.classification.product.level + + + + + + + + + + + + abc.classification.product.level.search (in product_abc_classification) + abc.classification.product.level + + + + + + + + + + + + + + + + Products ABC Classification + abc.classification.product.level + list,form + {'search_default_group_by_level': 1} + + +
diff --git a/product_abc_classification/views/abc_classification_profile.xml b/product_abc_classification/views/abc_classification_profile.xml new file mode 100644 index 00000000000..5af737940c6 --- /dev/null +++ b/product_abc_classification/views/abc_classification_profile.xml @@ -0,0 +1,84 @@ + + + + + abc.classification.profile.form (in product_abc_classification) + abc.classification.profile + +
+ +
+ +
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + abc.classification.profile.tree (in product_abc_classification) + abc.classification.profile + + + + + + + + ABC Classification profiles + abc.classification.profile + list,form + + +
diff --git a/product_abc_classification/views/product_category.xml b/product_abc_classification/views/product_category.xml new file mode 100644 index 00000000000..ff03f63168a --- /dev/null +++ b/product_abc_classification/views/product_category.xml @@ -0,0 +1,27 @@ + + + + + product.category.form - product_abc_classification + product.category + + + + + +