feat(jobs): step details quick-look modal for backend managers
Click a step's name in the embedded job-form list → opens a read-only
modal with everything a manager wants in one scroll: equipment,
schedule, master collect-measurements banner, operator instructions
(rich-text from recipe_node.description), measurement prompts list,
and values recorded so far.
Implementation: separate read-only form view bound to the embedded
field via context={'form_view_ref': '...'}. The standalone editable
form view stays registered for the Job Steps menu, so direct
navigation still loads the editable variant.
Three new computed/related fields on fp.job.step:
- quick_look_instructions (Html, related from recipe_node_id.description)
- quick_look_prompt_ids (filtered+sorted recipe_node.input_ids, step_input only)
- quick_look_recorded_value_ids (search across moves: input_value rows
whose move.from_step_id == self.id)
Plus a small action_open_full_form method that escapes from the modal
to the editable form when the manager actually needs to edit.
Edge cases:
- No recipe_node_id → instructions panel shows empty-state hint
- collect_measurements=False → amber banner: "Master switch off — no
values will be collected at runtime"
- Multiple moves on same step → values list shows all, newest first
Spec: docs/superpowers/specs/2026-04-30-step-details-modal-design.md.
Verified on entech: step "11. Hard Anodize Type III" populates with
516 chars instructions + 7 prompts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -872,3 +872,64 @@ class FpJobStep(models.Model):
|
||||
step.duration_running_minutes = closed + running
|
||||
else:
|
||||
step.duration_running_minutes = step.duration_actual or 0.0
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Sub 12d — Step Details Quick-Look modal
|
||||
# ------------------------------------------------------------------
|
||||
# Three computed/related fields that power the read-only manager
|
||||
# quick-look modal. The modal is bound via context= on the parent
|
||||
# job form's <field name="step_ids"/> — no TransientModel needed.
|
||||
|
||||
quick_look_instructions = fields.Html(
|
||||
string='Operator Instructions',
|
||||
related='recipe_node_id.description',
|
||||
readonly=True,
|
||||
)
|
||||
quick_look_collect_master = fields.Boolean(
|
||||
string='Collect Measurements',
|
||||
related='recipe_node_id.collect_measurements',
|
||||
readonly=True,
|
||||
)
|
||||
quick_look_prompt_ids = fields.Many2many(
|
||||
'fusion.plating.process.node.input',
|
||||
string='Prompts',
|
||||
compute='_compute_quick_look_prompt_ids',
|
||||
)
|
||||
quick_look_recorded_value_ids = fields.Many2many(
|
||||
'fp.job.step.move.input.value',
|
||||
string='Recorded Values',
|
||||
compute='_compute_quick_look_recorded_value_ids',
|
||||
)
|
||||
|
||||
@api.depends('recipe_node_id', 'recipe_node_id.input_ids')
|
||||
def _compute_quick_look_prompt_ids(self):
|
||||
for step in self:
|
||||
node = step.recipe_node_id
|
||||
if node:
|
||||
step.quick_look_prompt_ids = node.input_ids.filtered(
|
||||
lambda i: (i.kind or 'step_input') == 'step_input'
|
||||
).sorted('sequence')
|
||||
else:
|
||||
step.quick_look_prompt_ids = False
|
||||
|
||||
def _compute_quick_look_recorded_value_ids(self):
|
||||
Value = self.env['fp.job.step.move.input.value']
|
||||
for step in self:
|
||||
if not step.id:
|
||||
step.quick_look_recorded_value_ids = False
|
||||
continue
|
||||
step.quick_look_recorded_value_ids = Value.search([
|
||||
('move_id.from_step_id', '=', step.id),
|
||||
], order='create_date desc')
|
||||
|
||||
def action_open_full_form(self):
|
||||
"""From the quick-look modal, escape to the full editable form."""
|
||||
self.ensure_one()
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'fp.job.step',
|
||||
'res_id': self.id,
|
||||
'view_mode': 'form',
|
||||
'target': 'current',
|
||||
'name': self.name,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user