- 'Customer Project' plan (renamed from 'Project' to avoid duplicate with
project module's auto-created plan) — mandatory
- 'Department' plan (mandatory) — seeded with DEPT-DEV, DEPT-SALES,
DEPT-ADMIN, DEPT-HOSTING
- 'SR&ED Tag' plan (optional) — seeded with 7 tag values:
SRED-T4-DEV-SALARY, SRED-SPECIFIED-EMPLOYEE,
SRED-CONTRACTOR-CA-ARM-LENGTH, SRED-CONTRACTOR-CA-NON-ARM-LENGTH,
SRED-MATERIALS-CONSUMED, SRED-OVERHEAD-PROXY-BASIS, NOT-ELIGIBLE
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bakes the staging-side one-off collision clearing into the module install
itself so production install will execute the same sweep automatically.
For each of the 29 l10n_ca codes that conflict with Nexa's planned chart:
- If the account has zero postings: suffix code with '.OLD', mark inactive,
rename to '(l10n_ca LEGACY) <original>'
- If the account has postings (currently 115100 AR control with 240 lines
and 511100 Inside Purchases with 1 line): leave alone (Nexa renumbered
to 119100 / 511105 in the XML)
Idempotent — pre_init_hook re-running has no effect (already-suffixed
codes are skipped).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The template used 't-field="step['rate']"' for monetary values pulled
from dict rows. Odoo 19's QWeb asserts t-field has at least one dot
(it's strictly for record.field_name lookups). Replaced six bare-dict
t-field usages with t-esc; the existing t-options widget=monetary +
display_currency still applies for currency formatting.
Verified by rendering report for WO-30019 — 1229-byte valid PDF, no
QWeb error.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Renumbered to avoid collisions with pre-loaded l10n_ca codes:
- Due From Shareholder/Associated: 115xxx → 119xxx range (115100/115110 already
held l10n_ca AR control accounts with 240 postings)
- Cloud Infrastructure: 511100 → 511105 (511100 was l10n_ca 'Inside Purchases'
with 1 historical posting)
All other 28 colliding l10n_ca codes (118xxx, 213xxx, 214xxx, 221xxx, 311xxx,
411xxx, 413xxx, 511110-511210, 512100-512200, 611100-300, 612xxx) had zero
postings and were cleared in-place by suffixing existing codes with '.OLD'
via a one-off odoo-shell script on staging.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The post_init_hook attempt to set fiscalyear_lock_date=2025-12-31 fails
with RedirectWarning when unreconciled bank statement lines exist in
the period. Catch RedirectWarning/UserError/ValidationError, log a
clear instruction to set the lock manually after reconciliation, and
let install continue.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 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>
Bite-sized task plan to implement the CoA design against odoo-nexa
nexamain database. 12 phases:
0. Safety backup + staging clone
1. Module skeleton (nexa_coa_setup)
2. Chart of accounts (~110 new accounts across 1-6xxxxx)
3. Analytic plans (Project, Department, SR&ED Tag)
4. Hooks for archive-unused / rename-legacy
5. Tax cleanup
6. 8 fiscal positions with auto-detect
7. Service product categories
8. Westin/Divine partner records (RP-Associated tag)
9. 8 bank reconciliation rules
10. End-to-end test invoices (ON, US, intercompany)
11. Apply to production (with explicit GO/NO-GO gate)
12. Operating runbook
Each task has a verify-before / change / verify-after / commit cycle.
Staging clone (nexamain_staging) used for every phase before prod.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
_clone_subtree() in fp_part_composer_controller built node vals
manually and never copied source.input_ids — so 'Load Template'
copied the recipe tree structure but dropped every custom prompt,
leaving operators with empty data-capture screens. The fix iterates
input_ids and calls .copy({'node_id': new_node.id}) so kind,
target_min/max/unit, compliance_tag, hint, selection_options,
sequence — every field on the input model — flows through.
Verified on entech: ENP-ALUM-BASIC clone now shows all 105 prompts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Verified pass on entech (parent=30015): all linked docs share the
parent number, immutability + unlink-block + direct-invoice-block
all enforced. NCR/CAPA fall back to legacy sequences as designed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
short_wo now handles both naming schemes: new WO-NNNNN[-NN] (strips
WO-) and legacy WH/JOB/NNNNN (last slash segment). Customer-facing
Work Order column shows '30000-02' instead of 'WO-30000-02'.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A small grey 'Originally quoted as Q202605-200' line appears below
the SO heading once the order is confirmed. Uses invisible= on the
wrapper div (Odoo 19 forbids t-if in standard form views).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
Per-model counter fields on sale.order renamed to x_fc_pn_*_count
to avoid collision with pre-existing compute fields of the same
short name in bridge_mrp / receiving / configurator (silent
compute-override was suppressing the storage). 4 child models
(fp.certificate, fp.receiving, fusion.plating.delivery,
fusion.plating.pickup.request) now derive names as PFX-<parent>
with -NN suffix from the 2nd onward.
fusion.plating.pickup.request gains a sale_order_id field
(optional) so pickups created against an SO get parent-derived
names, while standalone pickups (pre-SO) fall back to PU/YYYY/NNNN.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Customer invoices (out_invoice / out_refund) can only be created via
sale.order._create_invoices() or with an invoice_origin matching an
existing SO. Applies to ALL users including admins. Once created,
the move's name is derived from the SO's parent number: IN-30000,
IN-30000-02, CN-30000, ... Pre-existing portal-job link on
action_post() preserved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures user-confirmed CRA registration & filing setup:
- Annual GST/HST filer (return Mar 31, instalments if prior net tax ≥ \$3k)
- Annual T2 filer (return Jun 30, balance due Mar 31 for CCPC)
- HST# 741224877 currently stored as 9-digit BN root only; normalize to
full 15-char '741224877 RT0001' for tax-report validation
- Quick Method opportunity downgraded — \$400k threshold applies to
associated-group totals; Nexa+Westin+Divine combined likely exceeds it
- Add HST cadence escalation flag (quarterly auto-trigger at \$1.5M)
- Acceptance criteria expanded with HST# format, filer config, and
intercompany invoice test case
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restructure Section 9 to handle Westin Healthcare Inc and Divine Mobility
Inc as Gurpreet's associated corporations (ITA s.256):
- Future intercompany flows go through normal AR/AP with partner records
tagged 'RP-Associated', not slush 'Due to/from' GL buckets
- 'Due to/from Associated Corporations' now reserved only for true
intercompany loans (no invoice)
- Surface SBD $500k sharing and SR&ED $3M sharing rules; Schedule 23
allocation drives major annual tax decisions
- Manpreet account archived (employee of another corp, not Nexa-related)
- Add transfer-pricing risk flag (ITA s.247, 10% penalty)
- Add multi-company Odoo as future sub-project
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
Comprehensive chart-of-accounts redesign for odoo-nexa nexamain DB:
hybrid approach over l10n_ca, three analytic plans (Project/Department/SR&ED
Tag), fiscal positions for auto tax handling, cleanup plan for the
~370 unused accounts and 49 messy taxes, automation hooks via product
categories and bank reconciliation rules.
Goals: CRA compliance, SR&ED claim infrastructure, zero-rated export
handling, one-click invoicing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Quote→SO→WO→IN→CoC→DLV→RCV→… all share a single parent number drawn
from the sale order. New abstract mixin centralises naming with atomic
counter increment, compliance-grade immutability, and a hard block on
direct invoice creation outside the SO workflow.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two issues on the Process Tree client action:
1. Back to Work Order kept growing breadcrumbs (WO -> Tree -> WO ->
Tree -> ...) because onBack used action.doAction() which PUSHES
a new act_window onto the stack instead of popping. Fixed by
trying action.restore() first (pops the Tree off the stack and
returns to the parent WO/Step controller). Falls through to
explicit doAction only when there's no parent in the stack
(direct URL access).
2. The empty-state banner referenced productionId, a dead variable
from the bridge_mrp era when the tree was tied to mrp.production.
Since the component now uses jobId (fp.job context key), the
"No manufacturing order selected" message ALWAYS fired regardless
of whether a job was loaded. Fixed by using jobId and updating
the message to "No work order selected".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
Two improvements to the Process Tree visualization opened from the
Work Order's Process Tree header button:
1. Back button returns to the Work Order (job form) instead of
Plant Overview. fp.job.action_open_process_tree now passes
back_job_id in the client-action context; process_tree.js
reads it via a new backJobId getter, updates the button label
to "Back to Work Order", and routes onBack to fp.job form.
The Plant Overview fallback stays for callers that don't pass
either back_step_id or back_job_id.
2. Completed operation/step cards now have a green fill (#1e8449)
and a subtle pulsing glow (box-shadow animation, 2.6s alternate)
so finished work pops against still-pending dark cards. Hover
pauses the animation so the click target is steady. Reuses the
same green the workflow-state slice already used.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User wanted Finish & Next to drop its text label like the other row
buttons, but stand out visually as the primary action. Solution:
icon-only with a vivid green color and a subtle pulse animation.
- New SCSS: fp_finish_btn.scss with branch-on-$o-webclient-color-
scheme so the dark bundle uses green-400 (pops on dark bg) and
light bundle uses green-600. Pulse animation 1.8s ease-in-out
infinite, scale 1.0 ↔ 1.18. Pauses on hover/focus so the click
target is steady.
- Registered in both web.assets_backend and web.assets_web_dark
per the project's dark-mode rule (CLAUDE.md).
- View: string="Finish & Next" → title="Finish & Next",
class drops "text-primary", gains "o_fp_finish_btn".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
UI: secondary row buttons in the embedded step list are now icon-only
with tooltips (Pause / Complete 1 → Next / Record / Skip / Move…).
Saves ~70% horizontal space. "Finish & Next" stays text+icon as the
primary action.
Fix: removed the racking-inspection gate from button_finish. Racking
is now a recipe step (not a separate inspection workflow), so the
"Racking inspection for ... is Inspecting — must be Done" error no
longer fires. _fp_check_racking_inspection_complete() helper is
preserved for diagnostics but no longer called from the finish path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User feedback: operators with small parts (e.g. valve bodies) batch
them through the whole recipe. The previous behavior — Finish & Next
raising "use Complete 1 → Next or Move..." when qty>1 — forced N
clicks for a workflow that's naturally one click.
Change: _fp_record_one_piece_auto_move now ALWAYS bulk-moves
qty_at_step parts to the next step in one move record, regardless of
whether the qty is seed-only (first / paperwork step) or real (parked
from an upstream move). Audit trail is preserved (one move row per
finish), operator gets one click.
Three buttons now map cleanly to the three workflows:
- Finish & Next: bulk all parts forward, finish, auto-start next
- Complete 1 -> Next: streaming flow, move 1 part, stay open
- Move...: explicit qty + destination wizard for partial batches
Verified end-to-end on entech: seed qty=6 + real-incoming qty=6 both
move forward in a single click each.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
The Received milestone was tied to recipe steps tagged
default_kind='receiving'. But receiving in this system is a pre-
recipe inbound logistics flow (fp.receiving model in
fusion_plating_receiving). When parts physically arrive, the flow
sets sale_order.x_fc_receiving_status to partial or received.
Changes:
- New trigger_on_parts_received Boolean on fp.job.workflow.state.
- _fp_is_passed_for_job branch: passes when sale_order's
x_fc_receiving_status is in (partial, received).
- _compute_workflow_state_id depends extended with
sale_order_id.x_fc_receiving_status so the bar recomputes
automatically when the receiving flow updates the SO.
- DB seed update: Received state drops trigger_default_kinds=
'receiving' and gains trigger_on_parts_received=True.
Verified end-to-end on entech: bar moves Confirmed → Received on
status change, regresses on rollback, accepts both 'partial' and
'received' as satisfying the milestone.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two coupled fixes so the workflow bar shows "In Progress" when work
is actually underway, even on recipes without kind tagging:
B. Auto-promote fp.job.state on first step start.
fp.job.step.write hook detects step transitions to in_progress
and promotes parent job state from 'confirmed' → 'in_progress'.
Without this, fp.job.state never reached 'in_progress' anywhere
in the codebase, so the trigger_on_job_state='in_progress'
path was dead code.
C. Smarter trigger_first_step_started for untagged recipes.
For tagged recipes (any step has kind in wet/bake/mask/rack),
keep the strict kind-based check. For untagged recipes (all
steps kind='other' or similar), fall back to "any step in
in_progress/paused/done" so the milestone fires regardless of
recipe authoring quality.
Verified end-to-end on entech with untagged steps:
- confirmed → in_progress when first step starts
- workflow bar tracks at in_progress through the work
- workflow bar advances to done when all steps done/skipped
Recipe authoring still encouraged for full Received / Inspected
intermediate states (those keep their default_kind triggers).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root cause: two compounding bugs in fp.job.workflow_state_id.
- The "Confirmed" state seed has no trigger fields set, so it
never passes _fp_is_passed_for_job.
- The _compute_workflow_state_id loop breaks on the first
non-passed state — so when Confirmed fails, every later
state stays unevaluated and the bar is stuck at Draft.
Fixes:
- Add trigger_on_job_state Selection field on fp.job.workflow.state
with values confirmed/in_progress/done. Passes when fp.job.state
>= the chosen value ("at least" semantics with explicit ordering
that treats on_hold==in_progress and cancelled outside the
progression). Lets workflow states key off the job's own state
when recipe default_kind tagging isn't present.
- Extend _fp_is_passed_for_job with the new branch.
- Change _compute_workflow_state_id from first-non-pass-breaks to
highest-passed-wins. Untagged/not-applicable states no longer
block the cascade — the bar shows the furthest milestone the
job has actually reached.
- Seed update (DB-side, since data is noupdate=1): Confirmed now
has trigger_on_job_state='confirmed'.
Result: Work Order # 00011 (state=confirmed, all 11 steps done/
skipped) now correctly shows the bar at "Done" instead of "Draft"
(via the existing trigger_all_steps_done on Done). Mid-flight
confirmed jobs without recipe tagging will show at least
"Confirmed" now.
Recipe authoring note (out of scope here): for accurate Received /
In Progress / Inspected intermediate states, recipe nodes still need
default_kind tagging (receiving / wet|bake|mask|rack / final_inspect).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
7-task plan: display rename (compute + view), qty gate on
button_finish with last-step exemption, action_complete_one_to_next
row button, auto-move shim on Finish & Next, view additions,
end-to-end smoke test, and repo sync-back.
14 unit tests in the existing TestQtyGate class covering all five
state-machine branches plus display-name format and Move wizard
zero-qty regression.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three coupled shop-floor corrections:
1. Job display rename: WH/JOB/00011 -> Work Order # 00011
via display_name compute (name stays stable for DB refs)
2. Quantity gate on button_finish: refuses if qty_at_step > 0
AND there is a downstream pending/ready step (last step exempt)
3. Partial-qty UX: new action_complete_one_to_next per-row button
for streaming flow; auto-move shim on Finish for 1-of-1; Move
wizard unchanged (already has zero-qty + over-qty guards)
Spec covers architecture, state transitions, test plan,
files-touched matrix, and explicit Out of Scope (qty_done auto-tick,
per-step scrap, cert PDF display).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements the milestone-cascade design (Phase 1) and catches the
fusion_plating_jobs / fusion_plating_certificates source up to entech.
Milestone cascade (this PR's core):
- fp.job: new computes all_steps_terminal, next_milestone_action,
next_milestone_label; dispatcher action_advance_next_milestone with
3 helpers (_action_open_draft_certs, _action_open_draft_delivery,
_action_mark_active_delivery_delivered); _resolve_required_cert_types
resolver; _fp_create_certificates rewritten to honour
part.certificate_requirement + partner flags + loop over resolved
cert types
- fp.job.workflow.state: new trigger_on_delivery_state Boolean;
_fp_is_passed_for_job extended with delivery-state branch;
Shipped state seed reroutes from default_kind=ship to the new trigger
- View: hide Finish & Next when all_steps_terminal; add 4 mutually-
exclusive milestone buttons (Mark Job Done / Issue Certs / Schedule
Delivery / Mark Shipped) bound to one dispatcher
- Cert gate (fusion_plating_certificates/models/fp_delivery.py):
action_mark_delivered hard-blocks on draft certs; manager bypass
via fp_skip_cert_gate=True context key
- 24 unit tests in test_fp_job_milestone_cascade.py covering computes,
resolver, dispatcher, cert gate
- Spec: docs/superpowers/specs/2026-05-12-job-milestone-cascade-design.md
- Plan: docs/superpowers/plans/2026-05-12-job-milestone-cascade.md
Other entech changes caught up in this sync (from earlier session
patches not previously committed):
- fp.job version bump series 18.x → 19.0
- res_users_views.xml addition (signature widget in user prefs)
- racking inspection smart button removal
- various view/manifest touch-ups
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces per-step Finish & Next with a context-aware milestone-advance
button cycling Mark Job Done → Issue Certs → Schedule Delivery →
Mark Shipped. Architecture, cascade, gates, files-touched, and the
cert-gate hard-block decision are all captured for implementation
planning.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>