[FIX] stock, mrp: merge SFP for 2 different MO

usecase to reproduce:
- Set the warehouse with manufacturing 3 steps
- Create a BoM with a Components that use a Sub BoM
- Set Both product MTO + Manufacture
- Create a MO

You have only 2 picking for the first MO with the SFP
that contains both the finished product and the intermediate
component. However they are created and moved at two different times
since you could not create the finished product withtout the
component, so there is no meaning to put them both in the same picking.

It happens because the _assign_picking will merge moves with the same
procurement group. In our case, it's the wanted behavior to have the
2 differents SFP linked to the same MO. So the solution would be to
add a condition to _assign_picking and don't merge moves if they come
from different MO, at this point the field created_production_id would
fit. However this solution is not robust if a customer creates a
new rule that add a 4th steps to the production process.

opw-2055688

closes odoo/odoo#35915

Signed-off-by: Nicolas Martinelli (nim) <nim@odoo.com>


Co-authored-by: Andrea Grazioso (agr-odoo) <agr@odoo.com>
This commit is contained in:
Arnold Moyaux
2019-08-27 11:32:54 +00:00
committed by Andrea Grazioso (agr-odoo)
co-authored by Andrea Grazioso
parent 906480093a
commit 40487aca5c
3 changed files with 109 additions and 4 deletions
+6
View File
@@ -283,3 +283,9 @@ class StockMove(models.Model):
def _should_be_assigned(self):
res = super(StockMove, self)._should_be_assigned()
return bool(res and not (self.production_id or self.raw_material_production_id))
def _search_picking_for_assignation_domain(self):
res = super(StockMove, self)._search_picking_for_assignation_domain()
if self.created_production_id:
res.append(('move_lines.created_production_id', '=', self.created_production_id.id))
return res
@@ -121,6 +121,100 @@ class TestMultistepManufacturingWarehouse(TestMrpCommon):
self.assertEqual(sam_move.picking_type_id, self.warehouse.sam_type_id)
self.assertFalse(sam_move.move_dest_ids)
def test_manufacturing_complex_product_3_steps(self):
""" Test MO/picking after manufacturing a complex product which uses
manufactured components. Ensure that everything is created and picked
correctly.
"""
# Create manifactured product which uses another manifactured
product_form = Form(self.env['product.product'])
product_form.name = 'Arrow'
product_form.type = 'product'
product_form.route_ids.clear()
product_form.route_ids.add(self.warehouse.manufacture_pull_id.route_id)
product_form.route_ids.add(self.warehouse.mto_pull_id.route_id)
self.complex_product = product_form.save()
## Create raw product for manufactured product
product_form = Form(self.env['product.product'])
product_form.name = 'Raw Iron'
product_form.type = 'product'
product_form.uom_id = self.uom_unit
product_form.uom_po_id = self.uom_unit
self.raw_product_2 = product_form.save()
## Create bom for manufactured product
bom_product_form = Form(self.env['mrp.bom'])
bom_product_form.product_id = self.complex_product
bom_product_form.product_tmpl_id = self.complex_product.product_tmpl_id
with bom_product_form.bom_line_ids.new() as line:
line.product_id = self.finished_product
line.product_qty = 1.0
with bom_product_form.bom_line_ids.new() as line:
line.product_id = self.raw_product_2
line.product_qty = 1.0
self.complex_bom = bom_product_form.save()
with Form(self.warehouse) as warehouse:
warehouse.manufacture_steps = 'pbm_sam'
production_form = Form(self.env['mrp.production'])
production_form.product_id = self.complex_product
production_form.picking_type_id = self.warehouse.manu_type_id
production = production_form.save()
move_raw_ids = production.move_raw_ids
self.assertEqual(len(move_raw_ids), 2)
sfp_move_raw_id, raw_move_raw_id = move_raw_ids
self.assertEqual(sfp_move_raw_id.product_id, self.finished_product)
self.assertEqual(raw_move_raw_id.product_id, self.raw_product_2)
for move_raw_id in move_raw_ids:
self.assertEqual(move_raw_id.picking_type_id, self.warehouse.manu_type_id)
pbm_move = move_raw_id.move_orig_ids
self.assertEqual(len(pbm_move), 1)
self.assertEqual(pbm_move.location_id, self.warehouse.lot_stock_id)
self.assertEqual(pbm_move.location_dest_id, self.warehouse.pbm_loc_id)
self.assertEqual(pbm_move.picking_type_id, self.warehouse.pbm_type_id)
move_finished_ids = production.move_finished_ids
self.assertEqual(len(move_finished_ids), 1)
self.assertEqual(move_finished_ids.product_id, self.complex_product)
self.assertEqual(move_finished_ids.picking_type_id, self.warehouse.manu_type_id)
sam_move = move_finished_ids.move_dest_ids
self.assertEqual(len(sam_move), 1)
self.assertEqual(sam_move.location_id, self.warehouse.sam_loc_id)
self.assertEqual(sam_move.location_dest_id, self.warehouse.lot_stock_id)
self.assertEqual(sam_move.picking_type_id, self.warehouse.sam_type_id)
self.assertFalse(sam_move.move_dest_ids)
pickings = production.picking_ids.sorted('id', reverse=True)
self.assertEqual(len(pickings), 3)
# Picking: SFP PostProcessing -> Stock
## Stick from Subprocess
picking = pickings[1]
self.assertEqual(len(picking.move_lines), 1)
picking.product_id = self.finished_product
# Picking: PC Stock -> Preprocessing
## Stick
## Raw Iron
picking = pickings[0]
self.assertEqual(len(picking.move_lines), 2)
picking.move_lines[0].product_id = self.finished_product
picking.move_lines[1].product_id = self.raw_product_2
# Picking: SFP PostProcessing -> Stock
## Arrow from this process
picking = pickings[2]
self.assertEqual(len(picking.move_lines), 1)
picking.product_id = self.complex_product
def test_manufacturing_flow(self):
""" Simulate a pick pack ship delivery combined with a picking before
manufacturing and store after manufacturing. Also ensure that the MO and
+9 -4
View File
@@ -672,15 +672,20 @@ class StockMove(models.Model):
}
}
def _search_picking_for_assignation(self):
self.ensure_one()
picking = self.env['stock.picking'].search([
def _search_picking_for_assignation_domain(self):
return [
('group_id', '=', self.group_id.id),
('location_id', '=', self.location_id.id),
('location_dest_id', '=', self.location_dest_id.id),
('picking_type_id', '=', self.picking_type_id.id),
('printed', '=', False),
('state', 'in', ['draft', 'confirmed', 'waiting', 'partially_available', 'assigned'])], limit=1)
('state', 'in', ['draft', 'confirmed', 'waiting',
'partially_available', 'assigned'])
]
def _search_picking_for_assignation(self):
self.ensure_one()
picking = self.env['stock.picking'].search(self._search_picking_for_assignation_domain(), limit=1)
return picking
def _assign_picking(self):