"""Persisted anomaly flags from the engine's variance detection. Each row captures one flagged report row variance. Used by the OWL anomaly_strip + the audit trail.""" from odoo import _, api, fields, models SEVERITY = [('low', 'Low'), ('medium', 'Medium'), ('high', 'High')] DIRECTION = [('increase', 'Increase'), ('decrease', 'Decrease')] class FusionReportAnomaly(models.Model): _name = "fusion.report.anomaly" _description = "Flagged Report Variance" _order = "detected_at desc, severity desc" report_id = fields.Many2one('fusion.report', required=True, ondelete='cascade') company_id = fields.Many2one('res.company', required=True, default=lambda self: self.env.company) period_from = fields.Date(required=True) period_to = fields.Date(required=True) row_id = fields.Char(required=True, help="Engine-generated row id (e.g. 'line_3').") label = fields.Char(required=True) current_amount = fields.Float() comparison_amount = fields.Float() variance_amount = fields.Float() variance_pct = fields.Float() severity = fields.Selection(SEVERITY, required=True) direction = fields.Selection(DIRECTION, required=True) detected_at = fields.Datetime(default=fields.Datetime.now, required=True) state = fields.Selection([ ('new', 'New'), ('acknowledged', 'Acknowledged'), ('investigating', 'Investigating'), ('resolved', 'Resolved'), ('dismissed', 'Dismissed'), ], default='new', required=True) notes = fields.Text() acknowledged_by = fields.Many2one('res.users') acknowledged_at = fields.Datetime() def action_acknowledge(self): self.write({ 'state': 'acknowledged', 'acknowledged_by': self.env.uid, 'acknowledged_at': fields.Datetime.now(), }) def action_dismiss(self): self.write({'state': 'dismissed'}) def action_resolve(self): self.write({'state': 'resolved'})