diff --git a/addons/stock/models/stock_location.py b/addons/stock/models/stock_location.py index b96421dfd34..3e7fccf5b5f 100644 --- a/addons/stock/models/stock_location.py +++ b/addons/stock/models/stock_location.py @@ -117,9 +117,9 @@ class Location(models.Model): ('view_location_id.parent_left', '<=', self.parent_left), ('view_location_id.parent_right', '>=', self.parent_left)], limit=1) - def should_impact_quants(self): + def should_bypass_reservation(self): self.ensure_one() - return False if self.usage in ('supplier', 'inventory', 'production', 'customer') else True + return self.usage in ('supplier', 'customer', 'inventory', 'production') or self.scrap_location class Route(models.Model): diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py index faab9a301e5..74fd5e67278 100644 --- a/addons/stock/models/stock_move_line.py +++ b/addons/stock/models/stock_move_line.py @@ -96,10 +96,10 @@ class StockMoveLine(models.Model): vals['ordered_qty'] = vals.get('product_uom_qty') ml = super(StockMoveLine, self).create(vals) if ml.state == 'done': - Quant = self.env['stock.quant'] - quantity = ml.product_uom_id._compute_quantity(ml.qty_done, ml.move_id.product_id.uom_id,rounding_method='HALF-UP') - in_date = None - if ml.location_id.should_impact_quants() and ml.product_id.type == 'product': + if ml.product_id.type == 'product': + Quant = self.env['stock.quant'] + quantity = ml.product_uom_id._compute_quantity(ml.qty_done, ml.move_id.product_id.uom_id,rounding_method='HALF-UP') + in_date = None available_qty, in_date = Quant._update_available_quantity(ml.product_id, ml.location_id, -quantity, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id) if available_qty < 0 and ml.lot_id: # see if we can compensate the negative quants with some untracked quants @@ -108,7 +108,6 @@ class StockMoveLine(models.Model): taken_from_untracked_qty = min(untracked_qty, abs(quantity)) Quant._update_available_quantity(ml.product_id, ml.location_id, -taken_from_untracked_qty, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id) Quant._update_available_quantity(ml.product_id, ml.location_id, taken_from_untracked_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id) - if ml.location_dest_id.should_impact_quants() and ml.product_id.type == 'product': Quant._update_available_quantity(ml.product_id, ml.location_dest_id, quantity, lot_id=ml.lot_id, package_id=ml.result_package_id, owner_id=ml.owner_id, in_date=in_date) next_moves = ml.move_id.move_dest_ids.filtered(lambda move: move.state not in ('done', 'cancel')) next_moves.do_unreserve() @@ -125,12 +124,13 @@ class StockMoveLine(models.Model): return super(StockMoveLine, self).write(vals) Quant = self.env['stock.quant'] + precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') # We forbid to change the reserved quantity in the interace, but it is needed in the # case of stock.move's split. # TODO Move me in the update if 'product_uom_qty' in vals: - for ml in self.filtered(lambda m: m.state in ('partially_available', 'assigned')): - if ml.location_id.should_impact_quants() and ml.product_id.type == 'product': + for ml in self.filtered(lambda m: m.state in ('partially_available', 'assigned') and m.product_id.type == 'product'): + if not ml.location_id.should_bypass_reservation(): 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, strict=True) @@ -154,8 +154,8 @@ class StockMoveLine(models.Model): updates[key] = self.env[model].browse(vals[key]) if updates: - 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': + for ml in self.filtered(lambda ml: ml.state in ['partially_available', 'assigned'] and ml.product_id.type == 'product'): + if not ml.location_id.should_bypass_reservation(): 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, strict=True) except UserError: @@ -164,7 +164,7 @@ class StockMoveLine(models.Model): else: raise - if updates.get('location_id', ml.location_id).should_impact_quants() and ml.product_id.type == 'product': + if not updates.get('location_id', ml.location_id).should_bypass_reservation(): new_product_qty = 0 try: q = Quant._update_reserved_quantity(ml.product_id, updates.get('location_id', ml.location_id), ml.product_qty, lot_id=updates.get('lot_id', ml.lot_id), @@ -186,18 +186,14 @@ class StockMoveLine(models.Model): # When editing a done move line, the reserved availability of a potential chained move is impacted. Take care of running again `action_assign` on the concerned moves. next_moves = self.env['stock.move'] if updates or 'qty_done' in vals: - for ml in self.filtered(lambda ml: ml.move_id.state == 'done'): + for ml in self.filtered(lambda ml: ml.move_id.state == 'done' and ml.product_id.type == 'product'): # undo the original move line - in_date = None - if ml.location_dest_id.should_impact_quants() and ml.product_id.type == 'product': - in_date = Quant._update_available_quantity(ml.product_id, ml.location_dest_id, -ml.qty_done, lot_id=ml.lot_id, + in_date = Quant._update_available_quantity(ml.product_id, ml.location_dest_id, -ml.qty_done, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id)[1] - if ml.location_id.should_impact_quants() and ml.product_id.type == 'product': - Quant._update_available_quantity(ml.product_id, ml.location_id, ml.qty_done, lot_id=ml.lot_id, + Quant._update_available_quantity(ml.product_id, ml.location_id, ml.qty_done, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id, in_date=in_date) # move what's been actually done - in_date = None product_id = ml.product_id location_id = updates.get('location_id', ml.location_id) location_dest_id = updates.get('location_dest_id', ml.location_dest_id) @@ -207,8 +203,9 @@ class StockMoveLine(models.Model): result_package_id = updates.get('result_package_id', ml.result_package_id) owner_id = updates.get('owner_id', ml.owner_id) quantity = ml.move_id.product_uom._compute_quantity(qty_done, ml.move_id.product_id.uom_id, rounding_method='HALF-UP') - if location_id.should_impact_quants() and ml.product_id.type == 'product': + if not location_id.should_bypass_reservation(): ml._free_reservation(product_id, location_id, quantity, lot_id=lot_id, package_id=package_id, owner_id=owner_id) + if not float_is_zero(quantity, precision_digits=precision): available_qty, in_date = Quant._update_available_quantity(product_id, location_id, -quantity, lot_id=lot_id, package_id=package_id, owner_id=owner_id) if available_qty < 0 and lot_id: # see if we can compensate the negative quants with some untracked quants @@ -217,8 +214,8 @@ class StockMoveLine(models.Model): taken_from_untracked_qty = min(untracked_qty, abs(available_qty)) Quant._update_available_quantity(product_id, location_id, -taken_from_untracked_qty, lot_id=False, package_id=package_id, owner_id=owner_id) Quant._update_available_quantity(product_id, location_id, taken_from_untracked_qty, lot_id=lot_id, package_id=package_id, owner_id=owner_id) - ml._free_reservation(ml.product_id, location_id, untracked_qty, lot_id=False, package_id=package_id, owner_id=owner_id) - if location_dest_id.should_impact_quants() and ml.product_id.type == 'product' and qty_done: + 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')) @@ -233,8 +230,8 @@ class StockMoveLine(models.Model): for ml in self: if ml.state in ('done', 'cancel'): raise UserError(_('You can not delete pack operations of a done picking')) - # 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): + # Unlinking a move line should unreserve. + if ml.product_id.type == 'product' and not ml.location_id.should_bypass_reservation() 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, strict=True) return super(StockMoveLine, self).unlink() @@ -290,16 +287,16 @@ class StockMoveLine(models.Model): # Now, we can actually move the quant. for ml in self - ml_to_delete: - if ml.product_id.type != 'consu': + 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 - if ml.location_id.should_impact_quants() and float_compare(ml.qty_done, ml.product_qty, precision_rounding=rounding) > 0: + if not ml.location_id.should_bypass_reservation() and float_compare(ml.qty_done, ml.product_qty, precision_rounding=rounding) > 0: extra_qty = ml.qty_done - ml.product_qty ml._free_reservation(ml.product_id, ml.location_id, extra_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id) # unreserve what's been reserved - if ml.location_id.should_impact_quants() and ml.product_id.type == 'product' and ml.product_qty: + if not ml.location_id.should_bypass_reservation() 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, strict=True) except UserError: @@ -307,18 +304,15 @@ class StockMoveLine(models.Model): # 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') - in_date = None - if ml.location_id.should_impact_quants() and ml.product_id.type == 'product': - available_qty, in_date = Quant._update_available_quantity(ml.product_id, ml.location_id, -quantity, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id) - if available_qty < 0 and ml.lot_id: - # see if we can compensate the negative quants with some untracked quants - untracked_qty = Quant._get_available_quantity(ml.product_id, ml.location_id, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id, strict=True) - if untracked_qty: - taken_from_untracked_qty = min(untracked_qty, abs(quantity)) - Quant._update_available_quantity(ml.product_id, ml.location_id, -taken_from_untracked_qty, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id) - Quant._update_available_quantity(ml.product_id, ml.location_id, taken_from_untracked_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id) - if ml.location_dest_id.should_impact_quants() and ml.product_id.type == 'product': - Quant._update_available_quantity(ml.product_id, ml.location_dest_id, quantity, lot_id=ml.lot_id, package_id=ml.result_package_id, owner_id=ml.owner_id, in_date=in_date) + available_qty, in_date = Quant._update_available_quantity(ml.product_id, ml.location_id, -quantity, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id) + if available_qty < 0 and ml.lot_id: + # see if we can compensate the negative quants with some untracked quants + untracked_qty = Quant._get_available_quantity(ml.product_id, ml.location_id, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id, strict=True) + if untracked_qty: + taken_from_untracked_qty = min(untracked_qty, abs(quantity)) + Quant._update_available_quantity(ml.product_id, ml.location_id, -taken_from_untracked_qty, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id) + Quant._update_available_quantity(ml.product_id, ml.location_id, taken_from_untracked_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id) + Quant._update_available_quantity(ml.product_id, ml.location_dest_id, quantity, lot_id=ml.lot_id, package_id=ml.result_package_id, owner_id=ml.owner_id, in_date=in_date) def _free_reservation(self, product_id, location_id, quantity, lot_id=None, package_id=None, owner_id=None): """ When editing a done move line or validating one with some forced quantities, it is diff --git a/addons/stock/models/stock_production_lot.py b/addons/stock/models/stock_production_lot.py index d9197ad96ac..06cc2917502 100644 --- a/addons/stock/models/stock_production_lot.py +++ b/addons/stock/models/stock_production_lot.py @@ -39,7 +39,9 @@ class ProductionLot(models.Model): @api.one def _product_qty(self): - self.product_qty = sum(self.quant_ids.mapped('quantity')) + # We only care for the quants in internal or transit locations. + quants = self.quant_ids.filtered(lambda q: q.location_id.usage in ['internal', 'transit']) + self.product_qty = sum(quants.mapped('quantity')) @api.multi def action_traceability(self): diff --git a/addons/stock/models/stock_traceability.py b/addons/stock/models/stock_traceability.py index bd2861e60a6..c40e7a76a47 100644 --- a/addons/stock/models/stock_traceability.py +++ b/addons/stock/models/stock_traceability.py @@ -90,6 +90,7 @@ class MrpStockReport(models.TransientModel): quant_ids = self.env['stock.quant'].search([ ('lot_id', '=', context.get('active_id')), ('quantity', '<', 0), + ('location_id.usage', '=', 'internal'), ]) res += self._lines(line_id, model_id=model_id, model='stock.quant', level=level, parent_quant=parent_quant, stream=stream, obj_ids=quant_ids) @@ -104,6 +105,7 @@ class MrpStockReport(models.TransientModel): quant_ids = self.env['stock.quant'].search([ ('lot_id', '=', context.get('active_id')), ('quantity', '>', 0), + ('location_id.usage', '=', 'internal'), ]) res += self._lines(line_id, model_id=model_id, model='stock.quant', level=level, parent_quant=parent_quant, stream=stream, obj_ids=quant_ids) diff --git a/addons/stock/test/packing.yml b/addons/stock/test/packing.yml index b1c18b0d767..b84e38e5985 100644 --- a/addons/stock/test/packing.yml +++ b/addons/stock/test/packing.yml @@ -72,11 +72,12 @@ !python {model: stock.picking, id: pick1}: | self.action_done() - - Check the system created 3 quants one with 120 pieces on pallet 1, one with 120 pieces on pallet 2 with lot A and 60 pieces on pallet 3 + Check the system created 5 quants one with 120 pieces on pallet 1, one with 120 pieces on pallet 2 with lot A and 60 pieces on pallet 3 in stock + and 1 with -180 pieces and another with -120 piece lot A in supplier - !python {model: stock.quant, id: False}: | quants = self.search([('product_id','=',ref('product1'))]) - assert len(quants.ids) == 3, "The number of quants created is not correct" + assert len(quants.ids) == 5, "The number of quants created is not correct" for quant in quants: if quant.package_id.name == 'Pallet 1': assert quant.quantity == 120, "Should have 120 pieces on pallet 1" @@ -137,7 +138,7 @@ elif rec.package_id.name == 'Pallet 3' and rec.location_id.id == ref('stock_location_stock'): assert rec.quantity == 50, "Should have 50 pieces in stock on pallet 3" else: - assert False, "Unrecognized quant" + assert rec.location_id.id != ref('stock_location_stock'), "Unrecognized quant in stock" - Check a backorder was created and on that backorder, prepare partial and process backorder - diff --git a/addons/stock/test/packingneg.yml b/addons/stock/test/packingneg.yml index 49931e0b1ca..ca3305dad0b 100644 --- a/addons/stock/test/packingneg.yml +++ b/addons/stock/test/packingneg.yml @@ -120,7 +120,7 @@ elif rec.package_id.name == 'Palneg 3' and rec.location_id.id == ref('stock_location_stock'): pallet_3_stock_qty += rec.quantity else: - assert False, "Unrecognized quant" + assert rec.location_id.id != ref('stock_location_stock'), "Unrecognized quant in stock" assert pallet_3_stock_qty == 50, "Should have 50 pieces in stock on pallet 3" - Create a picking for reconciling the negative quant @@ -148,8 +148,9 @@ Check the negative quant was reconciled - !python {model: stock.quant, id: False}: | - neg_quants = self.search([('product_id','=', ref('product_neg')), ('quantity', '<', 0)]) - assert len(neg_quants.ids) == 0, "Negative quants should have been reconciled" + neg_quants = self.search([('product_id','=', ref('product_neg')), ('quantity', '<', 0), ('location_id.id', '!=', + ref('stock_location_suppliers'))]) + assert len(neg_quants) == 0, "Negative quants should have been reconciled" pick = self.env['stock.picking'].browse(ref('delivery_reconcile')) customer_quant = self.search([ ('product_id', '=', ref('product_neg')), diff --git a/addons/stock/test/wiseoperator.yml b/addons/stock/test/wiseoperator.yml index b79072a9699..3814d744116 100644 --- a/addons/stock/test/wiseoperator.yml +++ b/addons/stock/test/wiseoperator.yml @@ -51,11 +51,11 @@ !python {model: stock.picking, id: pick1_wise}: | self.do_transfer() - - Check the system created 2 quants + Check the system created 3 quants - !python {model: stock.quant, id: False}: | records = self.search([('product_id','=',ref('product_wise'))]) - assert len(records.ids) == 2, "The number of quants created is not correct" + assert len(records.ids) == 3, "The number of quants created is not correct" - Make a delivery order of 5 pieces to the customer - @@ -138,7 +138,8 @@ Check there was no negative quant created by this picking - !python {model: stock.quant, id: False}: | - records = self.search([('product_id','=',ref('product_wise')), ('quantity', '<', 0.0)]) + records = self.search([('product_id','=',ref('product_wise')), ('quantity', '<', 0.0), ('location_id.id', '=', + ref('stock_location_stock'))]) assert len(records.ids) == 0, 'This should not have created a negative quant' - Check the other delivery order has changed its state back to partially available @@ -149,10 +150,10 @@ Process the second picking - !python {model: stock.picking, id: delivery_order_wise2}: | - self.do_transfer() + self.action_done() - Check all quants are in Customers and there are no negative quants anymore - !python {model: stock.quant, id: False}: | - records = self.search([('product_id','=',ref('product_wise'))]) - assert all([x.location_id.id==ref('stock_location_customers') and x.qty > 0.0 for x in records]), "Negative quant or wrong location detected" \ No newline at end of file + records = self.search([('product_id','=',ref('product_wise')), ('location_id', '!=', ref('stock_location_suppliers'))]) + assert all([x.location_id.id==ref('stock_location_customers') and x.quantity > 0.0 for x in records]), "Negative quant or wrong location detected" \ No newline at end of file diff --git a/addons/stock/tests/test_move.py b/addons/stock/tests/test_move.py index a0902c455ce..23ff2a8b99d 100644 --- a/addons/stock/tests/test_move.py +++ b/addons/stock/tests/test_move.py @@ -70,9 +70,9 @@ class StockMove(TransactionCase): move1.action_done() self.assertEqual(move1.state, 'done') # no quants are created in the supplier location - 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), -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)), 0.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) def test_in_2(self): @@ -111,9 +111,9 @@ class StockMove(TransactionCase): self.assertEqual(move1.state, 'done') # no quants are created in the supplier location - self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product3, self.supplier_location), 0.0) + self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product3, self.supplier_location), -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)), 0.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(quants), 1.0) for quant in quants: @@ -160,11 +160,11 @@ class StockMove(TransactionCase): self.assertEqual(move1.product_qty, 5) # don't change reservation self.assertEqual(move1.state, 'done') - # no quants are created in the supplier location - self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product2, self.supplier_location), 0.0) + # 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), -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)), 0.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(quants), 5.0) for quant in quants: @@ -211,9 +211,9 @@ class StockMove(TransactionCase): # validation move1.action_done() self.assertEqual(move1.state, 'done') - # # no quants are created in the customer location - 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) + # 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) # 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) @@ -260,7 +260,7 @@ class StockMove(TransactionCase): # validation move1.action_done() self.assertEqual(move1.state, 'done') - # # no quants are created in the customer location + # 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) # there should be no quant amymore in the stock location @@ -2466,12 +2466,14 @@ class StockMove(TransactionCase): move2.action_done() initial_in_date_lot2 = self.env['stock.quant'].search([ + ('location_id', '=', self.stock_location.id), ('product_id', '=', self.product3.id), ('lot_id', '=', lot2.id), ]).in_date # Edit lot1's incoming date. quant_lot1 = self.env['stock.quant'].search([ + ('location_id', '=', self.stock_location.id), ('product_id', '=', self.product3.id), ('lot_id', '=', lot1.id), ]) @@ -2508,6 +2510,7 @@ class StockMove(TransactionCase): # Check that lot1 correctly is back to stock with its right in_date quant_lot1 = self.env['stock.quant'].search([ + ('location_id.usage', '=', 'internal'), ('product_id', '=', self.product3.id), ('lot_id', '=', lot1.id), ]) @@ -2516,6 +2519,7 @@ class StockMove(TransactionCase): # Check that lo2 is in pack with is right in_date quant_lot2 = self.env['stock.quant'].search([ + ('location_id.usage', '=', 'internal'), ('product_id', '=', self.product3.id), ('lot_id', '=', lot2.id), ]) @@ -2567,12 +2571,14 @@ class StockMove(TransactionCase): move2.action_done() initial_in_date_lot2 = self.env['stock.quant'].search([ + ('location_id', '=', self.stock_location.id), ('product_id', '=', self.product3.id), ('lot_id', '=', lot2.id), ]).in_date # Edit lot1's incoming date. quant_lot1 = self.env['stock.quant'].search([ + ('location_id.usage', '=', 'internal'), ('product_id', '=', self.product3.id), ('lot_id', '=', lot1.id), ]) @@ -2606,6 +2612,7 @@ class StockMove(TransactionCase): }) quants = self.env['stock.quant'].search([ + ('location_id.usage', '=', 'internal'), ('product_id', '=', self.product3.id), ]) self.assertEqual(len(quants), 2) diff --git a/addons/stock/tests/test_stock_flow.py b/addons/stock/tests/test_stock_flow.py index 07b958578d6..7e44febe1e9 100644 --- a/addons/stock/tests/test_stock_flow.py +++ b/addons/stock/tests/test_stock_flow.py @@ -1415,9 +1415,9 @@ class TestStockFlow(TestStockCommon): # Check picking_in_package is in picking_pack_package self.assertEqual(picking_in_package.id, picking_pack_package.id, 'The package created in the picking in is not in the one created in picking pack') self.assertEqual(picking_pack_package.id, picking_out_package.id, 'The package created in the picking in is not in the one created in picking pack') - # Check that we haven't quant in customer location. - quant = self.StockQuantObj.search([('location_id', '=', self.customer_location)]) - self.assertEqual(len(quant), 0, 'There should be no package for customer location') + # Check that we have one quant in customer location. + quant = self.StockQuantObj.search([('product_id', '=', self.productA.id), ('location_id', '=', self.customer_location)]) + self.assertEqual(len(quant), 1, 'There should be one quant with package for customer location') # Check that the parent package of the quant is the picking_in_package def test_50_create_in_out_with_product_pack_lines(self): @@ -1471,9 +1471,11 @@ class TestStockFlow(TestStockCommon): packout2.package_id = pack2 packout2.qty_done = 1 picking_out.action_done() - # Check there are no negative quants + # Should be only 1 negative quant in supplier location neg_quants = self.env['stock.quant'].search([('product_id', '=', self.productE.id), ('quantity', '<', 0.0)]) - self.assertEqual(len(neg_quants), 0, 'There are negative quants!') + self.assertEqual(len(neg_quants), 1, 'There should be 1 negative quants for supplier!') + self.assertEqual(neg_quants.location_id.id, self.supplier_location, 'There shoud be 1 negative quants for supplier!') + quants = self.env['stock.quant']._gather(self.productE, self.env['stock.location'].browse(self.stock_location)) self.assertEqual(len(quants), 2, 'We should have exactly 2 quants in the end') @@ -1538,9 +1540,10 @@ class TestStockFlow(TestStockCommon): 'product_uom_id': self.productE.uom_id.id, }) picking_out.action_done() - # Check there are no negative quants + # Should be only 1 negative quant in supplier location neg_quants = self.env['stock.quant'].search([('product_id', '=', self.productE.id), ('quantity', '<', 0.0)]) - self.assertEqual(len(neg_quants), 0, 'There are negative quants!') + self.assertEqual(len(neg_quants), 1, 'There should be 1 negative quants for supplier!') + 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') diff --git a/addons/stock/tests/test_warehouse.py b/addons/stock/tests/test_warehouse.py index 039b90683cf..23a44df81c6 100644 --- a/addons/stock/tests/test_warehouse.py +++ b/addons/stock/tests/test_warehouse.py @@ -37,7 +37,7 @@ class TestWarehouse(TestStockCommon): self.assertEqual(inventory.move_ids.location_dest_id, self.env.ref('stock.location_inventory')) # Inventory loss self.assertEqual(inventory.move_ids.state, 'done') quants = self.env['stock.quant']._gather(self.product_1, self.env.ref('stock.location_inventory')) - self.assertEqual(len(quants), 0) # No quant created for inventory loss + self.assertEqual(len(quants), 1) # One quant created for inventory loss # Check quantity of product in various locations: current, its parent, brother and other self.assertEqual(self.env['stock.quant']._gather(self.product_1, self.warehouse_1.lot_stock_id).quantity, 35.0) @@ -67,9 +67,9 @@ class TestWarehouse(TestStockCommon): self.assertEqual(inventory.line_ids.product_id, self.product_1) self.assertEqual(inventory.line_ids.product_qty, 50.0) - # Check associated quants: 1 quant for the product and the quantity + # Check associated quants: 2 quants for the product and the quantity (1 in stock, 1 in inventory adjustment) quant = self.env['stock.quant'].search([('id', 'not in', self.existing_quants.ids)]) - self.assertEqual(len(quant), 1) + self.assertEqual(len(quant), 2) # print quant.name, quant.product_id, quant.location_id # TDE TODO: expand this test @@ -225,9 +225,9 @@ class TestWarehouse(TestStockCommon): quants = self.env['stock.quant'].search([('product_id', '=', productA.id), ('location_id', '=', stock_location.id)]) self.assertEqual(len(quants), 0) - # There should be no quant in the inventory loss location + # 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)]) - self.assertEqual(len(quant), 0) + self.assertEqual(len(quant), 1) class TestResupply(TestStockCommon): diff --git a/addons/stock/views/stock_quant_views.xml b/addons/stock/views/stock_quant_views.xml index 50625c85375..e92515f6037 100644 --- a/addons/stock/views/stock_quant_views.xml +++ b/addons/stock/views/stock_quant_views.xml @@ -48,7 +48,7 @@ - + @@ -68,7 +68,6 @@ - @@ -91,6 +90,7 @@ stock.quant form tree,form,pivot + {'search_default_internal_loc': 1, 'search_default_transit_loc': 1} This analysis gives you a fast overview on the current stock level of your products and their current inventory value.