folder rename
This commit is contained in:
7
fusion_plating/fusion_plating_batch/models/__init__.py
Normal file
7
fusion_plating/fusion_plating_batch/models/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
# Part of the Fusion Plating product family.
|
||||
|
||||
from . import fp_batch
|
||||
from . import fp_batch_chemistry
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
141
fusion_plating/fusion_plating_batch/models/fp_batch.py
Normal file
141
fusion_plating/fusion_plating_batch/models/fp_batch.py
Normal file
@@ -0,0 +1,141 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
# Part of the Fusion Plating product family.
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class FpBatch(models.Model):
|
||||
"""A rack or barrel load of parts being processed through a tank.
|
||||
|
||||
Lifecycle:
|
||||
|
||||
draft → loading → in_process → unloading → complete
|
||||
↗
|
||||
(any non-complete state) → cancelled
|
||||
"""
|
||||
_name = 'fusion.plating.batch'
|
||||
_description = 'Plating Batch (Rack/Barrel Load)'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_order = 'create_date desc'
|
||||
|
||||
name = fields.Char(
|
||||
string='Batch Reference',
|
||||
required=True,
|
||||
copy=False,
|
||||
readonly=True,
|
||||
default=lambda self: self.env['ir.sequence'].next_by_code(
|
||||
'fusion.plating.batch') or '/',
|
||||
tracking=True,
|
||||
)
|
||||
facility_id = fields.Many2one(
|
||||
'fusion.plating.facility',
|
||||
string='Facility',
|
||||
required=True,
|
||||
tracking=True,
|
||||
)
|
||||
bath_id = fields.Many2one(
|
||||
'fusion.plating.bath',
|
||||
string='Bath',
|
||||
required=True,
|
||||
tracking=True,
|
||||
)
|
||||
tank_id = fields.Many2one(
|
||||
'fusion.plating.tank',
|
||||
string='Tank',
|
||||
related='bath_id.tank_id',
|
||||
store=True,
|
||||
readonly=True,
|
||||
)
|
||||
process_type_id = fields.Many2one(
|
||||
related='bath_id.process_type_id',
|
||||
store=True,
|
||||
readonly=True,
|
||||
)
|
||||
state = fields.Selection(
|
||||
selection=[
|
||||
('draft', 'Draft'),
|
||||
('loading', 'Loading'),
|
||||
('in_process', 'In Process'),
|
||||
('unloading', 'Unloading'),
|
||||
('complete', 'Complete'),
|
||||
('cancelled', 'Cancelled'),
|
||||
],
|
||||
string='Status',
|
||||
default='draft',
|
||||
required=True,
|
||||
tracking=True,
|
||||
)
|
||||
rack_ref = fields.Char(string='Rack / Barrel Ref')
|
||||
part_count = fields.Integer(string='Part Count')
|
||||
start_time = fields.Datetime(string='Process Start', tracking=True)
|
||||
end_time = fields.Datetime(string='Process End', tracking=True)
|
||||
duration_minutes = fields.Float(
|
||||
string='Duration (min)',
|
||||
compute='_compute_duration',
|
||||
store=True,
|
||||
)
|
||||
chemistry_ids = fields.One2many(
|
||||
'fusion.plating.batch.chemistry',
|
||||
'batch_id',
|
||||
string='Chemistry Readings',
|
||||
)
|
||||
chemistry_count = fields.Integer(
|
||||
string='Readings',
|
||||
compute='_compute_chemistry_count',
|
||||
)
|
||||
operator_id = fields.Many2one(
|
||||
'res.users',
|
||||
string='Operator',
|
||||
default=lambda self: self.env.user,
|
||||
tracking=True,
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
'res.company',
|
||||
string='Company',
|
||||
default=lambda self: self.env.company,
|
||||
)
|
||||
notes = fields.Html(string='Notes')
|
||||
active = fields.Boolean(default=True)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Compute
|
||||
# -------------------------------------------------------------------------
|
||||
@api.depends('start_time', 'end_time')
|
||||
def _compute_duration(self):
|
||||
for rec in self:
|
||||
if rec.start_time and rec.end_time:
|
||||
delta = rec.end_time - rec.start_time
|
||||
rec.duration_minutes = delta.total_seconds() / 60.0
|
||||
else:
|
||||
rec.duration_minutes = 0.0
|
||||
|
||||
@api.depends('chemistry_ids')
|
||||
def _compute_chemistry_count(self):
|
||||
for rec in self:
|
||||
rec.chemistry_count = len(rec.chemistry_ids)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Actions
|
||||
# -------------------------------------------------------------------------
|
||||
def action_start_loading(self):
|
||||
self.write({'state': 'loading'})
|
||||
|
||||
def action_start_process(self):
|
||||
self.write({
|
||||
'state': 'in_process',
|
||||
'start_time': fields.Datetime.now(),
|
||||
})
|
||||
|
||||
def action_start_unloading(self):
|
||||
self.write({
|
||||
'state': 'unloading',
|
||||
'end_time': fields.Datetime.now(),
|
||||
})
|
||||
|
||||
def action_complete(self):
|
||||
self.write({'state': 'complete'})
|
||||
|
||||
def action_cancel(self):
|
||||
self.write({'state': 'cancelled'})
|
||||
@@ -0,0 +1,75 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
# Part of the Fusion Plating product family.
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class FpBatchChemistry(models.Model):
|
||||
"""A single chemistry reading taken during batch processing."""
|
||||
_name = 'fusion.plating.batch.chemistry'
|
||||
_description = 'Batch Chemistry Reading'
|
||||
_order = 'reading_time desc, id desc'
|
||||
|
||||
batch_id = fields.Many2one(
|
||||
'fusion.plating.batch',
|
||||
string='Batch',
|
||||
required=True,
|
||||
ondelete='cascade',
|
||||
)
|
||||
parameter_id = fields.Many2one(
|
||||
'fusion.plating.bath.parameter',
|
||||
string='Parameter',
|
||||
required=True,
|
||||
)
|
||||
value = fields.Float(string='Value', required=True)
|
||||
reading_time = fields.Datetime(
|
||||
string='Reading Time',
|
||||
default=fields.Datetime.now,
|
||||
)
|
||||
status = fields.Selection(
|
||||
selection=[
|
||||
('pass', 'Pass'),
|
||||
('warning', 'Warning'),
|
||||
('fail', 'Fail'),
|
||||
],
|
||||
string='Status',
|
||||
compute='_compute_status',
|
||||
store=True,
|
||||
)
|
||||
notes = fields.Char(string='Notes')
|
||||
|
||||
@api.depends('parameter_id', 'value')
|
||||
def _compute_status(self):
|
||||
"""Compare value against parameter target range.
|
||||
|
||||
Uses the parameter's default target range and warning tolerance.
|
||||
A reading within [target_min, target_max] is a pass. If it falls
|
||||
within the warning tolerance band outside that range, it is a
|
||||
warning. Otherwise it is a fail.
|
||||
"""
|
||||
for rec in self:
|
||||
if not rec.parameter_id:
|
||||
rec.status = 'pass'
|
||||
continue
|
||||
param = rec.parameter_id
|
||||
target_min = param.target_min
|
||||
target_max = param.target_max
|
||||
if not target_min and not target_max:
|
||||
rec.status = 'pass'
|
||||
continue
|
||||
# Value within target range = pass
|
||||
if target_min <= rec.value <= target_max:
|
||||
rec.status = 'pass'
|
||||
continue
|
||||
# Calculate warning band from tolerance %
|
||||
tolerance = (param.warning_tolerance or 0.0) / 100.0
|
||||
span = target_max - target_min if target_max != target_min else abs(target_max) or 1.0
|
||||
margin = span * tolerance
|
||||
warning_min = target_min - margin
|
||||
warning_max = target_max + margin
|
||||
if warning_min <= rec.value <= warning_max:
|
||||
rec.status = 'warning'
|
||||
else:
|
||||
rec.status = 'fail'
|
||||
Reference in New Issue
Block a user