[FIX] mrp_subcontracting_dropshipping: dropship from PO

Confirming a PO with a customer as delivery address does not work

To reproduce the issue:
1. In Settings, enable "Multi-Locations"
2. Create two products P_finished, P_compo
    - P_finished:
        - Add a vendor V
3. Create a bill of materials:
    - Product: P_finished
    - Type: Subcontract
    - Subcontractor: V
    - Components: 1 x P_compo
4. Create a PO:
    - Vendor: V
    - Products: 1 x P_finished
    - Deliver To: Dropship
    - Drop Ship Address: a customer
5. Confirm the PO

Error: a validation error is displayed: "[...] a mandatory field is not
set. [...] Model: Production Order (mrp.production), Field: Operation
Type (picking_type_id)"

When confirming the PO, we extract some values of the subcontracted SM
to create a MO:
https://github.com/odoo/odoo/blob/829369d3ca0530f1aad0599fbb1598810a928cc3/addons/mrp_subcontracting/models/stock_picking.py#L105-L117
However, because the SM is not created from the SO, it does not have any
`sale_line_id`. And, because its a dropshipped one, neither the SM nor
its picking type has a defined warehouse. As a result, `_get_warehouse`
does not return anything and we can't define the `picking_type_id` of
the MO. This is the reason why the error will be triggered on its
creation.

OPW-2922546

closes odoo/odoo#99223

X-original-commit: 28a20daab6a7097ecd26bf66eeb80b2f5ad294a8
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
Signed-off-by: Adrien Widart <awt@odoo.com>
This commit is contained in:
Adrien Widart
2022-08-31 09:31:58 +02:00
parent bec6cefd0c
commit 4ff8654bcb
2 changed files with 70 additions and 0 deletions
@@ -16,3 +16,15 @@ class StockPicking(models.Model):
res = super()._action_done()
self.move_ids.move_dest_ids._action_assign()
return res
def _prepare_subcontract_mo_vals(self, subcontract_move, bom):
res = super()._prepare_subcontract_mo_vals(subcontract_move, bom)
if not res.get('picking_type_id') and subcontract_move.location_dest_id.usage == 'customer':
# If the if-condition is respected, it means that `subcontract_move` is not
# related to a specific warehouse. This can happen if, for instance, the user
# confirms a PO with a subcontracted product that should be delivered to a
# customer (dropshipping). In that case, we can use a default warehouse to
# get the picking type
default_warehouse = self.env['stock.warehouse'].search([('company_id', '=', subcontract_move.company_id.id)], limit=1)
res['picking_type_id'] = default_warehouse.subcontracting_type_id.id,
return res
@@ -190,3 +190,61 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
dropship_po = self.env['purchase.order'].search([('partner_id', '=', vendor.id)])
self.assertEqual(dropship_po.dest_address_id, subcontractor)
def test_po_to_customer(self):
"""
Create and confirm a PO with a subcontracted move. The picking type of
the PO is 'Dropship' and the delivery address a customer.
"""
subcontractor, client = self.env['res.partner'].create([
{'name': 'SuperSubcontractor'},
{'name': 'SuperClient'},
])
p_finished, p_compo = self.env['product.product'].create([{
'name': 'Finished Product',
'type': 'product',
'seller_ids': [(0, 0, {'partner_id': subcontractor.id})],
}, {
'name': 'Component',
'type': 'consu',
}])
bom = self.env['mrp.bom'].create({
'product_tmpl_id': p_finished.product_tmpl_id.id,
'product_qty': 1,
'type': 'subcontract',
'subcontractor_ids': [(6, 0, subcontractor.ids)],
'bom_line_ids': [
(0, 0, {'product_id': p_compo.id, 'product_qty': 1}),
],
})
dropship_picking_type = self.env['stock.picking.type'].search([
('company_id', '=', self.env.company.id),
('default_location_src_id.usage', '=', 'supplier'),
('default_location_dest_id.usage', '=', 'customer'),
], limit=1, order='sequence')
po = self.env['purchase.order'].create({
"partner_id": subcontractor.id,
"picking_type_id": dropship_picking_type.id,
"dest_address_id": client.id,
"order_line": [(0, 0, {
'product_id': p_finished.id,
'name': p_finished.name,
'product_qty': 1.0,
})],
})
po.button_confirm()
mo = self.env['mrp.production'].search([('bom_id', '=', bom.id)])
self.assertEqual(mo.picking_type_id, self.warehouse.subcontracting_type_id)
delivery = po.picking_ids
delivery.move_line_ids.qty_done = 1.0
delivery.button_validate()
self.assertEqual(delivery.state, 'done')
self.assertEqual(mo.state, 'done')
self.assertEqual(po.order_line.qty_received, 1)