123 lines
3.9 KiB
Python
123 lines
3.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 dateutil.relativedelta import relativedelta
|
|
|
|
from odoo import api, fields, models
|
|
|
|
|
|
class FpNuclearPedigree(models.Model):
|
|
"""Nuclear Pedigree Traceability Record.
|
|
|
|
Nuclear pedigree is the unbroken chain of traceability from the raw
|
|
material a plated part was made from, through every processing
|
|
step, to the finished and shipped component. Nuclear operators use
|
|
pedigree records decades after shipment — long after the original
|
|
work order has aged out of normal systems — which is why the
|
|
retention clock is driven by the N299 level rather than a generic
|
|
document-retention rule.
|
|
|
|
Once the record is locked (typically at ship), the fields above the
|
|
chatter are read-only and all downstream edits should go through
|
|
the normal change-control process.
|
|
"""
|
|
_name = 'fusion.plating.nuclear.pedigree'
|
|
_description = 'Fusion Plating — Nuclear Pedigree Record'
|
|
_inherit = ['mail.thread', 'mail.activity.mixin']
|
|
_order = 'ship_date desc, id desc'
|
|
|
|
name = fields.Char(
|
|
string='Job / Lot Ref',
|
|
required=True,
|
|
tracking=True,
|
|
)
|
|
part_number = fields.Char(
|
|
string='Part Number',
|
|
tracking=True,
|
|
)
|
|
lot_serial = fields.Char(
|
|
string='Lot / Serial',
|
|
tracking=True,
|
|
)
|
|
customer_id = fields.Many2one(
|
|
'res.partner',
|
|
string='Customer',
|
|
required=True,
|
|
tracking=True,
|
|
)
|
|
n299_level_id = fields.Many2one(
|
|
'fusion.plating.n299.level',
|
|
string='CSA N299 Level',
|
|
tracking=True,
|
|
)
|
|
raw_material_cert_refs = fields.Char(
|
|
string='Raw Material Cert Refs',
|
|
help='Mill test report, CMTR, or material certificate reference(s).',
|
|
)
|
|
raw_material_heat_no = fields.Char(
|
|
string='Raw Material Heat No',
|
|
)
|
|
process_steps_json = fields.Text(
|
|
string='Process Steps',
|
|
help='Structured trail of each processing step the part went '
|
|
'through (station, operator, timestamp, parameters).',
|
|
)
|
|
operators_log = fields.Text(
|
|
string='Operators Log',
|
|
)
|
|
chemistry_cert_refs = fields.Char(
|
|
string='Chemistry Cert Refs',
|
|
)
|
|
test_results = fields.Html(
|
|
string='Test Results',
|
|
)
|
|
ship_date = fields.Date(
|
|
string='Ship Date',
|
|
tracking=True,
|
|
)
|
|
retention_until_date = fields.Date(
|
|
string='Retain Until',
|
|
compute='_compute_retention_until_date',
|
|
store=True,
|
|
help='Ship date plus the retention period of the assigned N299 level.',
|
|
)
|
|
attachment_ids = fields.Many2many(
|
|
'ir.attachment',
|
|
'fp_nuclear_pedigree_attachment_rel',
|
|
'pedigree_id',
|
|
'attachment_id',
|
|
string='Supporting Documents',
|
|
)
|
|
locked = fields.Boolean(
|
|
string='Locked',
|
|
default=False,
|
|
tracking=True,
|
|
help='Once the part has shipped, lock the pedigree to prevent '
|
|
'modification. Further changes must go through formal '
|
|
'change control.',
|
|
)
|
|
company_id = fields.Many2one(
|
|
'res.company',
|
|
string='Company',
|
|
default=lambda self: self.env.company,
|
|
)
|
|
active = fields.Boolean(default=True)
|
|
|
|
@api.depends('ship_date', 'n299_level_id.retention_years')
|
|
def _compute_retention_until_date(self):
|
|
for rec in self:
|
|
if rec.ship_date and rec.n299_level_id and rec.n299_level_id.retention_years:
|
|
rec.retention_until_date = rec.ship_date + relativedelta(
|
|
years=rec.n299_level_id.retention_years
|
|
)
|
|
else:
|
|
rec.retention_until_date = False
|
|
|
|
def action_lock(self):
|
|
self.write({'locked': True})
|
|
|
|
def action_unlock(self):
|
|
self.write({'locked': False})
|