diff options
| author | lase@odoo.com <lase@odoo.com> | 2025-02-04 07:37:49 +0000 |
|---|---|---|
| committer | lase@odoo.com <lase@odoo.com> | 2025-02-07 12:41:02 +0000 |
| commit | fd5e49305b7f2dcdf05b16c28fab6469a079b604 (patch) | |
| tree | 23dee021d4a44d2c4b7db16629eb60d2b5fcc59e | |
| parent | 68c1a13d73176273f54686ebbad2cc9bffb24c86 (diff) | |
[FIX] mrp{,_workorder}: ignore move raws without demand in reservation state
Issue:
Curently, the reservation state of a production is computed from the
status of the move raws that are not picked. This includes moves wihtout
demands which should not infer with the reservation state at all.
Steps to reproduce:
- In the settings, Enable "Multi-Step Routes"
- Go to Inventory > Configuration > Warehouse Management > Warehouses
- Put your warehouse in 2 steps-manufacturing
- Create a final product (FP) tracked by Serial number, and 2 storable
components: COMP1 and COMP2
- Create a BOM for FP:
- Component lines:
- 1 x COMP1
- 0 x COMP2 !!
- Operations:
- OP1 with an instruction of type "register production"
1. Create an MO for 1 x FP > Confirm
2. Validate the pick transfer for 1 unit of COMP1
> Currently OP1 is marked as ready
3. Mark the component line of COMP1 as "picked"
> OP1 is in waiting status
Same result as 3:
3'. Go to the shopfloor > register production from the MO card
> This will pick the COMP1 raw move and OP1 will disappear because of
> the "ready" filter
Cause of the issue:
Since you manufacture in 2 steps, the state of the raw move of COMP2
is waiting even though its demand is 0 (before and after you validated
the pick move for COMP1). However, only non picked moves are considered
to be relevant in the computation of the reservation state of the MO:
https://github.com/odoo/odoo/blob/d8fc04f5875dbd3098e58f382d0cea3c7c7c38af/addons/mrp/models/mrp_production.py#L613-L621
Once you pick the COMP1 move which should be the only relevant one the
reservation state of the MO becomes computed solely based on the state
of the COMP2 raw move and becomes "waiting". In turns, this triggers a
recomputation of the state of the workorders and flagging it as waiting:
https://github.com/odoo/odoo/blob/d8fc04f5875dbd3098e58f382d0cea3c7c7c38af/addons/mrp/models/mrp_workorder.py#L148-L165
opw-4383004
closes odoo/odoo#196682
X-original-commit: 88235a0fd3415a608ac1b0322a6b1f048f8bcaed
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
Signed-off-by: Lancelot Semal (lase) <lase@odoo.com>
| -rw-r--r-- | addons/mrp/models/mrp_production.py | 2 | ||||
| -rw-r--r-- | addons/mrp/tests/test_manual_consumption.py | 26 |
2 files changed, 27 insertions, 1 deletions
diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index dd03e8be1ed..e1998138a3b 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -619,7 +619,7 @@ class MrpProduction(models.Model): if production.state in ('draft', 'done', 'cancel'): production.reservation_state = False continue - relevant_move_state = production.move_raw_ids.filtered(lambda m: not m.picked)._get_relevant_state_among_moves() + relevant_move_state = production.move_raw_ids.filtered(lambda m: not (m.picked or float_is_zero(m.product_uom_qty, precision_rounding=m.product_uom.rounding)))._get_relevant_state_among_moves() # Compute reservation state according to its component's moves. if relevant_move_state == 'partially_available': if production.workorder_ids.operation_id and production.bom_id.ready_to_produce == 'asap': diff --git a/addons/mrp/tests/test_manual_consumption.py b/addons/mrp/tests/test_manual_consumption.py index f2e81eb6e45..b6e78165a2b 100644 --- a/addons/mrp/tests/test_manual_consumption.py +++ b/addons/mrp/tests/test_manual_consumption.py @@ -276,3 +276,29 @@ class TestManualConsumption(TestMrpCommon): self.assertEqual(mo.move_raw_ids.mapped('manual_consumption'), [True, False]) self.assertEqual(components[0].stock_quant_ids.reserved_quantity, 3.0) self.assertRecordValues(mo.move_raw_ids, [{'quantity': 3.0, 'picked': True}, {'quantity': 2.0, 'picked': True}]) + + def test_reservation_state_with_manual_consumption(self): + """ + Check that the reservation state of an MO is not influenced by moves without demand. + """ + self.warehouse_1.manufacture_steps = "pbm" + bom = self.bom_1 + components = bom.bom_line_ids.mapped('product_id') + components.is_storable = True + # make the second component optional + bom.bom_line_ids[-1].product_qty = 0.0 + self.env['stock.quant']._update_available_quantity(components[0], self.warehouse_1.lot_stock_id, 10.0) + mo_form = Form(self.env['mrp.production']) + mo_form.picking_type_id = self.warehouse_1.manu_type_id + mo_form.bom_id = bom + mo_form.product_qty = 4 + mo = mo_form.save() + mo.action_confirm() + self.assertRecordValues(mo.picking_ids.move_ids, [ + { "product_id": components[0].id, "product_uom_qty": 2.0} + ]) + self.assertEqual(mo.reservation_state, "waiting") + mo.picking_ids.button_validate() + self.assertEqual(mo.reservation_state, "assigned") + mo.move_raw_ids.filtered(lambda m: m.product_id == components[0]).picked = True + self.assertEqual(mo.reservation_state, "assigned") |
