[REF] stock: _action_done and cache invalidation

`_action_done` could unlink a quant. This happens when moving a product from a
location to another one and makes null the quantity of this product in one of
theses locations. In this case, we unlink the quant because it is now useless
and could be confusing on reports based on quants.

The unwanted side effect is that unlinking a record will invalidate the cache. In
some pathologic cases, like making an inventory adjustment of 600 products
and reseting their quantity back to 0, the time of the operation is around 15
minutes. With this patch that tries to work carefully with the cache invalidation,
the same operations takes around 15 secondes.

This commit do not unlink quant anymore, instead it use a 'garbage
collector' on quants without quantity. The garbage collector is trigger
on the scheduler and when the user open the inventory view.
This commit is contained in:
Arnold Moyaux
2018-09-18 14:53:03 +02:00
parent 6a81e671ea
commit c0d7a05730
16 changed files with 146 additions and 117 deletions
@@ -140,7 +140,7 @@ class TestMultistepManufacturingWarehouse(TestMrpCommon):
picking_stock_preprod.move_line_ids.qty_done = 4
picking_stock_preprod.action_done()
self.assertFalse(self.env['stock.quant']._gather(self.raw_product, self.warehouse.lot_stock_id))
self.assertFalse(sum(self.env['stock.quant']._gather(self.raw_product, self.warehouse.lot_stock_id).mapped('quantity')))
self.assertTrue(self.env['stock.quant']._gather(self.raw_product, self.warehouse.pbm_loc_id))
production_order.action_assign()
@@ -156,7 +156,7 @@ class TestMultistepManufacturingWarehouse(TestMrpCommon):
product_produce.do_produce()
production_order.button_mark_done()
self.assertFalse(self.env['stock.quant']._gather(self.raw_product, self.warehouse.pbm_loc_id))
self.assertFalse(sum(self.env['stock.quant']._gather(self.raw_product, self.warehouse.pbm_loc_id).mapped('quantity')))
self.assertEqual(picking_stock_postprod.state, 'assigned')
+1 -1
View File
@@ -233,7 +233,7 @@ class Inventory(models.Model):
def _get_inventory_lines_values(self):
# TDE CLEANME: is sql really necessary ? I don't think so
locations = self.env['stock.location'].search([('id', 'child_of', [self.location_id.id])])
domain = ' location_id in %s'
domain = ' location_id in %s AND quantity != 0'
args = (tuple(locations.ids),)
vals = []
+1 -2
View File
@@ -1124,7 +1124,7 @@ class StockMove(models.Model):
except UserError:
pass
move._unreserve_initial_demand(new_move)
move.move_line_ids._action_done()
moves_todo.mapped('move_line_ids')._action_done()
# Check the consistency of the result packages; there should be an unique location across
# the contained quants.
for result_package in moves_todo\
@@ -1142,7 +1142,6 @@ class StockMove(models.Model):
if picking:
picking._create_backorder()
return moves_todo
def unlink(self):
+2 -1
View File
@@ -304,6 +304,7 @@ class StockMoveLine(models.Model):
if not location_id.should_bypass_reservation():
ml._free_reservation(ml.product_id, location_id, untracked_qty, lot_id=False, package_id=package_id, owner_id=owner_id)
Quant._update_available_quantity(product_id, location_dest_id, quantity, lot_id=lot_id, package_id=result_package_id, owner_id=owner_id, in_date=in_date)
# Unreserve and reserve following move in order to have the real reserved quantity on move_line.
next_moves |= ml.move_id.move_dest_ids.filtered(lambda move: move.state not in ('done', 'cancel'))
@@ -359,6 +360,7 @@ class StockMoveLine(models.Model):
intended to be called when editing a `done` move (that's what the override of `write` here
is done.
"""
Quant = self.env['stock.quant']
# First, we loop over all the move lines to do a preliminary check: `qty_done` should not
# be negative and, according to the presence of a picking type or a linked inventory
@@ -412,7 +414,6 @@ class StockMoveLine(models.Model):
done_ml = self.env['stock.move.line']
for ml in self - ml_to_delete:
if ml.product_id.type == 'product':
Quant = self.env['stock.quant']
rounding = ml.product_uom_id.rounding
# if this move line is force assigned, unreserve elsewhere if needed
+18 -5
View File
@@ -198,7 +198,6 @@ class StockQuant(models.Model):
"""
self = self.sudo()
quants = self._gather(product_id, location_id, lot_id=lot_id, package_id=package_id, owner_id=owner_id, strict=True)
rounding = product_id.uom_id.rounding
incoming_dates = [d for d in quants.mapped('in_date') if d]
incoming_dates = [fields.Datetime.from_string(incoming_date) for incoming_date in incoming_dates]
@@ -219,9 +218,6 @@ class StockQuant(models.Model):
'quantity': quant.quantity + quantity,
'in_date': in_date,
})
# cleanup empty quants
if float_is_zero(quant.quantity, precision_rounding=rounding) and float_is_zero(quant.reserved_quantity, precision_rounding=rounding):
quant.unlink()
break
except OperationalError as e:
if e.pgcode == '55P03': # could not obtain the lock
@@ -292,6 +288,22 @@ class StockQuant(models.Model):
break
return reserved_quants
@api.model
def _unlink_zero_quants(self):
""" _update_available_quantity may leave quants with no
quantity and no reserved_quantity. It used to directly unlink
these zero quants but this proved to hurt the performance as
this method is often called in batch and each unlink invalidate
the cache. We defer the calls to unlink in this method.
"""
precision_digits = max(6, self.env.ref('product.decimal_product_uom').digits * 2)
# Use a select instead of ORM search for UoM robustness.
query = """SELECT id FROM stock_quant WHERE round(quantity::numeric, %s) = 0 AND round(reserved_quantity::numeric, %s) = 0;"""
params = (precision_digits, precision_digits)
self.env.cr.execute(query, params)
quant_ids = self.env['stock.quant'].browse([quant['id'] for quant in self.env.cr.dictfetchall()])
quant_ids.sudo().unlink()
@api.model
def _merge_quants(self):
""" In a situation where one transaction is updating a quant via
@@ -334,7 +346,8 @@ class QuantPackage(models.Model):
name = fields.Char(
'Package Reference', copy=False, index=True,
default=lambda self: self.env['ir.sequence'].next_by_code('stock.quant.package') or _('Unknown Pack'))
quant_ids = fields.One2many('stock.quant', 'package_id', 'Bulk Content', readonly=True)
quant_ids = fields.One2many('stock.quant', 'package_id', 'Bulk Content', readonly=True,
domain=['|', ('quantity', '!=', 0), ('reserved_quantity', '!=', 0)])
packaging_id = fields.Many2one(
'product.packaging', 'Package Type', index=True)
location_id = fields.Many2one(
+1
View File
@@ -372,6 +372,7 @@ class ProcurementGroup(models.Model):
# Merge duplicated quants
self.env['stock.quant']._merge_quants()
self.env['stock.quant']._unlink_zero_quants()
@api.model
def run_scheduler(self, use_new_cursor=False, company_id=False):
+1 -1
View File
@@ -48,7 +48,7 @@ class TestInventory(TransactionCase):
# check
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 0.0)
self.assertEqual(sum(self.env['stock.quant']._gather(self.product1, self.stock_location).mapped('quantity')), 0.0)
def test_inventory_2(self):
""" Check that adding a tracked product through an inventory adjustment work as expected.
+71 -62
View File
@@ -42,6 +42,10 @@ class StockMove(TransactionCase):
'categ_id': self.env.ref('product.product_category_all').id,
})
def gather_relevant(self, product_id, location_id, lot_id=None, package_id=None, owner_id=None, strict=False):
quants = self.env['stock.quant']._gather(product_id, location_id, lot_id=lot_id, package_id=package_id, owner_id=owner_id, strict=strict)
return quants.filtered(lambda q: not (q.quantity == 0 and q.reserved_quantity == 0))
def test_in_1(self):
""" Receive products from a supplier. Check that a move line is created and that the
reception correctly increase a single quant in stock.
@@ -79,8 +83,8 @@ class StockMove(TransactionCase):
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.supplier_location), 0.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.supplier_location, allow_negative=True), -100.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 100.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.supplier_location)), 1.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.supplier_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 1.0)
def test_in_2(self):
""" Receive 5 tracked products from a supplier. The create move line should have 5
@@ -118,12 +122,12 @@ class StockMove(TransactionCase):
self.assertEqual(move1.state, 'done')
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product3, self.supplier_location), 0.0)
supplier_quants = self.env['stock.quant']._gather(self.product3, self.supplier_location)
supplier_quants = self.gather_relevant(self.product3, self.supplier_location)
self.assertEqual(sum(supplier_quants.mapped('quantity')), -5.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product3, self.stock_location), 5.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product3, self.supplier_location)), 1.0)
quants = self.env['stock.quant']._gather(self.product3, self.stock_location)
self.assertEqual(len(self.gather_relevant(self.product3, self.supplier_location)), 1.0)
quants = self.gather_relevant(self.product3, self.stock_location)
self.assertEqual(len(quants), 1.0)
for quant in quants:
self.assertNotEqual(quant.in_date, False)
@@ -171,12 +175,12 @@ class StockMove(TransactionCase):
# Quant balance should result with 5 quant in supplier and stock
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product2, self.supplier_location), 0.0)
supplier_quants = self.env['stock.quant']._gather(self.product2, self.supplier_location)
supplier_quants = self.gather_relevant(self.product2, self.supplier_location)
self.assertEqual(sum(supplier_quants.mapped('quantity')), -5.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product2, self.stock_location), 5.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product2, self.supplier_location)), 5.0)
quants = self.env['stock.quant']._gather(self.product2, self.stock_location)
self.assertEqual(len(self.gather_relevant(self.product2, self.supplier_location)), 5.0)
quants = self.gather_relevant(self.product2, self.stock_location)
self.assertEqual(len(quants), 5.0)
for quant in quants:
self.assertNotEqual(quant.in_date, False)
@@ -187,7 +191,7 @@ class StockMove(TransactionCase):
"""
# make some stock
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 100)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 1.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 100.0)
# creation
@@ -211,7 +215,7 @@ class StockMove(TransactionCase):
self.assertEqual(len(move1.move_line_ids), 1)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0.0)
# Should be a reserved quantity and thus a quant.
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 1.0)
# fill the move line
move_line = move1.move_line_ids[0]
@@ -224,10 +228,10 @@ class StockMove(TransactionCase):
self.assertEqual(move1.state, 'done')
# Check there is one quant in customer location
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.customer_location), 100.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.customer_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.customer_location)), 1.0)
# there should be no quant amymore in the stock location
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 0.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 0.0)
def test_out_2(self):
""" Send a consumable product to a client. Check that a move line is created but
@@ -236,7 +240,7 @@ class StockMove(TransactionCase):
# make some stock
self.product1.type = 'consu'
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 0.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 0.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0.0)
# creation
@@ -260,7 +264,7 @@ class StockMove(TransactionCase):
self.assertEqual(len(move1.move_line_ids), 1)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0.0)
# Should be a reserved quantity and thus a quant.
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 0.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 0.0)
# fill the move line
move_line = move1.move_line_ids[0]
@@ -273,10 +277,10 @@ class StockMove(TransactionCase):
self.assertEqual(move1.state, 'done')
# no quants are created in the customer location since it's a consumable
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.customer_location), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.customer_location)), 0.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.customer_location)), 0.0)
# there should be no quant amymore in the stock location
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 0.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 0.0)
def test_mixed_tracking_reservation_1(self):
""" Send products tracked by lot to a customer. In your stock, there are tracked and
@@ -354,11 +358,11 @@ class StockMove(TransactionCase):
self.assertEqual(ml.product_qty, 1.0)
# no changes on quants, even if i made some move lines with a lot id whom reserved on untracked quants
self.assertEqual(len(self.env['stock.quant']._gather(self.product2, self.stock_location, strict=True)), 1.0) # with a qty of 2
self.assertEqual(len(self.env['stock.quant']._gather(self.product2, self.stock_location, lot_id=lot1, strict=True)), 1.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product2, self.stock_location, lot_id=lot2, strict=True)), 1.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product2, self.stock_location, lot_id=lot3, strict=True)), 0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product2, self.stock_location, lot_id=lot4, strict=True)), 0)
self.assertEqual(len(self.gather_relevant(self.product2, self.stock_location, strict=True)), 1.0) # with a qty of 2
self.assertEqual(len(self.gather_relevant(self.product2, self.stock_location, lot_id=lot1, strict=True)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product2, self.stock_location, lot_id=lot2, strict=True)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product2, self.stock_location, lot_id=lot3, strict=True)), 0)
self.assertEqual(len(self.gather_relevant(self.product2, self.stock_location, lot_id=lot4, strict=True)), 0)
move1.move_line_ids.write({'qty_done': 1.0})
@@ -522,7 +526,7 @@ class StockMove(TransactionCase):
# validating the move line should move the lot, not create a negative quant in stock
move1._action_done()
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)
self.assertEqual(len(self.gather_relevant(self.product2, self.stock_location)), 0.0)
def test_mixed_tracking_reservation_6(self):
# create an untracked quant
@@ -558,7 +562,7 @@ class StockMove(TransactionCase):
# validating the move line should move the lot, not create a negative quant in stock
move1._action_done()
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)
self.assertEqual(len(self.gather_relevant(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
@@ -604,7 +608,7 @@ class StockMove(TransactionCase):
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)
quants = self.gather_relevant(self.product2, self.stock_location)
self.assertEqual(len(quants), 0)
def test_mixed_tracking_reservation_8(self):
@@ -852,7 +856,7 @@ class StockMove(TransactionCase):
"""
# make some stock
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 150.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 1.0)
move1 = self.env['stock.move'].create({
'name': 'test_putaway_1',
@@ -864,7 +868,7 @@ class StockMove(TransactionCase):
})
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 150.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 1.0)
self.assertEqual(move1.availability, 100.0)
def test_availability_2(self):
@@ -873,7 +877,7 @@ class StockMove(TransactionCase):
"""
# make some stock
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 50.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 1.0)
move1 = self.env['stock.move'].create({
'name': 'test_putaway_1',
@@ -885,7 +889,7 @@ class StockMove(TransactionCase):
})
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 50.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 1.0)
self.assertEqual(move1.availability, 50.0)
def test_availability_3(self):
@@ -948,9 +952,9 @@ class StockMove(TransactionCase):
self.assertEqual(move1.move_line_ids.qty_done, 15)
self.assertEqual(move2.state, 'done')
stock_quants = self.env['stock.quant']._gather(self.product1, self.stock_location)
stock_quants = self.gather_relevant(self.product1, self.stock_location)
self.assertEqual(len(stock_quants), 0)
customer_quants = self.env['stock.quant']._gather(self.product1, self.customer_location)
customer_quants = self.gather_relevant(self.product1, self.customer_location)
self.assertEqual(customer_quants.quantity, 30)
self.assertEqual(customer_quants.reserved_quantity, 0)
@@ -1072,7 +1076,7 @@ class StockMove(TransactionCase):
self.assertEqual(move.product_uom.id, self.uom_dozen.id)
self.assertEqual(move.state, 'done')
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product2, self.customer_location), 12.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product2, self.customer_location)), 12)
self.assertEqual(len(self.gather_relevant(self.product2, self.customer_location)), 12)
def test_availability_8(self):
""" Test the assignment mechanism when the product quantity is decreased on a partially
@@ -1199,7 +1203,7 @@ class StockMove(TransactionCase):
"""
# make some stock
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 2)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 1.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 2)
# creation
@@ -1223,7 +1227,7 @@ class StockMove(TransactionCase):
self.assertEqual(len(move1.move_line_ids), 1)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0.0)
quants = self.env['stock.quant']._gather(self.product1, self.stock_location)
quants = self.gather_relevant(self.product1, self.stock_location)
self.assertEqual(len(quants), 1.0)
self.assertEqual(quants.quantity, 2.0)
self.assertEqual(quants.reserved_quantity, 2.0)
@@ -1240,7 +1244,7 @@ class StockMove(TransactionCase):
"""
# make some stock
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 2)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 1.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 2)
# creation
@@ -1264,7 +1268,7 @@ class StockMove(TransactionCase):
self.assertEqual(len(move1.move_line_ids), 1)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0.0)
quants = self.env['stock.quant']._gather(self.product1, self.stock_location)
quants = self.gather_relevant(self.product1, self.stock_location)
self.assertEqual(len(quants), 1.0)
self.assertEqual(quants.quantity, 2.0)
self.assertEqual(quants.reserved_quantity, 2.0)
@@ -1286,7 +1290,7 @@ class StockMove(TransactionCase):
'location_id': self.stock_location.id,
'quantity': 2,
})
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 2)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 2)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 5)
# creation
@@ -1310,7 +1314,7 @@ class StockMove(TransactionCase):
self.assertEqual(len(move1.move_line_ids), 1)
move1._do_unreserve()
quants = self.env['stock.quant']._gather(self.product1, self.stock_location)
quants = self.gather_relevant(self.product1, self.stock_location)
self.assertEqual(len(quants), 2.0)
for quant in quants:
self.assertEqual(quant.reserved_quantity, 0)
@@ -1362,7 +1366,7 @@ class StockMove(TransactionCase):
"""
# make some stock
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 1.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 1.0)
move_stock_pack = self.env['stock.move'].create({
'name': 'test_link_assign_1_1',
@@ -1404,7 +1408,7 @@ class StockMove(TransactionCase):
# make some stock
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 1.0, lot_id=lot1)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location, lot1)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location, lot1)), 1.0)
move_stock_pack = self.env['stock.move'].create({
'name': 'test_link_2_1',
@@ -1431,18 +1435,18 @@ class StockMove(TransactionCase):
move_line_stock_pack = move_stock_pack.move_line_ids[0]
self.assertEqual(move_line_stock_pack.lot_id.id, lot1.id)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location, lot_id=lot1), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location, lot1)), 1.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.pack_location, lot1)), 0.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location, lot1)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.pack_location, lot1)), 0.0)
move_line_stock_pack.qty_done = 1.0
move_stock_pack._action_done()
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location, lot_id=lot1), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location, lot1)), 0.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location, lot1)), 0.0)
move_line_pack_cust = move_pack_cust.move_line_ids[0]
self.assertEqual(move_line_pack_cust.lot_id.id, lot1.id)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.pack_location, lot_id=lot1), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.pack_location, lot1)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.pack_location, lot1)), 1.0)
def test_link_assign_3(self):
""" Test the assignment mechanism when three chained stock moves (2 sources, 1 dest) try to
@@ -1450,7 +1454,7 @@ class StockMove(TransactionCase):
"""
# make some stock
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 2.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 1.0)
move_stock_pack_1 = self.env['stock.move'].create({
'name': 'test_link_assign_1_1',
@@ -1494,8 +1498,8 @@ class StockMove(TransactionCase):
self.assertEqual(move_pack_cust.state, 'partially_available')
self.assertEqual(len(move_pack_cust.move_line_ids), 1)
# Should have 1 quant in stock_location and another in pack_location
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 1.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.pack_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.pack_location)), 1.0)
move_stock_pack_2._action_assign()
self.assertEqual(move_stock_pack_2.state, 'assigned')
@@ -1504,8 +1508,8 @@ class StockMove(TransactionCase):
move_stock_pack_2._action_done()
self.assertEqual(move_stock_pack_2.state, 'done')
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.pack_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 0.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.pack_location)), 1.0)
self.assertEqual(move_pack_cust.state, 'assigned')
self.assertEqual(len(move_pack_cust.move_line_ids), 1)
@@ -1526,7 +1530,7 @@ class StockMove(TransactionCase):
# make some stock
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 2.0, lot_id=lot1)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location, lot1)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location, lot1)), 1.0)
move_stock_pack_1 = self.env['stock.move'].create({
'name': 'test_link_assign_1_1',
@@ -1960,7 +1964,7 @@ class StockMove(TransactionCase):
# stock location: 2.0
# pack location: -1.0
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 2.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 1.0)
move_out = self.env['stock.move'].create({
'name': 'test_link_assign_out',
@@ -1974,7 +1978,7 @@ class StockMove(TransactionCase):
move_out._action_assign()
move_out.quantity_done = 1.0
move_out._action_done()
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.pack_location)), 1.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.pack_location)), 1.0)
move_stock_pack = self.env['stock.move'].create({
'name': 'test_link_assign_1_1',
@@ -2433,8 +2437,8 @@ class StockMove(TransactionCase):
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product3, self.stock_location), 0.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product3, self.stock_location, lot_id=lot1, strict=True), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product3, self.stock_location)), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product3, self.stock_location, lot_id=lot1, strict=True)), 0.0)
self.assertEqual(len(self.gather_relevant(self.product3, self.stock_location)), 0.0)
self.assertEqual(len(self.gather_relevant(self.product3, self.stock_location, lot_id=lot1, strict=True)), 0.0)
def test_edit_reserved_move_line_8(self):
""" Send 5 tracked products to a client, but some of these products do not have any lot set
@@ -2499,9 +2503,9 @@ class StockMove(TransactionCase):
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product3, self.stock_location), 0.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product3, self.stock_location, lot_id=lot1, strict=True), 0.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product3, self.stock_location, lot_id=lot2, strict=True), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product3, self.stock_location)), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product3, self.stock_location, lot_id=lot1, strict=True)), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product3, self.stock_location, lot_id=lot2, strict=True)), 0.0)
self.assertEqual(len(self.gather_relevant(self.product3, self.stock_location)), 0.0)
self.assertEqual(len(self.gather_relevant(self.product3, self.stock_location, lot_id=lot1, strict=True)), 0.0)
self.assertEqual(len(self.gather_relevant(self.product3, self.stock_location, lot_id=lot2, strict=True)), 0.0)
def test_edit_done_move_line_1(self):
""" Test that editing a done stock move line linked to an untracked product correctly and
@@ -2907,13 +2911,13 @@ class StockMove(TransactionCase):
move1.move_line_ids.qty_done = 10
move1._action_done()
quant = self.env['stock.quant']._gather(self.product1, self.stock_location)
quant = self.gather_relevant(self.product1, self.stock_location)
self.assertEqual(len(quant), 1.0)
# edit once done, we actually moved 2 products
move1.move_line_ids.qty_done = 0
quant = self.env['stock.quant']._gather(self.product1, self.stock_location)
quant = self.gather_relevant(self.product1, self.stock_location)
self.assertEqual(len(quant), 0.0)
self.assertEqual(move1.product_uom_qty, 0.0)
@@ -3098,7 +3102,7 @@ class StockMove(TransactionCase):
self.assertEqual(picking.move_lines.state, 'done')
self.assertEqual(picking.move_lines.quantity_done, 5.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 0.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 0.0)
# The backoder should contain a move for the other 5 produts.
backorder = self.env['stock.picking'].search([('backorder_id', '=', picking.id)])
@@ -3210,7 +3214,7 @@ class StockMove(TransactionCase):
self.assertEqual(picking.move_lines.move_line_ids.qty_done, 5.0)
# Check quants data
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 0.0)
self.assertEqual(len(self.gather_relevant(self.product1, self.stock_location)), 0.0)
def _create_picking_test_immediate_validate_5(self, picking_type_id, product_id):
picking = self.env['stock.picking'].create({
@@ -3541,7 +3545,7 @@ class StockMove(TransactionCase):
move1.move_line_ids.qty_done = 1
move1._action_done()
quant = self.env['stock.quant']._gather(self.product3, self.stock_location)
quant = self.gather_relevant(self.product3, self.stock_location)
self.assertEqual(len(quant), 1.0)
self.assertNotEqual(quant.in_date, False)
@@ -3561,7 +3565,7 @@ class StockMove(TransactionCase):
move2.move_line_ids.qty_done = 1
move2._action_done()
quant = self.env['stock.quant']._gather(self.product3, self.pack_location)
quant = self.gather_relevant(self.product3, self.pack_location)
self.assertEqual(len(quant), 1.0)
self.assertEqual(quant.in_date, initial_incoming_date)
@@ -3658,6 +3662,7 @@ class StockMove(TransactionCase):
('location_id.usage', '=', 'internal'),
('product_id', '=', self.product3.id),
('lot_id', '=', lot1.id),
('quantity', '!=', 0),
])
self.assertEqual(quant_lot1.location_id, self.stock_location)
self.assertAlmostEqual(quant_lot1.in_date, initial_in_date_lot1, delta=timedelta(seconds=1))
@@ -3667,6 +3672,7 @@ class StockMove(TransactionCase):
('location_id.usage', '=', 'internal'),
('product_id', '=', self.product3.id),
('lot_id', '=', lot2.id),
('quantity', '!=', 0),
])
self.assertEqual(quant_lot2.location_id, self.pack_location)
self.assertAlmostEqual(quant_lot2.in_date, initial_in_date_lot2, delta=timedelta(seconds=1))
@@ -3719,6 +3725,7 @@ class StockMove(TransactionCase):
('location_id', '=', self.stock_location.id),
('product_id', '=', self.product3.id),
('lot_id', '=', lot2.id),
('quantity', '!=', 0),
]).in_date
# Edit lot1's incoming date.
@@ -3726,6 +3733,7 @@ class StockMove(TransactionCase):
('location_id.usage', '=', 'internal'),
('product_id', '=', self.product3.id),
('lot_id', '=', lot1.id),
('quantity', '!=', 0),
])
from odoo.fields import Datetime
from datetime import timedelta
@@ -3760,6 +3768,7 @@ class StockMove(TransactionCase):
quants = self.env['stock.quant'].search([
('location_id.usage', '=', 'internal'),
('product_id', '=', self.product3.id),
('quantity', '!=', 0),
])
self.assertEqual(len(quants), 2)
for quant in quants:
+2 -2
View File
@@ -159,7 +159,7 @@ class TestPickShip(TestStockCommon):
self.assertEqual(picking_client.state, 'assigned', 'The picking should not assign what it does not have')
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.productA, stock_location), 0.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.productA, pack_location), 5.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.productA, stock_location)), 0.0)
self.assertEqual(sum(self.env['stock.quant']._gather(self.productA, stock_location).mapped('quantity')), 0.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.productA, pack_location)), 1.0)
def test_mto_moves_return(self):
@@ -681,7 +681,7 @@ class TestPickShip(TestStockCommon):
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.productA, pick_location), 5.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.productA, return_location), 5.0)
self.assertEqual(len(self.env['stock.quant'].search([('product_id', '=', self.productA.id)])), 2)
self.assertEqual(len(self.env['stock.quant'].search([('product_id', '=', self.productA.id), ('quantity', '!=', 0)])), 2)
class TestSinglePicking(TestStockCommon):
+1 -1
View File
@@ -125,7 +125,7 @@ class TestPackingNeg(TransactionCase):
# Check the quants that you have -20 pieces pallet 2 in stock, and a total quantity
# of 50 in stock from pallet 3 (should be 20+30, as it has been split by reservation)
records = self.env['stock.quant'].search([('product_id', '=', product_neg.id)])
records = self.env['stock.quant'].search([('product_id', '=', product_neg.id), ('quantity', '!=', '0')])
pallet_3_stock_qty = 0
for rec in records:
if rec.package_id.name == 'Palneg 2' and rec.location_id.id == self.ref('stock.stock_location_stock'):
+20 -16
View File
@@ -27,6 +27,10 @@ class StockQuant(TransactionCase):
'groups_id': [(6, 0, [self.env.ref('stock.group_stock_user').id])]
})
def gather_relevant(self, product_id, location_id, lot_id=None, package_id=None, owner_id=None, strict=False):
quants = self.env['stock.quant']._gather(product_id, location_id, lot_id=lot_id, package_id=package_id, owner_id=owner_id, strict=strict)
return quants.filtered(lambda q: not (q.quantity == 0 and q.reserved_quantity == 0))
def test_get_available_quantity_1(self):
""" Quantity availability with only one quant in a location.
"""
@@ -166,7 +170,7 @@ class StockQuant(TransactionCase):
'type': 'consu',
})
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)), 0)
self.assertEqual(len(self.gather_relevant(product1, stock_location)), 0)
with self.assertRaises(ValidationError):
self.env['stock.quant']._update_available_quantity(product1, stock_location, 1.0)
@@ -214,7 +218,7 @@ class StockQuant(TransactionCase):
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location), 2.0)
self.env['stock.quant']._update_available_quantity(product1, stock_location, 1.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location), 3.0)
self.assertEqual(len(self.env['stock.quant']._gather(product1, stock_location)), 2)
self.assertEqual(len(self.gather_relevant(product1, stock_location)), 2)
def test_increase_available_quantity_3(self):
""" Increase the available quantity when a concurrent transaction is already increasing
@@ -233,7 +237,7 @@ class StockQuant(TransactionCase):
self.env['stock.quant']._update_available_quantity(product, stock_location, 1.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(product, stock_location), available_quantity + 1)
self.assertEqual(len(self.env['stock.quant']._gather(product, stock_location, strict=True)), 2)
self.assertEqual(len(self.gather_relevant(product, stock_location, strict=True)), 2)
def test_increase_available_quantity_4(self):
""" Increase the available quantity when no quants are already in a location with a user without access right.
@@ -324,10 +328,10 @@ class StockQuant(TransactionCase):
'quantity': 1.0,
})
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location), 2.0)
self.assertEqual(len(self.env['stock.quant']._gather(product1, stock_location)), 2)
self.assertEqual(len(self.gather_relevant(product1, stock_location)), 2)
self.env['stock.quant']._update_available_quantity(product1, stock_location, -1.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location), 1.0)
self.assertEqual(len(self.env['stock.quant']._gather(product1, stock_location)), 1)
self.assertEqual(len(self.gather_relevant(product1, stock_location)), 1)
def test_decrease_available_quantity_3(self):
""" Decrease the available quantity when a concurrent transaction is already increasing
@@ -345,7 +349,7 @@ class StockQuant(TransactionCase):
self.env['stock.quant']._update_available_quantity(product, stock_location, -1.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(product, stock_location), available_quantity - 1)
self.assertEqual(len(self.env['stock.quant']._gather(product, stock_location, strict=True)), 2)
self.assertEqual(len(self.gather_relevant(product, stock_location, strict=True)), 2)
def test_decrease_available_quantity_4(self):
""" Decrease the available quantity that delete the quant. The active user should have
@@ -363,7 +367,7 @@ class StockQuant(TransactionCase):
})
self.env = self.env(user=self.demo_user)
self.env['stock.quant']._update_available_quantity(product1, stock_location, -1.0)
self.assertEqual(len(self.env['stock.quant']._gather(product1, stock_location)), 0)
self.assertEqual(len(self.gather_relevant(product1, stock_location)), 0)
def test_increase_reserved_quantity_1(self):
""" Increase the reserved quantity of quantity x when there's a single quant in a given
@@ -380,10 +384,10 @@ class StockQuant(TransactionCase):
'quantity': 10.0,
})
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)
self.assertEqual(len(self.gather_relevant(product1, stock_location)), 1)
self.env['stock.quant']._update_reserved_quantity(product1, stock_location, 10.0)
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.assertEqual(len(self.gather_relevant(product1, stock_location)), 1)
def test_increase_reserved_quantity_2(self):
""" Increase the reserved quantity of quantity x when there's two quants in a given
@@ -401,10 +405,10 @@ class StockQuant(TransactionCase):
'quantity': 5.0,
})
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)), 2)
self.assertEqual(len(self.gather_relevant(product1, stock_location)), 2)
self.env['stock.quant']._update_reserved_quantity(product1, stock_location, 10.0)
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)), 2)
self.assertEqual(len(self.gather_relevant(product1, stock_location)), 2)
def test_increase_reserved_quantity_3(self):
""" Increase the reserved quantity of quantity x when there's multiple quants in a given
@@ -442,10 +446,10 @@ class StockQuant(TransactionCase):
# total quantity: 58
# total reserved quantity: 29
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location), 29.0)
self.assertEqual(len(self.env['stock.quant']._gather(product1, stock_location)), 4)
self.assertEqual(len(self.gather_relevant(product1, stock_location)), 4)
self.env['stock.quant']._update_reserved_quantity(product1, stock_location, 10.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location), 19.0)
self.assertEqual(len(self.env['stock.quant']._gather(product1, stock_location)), 4)
self.assertEqual(len(self.gather_relevant(product1, stock_location)), 4)
def test_increase_reserved_quantity_4(self):
""" Increase the reserved quantity of quantity x when there's multiple quants in a given
@@ -469,7 +473,7 @@ class StockQuant(TransactionCase):
'reserved_quantity': 10.0,
})
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)), 2)
self.assertEqual(len(self.gather_relevant(product1, stock_location)), 2)
with self.assertRaises(UserError):
self.env['stock.quant']._update_reserved_quantity(product1, stock_location, 10.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, stock_location), 0.0)
@@ -499,10 +503,10 @@ class StockQuant(TransactionCase):
'reserved_quantity': 10.0,
})
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.assertEqual(len(self.gather_relevant(product1, stock_location)), 1)
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)
self.assertEqual(len(self.gather_relevant(product1, stock_location)), 1)
def test_increase_decrease_reserved_quantity_1(self):
""" Decrease then increase reserved quantity when no quant are in a location.
+9 -9
View File
@@ -283,16 +283,16 @@ class TestStockFlow(TestStockCommon):
self.assertEqual(sum(total_qty), 2.0, 'Expecting 2.0 Unit , got %.4f Unit on location stock!' % (sum(total_qty)))
self.assertEqual(self.productA.qty_available, 2.0, 'Wrong quantity available (%s found instead of 2.0)' % (self.productA.qty_available))
# Check quants and available quantity for product B
quants = self.StockQuantObj.search([('product_id', '=', self.productB.id), ('location_id', '=', self.stock_location)])
quants = self.StockQuantObj.search([('product_id', '=', self.productB.id), ('location_id', '=', self.stock_location), ('quantity', '!=', 0)])
self.assertFalse(quants, 'No quant should found as outgoing shipment took everything out of stock.')
self.assertEqual(self.productB.qty_available, 0.0, 'Product B should have zero quantity available.')
# Check quants and available quantity for product C
quants = self.StockQuantObj.search([('product_id', '=', self.productC.id), ('location_id', '=', self.stock_location)])
quants = self.StockQuantObj.search([('product_id', '=', self.productC.id), ('location_id', '=', self.stock_location), ('quantity', '!=', 0)])
total_qty = [quant.quantity for quant in quants]
self.assertEqual(sum(total_qty), 2.0, 'Expecting 2.0 Unit, got %.4f Unit on location stock!' % (sum(total_qty)))
self.assertEqual(self.productC.qty_available, 2.0, 'Wrong quantity available (%s found instead of 2.0)' % (self.productC.qty_available))
# Check quants and available quantity for product D
quant = self.StockQuantObj.search([('product_id', '=', self.productD.id), ('location_id', '=', self.stock_location)], limit=1)
quant = self.StockQuantObj.search([('product_id', '=', self.productD.id), ('location_id', '=', self.stock_location), ('quantity', '!=', 0)], limit=1)
self.assertEqual(quant.quantity, 1.0, 'Expecting 1.0 Unit , got %.4f Unit on location stock!' % (quant.quantity))
self.assertEqual(self.productD.qty_available, 1.0, 'Wrong quantity available (%s found instead of 1.0)' % (self.productD.qty_available))
@@ -395,21 +395,21 @@ class TestStockFlow(TestStockCommon):
# -----------------------------------------------------------------------
# Check quants and available quantity for product A.
quants = self.StockQuantObj.search([('product_id', '=', self.productA.id), ('location_id', '=', self.stock_location)])
quants = self.StockQuantObj.search([('product_id', '=', self.productA.id), ('location_id', '=', self.stock_location), ('quantity', '!=', 0)])
total_qty = [quant.quantity for quant in quants]
self.assertEqual(sum(total_qty), 12.0, 'Wrong total stock location quantity (%s found instead of 12)' % (sum(total_qty)))
self.assertEqual(self.productA.qty_available, 12.0, 'Wrong quantity available (%s found instead of 12)' % (self.productA.qty_available))
# Check quants and available quantity for product B.
quants = self.StockQuantObj.search([('product_id', '=', self.productB.id), ('location_id', '=', self.stock_location)])
quants = self.StockQuantObj.search([('product_id', '=', self.productB.id), ('location_id', '=', self.stock_location), ('quantity', '!=', 0)])
self.assertFalse(quants, 'No quant should found as outgoing shipment took everything out of stock')
self.assertEqual(self.productB.qty_available, 0.0, 'Total quantity in stock should be 0 as the backorder took everything out of stock')
# Check quants and available quantity for product C.
quants = self.StockQuantObj.search([('product_id', '=', self.productC.id), ('location_id', '=', self.stock_location)])
quants = self.StockQuantObj.search([('product_id', '=', self.productC.id), ('location_id', '=', self.stock_location), ('quantity', '!=', 0)])
total_qty = [quant.quantity for quant in quants]
self.assertEqual(sum(total_qty), 8.0, 'Wrong total stock location quantity (%s found instead of 8)' % (sum(total_qty)))
self.assertEqual(self.productC.qty_available, 8.0, 'Wrong quantity available (%s found instead of 8)' % (self.productC.qty_available))
# Check quants and available quantity for product D.
quants = self.StockQuantObj.search([('product_id', '=', self.productD.id), ('location_id', '=', self.stock_location)])
quants = self.StockQuantObj.search([('product_id', '=', self.productD.id), ('location_id', '=', self.stock_location), ('quantity', '!=', 0)])
total_qty = [quant.quantity for quant in quants]
self.assertEqual(sum(total_qty), 9.0, 'Wrong total stock location quantity (%s found instead of 9)' % (sum(total_qty)))
self.assertEqual(self.productD.qty_available, 9.0, 'Wrong quantity available (%s found instead of 9)' % (self.productD.qty_available))
@@ -421,7 +421,7 @@ class TestStockFlow(TestStockCommon):
back_order_out.action_done()
# Check stock location quants and available quantity for product A.
quants = self.StockQuantObj.search([('product_id', '=', self.productA.id), ('location_id', '=', self.stock_location)])
quants = self.StockQuantObj.search([('product_id', '=', self.productA.id), ('location_id', '=', self.stock_location), ('quantity', '!=', 0)])
total_qty = [quant.quantity for quant in quants]
self.assertGreaterEqual(float_round(sum(total_qty), precision_rounding=0.0001), 1, 'Total stock location quantity for product A should not be nagative.')
@@ -1549,7 +1549,7 @@ class TestStockFlow(TestStockCommon):
self.assertEqual(neg_quants.location_id.id, self.supplier_location, 'There shoud be 1 negative quants for supplier!')
# We should also make sure that when matching stock moves with pack operations, it takes the correct
quants = self.env['stock.quant']._gather(self.productE, self.env['stock.location'].browse(self.stock_location))
self.assertEqual(len(quants), 0, 'We should have no quants in the end')
self.assertEqual(sum(quants.mapped('quantity')), 0, 'We should have no quants in the end')
def test_70_picking_state_all_at_once_reserve(self):
""" This test will check that the state of the picking is correctly computed according
+4 -4
View File
@@ -169,7 +169,7 @@ class TestWarehouse(TestStockCommon):
return_pick.action_done()
quant = self.env['stock.quant'].search([('product_id', '=', productA.id), ('location_id', '=', stock_location.id)])
self.assertEqual(len(quant), 0)
self.assertEqual(sum(quant.mapped('quantity')), 0)
def test_inventory_adjustment_and_negative_quants_2(self):
"""Make sure negative quants get wiped out with an inventory adjustment"""
@@ -220,7 +220,7 @@ class TestWarehouse(TestStockCommon):
# There should be no quant in the stock location
quants = self.env['stock.quant'].search([('product_id', '=', productA.id), ('location_id', '=', stock_location.id)])
self.assertEqual(len(quants), 0)
self.assertEqual(sum(quants.mapped('quantity')), 0)
# There should be one quant in the inventory loss location
quant = self.env['stock.quant'].search([('product_id', '=', productA.id), ('location_id', '=', location_loss.id)])
@@ -385,7 +385,7 @@ class TestWarehouse(TestStockCommon):
# Check that the correct quantity has been provided to customer
self.assertEqual(self.env['stock.quant']._gather(product, customer_location).quantity, 1)
# Ensure there still no quants in distribution warehouse
self.assertEqual(len(self.env['stock.quant']._gather(product, warehouse_distribution_namur.lot_stock_id)), 0)
self.assertEqual(sum(self.env['stock.quant']._gather(product, warehouse_distribution_namur.lot_stock_id).mapped('quantity')), 0)
# Create the move for the shop Wavre. Should create a resupply from
# distribution warehouse Wavre.
@@ -429,7 +429,7 @@ class TestWarehouse(TestStockCommon):
# Check that the correct quantity has been provided to customer
self.assertEqual(self.env['stock.quant']._gather(product, customer_location).quantity, 2)
# Ensure there still no quants in distribution warehouse
self.assertEqual(len(self.env['stock.quant']._gather(product, warehouse_distribution_wavre.lot_stock_id)), 0)
self.assertEqual(sum(self.env['stock.quant']._gather(product, warehouse_distribution_wavre.lot_stock_id).mapped('quantity')), 0)
class TestResupply(TestStockCommon):
def setUp(self):
+2 -1
View File
@@ -171,5 +171,6 @@ class TestWiseOperator(TransactionCase):
records = self.env['stock.quant'].search([
('product_id', '=', product_wise.id),
('location_id', '!=', self.ref('stock.stock_location_suppliers'))])
self.assertTrue(all([x.location_id.id == self.ref('stock.stock_location_customers') and x.quantity > 0.0 for x in records]),
self.assertTrue(all([x.location_id.id == self.ref('stock.stock_location_customers') and x.quantity > 0.0 or
x.location_id.id != self.ref('stock.stock_location_customers') and x.quantity == 0.0 for x in records]),
"Negative quant or wrong location detected")
@@ -33,4 +33,5 @@ class StockQuantityHistory(models.TransientModel):
return action
else:
self.env['stock.quant']._merge_quants()
self.env['stock.quant']._unlink_zero_quants()
return self.env.ref('stock.quantsact').read()[0]
@@ -83,8 +83,8 @@ class TestBatchPicking(TransactionCase):
quant_B = self.env['stock.quant']._gather(self.productB, self.stock_location)
# ensure that quantity for picking has been moved
self.assertFalse(quant_A)
self.assertFalse(quant_B)
self.assertFalse(sum(quant_A.mapped('quantity')))
self.assertFalse(sum(quant_B.mapped('quantity')))
def test_simple_batch_with_wizard(self):
""" Test a simple batch picking with all quantity for picking available.
@@ -113,8 +113,8 @@ class TestBatchPicking(TransactionCase):
quant_B = self.env['stock.quant']._gather(self.productB, self.stock_location)
# ensure that quantity for picking has been moved
self.assertFalse(quant_A)
self.assertFalse(quant_B)
self.assertFalse(sum(quant_A.mapped('quantity')))
self.assertFalse(sum(quant_B.mapped('quantity')))
def test_batch_with_backorder_wizard(self):
""" Test a simple batch picking with only one quantity fully available.
@@ -148,8 +148,8 @@ class TestBatchPicking(TransactionCase):
quant_B = self.env['stock.quant']._gather(self.productB, self.stock_location)
# ensure that quantity for picking has been moved
self.assertFalse(quant_A)
self.assertFalse(quant_B)
self.assertFalse(sum(quant_A.mapped('quantity')))
self.assertFalse(sum(quant_B.mapped('quantity')))
def test_batch_with_immediate_transfer_and_backorder_wizard(self):
""" Test a simple batch picking with only one product fully available.
@@ -184,8 +184,8 @@ class TestBatchPicking(TransactionCase):
quant_B = self.env['stock.quant']._gather(self.productB, self.stock_location)
# ensure that quantity for picking has been moved
self.assertFalse(quant_A)
self.assertFalse(quant_B)
self.assertFalse(sum(quant_A.mapped('quantity')))
self.assertFalse(sum(quant_B.mapped('quantity')))
def test_batch_with_immediate_transfer_and_backorder_wizard_with_manual_operations(self):
""" Test a simple batch picking with only one quantity fully available.
@@ -221,5 +221,5 @@ class TestBatchPicking(TransactionCase):
quant_B = self.env['stock.quant']._gather(self.productB, self.stock_location)
# ensure that quantity for picking has been moved
self.assertFalse(quant_A)
self.assertFalse(quant_B)
self.assertFalse(sum(quant_A.mapped('quantity')))
self.assertFalse(sum(quant_B.mapped('quantity')))