**7a. NCR close gate** (fusion.plating.ncr.action_close)
Block close unless these are filled in:
• Description (what happened)
• Containment Actions (immediate response)
• Root Cause (why it happened)
• Disposition (use-as-is / rework / scrap / RTV decision)
A closed NCR without these is useless for AS9100 audits — it's
the entire point of an NCR to document what went wrong, why, and
how we responded. Empty-HTML strings like "<p><br></p>" are
detected as empty too.
**7b. CAPA close gate** (fusion.plating.capa.action_close)
Block close unless:
• Root Cause Analysis filled in
• Action Plan filled in
• Verification (date + verifier) recorded
• Effectiveness Notes filled when CAPA was marked Not Effective
AS9100 §10.2 / Nadcap require evidence of root-cause analysis,
the corrective/preventive action plan, AND that effectiveness
was verified before the loop is closed.
**8. Invoice ref defensive default** (account.move.create)
Auto-fills `ref` from the source SO's client_order_ref or
x_fc_po_number when the invoice is created with invoice_origin set
but no ref. Already populated on the SO confirm path; this catches
manually-created invoices that would otherwise miss it. Customer
AP teams reject invoices that don't quote their PO# back.
**9. Discharge sample close gate** (fusion.plating.discharge.sample.action_close)
Block close unless:
• Lab Report # set
• Results Received Date set
• At least one parameter reading on file
• Lab certificate/report attached
Without lab evidence the record fails any environmental compliance
audit — the whole point is to document the test was performed and
what the lab said.
**Simulator** (scripts/fp_e2e_workforce.py)
Adds 4 new negative tests (Test 8-11), all wrapped in savepoints:
✓ Test 8 : NCR close without RC/containment/disposition → blocked
✓ Test 9 : CAPA close without analysis/plan/verification → blocked
✓ Test 10: Discharge sample close without lab evidence → blocked
✓ Test 11: Invoice ref auto-fills from SO.client_order_ref → asserted
**Final E2E**: 52 PASS / 2 WARN / 0 FAIL out of 54 checks.
Both remaining WARNs are expected (bake-window auto-create,
first-piece gate — coating-driven, this coating doesn't trigger them).
11 negative tests in total now, every gate fires when triggered.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Following the workforce-E2E + required-fields audit, ship the first 6
high-priority gates so critical workflow + compliance fields can no
longer be left empty by accident.
**1. Invoice payment terms (account.move)**
- create() now auto-inherits `invoice_payment_term_id` from
partner.property_payment_term_id when missing
- action_post() raises UserError if still missing — accountant must
pick one before posting (prevents silent "immediate" due-date)
**2. MO facility (mrp.production)**
- action_confirm() auto-derives `x_fc_facility_id` if unset, in order:
SO override → res.company.x_fc_default_facility_id → first active
facility — then HARD GATES: raises UserError if still empty.
Without facility every downstream record (WO, batch, bath log,
cert) is missing the "where" half of the audit trail.
**3. WO facility (mrp.workorder)**
- Switched `x_fc_facility_id` from related (workcenter only) to a
proper compute that falls back to production_id.x_fc_facility_id.
Stub workcenters auto-created from process node names usually have
no facility — the MO always does (from #2 above).
**4. Thickness reading calibration_std (fp.thickness.reading)**
- `calibration_std_ref` is now `required=True` with sensible default
("NiP/Al STD SET SN 100174568"). Nadcap mandates which calibration
standard the gauge was checked against — without it the cert
data has no chain back to a metrology record.
**5. Delivery POD gate (fusion.plating.delivery)**
- action_mark_delivered() raises UserError if no `pod_id`. Driver
must capture POD on the iPad (recipient signature + photos +
notes) BEFORE marking delivered. Without POD there's no signed
receipt to back the invoice or defend a delivery dispute.
**6. Certificate spec_reference gate (fp.certificate)**
- action_issue() raises UserError if no `spec_reference`. The cert
ATTESTS to a spec — leaving it blank produces a piece of paper
that AS9100 / Nadcap auditors will (rightfully) reject.
**Simulator updated**: scripts/fp_e2e_workforce.py
- Sets net-30 on the test customer + ensures a default facility
- New PHASE 4c: 5 negative tests (one per new gate), each wrapped
in a SAVEPOINT so SQL constraint violations don't abort the txn
- Driver now creates POD on iPad BEFORE marking delivered
**Final E2E**: 48 PASS / 2 WARN / 0 FAIL out of 50 checks.
The 2 remaining WARNs (bake-window auto-create, first-piece gate)
are expected behaviour — both are coating-driven and the test
coating intentionally doesn't trigger them.
All 7 negative tests now pass:
✓ Test 1: WO start without operator → blocked
✓ Test 2: WO start on wet WO without bath/tank → blocked
✓ Test 3: MO confirm without facility → blocked
✓ Test 4: Cert issue without spec_reference → blocked
✓ Test 5: Delivery delivered without POD → blocked
✓ Test 6: Invoice post without payment terms → blocked
✓ Test 7: Thickness reading without cal std → blocked (DB NOT NULL)
Audit script (scripts/fp_required_fields_audit.py) committed too —
it's the diagnostic that surfaced these gaps and can be re-run to
catch new ones.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>