feat(sub12b): extend fusion.plating.rack — racking_state + tags + capacity

The existing 'state' field tracks wear (active/needs_strip/stripping/
retired). Sub 12b adds an orthogonal 'racking_state' (empty/loading/
loaded/in_use/awaiting_unrack/out_of_service) for the load lifecycle
— a rack can be wear-active AND racking-loaded simultaneously.

New fields:
  racking_state        — operational lifecycle
  tag_ids (M2M)        — fp.rack.tag chips on plant overview
  capacity_count       — soft warn (distinct from existing 'capacity')
  current_job_step_id  — compute, derived from latest fp.job.step.move
  current_tank_id      — compute
  current_part_count   — compute

Form view picks up the new fields under Sub-12b group + a 'Current
Use' panel that hides when racking_state is empty / out_of_service.

The compute references fp.job.step.move which lands in Task 4 — the
module won't load cleanly on entech until Tasks 4-5 ship; that's
expected for batch deployment at the end.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-04-27 21:03:09 -04:00
parent 86c0e230a1
commit d9ae45ce9b
2 changed files with 78 additions and 0 deletions

View File

@@ -115,3 +115,67 @@ class FpRack(models.Model):
"""Add `delta` to the rack's MTO count. Called by the WO finish hook."""
for rec in self:
rec.mto_count = (rec.mto_count or 0.0) + delta
# ===== Sub 12b — racking lifecycle (orthogonal to wear-tracking state) =
racking_state = fields.Selection(
[
('empty', 'Empty'),
('loading', 'Loading'),
('loaded', 'Loaded'),
('in_use', 'In Use'),
('awaiting_unrack', 'Awaiting Unrack'),
('out_of_service', 'Out of Service'),
],
string='Racking State', default='empty', tracking=True,
help='Operational state in the rack→step→tank flow. Distinct '
'from the wear-tracking `state` (active/needs_strip/...).',
)
tag_ids = fields.Many2many(
'fp.rack.tag',
'fp_rack_tag_rel', 'rack_id', 'tag_id',
string='Tags',
)
capacity_count = fields.Integer(
string='Capacity (parts) — soft warn',
help='Soft warning threshold — runtime informs operator when '
'rack is loaded beyond this. Not enforced. Distinct from '
'`capacity` field (planning capacity).',
)
current_job_step_id = fields.Many2one(
'fp.job.step', string='Current Step',
compute='_compute_current_use', store=True,
)
current_tank_id = fields.Many2one(
'fusion.plating.tank', string='Current Tank',
compute='_compute_current_use', store=True,
)
current_part_count = fields.Integer(
string='Parts on Rack',
compute='_compute_current_use', store=True,
)
@api.depends('racking_state')
def _compute_current_use(self):
# Walks the most recent fp.job.step.move row per rack to derive
# current step + tank + part count. For racks not currently in
# use, all values are blank.
Move = self.env['fp.job.step.move']
for rack in self:
if rack.racking_state in ('empty', 'out_of_service'):
rack.current_job_step_id = False
rack.current_tank_id = False
rack.current_part_count = 0
continue
recent = Move.search(
[('rack_id', '=', rack.id)],
order='move_datetime desc',
limit=1,
)
rack.current_job_step_id = recent.to_step_id if recent else False
# current_tank_id pulls from the destination step's tank if set
rack.current_tank_id = (
recent.to_tank_id or
(recent.to_step_id.tank_id if recent and recent.to_step_id else False)
) if recent else False
rack.current_part_count = recent.qty_moved if recent else 0

View File

@@ -68,6 +68,20 @@
<field name="strips_count"/>
</group>
</group>
<group>
<group string="Tags + Capacity (Sub 12b)">
<field name="racking_state"/>
<field name="tag_ids" widget="many2many_tags"
options="{'color_field': 'color'}"/>
<field name="capacity_count"/>
</group>
<group string="Current Use"
invisible="racking_state in ('empty','out_of_service')">
<field name="current_job_step_id" readonly="1"/>
<field name="current_tank_id" readonly="1"/>
<field name="current_part_count" readonly="1"/>
</group>
</group>
<group string="Notes">
<field name="notes" nolabel="1" colspan="2"/>
</group>