folder rename
This commit is contained in:
16
fusion_plating/fusion_plating_compliance/models/__init__.py
Normal file
16
fusion_plating/fusion_plating_compliance/models/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
from . import fp_jurisdiction
|
||||
from . import fp_regulator
|
||||
from . import fp_permit
|
||||
from . import fp_permit_condition
|
||||
from . import fp_discharge_limit
|
||||
from . import fp_discharge_sample
|
||||
from . import fp_discharge_sample_line
|
||||
from . import fp_waste_stream
|
||||
from . import fp_waste_manifest
|
||||
from . import fp_pollutant_inventory
|
||||
from . import fp_compliance_event
|
||||
from . import fp_spill_register
|
||||
from . import fp_facility
|
||||
@@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class FpComplianceEvent(models.Model):
|
||||
_name = 'fusion.plating.compliance.event'
|
||||
_description = 'Fusion Plating - Compliance Event'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_order = 'due_date, id'
|
||||
|
||||
name = fields.Char(string='Title', required=True, tracking=True)
|
||||
event_type = fields.Selection(
|
||||
[('report_due', 'Report Due'), ('permit_renewal', 'Permit Renewal'),
|
||||
('sample_due', 'Sample Due'), ('audit', 'Audit'), ('inspection', 'Inspection'),
|
||||
('training_expiry', 'Training Expiry'), ('other', 'Other')],
|
||||
string='Type', required=True, default='report_due', tracking=True,
|
||||
)
|
||||
facility_id = fields.Many2one('fusion.plating.facility', string='Facility', ondelete='cascade', tracking=True)
|
||||
company_id = fields.Many2one('res.company', related='facility_id.company_id', store=True, readonly=True)
|
||||
due_date = fields.Date(string='Due', required=True, tracking=True)
|
||||
owner_id = fields.Many2one('res.users', string='Owner', tracking=True)
|
||||
state = fields.Selection(
|
||||
[('upcoming', 'Upcoming'), ('due', 'Due'), ('overdue', 'Overdue'),
|
||||
('done', 'Done'), ('skipped', 'Skipped')],
|
||||
string='Status', default='upcoming', required=True, tracking=True,
|
||||
)
|
||||
permit_id = fields.Many2one('fusion.plating.permit', string='Related Permit', ondelete='set null')
|
||||
regulator_id = fields.Many2one('fusion.plating.regulator', string='Regulator', ondelete='set null')
|
||||
notes = fields.Html(string='Notes')
|
||||
|
||||
@api.model
|
||||
def _cron_refresh_states(self):
|
||||
today = fields.Date.context_today(self)
|
||||
events = self.search([('state', 'in', ('upcoming', 'due'))])
|
||||
for ev in events:
|
||||
if not ev.due_date:
|
||||
continue
|
||||
if ev.due_date < today:
|
||||
ev.state = 'overdue'
|
||||
elif ev.due_date == today:
|
||||
ev.state = 'due'
|
||||
|
||||
def action_mark_done(self):
|
||||
self.write({'state': 'done'})
|
||||
|
||||
def action_skip(self):
|
||||
self.write({'state': 'skipped'})
|
||||
@@ -0,0 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class FpDischargeLimit(models.Model):
|
||||
_name = 'fusion.plating.discharge.limit'
|
||||
_description = 'Fusion Plating - Discharge Limit'
|
||||
_order = 'jurisdiction_id, parameter, discharge_point'
|
||||
|
||||
name = fields.Char(string='Name', required=True)
|
||||
parameter = fields.Char(string='Parameter', required=True)
|
||||
jurisdiction_id = fields.Many2one('fusion.plating.jurisdiction', string='Jurisdiction', ondelete='restrict')
|
||||
regulation_ref = fields.Char(string='Regulation Reference')
|
||||
discharge_point = fields.Selection(
|
||||
[('sanitary', 'Sanitary Sewer'), ('storm', 'Storm Sewer'),
|
||||
('combined', 'Combined Sewer'), ('air', 'Air Emission'), ('other', 'Other')],
|
||||
string='Discharge Point', default='sanitary', required=True,
|
||||
)
|
||||
limit_value = fields.Float(string='Limit', digits=(16, 4))
|
||||
uom = fields.Char(string='UoM')
|
||||
limit_type = fields.Selection(
|
||||
[('max', 'Maximum'), ('min', 'Minimum'), ('range', 'Range'), ('ceiling', 'Hard Ceiling')],
|
||||
string='Limit Type', default='max', required=True,
|
||||
)
|
||||
min_value = fields.Float(string='Min Value', digits=(16, 4))
|
||||
reference_url = fields.Char(string='Source URL')
|
||||
notes = fields.Text(string='Notes')
|
||||
active = fields.Boolean(default=True)
|
||||
@@ -0,0 +1,66 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class FpDischargeSample(models.Model):
|
||||
_name = 'fusion.plating.discharge.sample'
|
||||
_description = 'Fusion Plating - Discharge Sample'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_order = 'sample_date desc, id desc'
|
||||
|
||||
name = fields.Char(string='Reference', required=True, copy=False, default=lambda s: s._default_name(), tracking=True)
|
||||
facility_id = fields.Many2one('fusion.plating.facility', string='Facility', required=True, ondelete='restrict', tracking=True)
|
||||
company_id = fields.Many2one('res.company', related='facility_id.company_id', store=True, readonly=True)
|
||||
sample_date = fields.Datetime(string='Sample Date', required=True, default=fields.Datetime.now, tracking=True)
|
||||
sample_point = fields.Char(string='Sample Point')
|
||||
collected_by_id = fields.Many2one('res.users', string='Collected By')
|
||||
chain_of_custody_ref = fields.Char(string='Chain of Custody #')
|
||||
lab_id = fields.Many2one('res.partner', string='Lab', domain=[('is_company', '=', True)])
|
||||
lab_report_ref = fields.Char(string='Lab Report #')
|
||||
received_date = fields.Date(string='Results Received')
|
||||
state = fields.Selection(
|
||||
[('draft', 'Draft'), ('sent_to_lab', 'Sent to Lab'), ('results_in', 'Results In'),
|
||||
('escalated', 'Escalated'), ('closed', 'Closed')],
|
||||
string='Status', default='draft', required=True, tracking=True,
|
||||
)
|
||||
line_ids = fields.One2many('fusion.plating.discharge.sample.line', 'sample_id', string='Parameters', copy=True)
|
||||
worst_status = fields.Selection(
|
||||
[('ok', 'OK'), ('warning', 'Warning'), ('out_of_spec', 'Out of Spec'), ('pending', 'Pending')],
|
||||
string='Worst Result', compute='_compute_worst_status', store=True,
|
||||
)
|
||||
notes = fields.Html(string='Notes')
|
||||
attachment_ids = fields.Many2many(
|
||||
'ir.attachment', 'fp_discharge_sample_attachment_rel', 'sample_id', 'attachment_id', string='Attachments',
|
||||
)
|
||||
active = fields.Boolean(default=True)
|
||||
|
||||
@api.model
|
||||
def _default_name(self):
|
||||
seq = self.env['ir.sequence'].next_by_code('fusion.plating.discharge.sample')
|
||||
return seq or '/'
|
||||
|
||||
@api.depends('line_ids', 'line_ids.status')
|
||||
def _compute_worst_status(self):
|
||||
order = ['out_of_spec', 'warning', 'pending', 'ok']
|
||||
for rec in self:
|
||||
statuses = [l.status for l in rec.line_ids if l.status]
|
||||
worst = 'pending'
|
||||
for s in order:
|
||||
if s in statuses:
|
||||
worst = s
|
||||
break
|
||||
rec.worst_status = worst if statuses else 'pending'
|
||||
|
||||
def action_send_to_lab(self):
|
||||
self.write({'state': 'sent_to_lab'})
|
||||
|
||||
def action_results_in(self):
|
||||
self.write({'state': 'results_in', 'received_date': fields.Date.context_today(self)})
|
||||
|
||||
def action_escalate(self):
|
||||
self.write({'state': 'escalated'})
|
||||
|
||||
def action_close(self):
|
||||
self.write({'state': 'closed'})
|
||||
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class FpDischargeSampleLine(models.Model):
|
||||
_name = 'fusion.plating.discharge.sample.line'
|
||||
_description = 'Fusion Plating - Discharge Sample Line'
|
||||
_order = 'sample_id, id'
|
||||
|
||||
sample_id = fields.Many2one('fusion.plating.discharge.sample', string='Sample', required=True, ondelete='cascade')
|
||||
limit_id = fields.Many2one('fusion.plating.discharge.limit', string='Limit', ondelete='restrict')
|
||||
parameter = fields.Char(string='Parameter', related='limit_id.parameter', store=True, readonly=False)
|
||||
value = fields.Float(string='Result', digits=(16, 4))
|
||||
uom = fields.Char(string='UoM')
|
||||
status = fields.Selection(
|
||||
[('ok', 'OK'), ('warning', 'Warning'), ('out_of_spec', 'Out of Spec'), ('pending', 'Pending')],
|
||||
string='Status', compute='_compute_status', store=True,
|
||||
)
|
||||
notes = fields.Char(string='Note')
|
||||
|
||||
@api.depends('value', 'limit_id', 'limit_id.limit_value', 'limit_id.limit_type', 'limit_id.min_value')
|
||||
def _compute_status(self):
|
||||
for rec in self:
|
||||
if not rec.limit_id:
|
||||
rec.status = 'pending'
|
||||
continue
|
||||
limit = rec.limit_id
|
||||
lt = limit.limit_type
|
||||
lv = limit.limit_value or 0.0
|
||||
if lt == 'max' or lt == 'ceiling':
|
||||
if lv <= 0:
|
||||
rec.status = 'pending'
|
||||
elif rec.value >= lv:
|
||||
rec.status = 'out_of_spec'
|
||||
elif rec.value >= 0.8 * lv:
|
||||
rec.status = 'warning'
|
||||
else:
|
||||
rec.status = 'ok'
|
||||
elif lt == 'min':
|
||||
if rec.value < lv:
|
||||
rec.status = 'out_of_spec'
|
||||
elif rec.value <= 1.2 * lv:
|
||||
rec.status = 'warning'
|
||||
else:
|
||||
rec.status = 'ok'
|
||||
elif lt == 'range':
|
||||
if rec.value < (limit.min_value or 0.0) or rec.value > lv:
|
||||
rec.status = 'out_of_spec'
|
||||
elif rec.value >= 0.8 * lv:
|
||||
rec.status = 'warning'
|
||||
else:
|
||||
rec.status = 'ok'
|
||||
else:
|
||||
rec.status = 'pending'
|
||||
@@ -0,0 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class FpFacility(models.Model):
|
||||
_inherit = 'fusion.plating.facility'
|
||||
|
||||
x_fp_eca_number = fields.Char(string='ECA / Approval Number')
|
||||
x_fp_sewer_permit_number = fields.Char(string='Sewer Permit Number')
|
||||
x_fp_waste_generator_number = fields.Char(string='Waste Generator Number')
|
||||
x_fp_compliance_notes = fields.Html(string='Compliance Notes')
|
||||
|
||||
permit_ids = fields.One2many('fusion.plating.permit', 'facility_id', string='Permits')
|
||||
waste_stream_ids = fields.One2many('fusion.plating.waste.stream', 'facility_id', string='Waste Streams')
|
||||
discharge_sample_ids = fields.One2many('fusion.plating.discharge.sample', 'facility_id', string='Discharge Samples')
|
||||
compliance_event_ids = fields.One2many('fusion.plating.compliance.event', 'facility_id', string='Compliance Events')
|
||||
spill_register_ids = fields.One2many('fusion.plating.spill.register', 'facility_id', string='Spill Register')
|
||||
|
||||
permit_count = fields.Integer(string='Permits', compute='_compute_compliance_counts')
|
||||
compliance_event_count = fields.Integer(string='Compliance Events', compute='_compute_compliance_counts')
|
||||
waste_stream_count = fields.Integer(string='Waste Streams', compute='_compute_compliance_counts')
|
||||
discharge_sample_count = fields.Integer(string='Samples', compute='_compute_compliance_counts')
|
||||
spill_count = fields.Integer(string='Spills', compute='_compute_compliance_counts')
|
||||
|
||||
@api.depends('permit_ids', 'compliance_event_ids', 'waste_stream_ids',
|
||||
'discharge_sample_ids', 'spill_register_ids')
|
||||
def _compute_compliance_counts(self):
|
||||
for rec in self:
|
||||
rec.permit_count = len(rec.permit_ids)
|
||||
rec.compliance_event_count = len(rec.compliance_event_ids)
|
||||
rec.waste_stream_count = len(rec.waste_stream_ids)
|
||||
rec.discharge_sample_count = len(rec.discharge_sample_ids)
|
||||
rec.spill_count = len(rec.spill_register_ids)
|
||||
@@ -0,0 +1,37 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class FpJurisdiction(models.Model):
|
||||
_name = 'fusion.plating.jurisdiction'
|
||||
_description = 'Fusion Plating - Jurisdiction'
|
||||
_parent_store = True
|
||||
_parent_name = 'parent_id'
|
||||
_order = 'parent_path, name'
|
||||
_rec_name = 'display_name'
|
||||
|
||||
name = fields.Char(string='Name', required=True, translate=True)
|
||||
code = fields.Char(string='Code', required=True)
|
||||
parent_id = fields.Many2one('fusion.plating.jurisdiction', string='Parent', ondelete='restrict', index=True)
|
||||
parent_path = fields.Char(index=True, unaccent=False)
|
||||
child_ids = fields.One2many('fusion.plating.jurisdiction', 'parent_id', string='Children')
|
||||
level = fields.Selection(
|
||||
[('country', 'Country'), ('province', 'Province / State'), ('municipality', 'Municipality')],
|
||||
string='Level', required=True, default='country',
|
||||
)
|
||||
active = fields.Boolean(string='Active', default=True)
|
||||
display_name = fields.Char(compute='_compute_display_name', store=True, recursive=True)
|
||||
|
||||
_sql_constraints = [
|
||||
('fp_jurisdiction_code_uniq', 'unique(code)', 'Jurisdiction code must be unique.'),
|
||||
]
|
||||
|
||||
@api.depends('name', 'code', 'parent_id.display_name')
|
||||
def _compute_display_name(self):
|
||||
for rec in self:
|
||||
if rec.parent_id:
|
||||
rec.display_name = f'{rec.parent_id.display_name} / {rec.name}'
|
||||
else:
|
||||
rec.display_name = rec.name or ''
|
||||
86
fusion_plating/fusion_plating_compliance/models/fp_permit.py
Normal file
86
fusion_plating/fusion_plating_compliance/models/fp_permit.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class FpPermit(models.Model):
|
||||
_name = 'fusion.plating.permit'
|
||||
_description = 'Fusion Plating - Regulatory Permit'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_order = 'expiry_date, name'
|
||||
|
||||
name = fields.Char(string='Title', required=True, tracking=True)
|
||||
permit_type = fields.Selection(
|
||||
[('eca', 'Environmental Compliance Approval'),
|
||||
('sewer', 'Sewer Use Permit'),
|
||||
('waste_generator', 'Waste Generator Registration'),
|
||||
('air', 'Air Permit'),
|
||||
('water', 'Water Taking / Discharge'),
|
||||
('fire', 'Fire / Hazmat'),
|
||||
('other', 'Other')],
|
||||
string='Type', required=True, default='eca', tracking=True,
|
||||
)
|
||||
number = fields.Char(string='Permit Number', tracking=True)
|
||||
facility_id = fields.Many2one('fusion.plating.facility', string='Facility', required=True, ondelete='cascade', tracking=True)
|
||||
company_id = fields.Many2one('res.company', related='facility_id.company_id', store=True, readonly=True)
|
||||
regulator_id = fields.Many2one('fusion.plating.regulator', string='Regulator', ondelete='restrict', tracking=True)
|
||||
jurisdiction_id = fields.Many2one('fusion.plating.jurisdiction', string='Jurisdiction', ondelete='restrict')
|
||||
issue_date = fields.Date(string='Issued', tracking=True)
|
||||
expiry_date = fields.Date(string='Expires', tracking=True)
|
||||
state = fields.Selection(
|
||||
[('draft', 'Draft'), ('pending', 'Pending'), ('active', 'Active'),
|
||||
('expired', 'Expired'), ('revoked', 'Revoked')],
|
||||
string='Status', default='draft', required=True, tracking=True,
|
||||
)
|
||||
renewal_reminder_days = fields.Integer(string='Reminder (days before expiry)', default=60)
|
||||
days_until_expiry = fields.Integer(string='Days Until Expiry', compute='_compute_days_until_expiry')
|
||||
is_expiring_soon = fields.Boolean(string='Expiring Soon', compute='_compute_days_until_expiry', search='_search_is_expiring_soon')
|
||||
condition_ids = fields.One2many('fusion.plating.permit.condition', 'permit_id', string='Conditions', copy=True)
|
||||
condition_count = fields.Integer(compute='_compute_condition_count')
|
||||
attachment_ids = fields.Many2many(
|
||||
'ir.attachment', 'fp_permit_attachment_rel', 'permit_id', 'attachment_id', string='Attachments',
|
||||
)
|
||||
notes = fields.Html(string='Notes')
|
||||
active = fields.Boolean(default=True)
|
||||
|
||||
@api.depends('expiry_date')
|
||||
def _compute_days_until_expiry(self):
|
||||
today = fields.Date.context_today(self)
|
||||
for rec in self:
|
||||
if rec.expiry_date:
|
||||
delta = (rec.expiry_date - today).days
|
||||
rec.days_until_expiry = delta
|
||||
rec.is_expiring_soon = 0 <= delta <= (rec.renewal_reminder_days or 60)
|
||||
else:
|
||||
rec.days_until_expiry = 0
|
||||
rec.is_expiring_soon = False
|
||||
|
||||
def _search_is_expiring_soon(self, operator, value):
|
||||
today = fields.Date.context_today(self)
|
||||
permits = self.search([('expiry_date', '!=', False)])
|
||||
soon_ids = []
|
||||
for p in permits:
|
||||
delta = (p.expiry_date - today).days
|
||||
if 0 <= delta <= (p.renewal_reminder_days or 60):
|
||||
soon_ids.append(p.id)
|
||||
if (operator == '=' and value) or (operator == '!=' and not value):
|
||||
return [('id', 'in', soon_ids)]
|
||||
return [('id', 'not in', soon_ids)]
|
||||
|
||||
@api.depends('condition_ids')
|
||||
def _compute_condition_count(self):
|
||||
for rec in self:
|
||||
rec.condition_count = len(rec.condition_ids)
|
||||
|
||||
def action_activate(self):
|
||||
self.write({'state': 'active'})
|
||||
|
||||
def action_set_pending(self):
|
||||
self.write({'state': 'pending'})
|
||||
|
||||
def action_revoke(self):
|
||||
self.write({'state': 'revoked'})
|
||||
|
||||
def action_mark_expired(self):
|
||||
self.write({'state': 'expired'})
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class FpPermitCondition(models.Model):
|
||||
_name = 'fusion.plating.permit.condition'
|
||||
_description = 'Fusion Plating - Permit Condition'
|
||||
_order = 'permit_id, sequence, id'
|
||||
|
||||
permit_id = fields.Many2one('fusion.plating.permit', string='Permit', required=True, ondelete='cascade')
|
||||
sequence = fields.Integer(default=10)
|
||||
name = fields.Char(string='Condition', required=True)
|
||||
description = fields.Html(string='Description')
|
||||
frequency = fields.Selection(
|
||||
[('one_time', 'One-time'), ('daily', 'Daily'), ('weekly', 'Weekly'),
|
||||
('monthly', 'Monthly'), ('quarterly', 'Quarterly'),
|
||||
('semi_annual', 'Semi-Annual'), ('annual', 'Annual'), ('on_demand', 'On Demand')],
|
||||
string='Frequency', default='annual',
|
||||
)
|
||||
next_due_date = fields.Date(string='Next Due')
|
||||
owner_id = fields.Many2one('res.users', string='Owner')
|
||||
status = fields.Selection(
|
||||
[('upcoming', 'Upcoming'), ('due', 'Due'), ('overdue', 'Overdue'), ('done', 'Done')],
|
||||
string='Status', default='upcoming',
|
||||
)
|
||||
@@ -0,0 +1,51 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class FpPollutantInventory(models.Model):
|
||||
_name = 'fusion.plating.pollutant.inventory'
|
||||
_description = 'Fusion Plating - Pollutant Inventory Entry'
|
||||
_order = 'year desc, facility_id, substance'
|
||||
|
||||
name = fields.Char(string='Reference', compute='_compute_name', store=True)
|
||||
year = fields.Integer(string='Year', required=True, default=lambda s: fields.Date.context_today(s).year)
|
||||
facility_id = fields.Many2one('fusion.plating.facility', string='Facility', required=True, ondelete='cascade')
|
||||
company_id = fields.Many2one('res.company', related='facility_id.company_id', store=True, readonly=True)
|
||||
substance = fields.Char(string='Substance', required=True)
|
||||
cas_number = fields.Char(string='CAS #')
|
||||
manufactured_kg = fields.Float(string='Manufactured (kg)', digits=(16, 3))
|
||||
processed_kg = fields.Float(string='Processed (kg)', digits=(16, 3))
|
||||
used_kg = fields.Float(string='Used (kg)', digits=(16, 3))
|
||||
released_kg = fields.Float(string='Released (kg)', digits=(16, 3))
|
||||
transferred_kg = fields.Float(string='Transferred (kg)', digits=(16, 3))
|
||||
threshold_kg = fields.Float(string='Threshold (kg)')
|
||||
threshold_exceeded = fields.Boolean(string='Threshold Exceeded', compute='_compute_threshold_exceeded', store=True)
|
||||
notes = fields.Text(string='Notes')
|
||||
|
||||
_sql_constraints = [
|
||||
('fp_pollutant_inv_uniq', 'unique(facility_id, year, substance)',
|
||||
'Pollutant inventory must be unique per facility / year / substance.'),
|
||||
]
|
||||
|
||||
@api.depends('year', 'substance', 'facility_id')
|
||||
def _compute_name(self):
|
||||
for rec in self:
|
||||
parts = []
|
||||
if rec.year:
|
||||
parts.append(str(rec.year))
|
||||
if rec.substance:
|
||||
parts.append(rec.substance)
|
||||
if rec.facility_id:
|
||||
parts.append(rec.facility_id.code or rec.facility_id.name or '')
|
||||
rec.name = ' / '.join(p for p in parts if p) or '/'
|
||||
|
||||
@api.depends('manufactured_kg', 'processed_kg', 'used_kg', 'threshold_kg')
|
||||
def _compute_threshold_exceeded(self):
|
||||
for rec in self:
|
||||
if rec.threshold_kg and rec.threshold_kg > 0:
|
||||
total = (rec.manufactured_kg or 0.0) + (rec.processed_kg or 0.0) + (rec.used_kg or 0.0)
|
||||
rec.threshold_exceeded = total >= rec.threshold_kg
|
||||
else:
|
||||
rec.threshold_exceeded = False
|
||||
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class FpRegulator(models.Model):
|
||||
_name = 'fusion.plating.regulator'
|
||||
_description = 'Fusion Plating - Regulator'
|
||||
_order = 'jurisdiction_id, name'
|
||||
|
||||
name = fields.Char(string='Name', required=True, translate=True)
|
||||
code = fields.Char(string='Code')
|
||||
jurisdiction_id = fields.Many2one('fusion.plating.jurisdiction', string='Jurisdiction', ondelete='restrict')
|
||||
category = fields.Selection(
|
||||
[('environmental', 'Environmental'), ('health_safety', 'Health & Safety'),
|
||||
('water', 'Water'), ('waste', 'Waste'), ('labour', 'Labour'),
|
||||
('transport', 'Transport'), ('other', 'Other')],
|
||||
string='Category', default='environmental',
|
||||
)
|
||||
website = fields.Char(string='Website')
|
||||
contact_info = fields.Text(string='Contact Information')
|
||||
active = fields.Boolean(default=True)
|
||||
@@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class FpSpillRegister(models.Model):
|
||||
_name = 'fusion.plating.spill.register'
|
||||
_description = 'Fusion Plating - Spill Register'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_order = 'spill_date desc, id desc'
|
||||
|
||||
name = fields.Char(string='Reference', required=True, copy=False, default=lambda s: s._default_name(), tracking=True)
|
||||
facility_id = fields.Many2one('fusion.plating.facility', string='Facility', required=True, ondelete='restrict', tracking=True)
|
||||
company_id = fields.Many2one('res.company', related='facility_id.company_id', store=True, readonly=True)
|
||||
spill_date = fields.Datetime(string='Spill Date', required=True, default=fields.Datetime.now, tracking=True)
|
||||
reported_by_id = fields.Many2one('res.users', string='Reported By', default=lambda s: s.env.user)
|
||||
substance = fields.Char(string='Substance', tracking=True)
|
||||
quantity = fields.Float(string='Quantity', digits=(16, 3))
|
||||
uom = fields.Char(string='UoM', default='L')
|
||||
location = fields.Char(string='Location')
|
||||
containment_action = fields.Text(string='Containment Action')
|
||||
regulator_notified = fields.Boolean(string='Regulator Notified', tracking=True)
|
||||
regulator_notification_date = fields.Datetime(string='Notification Sent')
|
||||
root_cause = fields.Text(string='Root Cause')
|
||||
corrective_action = fields.Text(string='Corrective Action')
|
||||
capa_ref = fields.Char(string='CAPA Reference')
|
||||
state = fields.Selection(
|
||||
[('reported', 'Reported'), ('contained', 'Contained'),
|
||||
('investigation', 'Investigation'), ('closed', 'Closed')],
|
||||
string='Status', default='reported', required=True, tracking=True,
|
||||
)
|
||||
attachment_ids = fields.Many2many(
|
||||
'ir.attachment', 'fp_spill_register_attachment_rel', 'spill_id', 'attachment_id', string='Attachments',
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _default_name(self):
|
||||
seq = self.env['ir.sequence'].next_by_code('fusion.plating.spill.register')
|
||||
return seq or '/'
|
||||
|
||||
def action_contain(self):
|
||||
self.write({'state': 'contained'})
|
||||
|
||||
def action_investigate(self):
|
||||
self.write({'state': 'investigation'})
|
||||
|
||||
def action_close(self):
|
||||
self.write({'state': 'closed'})
|
||||
@@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class FpWasteManifest(models.Model):
|
||||
_name = 'fusion.plating.waste.manifest'
|
||||
_description = 'Fusion Plating - Waste Manifest'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_order = 'ship_date desc, id desc'
|
||||
|
||||
name = fields.Char(string='Reference', required=True, copy=False, default=lambda s: s._default_name(), tracking=True)
|
||||
waste_stream_id = fields.Many2one('fusion.plating.waste.stream', string='Waste Stream', required=True, ondelete='restrict', tracking=True)
|
||||
facility_id = fields.Many2one('fusion.plating.facility', related='waste_stream_id.facility_id', store=True, readonly=True)
|
||||
company_id = fields.Many2one('res.company', related='facility_id.company_id', store=True, readonly=True)
|
||||
ship_date = fields.Date(string='Ship Date', default=fields.Date.context_today, tracking=True)
|
||||
quantity = fields.Float(string='Quantity', digits=(16, 3))
|
||||
uom = fields.Char(string='UoM', default='kg')
|
||||
carrier_id = fields.Many2one('res.partner', string='Carrier', domain=[('is_company', '=', True)], tracking=True)
|
||||
receiver_id = fields.Many2one('res.partner', string='Receiver', domain=[('is_company', '=', True)], tracking=True)
|
||||
manifest_number = fields.Char(string='Manifest #', tracking=True)
|
||||
state = fields.Selection(
|
||||
[('draft', 'Draft'), ('shipped', 'Shipped'), ('received', 'Received'), ('closed', 'Closed')],
|
||||
string='Status', default='draft', required=True, tracking=True,
|
||||
)
|
||||
attachment_ids = fields.Many2many(
|
||||
'ir.attachment', 'fp_waste_manifest_attachment_rel', 'manifest_id', 'attachment_id', string='Attachments',
|
||||
)
|
||||
notes = fields.Html(string='Notes')
|
||||
|
||||
@api.model
|
||||
def _default_name(self):
|
||||
seq = self.env['ir.sequence'].next_by_code('fusion.plating.waste.manifest')
|
||||
return seq or '/'
|
||||
|
||||
def action_ship(self):
|
||||
self.write({'state': 'shipped'})
|
||||
|
||||
def action_receive(self):
|
||||
self.write({'state': 'received'})
|
||||
|
||||
def action_close(self):
|
||||
self.write({'state': 'closed'})
|
||||
@@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class FpWasteStream(models.Model):
|
||||
_name = 'fusion.plating.waste.stream'
|
||||
_description = 'Fusion Plating - Waste Stream'
|
||||
_order = 'facility_id, name'
|
||||
|
||||
name = fields.Char(string='Stream', required=True)
|
||||
code = fields.Char(string='Code')
|
||||
facility_id = fields.Many2one('fusion.plating.facility', string='Facility', required=True, ondelete='cascade')
|
||||
company_id = fields.Many2one('res.company', related='facility_id.company_id', store=True, readonly=True)
|
||||
waste_class = fields.Char(string='Waste Class')
|
||||
description = fields.Text(string='Description')
|
||||
physical_state = fields.Selection(
|
||||
[('liquid', 'Liquid'), ('solid', 'Solid'), ('sludge', 'Sludge'), ('gas', 'Gas')],
|
||||
string='Physical State', default='liquid',
|
||||
)
|
||||
generation_rate = fields.Float(string='Generation Rate')
|
||||
generation_uom = fields.Char(string='Rate UoM', default='kg/day')
|
||||
disposal_method = fields.Char(string='Disposal Method')
|
||||
approved_carrier_id = fields.Many2one('res.partner', string='Approved Carrier', domain=[('is_company', '=', True)])
|
||||
approved_facility_id = fields.Many2one('res.partner', string='Approved Receiving Facility', domain=[('is_company', '=', True)])
|
||||
manifest_ids = fields.One2many('fusion.plating.waste.manifest', 'waste_stream_id', string='Manifests')
|
||||
active = fields.Boolean(default=True)
|
||||
Reference in New Issue
Block a user