feat(sub12b): plant overview Racks pane (Task 16)
Controller: extend /fp/shopfloor/plant_overview return payload to include 'racks' array (filtered to loaded/in_use/awaiting_unrack states). Each entry has tag chips, part count, current node breadcrumb, current step + tank code, and a precomputed next_step_id (next sequence in the job's recipe — operator overrides at runtime in the Move Rack dialog). JS: state.racks populated from payload. New openMoveRackDialog() method spawns FpMoveRackDialog. Notification when rack has no successor (last step of job). XML: top section above the existing work-centre columns. Renders rack rows with tags, part count, breadcrumb, and primary MOVE RACK button per row. Visible only when state.racks.length > 0. SCSS: minimal styling for the racks pane (extends move_dialogs.scss to keep all Sub 12b styles in one file). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1274,11 +1274,56 @@ class FpShopfloorController(http.Controller):
|
||||
'cards': cards_by_wc[0],
|
||||
})
|
||||
|
||||
# Sub 12b — Racks pane payload alongside the existing parts cards.
|
||||
# Filters to racks currently in active racking-state. Each entry
|
||||
# has the data the OWL plant overview's Racks pane renders:
|
||||
# tag chips, current node breadcrumb, part count, and the
|
||||
# rack-level MOVE RACK button target.
|
||||
rack_domain = [
|
||||
('racking_state', 'in', ('loaded', 'in_use', 'awaiting_unrack')),
|
||||
('active', '=', True),
|
||||
]
|
||||
if facility_id:
|
||||
rack_domain.append(('facility_id', '=', int(facility_id)))
|
||||
racks_payload = []
|
||||
for r in env['fusion.plating.rack'].search(rack_domain):
|
||||
cur_step = r.current_job_step_id
|
||||
racks_payload.append({
|
||||
'id': r.id,
|
||||
'name': r.name,
|
||||
'racking_state': r.racking_state,
|
||||
'tag_ids': [
|
||||
{'id': t.id, 'name': t.name, 'color': t.color}
|
||||
for t in r.tag_ids
|
||||
],
|
||||
'current_part_count': r.current_part_count,
|
||||
'current_node_name': cur_step.name if cur_step else '',
|
||||
'current_tank_code': (
|
||||
r.current_tank_id.code if r.current_tank_id else ''
|
||||
),
|
||||
'current_step_id': cur_step.id if cur_step else False,
|
||||
# Default destination: next sibling step in the recipe
|
||||
# sequence. Falls back to current_step_id if no successor
|
||||
# (operator can pick a different one in the dialog).
|
||||
'next_step_id': self._fp_next_step_id(cur_step) if cur_step else False,
|
||||
})
|
||||
|
||||
return {
|
||||
'facility_name': facility_name,
|
||||
'columns': columns,
|
||||
'racks': racks_payload,
|
||||
}
|
||||
|
||||
def _fp_next_step_id(self, step):
|
||||
"""Return the id of the next step in this job's recipe sequence,
|
||||
or False if `step` is the last."""
|
||||
if not step or not step.job_id:
|
||||
return False
|
||||
successors = step.job_id.step_ids.filtered(
|
||||
lambda s: s.sequence > step.sequence
|
||||
).sorted('sequence')
|
||||
return successors[0].id if successors else False
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Urgency scoring (v19.0.24.8.0)
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user