This commit is contained in:
gsinghpal
2026-04-28 19:39:37 -04:00
parent 2d42b33d68
commit 13e300d90e
103 changed files with 4959 additions and 331 deletions

View File

@@ -813,6 +813,120 @@ UNION ALL SELECT 'check', count(*) FROM fusion_plating_quality_check;
---
## Contract Review — Policy B (shipped 2026-04-28)
The `fp.contract.review` model (QA-005) was originally shipped as
"always optional, never blocks anything" (Sub 4). Audit 2026-04-28
revealed three integration holes:
1. The **Simple Recipe Editor library** had no Contract Review step
template, so authors couldn't drop QA-005 into a recipe at all.
2. Adding a node literally named "Contract Review" to a recipe did
**nothing** — no auto-create, no operator routing, no gate.
3. The pre-Sub-11 `contract_review_user_ids` approver list on
`fp.process.node` was dead — `mrp.workorder.button_finish` used to
gate on it, but `fp.job.step` never picked up the gate.
**Policy B (chosen 2026-04-28)** — Contract Review is REQUIRED on a
per-customer basis (`partner.x_fc_contract_review_required`), soft
elsewhere. Recipe-side enforcement closes the post-Sub-11 hole.
### What's wired
| Trigger | Behaviour |
|---|---|
| `fp.step.template.default_kind = 'contract_review'` | New kind in the Simple Editor library. Auto-seeds 3 inputs: Reviewer Initials / Date Reviewed / QA-005 Approved (pass_fail). |
| Library seeders (`_STARTER_KIND_BY_NAME`, `_seed_minimal_library`) | "Contract Review" is the FIRST entry in the minimal library. Authors drag-drop it into recipes from the Simple Editor sidebar. |
| `fp.job.step.button_start` on a Contract Review step | Auto-creates `fp.contract.review` for the linked part if missing, returns an act_window pointing at the QA-005 form. Operator gets routed straight to the form without hunting for the smart button on the part. |
| `fp.job.step.button_finish` on a Contract Review step | Blocks unless `fp.contract.review.state == 'complete'` AND current user is on `recipe.contract_review_user_ids` (when configured). Manager bypass: `fp_skip_contract_review_gate=True` in context. |
| Step detection | `_fp_is_contract_review_step()` matches case-insensitive name == "contract review" / "qa-005" OR `recipe_node_id.source_template_id.default_kind == 'contract_review'` (simple-editor library entry). |
### What stays optional (NOT enforced)
- Customers without `x_fc_contract_review_required=True` get the soft
banner only — no step-level block. The customer-flag gate is the
ONLY enforcement trigger.
- Adding a Contract Review node to a recipe for a customer that
doesn't require it is purely documentary; nothing fires.
### Why the part-side banner stays
The part-form banner ("New part created. Please complete the Contract
Review (QA-005) if applicable.") is independent of the recipe step.
It nudges QA before any job is started — an early-detection mechanism
distinct from the in-flight step gate. Both can fire on the same part
(banner first, then step gate later); one resolution clears both.
### Manager bypass examples
```python
# Skip the step-level gate from a privileged caller (script / shell)
step.with_context(fp_skip_contract_review_gate=True).button_finish()
```
### Files touched
- `fusion_plating/models/fp_step_template.py` — added `contract_review`
kind + 3 default inputs.
- `fusion_plating/models/fp_process_node.py` — **also added
`contract_review` to `default_kind` Selection here.** Easy to miss:
the node and the template have separate Selection fields and they
must stay in lockstep.
- `fusion_plating/__init__.py` — added "Contract Review" / "QA-005" to
`_STARTER_KIND_BY_NAME` + first entry in `_seed_minimal_library`,
exposed `fp_resolve_step_kind()` helper.
- `fusion_plating_jobs/models/fp_job_step.py` — added
`_fp_is_contract_review_step`, `_fp_resolve_contract_review_part`,
`_fp_open_contract_review`, `_fp_check_contract_review_complete`;
hooked into `button_start` (auto-open form) + `button_finish`
(gate). Sub 11's `contract_review_user_ids` field on
`fp.process.node` is now wired again.
### Bugs caught during the persona walkthrough (2026-04-28, fixed 12.4.1)
A scripted "brand-new estimator builds a recipe from scratch" walk
(`/tmp/fp_recipe_walkthrough.py` on entech) surfaced 7 real gaps; all
fixed in 19.0.12.4.1. The walk is preserved as a smoke test —
re-runnable on any DB to verify the library is healthy.
| # | Bug | Fix |
|---|---|---|
| 1 | `_seed_step_library_if_empty` skips when the library is non-empty, so existing DBs got NO Contract Review template after Policy B shipped. | Migration `19.0.12.4.1/post-migrate.py` — backfills the template if missing. |
| 2 | `fp.process.node.default_kind` Selection didn't include `contract_review`, so dropping the template into a recipe blew up with `ValueError`. The kind is on TWO models (template + node) and they drifted. | Added `contract_review` to the node's Selection too. |
| 3 | The library had only `racking` populated as a kind (1/16). 12 of 14 templates landed with `default_kind = NULL` because the original seeder used a brittle case-sensitive lookup. | Migration backfills `default_kind` via the new `fp_resolve_step_kind()` helper. |
| 4 | `_STARTER_KIND_BY_NAME` lookup was hyphen / -ing / case sensitive — "E-Nickel Plating" didn't match `'e-nickel plate'`, "DeRacking" didn't match `'de-racking'`, "Ready For Masking" didn't map to `gating`. | Expanded the lookup with 30+ alias entries + a "Ready for X → gating" prefix rule in `fp_resolve_step_kind()`. |
| 5 | The library was missing the canonical names a fresh estimator would type from scratch (Soak Clean, Rinse, Etch, Acid Dip, Desmut, Zincate, Drying, Inspection, Shipping, Water Break Test). The ENP-ALUM-BASIC seed included only the names from that one recipe. | Migration adds 13 canonical missing entries (Soak Clean, Electroclean, Rinse, Etch, Desmut, Zincate, Acid Dip, HCl Activation, Water Break Test, Drying, Inspection, Final Inspection, Shipping, Contract Review). |
| 6 | `_seed_minimal_library` (the fresh-DB fallback path) had only 15 entries, didn't include Contract Review, and used English names that don't match the 30+ aliases. | Added "Contract Review" as the first entry. Library is now bigger, but `fp_resolve_step_kind()` is the canonical way authors will get coverage. |
| 7 | `DEFAULT_INPUTS_BY_KIND` in `fp_step_template.py` still had free-text `target_unit` values (`'HH:MM'`, `'°F'`, `'sec'`, `'in'`, `'each'`) left over from before the 19.0.12.1.0 UoM cleanup. `action_seed_default_inputs()` blew up with `Wrong value for target_unit: 'HH:MM'` when called against the new Selection-typed column. | Translated to selection keys: `'sec' → 's'`, `'°F' → 'f'`, `'in' → 'in'`, `'each' → 'each'`, `'min' → 'min'`. Format-only strings (`'HH:MM'`) dropped — they're not units. |
The walkthrough script is checked into context at
`/tmp/fp_recipe_walkthrough.py` (rerun via odoo shell) and is the
recommended smoke test before any future library / step-template
changes ship.
---
## Record Inputs Wizard — ad-hoc rows (shipped 2026-04-28)
The backend `Record Inputs` button on the job-form Steps tab opened
an empty wizard when the recipe step had no `step_input` prompts
authored — operator had no way to log anything. Fixed by:
- Making `node_input_id` optional on
`fp.job.step.input.wizard.line`. Authored prompts still show
pre-filled + readonly; ad-hoc rows are fully editable (operator types
the prompt label + value).
- View now shows a helpful empty-state hint and an `Add a line` button.
- Commit step requires every ad-hoc row to have a Prompt label, then
serialises it into `value_text` of the resulting
`fp.job.step.move.input.value` (format `Prompt: value [unit]`) so
the chronological CoC report still renders the captured data.
Files: `fusion_plating_jobs/wizards/fp_job_step_input_wizard.py` +
`fp_job_step_input_wizard_views.xml`.
---
## Battle Tests — Real-World Operator Scenario Coverage
Persona-driven shop-floor scenarios that surfaced bugs / workflow holes. Every scenario has: