diff --git a/stock_inventory/models/stock_inventory.py b/stock_inventory/models/stock_inventory.py index 472618197c7b..69ce54d6ced2 100644 --- a/stock_inventory/models/stock_inventory.py +++ b/stock_inventory/models/stock_inventory.py @@ -253,23 +253,37 @@ def refresh_stock_quant_ids(self): for rec in self: rec.stock_quant_ids = rec._get_quants(rec.location_ids) + def _get_quant_joined_names(self, quants, field): + return ", ".join(quants.mapped(f"{field}.display_name")) + def action_state_to_in_progress(self): self.ensure_one() - active_rec = self.env["stock.inventory"].search( - [ - ("state", "=", "in_progress"), - ("location_ids", "child_of", self.location_ids.ids), - ], - limit=1, - ) - if active_rec: - raise UserError( - _( - "There's already an Adjustment in Process using one " - "requested Location: %s" - ) - % active_rec.name + search_filter = [ + ( + "location_id", + "child_of" if not self.exclude_sublocation else "in", + self.location_ids.ids, + ), + ("to_do", "=", True), + ] + + if self.product_ids: + search_filter.append(("product_id", "in", self.product_ids.ids)) + error_field = "product_id" + error_message = _( + "There are active adjustments for the requested products: %s" ) + else: + error_field = "location_id" + error_message = _( + "There's already an Adjustment in Process using one requested Location: %s" + ) + + quants = self.env["stock.quant"].search(search_filter) + if quants: + names = self._get_quant_joined_names(quants, error_field) + raise ValidationError(error_message % names) + quants = self._get_quants(self.location_ids) self.write( { @@ -291,7 +305,7 @@ def action_state_to_done(self): self.state = "done" self.stock_quant_ids.update( { - "to_do": True, + "to_do": False, "user_id": False, "inventory_date": False, } @@ -309,7 +323,7 @@ def action_state_to_draft(self): self.state = "draft" self.stock_quant_ids.update( { - "to_do": True, + "to_do": False, "user_id": False, "inventory_date": False, } @@ -365,30 +379,38 @@ def action_view_stock_moves(self): result["context"] = {} return result - @api.constrains("state", "location_ids") def _check_inventory_in_progress_not_override(self): - inventories = self.search([("state", "=", "in_progress")]) - for rec in inventories: - inventory = inventories.filtered( - lambda x, rec=rec: x.id != rec.id - and ( - any(i in x.location_ids for i in rec.location_ids) - or ( - any( - i in x.location_ids.child_internal_location_ids - for i in rec.location_ids - ) - and not x.exclude_sublocation + for rec in self: + if rec.state == "in_progress": + location_condition = [ + ( + "location_ids", + "child_of" if not rec.exclude_sublocation else "in", + rec.location_ids.ids, ) - ) - ) - if len(inventory) > 0: - raise ValidationError( - _( - "Cannot be more than one in progress inventory adjustment " - "affecting the same location at the same time." + ] + if rec.product_ids: + product_condition = [ + ("state", "=", "in_progress"), + ("id", "!=", rec.id), + ("product_ids", "in", rec.product_ids.ids), + ] + location_condition + inventories = self.search(product_condition) + else: + inventories = self.search( + [("state", "=", "in_progress"), ("id", "!=", rec.id)] + + location_condition ) - ) + for inventory in inventories: + if any( + i in inventory.location_ids.ids for i in rec.location_ids.ids + ): + raise ValidationError( + _( + "Cannot have more than one in-progress inventory adjustment " + "affecting the same location or product at the same time." + ) + ) @api.constrains("product_selection", "product_ids") def _check_one_product_in_product_selection(self): diff --git a/stock_inventory/models/stock_quant.py b/stock_inventory/models/stock_quant.py index a27ee12159ee..ba8b4afe4044 100644 --- a/stock_inventory/models/stock_quant.py +++ b/stock_inventory/models/stock_quant.py @@ -4,7 +4,7 @@ class StockQuant(models.Model): _inherit = "stock.quant" - to_do = fields.Boolean(default=True) + to_do = fields.Boolean(default=False) def _apply_inventory(self): res = super()._apply_inventory() diff --git a/stock_inventory/tests/test_stock_inventory.py b/stock_inventory/tests/test_stock_inventory.py index 6a5ccfad8f44..a662b3fb6998 100644 --- a/stock_inventory/tests/test_stock_inventory.py +++ b/stock_inventory/tests/test_stock_inventory.py @@ -408,3 +408,116 @@ def test_07_stock_inventory_auto_complete(self): self.assertEqual(inventory1.count_stock_moves, 2) self.assertEqual(inventory1.count_stock_quants, 2) self.assertEqual(inventory1.state, "done") + + def test_08_multiple_inventories_same_location(self): + quant1 = self.quant_model.search( + [ + ("product_id", "=", self.product.id), + ("location_id", "=", self.location1.id), + ] + ) + self.assertTrue(quant1) + self.quant_model.sudo().create( + { + "product_id": self.product2.id, + "quantity": 100.0, + "location_id": self.location1.id, + } + ) + quant2 = self.quant_model.search( + [ + ("product_id", "=", self.product2.id), + ("location_id", "=", self.location1.id), + ] + ) + self.assertTrue(quant2) + inventory1 = self.inventory_model.create( + { + "name": "Inventory_Test_8_1", + "product_selection": "manual", + "location_ids": [self.location1.id], + "product_ids": [self.product.id], + } + ) + inventory1.action_state_to_in_progress() + self.assertEqual(inventory1.state, "in_progress") + inventory2 = self.inventory_model.create( + { + "name": "Inventory_Test_8_2", + "product_selection": "manual", + "location_ids": [self.location1.id], + "product_ids": [self.product2.id], + } + ) + inventory2.action_state_to_in_progress() + self.assertEqual(inventory2.state, "in_progress") + self.assertEqual(inventory1.state, "in_progress") + + def test_09_product_inventory_global_and_sublocations_review(self): + self.location4 = self.location_model.create( + { + "name": "Location 4", + "usage": "internal", + "location_id": self.location1.id, + } + ) + location_global = self.location_model.create( + { + "name": "Global Location", + "usage": "internal", + } + ) + self.location1.location_id = location_global.id + self.location2.location_id = location_global.id + self.location3.location_id = location_global.id + self.location4.location_id = location_global.id + inventory_global = self.inventory_model.create( + { + "name": "Inventory_Global", + "product_selection": "manual", + "location_ids": [location_global.id], + "product_ids": [self.product.id], + } + ) + inventory_global.action_state_to_in_progress() + self.assertEqual(inventory_global.state, "in_progress") + inventory_sub_no_product = self.inventory_model.create( + { + "name": "Inventory_Sub_No_Product", + "product_selection": "manual", + "location_ids": [self.location4.id], + } + ) + inventory_sub_no_product.action_state_to_in_progress() + self.assertEqual(inventory_sub_no_product.state, "in_progress") + with self.assertRaises(ValidationError), self.cr.savepoint(): + inventory_sub_with_product = self.inventory_model.create( + { + "name": "Inventory_Sub_With_Product", + "product_selection": "manual", + "location_ids": [self.location1.id], + } + ) + inventory_sub_with_product.action_state_to_in_progress() + + def test_10_inventory_quant_to_do_states(self): + inventory = self.inventory_model.create( + { + "name": "Inventory_Test_10", + "product_selection": "manual", + "location_ids": [self.location1.id], + "product_ids": [self.product.id], + } + ) + inventory.action_state_to_in_progress() + quants = inventory.stock_quant_ids + self.assertTrue(all(quant.to_do for quant in quants)) + inventory.action_state_to_draft() + self.assertFalse(inventory.stock_quant_ids) + self.assertTrue(all(not quant.to_do for quant in quants)) + inventory.action_state_to_in_progress() + quants = inventory.stock_quant_ids + self.assertTrue(all(quant.to_do for quant in quants)) + self.assertTrue(inventory.stock_quant_ids) + inventory.action_state_to_done() + self.assertTrue(all(not quant.to_do for quant in quants))