mirror of
https://github.com/OCA/manufacture.git
synced 2025-01-28 16:37:15 +02:00
@@ -89,8 +89,11 @@ class MrpInventory(models.Model):
|
||||
@api.depends("planned_order_ids", "planned_order_ids.qty_released")
|
||||
def _compute_to_procure(self):
|
||||
for rec in self:
|
||||
rec.to_procure = sum(rec.planned_order_ids.mapped("mrp_qty")) - sum(
|
||||
rec.planned_order_ids.mapped("qty_released")
|
||||
rec.to_procure = (
|
||||
0.0
|
||||
if rec.supply_method == "phantom"
|
||||
else sum(rec.planned_order_ids.mapped("mrp_qty"))
|
||||
- sum(rec.planned_order_ids.mapped("qty_released"))
|
||||
)
|
||||
|
||||
@api.depends(
|
||||
|
||||
@@ -59,6 +59,7 @@ class MrpPlannedOrder(models.Model):
|
||||
mrp_action = fields.Selection(
|
||||
selection=[
|
||||
("manufacture", "Manufacturing Order"),
|
||||
("phantom", "Kit"),
|
||||
("buy", "Purchase Order"),
|
||||
("pull", "Pull From"),
|
||||
("push", "Push To"),
|
||||
|
||||
@@ -307,7 +307,3 @@ class ProductMRPArea(models.Model):
|
||||
def _get_locations(self):
|
||||
self.ensure_one()
|
||||
return self.mrp_area_id._get_locations()
|
||||
|
||||
def _should_create_planned_order(self):
|
||||
self.ensure_one()
|
||||
return not self.supply_method == "phantom"
|
||||
|
||||
@@ -401,11 +401,8 @@ class TestMrpMultiLevel(TestMrpMultiLevelCommon):
|
||||
sf_3_planned_order_1 = self.planned_order_obj.search(
|
||||
[("product_mrp_area_id.product_id", "=", self.sf_3.id)]
|
||||
)
|
||||
self.assertEqual(len(sf_3_planned_order_1), 0)
|
||||
sf_3_mrp_parameter = self.product_mrp_area_obj.search(
|
||||
[("product_id", "=", self.sf_3.id)]
|
||||
)
|
||||
self.assertEqual(sf_3_mrp_parameter.supply_method, "phantom")
|
||||
self.assertEqual(sf_3_planned_order_1.mrp_action, "phantom")
|
||||
self.assertEqual(sf_3_planned_order_1.mrp_qty, 10.0)
|
||||
# PP-3
|
||||
pp_3_line_1 = self.mrp_inventory_obj.search(
|
||||
[("product_mrp_area_id.product_id", "=", self.pp_3.id)]
|
||||
@@ -852,3 +849,53 @@ class TestMrpMultiLevel(TestMrpMultiLevelCommon):
|
||||
f"unexpected value for {key}: {inv[key]} "
|
||||
f"(expected {test_vals[key]} on {inv.date})",
|
||||
)
|
||||
|
||||
def test_25_phantom_comp_on_hand(self):
|
||||
"""
|
||||
A phantom product with positive qty_available (which is computed from the
|
||||
availability of its components) should not satisfy demand, because this leads
|
||||
to double counting qty_available of its component products.
|
||||
"""
|
||||
quant = self.quant_obj.sudo().create(
|
||||
{
|
||||
"product_id": self.pp_3.id,
|
||||
"inventory_quantity": 10.0,
|
||||
"location_id": self.stock_location.id,
|
||||
}
|
||||
)
|
||||
quant.action_apply_inventory()
|
||||
quant = self.quant_obj.sudo().create(
|
||||
{
|
||||
"product_id": self.pp_4.id,
|
||||
"inventory_quantity": 30.0,
|
||||
"location_id": self.stock_location.id,
|
||||
}
|
||||
)
|
||||
quant.action_apply_inventory()
|
||||
self.assertEqual(self.sf_3.qty_available, 10.0)
|
||||
self.mrp_multi_level_wiz.create({}).run_mrp_multi_level()
|
||||
# PP-3
|
||||
pp_3_line_1 = self.mrp_inventory_obj.search(
|
||||
[("product_mrp_area_id.product_id", "=", self.pp_3.id)]
|
||||
)
|
||||
self.assertEqual(len(pp_3_line_1), 1)
|
||||
self.assertEqual(pp_3_line_1.demand_qty, 20.0)
|
||||
self.assertEqual(pp_3_line_1.to_procure, 10.0)
|
||||
pp_3_planned_orders = self.planned_order_obj.search(
|
||||
[("product_mrp_area_id.product_id", "=", self.pp_3.id)]
|
||||
)
|
||||
self.assertEqual(len(pp_3_planned_orders), 1)
|
||||
self.assertEqual(pp_3_planned_orders.mrp_qty, 10)
|
||||
sf3_planned_orders = self.env["mrp.planned.order"].search(
|
||||
[("product_id", "=", self.sf_3.id)]
|
||||
)
|
||||
self.assertEqual(len(sf3_planned_orders), 1)
|
||||
# Trying to procure a kit planned order will have no effect.
|
||||
procure_wizard = (
|
||||
self.env["mrp.inventory.procure"]
|
||||
.with_context(
|
||||
active_model="mrp.planned.order", active_ids=sf3_planned_orders.ids
|
||||
)
|
||||
.create({})
|
||||
)
|
||||
self.assertEqual(len(procure_wizard.item_ids), 0)
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
<field name="name">mrp.planned.order.tree</field>
|
||||
<field name="model">mrp.planned.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree decoration-info="fixed != True">
|
||||
<tree
|
||||
decoration-info="fixed != True and mrp_action != 'phantom'"
|
||||
decoration-muted="mrp_action == 'phantom'"
|
||||
>
|
||||
<field name="name" />
|
||||
<field name="origin" />
|
||||
<field name="product_mrp_area_id" />
|
||||
@@ -17,6 +20,7 @@
|
||||
<field name="qty_released" />
|
||||
<field name="mrp_qty" />
|
||||
<field name="fixed" />
|
||||
<field name="mrp_action" optional="hide" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -62,6 +62,8 @@ class MrpInventoryProcure(models.TransientModel):
|
||||
elif active_model == "mrp.planned.order":
|
||||
mrp_planned_order_obj = self.env[active_model]
|
||||
for line in mrp_planned_order_obj.browse(active_ids):
|
||||
if line.mrp_action == "phantom":
|
||||
continue
|
||||
if line.qty_released < line.mrp_qty:
|
||||
items += item_obj.create(self._prepare_item(line))
|
||||
if items:
|
||||
|
||||
@@ -272,10 +272,7 @@ class MultiLevelMrp(models.TransientModel):
|
||||
order_data = self._prepare_planned_order_data(
|
||||
product_mrp_area_id, qty, mrp_date_supply, mrp_action_date, name, values
|
||||
)
|
||||
# Do not create planned order for products that are Kits
|
||||
planned_order = False
|
||||
if product_mrp_area_id._should_create_planned_order():
|
||||
planned_order = self.env["mrp.planned.order"].create(order_data)
|
||||
planned_order = self.env["mrp.planned.order"].create(order_data)
|
||||
qty_ordered = qty_ordered + qty
|
||||
|
||||
if product_mrp_area_id._to_be_exploded():
|
||||
@@ -536,7 +533,11 @@ class MultiLevelMrp(models.TransientModel):
|
||||
def _init_mrp_move_grouped_demand(self, product_mrp_area):
|
||||
last_date = None
|
||||
last_qty = 0.00
|
||||
onhand = product_mrp_area.qty_available
|
||||
onhand = (
|
||||
0.0
|
||||
if product_mrp_area.supply_method == "phantom"
|
||||
else product_mrp_area.qty_available
|
||||
)
|
||||
grouping_delta = product_mrp_area.mrp_nbr_days
|
||||
demand_origin = []
|
||||
|
||||
@@ -666,7 +667,11 @@ class MultiLevelMrp(models.TransientModel):
|
||||
|
||||
@api.model
|
||||
def _init_mrp_move_non_grouped_demand(self, product_mrp_area):
|
||||
onhand = product_mrp_area.qty_available
|
||||
onhand = (
|
||||
0.0
|
||||
if product_mrp_area.supply_method == "phantom"
|
||||
else product_mrp_area.qty_available
|
||||
)
|
||||
for move in product_mrp_area.mrp_move_ids:
|
||||
if self._exclude_move(move):
|
||||
continue
|
||||
@@ -815,7 +820,8 @@ class MultiLevelMrp(models.TransientModel):
|
||||
supply_qty = supply_qty_by_date.get(mdt, 0.0)
|
||||
mrp_inventory_data["supply_qty"] = abs(supply_qty)
|
||||
mrp_inventory_data["initial_on_hand_qty"] = on_hand_qty
|
||||
on_hand_qty += supply_qty + demand_qty
|
||||
if product_mrp_area.supply_method != "phantom":
|
||||
on_hand_qty += supply_qty + demand_qty
|
||||
mrp_inventory_data["final_on_hand_qty"] = on_hand_qty
|
||||
# Consider that MRP plan is followed exactly:
|
||||
running_availability += (
|
||||
@@ -854,7 +860,11 @@ class MultiLevelMrp(models.TransientModel):
|
||||
[("product_mrp_area_id", "=", product_mrp_area.id)], order="due_date"
|
||||
).mapped("due_date")
|
||||
mrp_dates = set(moves_dates + action_dates)
|
||||
on_hand_qty = product_mrp_area.qty_available
|
||||
on_hand_qty = (
|
||||
0.0
|
||||
if product_mrp_area.supply_method == "phantom"
|
||||
else product_mrp_area.qty_available
|
||||
)
|
||||
running_availability = on_hand_qty
|
||||
mrp_inventory_vals = []
|
||||
for mdt in sorted(mrp_dates):
|
||||
|
||||
Reference in New Issue
Block a user