diff --git a/mrp_subcontracting_skip_no_negative/README.rst b/mrp_subcontracting_skip_no_negative/README.rst index 527110e6..f91cf8a0 100644 --- a/mrp_subcontracting_skip_no_negative/README.rst +++ b/mrp_subcontracting_skip_no_negative/README.rst @@ -7,7 +7,7 @@ MRP Subcontracting Skip No Negative !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:be1748f4d86ee601f04fee6c3ecec9db99e0aff2f2628d99f01406f1a1e20cf5 + !! source digest: sha256:b4b59b86062f08db75f2e86b8b9fced861771da8483adf65c81e3a65c0ba1c79 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png @@ -76,6 +76,10 @@ Contributors * Aung Ko Ko Lin +* `Tecnativa `_: + + * Víctor Martínez + Maintainers ~~~~~~~~~~~ diff --git a/mrp_subcontracting_skip_no_negative/__manifest__.py b/mrp_subcontracting_skip_no_negative/__manifest__.py index 3d624be5..7288aa37 100644 --- a/mrp_subcontracting_skip_no_negative/__manifest__.py +++ b/mrp_subcontracting_skip_no_negative/__manifest__.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) { "name": "MRP Subcontracting Skip No Negative", - "version": "16.0.1.0.1", + "version": "16.0.1.0.2", "license": "AGPL-3", "author": "Quartile Limited, Odoo Community Association (OCA)", "website": "https://github.com/OCA/manufacture", 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 00000000..1aa15554 --- /dev/null +++ b/mrp_subcontracting_skip_no_negative/i18n/es.po @@ -0,0 +1,43 @@ +# 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-05 07:53+0000\n" +"PO-Revision-Date: 2024-11-05 08:53+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,name:mrp_subcontracting_skip_no_negative.model_stock_move +msgid "Stock Move" +msgstr "Movimiento de existencias" + +#. module: mrp_subcontracting_skip_no_negative +#: model:ir.model,name:mrp_subcontracting_skip_no_negative.model_stock_picking +msgid "Transfer" +msgstr "Transferencia" + +#. module: mrp_subcontracting_skip_no_negative +#. odoo-python +#: 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 '{name}' would become negative ({qty}) on the stock " +"location '{location}' 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 '{name}' se volvería negativo ({qty}) en la ubicación de " +"stock '{location}' y no se permite stock negativo para este producto y/o " +"ubicación." diff --git a/mrp_subcontracting_skip_no_negative/i18n/it.po b/mrp_subcontracting_skip_no_negative/i18n/it.po index cd875c89..798424e1 100644 --- a/mrp_subcontracting_skip_no_negative/i18n/it.po +++ b/mrp_subcontracting_skip_no_negative/i18n/it.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 16.0\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2023-11-16 12:43+0000\n" +"PO-Revision-Date: 2024-11-08 09:35+0000\n" "Last-Translator: mymage \n" "Language-Team: none\n" "Language: it\n" @@ -14,7 +14,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.17\n" +"X-Generator: Weblate 5.6.2\n" #. module: mrp_subcontracting_skip_no_negative #: model:ir.model,name:mrp_subcontracting_skip_no_negative.model_stock_move @@ -25,3 +25,18 @@ msgstr "Movimento di magazzino" #: model:ir.model,name:mrp_subcontracting_skip_no_negative.model_stock_picking msgid "Transfer" msgstr "Trasferimento" + +#. module: mrp_subcontracting_skip_no_negative +#. odoo-python +#: 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 '{name}' would become negative ({qty}) on the stock " +"location '{location}' and negative stock is not allowed for this product and/" +"or location." +msgstr "" +"Non è possibile convalidare questa operazione di magazzino perché il livello " +"di magazzino del prodotto componente '{name}' diventerebbe negativo ({qty}) " +"nell'ubicazione di magazzino '{location}' e per questo prodotto e/o " +"posizione non è consentito un magazzino negativo." 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 ce747ae4..fbc852f6 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 @@ -22,3 +22,14 @@ msgstr "" #: model:ir.model,name:mrp_subcontracting_skip_no_negative.model_stock_picking msgid "Transfer" msgstr "" + +#. module: mrp_subcontracting_skip_no_negative +#. odoo-python +#: 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 '{name}' would become negative ({qty}) on the stock " +"location '{location}' 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 35a10887..fde1a82d 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): @@ -31,7 +34,43 @@ def _action_done(self, cancel_backorder=False): ): continue moves_with_no_check -= move - res = super(StockMove, self - moves_with_no_check)._action_done( + # 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 '{name}' would become " + "negative ({qty}) on the stock location '{location}' and " + "negative stock is not allowed for this product and/or " + "location." + ).format( + name=product.display_name, + qty=new_qty, + location=location.complete_name, + ) + ) + moves_with_check = self - moves_with_no_check + res = super(StockMove, moves_with_check.with_context(no_backorder=True))._action_done( cancel_backorder=cancel_backorder ) res += super(StockMove, moves_with_no_check)._action_done( diff --git a/mrp_subcontracting_skip_no_negative/models/stock_picking.py b/mrp_subcontracting_skip_no_negative/models/stock_picking.py index 015c8554..9314f009 100644 --- a/mrp_subcontracting_skip_no_negative/models/stock_picking.py +++ b/mrp_subcontracting_skip_no_negative/models/stock_picking.py @@ -20,3 +20,8 @@ def _check_negative_quants_after_process(self): ] ) quants.check_negative_qty() + + def _create_backorder(self): + if self.env.context.get("no_backorder"): + return self.env['stock.picking'] + return super()._create_backorder() diff --git a/mrp_subcontracting_skip_no_negative/readme/CONTRIBUTORS.rst b/mrp_subcontracting_skip_no_negative/readme/CONTRIBUTORS.rst index cd4e44ca..4a81d357 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 cde0c711..404b1ea2 100644 --- a/mrp_subcontracting_skip_no_negative/static/description/index.html +++ b/mrp_subcontracting_skip_no_negative/static/description/index.html @@ -8,10 +8,11 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z 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. @@ -274,7 +275,7 @@ margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: grey; } /* line numbers */ +pre.code .ln { color: gray; } /* 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 } @@ -300,7 +301,7 @@ span.pre { white-space: pre } -span.problematic { +span.problematic, pre.problematic { color: red } span.section-subtitle { @@ -366,7 +367,7 @@

MRP Subcontracting Skip No Negative

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:be1748f4d86ee601f04fee6c3ecec9db99e0aff2f2628d99f01406f1a1e20cf5 +!! source digest: sha256:b4b59b86062f08db75f2e86b8b9fced861771da8483adf65c81e3a65c0ba1c79 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

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

This module skips the negative quantity check, provided by stock_no_negative, for @@ -413,12 +414,18 @@

Contributors

  • 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 46e6aa97..c6698572 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,50 +1,94 @@ # 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 class TestMrpSubcontractingSkipNoNegative(TestMrpSubcontractingCommon): - 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 + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env( + context=dict( + cls.env.context, + test_stock_no_negative=True, + ) + ) + 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 = subcontracting_receipt.with_context( - test_stock_no_negative=True + 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, + } ) - subcontracting_receipt.action_confirm() - self.assertEqual(subcontracting_receipt.state, "assigned") - immediate_wizard = subcontracting_receipt.button_validate() + + @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() + 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(self.subcontracting_receipt.state, "done") - # 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, - } + 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(subcontracting_receipt.state, "done") + self.assertEqual(self.subcontracting_receipt.state, "done")