# -*- 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})