diff --git a/product_stock_state/README.rst b/product_stock_state/README.rst new file mode 100644 index 00000000000..25ddf2eb55e --- /dev/null +++ b/product_stock_state/README.rst @@ -0,0 +1,150 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +=================== +Product Stock State +=================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:da3df1bb7f990265eeeb1c54b1e95006b4acdcf1385ac40de70dcb9574f4fafc + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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_stock_state + :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_stock_state + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/product-attribute&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds a "stock state" field on the product in order to inform +the user of its general stock state at a glance. + +The state value can be : + +- In Stock +- In Limited Stock (if qty available is under a threshold) +- Resupplying (if qty forcasted is > 0) +- Out of Stock (otherwise) + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +You can configure thresholds : + +- Globally, for a company, in the Sale Settings. It will be used for all + the products of the company. +- On product category form. It will be used for all the products of this + category, or of the child categories. (User should be part of the new + group 'Stock State Threshold by Category'.) +- On product template form, for a specific product. (User should be part + of the new group 'Stock State Threshold by Product'.) + +Usage +===== + +Open product tree view and observe the stock state + +|image| + +.. |image| image:: https://raw.githubusercontent.com/product_stock_state/static/description/product_product_tree.png + +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 +------- + +* Akretion +* GRAP + +Contributors +------------ + +- Sebastien Beau + +- Sylvain LE GAL + +- Kevin Khao + +- Simone Orsi + +- Watthanun Khorchai + +- `Trobz `__: + + - Tuan Nguyen + +Other credits +------------- + +The development of this module has been financially supported by: + +- Akretion R&D +- Adaptoo +- GRAP, Groupement Régional Alimentaire de Proximité + + +The migration of this module from 16.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. + +.. |maintainer-sebastienbeau| image:: https://github.com/sebastienbeau.png?size=40px + :target: https://github.com/sebastienbeau + :alt: sebastienbeau +.. |maintainer-legalsylvain| image:: https://github.com/legalsylvain.png?size=40px + :target: https://github.com/legalsylvain + :alt: legalsylvain +.. |maintainer-kevinkhao| image:: https://github.com/kevinkhao.png?size=40px + :target: https://github.com/kevinkhao + :alt: kevinkhao + +Current `maintainers `__: + +|maintainer-sebastienbeau| |maintainer-legalsylvain| |maintainer-kevinkhao| + +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_stock_state/__init__.py b/product_stock_state/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/product_stock_state/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/product_stock_state/__manifest__.py b/product_stock_state/__manifest__.py new file mode 100644 index 00000000000..36ec25294c6 --- /dev/null +++ b/product_stock_state/__manifest__.py @@ -0,0 +1,35 @@ +# Copyright 2017 Akretion (http://www.akretion.com). +# Copyright 2017-Today GRAP (http://www.grap.coop). +# @author Sébastien BEAU +# @author Sylvain LE GAL +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +{ + "name": "Product Stock State", + "summary": "Compute the state of a product's stock" + "the stock level and sale_ok field", + "version": "19.0.1.0.0", + "website": "https://github.com/OCA/product-attribute", + "author": " Akretion, GRAP, Odoo Community Association (OCA)", + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": ["sale_stock"], + "data": [ + "security/res_groups.xml", + "views/product_template_view.xml", + "views/product_product_view.xml", + "views/product_category_view.xml", + "views/res_config_settings_view.xml", + "views/res_company_view.xml", + "data/data.xml", + ], + "demo": [ + "demo/res_groups.xml", + "demo/product_product.xml", + "demo/product_category.xml", + ], + "qweb": [], + "maintainers": ["sebastienbeau", "legalsylvain", "kevinkhao"], +} diff --git a/product_stock_state/data/data.xml b/product_stock_state/data/data.xml new file mode 100644 index 00000000000..033a428247c --- /dev/null +++ b/product_stock_state/data/data.xml @@ -0,0 +1,8 @@ + + + + + Stock Threshold + 2 + + diff --git a/product_stock_state/demo/product_category.xml b/product_stock_state/demo/product_category.xml new file mode 100644 index 00000000000..e75d729cf56 --- /dev/null +++ b/product_stock_state/demo/product_category.xml @@ -0,0 +1,6 @@ + + + + 20 + + diff --git a/product_stock_state/demo/product_product.xml b/product_stock_state/demo/product_product.xml new file mode 100644 index 00000000000..f2fbb18f88d --- /dev/null +++ b/product_stock_state/demo/product_product.xml @@ -0,0 +1,16 @@ + + + + Product with threshold set on the company + + + + + Product with threshold set on the product + + 30 + + + 66 + + diff --git a/product_stock_state/demo/res_groups.xml b/product_stock_state/demo/res_groups.xml new file mode 100644 index 00000000000..eb6fde03e10 --- /dev/null +++ b/product_stock_state/demo/res_groups.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/product_stock_state/i18n/es.po b/product_stock_state/i18n/es.po new file mode 100644 index 00000000000..094477ace83 --- /dev/null +++ b/product_stock_state/i18n/es.po @@ -0,0 +1,165 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_stock_state +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2026-04-19 07:45+0000\n" +"Last-Translator: Ed-Spain \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 5.15.2\n" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_res_company +msgid "Companies" +msgstr "Compañías" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_res_config_settings +msgid "Config Settings" +msgstr "Ajustes Configuración" + +#. module: product_stock_state +#: model:ir.model.fields,help:product_stock_state.field_res_config_settings__stock_state_threshold +msgid "" +"Define custom value under which the stock state will pass from 'In Stock' to " +"'In Limited Stock' State." +msgstr "" +"Defina el valor personalizado por debajo del cual el estado de stock pasará " +"del estado 'En stock' al estado 'En stock limitado'." + +#. module: product_stock_state +#: model:ir.model.fields,help:product_stock_state.field_product_product__stock_state_threshold +#: model:ir.model.fields,help:product_stock_state.field_product_template__stock_state_threshold +msgid "" +"Define custom value under which the stock state will pass from 'In Stock' to " +"'In Limited Stock' State. If not set, Odoo will use the value defined in the " +"product category. If no value is defined in product category, it will use " +"the value defined for the company" +msgstr "" +"Defina el valor personalizado por debajo del cual el estado de stock pasará " +"del estado 'En stock' al estado 'En stock limitado'. Si no se establece, " +"Odoo utilizará el valor definido en la categoría de producto. Si no se " +"define ningún valor en la categoría de producto, se utilizará el valor " +"definido para la empresa" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__in_limited_stock +msgid "In Limited Stock" +msgstr "En existencias limitadas" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__in_stock +msgid "In Stock" +msgstr "Disponible" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_category__manual_stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__manual_stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_template__manual_stock_state_threshold +msgid "Manual Stock State Threshold" +msgstr "Umbral Manual del Estado de las Existencias" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__out_of_stock +msgid "Out Of Stock" +msgstr "Agotado" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_product_template +msgid "Product" +msgstr "Producto" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_product_category +msgid "Product Category" +msgstr "Categoría de Producto" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_product_product +msgid "Product Variant" +msgstr "Variante del Producto" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.res_config_settings_view_form_threshold +msgid "Product stock thresholds" +msgstr "Umbrales de stock del producto" + +#. module: product_stock_state +#: model:product.template,name:product_stock_state.product_setting_by_company_product_template +msgid "Product with threshold set on the company" +msgstr "Producto con umbral establecido en la compañía" + +#. module: product_stock_state +#: model:product.template,name:product_stock_state.product_setting_by_product_product_template +msgid "Product with threshold set on the product" +msgstr "Producto con umbral establecido en el producto" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__resupplying +msgid "Resupplying" +msgstr "Reabastecimiento" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.res_config_settings_view_form_threshold +msgid "Set stock state threshold" +msgstr "Establecer umbral de estado de existencias" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__stock_state +msgid "Stock State" +msgstr "Estado de Existencias" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_category__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_template__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_res_company__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_res_config_settings__stock_state_threshold +msgid "Stock State Threshold" +msgstr "Umbral del Estado de Existencias" + +#. module: product_stock_state +#: model:res.groups,name:product_stock_state.group_stock_state_by_category +msgid "Stock State Threshold by Category" +msgstr "Umbral del Estado de Existencias por Categoría" + +#. module: product_stock_state +#: model:res.groups,name:product_stock_state.group_stock_state_by_product +msgid "Stock State Threshold by Product" +msgstr "Umbral de Estado de Existencias por Producto" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.view_product_category_form +#: model_terms:ir.ui.view,arch_db:product_stock_state.view_product_template_form +msgid "Stock Threshold" +msgstr "Umbral de existencias" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.res_company_view_form_threshold +msgid "Stock parameters" +msgstr "Parámetros de existencias" + +#. module: product_stock_state +#: model:ir.model.fields,help:product_stock_state.field_product_category__stock_state_threshold +msgid "" +"The custom value under which the stock state of the products of this " +"category will pass from 'In Stock' to 'In Limited Stock' State. If not set, " +"Odoo will use the threshold defined at the company level." +msgstr "" +"El valor personalizado bajo el cual el estado de stock de los productos de " +"esta categoría pasará de 'En Stock' a 'En Existencias Limitadas'. Si no se " +"establece, Odoo utilizará el umbral definido a nivel de empresa." + +#~ msgid "Product stock thresholds" +#~ msgstr "Umbrales de stock de productos" + +#~ msgid "Stock threshold" +#~ msgstr "Umbral de existencias" diff --git a/product_stock_state/i18n/fr.po b/product_stock_state/i18n/fr.po new file mode 100644 index 00000000000..d4d921fbc89 --- /dev/null +++ b/product_stock_state/i18n/fr.po @@ -0,0 +1,177 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_stock_state +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-03-19 11:23+0000\n" +"PO-Revision-Date: 2020-03-19 12:37+0100\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" +"X-Generator: Poedit 2.3\n" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_res_company +msgid "Companies" +msgstr "Sociétés" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_res_config_settings +msgid "Config Settings" +msgstr "Paramètres de config" + +#. module: product_stock_state +#: model:ir.model.fields,help:product_stock_state.field_res_config_settings__stock_state_threshold +msgid "" +"Define custom value under which the stock state will pass from 'In Stock' to " +"'In Limited Stock' State." +msgstr "" +"Définir la valeur seuil déterminant l'état \"En stock\" et l'état \"En stock " +"limité\"." + +#. module: product_stock_state +#: model:ir.model.fields,help:product_stock_state.field_product_product__stock_state_threshold +#: model:ir.model.fields,help:product_stock_state.field_product_template__stock_state_threshold +msgid "" +"Define custom value under which the stock state will pass from 'In Stock' to " +"'In Limited Stock' State. If not set, Odoo will use the value defined in the " +"product category. If no value is defined in product category, it will use " +"the value defined for the company" +msgstr "" +"Définir la valeur seuil déterminant l'état \"En stock\" et l'état \"En stock " +"limité\". Si elle n'est pas définie, Odoo utilisera la valeur définie dans " +"la catégorie d'article. Si aucune valeur n'y est définie, Odoo utilisera la " +"valeur définie au niveau de l'entreprise" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__in_limited_stock +msgid "In Limited Stock" +msgstr "En stock limité" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__in_stock +msgid "In Stock" +msgstr "En stock" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_category__manual_stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__manual_stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_template__manual_stock_state_threshold +msgid "Manual Stock State Threshold" +msgstr "Seuil pour l'état du stock manuel" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__out_of_stock +msgid "Out Of Stock" +msgstr "En rupture de stock" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_product_template +msgid "Product" +msgstr "Article" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_product_category +msgid "Product Category" +msgstr "Catégorie d'article" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_product_product +msgid "Product Variant" +msgstr "" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.res_config_settings_view_form_threshold +msgid "Product stock thresholds" +msgstr "" + +#. module: product_stock_state +#: model:product.template,name:product_stock_state.product_setting_by_company_product_template +msgid "Product with threshold set on the company" +msgstr "Article avec seuil fixé au niveau de l'entreprise" + +#. module: product_stock_state +#: model:product.template,name:product_stock_state.product_setting_by_product_product_template +msgid "Product with threshold set on the product" +msgstr "Article avec seuil fixé au niveau de l'article" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__resupplying +msgid "Resupplying" +msgstr "En réapprovisionnement" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.res_config_settings_view_form_threshold +msgid "Set stock state threshold" +msgstr "Définir le seuil pour l'état du stock" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__stock_state +msgid "Stock State" +msgstr "État du stock" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_category__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_template__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_res_company__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_res_config_settings__stock_state_threshold +msgid "Stock State Threshold" +msgstr "Seuil pour l'état du stock" + +#. module: product_stock_state +#: model:res.groups,name:product_stock_state.group_stock_state_by_category +msgid "Stock State Threshold by Category" +msgstr "Seuil pour l'état du stock par catégorie" + +#. module: product_stock_state +#: model:res.groups,name:product_stock_state.group_stock_state_by_product +msgid "Stock State Threshold by Product" +msgstr "Seuil pour l'état du stock par produit" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.view_product_category_form +#: model_terms:ir.ui.view,arch_db:product_stock_state.view_product_template_form +msgid "Stock Threshold" +msgstr "Seuil de stock" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.res_company_view_form_threshold +msgid "Stock parameters" +msgstr "Paramètres de stock" + +#. module: product_stock_state +#: model:ir.model.fields,help:product_stock_state.field_product_category__stock_state_threshold +msgid "" +"The custom value under which the stock state of the products of this " +"category will pass from 'In Stock' to 'In Limited Stock' State. If not set, " +"Odoo will use the threshold defined at the company level." +msgstr "" +"La valeur personnalisée sous laquelle l'état de stock des articles sous " +"cette catégorie passera de l'état \"En stock\" à \"En stock limité\". Si " +"elle n'est pas définie, Odoo utilisera le seuil défini au niveau de " +"l'entreprise." + +#~ msgid "Product stock thresholds" +#~ msgstr "" +#~ "Seuils des articles d'états de stocks" + +#~ msgid "Stock threshold" +#~ msgstr "Seuil de stock" + +#~ msgid "Product Template" +#~ msgstr "Modèle d'article" + +#, fuzzy +#~ msgid "Units" +#~ msgstr "Unité(s)" + +#~ msgid "kg" +#~ msgstr "kg" diff --git a/product_stock_state/i18n/it.po b/product_stock_state/i18n/it.po new file mode 100644 index 00000000000..6d3f482948d --- /dev/null +++ b/product_stock_state/i18n/it.po @@ -0,0 +1,165 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_stock_state +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-02-05 12:06+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.6.2\n" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_res_company +msgid "Companies" +msgstr "Aziende" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_res_config_settings +msgid "Config Settings" +msgstr "Impostazioni configurazione" + +#. module: product_stock_state +#: model:ir.model.fields,help:product_stock_state.field_res_config_settings__stock_state_threshold +msgid "" +"Define custom value under which the stock state will pass from 'In Stock' to " +"'In Limited Stock' State." +msgstr "" +"Definire un valore personalizzato sotto il quale lo stato della giacienza " +"passerà da stato 'Disponibile' a 'Disponibilità limitata'." + +#. module: product_stock_state +#: model:ir.model.fields,help:product_stock_state.field_product_product__stock_state_threshold +#: model:ir.model.fields,help:product_stock_state.field_product_template__stock_state_threshold +msgid "" +"Define custom value under which the stock state will pass from 'In Stock' to " +"'In Limited Stock' State. If not set, Odoo will use the value defined in the " +"product category. If no value is defined in product category, it will use " +"the value defined for the company" +msgstr "" +"Definire un valore personalizzato sotto il quale lo stato giacenza passerà " +"da stato ' Disponibile' a 'Disponibilità limitata'. Se non impostato, Odoo " +"utilizzerà il valore definito nella categoria prodotto. Se non c'è un valore " +"definito nella categoria prodotto, verrà utilizzato il valore definito per " +"l'azienda" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__in_limited_stock +msgid "In Limited Stock" +msgstr "Disponibilità limitata" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__in_stock +msgid "In Stock" +msgstr "Disponibile" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_category__manual_stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__manual_stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_template__manual_stock_state_threshold +msgid "Manual Stock State Threshold" +msgstr "Soglia maunale stato giacenza" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__out_of_stock +msgid "Out Of Stock" +msgstr "Non disponibile" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_product_template +msgid "Product" +msgstr "Prodotto" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_product_category +msgid "Product Category" +msgstr "Categoria prodotto" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_product_product +msgid "Product Variant" +msgstr "Variante prodotto" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.res_config_settings_view_form_threshold +msgid "Product stock thresholds" +msgstr "Soglie giacenza prodotto" + +#. module: product_stock_state +#: model:product.template,name:product_stock_state.product_setting_by_company_product_template +msgid "Product with threshold set on the company" +msgstr "Prodotto con soglia impostata nell'azienda" + +#. module: product_stock_state +#: model:product.template,name:product_stock_state.product_setting_by_product_product_template +msgid "Product with threshold set on the product" +msgstr "Prodotto con soglia impostata nel prodotto" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__resupplying +msgid "Resupplying" +msgstr "Rifornimento" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.res_config_settings_view_form_threshold +msgid "Set stock state threshold" +msgstr "Imposta soglia stato giacenza" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__stock_state +msgid "Stock State" +msgstr "Stato giacenza" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_category__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_template__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_res_company__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_res_config_settings__stock_state_threshold +msgid "Stock State Threshold" +msgstr "Soglia stato giacenza" + +#. module: product_stock_state +#: model:res.groups,name:product_stock_state.group_stock_state_by_category +msgid "Stock State Threshold by Category" +msgstr "Soglia stato giacenza per categoria" + +#. module: product_stock_state +#: model:res.groups,name:product_stock_state.group_stock_state_by_product +msgid "Stock State Threshold by Product" +msgstr "Soglia stato giacenza per prodotto" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.view_product_category_form +#: model_terms:ir.ui.view,arch_db:product_stock_state.view_product_template_form +msgid "Stock Threshold" +msgstr "Soglia giacenza" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.res_company_view_form_threshold +msgid "Stock parameters" +msgstr "Parametri giacenza" + +#. module: product_stock_state +#: model:ir.model.fields,help:product_stock_state.field_product_category__stock_state_threshold +msgid "" +"The custom value under which the stock state of the products of this " +"category will pass from 'In Stock' to 'In Limited Stock' State. If not set, " +"Odoo will use the threshold defined at the company level." +msgstr "" +"Il valore personalizzato sotto il quale lo stato giacenza del prodotto di " +"questa categoria passerà da stato 'Disponibile' a 'Disponibilità limitata'. " +"Se non impostato, Odoo utilizzerà la soglia definita a livello azienda." + +#~ msgid "Product stock thresholds" +#~ msgstr "Soglie quantità prodotto" + +#~ msgid "Stock threshold" +#~ msgstr "Soglia giacenza" diff --git a/product_stock_state/i18n/nl.po b/product_stock_state/i18n/nl.po new file mode 100644 index 00000000000..1f17acf894f --- /dev/null +++ b/product_stock_state/i18n/nl.po @@ -0,0 +1,186 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_stock_state +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2021-05-09 16:47+0000\n" +"Last-Translator: Bosd \n" +"Language-Team: none\n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.3.2\n" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_res_company +msgid "Companies" +msgstr "Bedrijven" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: product_stock_state +#: model:ir.model.fields,help:product_stock_state.field_res_config_settings__stock_state_threshold +msgid "" +"Define custom value under which the stock state will pass from 'In Stock' to " +"'In Limited Stock' State." +msgstr "" +"Defieneer een aangepaste drempelwaarde waaronder de voorraad status zal " +"wijzigen van 'In voorraad' naar 'Beperkte voorraad' status." + +#. module: product_stock_state +#: model:ir.model.fields,help:product_stock_state.field_product_product__stock_state_threshold +#: model:ir.model.fields,help:product_stock_state.field_product_template__stock_state_threshold +msgid "" +"Define custom value under which the stock state will pass from 'In Stock' to " +"'In Limited Stock' State. If not set, Odoo will use the value defined in the " +"product category. If no value is defined in product category, it will use " +"the value defined for the company" +msgstr "" +"Defieneer een aangepast niveau waaronder de voorraad status zal wijzigen van " +"'In voorraad' naar 'Beperkte voorraad' status. Wanneer geen waarde is " +"opgegeven dan zal de standaard waarde zal worden gebruikt zoals opgegeven in " +"de product categorie. Wanneer er geen waarde is opgegeven in de product " +"categorie, dan wordt standaard waarde gebruikt uit de bedrijfsinstellingen" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__in_limited_stock +msgid "In Limited Stock" +msgstr "" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__in_stock +msgid "In Stock" +msgstr "" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_category__manual_stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__manual_stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_template__manual_stock_state_threshold +msgid "Manual Stock State Threshold" +msgstr "Handmatig voorraad niveau drempelwaarde" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__out_of_stock +msgid "Out Of Stock" +msgstr "" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_product_template +msgid "Product" +msgstr "Product" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_product_category +msgid "Product Category" +msgstr "Product Categorie" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_product_product +msgid "Product Variant" +msgstr "" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.res_config_settings_view_form_threshold +msgid "Product stock thresholds" +msgstr "" + +#. module: product_stock_state +#: model:product.template,name:product_stock_state.product_setting_by_company_product_template +msgid "Product with threshold set on the company" +msgstr "" + +#. module: product_stock_state +#: model:product.template,name:product_stock_state.product_setting_by_product_product_template +msgid "Product with threshold set on the product" +msgstr "Product met drempelwaarde bepaald op het product" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__resupplying +msgid "Resupplying" +msgstr "" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.res_config_settings_view_form_threshold +msgid "Set stock state threshold" +msgstr "Stel vooraad niveeau dremeplwaarde in" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__stock_state +msgid "Stock State" +msgstr "Voorraad status" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_category__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_template__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_res_company__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_res_config_settings__stock_state_threshold +msgid "Stock State Threshold" +msgstr "voorraadniveau drempelwaarde" + +#. module: product_stock_state +#: model:res.groups,name:product_stock_state.group_stock_state_by_category +msgid "Stock State Threshold by Category" +msgstr "voorraadniveau drempelwaarde op categorie" + +#. module: product_stock_state +#: model:res.groups,name:product_stock_state.group_stock_state_by_product +msgid "Stock State Threshold by Product" +msgstr "voorraadniveau drempelwaarde op product" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.view_product_category_form +#: model_terms:ir.ui.view,arch_db:product_stock_state.view_product_template_form +msgid "Stock Threshold" +msgstr "voorraad drempelwaarde" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.res_company_view_form_threshold +msgid "Stock parameters" +msgstr "voorraad parameters" + +#. module: product_stock_state +#: model:ir.model.fields,help:product_stock_state.field_product_category__stock_state_threshold +msgid "" +"The custom value under which the stock state of the products of this " +"category will pass from 'In Stock' to 'In Limited Stock' State. If not set, " +"Odoo will use the threshold defined at the company level." +msgstr "" +"Defieneer een aangepaste drempelwaarde waaronder de voorraad status zal " +"wijzigen van 'In voorraad' naar 'Beperkte voorraad' status. Wanneer niet " +"opgegeven, dan zal de drempelwaarde op het bedrijfsniveau worden gebruikt." + +#~ msgid "Product stock thresholds" +#~ msgstr "Product voorraad drempels" + +#~ msgid "Stock threshold" +#~ msgstr "voorraad drempelwaarde" + +#~ msgid "Display Name" +#~ msgstr "Weergavenaam" + +#~ msgid "ID" +#~ msgstr "ID" + +#~ msgid "Last Modified on" +#~ msgstr "Laatst bijgewerkt op" + +#~ msgid "Product Template" +#~ msgstr "Productsjabloon" + +#~ msgid "Units" +#~ msgstr "Eenheden" + +#~ msgid "kg" +#~ msgstr "kg" + +#~ msgid "m³" +#~ msgstr "m³" diff --git a/product_stock_state/i18n/product_stock_state.pot b/product_stock_state/i18n/product_stock_state.pot new file mode 100644 index 00000000000..2a4bc91c1a2 --- /dev/null +++ b/product_stock_state/i18n/product_stock_state.pot @@ -0,0 +1,146 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_stock_state +# +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_stock_state +#: model:ir.model,name:product_stock_state.model_res_company +msgid "Companies" +msgstr "" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: product_stock_state +#: model:ir.model.fields,help:product_stock_state.field_res_config_settings__stock_state_threshold +msgid "" +"Define custom value under which the stock state will pass from 'In Stock' to" +" 'In Limited Stock' State." +msgstr "" + +#. module: product_stock_state +#: model:ir.model.fields,help:product_stock_state.field_product_product__stock_state_threshold +#: model:ir.model.fields,help:product_stock_state.field_product_template__stock_state_threshold +msgid "" +"Define custom value under which the stock state will pass from 'In Stock' to" +" 'In Limited Stock' State. If not set, Odoo will use the value defined in " +"the product category. If no value is defined in product category, it will " +"use the value defined for the company" +msgstr "" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__in_limited_stock +msgid "In Limited Stock" +msgstr "" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__in_stock +msgid "In Stock" +msgstr "" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_category__manual_stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__manual_stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_template__manual_stock_state_threshold +msgid "Manual Stock State Threshold" +msgstr "" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__out_of_stock +msgid "Out Of Stock" +msgstr "" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_product_template +msgid "Product" +msgstr "" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_product_category +msgid "Product Category" +msgstr "" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_product_product +msgid "Product Variant" +msgstr "" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.res_config_settings_view_form_threshold +msgid "Product stock thresholds" +msgstr "" + +#. module: product_stock_state +#: model:product.template,name:product_stock_state.product_setting_by_company_product_template +msgid "Product with threshold set on the company" +msgstr "" + +#. module: product_stock_state +#: model:product.template,name:product_stock_state.product_setting_by_product_product_template +msgid "Product with threshold set on the product" +msgstr "" + +#. module: product_stock_state +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__resupplying +msgid "Resupplying" +msgstr "" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.res_config_settings_view_form_threshold +msgid "Set stock state threshold" +msgstr "" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__stock_state +msgid "Stock State" +msgstr "" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_category__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_product_template__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_res_company__stock_state_threshold +#: model:ir.model.fields,field_description:product_stock_state.field_res_config_settings__stock_state_threshold +msgid "Stock State Threshold" +msgstr "" + +#. module: product_stock_state +#: model:res.groups,name:product_stock_state.group_stock_state_by_category +msgid "Stock State Threshold by Category" +msgstr "" + +#. module: product_stock_state +#: model:res.groups,name:product_stock_state.group_stock_state_by_product +msgid "Stock State Threshold by Product" +msgstr "" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.view_product_category_form +#: model_terms:ir.ui.view,arch_db:product_stock_state.view_product_template_form +msgid "Stock Threshold" +msgstr "" + +#. module: product_stock_state +#: model_terms:ir.ui.view,arch_db:product_stock_state.res_company_view_form_threshold +msgid "Stock parameters" +msgstr "" + +#. module: product_stock_state +#: model:ir.model.fields,help:product_stock_state.field_product_category__stock_state_threshold +msgid "" +"The custom value under which the stock state of the products of this " +"category will pass from 'In Stock' to 'In Limited Stock' State. If not set, " +"Odoo will use the threshold defined at the company level." +msgstr "" diff --git a/product_stock_state/models/__init__.py b/product_stock_state/models/__init__.py new file mode 100644 index 00000000000..2af33978e1e --- /dev/null +++ b/product_stock_state/models/__init__.py @@ -0,0 +1,7 @@ +from . import ( + product_category, + product_product, + product_template, + res_company, + res_config_settings, +) diff --git a/product_stock_state/models/product_category.py b/product_stock_state/models/product_category.py new file mode 100644 index 00000000000..ce665989363 --- /dev/null +++ b/product_stock_state/models/product_category.py @@ -0,0 +1,32 @@ +# Copyright 2017-Today GRAP (http://www.grap.coop). +# Copyright 2018 ACSONE SA/NV +# Copyright 2018 Akretion (http://www.akretion.com). +# @author Sylvain LE GAL +# @author Sébastien BEAU +# @author Laurent Mignon +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class ProductCategory(models.Model): + _inherit = "product.category" + + stock_state_threshold = fields.Float( + compute="_compute_stock_state_threshold", + store=True, + help="The custom value under which the stock state of the products" + " of this category will pass from 'In Stock' to 'In Limited Stock'" + " State. If not set, Odoo will use the threshold defined at the" + " company level.", + digits="Stock Threshold", + recursive=True, + ) + manual_stock_state_threshold = fields.Float(digits="Stock Threshold") + + @api.depends("parent_id.stock_state_threshold", "manual_stock_state_threshold") + def _compute_stock_state_threshold(self): + for rec in self: + rec.stock_state_threshold = ( + rec.manual_stock_state_threshold or rec.parent_id.stock_state_threshold + ) diff --git a/product_stock_state/models/product_product.py b/product_stock_state/models/product_product.py new file mode 100644 index 00000000000..d879a4ae8d0 --- /dev/null +++ b/product_stock_state/models/product_product.py @@ -0,0 +1,83 @@ +# Copyright 2017-Today GRAP (http://www.grap.coop). +# Copyright 2018 ACSONE SA/NV +# Copyright 2018 Akretion (http://www.akretion.com). +# Copyright 2020 Camptocamp SA (http://www.camptocamp.com) +# @author Sylvain LE GAL +# @author Sébastien BEAU +# @author Laurent Mignon +# @author Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models +from odoo.tools.float_utils import float_compare + + +class ProductProduct(models.Model): + _inherit = "product.product" + + stock_state = fields.Selection( + selection=[ + ("in_stock", "In Stock"), + ("in_limited_stock", "In Limited Stock"), + ("resupplying", "Resupplying"), + ("out_of_stock", "Out Of Stock"), + ], + compute="_compute_stock_state", + ) + + def _get_qty_available_for_stock_state(self): + """ + This method can be overridden to provide the available qty. + In some cases you could prefer to use the qty_available - outgoing_qty + to take into account products reserved + """ + self.ensure_one() + return self.qty_available + + def _stock_state_check_in_stock(self, qty, precision): + return ( + float_compare( + qty, + self._get_stock_state_threshold(), + precision_digits=precision, + ) + == 1 + ) + + def _stock_state_check_in_limited_stock(self, qty, precision): + return float_compare(qty, 0, precision_digits=precision) == 1 + + def _stock_state_check_resupplying(self, qty, precision): + return float_compare(self.incoming_qty, 0, precision_digits=precision) == 1 + + def _stock_state_check_out_of_stock(self, qty, precision): + return True + + def _available_states(self): + return [x[0] for x in self._fields["stock_state"].selection] + + @api.depends( + "qty_available", + "incoming_qty", + "stock_state_threshold", + "company_id.stock_state_threshold", + ) + def _compute_stock_state(self): + precision = self.env["decimal.precision"].precision_get("Stock Threshold") + for product in self: + qty_available = product._get_qty_available_for_stock_state() + stock_state = False + for state in self._available_states(): + checker = getattr(product, "_stock_state_check_" + state) + if checker(qty_available, precision): + stock_state = state + break + product.stock_state = stock_state + + def _get_stock_state_threshold(self): + self.ensure_one() + threshold = self.stock_state_threshold + if not threshold: + # try to get threshold from current company + threshold = self.env.company.stock_state_threshold + return threshold diff --git a/product_stock_state/models/product_template.py b/product_stock_state/models/product_template.py new file mode 100644 index 00000000000..2ba49b7c45f --- /dev/null +++ b/product_stock_state/models/product_template.py @@ -0,0 +1,33 @@ +# Copyright 2017-Today GRAP (http://www.grap.coop). +# Copyright 2018 ACSONE SA/NV +# Copyright 2018 Akretion (http://www.akretion.com). +# @author Sylvain LE GAL +# @author Sébastien BEAU +# @author Laurent Mignon +# 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" + + stock_state_threshold = fields.Float( + compute="_compute_stock_state_threshold", + store=True, + help="Define custom value under which the stock state will pass from" + " 'In Stock' to 'In Limited Stock' State. If not set, Odoo will" + " use the value defined in the product category. If" + " no value is defined in product category, it will use the value" + " defined for the company", + digits="Stock Threshold", + ) + + manual_stock_state_threshold = fields.Float(digits="Stock Threshold") + + @api.depends("categ_id.stock_state_threshold", "manual_stock_state_threshold") + def _compute_stock_state_threshold(self): + for rec in self: + rec.stock_state_threshold = ( + rec.manual_stock_state_threshold or rec.categ_id.stock_state_threshold + ) diff --git a/product_stock_state/models/res_company.py b/product_stock_state/models/res_company.py new file mode 100644 index 00000000000..143c1ca4354 --- /dev/null +++ b/product_stock_state/models/res_company.py @@ -0,0 +1,11 @@ +# Copyright 2017-Today GRAP (http://www.grap.coop). +# @author Sylvain LE GAL +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + stock_state_threshold = fields.Float(default=10, digits="Stock Threshold") diff --git a/product_stock_state/models/res_config_settings.py b/product_stock_state/models/res_config_settings.py new file mode 100644 index 00000000000..0e3254cebe7 --- /dev/null +++ b/product_stock_state/models/res_config_settings.py @@ -0,0 +1,18 @@ +# Copyright 2017-Today GRAP (http://www.grap.coop). +# @author Sylvain LE GAL +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + stock_state_threshold = fields.Float( + related="company_id.stock_state_threshold", + string="Stock State Threshold", + readonly=False, + help="Define custom value" + " under which the stock state will pass from 'In Stock' to 'In Limited" + " Stock' State.", + ) diff --git a/product_stock_state/pyproject.toml b/product_stock_state/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/product_stock_state/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/product_stock_state/readme/CONFIGURE.md b/product_stock_state/readme/CONFIGURE.md new file mode 100644 index 00000000000..d805f2c78ff --- /dev/null +++ b/product_stock_state/readme/CONFIGURE.md @@ -0,0 +1,9 @@ +You can configure thresholds : + +- Globally, for a company, in the Sale Settings. It will be used for all + the products of the company. +- On product category form. It will be used for all the products of this + category, or of the child categories. (User should be part of the new + group 'Stock State Threshold by Category'.) +- On product template form, for a specific product. (User should be part + of the new group 'Stock State Threshold by Product'.) diff --git a/product_stock_state/readme/CONTRIBUTORS.md b/product_stock_state/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..7bee782bf79 --- /dev/null +++ b/product_stock_state/readme/CONTRIBUTORS.md @@ -0,0 +1,8 @@ +- Sebastien Beau \<\> +- Sylvain LE GAL \<\> +- Kevin Khao \<\> +- Simone Orsi \<\> +- Watthanun Khorchai \<\> + +- [Trobz](https://trobz.com): + - Tuan Nguyen \<\> diff --git a/product_stock_state/readme/CREDITS.md b/product_stock_state/readme/CREDITS.md new file mode 100644 index 00000000000..f4c7417a3d2 --- /dev/null +++ b/product_stock_state/readme/CREDITS.md @@ -0,0 +1,8 @@ +The development of this module has been financially supported by: + +- Akretion R&D +- Adaptoo +- GRAP, Groupement Régional Alimentaire de Proximité + \<\> + +The migration of this module from 16.0 to 18.0 was financially supported by Camptocamp. diff --git a/product_stock_state/readme/DESCRIPTION.md b/product_stock_state/readme/DESCRIPTION.md new file mode 100644 index 00000000000..24e1e11a995 --- /dev/null +++ b/product_stock_state/readme/DESCRIPTION.md @@ -0,0 +1,9 @@ +This module adds a "stock state" field on the product in order to inform +the user of its general stock state at a glance. + +The state value can be : + +- In Stock +- In Limited Stock (if qty available is under a threshold) +- Resupplying (if qty forcasted is \> 0) +- Out of Stock (otherwise) diff --git a/product_stock_state/readme/USAGE.md b/product_stock_state/readme/USAGE.md new file mode 100644 index 00000000000..a23b3b124e5 --- /dev/null +++ b/product_stock_state/readme/USAGE.md @@ -0,0 +1,3 @@ +Open product tree view and observe the stock state + +![image](/product_stock_state/static/description/product_product_tree.png) diff --git a/product_stock_state/security/res_groups.xml b/product_stock_state/security/res_groups.xml new file mode 100644 index 00000000000..95e0094c56e --- /dev/null +++ b/product_stock_state/security/res_groups.xml @@ -0,0 +1,9 @@ + + + + Stock State Threshold by Product + + + Stock State Threshold by Category + + diff --git a/product_stock_state/static/description/icon.png b/product_stock_state/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/product_stock_state/static/description/icon.png differ diff --git a/product_stock_state/static/description/index.html b/product_stock_state/static/description/index.html new file mode 100644 index 00000000000..8fbb2570955 --- /dev/null +++ b/product_stock_state/static/description/index.html @@ -0,0 +1,481 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Product Stock State

+ +

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

+

This module adds a “stock state” field on the product in order to inform +the user of its general stock state at a glance.

+

The state value can be :

+
    +
  • In Stock
  • +
  • In Limited Stock (if qty available is under a threshold)
  • +
  • Resupplying (if qty forcasted is > 0)
  • +
  • Out of Stock (otherwise)
  • +
+

Table of contents

+ +
+

Configuration

+

You can configure thresholds :

+
    +
  • Globally, for a company, in the Sale Settings. It will be used for all +the products of the company.
  • +
  • On product category form. It will be used for all the products of this +category, or of the child categories. (User should be part of the new +group ‘Stock State Threshold by Category’.)
  • +
  • On product template form, for a specific product. (User should be part +of the new group ‘Stock State Threshold by Product’.)
  • +
+
+
+

Usage

+

Open product tree view and observe the stock state

+

image

+
+
+

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

+
    +
  • Akretion
  • +
  • GRAP
  • +
+
+
+

Contributors

+ +
+
+

Other credits

+

The development of this module has been financially supported by:

+
    +
  • Akretion R&D
  • +
  • Adaptoo
  • +
  • GRAP, Groupement Régional Alimentaire de Proximité +<http://www.grap.coop>
  • +
+

The migration of this module from 16.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.

+

Current maintainers:

+

sebastienbeau legalsylvain kevinkhao

+

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_stock_state/static/description/product_product_tree.png b/product_stock_state/static/description/product_product_tree.png new file mode 100644 index 00000000000..62854b0b599 Binary files /dev/null and b/product_stock_state/static/description/product_product_tree.png differ diff --git a/product_stock_state/tests/__init__.py b/product_stock_state/tests/__init__.py new file mode 100644 index 00000000000..c914b7e695e --- /dev/null +++ b/product_stock_state/tests/__init__.py @@ -0,0 +1 @@ +from . import test_product_stock_state diff --git a/product_stock_state/tests/test_product_stock_state.py b/product_stock_state/tests/test_product_stock_state.py new file mode 100644 index 00000000000..963ba65da7b --- /dev/null +++ b/product_stock_state/tests/test_product_stock_state.py @@ -0,0 +1,83 @@ +# Copyright 2017-Today GRAP (http://www.grap.coop). +# Copyright 2020 Camptocamp SA (http://www.camptocamp.com) +# @author Sylvain LE GAL +# @author Simone Orsi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase + + +class TestProductStockState(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.company = cls.env.ref("base.main_company") + # Create categories locally + cls.category_saleable = cls.env["product.category"].create( + { + "name": "Test Saleable", + "manual_stock_state_threshold": 10, + } + ) + cls.category_furniture = cls.env["product.category"].create( + { + "name": "Test Furniture", + "parent_id": cls.category_saleable.id, + } + ) + # Create products locally + cls.product_chair = cls.env["product.product"].create( + { + "name": "Test Chair", + "categ_id": cls.category_furniture.id, + "type": "consu", + } + ) + cls.product_threshold_on_company = cls.env["product.product"].create( + { + "name": "Test Product Company Threshold", + "categ_id": cls.category_saleable.id, + "type": "consu", + "company_id": cls.company.id, + } + ) + cls.product_threshold_on_product = cls.env["product.product"].create( + { + "name": "Test Product Manual Threshold", + "categ_id": cls.category_saleable.id, + "type": "consu", + "manual_stock_state_threshold": 30, + } + ) + + def test_01_global_product(self): + """Test Global Settings""" + self.assertEqual( + self.product_threshold_on_company._get_stock_state_threshold(), + self.company.stock_state_threshold, + ) + + def test_02_category_setting_direct(self): + """Test Category Setting (Setting on the product category)""" + self.category_furniture.stock_state_threshold = 77 + self.assertEqual( + self.product_chair._get_stock_state_threshold(), + self.category_furniture.stock_state_threshold, + ) + + def test_03_category_setting_inherit(self): + """Test Category Setting (Setting on a parent category)""" + self.assertEqual( + self.product_chair._get_stock_state_threshold(), + self.category_saleable.stock_state_threshold, + ) + + def test_04_category_setting_inherit(self): + """Test Product Setting (Setting on a product unique template)""" + self.assertEqual( + self.product_threshold_on_product._get_stock_state_threshold(), 30 + ) + + def test_05_state_out_of_stock(self): + """Test Stock State computation""" + self.assertEqual(self.product_threshold_on_product.stock_state, "out_of_stock") diff --git a/product_stock_state/views/product_category_view.xml b/product_stock_state/views/product_category_view.xml new file mode 100644 index 00000000000..d4f37f24eb5 --- /dev/null +++ b/product_stock_state/views/product_category_view.xml @@ -0,0 +1,15 @@ + + + + product.category + + + + + + + + + + + diff --git a/product_stock_state/views/product_product_view.xml b/product_stock_state/views/product_product_view.xml new file mode 100644 index 00000000000..5d996591767 --- /dev/null +++ b/product_stock_state/views/product_product_view.xml @@ -0,0 +1,12 @@ + + + + product.product + + + + + + + + diff --git a/product_stock_state/views/product_template_view.xml b/product_stock_state/views/product_template_view.xml new file mode 100644 index 00000000000..913dd7e6036 --- /dev/null +++ b/product_stock_state/views/product_template_view.xml @@ -0,0 +1,15 @@ + + + + product.template + + + + + + + + + + + diff --git a/product_stock_state/views/res_company_view.xml b/product_stock_state/views/res_company_view.xml new file mode 100644 index 00000000000..1d3529ded47 --- /dev/null +++ b/product_stock_state/views/res_company_view.xml @@ -0,0 +1,17 @@ + + + + res.company.threshold + res.company + + + + + + + + + + + + diff --git a/product_stock_state/views/res_config_settings_view.xml b/product_stock_state/views/res_config_settings_view.xml new file mode 100644 index 00000000000..d23c1a571ef --- /dev/null +++ b/product_stock_state/views/res_config_settings_view.xml @@ -0,0 +1,22 @@ + + + + res.config.settings.threshold + res.config.settings + + + + + + + + + + +