mirror of
https://github.com/OCA/manufacture.git
synced 2025-01-28 16:37:15 +02:00
mrp_p_auto_validate: split qty on create
- split values for create in dedicated function - post messages on MO with modified qty - split MOs on create only on procurements
This commit is contained in:
committed by
Sébastien Alix
parent
bf7e47056a
commit
a8e4fa8ddb
@@ -1,3 +1,4 @@
|
||||
from . import mrp_bom
|
||||
from . import mrp_production
|
||||
from . import stock_picking
|
||||
from . import stock_rule
|
||||
|
||||
@@ -21,6 +21,7 @@ class MrpProduction(models.Model):
|
||||
@api.constrains("bom_id", "auto_validate", "product_qty")
|
||||
def check_bom_auto_validate(self):
|
||||
for mo in self:
|
||||
# FIXME: Handle different UOM between BOM and MO
|
||||
qty_ok = (
|
||||
tools.float_compare(
|
||||
mo.product_qty,
|
||||
@@ -91,3 +92,101 @@ class MrpProduction(models.Model):
|
||||
)
|
||||
wiz = wiz_model.create({})
|
||||
return wiz.action_backorder()
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, values_list):
|
||||
new_values_list, messages_to_post = self.adapt_values_qty_for_auto_validation(
|
||||
values_list
|
||||
)
|
||||
res = super().create(new_values_list)
|
||||
if messages_to_post:
|
||||
for pos, msg in messages_to_post.items():
|
||||
prod = res[pos]
|
||||
prod.message_post(body=msg)
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def adapt_values_qty_for_auto_validation(self, values_list):
|
||||
"""Adapt create values according to qty with auto validated BOM
|
||||
|
||||
If MOs are to be created with a BOM having auto validation, we must ensure
|
||||
the quantity of the MO is equal to the quantity of the BOM.
|
||||
However when MOs are created through procurements, the requested quantity
|
||||
is based on the procurement quantity, so we should either
|
||||
* increase the quantity to match the BOM if procurement value is lower
|
||||
* split the values to create one MO into multiple values to create multiple
|
||||
MOs matching the BOM quantity if procurement value is bigger
|
||||
"""
|
||||
messages_to_post = {}
|
||||
if not self.env.context.get("_split_create_values_for_auto_validation"):
|
||||
return values_list, messages_to_post
|
||||
new_values_list = []
|
||||
for values in values_list:
|
||||
bom_id = values.get("bom_id")
|
||||
if not bom_id:
|
||||
new_values_list.append(values)
|
||||
continue
|
||||
bom = self.env["mrp.bom"].browse(bom_id)
|
||||
if not bom.mo_auto_validation:
|
||||
new_values_list.append(values)
|
||||
continue
|
||||
create_qty = values.get("product_qty")
|
||||
create_uom = self.env["uom.uom"].browse(values.get("product_uom_id"))
|
||||
bom_qty = bom.product_qty
|
||||
bom_uom = bom.product_uom_id
|
||||
if create_uom != bom_uom:
|
||||
create_qty = create_uom._compute_quantity(create_qty, bom_uom)
|
||||
if (
|
||||
tools.float_compare(
|
||||
create_qty, bom_qty, precision_rounding=bom_uom.rounding
|
||||
)
|
||||
== 0
|
||||
):
|
||||
new_values_list.append(values)
|
||||
continue
|
||||
elif (
|
||||
tools.float_compare(
|
||||
create_qty, bom_qty, precision_rounding=bom_uom.rounding
|
||||
)
|
||||
< 0
|
||||
):
|
||||
procure_qty = values.get("product_qty")
|
||||
values["product_qty"] = bom_qty
|
||||
values["product_uom_id"] = bom_uom.id
|
||||
msg = _(
|
||||
"Quantity in procurement (%s %s) was increased to %s %s due to auto "
|
||||
"validation feature preventing to create an MO with a different "
|
||||
"qty than defined on the BOM."
|
||||
) % (
|
||||
procure_qty,
|
||||
create_uom.display_name,
|
||||
bom_qty,
|
||||
bom_uom.display_name,
|
||||
)
|
||||
messages_to_post[len(new_values_list)] = msg
|
||||
new_values_list.append(values)
|
||||
continue
|
||||
# If we get here we need to split the prepared MO values
|
||||
# into multiple MO values to respect BOM qty
|
||||
while (
|
||||
tools.float_compare(create_qty, 0, precision_rounding=bom_uom.rounding)
|
||||
> 0
|
||||
):
|
||||
new_values = values.copy()
|
||||
new_values["product_qty"] = bom_qty
|
||||
new_values["product_uom_id"] = bom_uom.id
|
||||
msg = _(
|
||||
"Quantity in procurement (%s %s) was split to multiple production "
|
||||
"orders of %s %s due to auto validation feature preventing to "
|
||||
"set a quantity to produce different than the quantity defined "
|
||||
"on the Bill of Materials."
|
||||
) % (
|
||||
values.get("product_qty"),
|
||||
create_uom.display_name,
|
||||
bom_qty,
|
||||
bom_uom.display_name,
|
||||
)
|
||||
messages_to_post[len(new_values_list)] = msg
|
||||
new_values_list.append(new_values)
|
||||
create_qty -= bom_qty
|
||||
return new_values_list, messages_to_post
|
||||
|
||||
13
mrp_production_auto_validate/models/stock_rule.py
Normal file
13
mrp_production_auto_validate/models/stock_rule.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# Copyright 2023 Camptocamp SA
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class StockRule(models.Model):
|
||||
_inherit = "stock.rule"
|
||||
|
||||
@api.model
|
||||
def _run_manufacture(self, procurements):
|
||||
return super(
|
||||
StockRule, self.with_context(_split_create_values_for_auto_validation=True)
|
||||
)._run_manufacture(procurements)
|
||||
@@ -14,10 +14,33 @@ class TestManufacturingOrderAutoValidate(SavepointCase):
|
||||
# "pick components" transfer operation
|
||||
cls.wh = cls.env.ref("stock.warehouse0")
|
||||
cls.wh.manufacture_steps = "pbm"
|
||||
# Configure the product to be replenished through manufacture route
|
||||
cls.product_template = cls.env.ref(
|
||||
"mrp.product_product_computer_desk_head_product_template"
|
||||
)
|
||||
cls.manufacture_route = cls.env.ref("mrp.route_warehouse0_manufacture")
|
||||
cls.product_template.route_ids = [(6, 0, [cls.manufacture_route.id])]
|
||||
# Configure the BoM to auto-validate manufacturing orders
|
||||
# NOTE: to ease tests we take a BoM with only one component
|
||||
cls.bom = cls.env.ref("mrp.mrp_bom_table_top") # Tracked by S/N
|
||||
cls.bom.mo_auto_validation = True
|
||||
cls.uom_unit = cls.env.ref("uom.product_uom_unit")
|
||||
|
||||
@classmethod
|
||||
def _replenish_product(cls, product, product_qty=1, product_uom=None):
|
||||
if product_uom is None:
|
||||
product_uom = cls.uom_unit
|
||||
wiz = (
|
||||
cls.env["product.replenish"]
|
||||
.with_context(default_product_id=product.id)
|
||||
.create(
|
||||
{
|
||||
"quantity": product_qty,
|
||||
"product_uom_id": product_uom.id,
|
||||
}
|
||||
)
|
||||
)
|
||||
wiz.launch_replenishment()
|
||||
|
||||
@classmethod
|
||||
def _create_manufacturing_order(cls, bom, product_qty=1):
|
||||
@@ -139,3 +162,36 @@ class TestManufacturingOrderAutoValidate(SavepointCase):
|
||||
self.assertFalse(order_done)
|
||||
self.assertEqual(order.state, "confirmed")
|
||||
self.assertEqual(order.product_qty, 2)
|
||||
|
||||
def test_keep_qty_on_replenishment(self):
|
||||
existing_mos = self.env["mrp.production"].search([])
|
||||
self._replenish_product(self.product_template.product_variant_id, product_qty=1)
|
||||
created_mos = self.env["mrp.production"].search(
|
||||
[("id", "not in", existing_mos.ids)]
|
||||
)
|
||||
self.assertEqual(len(created_mos), 1)
|
||||
self.assertEqual(created_mos.product_qty, self.bom.product_qty)
|
||||
self.assertFalse(any("split" in m.body for m in created_mos.message_ids))
|
||||
self.assertFalse(any("increased" in m.body for m in created_mos.message_ids))
|
||||
|
||||
def test_split_qty_on_replenishment(self):
|
||||
existing_mos = self.env["mrp.production"].search([])
|
||||
self._replenish_product(self.product_template.product_variant_id, product_qty=3)
|
||||
created_mos = self.env["mrp.production"].search(
|
||||
[("id", "not in", existing_mos.ids)]
|
||||
)
|
||||
self.assertEqual(len(created_mos), 3)
|
||||
for mo in created_mos:
|
||||
self.assertEqual(mo.product_qty, self.bom.product_qty)
|
||||
self.assertTrue(any("split" in m.body for m in mo.message_ids))
|
||||
|
||||
def test_raise_qty_on_replenishment(self):
|
||||
existing_mos = self.env["mrp.production"].search([])
|
||||
self.bom.product_qty = 5
|
||||
self._replenish_product(self.product_template.product_variant_id, product_qty=3)
|
||||
created_mos = self.env["mrp.production"].search(
|
||||
[("id", "not in", existing_mos.ids)]
|
||||
)
|
||||
self.assertEqual(len(created_mos), 1)
|
||||
self.assertEqual(created_mos.product_qty, self.bom.product_qty)
|
||||
self.assertTrue(any("increased" in m.body for m in created_mos.message_ids))
|
||||
|
||||
Reference in New Issue
Block a user