[FIX] mrp_subcontracting_dropshipping,purchase_stock: return to supplier
When returning a dropshipped and subcontracted product to the supplier
location, the received quantity of the PO line will be incorrect
To reproduce the issue:
1. Create two storable products P_compo, P_Finished
2. Create a BoM:
- Product: P_finished
- Type: Subcontracting
- Subcontractors: a subcontractor S
- Components: 1 x P_compo
3. Create and confirm a PO:
- Vendor: S
- Deliver To: Dropship
- Drop Ship Address: a partner P
- Products: 1 x P_finished
4. Validate the receipt
5. Create a return with 1 x P_finished:
- Update SO/PO quantities: True
- Return Location: Partner Locations/Vendors
6. Validate the return
7. Go back to the PO
Error: The qty received is 2, it should be 0
There is currently no code to handle the return of a dropshipped product
OPW-3030895
closes odoo/odoo#105863
X-original-commit: 0a03678889d46dfc39922c0df965c06457f28d79
Signed-off-by: Adrien Widart <awt@odoo.com>
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
This commit is contained in:
@@ -7,6 +7,10 @@ from odoo import models
|
||||
class StockMove(models.Model):
|
||||
_inherit = "stock.move"
|
||||
|
||||
def _is_purchase_return(self):
|
||||
res = super()._is_purchase_return()
|
||||
return res or self._is_dropshipped_returned()
|
||||
|
||||
def _is_dropshipped(self):
|
||||
res = super()._is_dropshipped()
|
||||
return res or (
|
||||
|
||||
@@ -195,7 +195,8 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
|
||||
"""
|
||||
Create and confirm a PO with a subcontracted move. The picking type of
|
||||
the PO is 'Dropship' and the delivery address a customer. Then, process
|
||||
a return with the stock location as destination
|
||||
a return with the stock location as destination and another return with
|
||||
the supplier as destination
|
||||
"""
|
||||
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
|
||||
self.env.user.write({'groups_id': [(4, grp_multi_loc.id)]})
|
||||
@@ -237,7 +238,7 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
|
||||
"order_line": [(0, 0, {
|
||||
'product_id': p_finished.id,
|
||||
'name': p_finished.name,
|
||||
'product_qty': 1.0,
|
||||
'product_qty': 2.0,
|
||||
})],
|
||||
})
|
||||
po.button_confirm()
|
||||
@@ -246,27 +247,46 @@ class TestSubcontractingDropshippingFlows(TestMrpSubcontractingCommon):
|
||||
self.assertEqual(mo.picking_type_id, self.warehouse.subcontracting_type_id)
|
||||
|
||||
delivery = po.picking_ids
|
||||
delivery.move_line_ids.qty_done = 1.0
|
||||
delivery.move_line_ids.qty_done = 2.0
|
||||
delivery.button_validate()
|
||||
|
||||
self.assertEqual(delivery.state, 'done')
|
||||
self.assertEqual(mo.state, 'done')
|
||||
self.assertEqual(po.order_line.qty_received, 1)
|
||||
self.assertEqual(po.order_line.qty_received, 2)
|
||||
|
||||
# return 1 x P_finished to the stock location
|
||||
stock_location = self.warehouse.lot_stock_id
|
||||
stock_location.return_location = True
|
||||
return_form = Form(self.env['stock.return.picking'].with_context(active_ids=delivery.ids, active_id=delivery.id, active_model='stock.picking'))
|
||||
with return_form.product_return_moves.edit(0) as line:
|
||||
line.quantity = 1.0
|
||||
return_form.location_id = stock_location
|
||||
return_wizard = return_form.save()
|
||||
return_picking_id, _pick_type_id = return_wizard._create_returns()
|
||||
|
||||
delivery_return = self.env['stock.picking'].browse(return_picking_id)
|
||||
delivery_return.move_line_ids.qty_done = 1.0
|
||||
delivery_return.button_validate()
|
||||
delivery_return01 = self.env['stock.picking'].browse(return_picking_id)
|
||||
delivery_return01.move_line_ids.qty_done = 1.0
|
||||
delivery_return01.button_validate()
|
||||
|
||||
self.assertEqual(delivery_return.state, 'done')
|
||||
self.assertEqual(delivery_return01.state, 'done')
|
||||
self.assertEqual(p_finished.qty_available, 1, 'One product has been returned to the stock location, so it should be available')
|
||||
self.assertEqual(po.order_line.qty_received, 1, 'One product has been returned to the stock location, so we should still consider it as received')
|
||||
self.assertEqual(po.order_line.qty_received, 2, 'One product has been returned to the stock location, so we should still consider it as received')
|
||||
|
||||
# return 1 x P_finished to the supplier location
|
||||
supplier_location = dropship_picking_type.default_location_src_id
|
||||
return_form = Form(self.env['stock.return.picking'].with_context(active_ids=delivery.ids, active_id=delivery.id, active_model='stock.picking'))
|
||||
with return_form.product_return_moves.edit(0) as line:
|
||||
line.quantity = 1.0
|
||||
return_form.location_id = supplier_location
|
||||
return_wizard = return_form.save()
|
||||
return_picking_id, _pick_type_id = return_wizard._create_returns()
|
||||
|
||||
delivery_return02 = self.env['stock.picking'].browse(return_picking_id)
|
||||
delivery_return02.move_line_ids.qty_done = 1.0
|
||||
delivery_return02.button_validate()
|
||||
|
||||
self.assertEqual(delivery_return02.state, 'done')
|
||||
self.assertEqual(po.order_line.qty_received, 1)
|
||||
|
||||
def test_po_to_subcontractor(self):
|
||||
"""
|
||||
|
||||
@@ -321,7 +321,7 @@ class PurchaseOrderLine(models.Model):
|
||||
# the PO. Therefore, we can skip them since they will be handled later on.
|
||||
for move in line._get_po_line_moves():
|
||||
if move.state == 'done':
|
||||
if move.location_dest_id.usage == "supplier":
|
||||
if move._is_purchase_return():
|
||||
if move.to_refund:
|
||||
total -= move.product_uom._compute_quantity(move.product_uom_qty, line.product_uom, rounding_method='HALF-UP')
|
||||
elif move.origin_returned_move_id and move.origin_returned_move_id._is_dropshipped() and not move._is_dropshipped_returned():
|
||||
@@ -330,17 +330,6 @@ class PurchaseOrderLine(models.Model):
|
||||
# receive the product physically in our stock. To avoid counting the
|
||||
# quantity twice, we do nothing.
|
||||
pass
|
||||
elif (
|
||||
move.location_dest_id.usage == "internal"
|
||||
and move.location_id.usage != "supplier"
|
||||
and move.warehouse_id
|
||||
and move.location_dest_id
|
||||
not in self.env["stock.location"].search(
|
||||
[("id", "child_of", move.warehouse_id.view_location_id.id)]
|
||||
)
|
||||
):
|
||||
if move.to_refund:
|
||||
total -= move.product_uom._compute_quantity(move.product_uom_qty, line.product_uom, rounding_method='HALF-UP')
|
||||
else:
|
||||
total += move.product_uom._compute_quantity(move.product_uom_qty, line.product_uom, rounding_method='HALF-UP')
|
||||
line._track_qty_received(total)
|
||||
|
||||
@@ -187,3 +187,12 @@ class StockMove(models.Model):
|
||||
raise UserError(
|
||||
_('Odoo is not able to generate the anglo saxon entries. The total valuation of %s is zero.') % related_aml.product_id.display_name)
|
||||
return valuation_price_unit_total, valuation_total_qty
|
||||
|
||||
def _is_purchase_return(self):
|
||||
self.ensure_one()
|
||||
return self.location_dest_id.usage == "supplier" or (
|
||||
self.location_dest_id.usage == "internal"
|
||||
and self.location_id.usage != "supplier"
|
||||
and self.warehouse_id
|
||||
and self.location_dest_id not in self.env["stock.location"].search([("id", "child_of", self.warehouse_id.view_location_id.id)])
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user