Commit Graph

46 Commits

Author SHA1 Message Date
gsinghpal
c9eb61ee0c feat(plating): hide quality smart buttons at zero + order-scope NCR/CAPA counts
On the sale.order and fp.job forms, the Holds/Checks/NCRs/CAPAs/RMAs quality
smart buttons (and the SO WO button) now hide when their count is 0, so the row
shows only quality work that exists. NCR and CAPA counts are re-scoped from
customer-wide to order/job via a shared _fp_quality_ncr_ids() helper (NCRs
reached through the order's RMAs + the order/job's holds), so each badge and the
list its button opens always agree. Also aligned the job RMA button's list
domain to its (already SO-scoped) count.

Reverts the Sub-12 Phase D "always-visible (zero is OK)" choice back to the
module's documented hide-at-zero convention.

- fusion_plating_quality 19.0.8.1.0 -> 19.0.8.2.0
- fusion_plating_jobs    19.0.12.4.0 -> 19.0.12.5.0

Deployed + verified on entech (badge == helper across sampled SOs/jobs).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 20:36:54 -04:00
gsinghpal
cecc699a70 fix(plating): trim default SO order-line columns to the plating set
Default-hide six order-line columns that aren't part of the plating
view (Product/product_template_id, Description Template, Specification,
Delivered Qty, Invoiced Qty, Taxes) by flipping them to optional="hide".
They stay available via the optional-columns toggle. Default-visible set
is now Customer-Facing, Part, Process/Recipe, Thickness, Serial, Job #,
Effective Deadline, Qty, Unit, Unit Price, Amount — for both quotations
and sales orders.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 00:42:20 -04:00
gsinghpal
5872583fbb fix(quality_dashboard): correct kanban xmlids per battle test (Task 9 fix)
Plan-time xmlids were wrong — entech battle test caught all 5
non-cert kanban xmlids missing. Real xmlids (queried via
ir.model.data on entech):
  hold:  action_fp_quality_hold     (was action_fusion_plating_quality_hold)
  ncr:   action_fp_ncr              (was action_fusion_plating_ncr)
  rma:   action_fp_rma              (was action_fusion_plating_rma)
  capa:  action_fp_capa             (was action_fusion_plating_capa)
  check: action_fp_quality_check    (was action_fusion_plating_quality_check)
cert stays unchanged — action_fp_certificate was already correct.

After fix: battle test ALL PASS — 6 sections in canonical order,
all xmlids resolve, 3 banner items pulled from real entech data
(5 draft certs, 3 of them overdue past 24h).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 12:28:52 -04:00
gsinghpal
c8db3915ea feat(quality_dashboard): manifest bump + battle test (Tasks 7-8)
Version 19.0.7.0.0 → 19.0.8.0.0 (triggers asset cache invalidation
on -u so the new template + SCSS load cleanly).

Battle test script: 6-check entech smoke. Validates snapshot shape,
canonical section order, required section keys, open_kanban_xmlid
resolves to act_window, banner item shape when items exist. Summary
prints per-section counts so you can eyeball the entech state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 12:25:31 -04:00
gsinghpal
547e7d66a9 feat(quality_dashboard): rewrite OWL component + template + SCSS (Task 6)
JS: single FpQualityDashboard component + BannerCard / BannerItem /
SectionCard / SectionRow sibling sub-components in the same file.
Fetches /fp/quality/dashboard/snapshot, 60s poll, deep-link
?tab=certificates scrolls to section-cert via scrollIntoView.

XML: outer wrapper + banner + 6 sections (t-foreach over
state.snapshot.sections). Each section has id='section-<type>' so
the deep-link target works. SectionRow has overdue-conditional
class for red subtitle highlight.

SCSS: local tokens for urgent/good/section-head with light+dark via
$o-webclient-color-scheme branch. 135deg gradients matching the
plant kanban polish. Mobile breakpoint at 900px collapses banner
grid to 1 col and stacks row Open button.

OLD TABS array, selectTab, openTab, totalOpen, totalOverdue all
deleted. Old template's tab tiles + per-tab panels deleted. Existing
per-model kanbans untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 12:24:52 -04:00
gsinghpal
bfeca0ac32 test(quality_dashboard): defensive guard tests (Task 5)
Covers: missing-field critical-customer check returns empty without
crashing; computed_at is a valid ISO timestamp; every section ships
a non-empty open_kanban_xmlid in module.xmlid format.

(missing-model test from the plan omitted — patching env.__contains__
was unsafe; the in-self.env guard is already exercised by Tasks 2-4
in production behavior. The other 3 defensive tests still cover the
missing-field path, which is the more common scenario.)

Phase 1 backend complete.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 12:22:25 -04:00
gsinghpal
40d563801a feat(quality_dashboard): banner with overdue + critical (Task 4)
_fetch_banner_candidates collects (overdue) OR (critical-customer +
open) records per type. _critical_customer_ids reuses partner.x_fc_rush
and partner.x_fc_vip flags when defined (gracefully no-ops when
absent). _critical_badge returns RUSH/VIP/AEROSPACE/AS9100 label
when the banner reason is critical-customer (no badge when overdue).
_build_banner ranks: overdue first by oldest, then critical-customer
by oldest, takes top 6, reports total_matching.

build() now collects banner candidates from every section in one
pass + invokes _build_banner once.

Tests cover overdue hold pickup, 6-cap with overflow count, and
all_clear when DB is empty.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 12:22:01 -04:00
gsinghpal
e271908109 feat(quality_dashboard): populate section items (Task 3)
_fetch_section_items pulls top-5 open records per type, ranked
overdue-first by oldest create_date. _build_item shapes each row
with id/name/customer/subtitle/urgency/open_action. _resolve_partner
defensively walks partner_id -> job_id.partner_id -> ncr_id.partner_id
per type. _build_subtitle generates the human-readable second line.

Tests cover empty list, 5-cap on 8-record set, and required item
keys (id/name/customer/subtitle/urgency/open_action).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 12:20:50 -04:00
gsinghpal
72f75fe754 feat(quality_dashboard): snapshot endpoint scaffold (Task 2)
Replaces /counts with /snapshot. Helper class FpQualityDashboardSnapshot
returns response with correct shape — banner placeholder + per-type
sections with open/overdue counts (reuses old counts endpoint
thresholds). Items + critical-customer banner come in Tasks 3-5.

Per CLAUDE.md Rule 13m, Model.sudo() on cross-module reads. Per
Rule 24 the in-self.env check guards missing-model paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 12:19:48 -04:00
gsinghpal
6cb352629a test(quality_dashboard): scaffold + shape tests (Task 1)
Tests for empty-DB all-clear, canonical section order, and required
keys on each section. All fail until Task 2 lands the snapshot helper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 12:19:06 -04:00
gsinghpal
5a039ae369 test(bt): post-shop state machine end-to-end smoke (Task 22)
10-step battle test covering: auto-advance on last step finish,
kanban placement, QM activity, ACL guard, cert issue advance,
activity auto-resolve, cert void regress, re-issue, manual ship.

Tolerant of partial state — branches around the awaiting_cert path
when partner doesn't require certs (uses awaiting_ship path instead),
SKIPs subsequent steps when prerequisites fail, rolls back at end so
the DB stays clean.

Run on entech via odoo-shell after deploy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 09:54:53 -04:00
gsinghpal
c00831a72a feat(quality_dashboard): sixth 'Certificates' tab (Tasks 14-16)
Counts endpoint: certificates block — open=draft, overdue=draft+>24h.
Falls back to {open:0, overdue:0} when fp.certificate isn't installed.

JS: TABS array gains the 6th entry. Existing data-driven OWL template
auto-renders both the header tile and the body panel. Tab opens the
fp.certificate kanban grouped by state, filtered to draft by default.

Deep-link: setup() reads action.context.params.tab. The
cert_awaiting_issuance notification email links to
/odoo/action-fp_quality_dashboard?tab=certificates and lands the QM
on the right tab automatically.

Template: 'Open across all 5' → 'Open across all <tabs.length>' so
it stays correct if more tabs are added later.

Manifest: fusion_plating_quality 19.0.6.6.6 → 19.0.7.0.0
(fusion_plating_certificates already in depends — no change needed).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 09:49:10 -04:00
gsinghpal
b06d28e7f6 feat(jobs+shopfloor): live-step priority chain + done-job filter
Fix the Shop Floor plant kanban so cards land in the right column:
- fp.job._compute_active_step_id walks priority chain
  (in_progress > paused > ready > pending), not just in_progress
- fp.job._compute_card_state edge case respects job.state='done'
  (no more bogus 'contract_review' label on done jobs)
- fp.job.step._compute_area_kind reads kind.area_kind directly;
  legacy _STEP_KIND_TO_AREA dict removed (50+ lines deleted)
- /fp/landing/plant_kanban filters out done/cancelled jobs from
  the live board

Migration 19.0.10.25.0 backfills template metadata (codes,
descriptions, icons, kind_id) on 30 unfinished library templates
and repoints recipe nodes for 6 unambiguous name patterns
(Blasting -> blast, Ready For X -> gating, De-Masking -> demask,
Scheduling -> gating, Nickel Strip -> wet_process,
Pre-Meas/Check Sulfamate -> inspect).

Battle test bt_s24_between_steps.py covers between-step routing,
paused step lifecycle, and done-job board filter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 17:06:53 -04:00
gsinghpal
7bcbcb4008 fix(plating-perms): deploy-time cascade fixes from entech I3
5 fixes discovered during the live deploy to entech LXC 111:

1. pre-migrate.py to rename old configurator's 'Shop Manager' group BEFORE
   new core 'Shop Manager v2' XML loads (cross-module name collision on
   res_groups_name_uniq).

2. res_company_views.xml: dropped ref() inside <field domain=> attribute
   (Odoo 19 view validator interprets it as a field name).

3. sale_order_views.xml: replaced 3 separate xpaths for amount_total /
   amount_untaxed / amount_tax with a single xpath on tax_totals widget
   (Odoo 19 sale.view_order_form uses one widget instead of separate fields).

4. fp_cert_security.xml: certificate_type field, not cert_type. FAIR is a
   separate model so the rule only restricts cert_type='nadcap_cert' now.

5. fp_certificate_views.xml + fp_capa_views.xml + fp_customer_spec_views.xml:
   stripped user_has_groups() from invisible= / readonly= attrs (Odoo 19
   view validator interprets as field name). Model-layer ACLs and ir.rules
   already enforce the same restrictions.

Also fixed res.groups.users -> user_ids in fp_migration.py (Odoo 19 rename,
caught when manually invoking _fp_notify_owners post-deploy).

CLAUDE.md updated with 4 new rules (13e cross-module name collisions,
13f ref() in domain, 13g tax_totals widget, 13h user_has_groups in attrs).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 09:07:13 -04:00
gsinghpal
de3ec7d97a feat(plating-sec): SO confirm gate + fix _administrator typo + Python sweep
Phase G of permissions overhaul.

G2: sale.order.action_confirm now requires group_fp_sales_manager
(spec Section 2.B). Sales Reps can save drafts but cannot move SOs
to 'sale' state. UserError raised with clear message if attempted.

G3: Fixed audit-finding-11 typo bug in 2 files. The original code
checked has_group('fusion_plating.group_fusion_plating_administrator'),
an xmlid that has NEVER existed - so the gate always returned False
and only the Manager-side check actually fired. Fixed both:
  - fusion_plating_invoicing/models/res_partner.py:34
  - fusion_plating_configurator/wizard/fp_direct_order_wizard.py:467
Both now check has_group('fusion_plating.group_fp_manager') which
transitively includes Owner via implied_ids.

G4: Swept all Python has_group() calls to reference new group xmlids.
Backward-compat keeps old refs working today (Phase A's implied_ids),
but the sweep ensures correctness after the 30-day rollback window
deletes old groups. Replacements:
  group_fusion_plating_operator    -> group_fp_technician
  group_fusion_plating_supervisor  -> group_fp_shop_manager_v2
  group_fusion_plating_manager     -> group_fp_manager
  group_fusion_plating_admin       -> group_fp_owner
  group_fusion_plating_cgp_officer -> group_fp_quality_manager
  group_fusion_plating_cgp_designated_official -> group_fp_owner
  group_fp_estimator               -> group_fp_sales_rep
  group_fp_accounting              -> group_fp_manager
  group_fp_receiving               -> group_fp_shop_manager_v2
  group_fp_shop_manager (legacy)   -> group_fp_manager

G1: test_sales_manager_gate.py covers the new confirm gate (SR
blocked, SMg allowed, Manager allowed via diamond implication).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 02:11:35 -04:00
gsinghpal
830b29ce49 feat(plating-landing): role-based dispatch resolver + picklist expansion
Phase E of permissions overhaul. The landing resolver now dispatches
based on the user's highest role (per spec Section 3):

  Owner          -> Manager Desk
  Quality Mgr    -> Quality Dashboard
  Manager        -> Manager Desk
  Sales Manager  -> Sale Orders
  Shop Manager   -> Plant Kanban (v2 layout) or Workstation (legacy)
  Sales Rep      -> Quotations
  Technician     -> Plant Kanban / Workstation

User override (x_fc_plating_landing_action_id) still wins; company
default and hardcoded Sale Orders are fallbacks. Layout-flag-aware via
ir.config_parameter['fusion_plating_shopfloor.layout'] (v2 vs legacy).

x_fc_pickable_landing field added to BOTH ir.actions.act_window AND
ir.actions.client (Manager Desk / Plant Kanban / Quality Dashboard
are client actions). Resolver helper polymorphically calls
_render_resolved() on either model.

Tagged 3 of 4 new actions pickable: Manager Desk, Plant Kanban,
Quality Dashboard. (action_fp_shopfloor_landing doesn't exist as an
XML record — it's a JS component name only; legacy layout falls
through to company default gracefully via raise_if_not_found=False.)

Tightened picklist domain to filter by user accessibility (Technician
no longer sees Manager Desk in the dropdown). New compute field
res.users.accessible_landing_action_ids runs check_access_rights on
each pickable action.

Tests in fusion_plating/tests/test_landing_resolver.py.

CLAUDE.md updated with two durable rules:
  - x_fc_pickable_landing lives on BOTH act_window and actions.client
  - Role-based dispatch precedence and helper API

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:56:37 -04:00
gsinghpal
269f9984ef feat(plating-views): Layer 3 — field/button gates per role
Phase D Task D5 of permissions overhaul. Adds explicit groups= to
form-level elements so non-matching roles don't even SEE the buttons
they can't use:

- SO Confirm button → group_fp_sales_manager (Sales Rep sees the SO
  in draft but no Confirm button — matches model-level gate from Phase G)
- SO pricing fields (price_unit/subtotal/total/untaxed/tax) →
  group_fp_sales_rep (Technician/Shop Manager don't see pricing if
  they navigate to an SO)
- Partner Account Hold tab → group_fp_manager (was the fold-in
  group_fp_accounting; the audit-finding-11 _administrator typo lives
  in res_partner.py and is Phase G's fix)
- CAPA Close + all state-transition buttons → group_fp_quality_manager;
  edit fields use readonly="not user_has_groups(...)" so Manager
  retains read+comment per spec section 2.C
- Audit Start/Findings/Close buttons → group_fp_quality_manager
- AVL Approve/Suspend/Reinstate/Remove → group_fp_quality_manager
  (model uses Suspend+Remove instead of spec's literal 'Disqualify';
  both surfaces gated, semantics match)
- Customer Spec edit fields → readonly for non-QM (Manager keeps
  read access per spec; only inputs lock)
- FAIR Approve/Reject buttons → group_fp_quality_manager (Submit-
  for-Review and Reset stay open to whoever created the FAIR)
- Certificate Issue button — Strategy B chosen: single button hidden
  when cert_type=nadcap_cert AND user is not QM. Cleaner than splitting
  into two buttons; no separate action_sign exists on fp.certificate
  (Issue is the sign+publish action). FAIR lives in its own model;
  fp.certificate only has nadcap_cert as a special type. The ir.rule
  from Phase C enforces model-level writes independently.
- CGP form buttons (7 view files: ai, controlled_good, psa,
  receipt_shipment, registration, security_incident, visitor) →
  group_fp_quality_manager on every action button

Defense in depth: ir.rules and ACLs (from Phases B + C) already
restrict model access. These view gates are the UI layer that
matches.

Concerns:
- Spec line 192 names 'sale.order view — x_fc_account_hold_override'
  but no such field exists in the codebase. Closest practical match
  was the partner-side Account Hold management tab, which already had
  a group= attribute. Re-gated there; no SO-side field to gate.
- AVL model has no action_disqualify per spec; uses suspend+remove.
  Both gated to QM.
- fp.certificate has no action_sign (only action_issue). FAIR's
  approve/reject covers the FAIR side; nadcap-cert Issue covers the
  cert side via Strategy B.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:45:39 -04:00
gsinghpal
36cd4341a7 feat(plating-menu): Layer 1+2 — explicit groups on top-level menus + submenus
Phase D Tasks D1-D4 of permissions overhaul. Adds explicit groups=
attributes to:
- 9 top-level Plating menus (matrix per spec Section 2.E)
- Quality submenus: Audits, Customer Specs, AVL → QM-only
- Compliance hub child submenus (CGP, General, Safety, Aerospace,
  Nuclear) → QM-only
- Operations submenus: Maintenance, Move Log, Labor History → Shop
  Manager+; Replenishment Suggestions → Manager+

Replaces fragile inheritance + action-ACL-based visibility with
explicit per-menu gates. Now every role's menu tree is deterministic.

Also adds fusion_plating/tests/test_menu_visibility.py — per-role
matrix tests using ir.ui.menu.search_count with the test user.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:35:11 -04:00
gsinghpal
84ed406c8e feat(plating-quality): split Manager vs Quality Manager permissions
Phase C of permissions overhaul (spec Section 2.C).

Manager keeps reactive Quality (NCR/Hold/Check/Cert/RMA — already gated
via Phase B sweep). QM gains exclusive write/create/unlink on strategic
Quality records:

- fusion.plating.capa: Manager → read-only (1,0,0,0); QM → full
- fusion.plating.audit: same split (if model present)
- fp.approved.vendor.list: same split (if model present)
- fusion.plating.customer.spec: same split
- Doc Control models: same split

Plus FAIR/Nadcap cert restriction via two new ir.rule records on
fp.certificate:
- Manager: write/create/unlink on certs where cert_type NOT in
  ('fair', 'nadcap')
- QM: write/create/unlink on all certs (overrides via OR within group)
- Read access unchanged for both (perm_read=False on the rules)

Tests in fusion_plating/tests/test_quality_split.py verify each side
of the split. Models that may not exist on all DBs (audit, AVL) use
skipTest gracefully.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:23:32 -04:00
gsinghpal
8eb2c2de95 refactor(plating-sec): sweep all ACL CSVs to new role group xmlids
Phase B of permissions overhaul. Mechanical text replacement across
11 ir.model.access.csv files:
  - group_fusion_plating_operator    -> fusion_plating.group_fp_technician
  - group_fusion_plating_supervisor  -> fusion_plating.group_fp_shop_manager_v2
  - group_fusion_plating_manager     -> fusion_plating.group_fp_manager
  - group_fusion_plating_admin       -> fusion_plating.group_fp_owner
  - group_fp_estimator (configurator)-> fusion_plating.group_fp_sales_rep
  - group_fp_accounting              -> fusion_plating.group_fp_manager
  - group_fp_receiving               -> fusion_plating.group_fp_shop_manager_v2
  - group_fp_shop_manager (legacy)   -> fusion_plating.group_fp_manager
  - group_fusion_plating_cgp_officer -> fusion_plating.group_fp_quality_manager
  - group_fusion_plating_cgp_designated_official -> fusion_plating.group_fp_owner

Backward-compat: old group xmlids still resolve (Phase A's implied_ids
chains keep old ACLs working for users still holding old groups).
This sweep ensures future-state correctness: when old groups are deleted
after the 30-day rollback window, ACLs continue resolving via the new
group xmlids.

Also adds fusion_plating/tests/test_acl_migration.py with sample-based
per-role access checks. The 2 CAPA tests are expected to fail until
Phase C implements the Manager/QM quality split.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:14:02 -04:00
gsinghpal
d3c5c25865 changes
Some checks failed
fusion_accounting CI / test (fusion_accounting_ai) (push) Has been cancelled
fusion_accounting CI / test (fusion_accounting_core) (push) Has been cancelled
fusion_accounting CI / test (fusion_accounting_migration) (push) Has been cancelled
2026-05-17 03:20:33 -04:00
gsinghpal
21754c1660 fix(specs): @api.depends on _compute_display_name — fixes 'Unnamed' dropdown
The _compute_display_name method on fusion.plating.customer.spec was
missing its @api.depends decorator. Without it, Odoo doesn't know
when to fire the compute, so display_name stayed NULL on:
- All seeded specs (created via XML data import)
- Any spec created later (the field was never recomputed)

Symptom: Specification dropdown on the SO line showed "Unnamed" for
every option, making spec selection useless.

Fix:
- @api.depends('code', 'revision', 'name') on _compute_display_name
- Imported `api` (was only `fields, models`)

Companion entech-side action: forced recompute on the 15 existing
specs via `env.add_to_compute(specs._fields['display_name'], specs)`
so the stored column was backfilled. New specs created via UI will
trigger the compute automatically going forward.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 08:36:00 -04:00
gsinghpal
bc7c771f20 chore(menu): promote Specifications + clarify misleading menu names
Specifications menu (urgent — workflow blocker for estimators):
- Moved from Configuration → Quality & Documents (manager-only) up
  to Plating → Quality (sequence 70). Now visible to estimator,
  supervisor, and manager.
- Renamed "Customer Specs" → "Specifications" — the seeded library
  includes industry standards (AMS, MIL, ASTM, BAC) not just
  customer-private specs.
- Action display name updated: "Customer Specifications" → "Specifications".
- Added action.help HTML so the empty-state placeholder explains
  the Specifications library purpose to first-time users.
- Old xmlid (menu_fp_config_customer_spec) preserved so existing
  links / breadcrumbs / search references continue to resolve.

Other clarifying renames:
- Safety: "JHSC" / "JHSC Meetings" → "H&S Committee (JHSC)" /
  "H&S Committee Meetings" — acronym was opaque to non-Canadian
  H&S folks.
- Operations: "Move Log" → "Parts & Rack Move Log" — generic name
  could be confused with chatter messages or stock moves.
- Configuration → Recipes & Steps: "Workflow States" →
  "Job Workflow Stages" — generic name; clarifies these are job
  state milestones (passed-stage tracking), not generic workflow.
- Compliance → General: child folder "Configuration" → "Reference
  Data" — three levels of "Configuration" nesting (Plating>Config
  vs Plating>Compliance>General>Config) was confusing.

No model / data changes. Pure menu metadata.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 08:05:19 -04:00
gsinghpal
d891002c84 feat(promote-customer-spec): Phase E — final removal of coating + treatment
DELETED entirely (model + view + ACL + data file + menu):
- fp.coating.config (configurator)
- fp.treatment (configurator + seeded data)
- fp.coating.thickness (configurator) — replaced by fp.recipe.thickness in Phase A
- fp.customer.price.list (configurator) — coating-keyed, no replacement

Field deletions:
- sale.order.x_fc_coating_config_id
- sale.order.line.x_fc_coating_config_id + x_fc_treatment_ids
- account.move.line.x_fc_coating_config_id
- fp.part.catalog.x_fc_default_coating_config_id + x_fc_default_treatment_ids
- fp.job.coating_config_id
- fp.pricing.rule.coating_config_id
- fp.quality.point.coating_config_ids
- fp.direct.order.line.coating_config_id + treatment_ids
- fp.sale.description.template.coating_config_id

Refactored:
- fp.quote.configurator.coating_config_id → recipe_id (now points at
  fusion.plating.process.node, the actual recipe). All compute, onchange,
  and matcher logic updated to use recipe directly. Quality inherit
  extends matcher with spec-tier scoring.
- fp.job._fp_create_certificates now reads spec from job.customer_spec_id
  and formats spec_reference as "code Rev rev". Same for thickness
  source — bake fields read from recipe_root (Phase A).
- fp.job.step.button_finish bake-window auto-spawn reads bake settings
  from recipe_root instead of coating.
- fp.certificate auto-fill spec_min_mils/max_mils from recipe (Phase A
  thickness fields) instead of coating.
- jobs/sale_order.py: job creation reads x_fc_customer_spec_id from
  line, drops coating refs and the legacy header-coating fallback.
- Wizards drop coating + treatment fields and refs.
- Configurator views drop x_fc_coating_config_id + x_fc_treatment_ids
  fields entirely. Quality inherits re-anchor on stable fields
  (x_fc_part_catalog_id, x_fc_internal_description, default_process_id,
  process_variant_id, substrate_material) so they keep working.
- Reports drop coating fallback elifs; print recipe / spec.
- Tablet payload drops coating_config_id from job.read fields.

Skipped (deferred to backlog):
- fusion_plating_bridge_mrp — module is uninstalled per Sub 11; source
  files retain coating refs but no runtime impact.
- fusion_plating_portal — circular dep (portal → quality → certs →
  portal). Customer-facing portal coating picker stays for now;
  promote-spec polish is a separate sub-project.

Verification: grep for "coating_config_id|fp.coating.config|
fp.treatment|fp.coating.thickness" in live (non-bridge_mrp,
non-portal, non-script, non-test) Python/XML/CSV returns 3 hits,
all in module / class docstrings explaining Phase E history.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 02:00:41 -04:00
gsinghpal
e0eacc2530 feat(promote-customer-spec): Phase D — reports + tablet payload include spec
Reports updated to print Specification (with revision via display_name):
- report_fp_sale.xml — header sections show "SPECIFICATION" instead
  of "COATING CONFIG", reads doc.x_fc_customer_spec_id (added on
  sale.order via quality inherit, computed from line.customer_spec_id)
- report_fp_wo_sticker.xml — propagates _spec alongside _coating
- fusion_plating_reports/report_fp_job_traveller.xml — header row
  now shows Specification (falls back to coating)
- fusion_plating_jobs/report_fp_job_traveller.xml — same fall-back
- fusion_plating_jobs/report_fp_job_sticker.xml — _spec added

sale.order.x_fc_customer_spec_id added as a stored compute on
sale.order (in quality) so reports can render order-level spec.
Mirrors the line's first spec; updates on line edit.

Tablet payload (shopfloor_controller.py):
- spec_label added to the job payload dict
- defensive 'customer_spec_id' in job._fields check (shopfloor doesn't
  depend on quality — circular if added)

Portal: deferred (same circular-dep issue, more substantial UI rewrite
needed; Phase E backlog item).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:30:05 -04:00
gsinghpal
c637f82ae2 feat(promote-customer-spec): Phase C — pricing, quality, job, cert re-keyed
Pricing:
- Quality inherit on fp.pricing.rule adds customer_spec_id + recipe_id
- Quality inherit on fp.quote.configurator adds customer_spec_id field
  + extends _find_matching_rule with priority chain:
    spec (+8) > recipe (+6) > coating (+4) > material (+2) > cert (+1)
- View inherit surfaces both new pickers on the rule form

Quality points:
- fp.quality.point now has customer_spec_ids + recipe_ids M2M filters
- Matcher (_matches + _find_matching) accepts new args
- Hook overrides on SO confirm + job confirm/done + step finish
  pass spec/recipe context through to the matcher
- View surfaces both new M2M widgets

Job:
- jobs/sale_order.py wires x_fc_customer_spec_id from SO line to
  fp.job.customer_spec_id on action_confirm

Cert:
- Quality inherit on fp.certificate adds customer_spec_id field +
  create() override auto-fills spec_reference from spec.code+revision
  Resolution priority: explicit spec_reference > cert.customer_spec_id
  > SO line spec (with print_on_cert) > legacy coating fallback

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:23:06 -04:00
gsinghpal
7cafab1b9f feat(promote-customer-spec): Phase B — two-picker SO line UX
Spec-side picker (x_fc_customer_spec_id / customer_spec_id) added on:
- sale.order.line (via quality inherit — onchange autofill, create()
  fallback to part default, _prepare_invoice_line carry)
- account.move.line (via quality inherit — invoice rendering)
- fp.part.catalog (via quality inherit — x_fc_default_customer_spec_id)
- fp.direct.order.line (via quality inherit — wizard picker + autofill)
- fp.direct.order.wizard (action_create_order post-creates spec on SO line)

Thickness picker switched to fp.recipe.thickness (replaces coating-scoped):
- sale.order.line.x_fc_thickness_id comodel + domain rewired to recipe
- account.move.line + fp.delivery same
- fp.direct.order.line.thickness_id same

View inherits in quality add Specification picker next to legacy
Primary Treatment column on:
- SO form line tree
- part catalog Default Treatments block
- direct-order wizard line tree + drawer

Wizard files (fp.contract.review.client.email.wizard) pulled from
entech into the repo — they were ahead of the repo. Quality __init__
now imports wizards/.

Legacy x_fc_coating_config_id + treatment_ids remain visible during
transition; Phase E removes them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:16:25 -04:00
gsinghpal
406cac1362 feat(promote-customer-spec): Phase A — recipe + spec foundation
- Add fp.recipe.thickness model (replaces fp.coating.thickness, scoped to recipe root)
- Add spec metadata + bake-relief fields to fusion.plating.process.node (recipe root):
  phosphorus_level, thickness_min/max/uom, thickness_option_ids,
  requires_bake_relief + bake_window_hours/temperature/duration
- Add recipe_ids M2M + print_on_cert to fusion.plating.customer.spec
- Add applicable_spec_ids reverse M2M as inherit in fusion_plating_quality
  (avoids circular dep — core can't reference customer.spec which lives in quality)
- Surface new fields on recipe form ("Specification & Bake" notebook page)
- Surface recipe linkage on customer spec form

Pure additive. Foundation for Phases B-E.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 00:50:17 -04:00
gsinghpal
f07e1bcce1 fix(chatter): wrap HTML message_post bodies in Markup() — 4 sites
Four message_post calls were passing strings with HTML tags as
plain `body=_(...)` instead of `body=Markup(_(...))`. Odoo escapes
non-Markup strings, so the chatter rendered "<b>QA Review failed</b>"
as literal text instead of bolding it.

Original bug surfaced via the Contract Review (QA-005) flow:
  body: "&lt;b&gt;QA Review failed&lt;/b&gt; by Garry Singh. Awaiting
  client information.&lt;br/&gt;&lt;b&gt;Reason:&lt;/b&gt;&lt;br/&gt;
  &lt;div data-oe-version=\"2.0\"&gt;Need to get updated
  drawing...&lt;/div&gt;"

Audit scan turned up three more identical patterns:

  fusion_plating/models/fp_parent_numbered_mixin.py:118
     "Issued <strong>%s</strong> to ..."
  fusion_plating_jobs/models/sale_order.py:282
     "Confirmed quote <strong>%s</strong> as <strong>%s</strong>."
  fusion_plating_quality/models/fp_contract_review.py:430
     "<b>QA Review failed</b> by ... <b>Reason:</b><br/>%(reason)s"
  fusion_plating_quality/models/fp_contract_review.py:524
     "<b>QA Review completed</b> by ... <b>Special Instructions
      captured:</b><br/>%(notes)s"

Fixes:
- Wrapped each body=_(...) with Markup(_(...)) using the
  Markup(template) % values pattern (auto-escapes the substituted
  values; user-supplied free text stays safe).
- For Html-field substitutions (qa_failure_reason,
  special_instructions), explicitly wrapped the value in Markup()
  so already-formatted HTML editor content (with data-oe-version="2.0"
  wrapper divs) flows through without being re-escaped.
- Added `from markupsafe import Markup` to the two files that
  didn't already import it (mixin + contract_review).

Drift cleanup: pulled the 180-line newer fp_contract_review.py
from entech to the local repo (added action_qa_review_failed,
action_open_client_email_wizard, action_view_client_emails,
action_complete_after_info, awaiting_info state, qa_failure_reason
+ special_instructions Html fields, etc. that had been edited on
entech without being committed).

Tested by re-posting via odoo shell on review 10: body now stores
"<b>QA Review failed</b>..." with literal HTML tags instead of
the double-escaped "&lt;b&gt;..." entities. Old chatter records
with the bad escape stay as-is in the audit trail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 08:41:39 -04:00
gsinghpal
95cb73d91a feat(numbering): wire NCR, CAPA, Hold, RMA into parent-numbered mixin
Hold derives parent via job_id.sale_order_id; RMA via sale_order_id
directly — both get HOLD-<parent> / RMA-<parent> names. NCR and CAPA
have no SO link in core, so they fall back to their legacy sequences
(NCR/YYYY/NNN, CAPA/YYYY/NNN); future modules can override the
_fp_parent_sale_order hook to enable parent naming.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 13:36:29 -04:00
gsinghpal
cd2584d6ee ui(rename): "Plating Job" -> "Work Order" / display "WO # 01368"
Standardise user-facing terminology across 5 modules (27 files):
  - display_name compute: 'Work Order # 01368' -> 'WO # 01368'
  - _description on 5 models: Plating Job{," Step"," Step Time Log"," Margin Report"," Recipe Node Override"} -> Work Order equivalents
  - field labels (string=...) on 13 Many2one / One2many fields
    across fp.batch, fp.thickness_reading, fp.quality.hold,
    fp.job_consumption, fp.portal.job, fp.certificate, fp.delivery,
    fp.quality.check, fp.racking.inspection, res.partner, sale.order
  - XML view labels: action names, list/form/search strings,
    portal template names, dashboard tile titles

What's deliberately preserved:
  - DB model name 'fp.job' (technical identifier — used by
    sale_order.x_fc_plating_job_ids and all comodel refs)
  - Module name 'fusion_plating_jobs' (directory / import path)
  - Settings -> Apps display label 'Fusion Plating Jobs' (module
    identity for Odoo's app picker)
  - 'Use Native Plating Jobs' migration toggle (internal mechanism
    flag, not user-facing terminology)

Verified on entech: WH/JOB/01368 now displays as 'WO # 01368'
everywhere humans look (form header, breadcrumbs, M2O dropdowns,
error messages, smart-button titles).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 08:22:09 -04:00
gsinghpal
ee80673579 fix(contract-review): WO step routes to QA-005 + auto-stage on part create
Two bugs fixed in one drop, both targeting the contract review (QA-005)
enforcement gap reported on entech.

## Bug 1 — WO step routed to wrong wizard

Symptom: clicking Finish & Next or Record on a Contract Review step in
WH/JOB/00339 opened the generic measurement wizard with three fake
prompts (Reviewer Initials / Date Reviewed / QA-005 Approved). No path
to the actual QA-005 form from the work order.

Root cause: action_finish_and_advance + action_open_input_wizard had no
branch for recipe_node.default_kind == 'contract_review'. The step.kind
mapping collapses contract_review -> 'other' so kind-based detection
wouldn't have worked either; gate has to live at the recipe-node layer.

Fix in fusion_plating_jobs/models/fp_job_step.py (v19.0.8.14.6):
- action_finish_and_advance:329 calls _fp_contract_review_redirect
  before the input-wizard branch
- action_open_input_wizard:844 same gate, keeps Record button consistent
- _fp_contract_review_redirect:866 (new) returns the part's
  action_start_contract_review() unless review.state in
  (complete, dismissed) — gate clears so the step can finish after
  the operator signs QA-005.

## Bug 2 — Part create did not enforce contract review

Symptom: spec called for a banner-only UX. User wanted true automatic
enforcement on first part creation under an enforced customer.

Fix in fusion_plating_quality/models/fp_part_catalog.py (v19.0.4.10.0):
- @api.model_create_multi def create() override
- _fp_enforce_contract_review_on_create() helper auto-stages the
  fp.contract.review record AND surfaces three prominent reminders:
    1. Sticky bus.bus warning toast (top-right, doesn't auto-dismiss)
    2. mail.activity (To Do) on the part for the current user
    3. Smart button on the part form lights up (review now exists)
- Idempotent: skips parts that already carry a review id
- Soft-fails: bus or activity outage doesn't block part creation
- create()-only — write/update flows never re-trigger

Sub 4's existing info banner stays as a fourth surface.

## Tests

- fusion_plating_jobs/tests/test_fp_job_extensions.py:
  +TestContractReviewStepRouting (5 tests covering both routing methods,
  the complete/dismissed gate-clear, and non-CR step regression)
- fusion_plating_quality/tests/test_part_catalog_contract_review_enforcement.py
  (NEW): 9 tests covering auto-create, batch create, idempotency,
  activity surface, bus surface, write-must-not-retrigger, soft-fail.
- docs/superpowers/tests/2026-04-22-sub4-smoke.py: flipped the
  "no review yet" assertion to "review auto-created" to match new
  behavior. Sign-flow assertions unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 20:02:52 -04:00
gsinghpal
13e300d90e changes 2026-04-28 19:39:37 -04:00
gsinghpal
3641b78a66 feat(phase2): Configuration grouped into 7 themed folders
Collapses the flat ~36-entry Configuration list into 7 navigable
folders + Settings (sibling, stays at top of Configuration). Existing
menu IDs unchanged so bookmarks + cross-module data refs still work
— only parent-id moves.

New folder menus (defined in fusion_plating core):
  menu_fp_config_shop_setup       Shop Setup
  menu_fp_config_recipes_steps    Recipes & Steps
  menu_fp_config_materials_tanks  Materials & Tanks
  menu_fp_config_workforce        Workforce
  menu_fp_config_quality_docs     Quality & Documents
  menu_fp_config_pricing_billing  Pricing & Billing
  menu_fp_config_reference_data   Reference Data

Routing per item (sources updated in their owning module):
  Shop Setup       Facilities, Work Centers, Work Centres, Process
                   Categories, Process Types, Bake Ovens, Shopfloor
                   Stations, Vehicles
  Recipes & Steps  Step Library, QC Checklist Templates, Quality Points
  Materials & Tanks  Bath Parameters, Replenishment Rules, Chemicals,
                     Rack Tags, Calibration Equipment, Calibration Events
  Workforce        Operator Certifications, Shop Roles, Training Types,
                   Quality Teams
  Quality & Documents  Customer Specs, Approved Vendor List, Quality
                       Tags, Quality Reasons, Quality Stages, N299
                       Levels, Notification Templates, Notification Log
  Pricing & Billing  Invoice Strategy Defaults, Account Holds
  Reference Data   Value Sets, Value Rotations
  (Settings remains as a sibling at top of Configuration, manager-gated)

Versions bumped: fusion_plating, fusion_plating_quality, _safety,
_shopfloor, _logistics, _culture, _invoicing, _notifications, _nuclear.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:45:21 -04:00
gsinghpal
2a4909be25 changes 2026-04-27 08:16:20 -04:00
gsinghpal
f08f328688 changes 2026-04-27 00:11:18 -04:00
gsinghpal
d9f58b9851 changes 2026-04-26 15:05:17 -04:00
gsinghpal
41d0908ade changes 2026-04-24 21:04:38 -04:00
gsinghpal
1393c9e6ac fix(plating): Sub 4 — tighten Contract Review checklist columns
The Checklist group used col=\"4\" which stretched each label+field
pair across a quarter of the sheet, producing a big empty gap
between the two columns. Replaced with the idiomatic Odoo two-
nested-group pattern so each column hugs its labels, removing the
dead space. Same fix applied to Section 3.0 and to the Outcome
blocks on both sections.

fusion_plating_quality → 19.0.2.3.0

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 21:56:27 -04:00
gsinghpal
af199bda9f feat(plating): Sub 4 — smart button on part form for Contract Review
Adds a "Contract Review" stat button to fp.part.catalog's button box.
Shows a coloured state badge (green=complete, blue=manager_review,
yellow=assistant_review, muted=dismissed, em-dash when none). Click
routes through action_start_contract_review so it opens the existing
review or lazy-creates one — same behaviour as the banner / tab.

fusion_plating_quality → 19.0.2.2.0

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 21:54:47 -04:00
gsinghpal
5d9c78f8ce feat(plating): Sub 4 — Check All / Clear All buttons + fix QA-005 PDF logo render
- New bulk-toggle actions on fp.contract.review flip all 10 checklist
  items in Section 2.0 (and all 11 in Section 3.0) in one click.
  Rendered as "Check All" / "Clear All" buttons above each checklist.
  User can still tick boxes individually. Buttons hide once the
  section is signed (locked).
- Fix QA-005 PDF: replaced `to_text(...)` (not in QWeb context) with
  `image_data_uri(...)` for the company logo embed. PDF now renders
  with the full colour ENTECH logo (render size 103 KB).
- Smoke test extended: 5 new assertions covering bulk-toggle on/off
  and locked-section guard. 17/17 pass on entech.

fusion_plating_quality → 19.0.2.1.0

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 21:50:18 -04:00
gsinghpal
21da526aa7 feat(plating): Sub 4 — Contract Review (optional, QA-005 1:1 PDF)
Per-part contract review record (fp.contract.review) gated by a
customer-level toggle, signed in two sections (QA Assistant → QA
Manager), settings-based signer rosters (no new res.groups), banner on
the part form that auto-dismisses once the first MO for the part hits
confirmed. QA-005 Rev. 0 paper form reproduced 1:1 in a QWeb PDF.

Never blocks MO/SO/WO — review is purely an audit artefact.

Smoke test run on entech: 12 assertions pass including the 25-cell
risk matrix parity with the paper form and 22 KB PDF render.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 21:43:06 -04:00
gsinghpal
11837ed4f5 fix(plating): Manager Desk premature-advance + 6 workflow enforcement gates
**1. Manager Desk: WO no longer jumps to "In Progress" on partial setup**

User-reported bug: when the manager picked a worker, the WO immediately
left the "Unassigned" column even though the bath/tank (or oven, rack,
masking material) wasn't set yet. Worker would see a half-set job in
their queue and couldn't start it.

Fix:
- New compute `mrp.workorder.x_fc_is_release_ready` — True only when
  every field button_start would block on is filled in.
- Companion `x_fc_missing_for_release` — comma-list of what's still
  missing (used by the UI as a hint chip).
- Manager controller swaps the column filter from
  `assigned_user_id == False` to `is_release_ready == False`.
- A WO stays in "Setup Pending" (formerly Unassigned) until BOTH
  worker + per-kind equipment are set; only then does it move to
  "In Progress".

**Manager Desk template + SCSS**

The user also said "the manager doesn't know what task they're
assigning". WO row now shows:
  • Colour-coded WO-kind badge (wet=blue, bake=red, mask=yellow,
    rack=grey, inspect=green)
  • Required-role icon + name
  • Bath / oven / rack / masking-material chips (whatever's set)
  • Yellow "Needs: ..." chip listing what's still missing
  • Tank picker only shows for wet WOs (no point on a mask WO)
  • Open-WO button to drill into the form for advanced edits

**2. Six enforcement gates patched (without breaking the workflow)**

Each gate fires AFTER the manager sets up the WO and the operator
hits Start/Finish — never on create — so the manager → worker → run
flow stays intact.

| # | Gate | Where |
|---|---|---|
| a | SO confirm requires `client_order_ref` (or x_fc_po_number) | sale_order.action_confirm |
| b | Cert issue requires thickness readings (when partner.x_fc_strict_thickness_required) | fp_certificate.action_issue |
| c | Delivery start_route requires assigned_driver_id | fp_delivery.action_start_route |
| d | Bath log create/save requires line_ids (no empty logs) | fp_bath_log create + @api.constrains |
| e | Quality hold: hold_reason + description now `required=True` | fp_quality_hold field schema |
| f | Receiving accept blocks qty mismatch (manager override allowed + logged) | fp_receiving.action_accept |

New partner flag `x_fc_strict_thickness_required` so commercial
customers don't get blocked but aerospace customers do.

**Verified** via `scripts/fp_enforcement_audit.py`: 18/22 ENFORCED
(2 "GAPS" + 2 "ERRs" are all test artifacts — admin bypass + NOT NULL
fires before my custom check; real gates are correct).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 12:54:00 -04:00
gsinghpal
c118b7c6b5 feat(plating): close compliance gaps 7-9 — NCR + CAPA + discharge + invoice ref
**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>
2026-04-19 10:35:27 -04:00
gsinghpal
eed4dc8a78 fix(plating): chatter HTML rendering + workflow stage banner UX
Two fixes from a single SO walkthrough screenshot:

**1. "Current stage" banner**
- Was placed `inside sheet` so it rendered at the BOTTOM of the form
  where users miss it. Moved to `before form/header` (same xpath
  pattern as the Account Hold banner) — now it's the first thing
  visible above the SO header.
- Was still showing "Shipped — awaiting invoice" after the invoice
  was posted because `_compute_workflow_stage` only advanced to
  `complete` when shipped + ALL paid; an unpaid posted invoice left
  the SO stuck on `shipped`. Added an `invoicing` branch: shipped +
  has_posted_invoice → invoicing. Banner invisible-list now also
  includes `invoicing` and `paid`, so the banner only shows for
  in-progress steps.

**2. Chatter messages rendering raw HTML tags as text**
Odoo 19 escapes any string passed to `message_post(body=...)`
unless wrapped in `markupsafe.Markup`. We had ~10 places posting
HTML (`<a href>`, `<b>`, `<br/>`, `<code>`, `<pre>`) that all
showed up as `&lt;a href=...&gt;` literal text in the chatter.

Wrapped each one with `Markup(_(...))` so the tags render. Files
touched:

- fusion_plating_bridge_mrp/models/sale_order.py
  (auto-MO failure code block, "Draft MO created" link,
   "Job assigned to <b>" message)
- fusion_plating_bridge_mrp/models/mrp_production.py
  ("Recipe steps" pre/br block on each WO)
- fusion_plating_bridge_mrp/models/fp_proficiency.py
  (operator promotion announcement)
- fusion_plating_configurator/models/fp_quote_configurator.py
  (SO link, 3D model attached, drawing attached, save to catalog)
- fusion_plating_configurator/models/fp_part_catalog.py
  (3D/drawing change tracking + propagation to linked quotes)
- fusion_plating_portal/models/fp_quote_request.py
  (RFQ → SO link)
- fusion_plating_quality/models/fp_quality_hold.py
  (hold status change)
- fusion_plating_shopfloor/controllers/manager_controller.py
  (worker / tank / manager-takeover assignments)

Verified on entech: SO S00038 stage now reads `invoicing` (banner
hidden), and a freshly posted message shows `<a href>` and `<b>`
as actual link + bold instead of escaped text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 08:36:00 -04:00
gsinghpal
7c7ef06057 folder rename 2026-04-16 20:53:53 -04:00