[IMP] mrp_subcontracting_skip_no_negative: Show negative stock error of subcontracting component

TT50668
This commit is contained in:
Víctor Martínez
2024-10-23 09:54:54 +02:00
parent dbd7cf9cea
commit 9f5c68498f
7 changed files with 182 additions and 32 deletions

View File

@@ -76,6 +76,10 @@ Contributors
* Aung Ko Ko Lin
* `Tecnativa <https://www.tecnativa.com>`_:
* Víctor Martínez
Maintainers
~~~~~~~~~~~

View File

@@ -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."

View File

@@ -6,6 +6,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-05 07:53+0000\n"
"PO-Revision-Date: 2024-11-05 07:53+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
@@ -22,3 +24,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 ""

View File

@@ -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,6 +34,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 '{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,
)
)
res = super(StockMove, self - moves_with_no_check)._action_done(
cancel_backorder=cancel_backorder
)

View File

@@ -1,3 +1,7 @@
* `Quartile <https://www.quartile.co>`__:
* Aung Ko Ko Lin
* `Tecnativa <https://www.tecnativa.com>`_:
* Víctor Martínez

View File

@@ -413,6 +413,10 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
<li>Aung Ko Ko Lin</li>
</ul>
</li>
<li><a class="reference external" href="https://www.tecnativa.com">Tecnativa</a>:<ul>
<li>Víctor Martínez</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">

View File

@@ -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
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.finished
move.product_uom_qty = 1
subcontracting_receipt = picking_form.save()
subcontracting_receipt = subcontracting_receipt.with_context(
test_stock_no_negative=True
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env = cls.env(
context=dict(
cls.env.context,
test_stock_no_negative=True,
)
)
subcontracting_receipt.action_confirm()
self.assertEqual(subcontracting_receipt.state, "assigned")
immediate_wizard = subcontracting_receipt.button_validate()
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 = cls.finished
move.product_uom_qty = 1
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")