diff --git a/stock_cycle_count/models/stock_inventory.py b/stock_cycle_count/models/stock_inventory.py index c813b3c02b3..bf342a68496 100644 --- a/stock_cycle_count/models/stock_inventory.py +++ b/stock_cycle_count/models/stock_inventory.py @@ -11,20 +11,6 @@ class StockInventory(models.Model): _inherit = "stock.inventory" - @api.depends("state", "stock_quant_ids") - def _compute_inventory_accuracy(self): - for inv in self: - theoretical = sum(inv.stock_quant_ids.mapped(lambda x: abs(x.quantity))) - abs_discrepancy = sum( - inv.stock_quant_ids.mapped(lambda x: abs(x.inventory_diff_quantity)) - ) - if theoretical: - inv.inventory_accuracy = max( - PERCENT * (theoretical - abs_discrepancy) / theoretical, 0.0 - ) - if not inv.stock_quant_ids and inv.state == "done": - inv.inventory_accuracy = PERCENT - prefill_counted_quantity = fields.Selection( string="Counted Quantities", help="Allows to start with a pre-filled counted quantity for each lines or " @@ -43,10 +29,10 @@ def _compute_inventory_accuracy(self): ) inventory_accuracy = fields.Float( string="Accuracy", - compute="_compute_inventory_accuracy", digits=(3, 2), store=True, group_operator="avg", + default=False, ) def _get_default_counted_quantitites(self): @@ -76,6 +62,26 @@ def _domain_cycle_count_candidate(self): ("location_id", "in", self.location_ids.ids), ] + def _calculate_inventory_accuracy(self): + for inv in self: + accuracy = 100 + sum_line_accuracy = 0 + sum_theoretical_qty = 0 + if inv.stock_move_ids: + for line in inv.stock_move_ids: + sum_line_accuracy += line.theoretical_qty * line.line_accuracy + sum_theoretical_qty += line.theoretical_qty + if sum_theoretical_qty != 0: + accuracy = (sum_line_accuracy / sum_theoretical_qty) * 100 + else: + accuracy = 0 + inv.update( + { + "inventory_accuracy": accuracy, + } + ) + return False + def _link_to_planned_cycle_count(self): self.ensure_one() domain = self._domain_cycle_count_candidate() @@ -100,11 +106,13 @@ def _link_to_planned_cycle_count(self): def action_state_to_done(self): res = super(StockInventory, self).action_state_to_done() + self._calculate_inventory_accuracy() self._update_cycle_state() return res def action_force_done(self): res = super(StockInventory, self).action_force_done() + self._calculate_inventory_accuracy() self._update_cycle_state() return res diff --git a/stock_cycle_count/tests/test_stock_cycle_count.py b/stock_cycle_count/tests/test_stock_cycle_count.py index b48ea426f74..06054bfa149 100644 --- a/stock_cycle_count/tests/test_stock_cycle_count.py +++ b/stock_cycle_count/tests/test_stock_cycle_count.py @@ -82,6 +82,9 @@ def setUp(self): self.product1 = self.product_model.create( {"name": "Test Product 1", "type": "product", "default_code": "PROD1"} ) + self.product2 = self.product_model.create( + {"name": "Test Product 2", "type": "product", "default_code": "PROD2"} + ) def _create_user(self, login, groups, company): group_ids = [group.id for group in groups] @@ -356,3 +359,121 @@ def test_cycle_count_contrains(self): inventory.exclude_sublocation = False company = self.env["res.company"].create({"name": "Test"}) inventory.company_id = company + + def test_inventory_adjustment_accuracy(self): + date = datetime.today() - timedelta(days=1) + # Create location + loc = self.stock_location_model.create( + {"name": "Test Location", "usage": "internal"} + ) + # Create stock quants for specific location + quant1 = self.quant_model.create( + { + "product_id": self.product1.id, + "location_id": loc.id, + "quantity": 10.0, + } + ) + quant2 = self.quant_model.create( + { + "product_id": self.product2.id, + "location_id": loc.id, + "quantity": 15.0, + } + ) + # Create adjustments for specific location + adjustment = self.inventory_model.create( + { + "name": "Pre-existing inventory", + "location_ids": [(4, loc.id)], + "date": date, + } + ) + # Start the adjustment + adjustment.action_state_to_in_progress() + # Check that there are stock quants for the specific location + self.assertTrue(self.env["stock.quant"].search([("location_id", "=", loc.id)])) + # Make the count of the stock + quant1.update( + { + "inventory_quantity": 5, + } + ) + quant2.update( + { + "inventory_quantity": 10, + } + ) + # Apply the changes + quant1._apply_inventory() + quant2._apply_inventory() + # Check that line_accuracy is calculated properly + sml = self.env["stock.move.line"].search( + [("location_id", "=", loc.id), ("product_id", "=", self.product1.id)] + ) + self.assertEqual(sml.line_accuracy, 0.5) + sml = self.env["stock.move.line"].search( + [("location_id", "=", loc.id), ("product_id", "=", self.product2.id)] + ) + self.assertEqual(sml.line_accuracy, 0.6667000000000001) + # Set Inventory Adjustment to Done + adjustment.action_state_to_done() + # Check that accuracy is correctly calculated + self.assertEqual(adjustment.inventory_accuracy, 60) + + def test_zero_inventory_adjustment_accuracy(self): + date = datetime.today() - timedelta(days=1) + # Create location + loc = self.stock_location_model.create( + {"name": "Test Location", "usage": "internal"} + ) + # Create stock quants for specific location + quant1 = self.quant_model.create( + { + "product_id": self.product1.id, + "location_id": loc.id, + "quantity": 0.0, + } + ) + quant2 = self.quant_model.create( + { + "product_id": self.product2.id, + "location_id": loc.id, + "quantity": 300.0, + } + ) + # Create adjustment for specific location + adjustment = self.inventory_model.create( + { + "name": "Pre-existing inventory qty zero", + "location_ids": [(4, loc.id)], + "date": date, + } + ) + # Start the adjustment + adjustment.action_state_to_in_progress() + # Check that there are stock quants for the specific location + self.assertTrue(self.env["stock.quant"].search([("location_id", "=", loc.id)])) + # Make the count of the stock + quant1.update( + { + "inventory_quantity": 5, + } + ) + quant2.update( + { + "inventory_quantity": 0, + } + ) + # Apply the changes + quant1._apply_inventory() + quant2._apply_inventory() + # Check that line_accuracy is calculated properly + sml = self.env["stock.move.line"].search( + [("location_id", "=", loc.id), ("product_id", "=", self.product1.id)] + ) + self.assertEqual(sml.line_accuracy, 0) + # Set Inventory Adjustment to Done + adjustment.action_state_to_done() + # Check that accuracy is correctly calculated + self.assertEqual(adjustment.inventory_accuracy, 0)