From 850bb6cae4d10d452ca59f834190585bf3e4f0de Mon Sep 17 00:00:00 2001 From: William Henrotin Date: Wed, 9 May 2018 15:55:59 +0200 Subject: [PATCH] [FIX] stock: block updating uom factor if reserved moves. This commit block users to change unit of measure factor. if there are reserved move on product using this UoM. This will allow the quantities to be correctly unreserved when the stock move will be confirmed. Task id : 1844684 --- addons/stock/models/product.py | 20 +++++++++ addons/stock/tests/__init__.py | 1 + addons/stock/tests/test_robustness.py | 65 +++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 addons/stock/tests/test_robustness.py diff --git a/addons/stock/models/product.py b/addons/stock/models/product.py index fef75714961..632983fab30 100644 --- a/addons/stock/models/product.py +++ b/addons/stock/models/product.py @@ -583,3 +583,23 @@ class ProductCategory(models.Model): category = category.parent_id routes |= category.route_ids self.total_route_ids = routes + + +class ProductUoM(models.Model): + _inherit = 'product.uom' + + def write(self, values): + # Users can not update the factor if open stock moves are based on it + if 'factor' in values or 'factor_inv' in values or 'category_id' in values: + stock_move_lines = self.env['stock.move.line'].search_count([ + ('product_uom_id.category_id', '=', self.category_id.id), + ('state', '!=', 'cancel'), + ]) + + if stock_move_lines: + raise UserError(_( + "You cannot change the ratio of this unit of mesure as some" + " products with this UoM have already been moved or are " + "currently reserved." + )) + return super(ProductUoM, self).write(values) diff --git a/addons/stock/tests/__init__.py b/addons/stock/tests/__init__.py index a9a18e270e4..f0272143725 100644 --- a/addons/stock/tests/__init__.py +++ b/addons/stock/tests/__init__.py @@ -9,3 +9,4 @@ from . import test_quant from . import test_inventory from . import test_move from . import test_move2 +from . import test_robustness diff --git a/addons/stock/tests/test_robustness.py b/addons/stock/tests/test_robustness.py new file mode 100644 index 00000000000..4e49a4c9bc1 --- /dev/null +++ b/addons/stock/tests/test_robustness.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.exceptions import UserError +from odoo.tests.common import TransactionCase + + +class TestRobustness(TransactionCase): + def setUp(self): + super(TestRobustness, self).setUp() + self.stock_location = self.env.ref('stock.stock_location_stock') + self.customer_location = self.env.ref('stock.stock_location_customers') + self.uom_unit = self.env.ref('product.product_uom_unit') + self.uom_dozen = self.env.ref('product.product_uom_dozen') + self.product1 = self.env['product.product'].create({ + 'name': 'Product A', + 'type': 'product', + 'categ_id': self.env.ref('product.product_category_all').id, + }) + + def test_uom_factor(self): + """ Changing the factor of a unit of measure shouldn't be allowed while + quantities are reserved, else the existing move lines won't be consistent + with the `reserved_quantity` on quants. + """ + # make some stock + self.env['stock.quant']._update_available_quantity( + self.product1, + self.stock_location, + 12, + ) + + # reserve a dozen + move1 = self.env['stock.move'].create({ + 'name': 'test_uom_rounding', + 'location_id': self.stock_location.id, + 'location_dest_id': self.customer_location.id, + 'product_id': self.product1.id, + 'product_uom': self.uom_dozen.id, + 'product_uom_qty': 1, + }) + move1._action_confirm() + move1._action_assign() + self.assertEqual(move1.state, 'assigned') + quant = self.env['stock.quant']._gather( + self.product1, + self.stock_location, + ) + + # assert the reservation + self.assertEqual(quant.reserved_quantity, 12) + self.assertEqual(move1.product_qty, 12) + + # change the factor + with self.assertRaises(UserError): + with self.cr.savepoint(): + move1.product_uom.factor = 0.05 + + # assert the reservation + self.assertEqual(quant.reserved_quantity, 12) + self.assertEqual(move1.state, 'assigned') + self.assertEqual(move1.product_qty, 12) + + # unreserve + move1._do_unreserve()