This commit is contained in:
gsinghpal
2026-05-25 08:17:29 -04:00
parent 5d5964a327
commit 80887d6098
19 changed files with 117 additions and 558 deletions

View File

@@ -5,7 +5,6 @@
from . import fp_shopfloor_station
from . import fp_bake_oven
from . import fp_bake_window
from . import fp_first_piece_gate
from . import fp_operator_queue
from . import fp_tank
from . import res_users

View File

@@ -1,128 +0,0 @@
# -*- 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 FpFirstPieceGate(models.Model):
"""First-piece inspection gate per routing.
Aerospace, nuclear and many automotive customers require that the FIRST
piece off a routing be inspected and dispositioned BEFORE the rest of
the lot is allowed to run. This model captures that gate.
"""
_name = 'fusion.plating.first.piece.gate'
_description = 'Fusion Plating — First-Piece Inspection Gate'
_inherit = ['mail.thread', 'mail.activity.mixin']
_order = 'first_piece_produced desc, id desc'
name = fields.Char(
string='Reference',
required=True,
copy=False,
readonly=True,
default=lambda self: self._default_name(),
tracking=True,
)
bath_id = fields.Many2one(
'fusion.plating.bath',
string='Bath',
ondelete='restrict',
tracking=True,
)
facility_id = fields.Many2one(
'fusion.plating.facility',
related='bath_id.facility_id',
store=True,
readonly=True,
)
part_ref = fields.Char(
string='Part Reference',
tracking=True,
)
customer_ref = fields.Char(
string='Customer Reference',
tracking=True,
)
routing_first_run = fields.Boolean(
string='First Run of Routing',
help='Tick if this is the first time this routing runs this part.',
)
first_piece_produced = fields.Datetime(
string='First Piece Produced',
tracking=True,
)
first_piece_inspected = fields.Datetime(
string='First Piece Inspected',
tracking=True,
)
inspector_id = fields.Many2one(
'res.users',
string='Inspector',
tracking=True,
)
result = fields.Selection(
[
('pending', 'Pending'),
('pass', 'Pass'),
('fail', 'Fail'),
],
string='Result',
default='pending',
required=True,
tracking=True,
)
rest_of_lot_released = fields.Boolean(
string='Rest of Lot Released',
tracking=True,
)
notes = fields.Html(
string='Notes',
)
status_color = fields.Integer(
compute='_compute_status_color',
)
active = fields.Boolean(default=True)
# ==========================================================================
@api.model
def _default_name(self):
seq = self.env['ir.sequence'].next_by_code('fusion.plating.first.piece.gate')
return seq or '/'
@api.depends('result', 'rest_of_lot_released')
def _compute_status_color(self):
for rec in self:
if rec.result == 'fail':
rec.status_color = 1 # red
elif rec.result == 'pass' and rec.rest_of_lot_released:
rec.status_color = 4 # green
elif rec.result == 'pass':
rec.status_color = 3 # yellow — passed but not released
else:
rec.status_color = 5 # purple — pending
# ==========================================================================
# Actions
# ==========================================================================
def action_mark_pass(self):
self.write({
'result': 'pass',
'first_piece_inspected': fields.Datetime.now(),
'inspector_id': self.env.user.id,
})
def action_mark_fail(self):
self.write({
'result': 'fail',
'first_piece_inspected': fields.Datetime.now(),
'inspector_id': self.env.user.id,
})
def action_release_lot(self):
self.filtered(lambda r: r.result == 'pass').write({
'rest_of_lot_released': True,
})

View File

@@ -65,20 +65,7 @@ class FpOperatorQueue(models.TransientModel):
'source_id': bw.id,
})
gate_domain = [('result', '=', 'pending')]
if facility_id:
gate_domain.append(('facility_id', '=', facility_id))
gates = self.env['fusion.plating.first.piece.gate'].search(gate_domain)
for g in gates:
rows.append({
'operator_id': user_id,
'facility_id': g.facility_id.id,
'label': f"First piece: {g.name}",
'description': f"Part {g.part_ref or '?'}",
'priority': 80,
'source_model': 'fusion.plating.first.piece.gate',
'source_id': g.id,
})
# First-piece-gate aggregation retired (19.0.33.2.0) with the model.
# ----- MRP work orders (if fusion_plating_bridge_mrp installed) -----
# Show two buckets, in this order:

View File

@@ -17,27 +17,22 @@ class FpTank(models.Model):
x_fp_shopfloor_queue_size = fields.Integer(
string='Shopfloor Queue',
compute='_compute_shopfloor_queue_size',
help='Number of bake windows + first-piece gates currently waiting on '
'this tank\'s current bath.',
help='Number of bake windows currently waiting on this tank\'s '
'current bath.',
)
def _compute_shopfloor_queue_size(self):
# First-piece-gate contribution dropped 19.0.33.2.0 with the model.
BakeWindow = self.env['fusion.plating.bake.window']
Gate = self.env['fusion.plating.first.piece.gate']
for rec in self:
bath_ids = rec.bath_ids.ids
if not bath_ids:
rec.x_fp_shopfloor_queue_size = 0
continue
count = BakeWindow.search_count([
rec.x_fp_shopfloor_queue_size = BakeWindow.search_count([
('bath_id', 'in', bath_ids),
('state', 'in', ('awaiting_bake', 'bake_in_progress')),
])
count += Gate.search_count([
('bath_id', 'in', bath_ids),
('result', '=', 'pending'),
])
rec.x_fp_shopfloor_queue_size = count
def action_open_tablet_view(self):
"""Open the tablet client action focused on this tank."""