diff --git a/stock_no_negative/README.rst b/stock_no_negative/README.rst new file mode 100644 index 000000000000..84800d984397 --- /dev/null +++ b/stock_no_negative/README.rst @@ -0,0 +1,134 @@ +======================= +Stock Disallow Negative +======================= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! 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%2Fstock--logistics--workflow-lightgray.png?logo=github + :target: https://github.com/OCA/stock-logistics-workflow/tree/13.0/stock_no_negative + :alt: OCA/stock-logistics-workflow +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/stock-logistics-workflow-13-0/stock-logistics-workflow-13-0-stock_no_negative + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/154/13.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +By default, Odoo allows negative stock. The advantage of negative stock +is that, if some stock levels are wrong in the ERP, you will not be blocked +when validating the picking for a customer... so you will still be able to +ship the products on time (it's an example !). The problem is that, after you +forced the stock level to negative, you are supposed to fix the stock level +later via an inventory ; but this action is often forgotten by users, +so you end up with negative stock levels in your ERP and it can stay like +this forever (or at least until the next full inventory). + +If you disallow negative stock in Odoo with this module, you will be blocked +when trying to validate a stock operation that will set the stock level of +a product and/or location as negative. So you will have to fix the +wrong stock level of that product without delay, in order to validate the +stock operation in Odoo...you can't forget it anymore ! + + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +By default, the stockable products will not be allowed to have a negative +stock. If you want to make some exceptions for some products, product +categories or locations, you can activate the option *Allow Negative Stock*: + +For products: + +#. Go to *Inventory / Master Data / Products* and in the + tab *General Information* activate this option. + +For product categories: + +#. Go to *Inventory / Configuration / Products / Product Categories* + and activate this option. + +For individual locations: + +#. Go to *Inventory / Configuration / Settings* and activate + the option *Storage Locations*. +#. Go to *Inventory / Configuration / Warehouse Management / Locations* and + activate the option the option *Allow Negative Stock* for the locations you + choose. + + +Usage +===== + +When you validate a stock operation (a stock move, a picking, +a manufacturing order, etc.) that will set the stock level of a +stockable product as negative, you will be blocked by an error message. +The consumable products can still have a negative stock level. + +Changelog +========= + +11.0.1.1.0 (2018-12-13) +~~~~~~~~~~~~~~~~~~~~~~~ + +* Add the ability to allow negative stock for individual stock locations. + +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 + +Contributors +~~~~~~~~~~~~ + +* Alexis de Lattre +* Eficent Business and IT Consulting Services S.L. + * Jordi Ballester +* Serpent Consulting Services Pvt. Ltd. +* Tecnativa + * Pedro M. Baeza + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/stock-logistics-workflow `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/stock_no_negative/__init__.py b/stock_no_negative/__init__.py new file mode 100644 index 000000000000..31c468823740 --- /dev/null +++ b/stock_no_negative/__init__.py @@ -0,0 +1,5 @@ +# ?? 2015-2016 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/stock_no_negative/__manifest__.py b/stock_no_negative/__manifest__.py new file mode 100644 index 000000000000..ee9c0c16cffb --- /dev/null +++ b/stock_no_negative/__manifest__.py @@ -0,0 +1,17 @@ +# ?? 2015-2016 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +{ + "name": "Stock Disallow Negative", + "version": "13.0.1.0.0", + "category": "Inventory, Logistic, Storage", + "license": "AGPL-3", + "summary": "Disallow negative stock levels by default", + "author": "Akretion,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/stock-logistics-workflow", + "depends": ["stock"], + "data": ["views/product_product_views.xml", "views/stock_location_views.xml"], + "installable": True, +} diff --git a/stock_no_negative/i18n/ar.po b/stock_no_negative/i18n/ar.po new file mode 100644 index 000000000000..0e2c01e6ee15 --- /dev/null +++ b/stock_no_negative/i18n/ar.po @@ -0,0 +1,97 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_no_negative +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-02-26 09:53+0000\n" +"PO-Revision-Date: 2019-02-26 09:53+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: stock_no_negative +#: code:addons/stock_no_negative/models/stock_quant.py:39 +#, python-format +msgid " lot '%s'" +msgstr " lote '%s'" + +#. module: stock_no_negative +#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__allow_negative_stock +#: model:ir.model.fields,field_description:stock_no_negative.field_product_product__allow_negative_stock +#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__allow_negative_stock +#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__allow_negative_stock +msgid "Allow Negative Stock" +msgstr "Permitir Stock Negativo" + +#. module: stock_no_negative +#: model:ir.model.fields,help:stock_no_negative.field_product_category__allow_negative_stock +msgid "" +"Allow negative stock levels for the stockable products attached to this " +"category. The options doesn't apply to products attached to sub-categories " +"of this category." +msgstr "" +"Permitir niveles de stock negativos para los productos en stock adjuntos a " +"esta categoría. Las opciones no se aplican a los productos adjuntos a " +"subcategorías de esta categoría." + +#. module: stock_no_negative +#: model:ir.model.fields,help:stock_no_negative.field_stock_location__allow_negative_stock +msgid "" +"Allow negative stock levels for the stockable products attached to this " +"location." +msgstr "" +"Permitir niveles de stock negativos para los productos en stock ubicados en " +"esta ubicación." + +#. module: stock_no_negative +#: model:ir.model.fields,help:stock_no_negative.field_product_product__allow_negative_stock +#: model:ir.model.fields,help:stock_no_negative.field_product_template__allow_negative_stock +msgid "" +"If this option is not active on this product nor on its product category and " +"that this product is a stockable product, then the validation of the related " +"stock moves will be blocked if the stock level becomes negative with the " +"stock move." +msgstr "" +"Si esta opción no está activa en este producto ni en su categoría de " +"producto y este producto es un producto almacenable, entonces la validación " +"de los movimientos de stock relacionados se bloqueará si el nivel de stock " +"se vuelve negativo con el movimiento de stock." + +#. module: stock_no_negative +#: model:ir.model,name:stock_no_negative.model_stock_location +msgid "Inventory Locations" +msgstr "Ubicaciones de inventario" + +#. module: stock_no_negative +#: model:ir.model,name:stock_no_negative.model_product_category +msgid "Product Category" +msgstr "Categoría de producto" + +#. module: stock_no_negative +#: model:ir.model,name:stock_no_negative.model_product_template +msgid "Product Template" +msgstr "Plantilla de producto" + +#. module: stock_no_negative +#: model:ir.model,name:stock_no_negative.model_stock_quant +msgid "Quants" +msgstr "Quants" + +#. module: stock_no_negative +#: code:addons/stock_no_negative/models/stock_quant.py:40 +#, python-format +msgid "" +"You cannot validate this stock operation because the stock level of the " +"product '%s'%s would become negative (%s) on the stock location '%s' and " +"negative stock is not allowed for this product and/or location." +msgstr "" +"No se puede validar esta operación de stock porque el nivel de stock del " +"producto '%s'%s se volvería negativo (%s) en la ubicación de stock '%s' y no " +"se permite stock negativo para este producto y/o ubicación." diff --git a/stock_no_negative/i18n/es.po b/stock_no_negative/i18n/es.po new file mode 100644 index 000000000000..0e2c01e6ee15 --- /dev/null +++ b/stock_no_negative/i18n/es.po @@ -0,0 +1,97 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_no_negative +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-02-26 09:53+0000\n" +"PO-Revision-Date: 2019-02-26 09:53+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: stock_no_negative +#: code:addons/stock_no_negative/models/stock_quant.py:39 +#, python-format +msgid " lot '%s'" +msgstr " lote '%s'" + +#. module: stock_no_negative +#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__allow_negative_stock +#: model:ir.model.fields,field_description:stock_no_negative.field_product_product__allow_negative_stock +#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__allow_negative_stock +#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__allow_negative_stock +msgid "Allow Negative Stock" +msgstr "Permitir Stock Negativo" + +#. module: stock_no_negative +#: model:ir.model.fields,help:stock_no_negative.field_product_category__allow_negative_stock +msgid "" +"Allow negative stock levels for the stockable products attached to this " +"category. The options doesn't apply to products attached to sub-categories " +"of this category." +msgstr "" +"Permitir niveles de stock negativos para los productos en stock adjuntos a " +"esta categoría. Las opciones no se aplican a los productos adjuntos a " +"subcategorías de esta categoría." + +#. module: stock_no_negative +#: model:ir.model.fields,help:stock_no_negative.field_stock_location__allow_negative_stock +msgid "" +"Allow negative stock levels for the stockable products attached to this " +"location." +msgstr "" +"Permitir niveles de stock negativos para los productos en stock ubicados en " +"esta ubicación." + +#. module: stock_no_negative +#: model:ir.model.fields,help:stock_no_negative.field_product_product__allow_negative_stock +#: model:ir.model.fields,help:stock_no_negative.field_product_template__allow_negative_stock +msgid "" +"If this option is not active on this product nor on its product category and " +"that this product is a stockable product, then the validation of the related " +"stock moves will be blocked if the stock level becomes negative with the " +"stock move." +msgstr "" +"Si esta opción no está activa en este producto ni en su categoría de " +"producto y este producto es un producto almacenable, entonces la validación " +"de los movimientos de stock relacionados se bloqueará si el nivel de stock " +"se vuelve negativo con el movimiento de stock." + +#. module: stock_no_negative +#: model:ir.model,name:stock_no_negative.model_stock_location +msgid "Inventory Locations" +msgstr "Ubicaciones de inventario" + +#. module: stock_no_negative +#: model:ir.model,name:stock_no_negative.model_product_category +msgid "Product Category" +msgstr "Categoría de producto" + +#. module: stock_no_negative +#: model:ir.model,name:stock_no_negative.model_product_template +msgid "Product Template" +msgstr "Plantilla de producto" + +#. module: stock_no_negative +#: model:ir.model,name:stock_no_negative.model_stock_quant +msgid "Quants" +msgstr "Quants" + +#. module: stock_no_negative +#: code:addons/stock_no_negative/models/stock_quant.py:40 +#, python-format +msgid "" +"You cannot validate this stock operation because the stock level of the " +"product '%s'%s would become negative (%s) on the stock location '%s' and " +"negative stock is not allowed for this product and/or location." +msgstr "" +"No se puede validar esta operación de stock porque el nivel de stock del " +"producto '%s'%s se volvería negativo (%s) en la ubicación de stock '%s' y no " +"se permite stock negativo para este producto y/o ubicación." diff --git a/stock_no_negative/i18n/fr.po b/stock_no_negative/i18n/fr.po new file mode 100644 index 000000000000..a6c13eecd64a --- /dev/null +++ b/stock_no_negative/i18n/fr.po @@ -0,0 +1,102 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_no_negative +# +# Translators: +# OCA Transbot , 2017 +# guillaume bauer , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-02-28 08:51+0000\n" +"PO-Revision-Date: 2018-02-28 08:51+0000\n" +"Last-Translator: guillaume bauer , 2017\n" +"Language-Team: French (https://www.transifex.com/oca/teams/23907/fr/)\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#. module: stock_no_negative +#: code:addons/stock_no_negative/models/stock_quant.py:39 +#, python-format +msgid " lot '%s'" +msgstr "lot%s" + +#. module: stock_no_negative +#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__allow_negative_stock +#: model:ir.model.fields,field_description:stock_no_negative.field_product_product__allow_negative_stock +#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__allow_negative_stock +#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__allow_negative_stock +msgid "Allow Negative Stock" +msgstr "Autoriser le stock n??gatif" + +#. module: stock_no_negative +#: model:ir.model.fields,help:stock_no_negative.field_product_category__allow_negative_stock +msgid "" +"Allow negative stock levels for the stockable products attached to this " +"category. The options doesn't apply to products attached to sub-categories " +"of this category." +msgstr "" +"Autorise les niveaux de stock n??gatif pour les articles stockables attach??" +"s ?? cette cat??gorie. Cette option ne s'applique pas aux articles attach??" +"s ?? des sous-cat??gories de cette cat??gorie." + +#. module: stock_no_negative +#: model:ir.model.fields,help:stock_no_negative.field_stock_location__allow_negative_stock +#, fuzzy +msgid "" +"Allow negative stock levels for the stockable products attached to this " +"location." +msgstr "" +"Autorise les niveaux de stock n??gatif pour les articles stockables attach??" +"s ?? cette cat??gorie. Cette option ne s'applique pas aux articles attach??" +"s ?? des sous-cat??gories de cette cat??gorie." + +#. module: stock_no_negative +#: model:ir.model.fields,help:stock_no_negative.field_product_product__allow_negative_stock +#: model:ir.model.fields,help:stock_no_negative.field_product_template__allow_negative_stock +msgid "" +"If this option is not active on this product nor on its product category and " +"that this product is a stockable product, then the validation of the related " +"stock moves will be blocked if the stock level becomes negative with the " +"stock move." +msgstr "" +"Si cette option n'est pas activ??e sur cet article ni sur la cat??gorie ?? " +"laquelle il est rattach?? et que cet article est un produit stockable, alors " +"la validation des mouvements de stock sera bloqu??e si le niveau de stock " +"devient n??gatif avec ce mouvement de stock." + +#. module: stock_no_negative +#: model:ir.model,name:stock_no_negative.model_stock_location +msgid "Inventory Locations" +msgstr "" + +#. module: stock_no_negative +#: model:ir.model,name:stock_no_negative.model_product_category +msgid "Product Category" +msgstr "Cat??gorie d'articles" + +#. module: stock_no_negative +#: model:ir.model,name:stock_no_negative.model_product_template +msgid "Product Template" +msgstr "Mod??le d'article" + +#. module: stock_no_negative +#: model:ir.model,name:stock_no_negative.model_stock_quant +msgid "Quants" +msgstr "Quants" + +#. module: stock_no_negative +#: code:addons/stock_no_negative/models/stock_quant.py:40 +#, fuzzy, python-format +msgid "" +"You cannot validate this stock operation because the stock level of the " +"product '%s'%s would become negative (%s) on the stock location '%s' and " +"negative stock is not allowed for this product and/or location." +msgstr "" +"Impossible de valider cette op??ration car le niveau de stock de ce produit " +"%s'%s deviendrait n??gative(%s) dans l'emplacement du stock '%s'. Un stock " +"n??gatif n'est pas permis pour ce produit." diff --git a/stock_no_negative/i18n/stock_no_negative.pot b/stock_no_negative/i18n/stock_no_negative.pot new file mode 100644 index 000000000000..f5141fa85f15 --- /dev/null +++ b/stock_no_negative/i18n/stock_no_negative.pot @@ -0,0 +1,71 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_no_negative +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.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: stock_no_negative +#: code:addons/stock_no_negative/models/stock_quant.py:39 +#, python-format +msgid " lot '%s'" +msgstr "" + +#. module: stock_no_negative +#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__allow_negative_stock +#: model:ir.model.fields,field_description:stock_no_negative.field_product_product__allow_negative_stock +#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__allow_negative_stock +#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__allow_negative_stock +msgid "Allow Negative Stock" +msgstr "" + +#. module: stock_no_negative +#: model:ir.model.fields,help:stock_no_negative.field_product_category__allow_negative_stock +msgid "Allow negative stock levels for the stockable products attached to this category. The options doesn't apply to products attached to sub-categories of this category." +msgstr "" + +#. module: stock_no_negative +#: model:ir.model.fields,help:stock_no_negative.field_stock_location__allow_negative_stock +msgid "Allow negative stock levels for the stockable products attached to this location." +msgstr "" + +#. module: stock_no_negative +#: model:ir.model.fields,help:stock_no_negative.field_product_product__allow_negative_stock +#: model:ir.model.fields,help:stock_no_negative.field_product_template__allow_negative_stock +msgid "If this option is not active on this product nor on its product category and that this product is a stockable product, then the validation of the related stock moves will be blocked if the stock level becomes negative with the stock move." +msgstr "" + +#. module: stock_no_negative +#: model:ir.model,name:stock_no_negative.model_stock_location +msgid "Inventory Locations" +msgstr "" + +#. module: stock_no_negative +#: model:ir.model,name:stock_no_negative.model_product_category +msgid "Product Category" +msgstr "" + +#. module: stock_no_negative +#: model:ir.model,name:stock_no_negative.model_product_template +msgid "Product Template" +msgstr "" + +#. module: stock_no_negative +#: model:ir.model,name:stock_no_negative.model_stock_quant +msgid "Quants" +msgstr "" + +#. module: stock_no_negative +#: code:addons/stock_no_negative/models/stock_quant.py:40 +#, python-format +msgid "You cannot validate this stock operation because the stock level of the product '%s'%s would become negative (%s) on the stock location '%s' and negative stock is not allowed for this product and/or location." +msgstr "" + diff --git a/stock_no_negative/models/__init__.py b/stock_no_negative/models/__init__.py new file mode 100644 index 000000000000..8d84cb148784 --- /dev/null +++ b/stock_no_negative/models/__init__.py @@ -0,0 +1,7 @@ +# ?? 2015-2016 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import product +from . import stock_quant +from . import stock_location diff --git a/stock_no_negative/models/product.py b/stock_no_negative/models/product.py new file mode 100644 index 000000000000..f95d7f4f1cad --- /dev/null +++ b/stock_no_negative/models/product.py @@ -0,0 +1,28 @@ +# ?? 2015-2016 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ProductCategory(models.Model): + _inherit = "product.category" + + allow_negative_stock = fields.Boolean( + string="Allow Negative Stock", + help="Allow negative stock levels for the stockable products " + "attached to this category. The options doesn't apply to products " + "attached to sub-categories of this category.", + ) + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + allow_negative_stock = fields.Boolean( + string="Allow Negative Stock", + help="If this option is not active on this product nor on its " + "product category and that this product is a stockable product, " + "then the validation of the related stock moves will be blocked if " + "the stock level becomes negative with the stock move.", + ) diff --git a/stock_no_negative/models/stock_location.py b/stock_no_negative/models/stock_location.py new file mode 100644 index 000000000000..8d7e6e5f9e33 --- /dev/null +++ b/stock_no_negative/models/stock_location.py @@ -0,0 +1,15 @@ +# ?? 2018 Eficent (https://www.eficent.com) +# @author Jordi Ballester +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class StockLocation(models.Model): + _inherit = "stock.location" + + allow_negative_stock = fields.Boolean( + string="Allow Negative Stock", + help="Allow negative stock levels for the stockable products " + "attached to this location.", + ) diff --git a/stock_no_negative/models/stock_quant.py b/stock_no_negative/models/stock_quant.py new file mode 100644 index 000000000000..bd79141a7c05 --- /dev/null +++ b/stock_no_negative/models/stock_quant.py @@ -0,0 +1,51 @@ +# ?? 2015-2017 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, api, models +from odoo.exceptions import ValidationError +from odoo.tools import config, float_compare + + +class StockQuant(models.Model): + _inherit = "stock.quant" + + @api.constrains("product_id", "quantity") + def check_negative_qty(self): + p = self.env["decimal.precision"].precision_get("Product Unit of Measure") + check_negative_qty = ( + config["test_enable"] and self.env.context.get("test_stock_no_negative") + ) or not config["test_enable"] + if not check_negative_qty: + return + + for quant in self: + disallowed_by_product = ( + not quant.product_id.allow_negative_stock + and not quant.product_id.categ_id.allow_negative_stock + ) + disallowed_by_location = not quant.location_id.allow_negative_stock + if ( + float_compare(quant.quantity, 0, precision_digits=p) == -1 + and quant.product_id.type == "product" + and quant.location_id.usage in ["internal", "transit"] + and disallowed_by_product + and disallowed_by_location + ): + msg_add = "" + if quant.lot_id: + msg_add = _(" lot '%s'") % quant.lot_id.name_get()[0][1] + raise ValidationError( + _( + "You cannot validate this stock operation because the " + "stock level of the product '%s'%s would become negative " + "(%s) on the stock location '%s' and negative stock is " + "not allowed for this product and/or location." + ) + % ( + quant.product_id.name, + msg_add, + quant.quantity, + quant.location_id.complete_name, + ) + ) diff --git a/stock_no_negative/readme/CONFIGURE.rst b/stock_no_negative/readme/CONFIGURE.rst new file mode 100644 index 000000000000..1ecf43331897 --- /dev/null +++ b/stock_no_negative/readme/CONFIGURE.rst @@ -0,0 +1,21 @@ +By default, the stockable products will not be allowed to have a negative +stock. If you want to make some exceptions for some products, product +categories or locations, you can activate the option *Allow Negative Stock*: + +For products: + +#. Go to *Inventory / Master Data / Products* and in the + tab *General Information* activate this option. + +For product categories: + +#. Go to *Inventory / Configuration / Products / Product Categories* + and activate this option. + +For individual locations: + +#. Go to *Inventory / Configuration / Settings* and activate + the option *Storage Locations*. +#. Go to *Inventory / Configuration / Warehouse Management / Locations* and + activate the option the option *Allow Negative Stock* for the locations you + choose. diff --git a/stock_no_negative/readme/CONTRIBUTORS.rst b/stock_no_negative/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000000..1ca4370a666f --- /dev/null +++ b/stock_no_negative/readme/CONTRIBUTORS.rst @@ -0,0 +1,8 @@ +* Alexis de Lattre +* Eficent Business and IT Consulting Services S.L. + * Jordi Ballester +* Serpent Consulting Services Pvt. Ltd. +* Tecnativa + * Pedro M. Baeza +* Spacefoot + * Quentin Delcourte diff --git a/stock_no_negative/readme/DESCRIPTION.rst b/stock_no_negative/readme/DESCRIPTION.rst new file mode 100644 index 000000000000..9144dac41ea3 --- /dev/null +++ b/stock_no_negative/readme/DESCRIPTION.rst @@ -0,0 +1,14 @@ +By default, Odoo allows negative stock. The advantage of negative stock +is that, if some stock levels are wrong in the ERP, you will not be blocked +when validating the picking for a customer... so you will still be able to +ship the products on time (it's an example !). The problem is that, after you +forced the stock level to negative, you are supposed to fix the stock level +later via an inventory ; but this action is often forgotten by users, +so you end up with negative stock levels in your ERP and it can stay like +this forever (or at least until the next full inventory). + +If you disallow negative stock in Odoo with this module, you will be blocked +when trying to validate a stock operation that will set the stock level of +a product and/or location as negative. So you will have to fix the +wrong stock level of that product without delay, in order to validate the +stock operation in Odoo...you can't forget it anymore ! diff --git a/stock_no_negative/readme/HISTORY.rst b/stock_no_negative/readme/HISTORY.rst new file mode 100644 index 000000000000..90dda83b70a8 --- /dev/null +++ b/stock_no_negative/readme/HISTORY.rst @@ -0,0 +1,10 @@ +13.0.1.0.0 (2020-01-03) +~~~~~~~~~~~~~~~~~~~~~~~ + +* [13.0][MIG] stock_no_negative + Remove all decorators @api.multi + +11.0.1.1.0 (2018-12-13) +~~~~~~~~~~~~~~~~~~~~~~~ + +* Add the ability to allow negative stock for individual stock locations. diff --git a/stock_no_negative/readme/USAGE.rst b/stock_no_negative/readme/USAGE.rst new file mode 100644 index 000000000000..004f5169f9c8 --- /dev/null +++ b/stock_no_negative/readme/USAGE.rst @@ -0,0 +1,4 @@ +When you validate a stock operation (a stock move, a picking, +a manufacturing order, etc.) that will set the stock level of a +stockable product as negative, you will be blocked by an error message. +The consumable products can still have a negative stock level. diff --git a/stock_no_negative/static/description/icon.png b/stock_no_negative/static/description/icon.png new file mode 100644 index 000000000000..3a0328b516c4 Binary files /dev/null and b/stock_no_negative/static/description/icon.png differ diff --git a/stock_no_negative/static/description/index.html b/stock_no_negative/static/description/index.html new file mode 100644 index 000000000000..1c970c3526bc --- /dev/null +++ b/stock_no_negative/static/description/index.html @@ -0,0 +1,482 @@ + + + + + + +Stock Disallow Negative + + + +
+

Stock Disallow Negative

+ + +

Beta License: AGPL-3 OCA/stock-logistics-workflow Translate me on Weblate Try me on Runbot

+

By default, Odoo allows negative stock. The advantage of negative stock +is that, if some stock levels are wrong in the ERP, you will not be blocked +when validating the picking for a customer… so you will still be able to +ship the products on time (it’s an example !). The problem is that, after you +forced the stock level to negative, you are supposed to fix the stock level +later via an inventory ; but this action is often forgotten by users, +so you end up with negative stock levels in your ERP and it can stay like +this forever (or at least until the next full inventory).

+

If you disallow negative stock in Odoo with this module, you will be blocked +when trying to validate a stock operation that will set the stock level of +a product and/or location as negative. So you will have to fix the +wrong stock level of that product without delay, in order to validate the +stock operation in Odoo…you can’t forget it anymore !

+

Table of contents

+ +
+

Configuration

+

By default, the stockable products will not be allowed to have a negative +stock. If you want to make some exceptions for some products, product +categories or locations, you can activate the option Allow Negative Stock:

+

For products:

+
    +
  1. Go to Inventory / Master Data / Products and in the +tab General Information activate this option.
  2. +
+

For product categories:

+
    +
  1. Go to Inventory / Configuration / Products / Product Categories +and activate this option.
  2. +
+

For individual locations:

+
    +
  1. Go to Inventory / Configuration / Settings and activate +the option Storage Locations.
  2. +
  3. Go to Inventory / Configuration / Warehouse Management / Locations and +activate the option the option Allow Negative Stock for the locations you +choose.
  4. +
+
+
+

Usage

+

When you validate a stock operation (a stock move, a picking, +a manufacturing order, etc.) that will set the stock level of a +stockable product as negative, you will be blocked by an error message. +The consumable products can still have a negative stock level.

+
+
+

Changelog

+
+

11.0.1.1.0 (2018-12-13)

+
    +
  • Add the ability to allow negative stock for individual stock locations.
  • +
+
+
+
+

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

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/stock-logistics-workflow project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/stock_no_negative/tests/__init__.py b/stock_no_negative/tests/__init__.py new file mode 100644 index 000000000000..629aec36e556 --- /dev/null +++ b/stock_no_negative/tests/__init__.py @@ -0,0 +1,8 @@ +# ?? 2015-2016 Akretion (http://www.akretion.com) +# @author Alexis de Lattre +# ?? 2016 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# ?? 2016 Serpent Consulting Services Pvt. Ltd. () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_stock_no_negative diff --git a/stock_no_negative/tests/test_stock_no_negative.py b/stock_no_negative/tests/test_stock_no_negative.py new file mode 100644 index 000000000000..9d2fb1ee9af8 --- /dev/null +++ b/stock_no_negative/tests/test_stock_no_negative.py @@ -0,0 +1,109 @@ +# Copyright 2015-2016 Akretion (http://www.akretion.com) - Alexis de Lattre +# Copyright 2016 Eficent (http://www.eficent.com) +# Copyright 2016 Serpent Consulting Services () +# Copyright 2018 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo.exceptions import ValidationError +from odoo.tests.common import TransactionCase + + +class TestStockNoNegative(TransactionCase): + at_install = False + post_install = True + + def setUp(self): + super(TestStockNoNegative, self).setUp() + self.product_model = self.env["product.product"] + self.product_ctg_model = self.env["product.category"] + self.picking_type_id = self.env.ref("stock.picking_type_out") + self.location_id = self.env.ref("stock.stock_location_stock") + self.location_dest_id = self.env.ref("stock.stock_location_customers") + # Create product category + self.product_ctg = self._create_product_category() + # Create a Product + self.product = self._create_product("test_product1") + self._create_picking() + + def _create_product_category(self): + product_ctg = self.product_ctg_model.create( + {"name": "test_product_ctg", "allow_negative_stock": False} + ) + return product_ctg + + def _create_product(self, name): + product = self.product_model.create( + { + "name": name, + "categ_id": self.product_ctg.id, + "type": "product", + "allow_negative_stock": False, + } + ) + return product + + def _create_picking(self): + self.stock_picking = ( + self.env["stock.picking"] + .with_context(test_stock_no_negative=True) + .create( + { + "picking_type_id": self.picking_type_id.id, + "move_type": "direct", + "location_id": self.location_id.id, + "location_dest_id": self.location_dest_id.id, + } + ) + ) + + self.stock_move = self.env["stock.move"].create( + { + "name": "Test Move", + "product_id": self.product.id, + "product_uom_qty": 100.0, + "product_uom": self.product.uom_id.id, + "picking_id": self.stock_picking.id, + "state": "draft", + "location_id": self.location_id.id, + "location_dest_id": self.location_dest_id.id, + "quantity_done": 100.0, + } + ) + + def test_check_constrains(self): + """Assert that constraint is raised when user + tries to validate the stock operation which would + make the stock level of the product negative """ + self.stock_picking.action_confirm() + with self.assertRaises(ValidationError): + self.stock_picking.button_validate() + + def test_true_allow_negative_stock_product(self): + """Assert that negative stock levels are allowed when + the allow_negative_stock is set active in the product""" + self.product.allow_negative_stock = True + self.stock_picking.action_confirm() + self.stock_picking.button_validate() + quant = self.env["stock.quant"].search( + [ + ("product_id", "=", self.product.id), + ("location_id", "=", self.location_id.id), + ] + ) + self.assertEqual(quant.quantity, -100) + + def test_true_allow_negative_stock_location(self): + """Assert that negative stock levels are allowed when + the allow_negative_stock is set active in the product""" + self.product.allow_negative_stock = False + self.location_id.allow_negative_stock = True + self.stock_picking.action_confirm() + self.stock_picking.button_validate() + quant = self.env["stock.quant"].search( + [ + ("product_id", "=", self.product.id), + ("location_id", "=", self.location_id.id), + ] + ) + self.assertEqual(quant.quantity, -100) diff --git a/stock_no_negative/views/product_product_views.xml b/stock_no_negative/views/product_product_views.xml new file mode 100644 index 000000000000..252fbdf98371 --- /dev/null +++ b/stock_no_negative/views/product_product_views.xml @@ -0,0 +1,33 @@ + + + + + + + stock_no_negative.product.template.form + product.template + + + + + + + + + + stock_no_negative.product.category.form + product.category + + + + + + + + + diff --git a/stock_no_negative/views/stock_location_views.xml b/stock_no_negative/views/stock_location_views.xml new file mode 100644 index 000000000000..dfb555389f08 --- /dev/null +++ b/stock_no_negative/views/stock_location_views.xml @@ -0,0 +1,22 @@ + + + + + + + stock.location.form.allow_negative_stock + stock.location + + + + + + + + +