177 lines
4.9 KiB
Python
177 lines
4.9 KiB
Python
# -*- coding: utf-8 -*-
|
||
# Copyright 2026 Nexa Systems Inc.
|
||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||
# Part of the Fusion Plating product family.
|
||
|
||
from odoo import api, fields, models
|
||
|
||
|
||
class FpRisk(models.Model):
|
||
"""Risk register entry.
|
||
|
||
Implements the operational risk management requirement from AS9100
|
||
Rev D §8.1.1. Each risk gets scored on a 1–5 likelihood and 1–5
|
||
impact scale; the multiplicative score drives the level classification
|
||
(low / medium / high / critical) used for escalation and reporting.
|
||
"""
|
||
_name = 'fusion.plating.risk'
|
||
_description = 'Fusion Plating — Risk Register Entry'
|
||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||
_order = 'risk_score desc, id desc'
|
||
|
||
name = fields.Char(
|
||
string='Reference',
|
||
required=True,
|
||
copy=False,
|
||
readonly=True,
|
||
default=lambda self: self._default_name(),
|
||
tracking=True,
|
||
)
|
||
title = fields.Char(
|
||
string='Title',
|
||
required=True,
|
||
tracking=True,
|
||
)
|
||
description = fields.Html(
|
||
string='Description',
|
||
)
|
||
category = fields.Selection(
|
||
[
|
||
('operational', 'Operational'),
|
||
('supply_chain', 'Supply Chain'),
|
||
('quality', 'Quality'),
|
||
('safety', 'Safety'),
|
||
('environmental', 'Environmental'),
|
||
('financial', 'Financial'),
|
||
('customer', 'Customer'),
|
||
('regulatory', 'Regulatory'),
|
||
],
|
||
string='Category',
|
||
default='operational',
|
||
required=True,
|
||
tracking=True,
|
||
)
|
||
likelihood = fields.Selection(
|
||
[
|
||
('1', '1 — Very Low'),
|
||
('2', '2 — Low'),
|
||
('3', '3 — Medium'),
|
||
('4', '4 — High'),
|
||
('5', '5 — Very High'),
|
||
],
|
||
string='Likelihood',
|
||
default='3',
|
||
required=True,
|
||
tracking=True,
|
||
)
|
||
impact = fields.Selection(
|
||
[
|
||
('1', '1 — Very Low'),
|
||
('2', '2 — Low'),
|
||
('3', '3 — Medium'),
|
||
('4', '4 — High'),
|
||
('5', '5 — Very High'),
|
||
],
|
||
string='Impact',
|
||
default='3',
|
||
required=True,
|
||
tracking=True,
|
||
)
|
||
risk_score = fields.Integer(
|
||
string='Score',
|
||
compute='_compute_risk_score',
|
||
store=True,
|
||
help='Likelihood × Impact (1–25).',
|
||
)
|
||
risk_level = fields.Selection(
|
||
[
|
||
('low', 'Low'),
|
||
('medium', 'Medium'),
|
||
('high', 'High'),
|
||
('critical', 'Critical'),
|
||
],
|
||
string='Level',
|
||
compute='_compute_risk_level',
|
||
store=True,
|
||
tracking=True,
|
||
)
|
||
mitigation_plan = fields.Html(
|
||
string='Mitigation Plan',
|
||
)
|
||
owner_id = fields.Many2one(
|
||
'res.users',
|
||
string='Owner',
|
||
default=lambda self: self.env.user,
|
||
tracking=True,
|
||
)
|
||
review_date = fields.Date(
|
||
string='Next Review',
|
||
tracking=True,
|
||
)
|
||
state = fields.Selection(
|
||
[
|
||
('identified', 'Identified'),
|
||
('assessed', 'Assessed'),
|
||
('treated', 'Treated'),
|
||
('monitored', 'Monitored'),
|
||
('closed', 'Closed'),
|
||
],
|
||
string='Status',
|
||
default='identified',
|
||
required=True,
|
||
tracking=True,
|
||
)
|
||
company_id = fields.Many2one(
|
||
'res.company',
|
||
string='Company',
|
||
default=lambda self: self.env.company,
|
||
)
|
||
active = fields.Boolean(default=True)
|
||
|
||
@api.model
|
||
def _default_name(self):
|
||
seq = self.env['ir.sequence'].next_by_code('fusion.plating.risk')
|
||
return seq or '/'
|
||
|
||
@api.model_create_multi
|
||
def create(self, vals_list):
|
||
for vals in vals_list:
|
||
if not vals.get('name') or vals.get('name') == '/':
|
||
vals['name'] = self._default_name()
|
||
return super().create(vals_list)
|
||
|
||
@api.depends('likelihood', 'impact')
|
||
def _compute_risk_score(self):
|
||
for rec in self:
|
||
try:
|
||
likely = int(rec.likelihood or 0)
|
||
impact = int(rec.impact or 0)
|
||
except (TypeError, ValueError):
|
||
likely, impact = 0, 0
|
||
rec.risk_score = likely * impact
|
||
|
||
@api.depends('risk_score')
|
||
def _compute_risk_level(self):
|
||
for rec in self:
|
||
score = rec.risk_score or 0
|
||
if score >= 20:
|
||
rec.risk_level = 'critical'
|
||
elif score >= 12:
|
||
rec.risk_level = 'high'
|
||
elif score >= 6:
|
||
rec.risk_level = 'medium'
|
||
else:
|
||
rec.risk_level = 'low'
|
||
|
||
def action_assess(self):
|
||
self.write({'state': 'assessed'})
|
||
|
||
def action_treat(self):
|
||
self.write({'state': 'treated'})
|
||
|
||
def action_monitor(self):
|
||
self.write({'state': 'monitored'})
|
||
|
||
def action_close(self):
|
||
self.write({'state': 'closed'})
|