feat(plating): session 2026-05-23 deploys — F1/F7/S22/S23 + UI fixes
Consolidated commit of session work already deployed to entech and verified via the deep audit + the persona walk: S22 — Signoff gate (fp.job.step.requires_signoff was 100% unenforced, 42/42 done steps had NULL signoff_user_id). Three-piece fix: _fp_autosign_if_required (captures finisher on button_finish), _fp_check_signoff_complete (raises UserError if NULL after autosign), action_signoff (explicit supervisor pre-sign). Bypass: fp_skip_signoff_gate=True. S23 — Transition-form gate (same dormant-field shape as S22, caught preventively before recipe authors flipped requires_transition_form on). Model helpers on fp.job.step.move + controller gate in move_controller (parts commit) + pre-reject in rack commit. F7 — Chatter standardization: _fp_create_qc_check_if_needed, _fp_fire_notification, _fp_create_delivery silent failures now also post to job chatter instead of only logging to file. UI fixes: - Critical Rule 20 documented + applied: OWL templates only expose Math as a global. Calling String(d) inside t-on-click throws 'v2 is not a function'. Fixed pin_pad.xml (string array instead of number array with String() coercion). Also swept parseInt/ parseFloat in recipe_tree_editor + simple_recipe_editor. - Notes panel HTML escape fix: chatter messages off /fp/workspace/load were rendered via t-out, escaping the HTML. Wrap with markup() in job_workspace.js refresh() before assigning to state. Versions: fusion_plating 19.0.20.8.0 → 19.0.20.9.0 fusion_plating_jobs 19.0.10.20.0 → 19.0.10.23.0 fusion_plating_shopfloor 19.0.30.2.0 → 19.0.30.5.0 All deployed to entech (LXC 111) and verified live. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -203,6 +203,13 @@ class FpTabletMoveController(http.Controller):
|
||||
for prompt_id, value in (prompt_values or {}).items():
|
||||
self._capture_prompt_value(move, int(prompt_id), value)
|
||||
|
||||
# S23 — required transition-input gate. Runs AFTER value capture
|
||||
# so the operator gets credit for whatever they filled in. Raises
|
||||
# UserError if to_step.requires_transition_form=True and any
|
||||
# required transition_input prompt has no value. Rollback unwinds
|
||||
# the move + value rows. Manager bypass: fp_skip_transition_form.
|
||||
move._fp_check_transition_inputs_complete()
|
||||
|
||||
# Advance qty_at_step counters
|
||||
to_step.qty_at_step_start = (to_step.qty_at_step_start or 0) + qty
|
||||
from_step.qty_at_step_finish = (from_step.qty_at_step_finish or 0) + qty
|
||||
@@ -298,6 +305,42 @@ class FpTabletMoveController(http.Controller):
|
||||
rack = Rack.browse(rack_id)
|
||||
to_step = Step.browse(to_step_id)
|
||||
|
||||
# S23 — pre-check: rack moves don't capture transition prompts
|
||||
# (no per-move dialog), so if to_step.requires_transition_form
|
||||
# we must reject up-front and force the operator through Move
|
||||
# Parts (which has the form UI). Without this check, rack moves
|
||||
# silently bypass the audit gate that Move Parts enforces.
|
||||
if (to_step.requires_transition_form
|
||||
and not request.env.context.get('fp_skip_transition_form')):
|
||||
# Use the same model helper for consistency — build a dummy
|
||||
# in-memory move to compute "missing" set, then surface a
|
||||
# clear message that points operators at the right tool.
|
||||
recipe_node = to_step.recipe_node_id
|
||||
required_prompts = recipe_node.input_ids if recipe_node else (
|
||||
request.env['fusion.plating.process.node.input']
|
||||
)
|
||||
if 'kind' in required_prompts._fields:
|
||||
required_prompts = required_prompts.filtered(
|
||||
lambda i: i.kind == 'transition_input')
|
||||
required_prompts = required_prompts.filtered(
|
||||
lambda i: i.required)
|
||||
if required_prompts:
|
||||
names = ', '.join(
|
||||
'"%s"' % (p.name or '').strip()
|
||||
for p in required_prompts
|
||||
)
|
||||
raise UserError(_(
|
||||
'Step "%(step)s" requires a transition form '
|
||||
'(%(n)s required prompt(s): %(names)s). '
|
||||
'Use Move Parts for one batch at a time so the form '
|
||||
'can be filled in, or have a manager override with '
|
||||
'context flag fp_skip_transition_form=True.'
|
||||
) % {
|
||||
'step': to_step.name,
|
||||
'n': len(required_prompts),
|
||||
'names': names,
|
||||
})
|
||||
|
||||
moves = []
|
||||
for batch in Step.search([('rack_id', '=', rack.id)]):
|
||||
qty = (batch.qty_done or 0) - (batch.qty_scrapped or 0)
|
||||
|
||||
Reference in New Issue
Block a user