[FIX] sale_stock: avoid traceback when returning a product
Steps to reproduce the bug:
- Create a storable product “P1”:
- Tracking: SN
- Update the qty: 1 unit, “SN1”
- Create a SO with and validate it
- Go to the delivery and validate it
- Create a return and validate
- Create a return of return
Problem:
error message: RecursionError: maximum recursion depth exceeded while
calling a Python object
The `_get_origin_moves` and `_get_destination_moves` functions add the
list of origin or destination moves, but the functions themselves are
called with that list, creating an infinite loop.
opw-3659818
closes odoo/odoo#148031
Related: odoo/enterprise#53695
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
This commit is contained in:
@@ -46,20 +46,9 @@ class StockMove(models.Model):
|
||||
return self.sale_line_id.order_id or res
|
||||
|
||||
def _get_sale_order_lines(self):
|
||||
""" Return all possible sale order lines for one or multiple stock moves. """
|
||||
def _get_origin_moves(move):
|
||||
origin_moves = move.move_orig_ids
|
||||
if origin_moves:
|
||||
origin_moves += _get_origin_moves(origin_moves)
|
||||
return origin_moves
|
||||
|
||||
def _get_destination_moves(move):
|
||||
destination_moves = move.move_dest_ids
|
||||
if destination_moves:
|
||||
destination_moves += _get_destination_moves(destination_moves)
|
||||
return destination_moves
|
||||
|
||||
return (self + _get_origin_moves(self) + _get_destination_moves(self)).sale_line_id
|
||||
""" Return all possible sale order lines for one stock move. """
|
||||
self.ensure_one()
|
||||
return (self + self.browse(self._rollup_move_origs() | self._rollup_move_dests())).sale_line_id
|
||||
|
||||
def _assign_picking_post_process(self, new=False):
|
||||
super(StockMove, self)._assign_picking_post_process(new=new)
|
||||
|
||||
@@ -1605,3 +1605,51 @@ class TestSaleStock(TestSaleCommon, ValuationReconciliationTestCommon):
|
||||
{'product_id': self.product_a.id, 'product_uom_qty': 0, 'quantity': 0, 'state': 'cancel'},
|
||||
{'product_id': self.product_b.id, 'product_uom_qty': 1, 'quantity': 0, 'state': 'waiting'},
|
||||
])
|
||||
|
||||
def test_create_so_return_with_tracked_product(self):
|
||||
"""
|
||||
Creates a sale order with a tracked product, validates it and its delivery, then creates a
|
||||
return validates it and finally creates a second return.
|
||||
"""
|
||||
self.product_a.tracking = 'serial'
|
||||
self.product_a.type = 'product'
|
||||
sn1 = self.env['stock.lot'].create({
|
||||
'name': 'SN0001',
|
||||
'product_id': self.product_a.id,
|
||||
'company_id': self.env.company.id,
|
||||
})
|
||||
self.env['stock.quant']._update_available_quantity(self.product_a, self.company_data['default_warehouse'].lot_stock_id, 1, lot_id=sn1)
|
||||
# Creates a sale order for 1 tracked product.
|
||||
sale_order = self._get_new_sale_order(amount=1, product=self.product_a)
|
||||
# validates the sale order, then validates the delivery.
|
||||
sale_order.action_confirm()
|
||||
self.assertTrue(sale_order.picking_ids)
|
||||
picking = sale_order.picking_ids
|
||||
picking.button_validate()
|
||||
|
||||
# Checks the delivery amount (must be 1).
|
||||
self.assertEqual(sale_order.order_line.qty_delivered, 1)
|
||||
# Creates a return from the delivery picking.
|
||||
return_picking_form = Form(self.env['stock.return.picking']
|
||||
.with_context(active_ids=picking.ids, active_id=picking.id,
|
||||
active_model='stock.picking'))
|
||||
return_wizard = return_picking_form.save()
|
||||
self.assertEqual(return_wizard.product_return_moves.quantity, 1)
|
||||
|
||||
# validates the return picking.
|
||||
res = return_wizard.create_returns()
|
||||
return_picking = self.env['stock.picking'].browse(res['res_id'])
|
||||
return_picking.button_validate()
|
||||
# Checks the delivery amount (must be 0).
|
||||
self.assertEqual(sale_order.order_line.qty_delivered, 0)
|
||||
return_picking_form = Form(self.env['stock.return.picking']
|
||||
.with_context(active_ids=return_picking.ids, active_id=return_picking.id,
|
||||
active_model='stock.picking'))
|
||||
return_wizard = return_picking_form.save()
|
||||
self.assertEqual(return_wizard.product_return_moves.quantity, 1)
|
||||
|
||||
# validates the return picking.
|
||||
res = return_wizard.create_returns()
|
||||
return_picking_2 = self.env['stock.picking'].browse(res['res_id'])
|
||||
return_picking_2.button_validate()
|
||||
self.assertEqual(return_wizard.product_return_moves.quantity, 1)
|
||||
|
||||
@@ -2143,6 +2143,7 @@ Please change the quantity done or the rounding precision of your unit of measur
|
||||
moves_to_reserve._action_assign()
|
||||
|
||||
def _rollup_move_dests(self, seen=False):
|
||||
self.ensure_one()
|
||||
if not seen:
|
||||
seen = OrderedSet()
|
||||
if self.id in seen:
|
||||
@@ -2153,6 +2154,7 @@ Please change the quantity done or the rounding precision of your unit of measur
|
||||
return seen
|
||||
|
||||
def _rollup_move_origs(self, seen=False):
|
||||
self.ensure_one()
|
||||
if not seen:
|
||||
seen = OrderedSet()
|
||||
if self.id in seen:
|
||||
|
||||
Reference in New Issue
Block a user