# Copyright 2017-18 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 UserError _logger = logging.getLogger(__name__) class StockCycleCount(models.Model): _name = "stock.cycle.count" _description = "Stock Cycle Counts" _inherit = "mail.thread" _order = "id desc" name = fields.Char(readonly=True) location_id = fields.Many2one( comodel_name="stock.location", string="Location", required=True, readonly=True, states={"draft": [("readonly", False)]}, ) responsible_id = fields.Many2one( comodel_name="res.users", string="Assigned to", readonly=True, states={"draft": [("readonly", False)], "open": [("readonly", False)]}, tracking=True, ) date_deadline = fields.Date( string="Required Date", 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", string="Cycle count rule", required=True, readonly=True, states={"draft": [("readonly", False)]}, tracking=True, ) state = fields.Selection( selection=[ ("draft", "Planned"), ("open", "Execution"), ("cancelled", "Cancelled"), ("done", "Done"), ], default="draft", tracking=True, ) stock_adjustment_ids = fields.One2many( comodel_name="stock.inventory", inverse_name="cycle_count_id", string="Inventory Adjustment", tracking=True, ) inventory_adj_count = fields.Integer(compute="_compute_inventory_adj_count") company_id = fields.Many2one( comodel_name="res.company", string="Company", required=True, default=lambda self: self.env.company, readonly=True, ) def write(self, vals): result = super().write(vals) if "responsible_id" in vals and not self.env.context.get("no_propagate"): stock_inventory_records = self.mapped("stock_adjustment_ids") for record in stock_inventory_records: if record.responsible_id.id != vals["responsible_id"]: record.with_context(no_propagate=True).write( {"responsible_id": vals["responsible_id"]} ) return result @api.depends("stock_adjustment_ids") def _compute_inventory_adj_count(self): for rec in self: rec.inventory_adj_count = len(rec.stock_adjustment_ids) def do_cancel(self): self.write({"state": "cancelled"}) def _prepare_inventory_adjustment(self): self.ensure_one() return { "name": "INV/{}".format(self.name), "cycle_count_id": self.id, "location_ids": [(4, self.location_id.id)], "exclude_sublocation": True, "responsible_id": self.responsible_id.id, } @api.model_create_multi def create(self, vals_list): for vals in vals_list: vals["name"] = ( self.env["ir.sequence"].next_by_code("stock.cycle.count") or "" ) return super().create(vals_list) def action_create_inventory_adjustment(self): if any([state != "draft" for state in self.mapped("state")]): raise UserError(_("You can only confirm cycle counts in state 'Planned'.")) for rec in self: data = rec._prepare_inventory_adjustment() inv = self.env["stock.inventory"].create(data) if rec.company_id.auto_start_inventory_from_cycle_count: try: inv.action_state_to_in_progress() except Exception as e: _logger.info("Error when beginning an adjustment: %s", str(e)) self.write({"state": "open"}) return True def action_view_inventory(self): action = self.env["ir.actions.act_window"]._for_xml_id( "stock_inventory.action_view_inventory_group_form" ) action["context"] = {} adjustment_ids = self.mapped("stock_adjustment_ids").ids if len(adjustment_ids) > 1: action["domain"] = [("id", "in", adjustment_ids)] elif len(adjustment_ids) == 1: res = self.env.ref("stock_inventory.view_inventory_group_form", False) 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