diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py index ed567ed0c9f..817932a1d8b 100644 --- a/addons/stock/models/stock_move_line.py +++ b/addons/stock/models/stock_move_line.py @@ -67,6 +67,12 @@ class StockMoveLine(models.Model): def _compute_product_qty(self): self.product_qty = self.product_uom_id._compute_quantity(self.product_uom_qty, self.product_id.uom_id, rounding_method='HALF-UP') + @api.constrains('lot_id', 'product_id') + def _check_lot_product(self): + for line in self: + if line.lot_id and line.product_id != line.lot_id.product_id: + raise ValidationError(_('This lot %s is incompatible with this product %s' % (line.lot_id.name, line.product_id.display_name))) + @api.one def _set_product_qty(self): """ The meaning of product_qty field changed lately and is now a functional field computing the quantity diff --git a/addons/stock/tests/test_move.py b/addons/stock/tests/test_move.py index 53b9b842cc7..b1810cd5193 100644 --- a/addons/stock/tests/test_move.py +++ b/addons/stock/tests/test_move.py @@ -2997,11 +2997,11 @@ class StockMove(TransactionCase): """ lot1 = self.env['stock.production.lot'].create({ 'name': 'lot1', - 'product_id': self.product1.id, + 'product_id': self.product3.id, }) lot2 = self.env['stock.production.lot'].create({ 'name': 'lot2', - 'product_id': self.product1.id, + 'product_id': self.product3.id, }) package1 = self.env['stock.quant.package'].create({'name': 'test_edit_reserved_move_line_5'}) diff --git a/addons/stock/tests/test_robustness.py b/addons/stock/tests/test_robustness.py index 5926a6e2487..b00409ff881 100644 --- a/addons/stock/tests/test_robustness.py +++ b/addons/stock/tests/test_robustness.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo.exceptions import UserError +from odoo.exceptions import UserError, ValidationError from odoo.tests.common import TransactionCase @@ -145,3 +145,71 @@ class TestRobustness(TransactionCase): self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location, package_id=package)), 0) self.assertEqual(self.env['stock.quant']._gather(self.product1, self.stock_location).reserved_quantity, 0) + + def test_lot_id_product_id_mix(self): + """ Make sure it isn't possible to create a move line with a lot incompatible with its + product. + """ + product1 = self.env['product.product'].create({ + 'name': 'Product 1', + 'type': 'product', + 'categ_id': self.env.ref('product.product_category_all').id, + 'tracking': 'lot', + }) + product2 = self.env['product.product'].create({ + 'name': 'Product 2', + 'type': 'product', + 'categ_id': self.env.ref('product.product_category_all').id, + 'tracking': 'lot', + }) + + lot1 = self.env['stock.production.lot'].create({ + 'name': 'lot1', + 'product_id': product1.id, + }) + lot2 = self.env['stock.production.lot'].create({ + 'name': 'lot2', + 'product_id': product2.id, + }) + + self.env['stock.quant']._update_available_quantity(product1, self.stock_location, 1, lot_id=lot1) + self.env['stock.quant']._update_available_quantity(product2, self.stock_location, 1, lot_id=lot2) + + move1 = self.env['stock.move'].create({ + 'name': 'test_lot_id_product_id_mix_move_1', + 'location_id': self.stock_location.id, + 'location_dest_id': self.customer_location.id, + 'product_id': product1.id, + 'product_uom': self.uom_unit.id, + 'product_uom_qty': 1.0, + }) + move2 = self.env['stock.move'].create({ + 'name': 'test_lot_id_product_id_mix_move_2', + 'location_id': self.stock_location.id, + 'location_dest_id': self.customer_location.id, + 'product_id': product2.id, + 'product_uom': self.uom_unit.id, + 'product_uom_qty': 1.0, + }) + (move1 + move2)._action_confirm() + + with self.assertRaises(ValidationError): + move1.write({'move_line_ids': [(0, 0, { + 'product_id': product1.id, + 'product_uom_id': self.uom_unit.id, + 'qty_done': 1, + 'lot_id': lot2.id, + 'location_id': move1.location_id.id, + 'location_dest_id': move1.location_dest_id.id, + })]}) + + with self.assertRaises(ValidationError): + move2.write({'move_line_ids': [(0, 0, { + 'product_id': product2.id, + 'product_uom_id': self.uom_unit.id, + 'qty_done': 1, + 'lot_id': lot1.id, + 'location_id': move2.location_id.id, + 'location_dest_id': move2.location_dest_id.id, + })]}) +