folder rename
This commit is contained in:
5
fusion_plating/fusion_plating_compliance/README.md
Normal file
5
fusion_plating/fusion_plating_compliance/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Fusion Plating - Compliance (Framework)
|
||||
|
||||
Jurisdiction-agnostic compliance framework. Region packs load Ontario, Toronto, etc.
|
||||
|
||||
Copyright (c) 2026 Nexa Systems Inc. OPL-1 license.
|
||||
4
fusion_plating/fusion_plating_compliance/__init__.py
Normal file
4
fusion_plating/fusion_plating_compliance/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
from . import models
|
||||
46
fusion_plating/fusion_plating_compliance/__manifest__.py
Normal file
46
fusion_plating/fusion_plating_compliance/__manifest__.py
Normal file
@@ -0,0 +1,46 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
{
|
||||
'name': 'Fusion Plating - Compliance (Framework)',
|
||||
'version': '19.0.1.0.0',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Jurisdiction-agnostic compliance framework: permits, discharge monitoring, waste manifests, pollutant inventory, compliance calendar, spill register.',
|
||||
'description': 'Generic compliance framework. Region packs load jurisdiction-specific data.',
|
||||
'author': 'Nexa Systems Inc.',
|
||||
'website': 'https://www.nexasystems.ca',
|
||||
'license': 'OPL-1',
|
||||
'price': 0.00,
|
||||
'currency': 'CAD',
|
||||
'depends': ['fusion_plating'],
|
||||
'data': [
|
||||
'security/fp_compliance_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'data/fp_sequence_data.xml',
|
||||
'data/fp_jurisdiction_data.xml',
|
||||
'data/fp_regulator_data.xml',
|
||||
'views/fp_jurisdiction_views.xml',
|
||||
'views/fp_regulator_views.xml',
|
||||
'views/fp_permit_views.xml',
|
||||
'views/fp_discharge_limit_views.xml',
|
||||
'views/fp_discharge_sample_views.xml',
|
||||
'views/fp_waste_stream_views.xml',
|
||||
'views/fp_waste_manifest_views.xml',
|
||||
'views/fp_pollutant_inventory_views.xml',
|
||||
'views/fp_compliance_event_views.xml',
|
||||
'views/fp_spill_register_views.xml',
|
||||
'views/fp_facility_views.xml',
|
||||
'views/fp_menu.xml',
|
||||
],
|
||||
'demo': [
|
||||
'data/fp_demo_compliance_data.xml',
|
||||
],
|
||||
'assets': {
|
||||
'web.assets_backend': [
|
||||
'fusion_plating_compliance/static/src/scss/fusion_plating_compliance.scss',
|
||||
],
|
||||
},
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'auto_install': False,
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc. — DEMO DATA (temporary)
|
||||
Remove this file and its manifest entry before production release.
|
||||
-->
|
||||
<odoo noupdate="1">
|
||||
|
||||
<!-- ========== Permits ========== -->
|
||||
<record id="demo_permit_eca" model="fusion.plating.permit">
|
||||
<field name="name">ECA — Main Plant Plating Operations</field>
|
||||
<field name="permit_type">eca</field>
|
||||
<field name="number">ECA-2024-0439</field>
|
||||
<field name="facility_id" ref="fusion_plating.demo_facility_main"/>
|
||||
<field name="issue_date" eval="(DateTime.today() - timedelta(days=365)).strftime('%Y-%m-%d')"/>
|
||||
<field name="expiry_date" eval="(DateTime.today() + timedelta(days=730)).strftime('%Y-%m-%d')"/>
|
||||
<field name="state">active</field>
|
||||
<field name="renewal_reminder_days">90</field>
|
||||
<field name="notes" type="html"><p>Covers all chrome, nickel and EN plating lines at main facility. Issued by MECP Ontario.</p></field>
|
||||
</record>
|
||||
|
||||
<record id="demo_permit_sewer" model="fusion.plating.permit">
|
||||
<field name="name">Sewer Use Permit — Main Plant</field>
|
||||
<field name="permit_type">sewer</field>
|
||||
<field name="number">SUP-2025-0112</field>
|
||||
<field name="facility_id" ref="fusion_plating.demo_facility_main"/>
|
||||
<field name="issue_date" eval="(DateTime.today() - timedelta(days=180)).strftime('%Y-%m-%d')"/>
|
||||
<field name="expiry_date" eval="(DateTime.today() + timedelta(days=180)).strftime('%Y-%m-%d')"/>
|
||||
<field name="state">active</field>
|
||||
<field name="renewal_reminder_days">60</field>
|
||||
<field name="notes" type="html"><p>Municipal sewer discharge permit. Requires monthly pH and metals sampling before outfall.</p></field>
|
||||
</record>
|
||||
|
||||
<record id="demo_permit_waste_gen" model="fusion.plating.permit">
|
||||
<field name="name">Waste Generator Registration — East Annex</field>
|
||||
<field name="permit_type">waste_generator</field>
|
||||
<field name="number">ONT-WGR-88412</field>
|
||||
<field name="facility_id" ref="fusion_plating.demo_facility_east"/>
|
||||
<field name="issue_date" eval="(DateTime.today() - timedelta(days=730)).strftime('%Y-%m-%d')"/>
|
||||
<field name="expiry_date" eval="(DateTime.today() - timedelta(days=30)).strftime('%Y-%m-%d')"/>
|
||||
<field name="state">expired</field>
|
||||
<field name="notes" type="html"><p>Expired — renewal application submitted, awaiting MECP approval.</p></field>
|
||||
</record>
|
||||
|
||||
<!-- ========== Waste Streams ========== -->
|
||||
<record id="demo_waste_stream_chrome_sludge" model="fusion.plating.waste.stream">
|
||||
<field name="name">Chrome Sludge</field>
|
||||
<field name="code">HW-263</field>
|
||||
<field name="facility_id" ref="fusion_plating.demo_facility_main"/>
|
||||
<field name="waste_class">263</field>
|
||||
<field name="description">Filter-press cake from hexavalent chrome waste treatment system.</field>
|
||||
<field name="physical_state">liquid</field>
|
||||
<field name="generation_rate">45.0</field>
|
||||
<field name="generation_uom">kg/day</field>
|
||||
<field name="disposal_method">Licensed hazardous waste facility</field>
|
||||
</record>
|
||||
|
||||
<record id="demo_waste_stream_spent_acid" model="fusion.plating.waste.stream">
|
||||
<field name="name">Spent Acid</field>
|
||||
<field name="code">HW-132</field>
|
||||
<field name="facility_id" ref="fusion_plating.demo_facility_main"/>
|
||||
<field name="waste_class">132</field>
|
||||
<field name="description">Spent sulphuric and hydrochloric acid from pickling tanks.</field>
|
||||
<field name="physical_state">liquid</field>
|
||||
<field name="generation_rate">120.0</field>
|
||||
<field name="generation_uom">L/day</field>
|
||||
<field name="disposal_method">Acid reclamation</field>
|
||||
</record>
|
||||
|
||||
<record id="demo_waste_stream_black_oxide" model="fusion.plating.waste.stream">
|
||||
<field name="name">Black Oxide Sludge</field>
|
||||
<field name="code">HW-252</field>
|
||||
<field name="facility_id" ref="fusion_plating.demo_facility_east"/>
|
||||
<field name="waste_class">252</field>
|
||||
<field name="description">Sludge from black oxide line waste treatment.</field>
|
||||
<field name="physical_state">sludge</field>
|
||||
<field name="generation_rate">10.0</field>
|
||||
<field name="generation_uom">kg/day</field>
|
||||
<field name="disposal_method">Stabilisation and secure landfill</field>
|
||||
</record>
|
||||
|
||||
<!-- ========== Waste Manifests ========== -->
|
||||
<record id="demo_manifest_shipped" model="fusion.plating.waste.manifest">
|
||||
<field name="name">WM-2026-0001</field>
|
||||
<field name="waste_stream_id" ref="demo_waste_stream_chrome_sludge"/>
|
||||
<field name="ship_date" eval="(DateTime.today() - timedelta(days=14)).strftime('%Y-%m-%d')"/>
|
||||
<field name="quantity">1250.0</field>
|
||||
<field name="uom">kg</field>
|
||||
<field name="manifest_number">ON-HW-2026-44810</field>
|
||||
<field name="state">shipped</field>
|
||||
<field name="notes" type="html"><p>Shipped via GFL Environmental. Tracking confirmation received.</p></field>
|
||||
</record>
|
||||
|
||||
<record id="demo_manifest_draft" model="fusion.plating.waste.manifest">
|
||||
<field name="name">WM-2026-0002</field>
|
||||
<field name="waste_stream_id" ref="demo_waste_stream_spent_acid"/>
|
||||
<field name="ship_date" eval="(DateTime.today()).strftime('%Y-%m-%d')"/>
|
||||
<field name="quantity">800.0</field>
|
||||
<field name="uom">L</field>
|
||||
<field name="state">draft</field>
|
||||
<field name="notes" type="html"><p>Pending carrier assignment for spent acid pickup.</p></field>
|
||||
</record>
|
||||
|
||||
<!-- ========== Spill Register ========== -->
|
||||
<record id="demo_spill_contained" model="fusion.plating.spill.register">
|
||||
<field name="name">SPILL-2026-001</field>
|
||||
<field name="facility_id" ref="fusion_plating.demo_facility_main"/>
|
||||
<field name="spill_date" eval="(DateTime.today() - timedelta(days=7)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="substance">Chromic Acid</field>
|
||||
<field name="quantity">5.0</field>
|
||||
<field name="uom">L</field>
|
||||
<field name="location">Chrome line — tank overflow berm</field>
|
||||
<field name="containment_action">Spill contained within secondary containment berm. Absorbent pads deployed. Area neutralised with soda ash.</field>
|
||||
<field name="regulator_notified" eval="True"/>
|
||||
<field name="regulator_notification_date" eval="(DateTime.today() - timedelta(days=7)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="state">contained</field>
|
||||
</record>
|
||||
|
||||
<record id="demo_spill_closed" model="fusion.plating.spill.register">
|
||||
<field name="name">SPILL-2026-002</field>
|
||||
<field name="facility_id" ref="fusion_plating.demo_facility_east"/>
|
||||
<field name="spill_date" eval="(DateTime.today() - timedelta(days=45)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="substance">Nickel Sulphate Solution</field>
|
||||
<field name="quantity">2.0</field>
|
||||
<field name="uom">L</field>
|
||||
<field name="location">East Annex — nickel line transfer pump</field>
|
||||
<field name="containment_action">Minor drip from pump seal. Caught by drip tray, cleaned immediately.</field>
|
||||
<field name="regulator_notified" eval="False"/>
|
||||
<field name="root_cause">Worn pump seal gasket.</field>
|
||||
<field name="corrective_action">Replaced pump seal. Added monthly seal inspection to PM schedule.</field>
|
||||
<field name="state">closed</field>
|
||||
</record>
|
||||
|
||||
<!-- ========== Pollutant Inventory ========== -->
|
||||
<record id="demo_pollutant_chromium" model="fusion.plating.pollutant.inventory">
|
||||
<field name="year" eval="DateTime.today().year"/>
|
||||
<field name="facility_id" ref="fusion_plating.demo_facility_main"/>
|
||||
<field name="substance">Chromium (and its compounds)</field>
|
||||
<field name="cas_number">7440-47-3</field>
|
||||
<field name="manufactured_kg">0.0</field>
|
||||
<field name="processed_kg">2200.0</field>
|
||||
<field name="used_kg">1800.0</field>
|
||||
<field name="released_kg">0.15</field>
|
||||
<field name="transferred_kg">850.0</field>
|
||||
<field name="threshold_kg">10000.0</field>
|
||||
<field name="notes">NPRI reporting threshold 10 tonnes. Includes hexavalent and trivalent forms.</field>
|
||||
</record>
|
||||
|
||||
<record id="demo_pollutant_nickel" model="fusion.plating.pollutant.inventory">
|
||||
<field name="year" eval="DateTime.today().year"/>
|
||||
<field name="facility_id" ref="fusion_plating.demo_facility_main"/>
|
||||
<field name="substance">Nickel (and its compounds)</field>
|
||||
<field name="cas_number">7440-02-0</field>
|
||||
<field name="manufactured_kg">0.0</field>
|
||||
<field name="processed_kg">3400.0</field>
|
||||
<field name="used_kg">2900.0</field>
|
||||
<field name="released_kg">0.08</field>
|
||||
<field name="transferred_kg">620.0</field>
|
||||
<field name="threshold_kg">10000.0</field>
|
||||
<field name="notes">NPRI reporting threshold 10 tonnes. Covers all nickel plating lines.</field>
|
||||
</record>
|
||||
|
||||
<!-- ========== Compliance Events ========== -->
|
||||
<record id="demo_event_upcoming" model="fusion.plating.compliance.event">
|
||||
<field name="name">Annual NPRI Report Submission</field>
|
||||
<field name="event_type">report_due</field>
|
||||
<field name="facility_id" ref="fusion_plating.demo_facility_main"/>
|
||||
<field name="due_date" eval="(DateTime.today() + timedelta(days=60)).strftime('%Y-%m-%d')"/>
|
||||
<field name="state">upcoming</field>
|
||||
<field name="notes" type="html"><p>National Pollutant Release Inventory annual report. Covers all substance releases and transfers for the calendar year.</p></field>
|
||||
</record>
|
||||
|
||||
<record id="demo_event_due" model="fusion.plating.compliance.event">
|
||||
<field name="name">Monthly Sewer Discharge Sample</field>
|
||||
<field name="event_type">sample_due</field>
|
||||
<field name="facility_id" ref="fusion_plating.demo_facility_main"/>
|
||||
<field name="due_date" eval="(DateTime.today()).strftime('%Y-%m-%d')"/>
|
||||
<field name="state">due</field>
|
||||
<field name="notes" type="html"><p>Grab sample required at outfall as per sewer use permit SUP-2025-0112. Parameters: pH, TSS, Cr, Ni, Zn, Cu.</p></field>
|
||||
</record>
|
||||
|
||||
<record id="demo_event_overdue" model="fusion.plating.compliance.event">
|
||||
<field name="name">East Annex Waste Generator Renewal</field>
|
||||
<field name="event_type">permit_renewal</field>
|
||||
<field name="facility_id" ref="fusion_plating.demo_facility_east"/>
|
||||
<field name="due_date" eval="(DateTime.today() - timedelta(days=30)).strftime('%Y-%m-%d')"/>
|
||||
<field name="state">overdue</field>
|
||||
<field name="notes" type="html"><p>Waste generator registration ONT-WGR-88412 expired. Renewal application submitted but not yet approved by MECP.</p></field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo noupdate="1">
|
||||
<record id="jurisdiction_ca" model="fusion.plating.jurisdiction">
|
||||
<field name="name">Canada</field><field name="code">CA</field><field name="level">country</field>
|
||||
</record>
|
||||
<record id="jurisdiction_us" model="fusion.plating.jurisdiction">
|
||||
<field name="name">United States</field><field name="code">US</field><field name="level">country</field>
|
||||
</record>
|
||||
<record id="jurisdiction_ca_on" model="fusion.plating.jurisdiction">
|
||||
<field name="name">Ontario</field><field name="code">CA-ON</field><field name="level">province</field>
|
||||
<field name="parent_id" ref="jurisdiction_ca"/>
|
||||
</record>
|
||||
<record id="jurisdiction_ca_qc" model="fusion.plating.jurisdiction">
|
||||
<field name="name">Quebec</field><field name="code">CA-QC</field><field name="level">province</field>
|
||||
<field name="parent_id" ref="jurisdiction_ca"/>
|
||||
</record>
|
||||
<record id="jurisdiction_ca_bc" model="fusion.plating.jurisdiction">
|
||||
<field name="name">British Columbia</field><field name="code">CA-BC</field><field name="level">province</field>
|
||||
<field name="parent_id" ref="jurisdiction_ca"/>
|
||||
</record>
|
||||
<record id="jurisdiction_ca_ab" model="fusion.plating.jurisdiction">
|
||||
<field name="name">Alberta</field><field name="code">CA-AB</field><field name="level">province</field>
|
||||
<field name="parent_id" ref="jurisdiction_ca"/>
|
||||
</record>
|
||||
<record id="jurisdiction_ca_mb" model="fusion.plating.jurisdiction">
|
||||
<field name="name">Manitoba</field><field name="code">CA-MB</field><field name="level">province</field>
|
||||
<field name="parent_id" ref="jurisdiction_ca"/>
|
||||
</record>
|
||||
<record id="jurisdiction_ca_sk" model="fusion.plating.jurisdiction">
|
||||
<field name="name">Saskatchewan</field><field name="code">CA-SK</field><field name="level">province</field>
|
||||
<field name="parent_id" ref="jurisdiction_ca"/>
|
||||
</record>
|
||||
<record id="jurisdiction_ca_ns" model="fusion.plating.jurisdiction">
|
||||
<field name="name">Nova Scotia</field><field name="code">CA-NS</field><field name="level">province</field>
|
||||
<field name="parent_id" ref="jurisdiction_ca"/>
|
||||
</record>
|
||||
<record id="jurisdiction_ca_nb" model="fusion.plating.jurisdiction">
|
||||
<field name="name">New Brunswick</field><field name="code">CA-NB</field><field name="level">province</field>
|
||||
<field name="parent_id" ref="jurisdiction_ca"/>
|
||||
</record>
|
||||
<record id="jurisdiction_ca_nl" model="fusion.plating.jurisdiction">
|
||||
<field name="name">Newfoundland and Labrador</field><field name="code">CA-NL</field><field name="level">province</field>
|
||||
<field name="parent_id" ref="jurisdiction_ca"/>
|
||||
</record>
|
||||
<record id="jurisdiction_ca_pe" model="fusion.plating.jurisdiction">
|
||||
<field name="name">Prince Edward Island</field><field name="code">CA-PE</field><field name="level">province</field>
|
||||
<field name="parent_id" ref="jurisdiction_ca"/>
|
||||
</record>
|
||||
<record id="jurisdiction_ca_yt" model="fusion.plating.jurisdiction">
|
||||
<field name="name">Yukon</field><field name="code">CA-YT</field><field name="level">province</field>
|
||||
<field name="parent_id" ref="jurisdiction_ca"/>
|
||||
</record>
|
||||
<record id="jurisdiction_ca_nt" model="fusion.plating.jurisdiction">
|
||||
<field name="name">Northwest Territories</field><field name="code">CA-NT</field><field name="level">province</field>
|
||||
<field name="parent_id" ref="jurisdiction_ca"/>
|
||||
</record>
|
||||
<record id="jurisdiction_ca_nu" model="fusion.plating.jurisdiction">
|
||||
<field name="name">Nunavut</field><field name="code">CA-NU</field><field name="level">province</field>
|
||||
<field name="parent_id" ref="jurisdiction_ca"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo noupdate="1">
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo noupdate="1">
|
||||
<record id="seq_fp_discharge_sample" model="ir.sequence">
|
||||
<field name="name">Fusion Plating: Discharge Sample</field>
|
||||
<field name="code">fusion.plating.discharge.sample</field>
|
||||
<field name="prefix">SAMP/%(year)s/</field>
|
||||
<field name="padding">5</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
<record id="seq_fp_waste_manifest" model="ir.sequence">
|
||||
<field name="name">Fusion Plating: Waste Manifest</field>
|
||||
<field name="code">fusion.plating.waste.manifest</field>
|
||||
<field name="prefix">WM/%(year)s/</field>
|
||||
<field name="padding">5</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
<record id="seq_fp_spill_register" model="ir.sequence">
|
||||
<field name="name">Fusion Plating: Spill Register</field>
|
||||
<field name="code">fusion.plating.spill.register</field>
|
||||
<field name="prefix">SPILL/%(year)s/</field>
|
||||
<field name="padding">5</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
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)
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="fp_permit_company_rule" model="ir.rule">
|
||||
<field name="name">Fusion Plating: Permit - multi-company</field>
|
||||
<field name="model_id" ref="model_fusion_plating_permit"/>
|
||||
<field name="global" eval="True"/>
|
||||
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
<record id="fp_discharge_sample_company_rule" model="ir.rule">
|
||||
<field name="name">Fusion Plating: Discharge Sample - multi-company</field>
|
||||
<field name="model_id" ref="model_fusion_plating_discharge_sample"/>
|
||||
<field name="global" eval="True"/>
|
||||
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
<record id="fp_waste_manifest_company_rule" model="ir.rule">
|
||||
<field name="name">Fusion Plating: Waste Manifest - multi-company</field>
|
||||
<field name="model_id" ref="model_fusion_plating_waste_manifest"/>
|
||||
<field name="global" eval="True"/>
|
||||
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
<record id="fp_waste_stream_company_rule" model="ir.rule">
|
||||
<field name="name">Fusion Plating: Waste Stream - multi-company</field>
|
||||
<field name="model_id" ref="model_fusion_plating_waste_stream"/>
|
||||
<field name="global" eval="True"/>
|
||||
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
<record id="fp_compliance_event_company_rule" model="ir.rule">
|
||||
<field name="name">Fusion Plating: Compliance Event - multi-company</field>
|
||||
<field name="model_id" ref="model_fusion_plating_compliance_event"/>
|
||||
<field name="global" eval="True"/>
|
||||
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
<record id="fp_spill_register_company_rule" model="ir.rule">
|
||||
<field name="name">Fusion Plating: Spill Register - multi-company</field>
|
||||
<field name="model_id" ref="model_fusion_plating_spill_register"/>
|
||||
<field name="global" eval="True"/>
|
||||
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
<record id="fp_pollutant_inventory_company_rule" model="ir.rule">
|
||||
<field name="name">Fusion Plating: Pollutant Inventory - multi-company</field>
|
||||
<field name="model_id" ref="model_fusion_plating_pollutant_inventory"/>
|
||||
<field name="global" eval="True"/>
|
||||
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,37 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_fp_jurisdiction_operator,fp.jurisdiction.operator,model_fusion_plating_jurisdiction,fusion_plating.group_fusion_plating_operator,1,0,0,0
|
||||
access_fp_jurisdiction_supervisor,fp.jurisdiction.supervisor,model_fusion_plating_jurisdiction,fusion_plating.group_fusion_plating_supervisor,1,1,1,0
|
||||
access_fp_jurisdiction_manager,fp.jurisdiction.manager,model_fusion_plating_jurisdiction,fusion_plating.group_fusion_plating_manager,1,1,1,1
|
||||
access_fp_regulator_operator,fp.regulator.operator,model_fusion_plating_regulator,fusion_plating.group_fusion_plating_operator,1,0,0,0
|
||||
access_fp_regulator_supervisor,fp.regulator.supervisor,model_fusion_plating_regulator,fusion_plating.group_fusion_plating_supervisor,1,1,1,0
|
||||
access_fp_regulator_manager,fp.regulator.manager,model_fusion_plating_regulator,fusion_plating.group_fusion_plating_manager,1,1,1,1
|
||||
access_fp_permit_operator,fp.permit.operator,model_fusion_plating_permit,fusion_plating.group_fusion_plating_operator,1,0,0,0
|
||||
access_fp_permit_supervisor,fp.permit.supervisor,model_fusion_plating_permit,fusion_plating.group_fusion_plating_supervisor,1,1,1,0
|
||||
access_fp_permit_manager,fp.permit.manager,model_fusion_plating_permit,fusion_plating.group_fusion_plating_manager,1,1,1,1
|
||||
access_fp_permit_condition_operator,fp.permit.condition.operator,model_fusion_plating_permit_condition,fusion_plating.group_fusion_plating_operator,1,0,0,0
|
||||
access_fp_permit_condition_supervisor,fp.permit.condition.supervisor,model_fusion_plating_permit_condition,fusion_plating.group_fusion_plating_supervisor,1,1,1,0
|
||||
access_fp_permit_condition_manager,fp.permit.condition.manager,model_fusion_plating_permit_condition,fusion_plating.group_fusion_plating_manager,1,1,1,1
|
||||
access_fp_discharge_limit_operator,fp.discharge.limit.operator,model_fusion_plating_discharge_limit,fusion_plating.group_fusion_plating_operator,1,0,0,0
|
||||
access_fp_discharge_limit_supervisor,fp.discharge.limit.supervisor,model_fusion_plating_discharge_limit,fusion_plating.group_fusion_plating_supervisor,1,1,1,0
|
||||
access_fp_discharge_limit_manager,fp.discharge.limit.manager,model_fusion_plating_discharge_limit,fusion_plating.group_fusion_plating_manager,1,1,1,1
|
||||
access_fp_discharge_sample_operator,fp.discharge.sample.operator,model_fusion_plating_discharge_sample,fusion_plating.group_fusion_plating_operator,1,0,0,0
|
||||
access_fp_discharge_sample_supervisor,fp.discharge.sample.supervisor,model_fusion_plating_discharge_sample,fusion_plating.group_fusion_plating_supervisor,1,1,1,0
|
||||
access_fp_discharge_sample_manager,fp.discharge.sample.manager,model_fusion_plating_discharge_sample,fusion_plating.group_fusion_plating_manager,1,1,1,1
|
||||
access_fp_discharge_sample_line_operator,fp.discharge.sample.line.operator,model_fusion_plating_discharge_sample_line,fusion_plating.group_fusion_plating_operator,1,0,0,0
|
||||
access_fp_discharge_sample_line_supervisor,fp.discharge.sample.line.supervisor,model_fusion_plating_discharge_sample_line,fusion_plating.group_fusion_plating_supervisor,1,1,1,0
|
||||
access_fp_discharge_sample_line_manager,fp.discharge.sample.line.manager,model_fusion_plating_discharge_sample_line,fusion_plating.group_fusion_plating_manager,1,1,1,1
|
||||
access_fp_waste_stream_operator,fp.waste.stream.operator,model_fusion_plating_waste_stream,fusion_plating.group_fusion_plating_operator,1,0,0,0
|
||||
access_fp_waste_stream_supervisor,fp.waste.stream.supervisor,model_fusion_plating_waste_stream,fusion_plating.group_fusion_plating_supervisor,1,1,1,0
|
||||
access_fp_waste_stream_manager,fp.waste.stream.manager,model_fusion_plating_waste_stream,fusion_plating.group_fusion_plating_manager,1,1,1,1
|
||||
access_fp_waste_manifest_operator,fp.waste.manifest.operator,model_fusion_plating_waste_manifest,fusion_plating.group_fusion_plating_operator,1,0,0,0
|
||||
access_fp_waste_manifest_supervisor,fp.waste.manifest.supervisor,model_fusion_plating_waste_manifest,fusion_plating.group_fusion_plating_supervisor,1,1,1,0
|
||||
access_fp_waste_manifest_manager,fp.waste.manifest.manager,model_fusion_plating_waste_manifest,fusion_plating.group_fusion_plating_manager,1,1,1,1
|
||||
access_fp_pollutant_inventory_operator,fp.pollutant.inventory.operator,model_fusion_plating_pollutant_inventory,fusion_plating.group_fusion_plating_operator,1,0,0,0
|
||||
access_fp_pollutant_inventory_supervisor,fp.pollutant.inventory.supervisor,model_fusion_plating_pollutant_inventory,fusion_plating.group_fusion_plating_supervisor,1,1,1,0
|
||||
access_fp_pollutant_inventory_manager,fp.pollutant.inventory.manager,model_fusion_plating_pollutant_inventory,fusion_plating.group_fusion_plating_manager,1,1,1,1
|
||||
access_fp_compliance_event_operator,fp.compliance.event.operator,model_fusion_plating_compliance_event,fusion_plating.group_fusion_plating_operator,1,0,0,0
|
||||
access_fp_compliance_event_supervisor,fp.compliance.event.supervisor,model_fusion_plating_compliance_event,fusion_plating.group_fusion_plating_supervisor,1,1,1,0
|
||||
access_fp_compliance_event_manager,fp.compliance.event.manager,model_fusion_plating_compliance_event,fusion_plating.group_fusion_plating_manager,1,1,1,1
|
||||
access_fp_spill_register_operator,fp.spill.register.operator,model_fusion_plating_spill_register,fusion_plating.group_fusion_plating_operator,1,1,1,0
|
||||
access_fp_spill_register_supervisor,fp.spill.register.supervisor,model_fusion_plating_spill_register,fusion_plating.group_fusion_plating_supervisor,1,1,1,0
|
||||
access_fp_spill_register_manager,fp.spill.register.manager,model_fusion_plating_spill_register,fusion_plating.group_fusion_plating_manager,1,1,1,1
|
||||
|
@@ -0,0 +1,74 @@
|
||||
// =============================================================================
|
||||
// Fusion Plating - Compliance backend styles
|
||||
// Copyright 2026 Nexa Systems Inc.
|
||||
// License OPL-1 (Odoo Proprietary License v1.0)
|
||||
//
|
||||
// THEME AWARENESS: this file uses ONLY CSS custom properties for colour, so
|
||||
// it adapts to light and dark mode without duplication. No hex codes.
|
||||
// =============================================================================
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Local helpers - tint a semantic colour against the surface
|
||||
// -----------------------------------------------------------------------------
|
||||
@mixin fp-comp-tint($color-var, $amount: 12%) {
|
||||
background-color: color-mix(in srgb, var(#{$color-var}) #{$amount}, transparent);
|
||||
color: var(#{$color-var});
|
||||
border: 1px solid color-mix(in srgb, var(#{$color-var}) 35%, transparent);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Permit kanban card - state border
|
||||
// -----------------------------------------------------------------------------
|
||||
.o_fp_permit_card {
|
||||
border-left-width: 4px;
|
||||
|
||||
&[data-state="draft"] {
|
||||
border-left-color: var(--bs-secondary-color);
|
||||
}
|
||||
&[data-state="pending"] {
|
||||
border-left-color: var(--bs-info, var(--o-action));
|
||||
}
|
||||
&[data-state="active"] {
|
||||
border-left-color: var(--bs-success);
|
||||
}
|
||||
&[data-state="expired"],
|
||||
&[data-state="revoked"] {
|
||||
border-left-color: var(--bs-danger);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Discharge sample kanban card - worst-result tinting
|
||||
// -----------------------------------------------------------------------------
|
||||
.o_fp_discharge_card {
|
||||
border-left-width: 4px;
|
||||
border-left-color: var(--bs-success);
|
||||
|
||||
&[data-status="warning"] {
|
||||
border-left-color: var(--bs-warning);
|
||||
}
|
||||
&[data-status="out_of_spec"] {
|
||||
border-left-color: var(--bs-danger);
|
||||
}
|
||||
&[data-status="pending"] {
|
||||
border-left-color: var(--bs-secondary-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Overdue indicator - small badge for kanban / list highlights
|
||||
// -----------------------------------------------------------------------------
|
||||
.o_fp_overdue_indicator {
|
||||
@include fp-comp-tint(--bs-danger, 14%);
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.02em;
|
||||
border-radius: 999px;
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_fp_compliance_event_list" model="ir.ui.view">
|
||||
<field name="name">fp.compliance.event.list</field>
|
||||
<field name="model">fusion.plating.compliance.event</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Compliance Calendar" decoration-danger="state == 'overdue'" decoration-warning="state == 'due'" decoration-success="state == 'done'">
|
||||
<field name="due_date"/>
|
||||
<field name="name"/>
|
||||
<field name="event_type"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="permit_id"/>
|
||||
<field name="regulator_id"/>
|
||||
<field name="owner_id"/>
|
||||
<field name="state" widget="badge"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_compliance_event_form" model="ir.ui.view">
|
||||
<field name="name">fp.compliance.event.form</field>
|
||||
<field name="model">fusion.plating.compliance.event</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Compliance Event">
|
||||
<header>
|
||||
<button name="action_mark_done" string="Mark Done" type="object" invisible="state in ('done', 'skipped')" class="oe_highlight"/>
|
||||
<button name="action_skip" string="Skip" type="object" invisible="state in ('done', 'skipped')"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="upcoming,due,overdue,done"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1><field name="name" placeholder="e.g. Submit Annual ECA Report"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="event_type"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="due_date"/>
|
||||
<field name="owner_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="permit_id"/>
|
||||
<field name="regulator_id"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Notes"><field name="notes" nolabel="1"/></group>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_compliance_event_kanban" model="ir.ui.view">
|
||||
<field name="name">fp.compliance.event.kanban</field>
|
||||
<field name="model">fusion.plating.compliance.event</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban class="o_fp_event_kanban" default_group_by="state">
|
||||
<field name="id"/>
|
||||
<field name="name"/>
|
||||
<field name="event_type"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="due_date"/>
|
||||
<field name="owner_id"/>
|
||||
<field name="state"/>
|
||||
<templates>
|
||||
<t t-name="card">
|
||||
<div class="o_fp_card" t-att-data-state="record.state.raw_value">
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div>
|
||||
<strong class="o_fp_card_title"><field name="name"/></strong>
|
||||
<div class="text-muted small"><field name="event_type"/></div>
|
||||
</div>
|
||||
<i class="fa fa-calendar text-muted" aria-hidden="true"/>
|
||||
</div>
|
||||
<div class="mt-2 small">Due: <field name="due_date"/></div>
|
||||
<div t-if="record.facility_id.value" class="mt-1 small">
|
||||
<field name="facility_id"/>
|
||||
</div>
|
||||
<div t-if="record.state.raw_value == 'overdue'" class="o_fp_overdue_indicator mt-2">
|
||||
Overdue
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_compliance_event_search" model="ir.ui.view">
|
||||
<field name="name">fp.compliance.event.search</field>
|
||||
<field name="model">fusion.plating.compliance.event</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Compliance Calendar">
|
||||
<field name="name"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="permit_id"/>
|
||||
<field name="owner_id"/>
|
||||
<filter string="Overdue" name="overdue" domain="[('state', '=', 'overdue')]"/>
|
||||
<filter string="Due" name="due" domain="[('state', '=', 'due')]"/>
|
||||
<filter string="Upcoming" name="upcoming" domain="[('state', '=', 'upcoming')]"/>
|
||||
<filter string="My Events" name="mine" domain="[('owner_id', '=', uid)]"/>
|
||||
<group>
|
||||
<filter string="Facility" name="group_facility" context="{'group_by': 'facility_id'}"/>
|
||||
<filter string="Type" name="group_type" context="{'group_by': 'event_type'}"/>
|
||||
<filter string="Status" name="group_state" context="{'group_by': 'state'}"/>
|
||||
<filter string="Owner" name="group_owner" context="{'group_by': 'owner_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_fp_compliance_event" model="ir.actions.act_window">
|
||||
<field name="name">Compliance Calendar</field>
|
||||
<field name="res_model">fusion.plating.compliance.event</field>
|
||||
<field name="view_mode">kanban,list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_compliance_event_search"/>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_fp_discharge_limit_list" model="ir.ui.view">
|
||||
<field name="name">fp.discharge.limit.list</field>
|
||||
<field name="model">fusion.plating.discharge.limit</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Discharge Limits" editable="bottom">
|
||||
<field name="name"/>
|
||||
<field name="parameter"/>
|
||||
<field name="jurisdiction_id"/>
|
||||
<field name="discharge_point"/>
|
||||
<field name="limit_type"/>
|
||||
<field name="min_value"/>
|
||||
<field name="limit_value"/>
|
||||
<field name="uom"/>
|
||||
<field name="regulation_ref"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_discharge_limit_form" model="ir.ui.view">
|
||||
<field name="name">fp.discharge.limit.form</field>
|
||||
<field name="model">fusion.plating.discharge.limit</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Discharge Limit">
|
||||
<sheet>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1><field name="name" placeholder="e.g. Nickel (total)"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="parameter"/>
|
||||
<field name="jurisdiction_id"/>
|
||||
<field name="discharge_point"/>
|
||||
<field name="regulation_ref"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="limit_type"/>
|
||||
<field name="min_value" invisible="limit_type != 'range'"/>
|
||||
<field name="limit_value"/>
|
||||
<field name="uom"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
</group>
|
||||
</group>
|
||||
<group><field name="reference_url" widget="url"/></group>
|
||||
<group string="Notes"><field name="notes" nolabel="1"/></group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_discharge_limit_search" model="ir.ui.view">
|
||||
<field name="name">fp.discharge.limit.search</field>
|
||||
<field name="model">fusion.plating.discharge.limit</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Discharge Limits">
|
||||
<field name="name"/>
|
||||
<field name="parameter"/>
|
||||
<field name="jurisdiction_id"/>
|
||||
<field name="regulation_ref"/>
|
||||
<filter string="Sanitary Sewer" name="sanitary" domain="[('discharge_point', '=', 'sanitary')]"/>
|
||||
<filter string="Storm Sewer" name="storm" domain="[('discharge_point', '=', 'storm')]"/>
|
||||
<filter string="Air Emission" name="air" domain="[('discharge_point', '=', 'air')]"/>
|
||||
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
|
||||
<group>
|
||||
<filter string="Jurisdiction" name="group_jurisdiction" context="{'group_by': 'jurisdiction_id'}"/>
|
||||
<filter string="Discharge Point" name="group_dp" context="{'group_by': 'discharge_point'}"/>
|
||||
<filter string="Parameter" name="group_param" context="{'group_by': 'parameter'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_fp_discharge_limit" model="ir.actions.act_window">
|
||||
<field name="name">Discharge Limits</field>
|
||||
<field name="res_model">fusion.plating.discharge.limit</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_discharge_limit_search"/>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_fp_discharge_sample_list" model="ir.ui.view">
|
||||
<field name="name">fp.discharge.sample.list</field>
|
||||
<field name="model">fusion.plating.discharge.sample</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Discharge Samples" decoration-danger="worst_status == 'out_of_spec'" decoration-warning="worst_status == 'warning'">
|
||||
<field name="name"/>
|
||||
<field name="sample_date"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="sample_point"/>
|
||||
<field name="lab_id"/>
|
||||
<field name="state" widget="badge"/>
|
||||
<field name="worst_status" widget="badge"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_discharge_sample_form" model="ir.ui.view">
|
||||
<field name="name">fp.discharge.sample.form</field>
|
||||
<field name="model">fusion.plating.discharge.sample</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Discharge Sample">
|
||||
<header>
|
||||
<button name="action_send_to_lab" string="Send to Lab" type="object" invisible="state != 'draft'" class="oe_highlight"/>
|
||||
<button name="action_results_in" string="Results In" type="object" invisible="state != 'sent_to_lab'" class="oe_highlight"/>
|
||||
<button name="action_escalate" string="Escalate" type="object" invisible="state not in ('results_in',)"/>
|
||||
<button name="action_close" string="Close" type="object" invisible="state not in ('results_in', 'escalated')"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,sent_to_lab,results_in,closed"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1><field name="name" readonly="1"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="facility_id"/>
|
||||
<field name="sample_date"/>
|
||||
<field name="sample_point"/>
|
||||
<field name="collected_by_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="lab_id"/>
|
||||
<field name="chain_of_custody_ref"/>
|
||||
<field name="lab_report_ref"/>
|
||||
<field name="received_date"/>
|
||||
<field name="worst_status" readonly="1" widget="badge"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Parameters">
|
||||
<field name="line_ids">
|
||||
<list editable="bottom">
|
||||
<field name="limit_id"/>
|
||||
<field name="parameter"/>
|
||||
<field name="value"/>
|
||||
<field name="uom"/>
|
||||
<field name="status" widget="badge"/>
|
||||
<field name="notes"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Attachments"><field name="attachment_ids" widget="many2many_binary"/></page>
|
||||
<page string="Notes"><field name="notes"/></page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_discharge_sample_kanban" model="ir.ui.view">
|
||||
<field name="name">fp.discharge.sample.kanban</field>
|
||||
<field name="model">fusion.plating.discharge.sample</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban class="o_fp_discharge_kanban" default_group_by="state">
|
||||
<field name="id"/>
|
||||
<field name="name"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="sample_date"/>
|
||||
<field name="sample_point"/>
|
||||
<field name="worst_status"/>
|
||||
<field name="state"/>
|
||||
<templates>
|
||||
<t t-name="card">
|
||||
<div class="o_fp_discharge_card o_fp_card" t-att-data-status="record.worst_status.raw_value">
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div>
|
||||
<strong class="o_fp_card_title"><field name="name"/></strong>
|
||||
<div class="text-muted small"><field name="sample_point"/></div>
|
||||
</div>
|
||||
<i class="fa fa-tint text-muted" aria-hidden="true"/>
|
||||
</div>
|
||||
<div class="mt-2 small"><field name="facility_id"/></div>
|
||||
<div class="mt-2 small"><field name="sample_date"/></div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_discharge_sample_search" model="ir.ui.view">
|
||||
<field name="name">fp.discharge.sample.search</field>
|
||||
<field name="model">fusion.plating.discharge.sample</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Discharge Samples">
|
||||
<field name="name"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="sample_point"/>
|
||||
<field name="lab_id"/>
|
||||
<filter string="Out of Spec" name="oos" domain="[('worst_status', '=', 'out_of_spec')]"/>
|
||||
<filter string="Warning" name="warn" domain="[('worst_status', '=', 'warning')]"/>
|
||||
<filter string="Open" name="open" domain="[('state', 'not in', ('closed',))]"/>
|
||||
<group>
|
||||
<filter string="Facility" name="group_facility" context="{'group_by': 'facility_id'}"/>
|
||||
<filter string="Status" name="group_state" context="{'group_by': 'state'}"/>
|
||||
<filter string="Sample Date" name="group_date" context="{'group_by': 'sample_date'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_fp_discharge_sample" model="ir.actions.act_window">
|
||||
<field name="name">Discharge Samples</field>
|
||||
<field name="res_model">fusion.plating.discharge.sample</field>
|
||||
<field name="view_mode">kanban,list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_discharge_sample_search"/>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_fp_facility_form_compliance" model="ir.ui.view">
|
||||
<field name="name">fp.facility.form.compliance</field>
|
||||
<field name="model">fusion.plating.facility</field>
|
||||
<field name="inherit_id" ref="fusion_plating.view_fp_facility_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<button name="%(action_fp_permit)d" type="action" class="oe_stat_button" icon="fa-file-text-o"
|
||||
context="{'search_default_facility_id': id}">
|
||||
<field name="permit_count" widget="statinfo" string="Permits"/>
|
||||
</button>
|
||||
<button name="%(action_fp_discharge_sample)d" type="action" class="oe_stat_button" icon="fa-tint"
|
||||
context="{'search_default_facility_id': id}">
|
||||
<field name="discharge_sample_count" widget="statinfo" string="Samples"/>
|
||||
</button>
|
||||
<button name="%(action_fp_compliance_event)d" type="action" class="oe_stat_button" icon="fa-calendar"
|
||||
context="{'search_default_facility_id': id}">
|
||||
<field name="compliance_event_count" widget="statinfo" string="Events"/>
|
||||
</button>
|
||||
<button name="%(action_fp_waste_stream)d" type="action" class="oe_stat_button" icon="fa-trash"
|
||||
context="{'search_default_facility_id': id}">
|
||||
<field name="waste_stream_count" widget="statinfo" string="Streams"/>
|
||||
</button>
|
||||
<button name="%(action_fp_spill_register)d" type="action" class="oe_stat_button" icon="fa-exclamation-triangle"
|
||||
context="{'search_default_facility_id': id}">
|
||||
<field name="spill_count" widget="statinfo" string="Spills"/>
|
||||
</button>
|
||||
</xpath>
|
||||
<xpath expr="//notebook" position="inside">
|
||||
<page string="Compliance">
|
||||
<group>
|
||||
<group string="Identifiers">
|
||||
<field name="x_fp_eca_number"/>
|
||||
<field name="x_fp_sewer_permit_number"/>
|
||||
<field name="x_fp_waste_generator_number"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Permits">
|
||||
<field name="permit_ids" nolabel="1">
|
||||
<list>
|
||||
<field name="name"/>
|
||||
<field name="permit_type"/>
|
||||
<field name="number"/>
|
||||
<field name="regulator_id"/>
|
||||
<field name="expiry_date"/>
|
||||
<field name="state"/>
|
||||
</list>
|
||||
</field>
|
||||
</group>
|
||||
<group string="Compliance Notes">
|
||||
<field name="x_fp_compliance_notes" nolabel="1"/>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_fp_jurisdiction_list" model="ir.ui.view">
|
||||
<field name="name">fp.jurisdiction.list</field>
|
||||
<field name="model">fusion.plating.jurisdiction</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Jurisdictions">
|
||||
<field name="display_name"/>
|
||||
<field name="code"/>
|
||||
<field name="level"/>
|
||||
<field name="parent_id"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_jurisdiction_form" model="ir.ui.view">
|
||||
<field name="name">fp.jurisdiction.form</field>
|
||||
<field name="model">fusion.plating.jurisdiction</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Jurisdiction">
|
||||
<sheet>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1><field name="name" placeholder="e.g. Ontario"/></h1>
|
||||
<div class="text-muted"><field name="code" placeholder="CA-ON"/></div>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="level"/>
|
||||
<field name="parent_id"/>
|
||||
</group>
|
||||
<group><field name="active" widget="boolean_toggle"/></group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Children">
|
||||
<field name="child_ids">
|
||||
<list>
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="level"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_jurisdiction_search" model="ir.ui.view">
|
||||
<field name="name">fp.jurisdiction.search</field>
|
||||
<field name="model">fusion.plating.jurisdiction</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Jurisdictions">
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="parent_id"/>
|
||||
<filter string="Countries" name="country" domain="[('level', '=', 'country')]"/>
|
||||
<filter string="Provinces / States" name="province" domain="[('level', '=', 'province')]"/>
|
||||
<filter string="Municipalities" name="municipality" domain="[('level', '=', 'municipality')]"/>
|
||||
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
|
||||
<group>
|
||||
<filter string="Level" name="group_level" context="{'group_by': 'level'}"/>
|
||||
<filter string="Parent" name="group_parent" context="{'group_by': 'parent_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_fp_jurisdiction" model="ir.actions.act_window">
|
||||
<field name="name">Jurisdictions</field>
|
||||
<field name="res_model">fusion.plating.jurisdiction</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_jurisdiction_search"/>
|
||||
</record>
|
||||
</odoo>
|
||||
17
fusion_plating/fusion_plating_compliance/views/fp_menu.xml
Normal file
17
fusion_plating/fusion_plating_compliance/views/fp_menu.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<menuitem id="menu_fp_compliance_root" name="Compliance" parent="fusion_plating.menu_fp_root" sequence="40"/>
|
||||
|
||||
<menuitem id="menu_fp_compliance_permit" name="Permits" parent="menu_fp_compliance_root" action="action_fp_permit" sequence="10"/>
|
||||
<menuitem id="menu_fp_compliance_discharge_sample" name="Discharge Samples" parent="menu_fp_compliance_root" action="action_fp_discharge_sample" sequence="20"/>
|
||||
<menuitem id="menu_fp_compliance_waste_manifest" name="Waste Manifests" parent="menu_fp_compliance_root" action="action_fp_waste_manifest" sequence="30"/>
|
||||
<menuitem id="menu_fp_compliance_calendar" name="Compliance Calendar" parent="menu_fp_compliance_root" action="action_fp_compliance_event" sequence="40"/>
|
||||
<menuitem id="menu_fp_compliance_spill_register" name="Spill Register" parent="menu_fp_compliance_root" action="action_fp_spill_register" sequence="50"/>
|
||||
|
||||
<menuitem id="menu_fp_compliance_config" name="Configuration" parent="menu_fp_compliance_root" sequence="100"/>
|
||||
<menuitem id="menu_fp_compliance_jurisdiction" name="Jurisdictions" parent="menu_fp_compliance_config" action="action_fp_jurisdiction" sequence="10"/>
|
||||
<menuitem id="menu_fp_compliance_regulator" name="Regulators" parent="menu_fp_compliance_config" action="action_fp_regulator" sequence="20"/>
|
||||
<menuitem id="menu_fp_compliance_discharge_limit" name="Discharge Limits" parent="menu_fp_compliance_config" action="action_fp_discharge_limit" sequence="30"/>
|
||||
<menuitem id="menu_fp_compliance_waste_stream" name="Waste Streams" parent="menu_fp_compliance_config" action="action_fp_waste_stream" sequence="40"/>
|
||||
<menuitem id="menu_fp_compliance_pollutant_inventory" name="Pollutant Inventory" parent="menu_fp_compliance_config" action="action_fp_pollutant_inventory" sequence="50"/>
|
||||
</odoo>
|
||||
@@ -0,0 +1,151 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_fp_permit_list" model="ir.ui.view">
|
||||
<field name="name">fp.permit.list</field>
|
||||
<field name="model">fusion.plating.permit</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Permits" decoration-warning="is_expiring_soon" decoration-danger="state == 'expired'">
|
||||
<field name="name"/>
|
||||
<field name="permit_type"/>
|
||||
<field name="number"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="regulator_id"/>
|
||||
<field name="issue_date"/>
|
||||
<field name="expiry_date"/>
|
||||
<field name="days_until_expiry"/>
|
||||
<field name="state" widget="badge"/>
|
||||
<field name="is_expiring_soon" column_invisible="1"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_permit_form" model="ir.ui.view">
|
||||
<field name="name">fp.permit.form</field>
|
||||
<field name="model">fusion.plating.permit</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Permit">
|
||||
<header>
|
||||
<button name="action_set_pending" string="Set Pending" type="object" invisible="state != 'draft'" class="oe_highlight"/>
|
||||
<button name="action_activate" string="Activate" type="object" invisible="state not in ('draft', 'pending')" class="oe_highlight"/>
|
||||
<button name="action_mark_expired" string="Mark Expired" type="object" invisible="state != 'active'"/>
|
||||
<button name="action_revoke" string="Revoke" type="object" invisible="state in ('draft', 'revoked')"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,pending,active,expired"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1><field name="name" placeholder="e.g. ECA #1234-ABCDEF"/></h1>
|
||||
<div class="text-muted"><field name="number" placeholder="Permit Number"/></div>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="permit_type"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="regulator_id"/>
|
||||
<field name="jurisdiction_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="issue_date"/>
|
||||
<field name="expiry_date"/>
|
||||
<field name="renewal_reminder_days"/>
|
||||
<field name="days_until_expiry" readonly="1"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Conditions">
|
||||
<field name="condition_ids">
|
||||
<list editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="frequency"/>
|
||||
<field name="next_due_date"/>
|
||||
<field name="owner_id"/>
|
||||
<field name="status" widget="badge"/>
|
||||
</list>
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="frequency"/>
|
||||
<field name="next_due_date"/>
|
||||
<field name="owner_id"/>
|
||||
<field name="status"/>
|
||||
</group>
|
||||
<group string="Description"><field name="description" nolabel="1"/></group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Attachments"><field name="attachment_ids" widget="many2many_binary"/></page>
|
||||
<page string="Notes"><field name="notes"/></page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_permit_kanban" model="ir.ui.view">
|
||||
<field name="name">fp.permit.kanban</field>
|
||||
<field name="model">fusion.plating.permit</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban class="o_fp_permit_kanban" default_group_by="state">
|
||||
<field name="id"/>
|
||||
<field name="name"/>
|
||||
<field name="number"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="expiry_date"/>
|
||||
<field name="days_until_expiry"/>
|
||||
<field name="state"/>
|
||||
<field name="is_expiring_soon"/>
|
||||
<templates>
|
||||
<t t-name="card">
|
||||
<div class="o_fp_permit_card o_fp_card" t-att-data-state="record.state.raw_value">
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div>
|
||||
<strong class="o_fp_card_title"><field name="name"/></strong>
|
||||
<div class="text-muted small"><field name="number"/></div>
|
||||
</div>
|
||||
<i class="fa fa-file-text-o text-muted" aria-hidden="true"/>
|
||||
</div>
|
||||
<div class="mt-2 small"><field name="facility_id"/></div>
|
||||
<div class="mt-2 small">Expires: <field name="expiry_date"/></div>
|
||||
<div t-if="record.is_expiring_soon.raw_value" class="o_fp_overdue_indicator mt-2">
|
||||
Renewal due in <field name="days_until_expiry"/> days
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_permit_search" model="ir.ui.view">
|
||||
<field name="name">fp.permit.search</field>
|
||||
<field name="model">fusion.plating.permit</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Permits">
|
||||
<field name="name"/>
|
||||
<field name="number"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="regulator_id"/>
|
||||
<filter string="Active" name="active" domain="[('state', '=', 'active')]"/>
|
||||
<filter string="Pending" name="pending" domain="[('state', '=', 'pending')]"/>
|
||||
<filter string="Expiring Soon" name="expiring" domain="[('is_expiring_soon', '=', True)]"/>
|
||||
<filter string="Expired" name="expired" domain="[('state', '=', 'expired')]"/>
|
||||
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
|
||||
<group>
|
||||
<filter string="Facility" name="group_facility" context="{'group_by': 'facility_id'}"/>
|
||||
<filter string="Type" name="group_type" context="{'group_by': 'permit_type'}"/>
|
||||
<filter string="Regulator" name="group_regulator" context="{'group_by': 'regulator_id'}"/>
|
||||
<filter string="Status" name="group_state" context="{'group_by': 'state'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_fp_permit" model="ir.actions.act_window">
|
||||
<field name="name">Permits</field>
|
||||
<field name="res_model">fusion.plating.permit</field>
|
||||
<field name="view_mode">kanban,list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_permit_search"/>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_fp_pollutant_inventory_list" model="ir.ui.view">
|
||||
<field name="name">fp.pollutant.inventory.list</field>
|
||||
<field name="model">fusion.plating.pollutant.inventory</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Pollutant Inventory" decoration-warning="threshold_exceeded">
|
||||
<field name="year"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="substance"/>
|
||||
<field name="cas_number"/>
|
||||
<field name="manufactured_kg" sum="Manufactured"/>
|
||||
<field name="processed_kg" sum="Processed"/>
|
||||
<field name="used_kg" sum="Used"/>
|
||||
<field name="released_kg" sum="Released"/>
|
||||
<field name="transferred_kg" sum="Transferred"/>
|
||||
<field name="threshold_exceeded" widget="boolean_toggle" readonly="1"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_pollutant_inventory_form" model="ir.ui.view">
|
||||
<field name="name">fp.pollutant.inventory.form</field>
|
||||
<field name="model">fusion.plating.pollutant.inventory</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Pollutant Inventory Entry">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="substance"/>
|
||||
<h1><field name="substance" placeholder="e.g. Nickel and its compounds"/></h1>
|
||||
<div class="text-muted"><field name="cas_number" placeholder="CAS Number"/></div>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="year"/>
|
||||
<field name="facility_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="threshold_kg"/>
|
||||
<field name="threshold_exceeded" readonly="1" widget="boolean_toggle"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Quantities (kg/year)">
|
||||
<group>
|
||||
<field name="manufactured_kg"/>
|
||||
<field name="processed_kg"/>
|
||||
<field name="used_kg"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="released_kg"/>
|
||||
<field name="transferred_kg"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Notes"><field name="notes" nolabel="1"/></group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_pollutant_inventory_search" model="ir.ui.view">
|
||||
<field name="name">fp.pollutant.inventory.search</field>
|
||||
<field name="model">fusion.plating.pollutant.inventory</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Pollutant Inventory">
|
||||
<field name="substance"/>
|
||||
<field name="cas_number"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="year"/>
|
||||
<filter string="Threshold Exceeded" name="exceeded" domain="[('threshold_exceeded', '=', True)]"/>
|
||||
<group>
|
||||
<filter string="Year" name="group_year" context="{'group_by': 'year'}"/>
|
||||
<filter string="Facility" name="group_facility" context="{'group_by': 'facility_id'}"/>
|
||||
<filter string="Substance" name="group_substance" context="{'group_by': 'substance'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_fp_pollutant_inventory" model="ir.actions.act_window">
|
||||
<field name="name">Pollutant Inventory</field>
|
||||
<field name="res_model">fusion.plating.pollutant.inventory</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_pollutant_inventory_search"/>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_fp_regulator_list" model="ir.ui.view">
|
||||
<field name="name">fp.regulator.list</field>
|
||||
<field name="model">fusion.plating.regulator</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Regulators">
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="jurisdiction_id"/>
|
||||
<field name="category"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_regulator_form" model="ir.ui.view">
|
||||
<field name="name">fp.regulator.form</field>
|
||||
<field name="model">fusion.plating.regulator</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Regulator">
|
||||
<sheet>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1><field name="name" placeholder="e.g. Ministry of Environment"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="code"/>
|
||||
<field name="jurisdiction_id"/>
|
||||
<field name="category"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="website" widget="url"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Contact"><field name="contact_info" nolabel="1"/></group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_regulator_search" model="ir.ui.view">
|
||||
<field name="name">fp.regulator.search</field>
|
||||
<field name="model">fusion.plating.regulator</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Regulators">
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="jurisdiction_id"/>
|
||||
<filter string="Environmental" name="environmental" domain="[('category', '=', 'environmental')]"/>
|
||||
<filter string="Water" name="water" domain="[('category', '=', 'water')]"/>
|
||||
<filter string="Waste" name="waste" domain="[('category', '=', 'waste')]"/>
|
||||
<filter string="Health and Safety" name="hs" domain="[('category', '=', 'health_safety')]"/>
|
||||
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
|
||||
<group>
|
||||
<filter string="Jurisdiction" name="group_jurisdiction" context="{'group_by': 'jurisdiction_id'}"/>
|
||||
<filter string="Category" name="group_category" context="{'group_by': 'category'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_fp_regulator" model="ir.actions.act_window">
|
||||
<field name="name">Regulators</field>
|
||||
<field name="res_model">fusion.plating.regulator</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_regulator_search"/>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_fp_spill_register_list" model="ir.ui.view">
|
||||
<field name="name">fp.spill.register.list</field>
|
||||
<field name="model">fusion.plating.spill.register</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Spill Register">
|
||||
<field name="name"/>
|
||||
<field name="spill_date"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="substance"/>
|
||||
<field name="quantity"/>
|
||||
<field name="uom"/>
|
||||
<field name="regulator_notified" widget="boolean_toggle"/>
|
||||
<field name="state" widget="badge"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_spill_register_form" model="ir.ui.view">
|
||||
<field name="name">fp.spill.register.form</field>
|
||||
<field name="model">fusion.plating.spill.register</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Spill Register">
|
||||
<header>
|
||||
<button name="action_contain" string="Contained" type="object" invisible="state != 'reported'" class="oe_highlight"/>
|
||||
<button name="action_investigate" string="Investigate" type="object" invisible="state not in ('contained',)"/>
|
||||
<button name="action_close" string="Close" type="object" invisible="state in ('reported', 'closed')"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="reported,contained,investigation,closed"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1><field name="name" readonly="1"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="facility_id"/>
|
||||
<field name="spill_date"/>
|
||||
<field name="reported_by_id"/>
|
||||
<field name="location"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="substance"/>
|
||||
<field name="quantity"/>
|
||||
<field name="uom"/>
|
||||
<field name="capa_ref"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Regulator Notification">
|
||||
<field name="regulator_notified"/>
|
||||
<field name="regulator_notification_date" invisible="not regulator_notified"/>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Containment"><field name="containment_action" nolabel="1"/></page>
|
||||
<page string="Root Cause"><field name="root_cause" nolabel="1"/></page>
|
||||
<page string="Corrective Action"><field name="corrective_action" nolabel="1"/></page>
|
||||
<page string="Attachments"><field name="attachment_ids" widget="many2many_binary"/></page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_spill_register_search" model="ir.ui.view">
|
||||
<field name="name">fp.spill.register.search</field>
|
||||
<field name="model">fusion.plating.spill.register</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Spill Register">
|
||||
<field name="name"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="substance"/>
|
||||
<filter string="Open" name="open" domain="[('state', '!=', 'closed')]"/>
|
||||
<filter string="Regulator Notified" name="notified" domain="[('regulator_notified', '=', True)]"/>
|
||||
<group>
|
||||
<filter string="Facility" name="group_facility" context="{'group_by': 'facility_id'}"/>
|
||||
<filter string="Status" name="group_state" context="{'group_by': 'state'}"/>
|
||||
<filter string="Substance" name="group_substance" context="{'group_by': 'substance'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_fp_spill_register" model="ir.actions.act_window">
|
||||
<field name="name">Spill Register</field>
|
||||
<field name="res_model">fusion.plating.spill.register</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_spill_register_search"/>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_fp_waste_manifest_list" model="ir.ui.view">
|
||||
<field name="name">fp.waste.manifest.list</field>
|
||||
<field name="model">fusion.plating.waste.manifest</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Waste Manifests">
|
||||
<field name="name"/>
|
||||
<field name="ship_date"/>
|
||||
<field name="waste_stream_id"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="quantity"/>
|
||||
<field name="uom"/>
|
||||
<field name="carrier_id"/>
|
||||
<field name="receiver_id"/>
|
||||
<field name="manifest_number"/>
|
||||
<field name="state" widget="badge"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_waste_manifest_form" model="ir.ui.view">
|
||||
<field name="name">fp.waste.manifest.form</field>
|
||||
<field name="model">fusion.plating.waste.manifest</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Waste Manifest">
|
||||
<header>
|
||||
<button name="action_ship" string="Ship" type="object" invisible="state != 'draft'" class="oe_highlight"/>
|
||||
<button name="action_receive" string="Mark Received" type="object" invisible="state != 'shipped'" class="oe_highlight"/>
|
||||
<button name="action_close" string="Close" type="object" invisible="state != 'received'"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,shipped,received,closed"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1><field name="name" readonly="1"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="waste_stream_id"/>
|
||||
<field name="facility_id" readonly="1"/>
|
||||
<field name="ship_date"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="quantity"/>
|
||||
<field name="uom"/>
|
||||
<field name="manifest_number"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Carrier and Receiver">
|
||||
<field name="carrier_id"/>
|
||||
<field name="receiver_id"/>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Attachments"><field name="attachment_ids" widget="many2many_binary"/></page>
|
||||
<page string="Notes"><field name="notes"/></page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_waste_manifest_search" model="ir.ui.view">
|
||||
<field name="name">fp.waste.manifest.search</field>
|
||||
<field name="model">fusion.plating.waste.manifest</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Waste Manifests">
|
||||
<field name="name"/>
|
||||
<field name="manifest_number"/>
|
||||
<field name="waste_stream_id"/>
|
||||
<field name="carrier_id"/>
|
||||
<field name="receiver_id"/>
|
||||
<filter string="Draft" name="draft" domain="[('state', '=', 'draft')]"/>
|
||||
<filter string="Shipped" name="shipped" domain="[('state', '=', 'shipped')]"/>
|
||||
<filter string="Received" name="received" domain="[('state', '=', 'received')]"/>
|
||||
<group>
|
||||
<filter string="Facility" name="group_facility" context="{'group_by': 'facility_id'}"/>
|
||||
<filter string="Waste Stream" name="group_stream" context="{'group_by': 'waste_stream_id'}"/>
|
||||
<filter string="Status" name="group_state" context="{'group_by': 'state'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_fp_waste_manifest" model="ir.actions.act_window">
|
||||
<field name="name">Waste Manifests</field>
|
||||
<field name="res_model">fusion.plating.waste.manifest</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_waste_manifest_search"/>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_fp_waste_stream_list" model="ir.ui.view">
|
||||
<field name="name">fp.waste.stream.list</field>
|
||||
<field name="model">fusion.plating.waste.stream</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Waste Streams">
|
||||
<field name="code"/>
|
||||
<field name="name"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="waste_class"/>
|
||||
<field name="physical_state"/>
|
||||
<field name="approved_carrier_id"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_waste_stream_form" model="ir.ui.view">
|
||||
<field name="name">fp.waste.stream.form</field>
|
||||
<field name="model">fusion.plating.waste.stream</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Waste Stream">
|
||||
<sheet>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1><field name="name" placeholder="e.g. Spent nickel bath"/></h1>
|
||||
<div class="text-muted"><field name="code" placeholder="WS-001"/></div>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="facility_id"/>
|
||||
<field name="waste_class"/>
|
||||
<field name="physical_state"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="generation_rate"/>
|
||||
<field name="generation_uom"/>
|
||||
<field name="disposal_method"/>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Approved Vendors">
|
||||
<field name="approved_carrier_id"/>
|
||||
<field name="approved_facility_id"/>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Description"><field name="description" nolabel="1"/></page>
|
||||
<page string="Manifests">
|
||||
<field name="manifest_ids">
|
||||
<list>
|
||||
<field name="name"/>
|
||||
<field name="ship_date"/>
|
||||
<field name="quantity"/>
|
||||
<field name="uom"/>
|
||||
<field name="manifest_number"/>
|
||||
<field name="state"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_fp_waste_stream_search" model="ir.ui.view">
|
||||
<field name="name">fp.waste.stream.search</field>
|
||||
<field name="model">fusion.plating.waste.stream</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Waste Streams">
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="facility_id"/>
|
||||
<field name="waste_class"/>
|
||||
<filter string="Liquid" name="liquid" domain="[('physical_state', '=', 'liquid')]"/>
|
||||
<filter string="Sludge" name="sludge" domain="[('physical_state', '=', 'sludge')]"/>
|
||||
<filter string="Solid" name="solid" domain="[('physical_state', '=', 'solid')]"/>
|
||||
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
|
||||
<group>
|
||||
<filter string="Facility" name="group_facility" context="{'group_by': 'facility_id'}"/>
|
||||
<filter string="Physical State" name="group_state" context="{'group_by': 'physical_state'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_fp_waste_stream" model="ir.actions.act_window">
|
||||
<field name="name">Waste Streams</field>
|
||||
<field name="res_model">fusion.plating.waste.stream</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fp_waste_stream_search"/>
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user