Commit Graph

1530 Commits

Author SHA1 Message Date
gsinghpal
71f4c41d5c merge: NexaCloud->Odoo billing cutover (spec + plan00 hermetic suite + plan01 cancel endpoint) 2026-06-02 09:17:43 -04:00
gsinghpal
2f6a8b33a9 docs(billing): CLAUDE.md centralized-billing + test-harness section; plan-01 note
Document fusion_centralize_billing as the Lago-superseding billing engine and the
isolated odoo-nexa test recipe (fresh DB + l10n_ca; never -u against live nexamain;
log_level/workers gotchas). Plan-01 doc: corrected the unsafe test command + added the
harness section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 09:17:41 -04:00
gsinghpal
4b832e7445 Update 2026-06-02-nexacloud-cutover-01-odoo-cancel-endpoint.md 2026-06-02 09:13:35 -04:00
gsinghpal
f67cefc213 feat(billing): _api_cancel_subscription service method + unit tests
Plan 01 (NexaCloud cutover) Task 1: cancel/close a subscription with the same
service-scoped authorization as _api_record_usage (resolve via
_fc_resolve_subscription; partner must be linked to this service). Idempotent
(no-op if already 6_churn). 5 unit tests, verified green on fcb_test
(fresh + l10n_ca). DELETE route + HttpCase follow in Task 2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 09:08:37 -04:00
gsinghpal
658611457e docs(CLAUDE.md): westin clone-verify recipe + orphaned-tax-FK trap + fusion_portal note
Capture the operational knowledge from the fusion_portal assessment-visit deploys:
the isolated _test addons-path clone-verify technique, the orphaned-tax-FK restore
trap (and the proof that prod -u is safe without touching the orphans because Odoo
skips a present FK), the backup/stage/swap/-u/cache-bust deploy flow with restart
gating, the surgical branch->main merge for branches that predate other merges, and
a fusion_portal module note (ENTERPRISE-only; visit funding-grouping architecture).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 09:08:33 -04:00
gsinghpal
4df35448c2 docs(fusion_plating): partial-order rollout fixes + open-items handoff
Consolidated handoff added to the Partial Order Handling section: the bugs
that only live tablet testing surfaced (phantom stage cards, scan-button
icons/labels, dark-mode undefined --bs-* vars, from-step predecessor block,
seeded-stage auto-finish on drain, gating fall-forward) and the open items
(discoverability badges, Scrap/Rework standalone buttons, automated tests
not written, dark-mode chip polish). Docs only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 09:06:53 -04:00
gsinghpal
1d6797f0d2 Merge fusion_repairs maintenance foundation (Plan 1) + 2 install fixes + CLAUDE.md rule 17 into main 2026-06-02 09:03:17 -04:00
gsinghpal
4622521729 docs(CLAUDE.md): Odoo 19 url_encode-in-mail-template rule + corrected fusion_repairs note
Rule 17: url_encode (and werkzeug url helpers) are not in the Odoo 19 mail.template QWeb render context -> opaque 'issue with this value' ParseError at install. fusion_repairs note corrected: NOT Community-installable (Enterprise ai+knowledge via fusion_portal->fusion_claims); test on the westin-fr-test Enterprise sandbox; --workers 0 + log_level=warn test-runner gotchas; noupdate templates load on fresh install only. Version 19.0.2.3.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 09:02:27 -04:00
gsinghpal
40a29081bf fix(fusion_portal): readable gradient for Start-a-Visit tile (live westin 19.0.2.10.1)
Inline tile gradient (no !important) was overridden by the theme .card rule,
rendering near-white with invisible white text. Dedicated .portal-visit-card
class (blue->indigo, distinct from the green New Assessment tile).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 08:59:59 -04:00
gsinghpal
11ab261ad9 test(billing): make fusion_centralize_billing suite hermetic (green baseline)
- test_usage / test_webhook setUp: get-or-create the cpu_seconds metric and
  nexacloud service so the suite no longer collides with existing rows.
- test_invoice_ledger: add _fc_ensure_ca_billing_env (activate CAD + a 13%
  sale tax matching _fc_tax_for) so the ledger tests pass on a clean DB.

Canonical test DB: a FRESH db with l10n_ca installed (a prod clone collides
on fixed-code fixtures across 5 test files). Full suite now exits 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 08:59:31 -04:00
gsinghpal
00f7e90a3d feat(fusion_repairs): maintenance foundation - policy + priced auto-contracts on sale (Plan 1)
Plan 1 of fusion_maintenance, verified on the Westin Enterprise sandbox (westin-fr-test) via odoo shell. Maintenance policy (enabled/interval/flat fee/service product) on the equipment category + per-product fee override; contract gains fee/source/serial/policy/currency; fixed the dead _spawn_maintenance_contracts and wired it into the existing action_confirm (delivery-date anchor w/ fallback, two-regime serial dedup, fee resolution product->category); reminder email shows the flat fee; category form exposes the policy. Verified: trigger creates 1 priced contract (fee 149, next_due commitment+6mo, source=sale); idempotent on re-confirm; product override beats category; no contract when category not maintainable; fee renders as $149.00. v19.0.2.3.0.

NOTE: mail_template_data.xml is noupdate=1 -> the fee line loads on fresh install (the prod deploy) but NOT on -u of an already-installed system. The Westin prod-config test container (workers + log_level=warn) does not run --test-enable post_install tests (a pre-existing module load issue under the test phase), so behaviour was verified by odoo shell instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 08:55:49 -04:00
gsinghpal
859a327738 fix(fusion_plating_jobs): gating steps fall forward to next stage's column
A "Ready for X" gating step (fp.step.kind code='gating') maps to
area_kind='receiving' in the taxonomy. For a MID-recipe gate (e.g.
"Ready for processing" between Racking and Plating) that snapped the
job's card back to the far-left Receiving column when work advanced into
it — the job looked like it vanished from the board.

_compute_area_kind now detects gating via the stable kind code and
resolves a gating step's column to the NEXT non-gating step's area (so
"Ready for processing" shows in Plating), keeping cards flowing
left→right. Falls back to the last real stage for a trailing gate.
Non-gating steps unchanged. Helpers: _fp_is_gating_step / _fp_raw_area_kind
(no recursion) / _fp_resolve_area_kind.

area_kind is a stored compute — recomputed all 537 live steps on entech.
Verified: WO-30061 "Ready for processing" area receiving→plating, card now
renders in the Plating column.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 08:53:47 -04:00
gsinghpal
a52f2bbebd fix(fusion_plating_jobs): gating steps fall forward to next stage's column
A "Ready for X" gating step (fp.step.kind code='gating') maps to
area_kind='receiving' in the taxonomy. For a MID-recipe gate (e.g.
"Ready for processing" between Racking and Plating) that snapped the
job's card back to the far-left Receiving column when work advanced into
it — the job looked like it vanished from the board.

_compute_area_kind now detects gating via the stable kind code and
resolves a gating step's column to the NEXT non-gating step's area (so
"Ready for processing" shows in Plating), keeping cards flowing
left→right. Falls back to the last real stage for a trailing gate.
Non-gating steps unchanged. Helpers: _fp_is_gating_step / _fp_raw_area_kind
(no recursion) / _fp_resolve_area_kind.

area_kind is a stored compute — recomputed all 537 live steps on entech.
Verified: WO-30061 "Ready for processing" area receiving→plating, card now
renders in the Plating column.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 08:51:24 -04:00
gsinghpal
9a8e1d7ab5 feat(fusion_portal): ADP/express->visit wiring, visit entry tile, email consolidation (live on westin 19.0.2.10.0)
- express save captures visit_id; visit-linked submit defers SO creation
  (saves draft + signature) and returns to the visit for grouping.
- portal dashboard 'Start a Visit' tile for sales reps.
- fix duplicate-authorizer completion email; visit grouped SOs email once per SO.
- define visit._assessment_sale_type (ADP grouping key) - fixes AttributeError.

Verified on a westin-v19 clone (load + ADP-grouping + combination-guard smoke
test, mail neutralised) then deployed to westin prod 19.0.2.10.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 08:50:25 -04:00
gsinghpal
451fc5eafd docs(billing): NexaCloud->Odoo cutover spec + plan 01 (cancel endpoint)
Increment design (phase #2 of the approved 2026-05-27 centralized-billing
spec) to make Odoo fusion_centralize_billing the system of record for
NexaCloud billing: build -> import -> dual-run -> gated flip, NexaCloud first,
one subscription per deployment, go-forward billing only. Plan 01 = the Odoo
subscription-cancel endpoint (test-first).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 08:38:48 -04:00
gsinghpal
7fcf38ca82 fix(fusion_plating_jobs): first/seeded stage never auto-finished on drain
_fp_try_autofinish_on_drain guarded on _fp_has_real_incoming() — the WRONG
direction. The first stage (e.g. Racking) is fed by the qty_at_step seed,
not an incoming move, so it never auto-finished when all its parts were
sent forward (operator sent everything out of Racking, step stayed
in_progress at qty 0). Now guards on a real OUTGOING move (parts left),
which covers the seeded first stage.

Still best-effort + gated: button_finish runs the required-input / sign-off
gates, so a step with an unrecorded required input (Racking's "Count the
Parts") won't auto-finish — it stays in_progress for a manual finish after
the input is recorded. Verified on entech.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 08:37:29 -04:00
gsinghpal
64a202ff6e fix(fusion_plating_shopfloor): partial advance blocked by from-step predecessor
The Move dialog's predecessor check flagged every unfinished step before
the destination — including the from_step itself, which is in-progress by
definition when advancing partial parts out of it. So any "Send → next"
to a not-yet-started step showed a hard "Predecessor not done: <from_step>"
blocker and greyed out SEND (reproduced on WO-30061: Racking → Ready for
processing). This broke partial advance for ALL quantities, not just
1-part orders.

Fix: _blockers_for_move only blocks unfinished steps STRICTLY BETWEEN
from_step and to_step (you'd be skipping an incomplete intermediate
stage). Immediate-next advance is allowed; skip-ahead still blocked;
backward (rework) moves unblocked. Verified on entech: blocker no longer
fires for Racking → Ready for processing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 08:30:12 -04:00
gsinghpal
13fabb0e79 feat(fusion_portal): assessment-visit redesign - live on westin 19.0.2.9.0
Bundles multiple assessments per home visit; on completion groups them by
funding workflow (x_fc_sale_type) into one draft sale order per workflow
(March of Dimes / ADP / ODSP / WSIB / private / hardship / insurance).
Adds the mobility scooter ADP device type, the power-mobility home-access
rule, ADP multi-device combination guard, and the portal visit workspace.

Verified on a westin-v19 clone (clean registry load + funding-grouping
smoke test) then deployed to westin prod (fusion_portal 19.0.2.9.0).
Prod's pre-existing orphaned tax links were preserved (Odoo skips existing
FKs), pending a later audit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 08:23:43 -04:00
gsinghpal
319de06ca6 fix(fusion_plating_shopfloor): finish-dialog text readable in dark mode (real fix)
Root cause (verified against the live compiled bundle): Odoo's backend
CSS never DEFINES --bs-body-color / --bs-secondary-color / --bs-*-bg as
custom properties (0 definitions; they're only referenced). So every
color: var(--bs-body-color, #1d1d1f) — and the earlier --bs-secondary-color
swap — resolved to the dark hex fallback in BOTH light and dark mode.
That's why the prior swaps never worked. Backend dark mode here is runtime
[data-bs-theme=dark] + SCSS literals, not those vars.

Fix: the finish-block dialog text now INHERITS the modal's theme-correct
colour (same as the readable title + "Count the Parts" list items) — the
broken line was the only one setting an explicit var() colour. Tinted
banners use translucent rgba() instead of color-mix-with-undefined-var.
Verified in the served bundle: o_fp_finish_block_msg{font-weight:500;}
(no colour override).

CLAUDE.md dark-mode guidance corrected (it had wrongly recommended those
undefined vars).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 02:31:09 -04:00
gsinghpal
903ceb10d0 fix(fusion_repairs): two install blockers found on first clean install (Westin Enterprise)
Surfaced by installing fusion_repairs into a westin-v19 clone (its first-ever clean install; cloud.md's 'installed locally' was stale). (1) Post-visit NPS mail template used url_encode(), which is NOT in Odoo 19's mail.template QWeb render context -> save-validation failed at install (ParseError 'issue with this value'); replaced with a string-method (.replace) fallback. (2) views/menus.xml defined menu_fusion_repairs_configuration AFTER the children referencing it as parent -> 'External ID not found in the system'; moved the parent definition above its children. fusion_repairs now installs cleanly (32 models, 11 templates) on the Enterprise stack.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 02:21:07 -04:00
gsinghpal
0499a1ad2e fix(fusion_plating_shopfloor): finish-dialog message readable in dark mode
The "N required input(s) haven't been recorded yet" line still read as
dark/dim in dark mode after the --text-secondary→--bs-secondary-color
swap, because --bs-secondary-color is muted/low-opacity. That line is
primary instruction text, so use the full-contrast var(--bs-body-color)
instead (+ font-weight 500). Reserve --bs-secondary-color for genuinely
secondary text.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 02:11:42 -04:00
gsinghpal
4f48bab6e9 feat(fusion_portal): funding-source selector on accessibility forms (#3)
* feat(fusion_portal): funding-source selector on accessibility forms

Reps can now mark an accessibility assessment's funding source on the web form
(Private / March of Dimes / ODSP / WSIB / Hardship / Insurance / Other) so the
generated draft sale order routes to the correct funding pipeline instead of
always defaulting to private pay. Adds Hardship to the x_fc_funding_source
selection + sale_type_map; the new form <select> is auto-serialised by the
existing FormData submit, and accessibility_assessment_save now maps
funding_source -> x_fc_funding_source. The model + SO routing were already in
place (2026-04 audit fix) — this closes the form + controller gap.

Plan: docs/superpowers/plans/2026-06-02-accessibility-funding-selector.md
Spec: docs/superpowers/specs/2026-06-02-assessment-visit-funding-design.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(fusion_portal): validate funding_source in accessibility save (parity with booking)

Coerce an unexpected/tampered funding_source to direct_private instead of passing
it raw into create() (which would raise on the Selection field). Mirrors the
/book-assessment controller; the whitelist is derived from the model selection so
it auto-covers hardship and any future values.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 01:44:19 -04:00
gsinghpal
b616375679 Merge fusion_maintenance brainstorm, design spec & Plan 1 into main
Docs only: the fusion_maintenance brief (+ Westin Step 0 / install-base sizing), the approved design spec (build into fusion_repairs; flat-fee per type; new-sale trigger + two-regime backfill; technician-aware booking on fusion_tasks), and Plan 1 (Foundation) + Plans 2-5 roadmap. Implementation pending.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 01:33:50 -04:00
gsinghpal
5c4a26b65f fix(fusion_plating_shopfloor): dark-mode text/background readability
Operators saw dark-on-dark (invisible) text in the workspace + "Cannot
Finish Step" dialog in Odoo dark mode.

Root cause: var(--text-secondary, #xxx) — a made-up variable that doesn't
exist in Odoo, so it always fell back to the hardcoded dark hex (invisible
on dark). Used 33× across job_workspace.scss + 5 component stylesheets.
Replaced with the real dark-aware var(--bs-secondary-color).

Also fixed paired backgrounds that would hide the now-theme-flipped text:
- finish-block action note → var(--bs-tertiary-bg) (was #f3f4f6).
- Tinted status banners (finish-block step, overtime timer, receiving
  status) → color-mix over var(--bs-body-bg) + var(--bs-body-color).
  Odoo's bootstrap lacks the BS5.3 -bg-subtle/-text-emphasis vars
  (verified against _root.scss), so color-mix is the dark-aware path.

Solid accent pills/dots (white text) and the color-coded plant-card chips
(light-bg + dark-text, readable in both) intentionally left as-is.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 01:30:51 -04:00
gsinghpal
b59ad6b21e style(fusion_plating_shopfloor): polish the scan button pair
Matched, intentional look for the two scan controls:
- Scan QR (camera, primary sticker-scan) — accent-filled blue, fa-qrcode.
- Enter Code (manual / scanner-gun) — accent-tinted secondary, fa-keyboard-o.
Both now use Font Awesome icons (no emoji), inline-flex aligned icon+label.
Enter Code's class restructured so scan-alt persists alongside the active
state when the drawer is open.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 01:30:51 -04:00
gsinghpal
8a1a09b150 fix(fusion_plating_shopfloor): scan buttons — single icon + clearer labels
The QrScanner component renders its own fa-qrcode icon; the board passed
it label '📷 Camera', so the camera button showed two icons (QR + camera
emoji). Drop the emoji → one icon.

Also clarify the two scan paths (they do different things):
- "Scan QR"  = camera scan of the printed job sticker (primary path)
- "Enter Code" = manual / hardware scanner-gun text drawer (no camera)
Reordered so the camera (sticker) scan reads first. Other QrScanner call
sites already pass plain/no labels — this was the only double-icon.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 01:30:51 -04:00
gsinghpal
a092c385ea fix(fusion_plating_shopfloor): job appearing in every not-yet-started stage
Regression from the partial-order board: _job_presences emitted a card
for any area containing a `ready` step. These recipes seed ALL downstream
steps to `ready` at job creation, so a job showed in every future stage
at once (e.g. WO-30061 across racking/receiving/plating/inspection) even
though no parts had advanced there.

Fix: a stage shows ONLY where parts physically are (qty_at_step > 0,
which includes the first-active seed) OR where a step is in_progress/
paused. A merely ready/pending future step with no parts no longer shows.
Strict sequential progress falls out for free — the qty_at_step seed sits
on the lowest-sequence non-terminal step and advances as each completes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 01:30:51 -04:00
gsinghpal
ca44461b6f feat(fusion_plating): partial order handling on the shop floor
Operators can now see and advance a job's parts across multiple stages
at once (e.g. 10 Masking / 20 Plating / 20 Baking on one 50-part job).
Tracking model C (fluid per-stage quantities + existing hold/scrap/
rework records for exceptions); board option 2 (a card per occupied
stage); wait-to-reconverge close. Additive only — no new model, no
migration, no change to the close/cert/ship lifecycle.

Board (fusion_plating_shopfloor/controllers/plant_kanban.py):
- One card PER (job, stage), composite key "{job_id}:{area}". Unsplit
  jobs render exactly as before. _job_presences/_render_presence;
  primary presence keeps full job card_state, secondary presences
  derive state from their focus step.

Card (plant_card.js/.xml/.scss):
- "20 of 50 here" badge; tap opens the workspace focused on that
  stage's step (focus_step_id, already accepted by the workspace).

Move + light-up (move_controller.py, fusion_plating_jobs/fp_job_step.py):
- Availability/pre-fill now from qty_at_step (step had no qty_done/
  qty_scrapped fields — the old read was always 0, dead path).
- Forward move auto-flips destination pending->ready (no auto-start;
  labour timer stays explicit) and auto-finishes a drained source
  (best-effort). Predecessor gate is qty-aware: a step with real
  arrived parts is startable regardless of upstream completion
  (_fp_has_real_incoming, single source of truth for can_start /
  blocker / button_start / move blockers).

Operator advance (job_workspace.js):
- "Send -> <next>" action on in_progress/paused steps opens the slimmed
  Move dialog (qty steppers, no keyboard; advanced fields collapsed).
  Was only wired into the deprecated shopfloor_tablet before.

Close (fp_job.py):
- button_mark_done counts move-based scrap (_fp_scrapped_via_moves) into
  qty_scrapped and derives qty_done = qty - scrapped (was blindly
  = job.qty, over-counting). Reconciliation gate unchanged.

Static-validated: pyflakes (py), lxml parse (xml), node --check (js).
Dynamic tests + browser check need an installed env (entech/trial) —
plating modules can't install on the local Community DB.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 01:30:51 -04:00
gsinghpal
249adf8145 docs(fusion_plating): shop-floor partial order handling design spec
Design for parts fanning across shop-floor stages (e.g. 10 at Masking,
20 at Plating, 20 at Baking on one 50-part job):

- Tracking model C — fluid per-stage quantities via existing qty_at_step;
  failed/held/rework subsets ride existing hold/scrap/rework records.
- Board Option 2 — a card per stage-presence (composite job:area keys);
  unsplit jobs render identically to today.
- Easy-advance operator flow — one "Send to next" action, steppers /
  rack-tap (no keyboard), intent-named Hold/Scrap/Rework buttons.
- Light-up plumbing — auto-ready on arrival, qty-aware predecessor gate,
  auto-finish source on drain; no auto-start (labour accuracy).
- Close — wait to reconverge; close/cert/ship/invoice lifecycle unchanged.

Additive only: no new core model, no data migration, no change to the
quantity model, OWL component tree, or close lifecycle.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 01:30:51 -04:00
gsinghpal
cc568b0ec8 docs(fusion_maintenance): Plan 1 (Foundation) implementation plan + Plans 2-5 roadmap
TDD plan for the enrollment+pricing foundation: maintenance policy fields on the equipment category (+ product fee override), maintenance-contract extensions, fix+wire the dead _spawn_maintenance_contracts into the existing action_confirm (delivery-date anchor, two-regime serial dedup, fee snapshot), fee line in the reminder email, category UI, version 19.0.2.3.0. Grounded in real source. Plans 2-5 (booking on fusion_tasks, visit log + checklist, two-regime backfill, office crons) roadmapped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 01:27:38 -04:00
gsinghpal
17d21bffb5 docs(fusion_maintenance): correct backfill for lifts (no serials) after live sizing
Live sizing on Westin: stair lifts ~254 customers / porch-VPL ~30 / lift chairs ~41, but lift serial coverage ~0 (12/416 stairlift lines). The serial-as-unit-key approach (valid for ADP wheelchairs) fails for lifts. Backfill now splits into two regimes: serial dedup for wheelchairs; partner+base-product+sale-line dedup for lifts with accessory-line exclusion via the per-product maintainable flag.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 01:21:08 -04:00
gsinghpal
aafc2db8a8 docs(fusion_portal): spec for assessment Visit + funding-routed sale orders
Brainstormed design: bundle a home visit's assessments (ADP + accessibility),
measurement-first with client/funding deferred, add-as-you-go workspace,
per-item funding selector (fixes the March-of-Dimes routing gap), and on
completion group items into ONE draft sale order per funding workflow
(ADP / MOD / ODSP / Hardship / private) reusing the existing pipelines.
Adds ADP multi-device + combination rules, a new mobility-scooter type, and a
power-mobility home-accessibility rule that feeds the accessibility upsell.
v1 keeps manual quotation (no auto-pricing); MOD $15k cap is a reminder only.
Phased 1-3; risks + file map included.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 01:20:26 -04:00
gsinghpal
6c3830fd4c docs(fusion_maintenance): approved design spec — extend fusion_repairs (booking, backfill, flat-fee)
Build maintenance INTO fusion_repairs (engine ~90% already there): per-category policy (interval + flat fee, product override); fix the dead contract-spawn trigger for new sales + a one-time idempotent backfill of the existing install base (lifts + fusion_claims wheelchairs); technician-aware self-serve booking on fusion_tasks availability (NO Enterprise appointment) creating a technician task; structured maintenance visit log + inspection cert for lifts; office follow-up crons; cost shown to client. Out of v1: SMS, /my/equipment, route optimization.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 01:14:49 -04:00
gsinghpal
12d383a8c2 docs(fusion_maintenance): add Westin install-base sizing to Step 0 results
Sized the real serial-tracked install base on sale.order.line: ~138 units / ~136 customers across all funders (walkers 68, wheelchairs 45, power bases 7, scooters 4, +14 with no ADP device_type). Serial# is captured ~only on equipment, so it doubles as a trackable-unit marker. ADP-only gating misses ~28 units (direct_private/adp_odsp/march_of_dimes) -> bridge should key on serial, funder-agnostic. Flags two data gaps (no-device_type units; non-ADP units lacking delivery_date) and reframes the MVP open question as volume (walkers/chairs) vs margin (powered units).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 00:33:12 -04:00
gsinghpal
139e917e09 docs(fusion_maintenance): record Step 0 live-grounding results from Westin prod
Ran Step 0 against Westin prod (westin-v19 on odoo-westin). Resolved the APP/DB placeholders (DO boxes dead; migrated on-prem to odoo-dev-app), added a dated STEP 0 RESULTS section, and corrected the open questions the live inspection disproved: no stair/porch lifts in Westin ADP data; Enterprise appointment already ships native token booking; fusion_repairs contract engine not deployed; device_type is the ADP billing-code catalog taxonomy, not the install base.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 00:22:51 -04:00
Claude
de3e0df5fc docs(fusion_maintenance): brainstorm + handoff brief for connected-env session
Captures the maintenance-followup design exploration so it can resume from a
Tailscale-connected environment with access to Westin production:
- fusion_repairs already has a maintenance contract/reminder/booking engine to reuse
- fusion_claims (sale.order.line + adp.device.code.device_type) is the trigger source
- locked decisions: same DB, Enterprise appointment, public self-serve token booking
- Step 0 live-inspection command pack to run on Westin prod before any code
- open questions (MVP cut, revenue mechanic, tech assignment, booking route)

https://claude.ai/code/session_011wfSKQfSWhKZcm1yzSGznW
2026-06-02 04:01:54 +00:00
gsinghpal
747c814249 refactor(fusion_portal): rename from fusion_authorizer_portal + modern photo cards on accessibility selector
Rename module fusion_authorizer_portal -> fusion_portal everywhere:
manifest/assets, controllers, models, views, JS (odoo.define + asset URLs),
migration MODULE constants; plus cross-module refs in fusion_schedule,
fusion_repairs, fusion_quotations (depends + inherit_id) and the pdf_filler
import in fusion_claims. Add rename_module.sql for the one-time in-place DB
rename (ir_module_module, ir_model_data, ir_ui_view.key,
ir_module_module_dependency) required on installed envs before -u fusion_portal.
Document the rename gotcha as rule 16 in CLAUDE.md.

Redesign the Accessibility Assessment selector: replace Font Awesome icon tiles
with photo-banner cards using 7 optimized images (1000x750 PNG -> 800x600 JPEG,
~8MB -> 488KB), per-type colour accent bar + centered pill button, hover
lift/zoom. Images ship as module static files so they deploy/sync with the module.

Drop the regenerable graphify-out cache from the module.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 22:38:14 -04:00
gsinghpal
c527c7cade fix(fusion_clock): migration must recompute net_hours/overtime, not just break
Recomputing only x_fclk_break_minutes left historical x_fclk_net_hours / x_fclk_overtime_hours stale (add_to_compute+flush of one field does not cascade to dependents). Recompute the full chain in dependency order. Caught verifying the entech deploy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 00:28:12 -04:00
gsinghpal
f7ec1e28f9 feat(fusion_clock): province-aware automatic unpaid break (2-tier)
Statutory unpaid break now deducts automatically from worked hours on every path - portal, kiosk, NFC, auto-clock-out cron, AND manual backend entry.

- new fusion.clock.break.rule per-province table (seed Ontario 5h->30, 10h->+30), resolved from the employee's company province with a global default fallback
- x_fclk_break_minutes is now a single idempotent stored compute (statutory(worked_hours) + penalties), replacing the 4 duplicated write sites (_apply_break_deduction x3 callsites + auto-clock-out cron + penalty write)
- retire break_threshold_hours (superseded by per-rule break1_after_hours); post-migrate drops the param and recomputes historical breaks
- 11 tests all green; module install + 19.0.4.1.0 migration verified on modsdev

Bump 19.0.4.0.3 -> 19.0.4.1.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 00:15:42 -04:00
gsinghpal
96b3f124f8 docs(fusion_clock): fix plan so the 19.0.4.1.0 migration fires in dev (bump in Task 4)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 23:50:08 -04:00
gsinghpal
2c32e7bcd0 docs(fusion_clock): implementation plan for province-aware auto break
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 23:46:42 -04:00
gsinghpal
aa9b95bd5d docs(fusion_clock): spec for province-aware automatic unpaid break
2-tier statutory break deduction: new fusion.clock.break.rule per-province table (seed Ontario 5h/30 + 10h/30); x_fclk_break_minutes becomes an idempotent stored compute (statutory + penalties) firing on every path incl. manual backend entry; collapses the 4 duplicated break-write sites into one calculator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 23:35:58 -04:00
gsinghpal
493f01827e Update res_config_settings.cpython-312.pyc 2026-05-31 22:53:43 -04:00
gsinghpal
2ab59bccde feat(fusion_clock): default clock-in/out times as 12-hour AM/PM dropdowns
People aren't good with 24h. Default Clock-In/Out are now AM/PM dropdowns (15-min
grid) instead of 24h float_time inputs. Stored value stays the float-string
(e.g. '9.0'), so all downstream float(get_param(...)) reads are unchanged;
persisted manually with get-snap for any off-grid value. Bump 19.0.4.0.3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 22:47:12 -04:00
gsinghpal
914c96a09a fix(fusion_clock): add role=button to kiosk Open links (a11y warning)
Odoo warns that <a class=btn> needs role=button. Bump 19.0.4.0.2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 22:31:30 -04:00
gsinghpal
b015958edc feat(fusion_clock): unify + label kiosk settings, add quick-open buttons
Settings tidy-up: one 'Kiosk' block holding both PIN Kiosk and NFC Kiosk
(clearly described so users know which is which), each with an Open-kiosk
button when enabled; Corrections + Sounds split into a 'Portal' block. Move
Auto-Wipe Photos under Photo Verification (was hidden for PIN-only clients).
Bump 19.0.4.0.0 -> 19.0.4.0.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 22:07:00 -04:00
gsinghpal
ca94a4c42a changes 2026-05-31 21:33:44 -04:00
gsinghpal
a5ec79013a feat(fusion_clock): PIN kiosk — polished photo-tile + PIN clock (opt-in)
A proper shared-device PIN kiosk for clients who don't want NFC: photo-tile grid
(+search) -> tap -> PIN (or first-use create) -> optional master-gated selfie ->
clock, in the NFC kiosk's dark glass + brand-gradient style. Built as an Odoo 19
Interaction; new pin_kiosk.scss (scoped); reworked clock_kiosk.py
(search +avatar/has_pin, verify_pin needs_setup, set_pin, clock via kiosk location).
Drops the redundant kiosk_pin_required (PIN always required); relabels the company
kiosk location; adds a PIN-kiosk app icon. Opt-in via enable_kiosk (off by default).
HttpCase tests added. Bump 19.0.4.0.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 21:25:32 -04:00
gsinghpal
b61e159e6f docs(fusion_clock): PIN kiosk implementation plan (TDD, 7 tasks)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 21:09:03 -04:00
gsinghpal
13a892c7ab docs(fusion_clock): PIN kiosk design spec (photo-tile + PIN, NFC-matching style)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 20:56:21 -04:00