Commit Graph

75 Commits

Author SHA1 Message Date
gsinghpal
457d9b7dbf fix(numbering): post-review fixes — credit notes, SO unlink, multi-part grouping, SQL whitelist
- B1: Add Credit Note wizard path was blocked because invoice_origin
  has copy=False and the wizard doesn't set fp_from_so_invoice. Now
  the validator allows reversals when reversed_entry_id points at a
  customer-facing move that itself went through the validator at
  original creation time. account.move._fp_parent_sale_order also
  walks self.reversed_entry_id._fp_parent_sale_order so the credit
  note inherits the parent number (CN-<parent>).

- Bug 1: sale.order.unlink() now blocks deletion when x_fc_parent_number
  is set (matches spec §6.2). Draft quotes remain freely deletable
  per Odoo standard. Applies to all users including admins.

- Bug 2: out_receipt added to CUSTOMER_TYPES so POS-style receipts
  hit the same SO-flow gate as out_invoice / out_refund.

- C1: WO grouping key changed from recipe.id to (recipe.id, part.id,
  coating.id). Bundling lines with different parts under one WO put
  first_line's part_number on the CoC header — silent compliance
  mis-attestation. Now distinct parts always get distinct WOs even
  when they share a recipe.

- C3: SQL whitelist (_FP_COUNTER_FIELD_RE) on _fp_assign_parent_name's
  interpolated counter field name. No user input today; defence in
  depth for future subclasses that might read the name from context.

Verified on entech: parent=30017, credit note = CN-30017,
multi-part SO produces 2 WOs (one per part), confirmed-SO unlink
blocked, out_receipt blocked, whitelist regex enforced.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:19:08 -04:00
gsinghpal
78d633f63f feat(numbering): immutable name/doc_index + unlink block on issued docs
write() override raises UserError if name or x_fc_doc_index is in
vals and differs from the stored value (bypass: context flag
fp_allow_name_rename=True for the SO-confirm rename + bulk WO
creation paths). unlink() override raises UserError for records
that have been issued a name; applies to all users including
admins — cancellation must go through the state machine.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 13:39:03 -04:00
gsinghpal
6c6fb8d2a4 feat(numbering): WO grouping by recipe + parent-derived bulk naming
Replaces x_fc_wo_group_tag grouping with resolved-recipe grouping.
Bare WO-<parent> when 1 recipe, WO-<parent>-NN zero-padded for N>1
ordered by min line sequence. fp.job inherits parent-numbered mixin
for the manual-add path; bulk SO-confirm sets names explicitly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 13:18:10 -04:00
gsinghpal
105909470f feat(numbering): add fp.parent.numbered.mixin abstract model
Atomic counter via SELECT FOR UPDATE on the parent SO row. Composes
child names as PREFIX-PARENT (bare for first) or PREFIX-PARENT-NN
(zero-padded 2-digit, then unpadded past 99). Subclasses implement
three hooks: _fp_parent_sale_order, _fp_name_prefix, _fp_parent_counter_field.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 13:09:17 -04:00
gsinghpal
fd9d4e775b feat(numbering): add fp.parent.number + fp.quote.number sequences
Parent sequence starts at 30000. Quote sequence is Q + YYYYMM + non-resetting
counter starting at 200. Phase 1 Task 1 of the parent-number hierarchy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 13:07:16 -04:00
gsinghpal
2d9779047b fix(jobs): registry load failure after Tier 2/3 persistence patches
Two compounding issues introduced by the persistence audit work:

1. fields.Text mismatch: sale.order.x_fc_internal_note and
   x_fc_external_note are actually fields.Html. I declared the
   sale.order.line related mirrors and the fp.job stored copies as
   fields.Text, so setup_related raised:
     TypeError: Type of related field
     sale.order.line.x_fc_internal_note is inconsistent with
     sale.order.x_fc_internal_note
   Fixed by switching both Note fields on fp.job and sale.order.line
   to fields.Html.

2. Module-load-order: Tier 3 fields (x_fc_delivery_method,
   x_fc_ship_via, x_fc_invoice_strategy) are defined in
   fusion_plating_jobs (related to sale.order via _inherit), but I
   referenced them in fusion_plating core's fp_job_views.xml — which
   loads BEFORE fusion_plating_jobs registers the fields. View
   validator raised "Field x_fc_delivery_method does not exist".
   Fixed by removing those 3 fields from the core view group and
   adding them via xpath in fusion_plating_jobs's fp_job_form_inherit
   (which loads after the fields are registered).

Both fixes deployed and verified — registry loads in 2s, all field
types match, related path resolves correctly. No data loss; the
fp.job rows that already had stored Text content for internal_note /
external_note will carry over into the Html field intact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 08:45:59 -04:00
gsinghpal
cba9a6da6b feat(jobs): mirror delivery_method/ship_via/invoice_strategy on fp.job
Tier 3 of the SO->fp.job persistence audit. Three logistics/billing
fields surface on fp.job as related read-only (not stored) mirrors:

- x_fc_delivery_method - Local Delivery / Shipping Partner / Customer
  Pickup. Cargo classification used by logistics planning.
- x_fc_ship_via - Carrier name (UPS, FedEx, customer pickup, etc.).
- x_fc_invoice_strategy - Deposit / Progress / Net Terms / COD-Prepay.
  Read by the invoicing module's hooks; mirroring on the WO is for
  manager visibility only.

These were intentionally chosen as related (not stored persisted)
because the SO is the authoritative source - the existing downstream
code (delivery + invoicing modules) already reads them off SO directly.
A stored copy would risk drift. Related auto-follows SO updates.

Same three fields also mirrored on sale.order.line as stored related
for per-line list visibility.

Closes the SO->fp.job persistence audit. All 10 operational fields
identified now flow through to the WO (7 stored + populated at confirm,
3 related read-only).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 08:39:12 -04:00
gsinghpal
15eac309ee feat(jobs): persist deadlines + planned start + notes on fp.job
Tier 2 of the SO->fp.job persistence audit. Four operational metadata
fields mirrored from sale.order:

- x_fc_internal_deadline (Date) - shop's internal target finish date,
  ahead of the customer-facing deadline. Kept separate from
  date_deadline (which scheduling code may adjust).
- x_fc_planned_start_date (Date) - customer-quoted planned start date.
  Kept separate from date_planned_start (Datetime, capacity-adjusted).
- x_fc_internal_note (Text) - shop-internal notes from the order.
- x_fc_external_note (Text) - customer-facing notes, printed on
  traveller / BoL / cert.

All four populate at SO confirm via _fp_auto_create_job, and surface
on sale.order.line as stored related fields for per-line visibility.
fp.job form view gets a Notes group alongside the Customer References
group from Tier 1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 08:37:19 -04:00
gsinghpal
7d37f5713c feat(jobs): persist Customer Job # + PO # + Rush Order on fp.job
Tier 1 of the SO->fp.job persistence audit. Three customer-reference
fields entered on sale.order's Plating tab were not flowing through
to fp.job (or SO lines), so the shop floor and printed paperwork
(traveller, BoL, cert) had to round-trip via sale_order_id every time.

Changes:
- fp.job: new x_fc_customer_job_number (Char, tracking), x_fc_po_number
  (Char, tracking), x_fc_rush_order (Boolean, tracking). All three
  populated by _fp_auto_create_job at SO confirm time.
- sale.order.line: x_fc_customer_job_number / x_fc_po_number added as
  stored related fields off order_id so per-line list views show the
  customer's references without navigating to the order header
  (x_fc_rush_order was already on lines).
- fp.job form view: small Customer References group under the title
  surfaces the three fields where the user expects them.

Verified end-to-end: SO -> SO line related fields -> fp.job direct
fields all carry the same value.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 08:34:56 -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
8b5472bf4e fix(jobs): qty gate false-positive on paperwork / first steps
The qty gate I added refused Finish on steps where qty_at_step > 0,
to force operators to move parts forward first. But the first-step
seed in _compute_qty_at_step gives the earliest non-terminal step
a notional qty = job.qty — a UI hint, not actual parked parts.
Paperwork steps (Contract Review, Inspection-by-paperwork, etc.)
sit on that seed, and the gate was blocking Finish with a misleading
error.

Fixes:
- button_finish gate now checks for REAL incoming moves before
  refusing. Seed-only qty (no incoming_move_ids filtered to non-
  self-loop) is exempt.
- _fp_record_one_piece_auto_move detects seed-only qty and bulk-
  moves ALL parts in one shot to the downstream step. Correct for
  paperwork / first steps where parts don't physically wait
  per-piece — one click finishes the paperwork and pushes the whole
  batch forward.

For steps with REAL incoming moves (parts actually moved here via
a Move record), the original gate semantics still apply: qty == 1
auto-moves one part; qty > 1 raises with the "use Complete 1 → Next
or Move…" message.

Verified on entech: Contract Review with seed qty=6 now finishes
cleanly, bulk-moving all 6 parts to the next step in one move.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 00:44:29 -04:00
gsinghpal
1c68fd0555 fix(jobs): auto-resync step.duration_actual on timelog edits
When a supervisor edits a timelog's date_started/date_finished (or
deletes a stale timelog), the parent step's "Actual Min" column
was showing stale data — duration_actual is a regular Float set
once by button_finish.

Adds:
- fp.job.step._fp_resum_duration_actual: quiet helper that re-sums
  duration_actual from time_log_ids.duration_minutes. Skip no-op
  updates so write traffic is minimised.
- fp.job.step.timelog.create/write/unlink hooks: call the helper
  on the affected parent step(s) so duration_actual stays
  consistent. Write hook only fires when date_started/date_finished/
  step_id changed (notes edits skip resync). step_id reassignment
  resyncs both old and new parent.
- Existing action_recompute_duration_from_timelogs (manual button)
  still posts a chatter entry for audit-trail use cases.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 23:53:45 -04:00
gsinghpal
b0070afc1b feat(jobs): step qty gate + partial-qty + display rename
Three coupled shop-floor corrections:
- fp.job._compute_display_name: renders "Work Order # 00011" in
  form header, breadcrumbs, M2O dropdowns, and error messages.
  DB name stays as WH/JOB/00011 - existing chatter/cert/delivery
  references unchanged.
- fp.job.step.button_finish: refuses if qty_at_step > 0 AND a
  downstream pending/ready step exists. Last runnable step is
  exempt (parts complete in place). Manager bypass via
  fp_skip_qty_gate=True context key.
- fp.job.step.action_complete_one_to_next: new per-row button
  "Complete 1 -> Next" for streaming flow (large parts going
  one-by-one). Records move(qty=1) to next step; if drain takes
  qty_at_step to 0, auto-finishes source + auto-starts destination
  via existing action_finish_and_advance.
- fp.job.step._fp_record_one_piece_auto_move: auto-move shim
  wired into action_finish_and_advance. qty=1 + downstream =>
  silently record move(1). qty>1 + downstream => raise pointing
  at Complete 1 -> Next. Last step always allowed.
- 16 new TestQtyGate tests covering gate / shim / auto-finish /
  last-step exemption / display rename / Move wizard zero-qty.

Spec: docs/superpowers/specs/2026-05-12-step-qty-gate-and-display-rename-design.md
Plan: docs/superpowers/plans/2026-05-12-step-qty-gate-and-display-rename.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 23:31:56 -04:00
gsinghpal
6b7b44264a changes 2026-05-10 10:25:12 -04:00
gsinghpal
586f05d567 chnages 2026-05-04 02:14:34 -04:00
gsinghpal
3cc393454d fix(simple-editor): HTML in chatter + library form + expand per-step inline edit
Three fixes from user feedback:

1. Chatter posting raw HTML
   _AUDIT_BODY in migration 19.0.18.8.0 was a plain str with <p>
   tags. message_post escaped it for safety, so the chatter pill
   rendered '<p><strong>...</strong></p>' literally to the recipe
   author. Wrapped in markupsafe.Markup so Odoo recognises it as
   safe HTML. Going forward: ANY message_post body containing HTML
   tags MUST be wrapped in Markup() — most callers already do this,
   the migration script was the outlier.

2. Library template editor showed raw <p> tags
   onOpenLibraryEdit was JSON-cloning the payload directly without
   running description through the existing _htmlToText helper that
   the per-step editor uses. Added the conversion. Save path
   (onSaveLibraryEditor + library_save) already wraps via
   _textToHtml so storage stays HTML-compatible.

3. Per-step inline form was missing critical fields — user had to
   delete + re-add a step to change Type/workflow trigger/parallel/signoff
   onToggleEdit now also captures default_kind, triggers_workflow_state_id,
   parallel_start, requires_signoff into the edit state. onSaveStep
   sends them in the write vals. Added _fpResetStepEdit helper to
   keep open/cancel/save reset paths in sync.

   New per-step form has:
     * Step Type (Default Kind) dropdown — drives workflow milestone
       triggers + step-kind routing (e.g. contract_review opens QA-005)
     * Triggers Workflow State dropdown (Sub 14) — per-step override
     * Parallel Start checkbox (Sub 13)
     * Require QA Sign-off checkbox

   step_write controller endpoint also gained a field whitelist —
   was previously accepting any vals dict from the client (security
   hole + opaque to maintainers).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 00:24:40 -04:00
gsinghpal
e54ffe7309 feat(jobs): Sub 14 polish — workflow state form layout + Simple Editor field
Two follow-ups on the workflow state work:

1) Form layout
   The "How triggers combine" help text was crammed into a 2-column
   group, taking ~25% of the available width. Pulled it out of the
   group and rendered as a full-width <div class="alert alert-info">
   below the trigger fields. Same fix applied to Notes — uses a
   <separator> + bare <field> for full sheet width.

2) Simple Recipe Editor support
   The trigger field was only exposed in the Tree Editor. Added it
   to the Simple Editor's inline library form too:

   * fp.step.template.triggers_workflow_state_id (new Many2one) —
     per-template default, snapshot-copied to recipe nodes when
     dropped into a recipe (added to _SNAPSHOT_FIELDS).
   * /fp/simple_recipe/workflow_states/list — new endpoint to feed
     the dropdown. Soft-fails when fusion_plating_jobs isn't
     installed (returns []).
   * Library editor JS — _fpEnsureWorkflowStatesLoaded helper
     caches the catalog on first open (create + edit paths both
     warm it). Save vals carry the trigger id.
   * Library editor XML — dropdown rendered after the flag
     checkboxes. Hidden when the catalog is empty so the form
     doesn't show a useless "— None —" pick.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 00:04:59 -04:00
gsinghpal
4c6bad04c5 Revert "feat(jobs): step sequences are 1, 2, 3, ... not 10, 20, 30, ..."
This reverts commit 32d48ea44d.
2026-05-03 23:31:02 -04:00
gsinghpal
32d48ea44d feat(jobs): step sequences are 1, 2, 3, ... not 10, 20, 30, ...
User feedback: operators kept asking why their work order said "Step 10"
for the first row. The 10-spacing was originally there to allow midpoint
inserts (insert sequence 15 between 10 and 20 without renumbering).
Tradeoff is operator confusion, and recipe authors rarely insert in the
middle anyway. Switching to 1-based contiguous sequences.

Files changed (every step-sequence allocation in the codebase):

fusion_plating_jobs/models/fp_job.py
  _generate_steps_from_recipe — seq_counter starts at 1, increments by 1.
  This is the path that builds fp.job.step records, so new jobs now show
  Step 1, 2, 3, ... in the work order.

fusion_plating_bridge_mrp/models/mrp_production.py
  Same change for the legacy MRP bridge so customers still on
  mrp.production also get 1-based numbering.

fusion_plating/controllers/recipe_controller.py
  - create_node: max_seq + 1
  - reorder_nodes: idx + 1
  - swap renumber: i (was i * 10)
  - paste-import renumber: i (was i * 10)
  - move_node: max_seq + 1
  - _copy_subtree (recipe duplicate/import): i (was i * 10)

fusion_plating/controllers/simple_recipe_controller.py
  - _sequence_for_position rewritten — always renumbers siblings to
    keep them contiguous. Returns pos + 1 for the inserted node.
    Old code used midpoint-with-fallback-to-renumber (10/20/30 spacing).
  - step_reorder: i (was i * 10)
  - library_input_add + step_add_input: existing_max + 1

What this DOESN'T do
  Existing fp.job.step records keep their old sequences (10, 20, ...).
  Re-confirm the SO to spawn a fresh job if you want the clean 1-based
  numbering on a current test job. No data migration — we're in dev
  and the user explicitly said test data is disposable.

What this DOES do
  Every NEW job created from this commit forward shows Step 1, 2, 3, ...
  Every NEW recipe step inserted via the simple editor / tree editor
  also gets sequence 1, 2, 3, ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 22:58:21 -04:00
gsinghpal
9794a98de9 feat(jobs): Sub 13 sequential step enforcement + Sub 12e v3 wizard
Two coherent feature drops shipping together because their fp_job_step
edits overlap. Both target operator workflow correctness.

## Sub 13 — Sequential step enforcement (recipe + per-step)

Background:
  Investigation on WH/JOB/00339 showed operators starting Incoming
  Inspection while Contract Review was still in_progress. Audit:
  98.7% of recipe operations system-wide had requires_predecessor_done
  = false (the legacy per-step opt-in defaults off, recipe authors
  rarely tick the box).

Architecture:
  Recipe-level toggle + per-step opt-out (Option A from /investigate).
  * fusion.plating.process.node.enforce_sequential — Boolean on the
    recipe root. Default True. When True, every operation under this
    recipe waits for earlier-sequence steps to finish before it can
    start.
  * fusion.plating.process.node.parallel_start — Boolean on operation
    nodes. When True, this step bypasses the sequential gate (e.g.
    paperwork or QA review that runs alongside production).
  * Mirrored on fp.step.template (parallel_start) so library steps
    carry the flag into snapshots.
  * fp.job.enforce_sequential — related from recipe_id. Snapshotted
    at job creation so a recipe author flipping the recipe's flag
    AFTER job generation does NOT change behaviour mid-run.
  * fp.job.step.parallel_start — related from recipe_node_id.
  * Decision matrix (encapsulated in
    fp.job.step._fp_should_block_predecessors):
        recipe.enforce_sequential | step.parallel_start | step.req_pred_done | block?
        --------------------------|---------------------|--------------------|------
                 True             |       False         |        any         |  YES
                 True             |       True          |        any         |   no
                 False            |        any          |       True         |  YES
                 False            |        any          |       False        |   no
  * Manager bypass via context fp_skip_predecessor_check=True (existing).

Runtime gates:
  * fp.job.step.button_start — calls _fp_should_block_predecessors;
    raises UserError naming the blocking earlier step(s).
  * fp.job.step.can_start — computed Boolean for view-side disable.
  * Move wizard predecessor check
    (fusion_plating_shopfloor/controllers/move_controller.py) — uses
    the same helper so tablet + backend behave identically.

UI surface:
  * Recipe form (fp_process_node_views.xml) — enforce_sequential
    toggle on recipe root, parallel_start checkbox on operations.
  * Step template form — parallel_start checkbox.
  * Simple Recipe Editor (inline library form) — Parallel Start
    checkbox + legacy flag demoted with muted styling + supervisor
    group gate.
  * Recipe Tree Editor (properties panel) — both flags exposed,
    only-show on the right node_type.
  * Controllers updated to allowlist + payload the new fields.

Migration:
  fusion_plating/migrations/19.0.18.12.0/post-migrate.py — sets
  enforce_sequential = TRUE on every existing recipe-root node.
  Idempotent. User confirmed dev-stage data, so retroactive flip
  is safe (no production jobs to disrupt).

Tests:
  TestSequentialEnforcement (10 tests) covering:
    * sequential mode blocks out-of-order start
    * first step always startable
    * predecessor finish/skip unlocks next
    * parallel_start opts out of gate
    * free-flow mode bypasses gate
    * legacy requires_predecessor_done still honoured in free-flow
    * manager bypass via context
    * can_start compute reflects state correctly
    * library template parallel_start snapshots into recipe-node

## Sub 12e — Record Inputs Wizard v3 (card layout, dark-mode aware)

Background:
  v2 wizard was a 17-column wide editable table. Operators got lost
  finding which value column applied to their row's type, horizontal
  scroll required on tablets, composite types crammed into one row.

New layout:
  * Each measurement renders as a stacked card (CSS Grid + display
    transformation on the existing list widget — preserves inline
    editing, no JS rewrite).
  * Card header: prompt name (large, bold) + type/unit pills.
  * Card body: ONLY the value widget for this row's type
    (number / boolean / date / text / photo / multi-point / panel).
  * Composite types (multi-point thickness 5x reading + avg, bath
    panel 4 fields) get inline sub-grid inside the card.
  * Empty state ("no measurement prompts") with friendly CTA.

Dark mode:
  * SCSS branches at compile time on $o-webclient-color-scheme
    (per fusion-plating/CLAUDE.md note).
  * Tokens: 7 surface colours + 4 ink levels with light/dark hex
    pairs, all behind var(--fp-*) custom properties for per-deploy
    override.
  * Registered in BOTH web.assets_backend AND web.assets_web_dark
    so each bundle compiles its own palette.

Tablet polish:
  @media (max-width: 900px) — collapse meta below prompt + bump
  numeric input min-height to 56px.

Defensive:
  * v2 view kept in the XML file (instant rollback by changing one
    view_id ref).
  * `:has(.o_invisible_modifier)` rule drops empty cells out of the
    grid so Odoo's invisible="..." doesn't punch holes in layout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 21:24:12 -04:00
gsinghpal
1da27ed6bf changes 2026-05-01 00:20:40 -04:00
gsinghpal
4213c44e51 feat(simple-editor): node_type bug fix + inline library authoring + back nav
Bucket 1 — Generation bug fix
- post-migrate.py for 19.0.18.8.0 promotes flat 'step' children of
  recipes to 'operation' so fp.job._generate_steps() picks them up.
  Filter is narrow: only direct children of node_type='recipe' get
  flipped, tree-editor sub-steps (parent.node_type='operation') are
  untouched. Idempotent. Posts an audit chatter note on each affected
  recipe.
- Simple Editor controller hardcodes node_type='operation' on insert
  + snapshot-import path so future recipes start correct.

Bucket 2 — Inline library authoring
- 6 new JSONRPC routes (/fp/simple_recipe/library/load + save +
  seed_defaults + input/{add,write,remove}, /fp/simple_recipe/tank/list).
- + New Step button in the right pane opens an inline form with name /
  kind / icon / instructions / stations / flags / prompts table.
- Pencil icon on each library row reopens the same form prefilled.
- Step Kind picker leads with 'Generic — no automatic behaviour'.
- 'Seed defaults from kind' calls action_seed_default_inputs server-side
  for kinds that have curated default prompts.

Bucket 3 — Back nav
- '← Recipes' button in the header (or '← Part' when opened from
  Process Composer) mirrors recipe_tree_editor.js, with
  clearBreadcrumbs:true to avoid stack pollution.

Verified on entech: LGPS1104's 19 'step' children now show as
'operation', migration chatter note posted on the recipe, asset cache
busted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 16:16:14 -04:00
gsinghpal
f990f29019 feat(plating-jobs): state-aware sort + default Open filter on Plating Jobs
The Plating Jobs list was sorted priority-desc, deadline-asc, id-desc — which
mixed Done (closed) jobs in with Confirmed (open) jobs and made the list look
chaotic to managers. Done jobs from weeks ago surfaced above active work.

Two changes:

1. New stored compute fp.job.state_priority (Integer, indexed) ranks states
   by managerial relevance: in_progress=0, confirmed=1, draft=2, on_hold=3,
   done=4, cancelled=5. _order now leads with state_priority asc, then
   priority desc, then date_deadline asc, then id desc. Active work bubbles
   to the top automatically.

2. Plating Jobs action defaults to a new 'Open' filter
   (state not in done, cancelled). Managers see only active work by default;
   they untick the filter to see history. Added On Hold + Cancelled filters
   too for full state coverage.

Verified on entech: top 10 jobs are now all in_progress, sorted by deadline
ascending. Existing 26-row list goes from chaotic to focused.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 23:48:34 -04:00
gsinghpal
ec0a07fbe9 fix(audit-trail): 3 production bugs found via end-to-end Anodize battle test
Battle-tested complete workflow on entech: ABC Manufacturing + Anodize
recipe (id=136) cloned to part-variant (id=1775) → SO S00276 confirmed →
fp.job 1234 with 17 steps → recorded 56 measurement values exercising all
13 input types (incl. all 4 new types) → CoC chronological report renders
69KB with all values incl. photo thumbnails.

Bugs found and fixed:

1. fp.process.node.input_ids missing copy=True — when a master recipe
   was cloned per-part (the standard variant pattern), the operator
   prompts on each step did NOT get copied to the variant. Result: jobs
   built from variants ran with zero prompts even though the master had
   them. Fixed: input_ids now copy=True so cloning auto-duplicates.

2. CoC chronological template read dest.input_ids where dest is
   fp.job.step. Steps don't carry input_ids — that field lives on the
   recipe node. Result: AttributeError aborted the entire CoC render.
   Fixed: walk via dest.recipe_node_id.input_ids; preserves the existing
   collect=True filter.

3. CoC chronological template used hasattr() in a t-value expression.
   QWeb's expression engine doesn't expose Python builtins, raised
   KeyError: 'hasattr'. Fixed: use 'collect' in i._fields instead.

Also enhanced photo rendering in CoC: was just "[Attachment]" placeholder;
now renders an actual <img> thumbnail (max 80px tall) plus the filename.

Battle-test script saved to fusion_plating/scripts/bt_e2e_anodize_v2.py
for re-runs / regression testing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 22:53:59 -04:00
gsinghpal
b187192c58 feat(step-library): full plating workflow coverage + per-recipe configurability + audit
Implements 2026-04-29-step-library-audit-design.md. Bumps fusion_plating
to 19.0.18.7.0, fusion_plating_jobs to 19.0.8.12.0, fusion_plating_reports
to 19.0.10.2.0.

LIBRARY EXPANSION
- 8 new Step Kinds: Receiving, Electroclean, Strike, Salt Spray,
  Adhesion Test, Hardness Test, Packaging, Tank Replenishment
- 4 new input types: photo, multi_point_thickness, bath_chemistry_panel, ph
- DEFAULT_INPUTS_BY_KIND rewritten to seed audit-grade prompts on every
  kind (bath IDs, photos, multi-point thickness, signatures, etc.)
- + Common Audit Fields one-click button on the library template form
- Default Operator Instructions relabel + alert callout

PER-RECIPE CONFIGURABILITY
- collect (Boolean) per recipe-step input prompt — opt out without delete
- collect_measurements (Boolean) master switch on recipe step — when off,
  wizard skips entirely
- template_input_id (Many2one) traceability link from recipe to library
- Recipe-step backend form view exposes the new fields with handle drag,
  toggle, target range, and library-source column

RUNTIME WIRING
- Step input wizard filters node.input_ids to step_input AND collect=True;
  short-circuits on collect_measurements=False
- New input types: photo (image widget + ir.attachment), multi-point
  thickness (5 readings + auto avg, skips empty cells), bath chemistry
  panel (pH/conc/temp/bath bundle), pH (0-14 numeric)
- Composite values JSON-serialized into value_text; photo via attachment

CoC REPORT
- Filters captured prompts to collect=True only
- Renders new input types with appropriate format

MIGRATION (post-migrate.py for 19.0.18.7.0)
- Backfills collect=True on recipe-step inputs
- Backfills collect_measurements=True on recipe steps
- Re-runs action_seed_default_inputs on every existing template
  (idempotent, preserves user edits)
- Backfills template_input_id by name-matching against source library
  template (handles JSONB vs varchar name columns)

SEED DATA
- 8 example templates (one per new kind) in fp_step_template_data.xml
  with noupdate=1

BATTLE TEST
- bt_step_library_audit.py: 29 assertions all PASS on entech

OWL EDITOR EXTENSION DEFERRED
- The simple recipe editor's per-step Instructions/Measurements
  expansions were not implemented in this pass; users configure via the
  backend recipe-step form. Track follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 22:13:54 -04:00
gsinghpal
a2fe1fcbcc changes 2026-04-29 03:35:33 -04:00
gsinghpal
13e300d90e changes 2026-04-28 19:39:37 -04:00
gsinghpal
afcd128f83 fix: rename ambiguous 'Work Center' / 'Work Centres' menus
Two distinct entities were both labelled 'Work Centre' (US/UK spelling
the only differentiator — confusing). Renamed by purpose, model IDs
unchanged so all 12+9 existing cross-refs keep working:

fusion.plating.work.center → 'Production Line'
  Physical shop-layout grouping that owns tanks. Examples: 'Line 1 —
  EN', 'Anodize Line', 'Prep Bay'. Has tank_ids (O2M),
  supported_process_ids (M2M), capacity_per_day. The thing tanks live
  in.

fp.work.centre → 'Routing Station'
  Per-job-step routing entity (post-Sub-11 mrp.workcenter replacement).
  Has 'kind' selection (wet_line / bake / mask / rack / inspect),
  cost_per_hour for fp.job.step rollup, default_bath_id +
  default_tank_id for release-ready validation. The thing a job step
  routes through.

Conceptually a Production Line CONTAINS many Routing Stations (e.g.
'EN Line' production line has wet-line, bake, inspect routing
stations on it).

Updated:
- _description on both models
- string= on the name fields
- list/form/search view strings
- act_window names ('Production Lines' / 'Routing Stations')
- menu items in fp_menu.xml + fp_jobs_menu.xml
- doc comments in both model files explaining the distinction

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:55:39 -04:00
gsinghpal
5f6c7af2a7 feat(phase3): tighten group-gating on operator-irrelevant top-levels
Three targeted gates so operators no longer see admin/audit views:

- KPIs (menu_fp_dashboard) → supervisor+. Operators don't need
  dashboards; their tablet shows what they need to do next.
- Move Log (menu_fp_job_step_move) → supervisor+. Operators see
  their own moves on the tablet; this top-level menu is the
  audit-of-everyone-else view.
- Replenishment Suggestions (menu_fp_replenishment_suggestions) →
  supervisor+. Purchasing decision, not operator concern.

Other top-levels were already correctly gated:
- Sales / Configurator → estimator
- Shipping & Receiving → group_fp_receiving
- Compliance hub → supervisor+
- Configuration → manager
- Shop Floor / Quality → operator (correctly visible to floor staff)
- Operations parent stays open; child menus enforce per-action gates

Net effect: a fresh operator now sees ~5 top-level menus instead of
the previous ~10. Supervisors see ~8. Managers see all.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:51:30 -04:00
gsinghpal
62c1315997 fix(phase2): load fp_menu.xml first so bucket folders exist before refs
fp_rack_tag_views.xml (and several other view files) reference the
new Phase-2 Configuration sub-folder menus (menu_fp_config_*) defined
in fp_menu.xml. Odoo's data loader is strictly sequential within a
module, so fp_menu.xml must come before any file that references
its bucket xmlids.

Caught by entech upgrade (ParseError on rack-tag menuitem).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:46:53 -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
0ad382e1a6 feat(phase1): top-level menu consolidation + landing-page resolver
Phase 1 collapses the Plating app's 17 top-level menus down to 6
domains (Sales, Operations, Receiving & Shipping, Quality,
Compliance, Configuration) so users no longer scroll a 17-item
sidebar to find one thing.

Re-parented (no XML id changes — bookmarks still work):
- fusion_plating_compliance.menu_fp_compliance_root
    → menu_fp_compliance_hub  (renamed 'General')
- fusion_plating_safety.menu_fp_safety_root
    → menu_fp_compliance_hub  (renamed 'Safety / WHMIS')
- fusion_plating_aerospace.menu_fp_aerospace
    → menu_fp_compliance_hub  (renamed 'Aerospace (AS9100 / Nadcap)')
- fusion_plating_nuclear.menu_fp_nuclear
    → menu_fp_compliance_hub  (renamed 'Nuclear (CSA N299 / CNSC)')
- fusion_plating_cgp.menu_fp_cgp
    → menu_fp_compliance_hub  (renamed 'Controlled Goods (CGP)')
- fusion_plating_certificates.menu_fp_certificates
    → menu_fp_quality (Certs are a Quality output, not a separate
      top-level concern)
- fusion_plating_bridge_maintenance.menu_fp_maintenance
    → menu_fp_operations
- fusion_plating.menu_fp_job_step_move (Move Log)
    → menu_fp_operations
- fusion_plating.menu_fp_job_step_timelog (Labor History)
    → menu_fp_operations

The new menu_fp_compliance_hub is supervisor-gated; underlying
verticals retain their own group locks (CGP officer, etc.).

Settings menu remains manager-gated through inheritance from
menu_fp_config (already in place).

NEW — Plating landing-page resolver:
- ir.actions.act_window.x_fc_pickable_landing  (Boolean tag for
  curated picklist; flagged on Sale Orders, Quotations, Process
  Recipes for Phase 1; more in Phase 2)
- res.company.x_fc_default_landing_action_id (admin sets fallback)
- res.users.x_fc_plating_landing_action_id (per-user override)
- ir.actions.server action_fp_resolve_plating_landing — picks
  user → company → Sale Orders fallback at click time
- menu_fp_root rewired to call the resolver
- User profile + Settings tabs surface the dropdowns

Configurator's earlier menu_fp_root override (action_fp_sale_orders
direct) removed — core's resolver now owns the routing.

Versions bumped: fusion_plating 19.0.11.0.0, configurator
19.0.17.16.0, plus 7 dependent modules patched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:33:37 -04:00
gsinghpal
3098fcfaf9 feat(sub12a+): drop-position simulator in Simple Recipe Editor
Replaces the per-row 'highlight whole row' feedback (operator
couldn't tell whether the new step would land before or after the
hovered row) with a precise insertion-point indicator.

How it works:
- Each row's onDragOver computes ev.clientY vs row midpoint.
  Above midpoint → insertion index = rowIndex (BEFORE).
  Below midpoint → insertion index = rowIndex + 1 (AFTER).
- An <o_fp_drop_indicator> div lives BEFORE the first row and
  AFTER every row. When state.dragOverIndex matches that slot's
  index, the div expands from height:0 to a 2.25rem dashed-green
  reservation strip with a ghost-preview chip ('↓ insert here →
  <icon> <step name>').
- onDragStart captures the dragged step/template's name + icon
  into state.dragPreviewLabel/Icon for the chip text.
- Smooth 80ms height/margin transition so the line glides between
  slots as the cursor moves rather than blinking.
- Trailing dropzone retains its existing 'Drop here to add at end'
  styling. Empty list shows 'Drag a library step here to start'.
- onDrop reads from state.dragOverIndex (set by the most-recent
  onDragOver) so we drop at the simulated position exactly.
- onDragLeave guards against child-element flicker via
  relatedTarget contains() check.
- onDragEnd clears state.dragPreviewLabel/Icon so a half-completed
  drag (cancelled by Esc) doesn't leave the chip stuck on screen.

fusion_plating → 19.0.10.4.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:08:51 -04:00
gsinghpal
7d3b8f132a fix(sub12c+): close 3 known gaps — rack travel ticket, cert statement, CoC actuals
Gap 1 — Rack Travel Ticket PDF (Sub 12b's Save+Print 404):
  + report_fp_rack_travel.xml in fusion_plating_reports — A5 landscape
    single page, big rack name, Code 128 of FP-RACK:<name>, tag chips,
    contained part-batches table.
  + ir.actions.report bound to fusion.plating.rack so it appears in
    the rack form's Print menu too.
  + Sub 12b's rack_parts_dialog.js Save+Print URL fixed to use the
    standard /report/pdf/<xmlid>/<id> route.

Gap 2 — Per-customer cert statement:
  + res.company.x_fc_default_cert_statement (company-level fallback).
  + res.partner.x_fc_cert_statement (per-customer override).
  + Surfaced on the partner form under the existing Cert + Document
    Routing block.
  + Chronological CoC body resolves: customer override → company
    default → hardcoded AS9100/ISO 9001 boilerplate. Three-tier
    fallback so existing certs without overrides keep working.

Gap 3 — Chronological CoC 'Actual' column:
  + Build a captured_values_by_input dict from the move's
    transition_input_value_ids (Sub 12b captures these on every
    Move Parts commit).
  + Render typed Actual: text → as-is, number → with target unit,
    boolean → PASS/FAIL, date → formatted, attachment → '[Attachment]'
    placeholder.
  + Falls back to prompts from the destination step's step_input list
    when no values were captured (still useful as audit-of-what-was-
    asked even if blank).

Version bumps:
  fusion_plating → 19.0.10.3.0
  fusion_plating_reports → 19.0.10.1.0
  fusion_plating_certificates → 19.0.5.3.0

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:55:48 -04:00
gsinghpal
f55193fb1b feat(sub12c): bump versions + manifest scaffolding (Task 1)
fusion_plating → 19.0.10.2.0 (Labor History views)
fusion_plating_jobs → 19.0.7.0.0 (Operator Traveller v2)
fusion_plating_reports → 19.0.10.0.0 (Chronological CoC body)

Adds data entries for the 2 new XML files (timelog views + coc
chronological).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:37:24 -04:00
gsinghpal
d78ef4228e feat(sub12b): bump versions + scaffold manifests
fusion_plating → 19.0.10.1.0
fusion_plating_shopfloor → 19.0.25.0.0

Adds data entries for fp_rack_tag_views.xml + fp_job_step_move_views.xml.
Adds 4 OWL dialogs + their templates + shared SCSS to the shopfloor
backend asset bundle (loaded after the existing manager_dashboard.js).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:00:18 -04:00
gsinghpal
7e98b48c01 feat(sub12a): bump fusion_plating to 19.0.10.0.0 + scaffold manifest
- Adds views/fp_step_template_views.xml to data list (after process_node_views).
- Adds simple_recipe_editor.{js,xml,scss} to web.assets_backend bundle.
- res_config_settings_views.xml + post_init_hook already wired — extend in place.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 20:29:37 -04:00
gsinghpal
cfe776be4c chore: session housekeeping — tank UX, plating menu defaults, WO label
- fusion_plating: tank field labels (Code → Tank Number, Tank → Tank Name)
  + state-control header buttons (Mark Empty/Filled/In Use/Draining/
  Maintenance/Out of Service) with chatter audit logging.
- fusion_plating_configurator: Plating app default landing screen = Sale
  Orders, while keeping menu name as 'Plating'.
- fusion_plating_jobs: SO smart-button label 'Plating Jobs' → 'WO'.

Already deployed and verified on entech earlier in the session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 20:27:35 -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
1491f2367b refactor(jobs): address code review feedback on Task 1.8 admin views
- I1: Lock time_log_ids list inside step Audit tab to read-only
  (no create/edit/delete on the nested list). Audit timelog rows
  are produced exclusively by button_start / button_finish; if a
  manager could hand-edit them, cost_total rollups would silently
  drift.
- I2: Add explicit list view (decoration on state) and search view
  (filters by state/kind, group_by state/work_centre/job) for
  fp.job.step. The Steps (Admin) menu was using Odoo's default
  auto-list with no filter, which would be unusable after a few
  weeks of step accumulation. Action now references the search
  view explicitly.

Manifest 19.0.8.7.0 -> 19.0.8.7.1.

Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:44:16 -04:00
gsinghpal
b2ae79b61f feat(jobs): add admin views and menu for Phase 1 models
Manager-only views during Phase 1 — operator UI rebuilt in Phase 6.
Top-level menu 'Plating Jobs (new)' (seq=47) groups the three
new act_window actions (Jobs, Steps Admin, Work Centres) so the
foundational models can be exercised through the UI without
touching the operator-facing menus that still serve mrp.production
and mrp.workorder.

Job form has Steps/Source/Costs notebook tabs. Step form has
Equipment/Plating Spec/Audit/Instructions tabs (Audit shows
the time log rows from Task 1.7). Search filters by state,
priority, partner, facility.

Manifest 19.0.8.6.1 → 19.0.8.7.0.

Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:38:36 -04:00
gsinghpal
54068b3d18 refactor(jobs): address code review feedback on fp.job.step.timelog (Task 1.7)
- I1: Add supervisor ACL row on fp.job.step.timelog. The other
  job-related models all have a 3-tier (operator/supervisor/
  manager) pattern; timelog had been operator+manager only with
  no comment explaining why. Adding the supervisor row for
  consistency — supervisors can write timelogs the same as
  operators.
- I2: Capture fields.Datetime.now() once in button_start and
  reuse for both the step's first-start audit timestamp and the
  new timelog's date_started. Mirrors button_finish's pattern.
  Eliminates microsecond drift between two related timestamps.
- M6: Update stale comment in fp_job_step.py that said
  'duration_actual will be sum of timelog rows once Task 1.7
  lands' — Task 1.7 has landed; replace with current behaviour.

Manifest 19.0.8.6.0 → 19.0.8.6.1.

Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:34:10 -04:00
gsinghpal
28892f56b5 feat(jobs): add fp.job.step.timelog for granular timer tracking
Each button_start opens a fresh timelog row; button_finish closes
the open row and recomputes step.duration_actual as the sum of all
interval durations. Replicates Odoo MRP's mrp.workorder.time_ids
granularity natively (no mrp dep).

Schema: step_id (M2O cascade), user_id, date_started,
date_finished, duration_minutes (computed, stored).

ACLs: operator get create permission on timelogs because
button_start creates them.

Tests: test_start_creates_timelog (asserts the log row exists,
date_finished is False, user_id is the current user) and
test_finish_closes_timelog (asserts log gets date_finished, has a
non-negative duration, and step.duration_actual matches).

Manifest 19.0.8.5.1 -> 19.0.8.6.0.

Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:27:26 -04:00
gsinghpal
57a3aea16f refactor(jobs): address code review feedback on fp.job.step (Task 1.6)
- I1: Replace 'Task 1.6' markers in stub method comments and
  NotImplementedError messages with forward-looking phrasing.
  Task 1.6 is what just shipped (the field expansion); the action
  stubs are deferred to an unspecified future task. Stale markers
  would have confused future readers/operators.
- I2: Add test_cost_total_recomputes_when_rate_changes — insurance
  test that verifies @api.depends('cost_per_hour') triggers through
  the related-from-work_centre chain. Catches future Odoo upgrades
  that break related-depends.

Manifest 19.0.8.5.0 → 19.0.8.5.1.

Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:22:15 -04:00
gsinghpal
91767f9f03 feat(jobs): add equipment, audit, plating-spec fields to fp.job.step
Equipment: bath_id, tank_id, rack_id (all in core). oven_id deferred
to a bridge module — fusion.plating.bake.oven lives in shopfloor and
core can't depend on it. masking_material_id deferred too — model
fusion.plating.masking.material does not yet exist anywhere; will be
added when the masking model lands.

Audit: signoff_user_id (readonly), facility_id (related from
work_centre_id, stored).

Plating spec: thickness_target, thickness_uom (um/mil/in),
dwell_time_minutes, bake_setpoint_temp, bake_actual_duration,
bake_chart_recorder_ref (Nadcap audit trail).

Recipe-related: requires_signoff, auto_complete, is_manual,
customer_visible (all related from recipe_node_id, stored, so
operator sees current values without re-querying process.node).

Cost rollup: cost_per_hour related from work_centre_id, cost_total
computed (duration_actual / 60 x rate), currency_id related too.
Full rollup-from-timelogs lands in Task 1.7.

Tests cover: facility_id related-field, thickness_uom default,
cost_total zero/non-zero paths.

Manifest 19.0.8.4.1 -> 19.0.8.5.0.

Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:15:26 -04:00
gsinghpal
688fe8317c refactor(jobs): address code review feedback on fp.job.step (Task 1.5)
- I2: Add TODO comment block + stub button_pause/button_skip/
  button_cancel that raise NotImplementedError. Makes the missing
  state-machine paths explicit instead of invisible gaps. Future
  Task 1.6 wires the real implementations; shop-floor buttons in
  Task 1.8 can already point to the right method names.
- I3: button_finish now preserves first-finish audit timestamp
  via 'if not step.date_finished:' guard, mirroring button_start.
  Future rework flow that re-opens a step won't lose the original
  finish data. The duration_actual rollup landing in Task 1.7 will
  use timelog rows for the most-recent interval if needed.
- I4: step_count and step_done_count are now store=True so list
  views and stat buttons (Task 1.8) don't recompute across all
  job rows on every render. step_progress_pct and current_step_id
  stay non-stored - they're cheap derivatives. Split compute methods
  so stored + non-stored fields don't share one method (Odoo flags
  the mix as inconsistent and recomputes stored fields whenever the
  non-stored one is read, defeating the perf gain).

Manifest 19.0.8.4.0 -> 19.0.8.4.1.

Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:09:16 -04:00
gsinghpal
c41a488b58 feat(jobs): add fp.job.step model with state machine
Per-operation model replacing mrp.workorder for plating. Each step
instantiates from a recipe operation node (recipe_node_id link).
Container/step nodes from the recipe template are rendered at view
time via that link — they don't get rows here. See spec §5.2
Option A.

7-state machine: pending → ready → in_progress → done, plus
paused, skipped, cancelled. button_start/button_finish enforce
the transitions.

Job header gets step_ids + step_count, step_done_count,
step_progress_pct, current_step_id (computed from steps).

Equipment, audit fields, plating-spec fields, time logs, and
release-ready validation come in Tasks 1.6 and 1.7.

Manifest 19.0.8.3.1 → 19.0.8.4.0.

Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 21:59:07 -04:00
gsinghpal
e4111ad000 refactor(jobs): address code review feedback on fp.job (Task 1.4)
- Move NOTE comment from between @api.depends and the function
  into a proper docstring on _compute_costs.
- Make currency_id required=True (default still in place — only
  a programmatic write would null it, but Monetary fields need a
  non-null anchor).
- Add test_current_location_for_confirmed to cover the title-case
  fallback branch in _compute_current_location.

Manifest 19.0.8.3.0 → 19.0.8.3.1.

Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 21:51:23 -04:00
gsinghpal
335dc2488e feat(jobs): add core-safe extension fields to fp.job
Scope was reduced from spec's full §5.1 list because 6 of the 10
plating-specific fields point to models in dependent modules
(configurator, quality, portal, logistics, bridge_mrp). Adding
those Many2ones in core would invert the dependency graph. They
move to their owning modules via _inherit = 'fp.job' and get
re-bundled by fusion_plating_jobs in Phase 2.

This commit lands the core-safe subset:
- sale_order_line_ids (sale_management is in core depends)
- recipe_id, start_at_node_id (process.node is in core)
- invoice_ids (account is reachable via sale_management → sale)
- Cost rollup: quoted_revenue / actual_cost / margin / margin_pct
  with placeholder compute (actual_cost = 0 until Task 1.5 wires
  fp.job.step.cost_total)
- current_location stub (full Bath/Oven rendering in Task 1.6)

Tests cover the cost-rollup math and the current_location stub.
Spec §5.1 has been re-tabulated with explicit 'Module' column.

Manifest 19.0.8.2.1 → 19.0.8.3.0.

Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 21:44:28 -04:00