summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/sale_stock/models/sale_order.py38
-rw-r--r--addons/sale_stock/tests/test_sale_stock.py66
2 files changed, 92 insertions, 12 deletions
diff --git a/addons/sale_stock/models/sale_order.py b/addons/sale_stock/models/sale_order.py
index b7a9a1620d1..45c2bf5432c 100644
--- a/addons/sale_stock/models/sale_order.py
+++ b/addons/sale_stock/models/sale_order.py
@@ -210,12 +210,15 @@ class SaleOrderLine(models.Model):
for line in self: # TODO: maybe one day, this should be done in SQL for performance sake
if line.qty_delivered_method == 'stock_move':
qty = 0.0
- for move in line.move_ids.filtered(lambda r: r.state == 'done' and not r.scrapped and line.product_id == r.product_id):
- if move.location_dest_id.usage == "customer":
- if not move.origin_returned_move_id or (move.origin_returned_move_id and move.to_refund):
- qty += move.product_uom._compute_quantity(move.product_uom_qty, line.product_uom)
- elif move.location_dest_id.usage != "customer" and move.to_refund:
- qty -= move.product_uom._compute_quantity(move.product_uom_qty, line.product_uom)
+ outgoing_moves, incoming_moves = line._get_outgoing_incoming_moves()
+ for move in outgoing_moves:
+ if move.state != 'done':
+ continue
+ qty += move.product_uom._compute_quantity(move.product_uom_qty, line.product_uom, rounding_method='HALF-UP')
+ for move in incoming_moves:
+ if move.state != 'done':
+ continue
+ qty -= move.product_uom._compute_quantity(move.product_uom_qty, line.product_uom, rounding_method='HALF-UP')
line.qty_delivered = qty
@api.model_create_multi
@@ -355,13 +358,26 @@ class SaleOrderLine(models.Model):
def _get_qty_procurement(self, previous_product_uom_qty=False):
self.ensure_one()
qty = 0.0
- for move in self.move_ids.filtered(lambda r: r.state != 'cancel'):
- if move.picking_code == 'outgoing':
- qty += move.product_uom._compute_quantity(move.product_uom_qty, self.product_uom, rounding_method='HALF-UP')
- elif move.picking_code == 'incoming':
- qty -= move.product_uom._compute_quantity(move.product_uom_qty, self.product_uom, rounding_method='HALF-UP')
+ outgoing_moves, incoming_moves = self._get_outgoing_incoming_moves()
+ for move in outgoing_moves:
+ qty += move.product_uom._compute_quantity(move.product_uom_qty, self.product_uom, rounding_method='HALF-UP')
+ for move in incoming_moves:
+ qty -= move.product_uom._compute_quantity(move.product_uom_qty, self.product_uom, rounding_method='HALF-UP')
return qty
+ def _get_outgoing_incoming_moves(self):
+ outgoing_moves = self.env['stock.move']
+ incoming_moves = self.env['stock.move']
+
+ for move in self.move_ids.filtered(lambda r: r.state != 'cancel' and not r.scrapped and self.product_id == r.product_id):
+ if move.location_dest_id.usage == "customer":
+ if not move.origin_returned_move_id or (move.origin_returned_move_id and move.to_refund):
+ outgoing_moves |= move
+ elif move.location_dest_id.usage != "customer" and move.to_refund:
+ incoming_moves |= move
+
+ return outgoing_moves, incoming_moves
+
def _get_procurement_group(self):
return self.order_id.procurement_group_id
diff --git a/addons/sale_stock/tests/test_sale_stock.py b/addons/sale_stock/tests/test_sale_stock.py
index 47fd2408bd3..a6b6c26940d 100644
--- a/addons/sale_stock/tests/test_sale_stock.py
+++ b/addons/sale_stock/tests/test_sale_stock.py
@@ -494,4 +494,68 @@ class TestSaleStock(TestSale):
})
so1.picking_ids.button_validate()
self.assertEqual(so1.picking_ids.state, 'done')
- self.assertEqual(so1.order_line.mapped('qty_delivered'), [1, 1, 1]) \ No newline at end of file
+ self.assertEqual(so1.order_line.mapped('qty_delivered'), [1, 1, 1])
+
+ def test_08_quantities(self):
+ """Change the picking code of the receipts to internal. Make a SO for 10 units, go to the
+ picking and return 5, edit the SO line to 15 units.
+
+ The purpose of the test is to check the consistencies across the delivered quantities and the
+ procurement quantities.
+ """
+ # Change the code of the picking type receipt
+ self.env['stock.picking.type'].search([('code', '=', 'incoming')]).write({'code': 'internal'})
+
+ # Sell and deliver 10 units
+ item1 = self.products['prod_order']
+ uom_unit = self.env.ref('uom.product_uom_unit')
+ so1 = self.env['sale.order'].create({
+ 'partner_id': self.partner.id,
+ 'order_line': [
+ (0, 0, {
+ 'name': item1.name,
+ 'product_id': item1.id,
+ 'product_uom_qty': 10,
+ 'product_uom': uom_unit.id,
+ 'price_unit': item1.list_price,
+ }),
+ ],
+ })
+ so1.action_confirm()
+
+ picking = so1.picking_ids
+ wiz_act = picking.button_validate()
+ wiz = self.env[wiz_act['res_model']].browse(wiz_act['res_id'])
+ wiz.process()
+
+ # Return 5 units
+ stock_return_picking_form = Form(self.env['stock.return.picking'].with_context(
+ active_ids=picking.ids,
+ active_id=picking.ids[0],
+ active_model='stock.picking'
+ ))
+ return_wiz = stock_return_picking_form.save()
+ for return_move in return_wiz.product_return_moves:
+ return_move.write({
+ 'quantity': 5,
+ 'to_refund': True
+ })
+ res = return_wiz.create_returns()
+ return_pick = self.env['stock.picking'].browse(res['res_id'])
+ wiz_act = return_pick.button_validate()
+ wiz = self.env[wiz_act['res_model']].browse(wiz_act['res_id'])
+ wiz.process()
+
+ self.assertEqual(so1.order_line.qty_delivered, 5)
+
+ # Deliver 15 instead of 10.
+ so1.write({
+ 'order_line': [
+ (1, so1.order_line[0].id, {'product_uom_qty': 15}),
+ ]
+ })
+
+ # A new move of 10 unit (15 - 5 units)
+ self.assertEqual(so1.order_line.qty_delivered, 5)
+ self.assertEqual(so1.picking_ids[-1].move_lines.product_qty, 10)
+