From e28db5cc46245552c6282a0e2e907f12ecdb3ddc Mon Sep 17 00:00:00 2001 From: Denis Roussel Date: Wed, 4 Dec 2024 15:49:38 +0100 Subject: [PATCH] [IMP] stock_location_product_restriction: Don't check zero quants on locations For location restriction violation, don't check zero quants --- .../models/stock_location.py | 59 ++++++++++++++++++- .../tests/test_stock_location.py | 29 +++++++-- 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/stock_location_product_restriction/models/stock_location.py b/stock_location_product_restriction/models/stock_location.py index ae4cae0a2..6e36c2772 100644 --- a/stock_location_product_restriction/models/stock_location.py +++ b/stock_location_product_restriction/models/stock_location.py @@ -70,7 +70,25 @@ class StockLocation(models.Model): @api.depends("product_restriction") def _compute_restriction_violation(self): records = self + self.env["stock.quant"].flush_model( + [ + "product_id", + "location_id", + "quantity", + "reserved_quantity", + "available_quantity", + "inventory_quantity", + ] + ) + self.flush_model( + [ + "product_restriction", + ] + ) ProductProduct = self.env["product.product"] + precision_digits = max( + 6, self.sudo().env.ref("product.decimal_product_uom").digits * 2 + ) SQL = """ SELECT stock_quant.location_id, @@ -82,6 +100,11 @@ class StockLocation(models.Model): stock_quant.location_id in %s and stock_location.id = stock_quant.location_id and stock_location.product_restriction = 'same' + /* Mimic the _unlink_zero_quant() query in Odoo */ + AND (NOT (round(quantity::numeric, %s) = 0 OR quantity IS NULL) + OR NOT round(reserved_quantity::numeric, %s) = 0 + OR NOT (round(inventory_quantity::numeric, %s) = 0 + OR inventory_quantity IS NULL)) GROUP BY stock_quant.location_id HAVING count(distinct(product_id)) > 1 @@ -93,7 +116,9 @@ class StockLocation(models.Model): if not ids: product_ids_by_location_id = dict() else: - self.env.cr.execute(SQL, (ids,)) + self.env.cr.execute( + SQL, (ids, precision_digits, precision_digits, precision_digits) + ) product_ids_by_location_id = dict(self.env.cr.fetchall()) for record in self: record_id = record.id @@ -112,12 +137,30 @@ class StockLocation(models.Model): record.restriction_violation_message = restriction_violation_message def _search_has_restriction_violation(self, operator, value): + precision_digits = max( + 6, self.sudo().env.ref("product.decimal_product_uom").digits * 2 + ) search_has_violation = ( # has_restriction_violation != False (operator in NEGATIVE_TERM_OPERATORS and not value) # has_restriction_violation = True or (operator not in NEGATIVE_TERM_OPERATORS and value) ) + self.env["stock.quant"].flush_model( + [ + "product_id", + "location_id", + "quantity", + "reserved_quantity", + "available_quantity", + "inventory_quantity", + ] + ) + self.flush_model( + [ + "product_restriction", + ] + ) SQL = """ SELECT stock_quant.location_id @@ -127,11 +170,23 @@ class StockLocation(models.Model): WHERE stock_location.id = stock_quant.location_id and stock_location.product_restriction = 'same' + /* Mimic the _unlink_zero_quant() query in Odoo */ + AND (NOT (round(quantity::numeric, %s) = 0 OR quantity IS NULL) + OR NOT round(reserved_quantity::numeric, %s) = 0 + OR NOT (round(inventory_quantity::numeric, %s) = 0 + OR inventory_quantity IS NULL)) GROUP BY stock_quant.location_id HAVING count(distinct(product_id)) > 1 """ - self.env.cr.execute(SQL) + self.env.cr.execute( + SQL, + ( + precision_digits, + precision_digits, + precision_digits, + ), + ) violation_ids = [r[0] for r in self.env.cr.fetchall()] if search_has_violation: op = "in" diff --git a/stock_location_product_restriction/tests/test_stock_location.py b/stock_location_product_restriction/tests/test_stock_location.py index 39e154191..d822d7344 100644 --- a/stock_location_product_restriction/tests/test_stock_location.py +++ b/stock_location_product_restriction/tests/test_stock_location.py @@ -38,7 +38,7 @@ class TestStockLocation(TransactionCase): # quants StockQuant = cls.env["stock.quant"] - StockQuant.create( + cls.quant_1_lvl_1_1_1 = StockQuant.create( { "product_id": cls.product_1.id, "location_id": cls.loc_lvl_1_1_1.id, @@ -46,7 +46,7 @@ class TestStockLocation(TransactionCase): "owner_id": cls.env.user.id, } ) - StockQuant.create( + cls.quant_2_lvl_1_1_1 = StockQuant.create( { "product_id": cls.product_2.id, "location_id": cls.loc_lvl_1_1_1.id, @@ -54,7 +54,7 @@ class TestStockLocation(TransactionCase): "owner_id": cls.env.user.id, } ) - StockQuant.create( + cls.quant_1_lvl_1_1_2 = StockQuant.create( { "product_id": cls.product_1.id, "location_id": cls.loc_lvl_1_1_2.id, @@ -62,7 +62,7 @@ class TestStockLocation(TransactionCase): "owner_id": cls.env.user.id, } ) - StockQuant.create( + cls.quant_2_lvl_1_1_2 = StockQuant.create( { "product_id": cls.product_2.id, "location_id": cls.loc_lvl_1_1_2.id, @@ -262,3 +262,24 @@ class TestStockLocation(TransactionCase): # Check location creation with Form(self.StockLocation) as location_form: location_form.name = "Test" + + def test_zero_quant(self): + """ + Data: + * Location level_1_1_1 with 2 different products no restriction + Test Case: + 1. Check restriction message + 2. Change product 1 quant to 0.0 + 3. Set restriction 'same' on location level_1_1_1 + 4. Check restriction message + Expected result: + 1. No restriction message + """ + self.loc_lvl_1_1_1.product_restriction = "any" + self.assertFalse(self.loc_lvl_1_1_1.has_restriction_violation) + self.assertFalse(self.loc_lvl_1_1_1.restriction_violation_message) + self.quant_1_lvl_1_1_1.inventory_quantity = 0.0 + self.quant_1_lvl_1_1_1._apply_inventory() + self.loc_lvl_1_1_1.product_restriction = "same" + self.assertFalse(self.loc_lvl_1_1_1.has_restriction_violation) + self.assertFalse(self.loc_lvl_1_1_1.restriction_violation_message)