[FIX] mrp_subcontracting: validate a partial picking
When validating a receipt, if the product is subcontracted and if the
picking is not fully done, it will be impossible to create a backorder
To reproduce the issue:
1. Create two products P_compo, P_finished
- Both storable
- Both tracked by lot
- P_compo must have the route "Resupply Subcontractor on Order"
2. Update P_compo's quantity: 4
3. Create a BoM:
- Product: P_finished
- BoM type: Subcontracting
- Subcontractors: a partner P
- Components: 1 x P_compo
4. In Inventory, create a planned transfer T:
- Operation Type: Receipt
- Receive From: P
- Operations: 4 x P_finished
5. Mark as Todo
6. Inventory > Delivery Orders, find the delivery of P_compo for P and
process it
7. Back to T, Record Components:
- Quantity: 3/4
- Set a Lot for P_finished
8. Validate T, Create Backorder
Error: a User Error is raised "You need to supply a Lot/Serial Number
for product: - P_finished"
Since P_finished is subcontracted, a related MO has been generated. On
step 7, when recording the used components, since all P_finished have
not been produced, a second MO is created for the last P_finished.
However, when validating T, both MOs are selected and validated. This is
an error since the user has not yet recorded the used components for the
last MO. The latter should not be validated.
OPW-2582538
closes odoo/odoo#78496
X-original-commit: 21068076b4a6b134f5f07c4c03de7c4eb37b4688
Signed-off-by: William Henrotin <Whenrow@users.noreply.github.com>
This commit is contained in:
committed by
William Henrotin
parent
bac2a86ce2
commit
67e19a5dc7
@@ -62,7 +62,7 @@ class MrpProduction(models.Model):
|
||||
backorder._set_qty_producing()
|
||||
|
||||
self.product_qty = self.qty_producing
|
||||
action = self._get_subcontract_move()._action_record_components()
|
||||
action = self._get_subcontract_move().filtered(lambda m: m.state not in ('done', 'cancel'))._action_record_components()
|
||||
action['res_id'] = backorder.id
|
||||
return action
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
@@ -75,7 +75,7 @@ class MrpProduction(models.Model):
|
||||
def _update_finished_move(self):
|
||||
""" After producing, set the move line on the subcontract picking. """
|
||||
self.ensure_one()
|
||||
subcontract_move_id = self._get_subcontract_move()
|
||||
subcontract_move_id = self._get_subcontract_move().filtered(lambda m: m.state not in ('done', 'cancel'))
|
||||
if subcontract_move_id:
|
||||
quantity = self.qty_producing
|
||||
if self.lot_producing_id:
|
||||
@@ -141,6 +141,8 @@ class MrpProduction(models.Model):
|
||||
return False
|
||||
if not all(line.lot_id for line in mo.move_raw_ids.filtered(lambda sm: sm.has_tracking != 'none').move_line_ids):
|
||||
return False
|
||||
if mo.product_id.tracking != 'none' and not mo.lot_producing_id:
|
||||
return False
|
||||
return True
|
||||
|
||||
return self.filtered(filter_in)
|
||||
|
||||
@@ -836,3 +836,75 @@ class TestSubcontractingTracking(TransactionCase):
|
||||
self.assertEqual(avail_qty_comp1, -todo_nb)
|
||||
self.assertEqual(avail_qty_comp2, -todo_nb)
|
||||
self.assertEqual(avail_qty_finished, todo_nb)
|
||||
|
||||
def test_flow_tracked_backorder02(self):
|
||||
""" Both component and finished product are tracked by lot. """
|
||||
todo_nb = 4
|
||||
resupply_sub_on_order_route = self.env['stock.location.route'].search([('name', '=', 'Resupply Subcontractor on Order')])
|
||||
finished_product, component = self.env['product.product'].create([{
|
||||
'name': 'SuperProduct',
|
||||
'type': 'product',
|
||||
'tracking': 'lot',
|
||||
}, {
|
||||
'name': 'Component',
|
||||
'type': 'product',
|
||||
'tracking': 'lot',
|
||||
'route_ids': [(4, resupply_sub_on_order_route.id)],
|
||||
}])
|
||||
|
||||
bom_form = Form(self.env['mrp.bom'])
|
||||
bom_form.type = 'subcontract'
|
||||
bom_form.subcontractor_ids.add(self.subcontractor_partner1)
|
||||
bom_form.product_tmpl_id = finished_product.product_tmpl_id
|
||||
with bom_form.bom_line_ids.new() as bom_line:
|
||||
bom_line.product_id = component
|
||||
bom_line.product_qty = 1
|
||||
bom = bom_form.save()
|
||||
|
||||
finished_lot, component_lot = self.env['stock.production.lot'].create([{
|
||||
'name': 'lot_%s' % product.name,
|
||||
'product_id': product.id,
|
||||
'company_id': self.env.company.id,
|
||||
} for product in [finished_product, component]])
|
||||
|
||||
self.env['stock.quant']._update_available_quantity(component, self.env.ref('stock.stock_location_stock'), todo_nb, lot_id=component_lot)
|
||||
|
||||
# Create a receipt picking from the subcontractor
|
||||
picking_form = Form(self.env['stock.picking'])
|
||||
picking_form.picking_type_id = self.env.ref('stock.picking_type_in')
|
||||
picking_form.partner_id = self.subcontractor_partner1
|
||||
with picking_form.move_ids_without_package.new() as move:
|
||||
move.product_id = finished_product
|
||||
move.product_uom_qty = todo_nb
|
||||
picking_receipt = picking_form.save()
|
||||
picking_receipt.action_confirm()
|
||||
|
||||
mo = self.env['mrp.production'].search([('bom_id', '=', bom.id)])
|
||||
|
||||
# Process the delivery of the components
|
||||
compo_picking = mo.picking_ids
|
||||
compo_picking.action_assign()
|
||||
wizard_data = compo_picking.button_validate()
|
||||
wizard = Form(self.env[wizard_data['res_model']].with_context(wizard_data['context'])).save()
|
||||
wizard.process()
|
||||
|
||||
for qty in [3, 1]:
|
||||
# Record the receiption of <qty> finished products
|
||||
picking_receipt = self.env['stock.picking'].search([('partner_id', '=', self.subcontractor_partner1.id), ('state', '!=', 'done')])
|
||||
action = picking_receipt.action_record_components()
|
||||
mo = self.env['mrp.production'].browse(action['res_id'])
|
||||
mo_form = Form(mo.with_context(**action['context']), view=action['view_id'])
|
||||
mo_form.qty_producing = qty
|
||||
mo_form.lot_producing_id = finished_lot
|
||||
with mo_form.move_line_raw_ids.edit(0) as ml:
|
||||
ml.lot_id = component_lot
|
||||
mo = mo_form.save()
|
||||
mo.subcontracting_record_component()
|
||||
|
||||
# Validate the picking and create a backorder
|
||||
wizard_data = picking_receipt.button_validate()
|
||||
if qty == 3:
|
||||
wizard = Form(self.env[wizard_data['res_model']].with_context(wizard_data['context'])).save()
|
||||
wizard.process()
|
||||
|
||||
self.assertEqual(picking_receipt.state, 'done')
|
||||
|
||||
Reference in New Issue
Block a user