folder rename
This commit is contained in:
180
fusion_plating/fusion_plating_safety/models/fp_sds.py
Normal file
180
fusion_plating/fusion_plating_safety/models/fp_sds.py
Normal file
@@ -0,0 +1,180 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
# Part of the Fusion Plating product family.
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class FpSds(models.Model):
|
||||
"""Safety Data Sheet library entry.
|
||||
|
||||
A Safety Data Sheet (SDS) is a 16-section document supplied by a chemical
|
||||
manufacturer that describes the hazards, handling, storage, exposure
|
||||
controls and emergency information for a product. Under the WHMIS 2015 /
|
||||
GHS framework an SDS is considered current for three years from its
|
||||
issue date — after which a refresh from the supplier is required.
|
||||
|
||||
Each SDS in the library carries supplier metadata, hazard classification,
|
||||
GHS pictogram codes, language coverage and a link to the original PDF
|
||||
via ir.attachment.
|
||||
"""
|
||||
_name = 'fusion.plating.sds'
|
||||
_description = 'Fusion Plating — Safety Data Sheet'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_order = 'product_name, version desc, issue_date desc'
|
||||
_rec_name = 'name'
|
||||
|
||||
name = fields.Char(
|
||||
string='Reference',
|
||||
required=True,
|
||||
tracking=True,
|
||||
help='Internal reference for this SDS entry, often the product name.',
|
||||
)
|
||||
product_name = fields.Char(
|
||||
string='Product Name',
|
||||
tracking=True,
|
||||
)
|
||||
supplier_name = fields.Char(
|
||||
string='Supplier (Text)',
|
||||
tracking=True,
|
||||
help='Free-text supplier name as printed on the SDS, used when '
|
||||
'no res.partner record exists yet.',
|
||||
)
|
||||
supplier_id = fields.Many2one(
|
||||
'res.partner',
|
||||
string='Supplier',
|
||||
tracking=True,
|
||||
)
|
||||
cas_number = fields.Char(
|
||||
string='CAS Number',
|
||||
tracking=True,
|
||||
)
|
||||
version = fields.Char(
|
||||
string='Version',
|
||||
tracking=True,
|
||||
)
|
||||
issue_date = fields.Date(
|
||||
string='Issue Date',
|
||||
tracking=True,
|
||||
)
|
||||
expiry_date = fields.Date(
|
||||
string='Expiry Date',
|
||||
compute='_compute_expiry_date',
|
||||
store=True,
|
||||
tracking=True,
|
||||
help='Computed as issue date + 3 years per WHMIS / GHS rule. '
|
||||
'Refresh from the supplier is required before this date.',
|
||||
)
|
||||
state = fields.Selection(
|
||||
[
|
||||
('current', 'Current'),
|
||||
('expiring_soon', 'Expiring Soon'),
|
||||
('expired', 'Expired'),
|
||||
('withdrawn', 'Withdrawn'),
|
||||
],
|
||||
string='Status',
|
||||
compute='_compute_state',
|
||||
store=True,
|
||||
tracking=True,
|
||||
)
|
||||
hazard_class = fields.Selection(
|
||||
[
|
||||
('flammable', 'Flammable'),
|
||||
('oxidizer', 'Oxidizer'),
|
||||
('compressed_gas', 'Compressed Gas'),
|
||||
('corrosive', 'Corrosive'),
|
||||
('toxic', 'Toxic'),
|
||||
('carcinogen', 'Carcinogen'),
|
||||
('reproductive_toxin', 'Reproductive Toxin'),
|
||||
('sensitizer', 'Sensitizer'),
|
||||
('aquatic_toxin', 'Aquatic Toxin'),
|
||||
('other', 'Other'),
|
||||
],
|
||||
string='Primary Hazard Class',
|
||||
tracking=True,
|
||||
)
|
||||
ghs_pictograms = fields.Char(
|
||||
string='GHS Pictograms',
|
||||
help='Comma-separated GHS pictogram codes, e.g. GHS01,GHS02,GHS05.',
|
||||
)
|
||||
language = fields.Selection(
|
||||
[
|
||||
('en', 'English'),
|
||||
('fr', 'French'),
|
||||
('both', 'Bilingual (EN/FR)'),
|
||||
],
|
||||
string='Language',
|
||||
default='en',
|
||||
)
|
||||
attachment_id = fields.Many2one(
|
||||
'ir.attachment',
|
||||
string='SDS Document',
|
||||
help='The original SDS PDF supplied by the manufacturer.',
|
||||
)
|
||||
notes = fields.Html(
|
||||
string='Notes',
|
||||
)
|
||||
withdrawn = fields.Boolean(
|
||||
string='Withdrawn',
|
||||
tracking=True,
|
||||
help='Manually mark this SDS as withdrawn (e.g. product discontinued).',
|
||||
)
|
||||
active = fields.Boolean(default=True)
|
||||
|
||||
company_id = fields.Many2one(
|
||||
'res.company',
|
||||
string='Company',
|
||||
default=lambda self: self.env.company,
|
||||
)
|
||||
|
||||
chemical_ids = fields.One2many(
|
||||
'fusion.plating.chemical',
|
||||
'sds_id',
|
||||
string='Chemicals',
|
||||
)
|
||||
chemical_count = fields.Integer(
|
||||
compute='_compute_chemical_count',
|
||||
)
|
||||
|
||||
# ==========================================================================
|
||||
# Computes
|
||||
# ==========================================================================
|
||||
@api.depends('issue_date')
|
||||
def _compute_expiry_date(self):
|
||||
for rec in self:
|
||||
if rec.issue_date:
|
||||
rec.expiry_date = rec.issue_date + relativedelta(years=3)
|
||||
else:
|
||||
rec.expiry_date = False
|
||||
|
||||
@api.depends('expiry_date', 'withdrawn')
|
||||
def _compute_state(self):
|
||||
today = fields.Date.context_today(self)
|
||||
warn_window = today + relativedelta(months=3)
|
||||
for rec in self:
|
||||
if rec.withdrawn:
|
||||
rec.state = 'withdrawn'
|
||||
elif not rec.expiry_date:
|
||||
rec.state = 'current'
|
||||
elif rec.expiry_date < today:
|
||||
rec.state = 'expired'
|
||||
elif rec.expiry_date <= warn_window:
|
||||
rec.state = 'expiring_soon'
|
||||
else:
|
||||
rec.state = 'current'
|
||||
|
||||
def _compute_chemical_count(self):
|
||||
for rec in self:
|
||||
rec.chemical_count = len(rec.chemical_ids)
|
||||
|
||||
# ==========================================================================
|
||||
# Actions
|
||||
# ==========================================================================
|
||||
def action_mark_withdrawn(self):
|
||||
self.write({'withdrawn': True})
|
||||
|
||||
def action_mark_active(self):
|
||||
self.write({'withdrawn': False})
|
||||
Reference in New Issue
Block a user