# -*- coding: utf-8 -*- # Copyright 2024-2026 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) """Repair warranty coverage. Tracks the 30/90-day warranty we offer on completed repair work. When a new repair is created on the same equipment within the coverage window, the intake wizard / portal shows a banner: "This repair may be covered by our warranty - no charge". Phase 2 ships the model + manual creation from a completed repair. Phase 4 will add automatic creation when a repair moves to 'done'. """ from datetime import timedelta from odoo import api, fields, models class FusionRepairWarrantyCoverage(models.Model): _name = 'fusion.repair.warranty.coverage' _description = 'Repair Warranty Coverage' _order = 'expiry_date desc, id desc' name = fields.Char(string='Reference', compute='_compute_name', store=True) repair_id = fields.Many2one( 'repair.order', string='Original Repair', required=True, ondelete='cascade', index=True, ) partner_id = fields.Many2one( 'res.partner', string='Client', related='repair_id.partner_id', store=True, index=True, ) product_id = fields.Many2one( 'product.product', string='Equipment', related='repair_id.product_id', store=True, index=True, ) lot_id = fields.Many2one( 'stock.lot', string='Serial Number', related='repair_id.lot_id', store=True, ) start_date = fields.Date( string='Start Date', required=True, default=fields.Date.context_today, ) coverage_days = fields.Integer( string='Coverage Window (days)', default=30, required=True, ) expiry_date = fields.Date( string='Expires', compute='_compute_expiry_date', store=True, ) # Non-stored compute - DO NOT add store=True. The 'active vs not' status is # time-dependent (today >= expiry_date), and a stored compute would never # auto-refresh as days pass. find_active_for() filters by expiry_date directly. is_active = fields.Boolean( string='Active', compute='_compute_is_active', ) notes = fields.Text() company_id = fields.Many2one( 'res.company', string='Company', related='repair_id.company_id', store=True, ) @api.depends('repair_id.name', 'expiry_date') def _compute_name(self): for w in self: w.name = ( f"Warranty {w.repair_id.name or '?'} (until {w.expiry_date or '?'})" ) @api.depends('start_date', 'coverage_days') def _compute_expiry_date(self): for w in self: if w.start_date and w.coverage_days: w.expiry_date = w.start_date + timedelta(days=w.coverage_days) else: w.expiry_date = False @api.depends('expiry_date') def _compute_is_active(self): today = fields.Date.context_today(self) for w in self: w.is_active = bool(w.expiry_date and w.expiry_date >= today) # ------------------------------------------------------------------ # LOOKUP # ------------------------------------------------------------------ @api.model def find_active_for(self, partner_id, product_id=None, lot_id=None): """Return active warranty coverage matching the partner + equipment, if any. Requires at least one of lot_id or product_id - without an equipment identifier we would match any warranty on the partner, which would falsely flag unrelated equipment as covered. """ if not partner_id: return self.browse() if not lot_id and not product_id: return self.browse() today = fields.Date.context_today(self) domain = [ ('partner_id', '=', partner_id), ('expiry_date', '>=', today), ] if lot_id: domain.append(('lot_id', '=', lot_id)) elif product_id: domain.append(('product_id', '=', product_id)) return self.search(domain, order='expiry_date desc', limit=1)