From 97fc724959683359ff97a882543269fee2b272a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Wed, 23 Oct 2024 09:54:54 +0200 Subject: [PATCH] [IMP] mrp_subcontracting_skip_no_negative: Show negative stock error of subcontracting component TT50668 --- .../README.rst | 4 + .../i18n/es.po | 50 ++++++++++ .../mrp_subcontracting_skip_no_negative.pot | 11 +++ .../models/stock_move.py | 40 +++++++- .../readme/CONTRIBUTORS.rst | 4 + .../static/description/index.html | 15 +-- ...est_mrp_subcontracting_skip_no_negative.py | 92 +++++++++++++------ 7 files changed, 181 insertions(+), 35 deletions(-) create mode 100644 mrp_subcontracting_skip_no_negative/i18n/es.po diff --git a/mrp_subcontracting_skip_no_negative/README.rst b/mrp_subcontracting_skip_no_negative/README.rst index 3c3f51e86..75fd9ac17 100644 --- a/mrp_subcontracting_skip_no_negative/README.rst +++ b/mrp_subcontracting_skip_no_negative/README.rst @@ -76,6 +76,10 @@ Contributors * Aung Ko Ko Lin +* `Tecnativa `_: + + * Víctor Martínez + Maintainers ~~~~~~~~~~~ diff --git a/mrp_subcontracting_skip_no_negative/i18n/es.po b/mrp_subcontracting_skip_no_negative/i18n/es.po new file mode 100644 index 000000000..ec0d9adfd --- /dev/null +++ b/mrp_subcontracting_skip_no_negative/i18n/es.po @@ -0,0 +1,50 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mrp_subcontracting_skip_no_negative +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-11-04 08:50+0000\n" +"PO-Revision-Date: 2024-11-04 09:51+0100\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" +"X-Generator: Poedit 3.0.1\n" + +#. module: mrp_subcontracting_skip_no_negative +#: model:ir.model.fields,field_description:mrp_subcontracting_skip_no_negative.field_stock_move__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: mrp_subcontracting_skip_no_negative +#: model:ir.model.fields,field_description:mrp_subcontracting_skip_no_negative.field_stock_move__id +msgid "ID" +msgstr "ID" + +#. module: mrp_subcontracting_skip_no_negative +#: model:ir.model.fields,field_description:mrp_subcontracting_skip_no_negative.field_stock_move____last_update +msgid "Last Modified on" +msgstr "Última modificación el" + +#. module: mrp_subcontracting_skip_no_negative +#: model:ir.model,name:mrp_subcontracting_skip_no_negative.model_stock_move +msgid "Stock Move" +msgstr "Movimiento de existencias" + +#. module: mrp_subcontracting_skip_no_negative +#: code:addons/mrp_subcontracting_skip_no_negative/models/stock_move.py:0 +#, python-format +msgid "" +"You cannot validate this stock operation because the stock level of the " +"component product '%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 componente '%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/mrp_subcontracting_skip_no_negative/i18n/mrp_subcontracting_skip_no_negative.pot b/mrp_subcontracting_skip_no_negative/i18n/mrp_subcontracting_skip_no_negative.pot index a169c66ce..0aedc2a10 100644 --- a/mrp_subcontracting_skip_no_negative/i18n/mrp_subcontracting_skip_no_negative.pot +++ b/mrp_subcontracting_skip_no_negative/i18n/mrp_subcontracting_skip_no_negative.pot @@ -6,6 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 14.0\n" "Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-11-04 08:50+0000\n" +"PO-Revision-Date: 2024-11-04 08:50+0000\n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -32,3 +34,12 @@ msgstr "" #: model:ir.model,name:mrp_subcontracting_skip_no_negative.model_stock_move msgid "Stock Move" msgstr "" + +#. module: mrp_subcontracting_skip_no_negative +#: code:addons/mrp_subcontracting_skip_no_negative/models/stock_move.py:0 +#, python-format +msgid "" +"You cannot validate this stock operation because the stock level of the " +"component product '%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/mrp_subcontracting_skip_no_negative/models/stock_move.py b/mrp_subcontracting_skip_no_negative/models/stock_move.py index 60057dfdd..b54972d02 100644 --- a/mrp_subcontracting_skip_no_negative/models/stock_move.py +++ b/mrp_subcontracting_skip_no_negative/models/stock_move.py @@ -1,7 +1,10 @@ # Copyright 2023 Quartile Limited +# Copyright 2024 Tecnativa - Víctor Martínez # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo import models +from odoo import _, models +from odoo.exceptions import ValidationError +from odoo.tools import config, float_compare class StockMove(models.Model): @@ -35,6 +38,41 @@ class StockMove(models.Model): ): continue moves_with_no_check -= move + # If you have not been able to allocate previously it is because there is + # no stock, therefore it will leave the stock negative, we deduct the + # quantity checking the components and show the corresponding error. + test_condition = ( + config["test_enable"] and self.env.context.get("test_stock_no_negative") + ) or not config["test_enable"] + if not test_condition: + continue + qty_precision = self.env["decimal.precision"].precision_get( + "Product Unit of Measure" + ) + for p_move in unassigned_productions.move_raw_ids.filtered( + lambda x: x.state != "assigned" + and not x.product_id.allow_negative_stock + and not x.product_id.categ_id.allow_negative_stock + and not x.location_id.allow_negative_stock + ): + product = p_move.product_id.sudo() + location = p_move.location_id + location_qty = product.with_context(location=location.id).free_qty + new_qty = location_qty - p_move.product_uom_qty + if float_compare(new_qty, 0, precision_digits=qty_precision) == -1: + raise ValidationError( + _( + "You cannot validate this stock operation because the " + "stock level of the component product '%s' would become " + "negative (%s) on the stock location '%s' and negative " + "stock is not allowed for this product and/or location." + ) + % ( + product.display_name, + new_qty, + location.complete_name, + ) + ) res = super(StockMove, self - moves_with_no_check)._action_done( cancel_backorder=cancel_backorder ) diff --git a/mrp_subcontracting_skip_no_negative/readme/CONTRIBUTORS.rst b/mrp_subcontracting_skip_no_negative/readme/CONTRIBUTORS.rst index cd4e44ca9..4a81d357e 100644 --- a/mrp_subcontracting_skip_no_negative/readme/CONTRIBUTORS.rst +++ b/mrp_subcontracting_skip_no_negative/readme/CONTRIBUTORS.rst @@ -1,3 +1,7 @@ * `Quartile `__: * Aung Ko Ko Lin + +* `Tecnativa `_: + + * Víctor Martínez diff --git a/mrp_subcontracting_skip_no_negative/static/description/index.html b/mrp_subcontracting_skip_no_negative/static/description/index.html index 11fdba25b..37cb914be 100644 --- a/mrp_subcontracting_skip_no_negative/static/description/index.html +++ b/mrp_subcontracting_skip_no_negative/static/description/index.html @@ -8,11 +8,10 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $ +:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ :Copyright: This stylesheet has been placed in the public domain. Default cascading style sheet for the HTML output of Docutils. -Despite the name, some widely supported CSS2 features are used. See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to customize this style sheet. @@ -275,7 +274,7 @@ pre.literal-block, pre.doctest-block, pre.math, pre.code { margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: gray; } /* line numbers */ +pre.code .ln { color: grey; } /* line numbers */ pre.code, code { background-color: #eeeeee } pre.code .comment, code .comment { color: #5C6576 } pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } @@ -301,7 +300,7 @@ span.option { span.pre { white-space: pre } -span.problematic, pre.problematic { +span.problematic { color: red } span.section-subtitle { @@ -414,14 +413,16 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
  • Aung Ko Ko Lin
  • +
  • Tecnativa:
      +
    • Víctor Martínez
    • +
    +
  • Maintainers

    This module is maintained by the OCA.

    - -Odoo Community Association - +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.

    diff --git a/mrp_subcontracting_skip_no_negative/tests/test_mrp_subcontracting_skip_no_negative.py b/mrp_subcontracting_skip_no_negative/tests/test_mrp_subcontracting_skip_no_negative.py index 4c298db12..c6698572c 100644 --- a/mrp_subcontracting_skip_no_negative/tests/test_mrp_subcontracting_skip_no_negative.py +++ b/mrp_subcontracting_skip_no_negative/tests/test_mrp_subcontracting_skip_no_negative.py @@ -1,8 +1,10 @@ # Copyright 2023 Quartile Limited +# Copyright 2024 Tecnativa - Víctor Martínez # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from odoo.exceptions import ValidationError from odoo.tests import Form +from odoo.tools import mute_logger from odoo.addons.mrp_subcontracting.tests.common import TestMrpSubcontractingCommon @@ -17,40 +19,76 @@ class TestMrpSubcontractingSkipNoNegative(TestMrpSubcontractingCommon): test_stock_no_negative=True, ) ) - - def test_mrp_subcontracting_skip_no_negative(self): - picking_form = Form(self.env["stock.picking"]) - picking_form.picking_type_id = self.env.ref("stock.picking_type_in") - picking_form.partner_id = self.subcontractor_partner1 + picking_form = Form(cls.env["stock.picking"]) + picking_form.picking_type_id = cls.env.ref("stock.picking_type_in") + picking_form.partner_id = cls.subcontractor_partner1 with picking_form.move_ids_without_package.new() as move: - move.product_id = self.finished + move.product_id = cls.finished move.product_uom_qty = 1 - subcontracting_receipt = picking_form.save() - subcontracting_receipt.action_confirm() - self.assertEqual(subcontracting_receipt.state, "assigned") - immediate_wizard = subcontracting_receipt.sudo().button_validate() + cls.subcontracting_receipt = picking_form.save() + + def _create_stock_quant(self, product, qty): + self.env["stock.quant"].create( + { + "product_id": product.id, + "location_id": self.subcontractor_partner1.property_stock_subcontractor.id, + "quantity": qty, + } + ) + + @mute_logger("odoo.models.unlink") + def test_mrp_subcontracting_skip_no_negative_01(self): + self.subcontracting_receipt.action_confirm() + self.assertEqual(self.subcontracting_receipt.state, "assigned") + immediate_wizard = self.subcontracting_receipt.sudo().button_validate() self.assertEqual(immediate_wizard.get("res_model"), "stock.immediate.transfer") immediate_wizard_form = Form( self.env[immediate_wizard["res_model"]].with_context( **immediate_wizard["context"] ) ).save() - with self.assertRaises(ValidationError): + # Component1 error + with self.assertRaises(ValidationError) as e1: immediate_wizard_form.process() - # Create component stock, and subcontracting receipt should now be successful. - self.env["stock.quant"].create( - { - "product_id": self.comp1.id, - "location_id": self.subcontractor_partner1.property_stock_subcontractor.id, - "quantity": 10, - } - ) - self.env["stock.quant"].create( - { - "product_id": self.comp2.id, - "location_id": self.subcontractor_partner1.property_stock_subcontractor.id, - "quantity": 10, - } - ) + self.assertIn("Component1", str(e1.exception)) + # Create comp1 stock, and try subcontracting receipt process. + self._create_stock_quant(self.comp1, 10) + # Component2 error + with self.assertRaises(ValidationError) as e2: + immediate_wizard_form.process() + self.assertIn("Component2", str(e2.exception)) + # Create comp2 stock, and subcontracting receipt should now be successful. + self._create_stock_quant(self.comp2, 10) immediate_wizard_form.process() - self.assertEqual(subcontracting_receipt.state, "done") + self.assertEqual(self.subcontracting_receipt.state, "done") + + def test_mrp_subcontracting_skip_no_negative_03(self): + self._create_stock_quant(self.comp1, 10) + self._create_stock_quant(self.comp2, 10) + self.subcontracting_receipt.action_confirm() + self.assertEqual(self.subcontracting_receipt.state, "assigned") + immediate_wizard = self.subcontracting_receipt.sudo().button_validate() + self.assertEqual(immediate_wizard.get("res_model"), "stock.immediate.transfer") + immediate_wizard_form = Form( + self.env[immediate_wizard["res_model"]].with_context( + **immediate_wizard["context"] + ) + ).save() + immediate_wizard_form.process() + self.assertEqual(self.subcontracting_receipt.state, "done") + + def test_mrp_subcontracting_skip_no_negative_04(self): + self.subcontractor_partner1.property_stock_subcontractor.allow_negative_stock = ( + True + ) + self.subcontracting_receipt.action_confirm() + self.assertEqual(self.subcontracting_receipt.state, "assigned") + immediate_wizard = self.subcontracting_receipt.sudo().button_validate() + self.assertEqual(immediate_wizard.get("res_model"), "stock.immediate.transfer") + immediate_wizard_form = Form( + self.env[immediate_wizard["res_model"]].with_context( + **immediate_wizard["context"] + ) + ).save() + immediate_wizard_form.process() + self.assertEqual(self.subcontracting_receipt.state, "done")