diff --git a/stock_cycle_count/models/stock_cycle_count.py b/stock_cycle_count/models/stock_cycle_count.py index ea373c2a3175..01da4cfb0305 100644 --- a/stock_cycle_count/models/stock_cycle_count.py +++ b/stock_cycle_count/models/stock_cycle_count.py @@ -32,6 +32,21 @@ class StockCycleCount(models.Model): readonly=True, states={"draft": [("readonly", False)]}, tracking=True, + compute="_compute_date_deadline", + inverse="_inverse_date_deadline", + store=True, + ) + automatic_deadline_date = fields.Date( + string="Automatic Required Date", + readonly=True, + states={"draft": [("readonly", False)]}, + tracking=True, + ) + manual_deadline_date = fields.Date( + string="Manual Required Date", + readonly=True, + states={"draft": [("readonly", False)]}, + tracking=True, ) cycle_count_rule_id = fields.Many2one( comodel_name="stock.cycle.count.rule", @@ -121,3 +136,15 @@ def action_view_inventory(self): action["views"] = [(res and res.id or False, "form")] action["res_id"] = adjustment_ids and adjustment_ids[0] or False return action + + @api.depends("automatic_deadline_date", "manual_deadline_date") + def _compute_date_deadline(self): + for rec in self: + if rec.manual_deadline_date: + rec.date_deadline = rec.manual_deadline_date + else: + rec.date_deadline = rec.automatic_deadline_date + + def _inverse_date_deadline(self): + for rec in self: + rec.manual_deadline_date = rec.date_deadline diff --git a/stock_cycle_count/models/stock_cycle_count_rule.py b/stock_cycle_count/models/stock_cycle_count_rule.py index 30ee41d0bae1..2a3339a5202e 100644 --- a/stock_cycle_count/models/stock_cycle_count_rule.py +++ b/stock_cycle_count/models/stock_cycle_count_rule.py @@ -169,7 +169,7 @@ def _compute_rule_periodic(self, locs): .search( [ ("location_ids", "in", [loc.id]), - ("state", "in", ["confirm", "done", "draft"]), + ("state", "in", ["in_progress", "done", "draft"]), ], order="date desc", limit=1, diff --git a/stock_cycle_count/models/stock_inventory.py b/stock_cycle_count/models/stock_inventory.py index e38ff165678d..619e793639f8 100644 --- a/stock_cycle_count/models/stock_inventory.py +++ b/stock_cycle_count/models/stock_inventory.py @@ -1,12 +1,15 @@ # Copyright 2017-2022 ForgeFlow S.L. # (http://www.forgeflow.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +import logging from odoo import _, api, fields, models from odoo.exceptions import ValidationError PERCENT = 100.0 +_logger = logging.getLogger(__name__) + class StockInventory(models.Model): _inherit = "stock.inventory" @@ -171,3 +174,13 @@ def _check_cycle_count_consistency(self): ) % msg ) + + def action_state_to_in_progress(self): + res = super().action_state_to_in_progress() + self.stock_quant_ids.update( + { + "user_id": self.cycle_count_id.responsible_id, + "inventory_date": self.cycle_count_id.date_deadline, + } + ) + return res diff --git a/stock_cycle_count/models/stock_location.py b/stock_cycle_count/models/stock_location.py index 9210ce52b7ff..ae3e6d493e27 100644 --- a/stock_cycle_count/models/stock_location.py +++ b/stock_cycle_count/models/stock_location.py @@ -101,7 +101,7 @@ def create_zero_confirmation_cycle_count(self): ) self.env["stock.cycle.count"].create( { - "date_deadline": date, + "automatic_deadline_date": date, "location_id": self.id, "cycle_count_rule_id": rule.id, "state": "draft", diff --git a/stock_cycle_count/models/stock_warehouse.py b/stock_cycle_count/models/stock_warehouse.py index 75a392d7ebf0..b9d04fe8fbdb 100644 --- a/stock_cycle_count/models/stock_warehouse.py +++ b/stock_cycle_count/models/stock_warehouse.py @@ -70,7 +70,7 @@ def _cycle_count_rules_to_compute(self): @api.model def _prepare_cycle_count(self, cycle_count_proposed): return { - "date_deadline": cycle_count_proposed["date"], + "automatic_deadline_date": cycle_count_proposed["date"], "location_id": cycle_count_proposed["location"].id, "cycle_count_rule_id": cycle_count_proposed["rule_type"].id, "state": "draft", @@ -115,7 +115,7 @@ def action_compute_cycle_count_rules(self): ) cc_to_update.write( { - "date_deadline": cycle_count_proposed_date, + "automatic_deadline_date": cycle_count_proposed_date, "cycle_count_rule_id": cycle_count_proposed[ "rule_type" ].id, @@ -138,6 +138,24 @@ def cron_cycle_count(self): try: whs = self.search([]) whs.action_compute_cycle_count_rules() + today = fields.Date.today() + cycle_counts = self.env["stock.cycle.count"].search( + [("date_deadline", "<=", today), ("state", "=", "draft")] + ) + for cycle_count in cycle_counts: + open_cycle_counts = self.env["stock.cycle.count"].search( + [ + ("location_id", "=", cycle_count.location_id.id), + ("state", "=", "open"), + ] + ) + if open_cycle_counts: + continue + cycle_count.action_create_inventory_adjustment() + try: + cycle_count.stock_adjustment_ids.action_state_to_in_progress() + except Exception as e: + _logger.info("Error when beginning an adjustment: %s", str(e)) except Exception as e: _logger.info("Error while running stock_cycle_count cron job: %s", str(e)) raise diff --git a/stock_cycle_count/tests/test_stock_cycle_count.py b/stock_cycle_count/tests/test_stock_cycle_count.py index 4e55ab264e5c..b48ea426f744 100644 --- a/stock_cycle_count/tests/test_stock_cycle_count.py +++ b/stock_cycle_count/tests/test_stock_cycle_count.py @@ -153,7 +153,7 @@ def test_cycle_count_planner(self): "name": "To be cancelled when running cron job.", "cycle_count_rule_id": self.rule_periodic.id, "location_id": loc.id, - "date_deadline": date_pre_existing_cc, + "automatic_deadline_date": date_pre_existing_cc, } ) self.assertEqual( @@ -188,14 +188,32 @@ def test_cycle_count_planner(self): move1._action_assign() move1.move_line_ids[0].qty_done = 1.0 move1._action_done() + # Remove the pre_existing_count + self.inventory_model.search( + [("cycle_count_id", "=", pre_existing_count.id)], limit=1 + ).unlink() + pre_existing_count.unlink() + # Execute cron for first time wh.cron_cycle_count() - self.assertNotEqual( - pre_existing_count.date_deadline, - date_pre_existing_cc, - "Date of pre-existing cycle counts has not been " "updated.", + # There are counts in state open(execution) and not in state draft + open_counts = self.cycle_count_model.search( + [("location_id", "in", locs.ids), ("state", "=", "open")] ) - counts = self.cycle_count_model.search([("location_id", "in", locs.ids)]) - self.assertTrue(counts, "Cycle counts not planned") + self.assertTrue(open_counts, "Cycle counts in execution state") + draft_counts = self.cycle_count_model.search( + [("location_id", "in", locs.ids), ("state", "=", "draft")] + ) + self.assertFalse(draft_counts, "No Cycle counts in draft state") + # Execute the cron for second time + wh.cron_cycle_count() + # New cycle counts for same location created in draft state + draft_counts = self.cycle_count_model.search( + [("location_id", "in", locs.ids), ("state", "=", "draft")] + ) + self.assertTrue(draft_counts, "No Cycle counts in draft state") + # Inventory adjustment only started for cycle counts in open state + self.assertTrue(open_counts.stock_adjustment_ids) + self.assertFalse(draft_counts.stock_adjustment_ids) # Zero-confirmations: count = self.cycle_count_model.search( [ diff --git a/stock_cycle_count/views/stock_cycle_count_view.xml b/stock_cycle_count/views/stock_cycle_count_view.xml index e4495efeb26e..c607433bd246 100644 --- a/stock_cycle_count/views/stock_cycle_count_view.xml +++ b/stock_cycle_count/views/stock_cycle_count_view.xml @@ -10,6 +10,7 @@