From db7e72eedd70283e8b18b768a49a35cf0d5d0895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Sat, 7 Jan 2017 17:45:21 +0100 Subject: [PATCH 01/37] [ADD] add product_stock_state --- product_stock_state/__init__.py | 3 ++ product_stock_state/__openerp__.py | 32 ++++++++++++++ product_stock_state/models/__init__.py | 3 ++ product_stock_state/models/product.py | 50 ++++++++++++++++++++++ product_stock_state/views/product_view.xml | 30 +++++++++++++ 5 files changed, 118 insertions(+) create mode 100644 product_stock_state/__init__.py create mode 100644 product_stock_state/__openerp__.py create mode 100644 product_stock_state/models/__init__.py create mode 100644 product_stock_state/models/product.py create mode 100644 product_stock_state/views/product_view.xml diff --git a/product_stock_state/__init__.py b/product_stock_state/__init__.py new file mode 100644 index 00000000000..cde864bae21 --- /dev/null +++ b/product_stock_state/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import models diff --git a/product_stock_state/__openerp__.py b/product_stock_state/__openerp__.py new file mode 100644 index 00000000000..60ea2233dc5 --- /dev/null +++ b/product_stock_state/__openerp__.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Product Stock State", + "summary": + "Compute the state stock based on" + "the stock level and sale_ok field", + "version": "8.0.1.0.0", + "category": "Uncategorized", + "website": "www.akretion.com", + "author": " Akretion", + "license": "AGPL-3", + "application": False, + "installable": True, + "external_dependencies": { + "python": [], + "bin": [], + }, + "depends": [ + "sale_stock", + ], + "data": [ + 'views/product_view.xml', + ], + "demo": [ + ], + "qweb": [ + ] +} diff --git a/product_stock_state/models/__init__.py b/product_stock_state/models/__init__.py new file mode 100644 index 00000000000..d7d73083c45 --- /dev/null +++ b/product_stock_state/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import product diff --git a/product_stock_state/models/product.py b/product_stock_state/models/product.py new file mode 100644 index 00000000000..6403dc5f6c1 --- /dev/null +++ b/product_stock_state/models/product.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields, api + + +class ProductMixing(models.AbstractModel): + _name = 'product.mixing' + _description = 'Product Mixing' + + stock_state = fields.Selection([ + ('in_stock', 'In Stock'), + ('out_of_stock', 'Out Of Stock'), + ('resupplying', 'Resupplying'), + ('in_limited_stock', 'In Limited Stock'), + ], + compute='_compute_stock_state') + + def _get_stock_state(self): + self.ensure_one() + if not self.sale_ok: + return 'out_of_stock' + elif self.qty_available <= 0: + return 'resupplying' + elif self.qty_available <= self._level_for_limited_stock(): + return 'in_limited_stock' + else: + return 'in_stock' + + def _compute_stock_state(self): + for record in self: + record.stock_state = record._get_stock_state() + + +class ProductTemplate(models.Model): + _inherit = ['product.template', 'product.mixing'] + _name = 'product.template' + + def _level_for_limited_stock(self): + return self.product_variant_count * 10 + + +class ProductProduct(models.Model): + _inherit = ['product.product', 'product.mixing'] + _name = 'product.product' + + def _level_for_limited_stock(self): + return 10 diff --git a/product_stock_state/views/product_view.xml b/product_stock_state/views/product_view.xml new file mode 100644 index 00000000000..25e13166614 --- /dev/null +++ b/product_stock_state/views/product_view.xml @@ -0,0 +1,30 @@ + + + + + + product.template + + + + + + + + + + product.template + + + +
+
+ +
+
+
+
+ +
+
+ From e54b4af031be87bfd7acfd4f85d74324beacd1b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Sun, 8 Jan 2017 00:16:09 +0100 Subject: [PATCH 02/37] [PEP] pep8 cleanup --- product_stock_state/models/product.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product_stock_state/models/product.py b/product_stock_state/models/product.py index 6403dc5f6c1..0f8a988d9ce 100644 --- a/product_stock_state/models/product.py +++ b/product_stock_state/models/product.py @@ -3,7 +3,7 @@ # @author Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import models, fields, api +from openerp import fields, models class ProductMixing(models.AbstractModel): From 50a65a1da2213805ec6755a6f51122f9a59cfd80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Thu, 20 Jul 2017 23:43:35 +0200 Subject: [PATCH 03/37] [FIX] remove warning message, add missing readme, remove useless field in demo data --- product_stock_state/README.rst | 55 ++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 product_stock_state/README.rst diff --git a/product_stock_state/README.rst b/product_stock_state/README.rst new file mode 100644 index 00000000000..8fd34860c09 --- /dev/null +++ b/product_stock_state/README.rst @@ -0,0 +1,55 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +======================== +Product Stock State +======================== + +This module add a stock state on the product in order to give an human readable information +without giving the current quantity in stock + +Installation +============ + +Nothing special required + + +Configuration +============= + +Nothing required + +Usage +===== + +Go on the product tree/form and see the stock state + +Known issues / Roadmap +====================== + +* Nothing + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Contributors +------------ + +* Sebastien Beau + +Funders +------- + +The development of this module has been financially supported by: + +* Akretion R&D +* Adaptoo From 5aebe958b0a250d7fffa5b56ec414b1b92890820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Tue, 1 Aug 2017 16:39:16 +0200 Subject: [PATCH 04/37] [IMP] simplify product_stock_state by adding it only on product_product --- product_stock_state/models/product.py | 18 ++---------------- product_stock_state/views/product_view.xml | 8 ++++---- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/product_stock_state/models/product.py b/product_stock_state/models/product.py index 0f8a988d9ce..9a9479c5e37 100644 --- a/product_stock_state/models/product.py +++ b/product_stock_state/models/product.py @@ -6,9 +6,8 @@ from openerp import fields, models -class ProductMixing(models.AbstractModel): - _name = 'product.mixing' - _description = 'Product Mixing' +class ProductProduct(models.Model): + _inherit = 'product.product' stock_state = fields.Selection([ ('in_stock', 'In Stock'), @@ -33,18 +32,5 @@ def _compute_stock_state(self): for record in self: record.stock_state = record._get_stock_state() - -class ProductTemplate(models.Model): - _inherit = ['product.template', 'product.mixing'] - _name = 'product.template' - - def _level_for_limited_stock(self): - return self.product_variant_count * 10 - - -class ProductProduct(models.Model): - _inherit = ['product.product', 'product.mixing'] - _name = 'product.product' - def _level_for_limited_stock(self): return 10 diff --git a/product_stock_state/views/product_view.xml b/product_stock_state/views/product_view.xml index 25e13166614..4fa62ff0a4d 100644 --- a/product_stock_state/views/product_view.xml +++ b/product_stock_state/views/product_view.xml @@ -3,8 +3,8 @@ - product.template - + product.product + @@ -13,8 +13,8 @@ - product.template - + product.product +
From f7fa91e0cf1d88de91bd20fa9e977c03019fbb07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Valyi?= Date: Mon, 2 Oct 2017 12:06:41 +0200 Subject: [PATCH 05/37] [MIG] Make modules uninstallable --- product_stock_state/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product_stock_state/__openerp__.py b/product_stock_state/__openerp__.py index 60ea2233dc5..42c79d39d13 100644 --- a/product_stock_state/__openerp__.py +++ b/product_stock_state/__openerp__.py @@ -14,7 +14,7 @@ "author": " Akretion", "license": "AGPL-3", "application": False, - "installable": True, + 'installable': False, "external_dependencies": { "python": [], "bin": [], From efc20ee114cba09dbdd05b83b4bdfeabbd676f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Valyi?= Date: Mon, 2 Oct 2017 12:06:49 +0200 Subject: [PATCH 06/37] [MIG] Rename manifest files --- product_stock_state/{__openerp__.py => __manifest__.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename product_stock_state/{__openerp__.py => __manifest__.py} (100%) diff --git a/product_stock_state/__openerp__.py b/product_stock_state/__manifest__.py similarity index 100% rename from product_stock_state/__openerp__.py rename to product_stock_state/__manifest__.py From 5b6d161dc07ca33e8f1860085776c6abf846372d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Mon, 2 Oct 2017 14:51:15 +0200 Subject: [PATCH 07/37] [IMP] mass update with sed for odoo import and odoo UserError --- product_stock_state/models/product.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product_stock_state/models/product.py b/product_stock_state/models/product.py index 9a9479c5e37..2599bb4069a 100644 --- a/product_stock_state/models/product.py +++ b/product_stock_state/models/product.py @@ -3,7 +3,7 @@ # @author Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import fields, models +from odoo import fields, models class ProductProduct(models.Model): From b7f196b02dbd6c3465f7e1e13af0d821f13987ed Mon Sep 17 00:00:00 2001 From: Sylvain LE GAL Date: Tue, 3 Oct 2017 04:30:35 +0200 Subject: [PATCH 08/37] [PORT+IMP][10.0] product_stock_state --- product_stock_state/README.rst | 40 ++++-- product_stock_state/__manifest__.py | 19 ++- product_stock_state/demo/product_category.xml | 10 ++ product_stock_state/demo/product_product.xml | 16 +++ product_stock_state/demo/res_company.xml | 9 ++ product_stock_state/demo/res_groups.xml | 17 +++ product_stock_state/i18n/fr.po | 123 ++++++++++++++++++ product_stock_state/models/__init__.py | 7 +- product_stock_state/models/product.py | 36 ----- .../models/product_category.py | 15 +++ product_stock_state/models/product_product.py | 51 ++++++++ .../models/product_template.py | 17 +++ product_stock_state/models/res_company.py | 12 ++ .../models/sale_config_settings.py | 16 +++ product_stock_state/security/res_groups.xml | 15 +++ .../description/product_product_tree.png | Bin 0 -> 40505 bytes product_stock_state/tests/__init__.py | 3 + .../tests/test_product_stock_state.py | 52 ++++++++ .../views/product_category_view.xml | 15 +++ .../views/product_product_view.xml | 15 +++ .../views/product_template_view.xml | 15 +++ product_stock_state/views/product_view.xml | 30 ----- .../views/sale_config_settings_view.xml | 15 +++ 23 files changed, 466 insertions(+), 82 deletions(-) create mode 100644 product_stock_state/demo/product_category.xml create mode 100644 product_stock_state/demo/product_product.xml create mode 100644 product_stock_state/demo/res_company.xml create mode 100644 product_stock_state/demo/res_groups.xml create mode 100644 product_stock_state/i18n/fr.po delete mode 100644 product_stock_state/models/product.py create mode 100644 product_stock_state/models/product_category.py create mode 100644 product_stock_state/models/product_product.py create mode 100644 product_stock_state/models/product_template.py create mode 100644 product_stock_state/models/res_company.py create mode 100644 product_stock_state/models/sale_config_settings.py create mode 100644 product_stock_state/security/res_groups.xml create mode 100644 product_stock_state/static/description/product_product_tree.png create mode 100644 product_stock_state/tests/__init__.py create mode 100644 product_stock_state/tests/test_product_stock_state.py create mode 100644 product_stock_state/views/product_category_view.xml create mode 100644 product_stock_state/views/product_product_view.xml create mode 100644 product_stock_state/views/product_template_view.xml delete mode 100644 product_stock_state/views/product_view.xml create mode 100644 product_stock_state/views/sale_config_settings_view.xml diff --git a/product_stock_state/README.rst b/product_stock_state/README.rst index 8fd34860c09..233acb8a809 100644 --- a/product_stock_state/README.rst +++ b/product_stock_state/README.rst @@ -2,33 +2,49 @@ :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 -======================== +=================== Product Stock State -======================== +=================== -This module add a stock state on the product in order to give an human readable information -without giving the current quantity in stock +This module add a stock state on the product in order to give an human readable +information without giving the current quantity in stock. -Installation -============ - -Nothing special required +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) Configuration ============= -Nothing required +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 ===== -Go on the product tree/form and see the stock state +Go on the product tree and see the stock state + +.. image:: /product_stock_state/static/description/product_product_tree.png + :width: 800 px Known issues / Roadmap ====================== -* Nothing +* Company settings is in sale configuration, but it should be better on + stock configuration. It is not possible for the time being, because + stock.config.settings doesn't have company_id field in Odoo. Bug Tracker =========== @@ -45,6 +61,7 @@ Contributors ------------ * Sebastien Beau +* Sylvain LE GAL Funders ------- @@ -53,3 +70,4 @@ The development of this module has been financially supported by: * Akretion R&D * Adaptoo +* GRAP, Groupement Régional Alimentaire de Proximité diff --git a/product_stock_state/__manifest__.py b/product_stock_state/__manifest__.py index 42c79d39d13..07296e01eed 100644 --- a/product_stock_state/__manifest__.py +++ b/product_stock_state/__manifest__.py @@ -1,20 +1,23 @@ # -*- coding: utf-8 -*- # 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 stock based on" "the stock level and sale_ok field", - "version": "8.0.1.0.0", + "version": "10.0.1.0.0", "category": "Uncategorized", "website": "www.akretion.com", - "author": " Akretion", + "author": " Akretion,GRAP", "license": "AGPL-3", "application": False, - 'installable': False, + 'installable': True, "external_dependencies": { "python": [], "bin": [], @@ -23,9 +26,17 @@ "sale_stock", ], "data": [ - 'views/product_view.xml', + 'security/res_groups.xml', + 'views/product_template_view.xml', + 'views/product_product_view.xml', + 'views/product_category_view.xml', + 'views/sale_config_settings_view.xml', ], "demo": [ + 'demo/res_groups.xml', + 'demo/product_category.xml', + 'demo/product_product.xml', + 'demo/product_category.xml', ], "qweb": [ ] diff --git a/product_stock_state/demo/product_category.xml b/product_stock_state/demo/product_category.xml new file mode 100644 index 00000000000..aed83ccc19f --- /dev/null +++ b/product_stock_state/demo/product_category.xml @@ -0,0 +1,10 @@ + + + + + + 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..01075fdb831 --- /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 + + + + diff --git a/product_stock_state/demo/res_company.xml b/product_stock_state/demo/res_company.xml new file mode 100644 index 00000000000..d7c87e67962 --- /dev/null +++ b/product_stock_state/demo/res_company.xml @@ -0,0 +1,9 @@ + + + + + 10 + + + + diff --git a/product_stock_state/demo/res_groups.xml b/product_stock_state/demo/res_groups.xml new file mode 100644 index 00000000000..f9c834d2618 --- /dev/null +++ b/product_stock_state/demo/res_groups.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/product_stock_state/i18n/fr.po b/product_stock_state/i18n/fr.po new file mode 100644 index 00000000000..e092b7ba5cb --- /dev/null +++ b/product_stock_state/i18n/fr.po @@ -0,0 +1,123 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * product_stock_state +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-03 02:23+0000\n" +"PO-Revision-Date: 2017-10-03 02:23+0000\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 "Sociétés" + +#. module: product_stock_state +#: model:ir.model.fields,help:product_stock_state.field_product_category_stock_state_threshold +msgid "Define custom value under wich 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 value defined for the company" +msgstr "Définir une valeur personnalisée en dessous de laquelle l'état du stock des produits de cette categorie passera de 'En stock' à 'En stock limité'. Si la valeur n'est pas définie, Odoo utilisera la valeur défini pour la société" + +#. module: product_stock_state +#: model:ir.model.fields,help:product_stock_state.field_sale_config_settings_stock_state_threshold +msgid "Define custom value under wich the stock state will pass from 'In Stock' to 'In Limited Stock' State." +msgstr "Définir une valeur personnalisée en dessous de laquelle l'état du stock passera de 'En stock' à '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 wich 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 une valeur personnalisée en dessous de laquelle l'état du stock passera de 'En stock' à 'En stock limité'. Si la valeur n'est pas définie, Odoo utilisera la valeur définie sur la catégorie. Si la catégorie n'a pas de valeur, Odoo utilisera la valeur définie au niveau de la société" + +#. module: product_stock_state +#: selection:product.product,stock_state:0 +msgid "In Limited Stock" +msgstr "En stock limité" + +#. module: product_stock_state +#: selection:product.product,stock_state:0 +msgid "In Stock" +msgstr "En stock" + +#. module: product_stock_state +#: selection:product.product,stock_state:0 +msgid "Out Of Stock" +msgstr "En rupture de stock" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_product_product +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_template +msgid "Product Template" +msgstr "Modèle d'article" + +#. module: product_stock_state +#: model:product.product,name:product_stock_state.product_setting_by_company +#: model:product.template,name:product_stock_state.product_setting_by_company_product_template +msgid "Product with threshold set on the company" +msgstr "Product with threshold set on the company" + +#. module: product_stock_state +#: model:product.product,name:product_stock_state.product_setting_by_product +#: model:product.template,name:product_stock_state.product_setting_by_product_product_template +msgid "Product with threshold set on the product" +msgstr "Product with threshold set on the product" + +#. module: product_stock_state +#: selection:product.product,stock_state:0 +msgid "Resupplying" +msgstr "En réapprovisionnement" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_sale_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:ir.model.fields,field_description:product_stock_state.field_product_product_stock_state +msgid "Stock state" +msgstr "Etat 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 +msgid "Stock state threshold" +msgstr "Seuil pour l'état du stock" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_sale_config_settings +msgid "sale.config.settings" +msgstr "sale.config.settings" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_stock_config_settings +msgid "stock.config.settings" +msgstr "stock.config.settings" + diff --git a/product_stock_state/models/__init__.py b/product_stock_state/models/__init__.py index d7d73083c45..22ef06b7ce7 100644 --- a/product_stock_state/models/__init__.py +++ b/product_stock_state/models/__init__.py @@ -1,3 +1,8 @@ # -*- coding: utf-8 -*- -from . import product +from . import res_company +from . import sale_config_settings +from . import product_category +from . import product_template +from . import product_product + diff --git a/product_stock_state/models/product.py b/product_stock_state/models/product.py deleted file mode 100644 index 2599bb4069a..00000000000 --- a/product_stock_state/models/product.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2017 Akretion (http://www.akretion.com). -# @author Sébastien BEAU -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import fields, models - - -class ProductProduct(models.Model): - _inherit = 'product.product' - - stock_state = fields.Selection([ - ('in_stock', 'In Stock'), - ('out_of_stock', 'Out Of Stock'), - ('resupplying', 'Resupplying'), - ('in_limited_stock', 'In Limited Stock'), - ], - compute='_compute_stock_state') - - def _get_stock_state(self): - self.ensure_one() - if not self.sale_ok: - return 'out_of_stock' - elif self.qty_available <= 0: - return 'resupplying' - elif self.qty_available <= self._level_for_limited_stock(): - return 'in_limited_stock' - else: - return 'in_stock' - - def _compute_stock_state(self): - for record in self: - record.stock_state = record._get_stock_state() - - def _level_for_limited_stock(self): - return 10 diff --git a/product_stock_state/models/product_category.py b/product_stock_state/models/product_category.py new file mode 100644 index 00000000000..2f528bc308b --- /dev/null +++ b/product_stock_state/models/product_category.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# 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 ProductCategory(models.Model): + _inherit = 'product.category' + + stock_state_threshold = fields.Float( + help="Define custom value under wich 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 value defined for the company") diff --git a/product_stock_state/models/product_product.py b/product_stock_state/models/product_product.py new file mode 100644 index 00000000000..f7db1ebb52b --- /dev/null +++ b/product_stock_state/models/product_product.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# 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). + +from odoo import fields, models + + +class ProductProduct(models.Model): + _inherit = 'product.product' + + _STOCK_STATE_SELECTION = [ + ('in_stock', 'In Stock'), + ('in_limited_stock', 'In Limited Stock'), + ('resupplying', 'Resupplying'), + ('out_of_stock', 'Out Of Stock'), + ] + + stock_state = fields.Selection( + selection=_STOCK_STATE_SELECTION, compute='_compute_stock_state') + + def _compute_stock_state(self): + for product in self: + if product.qty_available >= product._get_stock_state_threshold(): + product.stock_state = 'in_stock' + elif product.qty_available > 0: + product.stock_state = 'in_limited_stock' + elif product.virtual_available > 0: + product.stock_state = 'resupplying' + else: + product.stock_state = 'out_of_stock' + + def _get_stock_state_threshold(self): + self.ensure_one() + threshold = self.stock_state_threshold + + if not threshold: + threshold = self.categ_id.stock_state_threshold + # try to get threshold from parent categories + category = self.categ_id + while category.parent_id and not threshold: + category = category.parent_id + threshold = category.stock_state_threshold + + if not threshold: + # try to get threshold from current company + threshold = self.env.user.company_id.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..ee95c2dc38a --- /dev/null +++ b/product_stock_state/models/product_template.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +# 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 ProductTemplate(models.Model): + _inherit = 'product.template' + + stock_state_threshold = fields.Float( + help="Define custom value under wich 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") diff --git a/product_stock_state/models/res_company.py b/product_stock_state/models/res_company.py new file mode 100644 index 00000000000..980d91e2048 --- /dev/null +++ b/product_stock_state/models/res_company.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# 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() diff --git a/product_stock_state/models/sale_config_settings.py b/product_stock_state/models/sale_config_settings.py new file mode 100644 index 00000000000..6ec18ed7dee --- /dev/null +++ b/product_stock_state/models/sale_config_settings.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# 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 SaleConfigSettings(models.TransientModel): + _inherit = 'sale.config.settings' + + stock_state_threshold = fields.Float( + related='company_id.stock_state_threshold', + string="Stock State Threshold *", help="Define custom value" + " under wich the stock state will pass from 'In Stock' to 'In Limited" + " Stock' State.") diff --git a/product_stock_state/security/res_groups.xml b/product_stock_state/security/res_groups.xml new file mode 100644 index 00000000000..6d9b32341b0 --- /dev/null +++ b/product_stock_state/security/res_groups.xml @@ -0,0 +1,15 @@ + + + + + Stock State Threshold by Product + + + + + Stock State Threshold by Category + + + + + 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 0000000000000000000000000000000000000000..62854b0b599f7e95962e9171e8c99ac35ef08d28 GIT binary patch literal 40505 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 +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo.tests.common import TransactionCase + + +class TestProductStockState(TransactionCase): + + def setUp(self): + super(TestProductStockState, self).setUp() + self.company = self.env.ref('base.main_company') + self.category = self.env.ref('product.product_category_1') + self.product_by_company = self.env.ref( + 'product_stock_state.product_setting_by_company') + self.product_by_categ_direct = self.env.ref( + 'stock.product_icecream') + self.product_by_categ_inherit = self.env.ref( + 'product.product_order_01') + self.product_by_product = self.env.ref( + 'product_stock_state.product_setting_by_product') + + def test_01_global_product(self): + """Test Global Settings""" + self.assertEqual( + self.product_by_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.assertEqual( + self.product_by_categ_direct._get_stock_state_threshold(), + self.category.stock_state_threshold) + + def test_03_category_setting_inherit(self): + """Test Category Setting (Setting on a parent category)""" + self.assertEqual( + self.product_by_categ_inherit._get_stock_state_threshold(), + self.category.stock_state_threshold) + + def test_04_category_setting_inherit(self): + """Test Product Setting (Setting on a product unique template)""" + self.assertEqual( + self.product_by_product._get_stock_state_threshold(), + 30) + + def test_05_state_out_of_stock(self): + """Test Stock State computation""" + self.assertEqual( + self.product_by_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..8eb99f7175e --- /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..81d7cf0b3b7 --- /dev/null +++ b/product_stock_state/views/product_product_view.xml @@ -0,0 +1,15 @@ + + + + + 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..b246809f084 --- /dev/null +++ b/product_stock_state/views/product_template_view.xml @@ -0,0 +1,15 @@ + + + + + product.template + + + + + + + + + + diff --git a/product_stock_state/views/product_view.xml b/product_stock_state/views/product_view.xml deleted file mode 100644 index 4fa62ff0a4d..00000000000 --- a/product_stock_state/views/product_view.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - product.product - - - - - - - - - - product.product - - - -

-
- -
-
- - - - - - diff --git a/product_stock_state/views/sale_config_settings_view.xml b/product_stock_state/views/sale_config_settings_view.xml new file mode 100644 index 00000000000..5b0c9c4bb02 --- /dev/null +++ b/product_stock_state/views/sale_config_settings_view.xml @@ -0,0 +1,15 @@ + + + + + sale.config.settings + + + + + + + + + + From 523efe5cfa0d9a2915e3f6b6c988f6a37fef47e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Tue, 14 Aug 2018 17:27:36 +0200 Subject: [PATCH 09/37] [ADD] add module for exporting the stock state of the product. This allow to export only the quantity when the stock level is limited and so reduce the number of push in algolia --- product_stock_state/models/res_company.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product_stock_state/models/res_company.py b/product_stock_state/models/res_company.py index 980d91e2048..fb4a1ddbd82 100644 --- a/product_stock_state/models/res_company.py +++ b/product_stock_state/models/res_company.py @@ -9,4 +9,4 @@ class ResCompany(models.Model): _inherit = 'res.company' - stock_state_threshold = fields.Float() + stock_state_threshold = fields.Float(default=10) From f8ca3d6d5dfc848277a9dbb9600b1fe0a7a8b417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Wed, 12 Sep 2018 09:30:26 +0200 Subject: [PATCH 10/37] [IMP] add computed field as proposed by Laurent Mignon @ Acsone --- product_stock_state/demo/product_category.xml | 2 +- product_stock_state/demo/product_product.xml | 2 +- product_stock_state/models/product_category.py | 17 ++++++++++++++++- product_stock_state/models/product_product.py | 16 ++++------------ product_stock_state/models/product_template.py | 18 +++++++++++++++++- .../views/product_category_view.xml | 1 + .../views/product_template_view.xml | 1 + 7 files changed, 41 insertions(+), 16 deletions(-) diff --git a/product_stock_state/demo/product_category.xml b/product_stock_state/demo/product_category.xml index aed83ccc19f..96d1a659048 100644 --- a/product_stock_state/demo/product_category.xml +++ b/product_stock_state/demo/product_category.xml @@ -3,7 +3,7 @@ - 20 + 20 diff --git a/product_stock_state/demo/product_product.xml b/product_stock_state/demo/product_product.xml index 01075fdb831..e79b9cf1029 100644 --- a/product_stock_state/demo/product_product.xml +++ b/product_stock_state/demo/product_product.xml @@ -9,7 +9,7 @@ Product with threshold set on the product - 30 + 30 diff --git a/product_stock_state/models/product_category.py b/product_stock_state/models/product_category.py index 2f528bc308b..ea8547c5044 100644 --- a/product_stock_state/models/product_category.py +++ b/product_stock_state/models/product_category.py @@ -1,15 +1,30 @@ # -*- coding: utf-8 -*- # 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 fields, models +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="Define custom value under wich 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 value defined for the company") + manual_stock_state_threshold = fields.Float() + + @api.multi + @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 index f7db1ebb52b..d382000fcfa 100644 --- a/product_stock_state/models/product_product.py +++ b/product_stock_state/models/product_product.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- -# Copyright 2017 Akretion (http://www.akretion.com). # Copyright 2017-Today GRAP (http://www.grap.coop). -# @author Sébastien BEAU +# 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 fields, models @@ -35,17 +37,7 @@ def _compute_stock_state(self): def _get_stock_state_threshold(self): self.ensure_one() threshold = self.stock_state_threshold - - if not threshold: - threshold = self.categ_id.stock_state_threshold - # try to get threshold from parent categories - category = self.categ_id - while category.parent_id and not threshold: - category = category.parent_id - threshold = category.stock_state_threshold - if not threshold: # try to get threshold from current company threshold = self.env.user.company_id.stock_state_threshold - return threshold diff --git a/product_stock_state/models/product_template.py b/product_stock_state/models/product_template.py index ee95c2dc38a..86133b0979e 100644 --- a/product_stock_state/models/product_template.py +++ b/product_stock_state/models/product_template.py @@ -1,17 +1,33 @@ # -*- coding: utf-8 -*- # 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 fields, models +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 wich 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") + + manual_stock_state_threshold = fields.Float() + + @api.multi + @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/views/product_category_view.xml b/product_stock_state/views/product_category_view.xml index 8eb99f7175e..8f9fbbdcf58 100644 --- a/product_stock_state/views/product_category_view.xml +++ b/product_stock_state/views/product_category_view.xml @@ -7,6 +7,7 @@ + diff --git a/product_stock_state/views/product_template_view.xml b/product_stock_state/views/product_template_view.xml index b246809f084..028dd5c64cd 100644 --- a/product_stock_state/views/product_template_view.xml +++ b/product_stock_state/views/product_template_view.xml @@ -7,6 +7,7 @@ + From ad717e7c56a13c8c6c4f3aa6a150cb40be0ecdc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Thu, 21 Mar 2019 12:47:40 +0100 Subject: [PATCH 11/37] [FIX] fix pep8, pylint and flaek8 --- product_stock_state/demo/res_company.xml | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 product_stock_state/demo/res_company.xml diff --git a/product_stock_state/demo/res_company.xml b/product_stock_state/demo/res_company.xml deleted file mode 100644 index d7c87e67962..00000000000 --- a/product_stock_state/demo/res_company.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - 10 - - - - From a13b1a4970ac5e9f6d785539f10c5f9bf6f7c59b Mon Sep 17 00:00:00 2001 From: "Laurent Mignon (ACSONE)" Date: Mon, 6 May 2019 17:40:28 +0200 Subject: [PATCH 12/37] [IMP] Add pre-commit hook and reformat code thanks to black --- product_stock_state/__manifest__.py | 37 ++++++++----------- product_stock_state/demo/product_category.xml | 1 - product_stock_state/demo/product_product.xml | 1 - product_stock_state/demo/res_groups.xml | 1 - product_stock_state/models/__init__.py | 1 - .../models/product_category.py | 16 +++++--- product_stock_state/models/product_product.py | 21 ++++++----- .../models/product_template.py | 16 +++++--- product_stock_state/models/res_company.py | 2 +- .../models/sale_config_settings.py | 10 +++-- product_stock_state/security/res_groups.xml | 1 - .../tests/test_product_stock_state.py | 33 +++++++++-------- .../views/product_category_view.xml | 1 - .../views/product_product_view.xml | 1 - .../views/product_template_view.xml | 1 - .../views/sale_config_settings_view.xml | 1 - 16 files changed, 71 insertions(+), 73 deletions(-) diff --git a/product_stock_state/__manifest__.py b/product_stock_state/__manifest__.py index 07296e01eed..88e8bfab097 100644 --- a/product_stock_state/__manifest__.py +++ b/product_stock_state/__manifest__.py @@ -8,36 +8,29 @@ { "name": "Product Stock State", - "summary": - "Compute the state stock based on" - "the stock level and sale_ok field", + "summary": "Compute the state stock based on" + "the stock level and sale_ok field", "version": "10.0.1.0.0", "category": "Uncategorized", "website": "www.akretion.com", "author": " Akretion,GRAP", "license": "AGPL-3", "application": False, - 'installable': True, - "external_dependencies": { - "python": [], - "bin": [], - }, - "depends": [ - "sale_stock", - ], + "installable": True, + "external_dependencies": {"python": [], "bin": []}, + "depends": ["sale_stock"], "data": [ - 'security/res_groups.xml', - 'views/product_template_view.xml', - 'views/product_product_view.xml', - 'views/product_category_view.xml', - 'views/sale_config_settings_view.xml', + "security/res_groups.xml", + "views/product_template_view.xml", + "views/product_product_view.xml", + "views/product_category_view.xml", + "views/sale_config_settings_view.xml", ], "demo": [ - 'demo/res_groups.xml', - 'demo/product_category.xml', - 'demo/product_product.xml', - 'demo/product_category.xml', + "demo/res_groups.xml", + "demo/product_category.xml", + "demo/product_product.xml", + "demo/product_category.xml", ], - "qweb": [ - ] + "qweb": [], } diff --git a/product_stock_state/demo/product_category.xml b/product_stock_state/demo/product_category.xml index 96d1a659048..16359f88294 100644 --- a/product_stock_state/demo/product_category.xml +++ b/product_stock_state/demo/product_category.xml @@ -7,4 +7,3 @@ - diff --git a/product_stock_state/demo/product_product.xml b/product_stock_state/demo/product_product.xml index e79b9cf1029..2bc7ef99dd9 100644 --- a/product_stock_state/demo/product_product.xml +++ b/product_stock_state/demo/product_product.xml @@ -13,4 +13,3 @@ - diff --git a/product_stock_state/demo/res_groups.xml b/product_stock_state/demo/res_groups.xml index f9c834d2618..3ad96483a40 100644 --- a/product_stock_state/demo/res_groups.xml +++ b/product_stock_state/demo/res_groups.xml @@ -14,4 +14,3 @@ - diff --git a/product_stock_state/models/__init__.py b/product_stock_state/models/__init__.py index 22ef06b7ce7..7268bf19b5c 100644 --- a/product_stock_state/models/__init__.py +++ b/product_stock_state/models/__init__.py @@ -5,4 +5,3 @@ from . import product_category from . import product_template from . import product_product - diff --git a/product_stock_state/models/product_category.py b/product_stock_state/models/product_category.py index ea8547c5044..d7cd968b79e 100644 --- a/product_stock_state/models/product_category.py +++ b/product_stock_state/models/product_category.py @@ -11,20 +11,24 @@ class ProductCategory(models.Model): - _inherit = 'product.category' + _inherit = "product.category" stock_state_threshold = fields.Float( - compute='_compute_stock_state_threshold', + compute="_compute_stock_state_threshold", store=True, help="Define custom value under wich 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 value defined for the company") + " State. If not set, Odoo will use the value defined for the company", + ) manual_stock_state_threshold = fields.Float() @api.multi - @api.depends('parent_id.stock_state_threshold', - 'manual_stock_state_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\ + 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 index d382000fcfa..758202d6465 100644 --- a/product_stock_state/models/product_product.py +++ b/product_stock_state/models/product_product.py @@ -11,28 +11,29 @@ class ProductProduct(models.Model): - _inherit = 'product.product' + _inherit = "product.product" _STOCK_STATE_SELECTION = [ - ('in_stock', 'In Stock'), - ('in_limited_stock', 'In Limited Stock'), - ('resupplying', 'Resupplying'), - ('out_of_stock', 'Out Of Stock'), + ("in_stock", "In Stock"), + ("in_limited_stock", "In Limited Stock"), + ("resupplying", "Resupplying"), + ("out_of_stock", "Out Of Stock"), ] stock_state = fields.Selection( - selection=_STOCK_STATE_SELECTION, compute='_compute_stock_state') + selection=_STOCK_STATE_SELECTION, compute="_compute_stock_state" + ) def _compute_stock_state(self): for product in self: if product.qty_available >= product._get_stock_state_threshold(): - product.stock_state = 'in_stock' + product.stock_state = "in_stock" elif product.qty_available > 0: - product.stock_state = 'in_limited_stock' + product.stock_state = "in_limited_stock" elif product.virtual_available > 0: - product.stock_state = 'resupplying' + product.stock_state = "resupplying" else: - product.stock_state = 'out_of_stock' + product.stock_state = "out_of_stock" def _get_stock_state_threshold(self): self.ensure_one() diff --git a/product_stock_state/models/product_template.py b/product_stock_state/models/product_template.py index 86133b0979e..460646395eb 100644 --- a/product_stock_state/models/product_template.py +++ b/product_stock_state/models/product_template.py @@ -11,23 +11,27 @@ class ProductTemplate(models.Model): - _inherit = 'product.template' + _inherit = "product.template" stock_state_threshold = fields.Float( - compute='_compute_stock_state_threshold', + compute="_compute_stock_state_threshold", store=True, help="Define custom value under wich 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") + " defined for the company", + ) manual_stock_state_threshold = fields.Float() @api.multi - @api.depends('categ_id.stock_state_threshold', - 'manual_stock_state_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\ + 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 index fb4a1ddbd82..7d86445c1ef 100644 --- a/product_stock_state/models/res_company.py +++ b/product_stock_state/models/res_company.py @@ -7,6 +7,6 @@ class ResCompany(models.Model): - _inherit = 'res.company' + _inherit = "res.company" stock_state_threshold = fields.Float(default=10) diff --git a/product_stock_state/models/sale_config_settings.py b/product_stock_state/models/sale_config_settings.py index 6ec18ed7dee..244687aa132 100644 --- a/product_stock_state/models/sale_config_settings.py +++ b/product_stock_state/models/sale_config_settings.py @@ -7,10 +7,12 @@ class SaleConfigSettings(models.TransientModel): - _inherit = 'sale.config.settings' + _inherit = "sale.config.settings" stock_state_threshold = fields.Float( - related='company_id.stock_state_threshold', - string="Stock State Threshold *", help="Define custom value" + related="company_id.stock_state_threshold", + string="Stock State Threshold *", + help="Define custom value" " under wich the stock state will pass from 'In Stock' to 'In Limited" - " Stock' State.") + " Stock' State.", + ) diff --git a/product_stock_state/security/res_groups.xml b/product_stock_state/security/res_groups.xml index 6d9b32341b0..2ef008e687b 100644 --- a/product_stock_state/security/res_groups.xml +++ b/product_stock_state/security/res_groups.xml @@ -12,4 +12,3 @@ - diff --git a/product_stock_state/tests/test_product_stock_state.py b/product_stock_state/tests/test_product_stock_state.py index 35d83292e80..9cbd44f1a54 100644 --- a/product_stock_state/tests/test_product_stock_state.py +++ b/product_stock_state/tests/test_product_stock_state.py @@ -8,45 +8,48 @@ class TestProductStockState(TransactionCase): - def setUp(self): super(TestProductStockState, self).setUp() - self.company = self.env.ref('base.main_company') - self.category = self.env.ref('product.product_category_1') + self.company = self.env.ref("base.main_company") + self.category = self.env.ref("product.product_category_1") self.product_by_company = self.env.ref( - 'product_stock_state.product_setting_by_company') - self.product_by_categ_direct = self.env.ref( - 'stock.product_icecream') + "product_stock_state.product_setting_by_company" + ) + self.product_by_categ_direct = self.env.ref("stock.product_icecream") self.product_by_categ_inherit = self.env.ref( - 'product.product_order_01') + "product.product_order_01" + ) self.product_by_product = self.env.ref( - 'product_stock_state.product_setting_by_product') + "product_stock_state.product_setting_by_product" + ) def test_01_global_product(self): """Test Global Settings""" self.assertEqual( self.product_by_company._get_stock_state_threshold(), - self.company.stock_state_threshold) + self.company.stock_state_threshold, + ) def test_02_category_setting_direct(self): """Test Category Setting (Setting on the product category)""" self.assertEqual( self.product_by_categ_direct._get_stock_state_threshold(), - self.category.stock_state_threshold) + self.category.stock_state_threshold, + ) def test_03_category_setting_inherit(self): """Test Category Setting (Setting on a parent category)""" self.assertEqual( self.product_by_categ_inherit._get_stock_state_threshold(), - self.category.stock_state_threshold) + self.category.stock_state_threshold, + ) def test_04_category_setting_inherit(self): """Test Product Setting (Setting on a product unique template)""" self.assertEqual( - self.product_by_product._get_stock_state_threshold(), - 30) + self.product_by_product._get_stock_state_threshold(), 30 + ) def test_05_state_out_of_stock(self): """Test Stock State computation""" - self.assertEqual( - self.product_by_product.stock_state, 'out_of_stock') + self.assertEqual(self.product_by_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 index 8f9fbbdcf58..9e2f60a0ec3 100644 --- a/product_stock_state/views/product_category_view.xml +++ b/product_stock_state/views/product_category_view.xml @@ -13,4 +13,3 @@ - diff --git a/product_stock_state/views/product_product_view.xml b/product_stock_state/views/product_product_view.xml index 81d7cf0b3b7..bebea8f2c5e 100644 --- a/product_stock_state/views/product_product_view.xml +++ b/product_stock_state/views/product_product_view.xml @@ -12,4 +12,3 @@ - diff --git a/product_stock_state/views/product_template_view.xml b/product_stock_state/views/product_template_view.xml index 028dd5c64cd..3a4991f2737 100644 --- a/product_stock_state/views/product_template_view.xml +++ b/product_stock_state/views/product_template_view.xml @@ -13,4 +13,3 @@ - diff --git a/product_stock_state/views/sale_config_settings_view.xml b/product_stock_state/views/sale_config_settings_view.xml index 5b0c9c4bb02..3e4c7ea927d 100644 --- a/product_stock_state/views/sale_config_settings_view.xml +++ b/product_stock_state/views/sale_config_settings_view.xml @@ -12,4 +12,3 @@ - From e1e63393def189ab42d5e7d0ca963f740a1ede22 Mon Sep 17 00:00:00 2001 From: Denis Roussel Date: Fri, 10 May 2019 11:28:27 +0200 Subject: [PATCH 13/37] [12.0] Set installable False to all modules --- product_stock_state/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product_stock_state/__manifest__.py b/product_stock_state/__manifest__.py index 88e8bfab097..f556b20a1fe 100644 --- a/product_stock_state/__manifest__.py +++ b/product_stock_state/__manifest__.py @@ -16,7 +16,7 @@ "author": " Akretion,GRAP", "license": "AGPL-3", "application": False, - "installable": True, + "installable": False, "external_dependencies": {"python": [], "bin": []}, "depends": ["sale_stock"], "data": [ From b85ac38f4f63d9c241a81d6944f97722fff4c6e8 Mon Sep 17 00:00:00 2001 From: "Laurent Mignon (ACSONE)" Date: Fri, 9 Aug 2019 16:09:10 +0200 Subject: [PATCH 14/37] [IMP] product_stock_state: Allows to override the way to get the qty available: This change is required to be able to use this addon with stock_available_immedialtly --- product_stock_state/models/product_product.py | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/product_stock_state/models/product_product.py b/product_stock_state/models/product_product.py index 758202d6465..8e7a4a08c6e 100644 --- a/product_stock_state/models/product_product.py +++ b/product_stock_state/models/product_product.py @@ -7,7 +7,7 @@ # @author Laurent Mignon # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models +from odoo import api, fields, models class ProductProduct(models.Model): @@ -24,13 +24,26 @@ class ProductProduct(models.Model): selection=_STOCK_STATE_SELECTION, compute="_compute_stock_state" ) + @api.multi + def _get_qty_available(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 + + @api.multi + @api.depends("qty_available", "incoming_qty") def _compute_stock_state(self): for product in self: - if product.qty_available >= product._get_stock_state_threshold(): + qty_available = product._get_qty_available() + if qty_available >= product._get_stock_state_threshold(): product.stock_state = "in_stock" - elif product.qty_available > 0: + elif qty_available > 0: product.stock_state = "in_limited_stock" - elif product.virtual_available > 0: + elif product.incoming_qty > 0: product.stock_state = "resupplying" else: product.stock_state = "out_of_stock" From 36d6a5f31b40654462116406e6235433a2984177 Mon Sep 17 00:00:00 2001 From: Kevin Khao Date: Fri, 6 Mar 2020 16:42:23 +0100 Subject: [PATCH 15/37] [12.0][ADD] product_stock_state: reformat for OCA, misc. improvements and cleanup --- product_stock_state/README.rst | 101 +++- product_stock_state/__init__.py | 2 - product_stock_state/__manifest__.py | 18 +- product_stock_state/data/data.xml | 10 + product_stock_state/demo/product_category.xml | 1 - product_stock_state/demo/product_product.xml | 5 + product_stock_state/demo/res_groups.xml | 6 +- product_stock_state/i18n/fr.po | 110 +++-- .../i18n/product_stock_state.pot | 160 ++++++ product_stock_state/models/__init__.py | 4 +- .../models/product_category.py | 13 +- product_stock_state/models/product_product.py | 32 +- .../models/product_template.py | 10 +- product_stock_state/models/res_company.py | 6 +- ...fig_settings.py => res_config_settings.py} | 10 +- product_stock_state/readme/CONFIGURE.rst | 11 + product_stock_state/readme/CONTRIBUTORS.rst | 3 + product_stock_state/readme/CREDITS.rst | 6 + product_stock_state/readme/DESCRIPTION.rst | 8 + product_stock_state/readme/USAGE.rst | 4 + .../static/description/index.html | 461 ++++++++++++++++++ product_stock_state/tests/__init__.py | 2 - .../tests/test_product_stock_state.py | 33 +- .../views/product_category_view.xml | 12 +- .../views/res_company_view.xml | 19 + .../views/res_config_settings_view.xml | 31 ++ .../views/sale_config_settings_view.xml | 14 - 27 files changed, 954 insertions(+), 138 deletions(-) create mode 100644 product_stock_state/data/data.xml create mode 100644 product_stock_state/i18n/product_stock_state.pot rename product_stock_state/models/{sale_config_settings.py => res_config_settings.py} (61%) create mode 100644 product_stock_state/readme/CONFIGURE.rst create mode 100644 product_stock_state/readme/CONTRIBUTORS.rst create mode 100644 product_stock_state/readme/CREDITS.rst create mode 100644 product_stock_state/readme/DESCRIPTION.rst create mode 100644 product_stock_state/readme/USAGE.rst create mode 100644 product_stock_state/static/description/index.html create mode 100644 product_stock_state/views/res_company_view.xml create mode 100644 product_stock_state/views/res_config_settings_view.xml delete mode 100644 product_stock_state/views/sale_config_settings_view.xml diff --git a/product_stock_state/README.rst b/product_stock_state/README.rst index 233acb8a809..b6c169af93c 100644 --- a/product_stock_state/README.rst +++ b/product_stock_state/README.rst @@ -1,13 +1,31 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 - =================== Product Stock State =================== -This module add a stock state on the product in order to give an human readable -information without giving the current quantity in stock. +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fproduct--attribute-lightgray.png?logo=github + :target: https://github.com/OCA/product-attribute/tree/12.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-12-0/product-attribute-12-0-product_stock_state + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/135/12.0 + :alt: Try me on Runbot + +|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 : @@ -16,6 +34,11 @@ The state value can be : * Resupplying (if qty forcasted is > 0) * Out of Stock (otherwise) +**Table of contents** + +.. contents:: + :local: + Configuration ============= @@ -34,40 +57,74 @@ You can configure thresholds : Usage ===== -Go on the product tree and see the stock state +Open product tree view and observe the stock state -.. image:: /product_stock_state/static/description/product_product_tree.png +.. image:: https://raw.githubusercontent.com/product_stock_state/static/description/product_product_tree.png :width: 800 px -Known issues / Roadmap -====================== - -* Company settings is in sale configuration, but it should be better on - stock configuration. It is not possible for the time being, because - stock.config.settings doesn't have company_id field in Odoo. - Bug Tracker =========== -Bugs are tracked on `GitHub Issues -`_. In case of trouble, please -check there if your issue has already been reported. If you spotted it first, -help us smashing it by providing a detailed and welcomed feedback. +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. Credits ======= +Authors +~~~~~~~ + +* Akretion +* GRAP + Contributors ------------- +~~~~~~~~~~~~ * Sebastien Beau * Sylvain LE GAL +* Kevin Khao -Funders -------- +Other credits +~~~~~~~~~~~~~ The development of this module has been financially supported by: * Akretion R&D * Adaptoo * GRAP, Groupement Régional Alimentaire de Proximité + + +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 index cde864bae21..0650744f6bc 100644 --- a/product_stock_state/__init__.py +++ b/product_stock_state/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - from . import models diff --git a/product_stock_state/__manifest__.py b/product_stock_state/__manifest__.py index f556b20a1fe..9ab162b9c1e 100644 --- a/product_stock_state/__manifest__.py +++ b/product_stock_state/__manifest__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 Akretion (http://www.akretion.com). # Copyright 2017-Today GRAP (http://www.grap.coop). # @author Sébastien BEAU @@ -8,23 +7,23 @@ { "name": "Product Stock State", - "summary": "Compute the state stock based on" + "summary": "Compute the state of a product's stock" "the stock level and sale_ok field", - "version": "10.0.1.0.0", - "category": "Uncategorized", - "website": "www.akretion.com", - "author": " Akretion,GRAP", + "version": "12.0.1.0.0", + "website": "https://github.com/oca/product-attribute", + "author": " Akretion, GRAP, Odoo Community Association (OCA)", "license": "AGPL-3", "application": False, - "installable": False, - "external_dependencies": {"python": [], "bin": []}, + "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/sale_config_settings_view.xml", + "views/res_config_settings_view.xml", + "views/res_company_view.xml", + "data/data.xml", ], "demo": [ "demo/res_groups.xml", @@ -33,4 +32,5 @@ "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..99e3619bf1a --- /dev/null +++ b/product_stock_state/data/data.xml @@ -0,0 +1,10 @@ + + + + + + Stock Threshold + 2 + + + diff --git a/product_stock_state/demo/product_category.xml b/product_stock_state/demo/product_category.xml index 16359f88294..3167884e3ad 100644 --- a/product_stock_state/demo/product_category.xml +++ b/product_stock_state/demo/product_category.xml @@ -1,7 +1,6 @@ - 20 diff --git a/product_stock_state/demo/product_product.xml b/product_stock_state/demo/product_product.xml index 2bc7ef99dd9..bc71beaf12e 100644 --- a/product_stock_state/demo/product_product.xml +++ b/product_stock_state/demo/product_product.xml @@ -4,6 +4,7 @@ Product with threshold set on the company + @@ -12,4 +13,8 @@ 30 + + 66 + + diff --git a/product_stock_state/demo/res_groups.xml b/product_stock_state/demo/res_groups.xml index 3ad96483a40..c3e52facf78 100644 --- a/product_stock_state/demo/res_groups.xml +++ b/product_stock_state/demo/res_groups.xml @@ -2,15 +2,15 @@ - + - + - + diff --git a/product_stock_state/i18n/fr.po b/product_stock_state/i18n/fr.po index e092b7ba5cb..ea8b8602d6d 100644 --- a/product_stock_state/i18n/fr.po +++ b/product_stock_state/i18n/fr.po @@ -1,19 +1,26 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: -# * product_stock_state +# * product_stock_state # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 10.0\n" +"Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-10-03 02:23+0000\n" -"PO-Revision-Date: 2017-10-03 02:23+0000\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" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" +"Content-Transfer-Encoding: 8bit\n" "Plural-Forms: \n" +"Language: fr\n" +"X-Generator: Poedit 2.3\n" + +#. 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 "Seuils des articles d'états de stocks" #. module: product_stock_state #: model:ir.model,name:product_stock_state.model_res_company @@ -21,20 +28,20 @@ msgid "Companies" msgstr "Sociétés" #. module: product_stock_state -#: model:ir.model.fields,help:product_stock_state.field_product_category_stock_state_threshold -msgid "Define custom value under wich 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 value defined for the company" -msgstr "Définir une valeur personnalisée en dessous de laquelle l'état du stock des produits de cette categorie passera de 'En stock' à 'En stock limité'. Si la valeur n'est pas définie, Odoo utilisera la valeur défini pour la société" +#: 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_sale_config_settings_stock_state_threshold -msgid "Define custom value under wich the stock state will pass from 'In Stock' to 'In Limited Stock' State." -msgstr "Définir une valeur personnalisée en dessous de laquelle l'état du stock passera de 'En stock' à 'En stock limité'." +#: 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 wich 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 une valeur personnalisée en dessous de laquelle l'état du stock passera de 'En stock' à 'En stock limité'. Si la valeur n'est pas définie, Odoo utilisera la valeur définie sur la catégorie. Si la catégorie n'a pas de valeur, Odoo utilisera la valeur définie au niveau de la société" +#: 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 #: selection:product.product,stock_state:0 @@ -46,6 +53,13 @@ msgstr "En stock limité" 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 #: selection:product.product,stock_state:0 msgid "Out Of Stock" @@ -70,13 +84,13 @@ msgstr "Modèle d'article" #: model:product.product,name:product_stock_state.product_setting_by_company #: model:product.template,name:product_stock_state.product_setting_by_company_product_template msgid "Product with threshold set on the company" -msgstr "Product with threshold set on the company" +msgstr "Article avec seuil fixé au niveau de l'entreprise" #. module: product_stock_state #: model:product.product,name:product_stock_state.product_setting_by_product #: model:product.template,name:product_stock_state.product_setting_by_product_product_template msgid "Product with threshold set on the product" -msgstr "Product with threshold set on the product" +msgstr "Article avec seuil fixé au niveau de l'article" #. module: product_stock_state #: selection:product.product,stock_state:0 @@ -84,9 +98,23 @@ msgid "Resupplying" msgstr "En réapprovisionnement" #. module: product_stock_state -#: model:ir.model.fields,field_description:product_stock_state.field_sale_config_settings_stock_state_threshold -msgid "Stock State Threshold *" -msgstr "Seuil pour l'état du stock *" +#: 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 @@ -99,25 +127,37 @@ msgid "Stock State Threshold by Product" msgstr "Seuil pour l'état du stock par produit" #. module: product_stock_state -#: model:ir.model.fields,field_description:product_stock_state.field_product_product_stock_state -msgid "Stock state" -msgstr "Etat du stock" +#: model_terms:ir.ui.view,arch_db:product_stock_state.view_product_category_form +msgid "Stock Threshold" +msgstr "Seuil de 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 -msgid "Stock state threshold" -msgstr "Seuil pour l'état du stock" +#: 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,name:product_stock_state.model_sale_config_settings -msgid "sale.config.settings" -msgstr "sale.config.settings" +#: model_terms:ir.ui.view,arch_db:product_stock_state.res_config_settings_view_form_threshold +msgid "Stock threshold" +msgstr "Seuil de stock" #. module: product_stock_state -#: model:ir.model,name:product_stock_state.model_stock_config_settings -msgid "stock.config.settings" -msgstr "stock.config.settings" +#: 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." +#. module: product_stock_state +#: model:product.product,uom_name:product_stock_state.product_setting_by_company +#: model:product.product,uom_name:product_stock_state.product_setting_by_product +#: model:product.template,uom_name:product_stock_state.product_setting_by_company_product_template +#: model:product.template,uom_name:product_stock_state.product_setting_by_product_product_template +msgid "Unit(s)" +msgstr "Unité(s)" + +#. module: product_stock_state +#: model:product.product,weight_uom_name:product_stock_state.product_setting_by_company +#: model:product.product,weight_uom_name:product_stock_state.product_setting_by_product +#: model:product.template,weight_uom_name:product_stock_state.product_setting_by_company_product_template +#: model:product.template,weight_uom_name:product_stock_state.product_setting_by_product_product_template +msgid "kg" +msgstr "kg" 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..37c8bbf34fd --- /dev/null +++ b/product_stock_state/i18n/product_stock_state.pot @@ -0,0 +1,160 @@ +# 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" +"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_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: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 +#: selection:product.product,stock_state:0 +msgid "In Limited Stock" +msgstr "" + +#. module: product_stock_state +#: selection:product.product,stock_state:0 +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 +#: selection:product.product,stock_state:0 +msgid "Out Of Stock" +msgstr "" + +#. module: product_stock_state +#: model:ir.model,name:product_stock_state.model_product_product +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_template +msgid "Product Template" +msgstr "" + +#. module: product_stock_state +#: model:product.product,name:product_stock_state.product_setting_by_company +#: 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.product,name:product_stock_state.product_setting_by_product +#: 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 +#: selection:product.product,stock_state:0 +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 +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_terms:ir.ui.view,arch_db:product_stock_state.res_config_settings_view_form_threshold +msgid "Stock threshold" +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 "" + +#. module: product_stock_state +#: model:product.product,uom_name:product_stock_state.product_setting_by_company +#: model:product.product,uom_name:product_stock_state.product_setting_by_product +#: model:product.template,uom_name:product_stock_state.product_setting_by_company_product_template +#: model:product.template,uom_name:product_stock_state.product_setting_by_product_product_template +msgid "Unit(s)" +msgstr "" + +#. module: product_stock_state +#: model:product.product,weight_uom_name:product_stock_state.product_setting_by_company +#: model:product.product,weight_uom_name:product_stock_state.product_setting_by_product +#: model:product.template,weight_uom_name:product_stock_state.product_setting_by_company_product_template +#: model:product.template,weight_uom_name:product_stock_state.product_setting_by_product_product_template +msgid "kg" +msgstr "" + diff --git a/product_stock_state/models/__init__.py b/product_stock_state/models/__init__.py index 7268bf19b5c..b9328a0a014 100644 --- a/product_stock_state/models/__init__.py +++ b/product_stock_state/models/__init__.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- - from . import res_company -from . import sale_config_settings +from . import res_config_settings from . import product_category from . import product_template from . import product_product diff --git a/product_stock_state/models/product_category.py b/product_stock_state/models/product_category.py index d7cd968b79e..bfbcf3fab0b 100644 --- a/product_stock_state/models/product_category.py +++ b/product_stock_state/models/product_category.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017-Today GRAP (http://www.grap.coop). # Copyright 2018 ACSONE SA/NV # Copyright 2018 Akretion (http://www.akretion.com). @@ -8,6 +7,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import api, fields, models +from odoo.addons import decimal_precision as dp class ProductCategory(models.Model): @@ -16,13 +16,16 @@ class ProductCategory(models.Model): stock_state_threshold = fields.Float( compute="_compute_stock_state_threshold", store=True, - help="Define custom value under wich the stock state of the products" + 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 value defined for the company", + " State. If not set, Odoo will use the threshold defined at the" + " company level.", + digits=dp.get_precision("Stock Threshold"), + ) + manual_stock_state_threshold = fields.Float( + digits=dp.get_precision("Stock Threshold") ) - manual_stock_state_threshold = fields.Float() - @api.multi @api.depends( "parent_id.stock_state_threshold", "manual_stock_state_threshold" ) diff --git a/product_stock_state/models/product_product.py b/product_stock_state/models/product_product.py index 8e7a4a08c6e..fa6bf9aaa0e 100644 --- a/product_stock_state/models/product_product.py +++ b/product_stock_state/models/product_product.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017-Today GRAP (http://www.grap.coop). # Copyright 2018 ACSONE SA/NV # Copyright 2018 Akretion (http://www.akretion.com). @@ -8,6 +7,7 @@ # 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): @@ -24,8 +24,7 @@ class ProductProduct(models.Model): selection=_STOCK_STATE_SELECTION, compute="_compute_stock_state" ) - @api.multi - def _get_qty_available(self): + 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 @@ -34,16 +33,33 @@ def _get_qty_available(self): self.ensure_one() return self.qty_available - @api.multi @api.depends("qty_available", "incoming_qty") def _compute_stock_state(self): for product in self: - qty_available = product._get_qty_available() - if qty_available >= product._get_stock_state_threshold(): + qty_available = product._get_qty_available_for_stock_state() + precision = self.env["decimal.precision"].precision_get( + "Stock Threshold" + ) + if ( + float_compare( + qty_available, + product._get_stock_state_threshold(), + precision_digits=precision, + ) + == 1 + ): product.stock_state = "in_stock" - elif qty_available > 0: + elif ( + float_compare(qty_available, 0, precision_digits=precision,) + == 1 + ): product.stock_state = "in_limited_stock" - elif product.incoming_qty > 0: + elif ( + float_compare( + product.incoming_qty, 0, precision_digits=precision, + ) + == 1 + ): product.stock_state = "resupplying" else: product.stock_state = "out_of_stock" diff --git a/product_stock_state/models/product_template.py b/product_stock_state/models/product_template.py index 460646395eb..0022e6f2ee4 100644 --- a/product_stock_state/models/product_template.py +++ b/product_stock_state/models/product_template.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017-Today GRAP (http://www.grap.coop). # Copyright 2018 ACSONE SA/NV # Copyright 2018 Akretion (http://www.akretion.com). @@ -8,6 +7,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import api, fields, models +from odoo.addons import decimal_precision as dp class ProductTemplate(models.Model): @@ -16,16 +16,18 @@ class ProductTemplate(models.Model): stock_state_threshold = fields.Float( compute="_compute_stock_state_threshold", store=True, - help="Define custom value under wich the stock state will pass from" + 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=dp.get_precision("Stock Threshold"), ) - manual_stock_state_threshold = fields.Float() + manual_stock_state_threshold = fields.Float( + digits=dp.get_precision("Stock Threshold") + ) - @api.multi @api.depends( "categ_id.stock_state_threshold", "manual_stock_state_threshold" ) diff --git a/product_stock_state/models/res_company.py b/product_stock_state/models/res_company.py index 7d86445c1ef..351899f9dcf 100644 --- a/product_stock_state/models/res_company.py +++ b/product_stock_state/models/res_company.py @@ -1,12 +1,14 @@ -# -*- coding: utf-8 -*- # 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 +from odoo.addons import decimal_precision as dp class ResCompany(models.Model): _inherit = "res.company" - stock_state_threshold = fields.Float(default=10) + stock_state_threshold = fields.Float( + default=10, digits=dp.get_precision("Stock Threshold") + ) diff --git a/product_stock_state/models/sale_config_settings.py b/product_stock_state/models/res_config_settings.py similarity index 61% rename from product_stock_state/models/sale_config_settings.py rename to product_stock_state/models/res_config_settings.py index 244687aa132..0e3254cebe7 100644 --- a/product_stock_state/models/sale_config_settings.py +++ b/product_stock_state/models/res_config_settings.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017-Today GRAP (http://www.grap.coop). # @author Sylvain LE GAL # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). @@ -6,13 +5,14 @@ from odoo import fields, models -class SaleConfigSettings(models.TransientModel): - _inherit = "sale.config.settings" +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" stock_state_threshold = fields.Float( related="company_id.stock_state_threshold", - string="Stock State Threshold *", + string="Stock State Threshold", + readonly=False, help="Define custom value" - " under wich the stock state will pass from 'In Stock' to 'In Limited" + " under which the stock state will pass from 'In Stock' to 'In Limited" " Stock' State.", ) diff --git a/product_stock_state/readme/CONFIGURE.rst b/product_stock_state/readme/CONFIGURE.rst new file mode 100644 index 00000000000..cc05be1bab2 --- /dev/null +++ b/product_stock_state/readme/CONFIGURE.rst @@ -0,0 +1,11 @@ +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.rst b/product_stock_state/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..39fd70c8dea --- /dev/null +++ b/product_stock_state/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Sebastien Beau +* Sylvain LE GAL +* Kevin Khao diff --git a/product_stock_state/readme/CREDITS.rst b/product_stock_state/readme/CREDITS.rst new file mode 100644 index 00000000000..f8efa9787db --- /dev/null +++ b/product_stock_state/readme/CREDITS.rst @@ -0,0 +1,6 @@ +The development of this module has been financially supported by: + +* Akretion R&D +* Adaptoo +* GRAP, Groupement Régional Alimentaire de Proximité + diff --git a/product_stock_state/readme/DESCRIPTION.rst b/product_stock_state/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..1291f2d688c --- /dev/null +++ b/product_stock_state/readme/DESCRIPTION.rst @@ -0,0 +1,8 @@ +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.rst b/product_stock_state/readme/USAGE.rst new file mode 100644 index 00000000000..81c026df601 --- /dev/null +++ b/product_stock_state/readme/USAGE.rst @@ -0,0 +1,4 @@ +Open product tree view and observe the stock state + +.. image:: /product_stock_state/static/description/product_product_tree.png + :width: 800 px diff --git a/product_stock_state/static/description/index.html b/product_stock_state/static/description/index.html new file mode 100644 index 00000000000..bd4d3162438 --- /dev/null +++ b/product_stock_state/static/description/index.html @@ -0,0 +1,461 @@ + + + + + + +Product Stock State + + + +
+

Product Stock State

+ + +

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

+

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

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

+

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

+
+
+

Credits

+
+

Authors

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

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

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/tests/__init__.py b/product_stock_state/tests/__init__.py index 25fba521460..c914b7e695e 100644 --- a/product_stock_state/tests/__init__.py +++ b/product_stock_state/tests/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - 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 index 9cbd44f1a54..d160166e920 100644 --- a/product_stock_state/tests/test_product_stock_state.py +++ b/product_stock_state/tests/test_product_stock_state.py @@ -1,55 +1,54 @@ -# -*- coding: utf-8 -*- # 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.tests.common import TransactionCase class TestProductStockState(TransactionCase): def setUp(self): - super(TestProductStockState, self).setUp() + super().setUp() self.company = self.env.ref("base.main_company") - self.category = self.env.ref("product.product_category_1") - self.product_by_company = self.env.ref( + self.category_furniture = self.env.ref("product.product_category_5") + self.category_saleable = self.env.ref("product.product_category_1") + self.product_chair = self.env.ref("product.product_product_12") + self.product_threshold_on_company = self.env.ref( "product_stock_state.product_setting_by_company" ) - self.product_by_categ_direct = self.env.ref("stock.product_icecream") - self.product_by_categ_inherit = self.env.ref( - "product.product_order_01" - ) - self.product_by_product = self.env.ref( + self.product_threshold_on_product = self.env.ref( "product_stock_state.product_setting_by_product" ) def test_01_global_product(self): """Test Global Settings""" self.assertEqual( - self.product_by_company._get_stock_state_threshold(), + 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_by_categ_direct._get_stock_state_threshold(), - self.category.stock_state_threshold, + 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_by_categ_inherit._get_stock_state_threshold(), - self.category.stock_state_threshold, + 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_by_product._get_stock_state_threshold(), 30 + 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_by_product.stock_state, "out_of_stock") + 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 index 9e2f60a0ec3..a822834eb24 100644 --- a/product_stock_state/views/product_category_view.xml +++ b/product_stock_state/views/product_category_view.xml @@ -1,15 +1,15 @@ - product.category - - - - + + + + + + - 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..d35ce8fd801 --- /dev/null +++ b/product_stock_state/views/res_company_view.xml @@ -0,0 +1,19 @@ + + + + + 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..63c101d1170 --- /dev/null +++ b/product_stock_state/views/res_config_settings_view.xml @@ -0,0 +1,31 @@ + + + + + res.config.settings.threshold + res.config.settings + + + + +
+
+
+
+ Product stock thresholds +
+ Set stock state threshold +
+
+
+
+
+
+
+
+
+
+
+
diff --git a/product_stock_state/views/sale_config_settings_view.xml b/product_stock_state/views/sale_config_settings_view.xml deleted file mode 100644 index 3e4c7ea927d..00000000000 --- a/product_stock_state/views/sale_config_settings_view.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - sale.config.settings - - - - - - - - - From a9ab4d9c3cf3314743f7addd9d248f9cef5478f3 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 27 Mar 2020 16:13:03 +0000 Subject: [PATCH 16/37] [ADD] icon.png --- product_stock_state/static/description/icon.png | Bin 0 -> 9455 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 product_stock_state/static/description/icon.png diff --git a/product_stock_state/static/description/icon.png b/product_stock_state/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 From efdfd56ebf1f7bf0b9f4a58fb06fc49f54c189c7 Mon Sep 17 00:00:00 2001 From: OCA Transbot Date: Mon, 27 Apr 2020 18:49:01 +0000 Subject: [PATCH 17/37] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: product-attribute-12.0/product-attribute-12.0-product_stock_state Translate-URL: https://translation.odoo-community.org/projects/product-attribute-12-0/product-attribute-12-0-product_stock_state/ --- product_stock_state/i18n/fr.po | 36 ++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/product_stock_state/i18n/fr.po b/product_stock_state/i18n/fr.po index ea8b8602d6d..22b04d43b53 100644 --- a/product_stock_state/i18n/fr.po +++ b/product_stock_state/i18n/fr.po @@ -10,17 +10,18 @@ msgstr "" "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" -"Language: fr\n" "X-Generator: Poedit 2.3\n" #. 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 "Seuils des articles d'états de stocks" +msgstr "" +"Seuils des articles d'états de stocks" #. module: product_stock_state #: model:ir.model,name:product_stock_state.model_res_company @@ -34,14 +35,26 @@ 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é\"." +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" +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 #: selection:product.product,stock_state:0 @@ -143,8 +156,15 @@ msgstr "Seuil 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 "" +"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." #. module: product_stock_state #: model:product.product,uom_name:product_stock_state.product_setting_by_company From 3e968e0a7c05253373534d5e11995334d13f9835 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 2 Jul 2020 15:09:25 +0200 Subject: [PATCH 18/37] [REF] product_stock_state: Black python code --- product_stock_state/models/product_product.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product_stock_state/models/product_product.py b/product_stock_state/models/product_product.py index fa6bf9aaa0e..7789cefa455 100644 --- a/product_stock_state/models/product_product.py +++ b/product_stock_state/models/product_product.py @@ -50,13 +50,13 @@ def _compute_stock_state(self): ): product.stock_state = "in_stock" elif ( - float_compare(qty_available, 0, precision_digits=precision,) + float_compare(qty_available, 0, precision_digits=precision) == 1 ): product.stock_state = "in_limited_stock" elif ( float_compare( - product.incoming_qty, 0, precision_digits=precision, + product.incoming_qty, 0, precision_digits=precision ) == 1 ): From cff0e833eb4956fa136bc15d18f661c4a6625fc9 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 2 Jul 2020 15:09:25 +0200 Subject: [PATCH 19/37] [MIG] product_stock_state: Migration to 13.0 --- product_stock_state/__manifest__.py | 2 +- product_stock_state/data/data.xml | 8 ++++++-- product_stock_state/demo/product_category.xml | 4 +--- product_stock_state/demo/product_product.xml | 12 ++++-------- product_stock_state/demo/res_groups.xml | 6 +----- .../models/product_category.py | 8 +++----- .../models/product_template.py | 7 ++----- product_stock_state/models/res_company.py | 1 + product_stock_state/readme/CREDITS.rst | 1 - product_stock_state/security/res_groups.xml | 9 +++------ .../tests/test_product_stock_state.py | 4 +--- .../views/product_category_view.xml | 8 ++++---- .../views/product_product_view.xml | 8 +++----- .../views/product_template_view.xml | 10 ++++------ .../views/res_company_view.xml | 6 +++--- .../views/res_config_settings_view.xml | 19 +++++++++++++------ 16 files changed, 50 insertions(+), 63 deletions(-) diff --git a/product_stock_state/__manifest__.py b/product_stock_state/__manifest__.py index 9ab162b9c1e..3adae538611 100644 --- a/product_stock_state/__manifest__.py +++ b/product_stock_state/__manifest__.py @@ -9,7 +9,7 @@ "name": "Product Stock State", "summary": "Compute the state of a product's stock" "the stock level and sale_ok field", - "version": "12.0.1.0.0", + "version": "13.0.1.0.0", "website": "https://github.com/oca/product-attribute", "author": " Akretion, GRAP, Odoo Community Association (OCA)", "license": "AGPL-3", diff --git a/product_stock_state/data/data.xml b/product_stock_state/data/data.xml index 99e3619bf1a..f0261f5b94c 100644 --- a/product_stock_state/data/data.xml +++ b/product_stock_state/data/data.xml @@ -1,8 +1,12 @@ - + - + Stock Threshold 2 diff --git a/product_stock_state/demo/product_category.xml b/product_stock_state/demo/product_category.xml index 3167884e3ad..dd9f0bc0e0e 100644 --- a/product_stock_state/demo/product_category.xml +++ b/product_stock_state/demo/product_category.xml @@ -1,8 +1,6 @@ - + - 20 - diff --git a/product_stock_state/demo/product_product.xml b/product_stock_state/demo/product_product.xml index bc71beaf12e..fb0bfb1dc3d 100644 --- a/product_stock_state/demo/product_product.xml +++ b/product_stock_state/demo/product_product.xml @@ -1,20 +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 index c3e52facf78..eb6fde03e10 100644 --- a/product_stock_state/demo/res_groups.xml +++ b/product_stock_state/demo/res_groups.xml @@ -1,16 +1,12 @@ - + - - - - diff --git a/product_stock_state/models/product_category.py b/product_stock_state/models/product_category.py index bfbcf3fab0b..ea5655220aa 100644 --- a/product_stock_state/models/product_category.py +++ b/product_stock_state/models/product_category.py @@ -7,6 +7,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import api, fields, models + from odoo.addons import decimal_precision as dp @@ -26,12 +27,9 @@ class ProductCategory(models.Model): digits=dp.get_precision("Stock Threshold") ) - @api.depends( - "parent_id.stock_state_threshold", "manual_stock_state_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 + rec.manual_stock_state_threshold or rec.parent_id.stock_state_threshold ) diff --git a/product_stock_state/models/product_template.py b/product_stock_state/models/product_template.py index 0022e6f2ee4..9d6feb521a9 100644 --- a/product_stock_state/models/product_template.py +++ b/product_stock_state/models/product_template.py @@ -28,12 +28,9 @@ class ProductTemplate(models.Model): digits=dp.get_precision("Stock Threshold") ) - @api.depends( - "categ_id.stock_state_threshold", "manual_stock_state_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 + 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 index 351899f9dcf..28670ce52c1 100644 --- a/product_stock_state/models/res_company.py +++ b/product_stock_state/models/res_company.py @@ -3,6 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import fields, models + from odoo.addons import decimal_precision as dp diff --git a/product_stock_state/readme/CREDITS.rst b/product_stock_state/readme/CREDITS.rst index f8efa9787db..7d07d9765c9 100644 --- a/product_stock_state/readme/CREDITS.rst +++ b/product_stock_state/readme/CREDITS.rst @@ -3,4 +3,3 @@ The development of this module has been financially supported by: * Akretion R&D * Adaptoo * GRAP, Groupement Régional Alimentaire de Proximité - diff --git a/product_stock_state/security/res_groups.xml b/product_stock_state/security/res_groups.xml index 2ef008e687b..a95086dde26 100644 --- a/product_stock_state/security/res_groups.xml +++ b/product_stock_state/security/res_groups.xml @@ -1,14 +1,11 @@ - + - Stock State Threshold by Product - + - Stock State Threshold by Category - + - diff --git a/product_stock_state/tests/test_product_stock_state.py b/product_stock_state/tests/test_product_stock_state.py index d160166e920..a3605f37c84 100644 --- a/product_stock_state/tests/test_product_stock_state.py +++ b/product_stock_state/tests/test_product_stock_state.py @@ -49,6 +49,4 @@ def test_04_category_setting_inherit(self): def test_05_state_out_of_stock(self): """Test Stock State computation""" - self.assertEqual( - self.product_threshold_on_product.stock_state, "out_of_stock" - ) + 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 index a822834eb24..d4f37f24eb5 100644 --- a/product_stock_state/views/product_category_view.xml +++ b/product_stock_state/views/product_category_view.xml @@ -1,13 +1,13 @@ - + product.category - + - - + + diff --git a/product_stock_state/views/product_product_view.xml b/product_stock_state/views/product_product_view.xml index bebea8f2c5e..5d996591767 100644 --- a/product_stock_state/views/product_product_view.xml +++ b/product_stock_state/views/product_product_view.xml @@ -1,14 +1,12 @@ - + - product.product - + - + - diff --git a/product_stock_state/views/product_template_view.xml b/product_stock_state/views/product_template_view.xml index 3a4991f2737..2032b8eb8a4 100644 --- a/product_stock_state/views/product_template_view.xml +++ b/product_stock_state/views/product_template_view.xml @@ -1,15 +1,13 @@ - + - product.template - + - - + + - diff --git a/product_stock_state/views/res_company_view.xml b/product_stock_state/views/res_company_view.xml index d35ce8fd801..25ac176d4fe 100644 --- a/product_stock_state/views/res_company_view.xml +++ b/product_stock_state/views/res_company_view.xml @@ -1,15 +1,15 @@ - + 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 index 63c101d1170..b84a20005c2 100644 --- a/product_stock_state/views/res_config_settings_view.xml +++ b/product_stock_state/views/res_config_settings_view.xml @@ -1,13 +1,16 @@ - + res.config.settings.threshold res.config.settings - - + + - +
@@ -18,8 +21,12 @@
-
From 4653dd912d1e47df55cec31ea401bc5f81114442 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 2 Jul 2020 15:28:40 +0200 Subject: [PATCH 20/37] product_stock_state: improve api and tests --- product_stock_state/models/product_product.py | 70 +++++++++++-------- .../models/product_template.py | 1 + product_stock_state/readme/CONTRIBUTORS.rst | 1 + .../tests/test_product_stock_state.py | 23 +++--- 4 files changed, 56 insertions(+), 39 deletions(-) diff --git a/product_stock_state/models/product_product.py b/product_stock_state/models/product_product.py index 7789cefa455..56eac078026 100644 --- a/product_stock_state/models/product_product.py +++ b/product_stock_state/models/product_product.py @@ -1,9 +1,11 @@ # 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 @@ -21,9 +23,12 @@ class ProductProduct(models.Model): ] stock_state = fields.Selection( - selection=_STOCK_STATE_SELECTION, compute="_compute_stock_state" + selection="_selection_stock_state", compute="_compute_stock_state" ) + def _selection_stock_state(self): + return self._STOCK_STATE_SELECTION + def _get_qty_available_for_stock_state(self): """ This method can be overridden to provide the available qty. @@ -33,41 +38,48 @@ def _get_qty_available_for_stock_state(self): self.ensure_one() return self.qty_available - @api.depends("qty_available", "incoming_qty") + 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._selection_stock_state()] + + @api.depends( + "qty_available", + "incoming_qty", + "stock_state_threshold", + "company_id.stock_state_threshold", + ) def _compute_stock_state(self): for product in self: qty_available = product._get_qty_available_for_stock_state() - precision = self.env["decimal.precision"].precision_get( - "Stock Threshold" - ) - if ( - float_compare( - qty_available, - product._get_stock_state_threshold(), - precision_digits=precision, - ) - == 1 - ): - product.stock_state = "in_stock" - elif ( - float_compare(qty_available, 0, precision_digits=precision) - == 1 - ): - product.stock_state = "in_limited_stock" - elif ( - float_compare( - product.incoming_qty, 0, precision_digits=precision - ) - == 1 - ): - product.stock_state = "resupplying" - else: - product.stock_state = "out_of_stock" + precision = self.env["decimal.precision"].precision_get("Stock Threshold") + 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.user.company_id.stock_state_threshold + 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 index 9d6feb521a9..e0c7d4f6685 100644 --- a/product_stock_state/models/product_template.py +++ b/product_stock_state/models/product_template.py @@ -7,6 +7,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import api, fields, models + from odoo.addons import decimal_precision as dp diff --git a/product_stock_state/readme/CONTRIBUTORS.rst b/product_stock_state/readme/CONTRIBUTORS.rst index 39fd70c8dea..3c8e7ba2217 100644 --- a/product_stock_state/readme/CONTRIBUTORS.rst +++ b/product_stock_state/readme/CONTRIBUTORS.rst @@ -1,3 +1,4 @@ * Sebastien Beau * Sylvain LE GAL * Kevin Khao +* Simone Orsi diff --git a/product_stock_state/tests/test_product_stock_state.py b/product_stock_state/tests/test_product_stock_state.py index a3605f37c84..4fed84ba0a8 100644 --- a/product_stock_state/tests/test_product_stock_state.py +++ b/product_stock_state/tests/test_product_stock_state.py @@ -1,21 +1,24 @@ # 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 +from odoo.tests.common import SavepointCase -class TestProductStockState(TransactionCase): - def setUp(self): - super().setUp() - self.company = self.env.ref("base.main_company") - self.category_furniture = self.env.ref("product.product_category_5") - self.category_saleable = self.env.ref("product.product_category_1") - self.product_chair = self.env.ref("product.product_product_12") - self.product_threshold_on_company = self.env.ref( +class TestProductStockState(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.company = cls.env.ref("base.main_company") + cls.category_furniture = cls.env.ref("product.product_category_5") + cls.category_saleable = cls.env.ref("product.product_category_1") + cls.product_chair = cls.env.ref("product.product_product_12") + cls.product_threshold_on_company = cls.env.ref( "product_stock_state.product_setting_by_company" ) - self.product_threshold_on_product = self.env.ref( + cls.product_threshold_on_product = cls.env.ref( "product_stock_state.product_setting_by_product" ) From 5ba24b270b5974175f9f35c81770bb73df1b270a Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Tue, 21 Jul 2020 16:05:01 +0200 Subject: [PATCH 21/37] Fix odoo warning about deprecated get_precision [UPD] Update product_stock_state.pot [UPD] README.rst Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: product-attribute-13.0/product-attribute-13.0-product_stock_state Translate-URL: https://translation.odoo-community.org/projects/product-attribute-13-0/product-attribute-13-0-product_stock_state/ Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: product-attribute-13.0/product-attribute-13.0-product_stock_state Translate-URL: https://translation.odoo-community.org/projects/product-attribute-13-0/product-attribute-13-0-product_stock_state/ --- product_stock_state/README.rst | 12 ++--- product_stock_state/i18n/fr.po | 44 ++++++---------- .../i18n/product_stock_state.pot | 52 ++++++------------- .../models/product_category.py | 8 +-- .../models/product_template.py | 8 +-- product_stock_state/models/res_company.py | 6 +-- .../static/description/index.html | 7 +-- 7 files changed, 47 insertions(+), 90 deletions(-) diff --git a/product_stock_state/README.rst b/product_stock_state/README.rst index b6c169af93c..b9fec6f33c7 100644 --- a/product_stock_state/README.rst +++ b/product_stock_state/README.rst @@ -14,13 +14,13 @@ Product Stock State :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/12.0/product_stock_state + :target: https://github.com/OCA/product-attribute/tree/13.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-12-0/product-attribute-12-0-product_stock_state + :target: https://translation.odoo-community.org/projects/product-attribute-13-0/product-attribute-13-0-product_stock_state :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/135/12.0 + :target: https://runbot.odoo-community.org/runbot/135/13.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -68,7 +68,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -87,6 +87,7 @@ Contributors * Sebastien Beau * Sylvain LE GAL * Kevin Khao +* Simone Orsi Other credits ~~~~~~~~~~~~~ @@ -97,7 +98,6 @@ The development of this module has been financially supported by: * Adaptoo * GRAP, Groupement Régional Alimentaire de Proximité - Maintainers ~~~~~~~~~~~ @@ -125,6 +125,6 @@ Current `maintainers `__: |maintainer-sebastienbeau| |maintainer-legalsylvain| |maintainer-kevinkhao| -This module is part of the `OCA/product-attribute `_ project on GitHub. +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/i18n/fr.po b/product_stock_state/i18n/fr.po index 22b04d43b53..47102868531 100644 --- a/product_stock_state/i18n/fr.po +++ b/product_stock_state/i18n/fr.po @@ -56,16 +56,6 @@ msgstr "" "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 -#: selection:product.product,stock_state:0 -msgid "In Limited Stock" -msgstr "En stock limité" - -#. module: product_stock_state -#: selection:product.product,stock_state:0 -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 @@ -73,11 +63,6 @@ msgstr "En stock" msgid "Manual Stock State Threshold" msgstr "Seuil pour l'état du stock manuel" -#. module: product_stock_state -#: selection:product.product,stock_state:0 -msgid "Out Of Stock" -msgstr "En rupture de stock" - #. module: product_stock_state #: model:ir.model,name:product_stock_state.model_product_product msgid "Product" @@ -105,11 +90,6 @@ msgstr "Article avec seuil fixé au niveau de l'entreprise" msgid "Product with threshold set on the product" msgstr "Article avec seuil fixé au niveau de l'article" -#. module: product_stock_state -#: selection:product.product,stock_state:0 -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" @@ -171,13 +151,21 @@ msgstr "" #: model:product.product,uom_name:product_stock_state.product_setting_by_product #: model:product.template,uom_name:product_stock_state.product_setting_by_company_product_template #: model:product.template,uom_name:product_stock_state.product_setting_by_product_product_template -msgid "Unit(s)" +#, fuzzy +msgid "Units" msgstr "Unité(s)" -#. module: product_stock_state -#: model:product.product,weight_uom_name:product_stock_state.product_setting_by_company -#: model:product.product,weight_uom_name:product_stock_state.product_setting_by_product -#: model:product.template,weight_uom_name:product_stock_state.product_setting_by_company_product_template -#: model:product.template,weight_uom_name:product_stock_state.product_setting_by_product_product_template -msgid "kg" -msgstr "kg" +#~ msgid "In Limited Stock" +#~ msgstr "En stock limité" + +#~ msgid "In Stock" +#~ msgstr "En stock" + +#~ msgid "Out Of Stock" +#~ msgstr "En rupture de stock" + +#~ msgid "Resupplying" +#~ msgstr "En réapprovisionnement" + +#~ msgid "kg" +#~ msgstr "kg" diff --git a/product_stock_state/i18n/product_stock_state.pot b/product_stock_state/i18n/product_stock_state.pot index 37c8bbf34fd..603b1694b4a 100644 --- a/product_stock_state/i18n/product_stock_state.pot +++ b/product_stock_state/i18n/product_stock_state.pot @@ -1,12 +1,12 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: -# * product_stock_state +# * product_stock_state # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 12.0\n" +"Project-Id-Version: Odoo Server 13.0\n" "Report-Msgid-Bugs-To: \n" -"Last-Translator: <>\n" +"Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -30,23 +30,19 @@ 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." +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 -#: selection:product.product,stock_state:0 -msgid "In Limited Stock" -msgstr "" - -#. module: product_stock_state -#: selection:product.product,stock_state:0 -msgid "In Stock" +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 @@ -56,11 +52,6 @@ msgstr "" msgid "Manual Stock State Threshold" msgstr "" -#. module: product_stock_state -#: selection:product.product,stock_state:0 -msgid "Out Of Stock" -msgstr "" - #. module: product_stock_state #: model:ir.model,name:product_stock_state.model_product_product msgid "Product" @@ -88,11 +79,6 @@ msgstr "" msgid "Product with threshold set on the product" msgstr "" -#. module: product_stock_state -#: selection:product.product,stock_state:0 -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" @@ -139,7 +125,10 @@ 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." +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 "" #. module: product_stock_state @@ -147,14 +136,5 @@ msgstr "" #: model:product.product,uom_name:product_stock_state.product_setting_by_product #: model:product.template,uom_name:product_stock_state.product_setting_by_company_product_template #: model:product.template,uom_name:product_stock_state.product_setting_by_product_product_template -msgid "Unit(s)" +msgid "Units" msgstr "" - -#. module: product_stock_state -#: model:product.product,weight_uom_name:product_stock_state.product_setting_by_company -#: model:product.product,weight_uom_name:product_stock_state.product_setting_by_product -#: model:product.template,weight_uom_name:product_stock_state.product_setting_by_company_product_template -#: model:product.template,weight_uom_name:product_stock_state.product_setting_by_product_product_template -msgid "kg" -msgstr "" - diff --git a/product_stock_state/models/product_category.py b/product_stock_state/models/product_category.py index ea5655220aa..c2078ac8088 100644 --- a/product_stock_state/models/product_category.py +++ b/product_stock_state/models/product_category.py @@ -8,8 +8,6 @@ from odoo import api, fields, models -from odoo.addons import decimal_precision as dp - class ProductCategory(models.Model): _inherit = "product.category" @@ -21,11 +19,9 @@ class ProductCategory(models.Model): " 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=dp.get_precision("Stock Threshold"), - ) - manual_stock_state_threshold = fields.Float( - digits=dp.get_precision("Stock Threshold") + digits="Stock Threshold", ) + 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): diff --git a/product_stock_state/models/product_template.py b/product_stock_state/models/product_template.py index e0c7d4f6685..2ba49b7c45f 100644 --- a/product_stock_state/models/product_template.py +++ b/product_stock_state/models/product_template.py @@ -8,8 +8,6 @@ from odoo import api, fields, models -from odoo.addons import decimal_precision as dp - class ProductTemplate(models.Model): _inherit = "product.template" @@ -22,12 +20,10 @@ class ProductTemplate(models.Model): " 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=dp.get_precision("Stock Threshold"), + digits="Stock Threshold", ) - manual_stock_state_threshold = fields.Float( - digits=dp.get_precision("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): diff --git a/product_stock_state/models/res_company.py b/product_stock_state/models/res_company.py index 28670ce52c1..143c1ca4354 100644 --- a/product_stock_state/models/res_company.py +++ b/product_stock_state/models/res_company.py @@ -4,12 +4,8 @@ from odoo import fields, models -from odoo.addons import decimal_precision as dp - class ResCompany(models.Model): _inherit = "res.company" - stock_state_threshold = fields.Float( - default=10, digits=dp.get_precision("Stock Threshold") - ) + stock_state_threshold = fields.Float(default=10, digits="Stock Threshold") diff --git a/product_stock_state/static/description/index.html b/product_stock_state/static/description/index.html index bd4d3162438..4c8da2ad8d3 100644 --- a/product_stock_state/static/description/index.html +++ b/product_stock_state/static/description/index.html @@ -367,7 +367,7 @@

Product Stock State

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

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

+

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

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 :

@@ -452,7 +453,7 @@

Maintainers

promote its widespread use.

Current maintainers:

sebastienbeau legalsylvain kevinkhao

-

This module is part of the OCA/product-attribute project on GitHub.

+

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.

From 588442610e7433caa2ec65f7c940985703c13dc0 Mon Sep 17 00:00:00 2001 From: watthanun Date: Wed, 11 Nov 2020 14:13:46 +0700 Subject: [PATCH 22/37] [IMP] product_stock_state: black, isort, prettier --- product_stock_state/__manifest__.py | 2 +- product_stock_state/models/product_product.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/product_stock_state/__manifest__.py b/product_stock_state/__manifest__.py index 3adae538611..1d5f087f3b8 100644 --- a/product_stock_state/__manifest__.py +++ b/product_stock_state/__manifest__.py @@ -10,7 +10,7 @@ "summary": "Compute the state of a product's stock" "the stock level and sale_ok field", "version": "13.0.1.0.0", - "website": "https://github.com/oca/product-attribute", + "website": "https://github.com/OCA/product-attribute", "author": " Akretion, GRAP, Odoo Community Association (OCA)", "license": "AGPL-3", "application": False, diff --git a/product_stock_state/models/product_product.py b/product_stock_state/models/product_product.py index 56eac078026..664627a02bd 100644 --- a/product_stock_state/models/product_product.py +++ b/product_stock_state/models/product_product.py @@ -41,7 +41,9 @@ def _get_qty_available_for_stock_state(self): def _stock_state_check_in_stock(self, qty, precision): return ( float_compare( - qty, self._get_stock_state_threshold(), precision_digits=precision, + qty, + self._get_stock_state_threshold(), + precision_digits=precision, ) == 1 ) From d9fbbf7aad2ad7ccc21ef852147fdfa950b75422 Mon Sep 17 00:00:00 2001 From: watthanun Date: Wed, 11 Nov 2020 14:48:07 +0700 Subject: [PATCH 23/37] [MIG] product_stock_state: Migration to 14.0 [UPD] Update product_stock_state.pot --- product_stock_state/README.rst | 11 +++-- product_stock_state/__manifest__.py | 2 +- product_stock_state/i18n/fr.po | 46 +++++++++++++++++-- .../i18n/product_stock_state.pot | 45 +++++++++++++++++- product_stock_state/models/product_product.py | 2 +- product_stock_state/readme/CONTRIBUTORS.rst | 1 + .../static/description/index.html | 7 +-- 7 files changed, 100 insertions(+), 14 deletions(-) diff --git a/product_stock_state/README.rst b/product_stock_state/README.rst index b9fec6f33c7..3e0755d3d33 100644 --- a/product_stock_state/README.rst +++ b/product_stock_state/README.rst @@ -14,13 +14,13 @@ Product Stock State :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/13.0/product_stock_state + :target: https://github.com/OCA/product-attribute/tree/14.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-13-0/product-attribute-13-0-product_stock_state + :target: https://translation.odoo-community.org/projects/product-attribute-14-0/product-attribute-14-0-product_stock_state :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/135/13.0 + :target: https://runbot.odoo-community.org/runbot/135/14.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -68,7 +68,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -88,6 +88,7 @@ Contributors * Sylvain LE GAL * Kevin Khao * Simone Orsi +* Watthanun Khorchai Other credits ~~~~~~~~~~~~~ @@ -125,6 +126,6 @@ Current `maintainers `__: |maintainer-sebastienbeau| |maintainer-legalsylvain| |maintainer-kevinkhao| -This module is part of the `OCA/product-attribute `_ project on GitHub. +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/__manifest__.py b/product_stock_state/__manifest__.py index 1d5f087f3b8..44b33e5ea15 100644 --- a/product_stock_state/__manifest__.py +++ b/product_stock_state/__manifest__.py @@ -9,7 +9,7 @@ "name": "Product Stock State", "summary": "Compute the state of a product's stock" "the stock level and sale_ok field", - "version": "13.0.1.0.0", + "version": "14.0.1.0.0", "website": "https://github.com/OCA/product-attribute", "author": " Akretion, GRAP, Odoo Community Association (OCA)", "license": "AGPL-3", diff --git a/product_stock_state/i18n/fr.po b/product_stock_state/i18n/fr.po index 47102868531..85009531b75 100644 --- a/product_stock_state/i18n/fr.po +++ b/product_stock_state/i18n/fr.po @@ -56,6 +56,33 @@ msgstr "" "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,field_description:product_stock_state.field_product_category__display_name +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__display_name +#: model:ir.model.fields,field_description:product_stock_state.field_product_template__display_name +#: model:ir.model.fields,field_description:product_stock_state.field_res_company__display_name +#: model:ir.model.fields,field_description:product_stock_state.field_res_config_settings__display_name +msgid "Display Name" +msgstr "" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_category__id +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__id +#: model:ir.model.fields,field_description:product_stock_state.field_product_template__id +#: model:ir.model.fields,field_description:product_stock_state.field_res_company__id +#: model:ir.model.fields,field_description:product_stock_state.field_res_config_settings__id +msgid "ID" +msgstr "" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_category____last_update +#: model:ir.model.fields,field_description:product_stock_state.field_product_product____last_update +#: model:ir.model.fields,field_description:product_stock_state.field_product_template____last_update +#: model:ir.model.fields,field_description:product_stock_state.field_res_company____last_update +#: model:ir.model.fields,field_description:product_stock_state.field_res_config_settings____last_update +msgid "Last Modified on" +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 @@ -155,6 +182,22 @@ msgstr "" msgid "Units" msgstr "Unité(s)" +#. module: product_stock_state +#: model:product.product,weight_uom_name:product_stock_state.product_setting_by_company +#: model:product.product,weight_uom_name:product_stock_state.product_setting_by_product +#: model:product.template,weight_uom_name:product_stock_state.product_setting_by_company_product_template +#: model:product.template,weight_uom_name:product_stock_state.product_setting_by_product_product_template +msgid "kg" +msgstr "kg" + +#. module: product_stock_state +#: model:product.product,volume_uom_name:product_stock_state.product_setting_by_company +#: model:product.product,volume_uom_name:product_stock_state.product_setting_by_product +#: model:product.template,volume_uom_name:product_stock_state.product_setting_by_company_product_template +#: model:product.template,volume_uom_name:product_stock_state.product_setting_by_product_product_template +msgid "m³" +msgstr "" + #~ msgid "In Limited Stock" #~ msgstr "En stock limité" @@ -166,6 +209,3 @@ msgstr "Unité(s)" #~ msgid "Resupplying" #~ msgstr "En réapprovisionnement" - -#~ msgid "kg" -#~ msgstr "kg" diff --git a/product_stock_state/i18n/product_stock_state.pot b/product_stock_state/i18n/product_stock_state.pot index 603b1694b4a..be9798c1f2d 100644 --- a/product_stock_state/i18n/product_stock_state.pot +++ b/product_stock_state/i18n/product_stock_state.pot @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 13.0\n" +"Project-Id-Version: Odoo Server 14.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: \n" "Language-Team: \n" @@ -45,6 +45,33 @@ msgid "" "use the value defined for the company" msgstr "" +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_category__display_name +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__display_name +#: model:ir.model.fields,field_description:product_stock_state.field_product_template__display_name +#: model:ir.model.fields,field_description:product_stock_state.field_res_company__display_name +#: model:ir.model.fields,field_description:product_stock_state.field_res_config_settings__display_name +msgid "Display Name" +msgstr "" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_category__id +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__id +#: model:ir.model.fields,field_description:product_stock_state.field_product_template__id +#: model:ir.model.fields,field_description:product_stock_state.field_res_company__id +#: model:ir.model.fields,field_description:product_stock_state.field_res_config_settings__id +msgid "ID" +msgstr "" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_category____last_update +#: model:ir.model.fields,field_description:product_stock_state.field_product_product____last_update +#: model:ir.model.fields,field_description:product_stock_state.field_product_template____last_update +#: model:ir.model.fields,field_description:product_stock_state.field_res_company____last_update +#: model:ir.model.fields,field_description:product_stock_state.field_res_config_settings____last_update +msgid "Last Modified on" +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 @@ -138,3 +165,19 @@ msgstr "" #: model:product.template,uom_name:product_stock_state.product_setting_by_product_product_template msgid "Units" msgstr "" + +#. module: product_stock_state +#: model:product.product,weight_uom_name:product_stock_state.product_setting_by_company +#: model:product.product,weight_uom_name:product_stock_state.product_setting_by_product +#: model:product.template,weight_uom_name:product_stock_state.product_setting_by_company_product_template +#: model:product.template,weight_uom_name:product_stock_state.product_setting_by_product_product_template +msgid "kg" +msgstr "" + +#. module: product_stock_state +#: model:product.product,volume_uom_name:product_stock_state.product_setting_by_company +#: model:product.product,volume_uom_name:product_stock_state.product_setting_by_product +#: model:product.template,volume_uom_name:product_stock_state.product_setting_by_company_product_template +#: model:product.template,volume_uom_name:product_stock_state.product_setting_by_product_product_template +msgid "m³" +msgstr "" diff --git a/product_stock_state/models/product_product.py b/product_stock_state/models/product_product.py index 664627a02bd..39960a0428e 100644 --- a/product_stock_state/models/product_product.py +++ b/product_stock_state/models/product_product.py @@ -67,9 +67,9 @@ def _available_states(self): "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() - precision = self.env["decimal.precision"].precision_get("Stock Threshold") stock_state = False for state in self._available_states(): checker = getattr(product, "_stock_state_check_" + state) diff --git a/product_stock_state/readme/CONTRIBUTORS.rst b/product_stock_state/readme/CONTRIBUTORS.rst index 3c8e7ba2217..f17c052e446 100644 --- a/product_stock_state/readme/CONTRIBUTORS.rst +++ b/product_stock_state/readme/CONTRIBUTORS.rst @@ -2,3 +2,4 @@ * Sylvain LE GAL * Kevin Khao * Simone Orsi +* Watthanun Khorchai diff --git a/product_stock_state/static/description/index.html b/product_stock_state/static/description/index.html index 4c8da2ad8d3..6bf7c45b7c0 100644 --- a/product_stock_state/static/description/index.html +++ b/product_stock_state/static/description/index.html @@ -367,7 +367,7 @@

Product Stock State

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

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

+

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

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 :

@@ -453,7 +454,7 @@

Maintainers

promote its widespread use.

Current maintainers:

sebastienbeau legalsylvain kevinkhao

-

This module is part of the OCA/product-attribute project on GitHub.

+

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.

From 5673930ec7bb48a9c8cf1217ca5fa2e2c566f652 Mon Sep 17 00:00:00 2001 From: Bosd Date: Fri, 23 Apr 2021 18:09:16 +0000 Subject: [PATCH 24/37] Added translation using Weblate (Dutch) --- product_stock_state/i18n/nl.po | 196 +++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 product_stock_state/i18n/nl.po diff --git a/product_stock_state/i18n/nl.po b/product_stock_state/i18n/nl.po new file mode 100644 index 00000000000..c1f3be9aafc --- /dev/null +++ b/product_stock_state/i18n/nl.po @@ -0,0 +1,196 @@ +# 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_terms:ir.ui.view,arch_db:product_stock_state.res_config_settings_view_form_threshold +msgid "Product stock thresholds" +msgstr "Product voorraad drempels" + +#. 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,field_description:product_stock_state.field_product_category__display_name +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__display_name +#: model:ir.model.fields,field_description:product_stock_state.field_product_template__display_name +#: model:ir.model.fields,field_description:product_stock_state.field_res_company__display_name +#: model:ir.model.fields,field_description:product_stock_state.field_res_config_settings__display_name +msgid "Display Name" +msgstr "Weergavenaam" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_category__id +#: model:ir.model.fields,field_description:product_stock_state.field_product_product__id +#: model:ir.model.fields,field_description:product_stock_state.field_product_template__id +#: model:ir.model.fields,field_description:product_stock_state.field_res_company__id +#: model:ir.model.fields,field_description:product_stock_state.field_res_config_settings__id +msgid "ID" +msgstr "ID" + +#. module: product_stock_state +#: model:ir.model.fields,field_description:product_stock_state.field_product_category____last_update +#: model:ir.model.fields,field_description:product_stock_state.field_product_product____last_update +#: model:ir.model.fields,field_description:product_stock_state.field_product_template____last_update +#: model:ir.model.fields,field_description:product_stock_state.field_res_company____last_update +#: model:ir.model.fields,field_description:product_stock_state.field_res_config_settings____last_update +msgid "Last Modified on" +msgstr "Laatst bijgewerkt op" + +#. 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,name:product_stock_state.model_product_product +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_template +msgid "Product Template" +msgstr "Productsjabloon" + +#. module: product_stock_state +#: model:product.product,name:product_stock_state.product_setting_by_company +#: 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.product,name:product_stock_state.product_setting_by_product +#: 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_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 +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_terms:ir.ui.view,arch_db:product_stock_state.res_config_settings_view_form_threshold +msgid "Stock threshold" +msgstr "voorraad drempelwaarde" + +#. 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." + +#. module: product_stock_state +#: model:product.product,uom_name:product_stock_state.product_setting_by_company +#: model:product.product,uom_name:product_stock_state.product_setting_by_product +#: model:product.template,uom_name:product_stock_state.product_setting_by_company_product_template +#: model:product.template,uom_name:product_stock_state.product_setting_by_product_product_template +msgid "Units" +msgstr "Eenheden" + +#. module: product_stock_state +#: model:product.product,weight_uom_name:product_stock_state.product_setting_by_company +#: model:product.product,weight_uom_name:product_stock_state.product_setting_by_product +#: model:product.template,weight_uom_name:product_stock_state.product_setting_by_company_product_template +#: model:product.template,weight_uom_name:product_stock_state.product_setting_by_product_product_template +msgid "kg" +msgstr "kg" + +#. module: product_stock_state +#: model:product.product,volume_uom_name:product_stock_state.product_setting_by_company +#: model:product.product,volume_uom_name:product_stock_state.product_setting_by_product +#: model:product.template,volume_uom_name:product_stock_state.product_setting_by_company_product_template +#: model:product.template,volume_uom_name:product_stock_state.product_setting_by_product_product_template +msgid "m³" +msgstr "m³" From c1ad0d58f4b83dc74ad3b3acc34e5ed720141455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Vi=20Or?= Date: Thu, 1 Dec 2022 15:23:07 +0100 Subject: [PATCH 25/37] [MIG] product_stock_state: Migration to 15.0 --- product_stock_state/__manifest__.py | 2 +- product_stock_state/data/data.xml | 12 ++-- .../models/product_category.py | 1 + .../tests/test_product_stock_state.py | 4 +- .../views/product_template_view.xml | 4 +- .../views/res_company_view.xml | 30 +++++----- .../views/res_config_settings_view.xml | 60 +++++++++---------- 7 files changed, 54 insertions(+), 59 deletions(-) diff --git a/product_stock_state/__manifest__.py b/product_stock_state/__manifest__.py index 44b33e5ea15..04c31bbcaab 100644 --- a/product_stock_state/__manifest__.py +++ b/product_stock_state/__manifest__.py @@ -9,7 +9,7 @@ "name": "Product Stock State", "summary": "Compute the state of a product's stock" "the stock level and sale_ok field", - "version": "14.0.1.0.0", + "version": "15.0.1.0.0", "website": "https://github.com/OCA/product-attribute", "author": " Akretion, GRAP, Odoo Community Association (OCA)", "license": "AGPL-3", diff --git a/product_stock_state/data/data.xml b/product_stock_state/data/data.xml index f0261f5b94c..b7e7a2c727a 100644 --- a/product_stock_state/data/data.xml +++ b/product_stock_state/data/data.xml @@ -1,14 +1,12 @@ - - + + forcecreate="True" + id="decimal_stock_threshold" + model="decimal.precision" + > Stock Threshold 2 - diff --git a/product_stock_state/models/product_category.py b/product_stock_state/models/product_category.py index c2078ac8088..ce665989363 100644 --- a/product_stock_state/models/product_category.py +++ b/product_stock_state/models/product_category.py @@ -20,6 +20,7 @@ class ProductCategory(models.Model): " 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") diff --git a/product_stock_state/tests/test_product_stock_state.py b/product_stock_state/tests/test_product_stock_state.py index 4fed84ba0a8..1a77ffffbb2 100644 --- a/product_stock_state/tests/test_product_stock_state.py +++ b/product_stock_state/tests/test_product_stock_state.py @@ -4,10 +4,10 @@ # @author Simone Orsi # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo.tests.common import SavepointCase +from odoo.tests.common import TransactionCase -class TestProductStockState(SavepointCase): +class TestProductStockState(TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() diff --git a/product_stock_state/views/product_template_view.xml b/product_stock_state/views/product_template_view.xml index 2032b8eb8a4..1c017389e7c 100644 --- a/product_stock_state/views/product_template_view.xml +++ b/product_stock_state/views/product_template_view.xml @@ -4,10 +4,10 @@ product.template - + - +
diff --git a/product_stock_state/views/res_company_view.xml b/product_stock_state/views/res_company_view.xml index 25ac176d4fe..1d3529ded47 100644 --- a/product_stock_state/views/res_company_view.xml +++ b/product_stock_state/views/res_company_view.xml @@ -1,19 +1,17 @@ - - - res.company.threshold - res.company - - - - - - - - - - - - + + 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 index b84a20005c2..2e1b6b4ba22 100644 --- a/product_stock_state/views/res_config_settings_view.xml +++ b/product_stock_state/views/res_config_settings_view.xml @@ -1,38 +1,36 @@ - - - res.config.settings.threshold - res.config.settings - - - - -
-
+ + res.config.settings.threshold + res.config.settings + + + + +
+
+
+
+ Product stock thresholds +
+ Set stock state threshold
-
- Product stock thresholds -
- Set stock state threshold -
-
-
-
+
+
+
- - - - +
+ + + From 907bd13a85c1c78788466d29367c5329046808ff Mon Sep 17 00:00:00 2001 From: sbejaoui Date: Wed, 17 May 2023 18:57:11 +0200 Subject: [PATCH 26/37] [MIG] - product_stock_state --- product_stock_state/README.rst | 23 ++++--- product_stock_state/__manifest__.py | 3 +- .../i18n/product_stock_state.pot | 69 +++++-------------- product_stock_state/models/product_product.py | 20 +++--- .../static/description/index.html | 52 +++++++------- 5 files changed, 67 insertions(+), 100 deletions(-) diff --git a/product_stock_state/README.rst b/product_stock_state/README.rst index 3e0755d3d33..216df7eba7c 100644 --- a/product_stock_state/README.rst +++ b/product_stock_state/README.rst @@ -2,10 +2,13 @@ Product Stock State =================== -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:9647fddf8c117bc8bb920d939166db6fb0eae338663e350552e6e86166581f7a + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status @@ -14,16 +17,16 @@ Product Stock State :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/14.0/product_stock_state + :target: https://github.com/OCA/product-attribute/tree/16.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-14-0/product-attribute-14-0-product_stock_state + :target: https://translation.odoo-community.org/projects/product-attribute-16-0/product-attribute-16-0-product_stock_state :alt: Translate me on Weblate -.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/135/14.0 - :alt: Try me on Runbot +.. |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=16.0 + :alt: Try me on Runboat -|badge1| |badge2| |badge3| |badge4| |badge5| +|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. @@ -67,8 +70,8 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +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. @@ -126,6 +129,6 @@ Current `maintainers `__: |maintainer-sebastienbeau| |maintainer-legalsylvain| |maintainer-kevinkhao| -This module is part of the `OCA/product-attribute `_ project on GitHub. +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/__manifest__.py b/product_stock_state/__manifest__.py index 04c31bbcaab..126bbb22876 100644 --- a/product_stock_state/__manifest__.py +++ b/product_stock_state/__manifest__.py @@ -9,7 +9,7 @@ "name": "Product Stock State", "summary": "Compute the state of a product's stock" "the stock level and sale_ok field", - "version": "15.0.1.0.0", + "version": "16.0.1.0.0", "website": "https://github.com/OCA/product-attribute", "author": " Akretion, GRAP, Odoo Community Association (OCA)", "license": "AGPL-3", @@ -27,7 +27,6 @@ ], "demo": [ "demo/res_groups.xml", - "demo/product_category.xml", "demo/product_product.xml", "demo/product_category.xml", ], diff --git a/product_stock_state/i18n/product_stock_state.pot b/product_stock_state/i18n/product_stock_state.pot index be9798c1f2d..bc3e8029201 100644 --- a/product_stock_state/i18n/product_stock_state.pot +++ b/product_stock_state/i18n/product_stock_state.pot @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 14.0\n" +"Project-Id-Version: Odoo Server 16.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: \n" "Language-Team: \n" @@ -46,30 +46,13 @@ msgid "" msgstr "" #. module: product_stock_state -#: model:ir.model.fields,field_description:product_stock_state.field_product_category__display_name -#: model:ir.model.fields,field_description:product_stock_state.field_product_product__display_name -#: model:ir.model.fields,field_description:product_stock_state.field_product_template__display_name -#: model:ir.model.fields,field_description:product_stock_state.field_res_company__display_name -#: model:ir.model.fields,field_description:product_stock_state.field_res_config_settings__display_name -msgid "Display Name" +#: 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,field_description:product_stock_state.field_product_category__id -#: model:ir.model.fields,field_description:product_stock_state.field_product_product__id -#: model:ir.model.fields,field_description:product_stock_state.field_product_template__id -#: model:ir.model.fields,field_description:product_stock_state.field_res_company__id -#: model:ir.model.fields,field_description:product_stock_state.field_res_config_settings__id -msgid "ID" -msgstr "" - -#. module: product_stock_state -#: model:ir.model.fields,field_description:product_stock_state.field_product_category____last_update -#: model:ir.model.fields,field_description:product_stock_state.field_product_product____last_update -#: model:ir.model.fields,field_description:product_stock_state.field_product_template____last_update -#: model:ir.model.fields,field_description:product_stock_state.field_res_company____last_update -#: model:ir.model.fields,field_description:product_stock_state.field_res_config_settings____last_update -msgid "Last Modified on" +#: model:ir.model.fields.selection,name:product_stock_state.selection__product_product__stock_state__in_stock +msgid "In Stock" msgstr "" #. module: product_stock_state @@ -80,7 +63,12 @@ msgid "Manual Stock State Threshold" msgstr "" #. module: product_stock_state -#: model:ir.model,name:product_stock_state.model_product_product +#: 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 "" @@ -90,22 +78,25 @@ msgid "Product Category" msgstr "" #. module: product_stock_state -#: model:ir.model,name:product_stock_state.model_product_template -msgid "Product Template" +#: model:ir.model,name:product_stock_state.model_product_product +msgid "Product Variant" msgstr "" #. module: product_stock_state -#: model:product.product,name:product_stock_state.product_setting_by_company #: 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.product,name:product_stock_state.product_setting_by_product #: 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" @@ -157,27 +148,3 @@ msgid "" "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 "" - -#. module: product_stock_state -#: model:product.product,uom_name:product_stock_state.product_setting_by_company -#: model:product.product,uom_name:product_stock_state.product_setting_by_product -#: model:product.template,uom_name:product_stock_state.product_setting_by_company_product_template -#: model:product.template,uom_name:product_stock_state.product_setting_by_product_product_template -msgid "Units" -msgstr "" - -#. module: product_stock_state -#: model:product.product,weight_uom_name:product_stock_state.product_setting_by_company -#: model:product.product,weight_uom_name:product_stock_state.product_setting_by_product -#: model:product.template,weight_uom_name:product_stock_state.product_setting_by_company_product_template -#: model:product.template,weight_uom_name:product_stock_state.product_setting_by_product_product_template -msgid "kg" -msgstr "" - -#. module: product_stock_state -#: model:product.product,volume_uom_name:product_stock_state.product_setting_by_company -#: model:product.product,volume_uom_name:product_stock_state.product_setting_by_product -#: model:product.template,volume_uom_name:product_stock_state.product_setting_by_company_product_template -#: model:product.template,volume_uom_name:product_stock_state.product_setting_by_product_product_template -msgid "m³" -msgstr "" diff --git a/product_stock_state/models/product_product.py b/product_stock_state/models/product_product.py index 39960a0428e..d879a4ae8d0 100644 --- a/product_stock_state/models/product_product.py +++ b/product_stock_state/models/product_product.py @@ -15,20 +15,16 @@ class ProductProduct(models.Model): _inherit = "product.product" - _STOCK_STATE_SELECTION = [ - ("in_stock", "In Stock"), - ("in_limited_stock", "In Limited Stock"), - ("resupplying", "Resupplying"), - ("out_of_stock", "Out Of Stock"), - ] - stock_state = fields.Selection( - selection="_selection_stock_state", compute="_compute_stock_state" + selection=[ + ("in_stock", "In Stock"), + ("in_limited_stock", "In Limited Stock"), + ("resupplying", "Resupplying"), + ("out_of_stock", "Out Of Stock"), + ], + compute="_compute_stock_state", ) - def _selection_stock_state(self): - return self._STOCK_STATE_SELECTION - def _get_qty_available_for_stock_state(self): """ This method can be overridden to provide the available qty. @@ -58,7 +54,7 @@ def _stock_state_check_out_of_stock(self, qty, precision): return True def _available_states(self): - return [x[0] for x in self._selection_stock_state()] + return [x[0] for x in self._fields["stock_state"].selection] @api.depends( "qty_available", diff --git a/product_stock_state/static/description/index.html b/product_stock_state/static/description/index.html index 6bf7c45b7c0..138be0c7f6d 100644 --- a/product_stock_state/static/description/index.html +++ b/product_stock_state/static/description/index.html @@ -1,20 +1,20 @@ - + - + Product Stock State -
-

Product Stock State

+
+ + +Odoo Community Association + +
+

Product Stock State

-

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

+

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 :

@@ -395,7 +400,7 @@

Product Stock State

-

Configuration

+

Configuration

You can configure thresholds :

  • Globally, for a company, in the Sale Settings. It will be used for all @@ -408,29 +413,29 @@

    Configuration

-

Usage

+

Usage

Open product tree view and observe the stock state

image

-

Bug Tracker

+

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.

+feedback.

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

-

Credits

+

Credits

-

Authors

+

Authors

  • Akretion
  • GRAP
-

Other credits

+

Other credits

The development of this module has been financially supported by:

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -466,10 +471,11 @@

Maintainers

promote its widespread use.

Current maintainers:

sebastienbeau legalsylvain kevinkhao

-

This module is part of the OCA/product-attribute project on GitHub.

+

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/tests/test_product_stock_state.py b/product_stock_state/tests/test_product_stock_state.py index 1a77ffffbb2..963ba65da7b 100644 --- a/product_stock_state/tests/test_product_stock_state.py +++ b/product_stock_state/tests/test_product_stock_state.py @@ -12,14 +12,42 @@ class TestProductStockState(TransactionCase): def setUpClass(cls): super().setUpClass() cls.company = cls.env.ref("base.main_company") - cls.category_furniture = cls.env.ref("product.product_category_5") - cls.category_saleable = cls.env.ref("product.product_category_1") - cls.product_chair = cls.env.ref("product.product_product_12") - cls.product_threshold_on_company = cls.env.ref( - "product_stock_state.product_setting_by_company" - ) - cls.product_threshold_on_product = cls.env.ref( - "product_stock_state.product_setting_by_product" + # 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):