feat(jobs): Record Inputs OWL Dialog (v4) — replaces list-as-cards hack

Scrapped the v2/v3 form-view + list-as-cards CSS approach after
extensive failure to make Odoo's editable list look like cards.
Built a proper OWL Dialog component instead, mirroring the pattern
used by fusion_plating_shopfloor's move_parts_dialog.js.

What changed
============
* New OWL Dialog: fp_record_inputs_dialog.js
  - Loads step + prompt definitions via /fp/record_inputs/load
  - Renders each prompt as a semantic <div class="o_fp_ri_card">
  - Per-row widget chosen by input_type:
      numeric/temperature/thickness/time_seconds/ph -> number input
      boolean/pass_fail   -> custom CSS toggle (clearer than Bootstrap)
      date                -> datetime-local input
      photo               -> file picker w/ preview + clear
      multi_point_thickness -> 5-cell grid + live average
      bath_chemistry_panel  -> pH/Conc/Temp/Bath grid
      selection           -> dropdown sourced from selection_options
      text/signature/...  -> text input
  - Live in-range hint for numeric prompts
      ("in range" / "below target" / "above target")
  - Save validates ad-hoc rows have a Prompt label
  - Save dispatches the next_action returned by the wizard model
    (e.g. action_finish_and_advance for the Finish & Next flow)

* New XML template: fp_record_inputs_dialog.xml
  Full DOM control. No fighting Odoo's list view, no class-stripping
  bugs from canUseFormatter, no read-mode-vs-edit-mode CSS dance.

* New SCSS: fp_record_inputs_dialog.scss
  - Dark mode aware (compile-time @if $o-webclient-color-scheme==dark)
  - Pure semantic selectors (.o_fp_ri_card, .o_fp_ri_input, etc.)
  - 14 surface tokens with light/dark hex pairs
  - Tablet polish via @media (max-width: 768px)
  - Custom toggle widget (no <input type="checkbox"> hidden trick)

* New controller: controllers/record_inputs.py
  - /fp/record_inputs/load: returns step + prompts payload
  - /fp/record_inputs/commit: creates a wizard, populates lines,
    calls action_commit (reuses existing audit-trail / synthetic
    move semantics — no commit logic duplicated)

* fp_job_step.py wired to dispatch the new action
  - _fp_open_input_wizard returns
    { type: 'ir.actions.client', tag: 'fp_record_inputs_dialog' }
  - action_open_input_wizard same
  - Contract-review redirect gate preserved (Sub 4 work intact)

* Manifest registers JS/XML/SCSS in BOTH backend + dark bundles
  per the dark-mode pattern in CLAUDE.md.

What was kept
=============
* fp.job.step.input.wizard TransientModel — UNCHANGED. The new
  controller's commit endpoint creates a wizard record and calls
  action_commit() on it, so all the audit-trail / synthetic-move
  / chatter logic stays in Python where it belongs.
* v2 + v3 form views still exist in the XML file. If the OWL
  dialog ever fails, switch action_open_input_wizard back to
  ir.actions.act_window with view_id=v2 or v3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-03 22:17:30 -04:00
parent 328599d539
commit d53fd53b80
7 changed files with 1186 additions and 36 deletions

View File

@@ -481,26 +481,24 @@ class FpJobStep(models.Model):
return already == 0
def _fp_open_input_wizard(self, advance_after=False):
"""Open the simplified Record Inputs dialog. When advance_after
is True, the wizard's Save button finishes the step and starts
the next one as a single atomic flow."""
"""Open the Record Inputs OWL dialog (Sub 12e v4).
Replaces the form-view-based wizard with a custom OWL Dialog
component (fp_record_inputs_dialog.js). The dialog renders
each prompt as a proper card with semantic HTML — no more
list-cell-as-card CSS hacks.
When advance_after is True, the dialog's Save button commits
values then dispatches the result of action_finish_and_advance
so the step finishes + auto-starts the next step in one flow.
"""
self.ensure_one()
view = self.env.ref(
'fusion_plating_jobs.view_fp_job_step_input_wizard_form_v3'
)
return {
'type': 'ir.actions.act_window',
'res_model': 'fp.job.step.input.wizard',
'view_mode': 'form',
'view_id': view.id,
'views': [(view.id, 'form')],
'target': 'new',
'name': _('Record Inputs — %s') % self.name,
'context': {
**dict(self.env.context),
'default_step_id': self.id,
'active_id': self.id,
'fp_advance_after_save': advance_after,
'type': 'ir.actions.client',
'tag': 'fp_record_inputs_dialog',
'params': {
'step_id': self.id,
'advance_after': bool(advance_after),
},
}
@@ -916,24 +914,23 @@ class FpJobStep(models.Model):
}
def action_open_input_wizard(self):
"""Open the Input Recording wizard for this step.
"""Open the Record Inputs OWL dialog from the per-row Record
button on the job form.
Contract-review steps redirect to the QA-005 form (same gate as
action_finish_and_advance) so the per-row "Record" button stays
consistent with "Finish & Next".
action_finish_and_advance) so the per-row Record button stays
consistent with Finish & Next.
"""
self.ensure_one()
cr_action = self._fp_contract_review_redirect()
if cr_action:
return cr_action
return {
'type': 'ir.actions.act_window',
'res_model': 'fp.job.step.input.wizard',
'view_mode': 'form',
'target': 'new',
'name': _('Record Inputs — %s') % self.name,
'context': {
'default_step_id': self.id,
'type': 'ir.actions.client',
'tag': 'fp_record_inputs_dialog',
'params': {
'step_id': self.id,
'advance_after': False,
},
}