[FIX] stock: move line: decrease reserved quantity

When we decrease the reserved quantity, the argument 'strict' must be
set to 'True', else we might unreserve quants of another move line.
This behavior has been lost in rev: https://github.com/odoo/odoo/commit/9bb696e13abcbf95c9552fab60b1dfb997afaa59

We introduce a test to show where an issue happened without this
patch: have tracked and untracked quants for the same product in stock,
reserve them and edit the untracked move line in order to set a lot to
it. The first step will be to unreserve the move line without lot, and
if we do not pass a Strict kw, stock.quant's `_gather` will return
quants with lot and unreserve on them. Everything is now inconsistent
and lead to issues upon validation.
This commit is contained in:
Pierre Masereel
2017-07-26 16:42:00 +02:00
committed by Simon Lejeune
parent 50794f64af
commit 7a39ab50c5
3 changed files with 58 additions and 11 deletions
+7 -7
View File
@@ -132,10 +132,10 @@ class StockMoveLine(models.Model):
if ml.location_id.should_impact_quants() and ml.product_id.type == 'product':
qty_to_decrease = ml.product_qty - ml.product_uom_id._compute_quantity(vals['product_uom_qty'], ml.product_id.uom_id, rounding_method='HALF-UP')
try:
Quant._update_reserved_quantity(ml.product_id, ml.location_id, -qty_to_decrease, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id)
Quant._update_reserved_quantity(ml.product_id, ml.location_id, -qty_to_decrease, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id, strict=True)
except UserError:
if ml.lot_id:
Quant._update_reserved_quantity(ml.product_id, ml.location_id, -qty_to_decrease, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id)
Quant._update_reserved_quantity(ml.product_id, ml.location_id, -qty_to_decrease, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id, strict=True)
else:
raise
@@ -156,10 +156,10 @@ class StockMoveLine(models.Model):
for ml in self.filtered(lambda ml: ml.state in ['partially_available', 'assigned']):
if ml.location_id.should_impact_quants() and ml.product_id.type == 'product':
try:
Quant._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id)
Quant._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id, strict=True)
except UserError:
if ml.lot_id:
Quant._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id)
Quant._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id, strict=True)
else:
raise
@@ -233,7 +233,7 @@ class StockMoveLine(models.Model):
# Unlinking a pack operation should unreserve.
if ml.location_id.should_impact_quants() and ml.product_id.type == 'product' and not float_is_zero(ml.product_qty, precision_digits=precision):
self.env['stock.quant']._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=ml.lot_id,
package_id=ml.package_id, owner_id=ml.owner_id)
package_id=ml.package_id, owner_id=ml.owner_id, strict=True)
return super(StockMoveLine, self).unlink()
def action_done(self):
@@ -298,9 +298,9 @@ class StockMoveLine(models.Model):
# unreserve what's been reserved
if ml.location_id.should_impact_quants() and ml.product_id.type == 'product' and ml.product_qty:
try:
Quant._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id)
Quant._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id, strict=True)
except UserError:
Quant._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id)
Quant._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id, strict=True)
# move what's been actually done
quantity = ml.product_uom_id._compute_quantity(ml.qty_done, ml.move_id.product_id.uom_id, rounding_method='HALF-UP')
+47
View File
@@ -544,6 +544,53 @@ class StockMove(TransactionCase):
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product2, self.stock_location), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product2, self.stock_location)), 0.0)
def test_mixed_tracking_reservation_7(self):
""" Similar test_mixed_tracking_reservation_2 but creates first the tracked quant, then the
untracked ones. When adding a lot to the untracked move line, it should not decrease the
untracked quant then increase a non-existing tracked one that will fallback on the
untracked quant.
"""
lot1 = self.env['stock.production.lot'].create({
'name': 'lot1',
'product_id': self.product2.id,
})
lot2 = self.env['stock.production.lot'].create({
'name': 'lot2',
'product_id': self.product2.id,
})
self.env['stock.quant']._update_available_quantity(self.product2, self.stock_location, 1, lot_id=lot1)
self.env['stock.quant']._update_available_quantity(self.product2, self.stock_location, 1)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product2, self.stock_location), 2.0)
# creation
move1 = self.env['stock.move'].create({
'name': 'test_in_1',
'location_id': self.stock_location.id,
'location_dest_id': self.customer_location.id,
'product_id': self.product2.id,
'product_uom': self.uom_unit.id,
'product_uom_qty': 2.0,
})
move1.action_confirm()
move1.action_assign()
self.assertEqual(len(move1.move_line_ids), 2)
for ml in move1.move_line_ids:
self.assertEqual(ml.product_qty, 1.0)
untracked_move_line = move1.move_line_ids.filtered(lambda ml: not ml.lot_id).lot_id = lot2
for ml in move1.move_line_ids:
self.assertEqual(ml.product_qty, 1.0)
move1.move_line_ids.write({'qty_done': 1.0})
move1.action_done()
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product2, self.stock_location), 0.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product2, self.stock_location, lot_id=lot1, strict=True), 0.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product2, self.stock_location, lot_id=lot2, strict=True), 0.0)
quants = self.env['stock.quant']._gather(self.product2, self.stock_location)
self.assertEqual(len(quants), 0)
def test_putaway_1(self):
""" Receive products from a supplier. Check that putaway rules are rightly applied on
the receipt move line.
+4 -4
View File
@@ -475,7 +475,7 @@ class StockQuant(TransactionCase):
})
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(product1, stock_location)), 1)
self.env['stock.quant']._update_reserved_quantity(product1, stock_location, -10.0)
self.env['stock.quant']._update_reserved_quantity(product1, stock_location, -10.0, strict=True)
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location), 10.0)
self.assertEqual(len(self.env['stock.quant']._gather(product1, stock_location)), 1)
@@ -491,7 +491,7 @@ class StockQuant(TransactionCase):
self.env['stock.quant']._update_reserved_quantity(product1, stock_location, 1.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location), 0.0)
with self.assertRaises(UserError):
self.env['stock.quant']._update_reserved_quantity(product1, stock_location, -1.0)
self.env['stock.quant']._update_reserved_quantity(product1, stock_location, -1.0, strict=True)
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location), 0.0)
def test_action_done_1(self):
@@ -505,7 +505,7 @@ class StockQuant(TransactionCase):
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location), 2.0)
self.env['stock.quant']._update_reserved_quantity(product1, stock_location, 2.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location), 0.0)
self.env['stock.quant']._update_reserved_quantity(product1, stock_location, -2.0)
self.env['stock.quant']._update_reserved_quantity(product1, stock_location, -2.0, strict=True)
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location), 2.0)
self.env['stock.quant']._update_available_quantity(product1, stock_location, -2.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location), 0.0)
@@ -545,7 +545,7 @@ class StockQuant(TransactionCase):
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location, lot_id=lot1), 1.0)
with self.assertRaises(UserError):
self.env['stock.quant']._update_reserved_quantity(product1, stock_location, -1.0)
self.env['stock.quant']._update_reserved_quantity(product1, stock_location, -1.0, strict=True)
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location), 2.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location, strict=True), 1.0)