mirror of
https://github.com/OCA/manufacture.git
synced 2025-01-28 16:37:15 +02:00
[IMP] mrp_multi_level: safety stock
When a product has a safety stock in an area, and the current stock is below safety, but there are moves in the future, mrp_multi_level does not compute an immediate action to get back to the safety stock. This PR changes this behavior: when the mrp_moves are considered, before processing the first move in the future, we insert a resupply action to rebuild the safety stock. We also add some refactoring in the process: * add extension point on the wizard to compute the quantity to reorder (so we can choose in a separate module whether to rebuild safety stock or not) * add extension point on the wizard to get the date at which the safety stock must be rebuilt (defaults to today) * make the code of the wizard symetric between the groupes and non grouped configuration
This commit is contained in:
committed by
JasminSForgeFlow
parent
2bbef8657c
commit
33329e0d5f
@@ -15,6 +15,7 @@ class TestMrpMultiLevelCommon(TransactionCase):
|
||||
cls.po_obj = cls.env["purchase.order"]
|
||||
cls.product_obj = cls.env["product.product"]
|
||||
cls.loc_obj = cls.env["stock.location"]
|
||||
cls.quant_obj = cls.env["stock.quant"]
|
||||
cls.mrp_area_obj = cls.env["mrp.area"]
|
||||
cls.product_mrp_area_obj = cls.env["product.mrp.area"]
|
||||
cls.partner_obj = cls.env["res.partner"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright 2018-19 ForgeFlow S.L. (https://www.forgeflow.com)
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date, datetime, timedelta
|
||||
|
||||
from odoo import fields
|
||||
|
||||
@@ -445,3 +445,82 @@ class TestMrpMultiLevel(TestMrpMultiLevelCommon):
|
||||
]
|
||||
product_mrp_area._compute_supply_method()
|
||||
self.assertEqual(product_mrp_area.supply_method, "buy")
|
||||
|
||||
def test_18_priorize_safety_stock(self):
|
||||
now = datetime.now()
|
||||
product = self.prod_test # has Buy route
|
||||
product.seller_ids[0].delay = 2 # set a purchase lead time
|
||||
self.quant_obj._update_available_quantity(product, self.cases_loc, 5)
|
||||
self.product_mrp_area_obj.create(
|
||||
{
|
||||
"product_id": product.id,
|
||||
"mrp_area_id": self.cases_area.id,
|
||||
"mrp_minimum_stock": 15,
|
||||
"mrp_applicable": True, # needed?
|
||||
}
|
||||
)
|
||||
self._create_picking_out(
|
||||
product, 6.0, now + timedelta(days=3), location=self.cases_loc
|
||||
)
|
||||
self._create_picking_in(
|
||||
product, 10.0, now + timedelta(days=7), location=self.cases_loc
|
||||
)
|
||||
self._create_picking_out(
|
||||
product, 12.0, now + timedelta(days=14), location=self.cases_loc
|
||||
)
|
||||
self.mrp_multi_level_wiz.create(
|
||||
{"mrp_area_ids": [(6, 0, self.cases_area.ids)]}
|
||||
).run_mrp_multi_level()
|
||||
inventory = self.mrp_inventory_obj.search(
|
||||
[
|
||||
("mrp_area_id", "=", self.cases_area.id),
|
||||
("product_id", "=", product.id),
|
||||
]
|
||||
)
|
||||
expected = [
|
||||
{
|
||||
"date": now.date(),
|
||||
"demand_qty": 0.0,
|
||||
"final_on_hand_qty": 5.0,
|
||||
"initial_on_hand_qty": 5.0,
|
||||
"running_availability": 15.0,
|
||||
"supply_qty": 0.0,
|
||||
"to_procure": 10.0,
|
||||
},
|
||||
{
|
||||
"date": now.date() + timedelta(days=3),
|
||||
"demand_qty": 6.0,
|
||||
"final_on_hand_qty": -1.0,
|
||||
"initial_on_hand_qty": 5.0,
|
||||
"running_availability": 15.0,
|
||||
"supply_qty": 0.0,
|
||||
"to_procure": 6.0,
|
||||
},
|
||||
{
|
||||
"date": now.date() + timedelta(days=7),
|
||||
"demand_qty": 0.0,
|
||||
"final_on_hand_qty": 9.0,
|
||||
"initial_on_hand_qty": -1.0,
|
||||
"running_availability": 25.0,
|
||||
"supply_qty": 10.0,
|
||||
"to_procure": 0.0,
|
||||
},
|
||||
{
|
||||
"date": now.date() + timedelta(days=14),
|
||||
"demand_qty": 12.0,
|
||||
"final_on_hand_qty": -3.0,
|
||||
"initial_on_hand_qty": 9.0,
|
||||
"running_availability": 15.0,
|
||||
"supply_qty": 0.0,
|
||||
"to_procure": 2.0,
|
||||
},
|
||||
]
|
||||
self.assertEqual(len(expected), len(inventory))
|
||||
for test_vals, inv in zip(expected, inventory):
|
||||
for key in test_vals:
|
||||
self.assertEqual(
|
||||
test_vals[key],
|
||||
inv[key],
|
||||
f"unexpected value for {key}: {inv[key]} "
|
||||
f"(expected {test_vals[key]} on {inv.date}",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user