Skip to content

Commit 81e5ce0

Browse files
committed
[FIX] mrp: find child/source MO
When a MO is generated in order to supply another one, the smart buttons "Source MO"/"Child MO" are still invisible. To reproduce the issue: 1. In Settings, enable "Multi-Step Routes" 2. Enable MTO route 3. Edit the warehouse: 3-steps manufacturing 4. Create 3 stored products Super, Sub and Compo: - Sub has two routes: MTO and Manufacture 5. Create two BoMs: - For 1 x Super: - 1 x Sub - For 1 x Sub: - 1 x Compo 6. Create and confirm a MO with 1 x Super Error: On confirmation, a second MO is created to produce one Sub, which is correct. However, neither the first MO nor the generated one has a link with the other one (through the smart button "Source MO"/"Child MO") When confirming the MO, a SM is created to bring one Sub to the pre-production location. Thanks to MTO route, this will generate another SM that brings one Sub from post-production to stock location (this will lead to the creation of the second MO). However, the other SM (1 x Sub from Post to Stock) doesn't have the same procurement group. This is the reason why it is not found by the `_compute` methods. OPW-2730830 closes odoo#94185 X-original-commit: 4870606 Signed-off-by: Arnold Moyaux (arm) <[email protected]> Signed-off-by: Adrien Widart <[email protected]>
1 parent 7d7c53e commit 81e5ce0

File tree

2 files changed

+77
-6
lines changed

2 files changed

+77
-6
lines changed

addons/mrp/models/mrp_production.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -268,15 +268,17 @@ def _get_default_is_locked(self):
268268
forecasted_issue = fields.Boolean(compute='_compute_forecasted_issue')
269269
show_serial_mass_produce = fields.Boolean('Display the serial mass product wizard action moves', compute='_compute_show_serial_mass_produce')
270270

271-
@api.depends('procurement_group_id.stock_move_ids.created_production_id.procurement_group_id.mrp_production_ids')
271+
@api.depends('procurement_group_id.stock_move_ids.created_production_id.procurement_group_id.mrp_production_ids',
272+
'procurement_group_id.stock_move_ids.move_orig_ids.created_production_id.procurement_group_id.mrp_production_ids')
272273
def _compute_mrp_production_child_count(self):
273274
for production in self:
274-
production.mrp_production_child_count = len(production.procurement_group_id.stock_move_ids.created_production_id.procurement_group_id.mrp_production_ids - production)
275+
production.mrp_production_child_count = len(production._get_children())
275276

276-
@api.depends('move_dest_ids.group_id.mrp_production_ids')
277+
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.mrp_production_ids',
278+
'procurement_group_id.stock_move_ids.move_dest_ids.group_id.mrp_production_ids')
277279
def _compute_mrp_production_source_count(self):
278280
for production in self:
279-
production.mrp_production_source_count = len(production.procurement_group_id.mrp_production_ids.move_dest_ids.group_id.mrp_production_ids - production)
281+
production.mrp_production_source_count = len(production._get_sources())
280282

281283
@api.depends('procurement_group_id.mrp_production_ids')
282284
def _compute_mrp_production_backorder(self):
@@ -1088,9 +1090,21 @@ def _autoconfirm_production(self):
10881090

10891091
self.workorder_ids.filtered(lambda w: w.state not in ['done', 'cancel'])._action_confirm()
10901092

1093+
def _get_children(self):
1094+
self.ensure_one()
1095+
procurement_moves = self.procurement_group_id.stock_move_ids
1096+
child_moves = procurement_moves.move_orig_ids
1097+
return (procurement_moves | child_moves).created_production_id.procurement_group_id.mrp_production_ids - self
1098+
1099+
def _get_sources(self):
1100+
self.ensure_one()
1101+
dest_moves = self.procurement_group_id.mrp_production_ids.move_dest_ids
1102+
parent_moves = self.procurement_group_id.stock_move_ids.move_dest_ids
1103+
return (dest_moves | parent_moves).group_id.mrp_production_ids - self
1104+
10911105
def action_view_mrp_production_childs(self):
10921106
self.ensure_one()
1093-
mrp_production_ids = self.procurement_group_id.stock_move_ids.created_production_id.procurement_group_id.mrp_production_ids.ids
1107+
mrp_production_ids = self._get_children().ids
10941108
action = {
10951109
'res_model': 'mrp.production',
10961110
'type': 'ir.actions.act_window',
@@ -1110,7 +1124,7 @@ def action_view_mrp_production_childs(self):
11101124

11111125
def action_view_mrp_production_sources(self):
11121126
self.ensure_one()
1113-
mrp_production_ids = self.procurement_group_id.mrp_production_ids.move_dest_ids.group_id.mrp_production_ids.ids
1127+
mrp_production_ids = self._get_sources().ids
11141128
action = {
11151129
'res_model': 'mrp.production',
11161130
'type': 'ir.actions.act_window',

addons/mrp/tests/test_order.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2617,3 +2617,60 @@ def test_qty_update_and_method_reservation(self):
26172617
wizard.change_prod_qty()
26182618

26192619
self.assertFalse(mo.move_raw_ids.move_line_ids)
2620+
2621+
def test_source_and_child_mo(self):
2622+
"""
2623+
Suppose three manufactured products A, B and C. C is a component of B
2624+
and B is a component of A. If B and C have the routes MTO + Manufacture,
2625+
when producing one A, it should generate a MO for B and C. Moreover,
2626+
starting from one of the MOs, we should be able to find the source/child
2627+
MO.
2628+
(The test checks the flow in 1-step, 2-steps and 3-steps manufacturing)
2629+
"""
2630+
warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1)
2631+
mto_route = warehouse.mto_pull_id.route_id
2632+
manufacture_route = warehouse.manufacture_pull_id.route_id
2633+
mto_route.active = True
2634+
2635+
grandparent, parent, child = self.env['product.product'].create([{
2636+
'name': n,
2637+
'type': 'product',
2638+
'route_ids': [(6, 0, mto_route.ids + manufacture_route.ids)],
2639+
} for n in ['grandparent', 'parent', 'child']])
2640+
component = self.env['product.product'].create({
2641+
'name': 'component',
2642+
'type': 'consu',
2643+
})
2644+
2645+
self.env['mrp.bom'].create([{
2646+
'product_tmpl_id': finished_product.product_tmpl_id.id,
2647+
'product_qty': 1,
2648+
'type': 'normal',
2649+
'bom_line_ids': [
2650+
(0, 0, {'product_id': compo.id, 'product_qty': 1}),
2651+
],
2652+
} for finished_product, compo in [(grandparent, parent), (parent, child), (child, component)]])
2653+
2654+
none_production = self.env['mrp.production']
2655+
for steps, case_description, in [('mrp_one_step', '1-step Manufacturing'), ('pbm', '2-steps Manufacturing'), ('pbm_sam', '3-steps Manufacturing')]:
2656+
warehouse.manufacture_steps = steps
2657+
2658+
grandparent_production_form = Form(self.env['mrp.production'])
2659+
grandparent_production_form.product_id = grandparent
2660+
grandparent_production = grandparent_production_form.save()
2661+
grandparent_production.action_confirm()
2662+
2663+
child_production, parent_production = self.env['mrp.production'].search([('product_id', 'in', (parent + child).ids)], order='id desc', limit=2)
2664+
2665+
for source_mo, mo, product, child_mo in [(none_production, grandparent_production, grandparent, parent_production),
2666+
(grandparent_production, parent_production, parent, child_production),
2667+
(parent_production, child_production, child, none_production)]:
2668+
2669+
self.assertEqual(mo.product_id, product, '[%s] There should be a MO for product %s' % (case_description, product.display_name))
2670+
self.assertEqual(mo.mrp_production_source_count, len(source_mo), '[%s] Incorrect value for product %s' % (case_description, product.display_name))
2671+
self.assertEqual(mo.mrp_production_child_count, len(child_mo), '[%s] Incorrect value for product %s' % (case_description, product.display_name))
2672+
2673+
source_action = mo.action_view_mrp_production_sources()
2674+
child_action = mo.action_view_mrp_production_childs()
2675+
self.assertEqual(source_action.get('res_id', False), source_mo.id, '[%s] Incorrect value for product %s' % (case_description, product.display_name))
2676+
self.assertEqual(child_action.get('res_id', False), child_mo.id, '[%s] Incorrect value for product %s' % (case_description, product.display_name))

0 commit comments

Comments
 (0)