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>
17 KiB
Assessment Visit — bundled, funding-routed assessments
Date: 2026-06-02
Module: fusion_portal (depends on fusion_claims, fusion_tasks); live on odoo-westin (DB westin-v19)
Status: Draft for review
Author: Brainstormed with Gurpreet (Fusion / Westin Healthcare)
1. Problem & goals
A sales rep visits a client's home with an occupational therapist (OT) and the client present for only 30–45 minutes, and the OT's time is the scarcest resource. In that window the team often does more than one assessment — a wheelchair (ADP) plus, opportunistically, accessibility products the rep spots (a ramp at the front steps, a stair lift inside, a tub cutout, a patient lift for transfers). Today each assessment is a separate, standalone web form that re-collects the client's details and creates its own sale order, and the front-end forms give the rep no way to mark a case's funding source — so March-of-Dimes work silently defaults to private pay and never reaches the MOD pipeline.
Goals
- One visit, many assessments, entered once. Bundle every assessment from one home visit; capture the client + funding details a single time.
- Measurement-first. Capture measurements while the OT is present; defer client/health-card data to after they leave; let the OT sign the ADP application on the spot.
- Add as you go. The rep adds an assessment/product the instant they spot it — repeatable, with a location tag (Front / Back / Inside).
- Route by funding workflow. On completion the visit emits one sale order per funding workflow (ADP, March of Dimes, ODSP, WSIB, private, …) — never one combined SO, and never a separate SO per item within the same funding.
- Let the rep set funding at assessment time (the real MOD "tracking" gap).
- ADP multi-device with valid-combination rules, including a new mobility scooter type and a home-accessibility hard rule for power mobility that feeds the accessibility upsell.
Non-goals (v1): voice/dictated entry; rebuilding the measurement math; a new MOD/ADP claim model (the pipelines already exist — we reuse them).
2. Current state (verified against source)
- Two assessment models, already two separate SO lineages.
fusion.assessment(ADP: rollator/wheelchair/powerchair) andfusion.accessibility.assessment(the 7 lift/mod types) each have their own_create_draft_sale_order(assessment.py:587,accessibility_assessment.py:751), their ownx_fc_sale_type, and their own state machine — ADP's 24-statex_fc_adp_application_statusvs MOD's 16-statex_fc_mod_status. Each guards against a second SO (accessibility_assessment.py:503-511). SO back-links are scalar Many2one:assessment_id,accessibility_assessment_id(fusion_portal/models/sale_order.py:37,48). - SOs are born with no order lines. Specs become a chatter HTML note (
_format_assessment_html_table,accessibility_assessment.py:815); a human prices the draft afterward. No per-type product mapping exists. - Funding is modelled but not on the measurement forms.
x_fc_funding_source(required, defaultdirect_private) on the accessibility model — valuesmarch_of_dimes,odsp,wsib,insurance,direct_private,other(accessibility_assessment.py:71-87) — is present on the public booking form but absent from all 7 measurement forms, so they default to private. Canonical billing typesale.order.x_fc_sale_type(fusion_claims/models/sale_order.py:320) carries the full set incl.adp,adp_odsp,march_of_dimes, etc. - MOD tracking already exists as
x_fc_mod_status(16 states) + ~60x_fc_mod_*fields (HVMP reference #, vendor code, drawings, PCA, POD, approved/payment amounts, dated audit trail) + MOD views + ~7 wizards + ~40 MOD/ODSP stage emails (fusion_claims/models/sale_order.py:438,877). An accessibility assessment fundedmarch_of_dimesalready lands its SO in this pipeline atneed_to_schedule. The gap is purely that the rep can't choosemarch_of_dimeson the form. - Emails are mostly Python-built via the shared
fusion.email.builder.mixin._email_build(fusion_tasks/models/email_builder_mixin.py:8), gated byir.config_parameterfusion_claims.enable_email_notifications. Completion email fires from inside_create_draft_sale_order(assessment.py:847;accessibility_assessment.py:624). Stage emails (_adp_send_stage_email,_mod_email_build,_odsp_email_build) are keyed off the SO's funding type + status, so they keep working per-SO unchanged. - Known bug: backend ADP
action_complete()sends the authorizer two completion emails (template pair atassessment.py:494+ inline report via:847). Must consolidate before fanning out across a visit.
3. The design
3.1 The Visit aggregate (only net-new model)
fusion.assessment.visit — the hub for one home visit.
- Client/context, entered once:
partner_id, address fields,visit_date,sales_rep_id,authorizer_id(OT),x_fc_funding_source-style default,state(measuring→client_pending→done). - Links to its assessments:
adp_assessment_ids(One2many →fusion.assessment) andaccessibility_assessment_ids(One2many →fusion.accessibility.assessment). Each assessment gainsvisit_id. - Links to its sale orders:
sale_order_ids(One2many →sale.order) — one per funding workflow it produced. - On the SO side, add
visit_id. Each assessment already carriessale_order_id(Many2one —accessibility_assessment.py:153,assessment.py:422), so several same-funding assessments can already point at one SO; the redundant scalarassessment_id/accessibility_assessment_idon the SO (fusion_portal/models/sale_order.py:37,48) become One2many (or are dropped in favour of thesale_order_idreverse) so an SO no longer assumes a single source assessment.
Client info moves to the Visit as the single source of truth; the per-assessment client_name-required gate is relaxed (the model keeps the field for back-compat / standalone use but the Visit flow fills it from partner_id).
3.2 Add-as-you-go workspace (portal UX)
A portal "visit workspace" (reps are portal users, tablet-first):
- Always-present "+ Add" → pick a type + location tag (Front / Back / Inside / custom) → drop straight into the existing measurement form for that type. No client paperwork required to start.
- Each added assessment is a card showing type, location, status (To measure / Measured / Signed), and — once priced — its amount.
- Measurement-first: the forms render with client fields hidden/optional; a deferred "Client + funding" step is completed after the OT leaves and is shared by every item.
- The OT signs the ADP application (Page 11) inline on the wheelchair/ADP item, on-site, independent of client demographics (reuse
portal_assessment_expressPage-11 section + signature pad). - Mockups (for reference, in repo
docs/mockups/if committed):fusion_portal_new_approach_mockup.html.
3.3 Multi-instance + location tags
Any type can be added more than once, each its own assessment record with a location label ("Main stairs", "Basement", "Front porch"). Two stair lifts = two assessment records (→ two lines on the same funding SO; see §3.6). A "Same as the previous" action copies shared options so the rep only re-enters the differing measurements.
3.4 Per-item funding selector — the MOD gap fix
Expose x_fc_funding_source on each accessibility assessment in the flow: Private Pay / March of Dimes / ODSP / WSIB / Hardship / Insurance / Other. This one field drives the existing sale_type_map → x_fc_sale_type → correct pipeline (MOD 16-state tracker, ODSP, hardship, …). Defaults to the previous item's funding so an all-MOD visit isn't re-picked each time. ADP/wheelchair items are fixed to ADP (no picker). This is the minimal change that closes the "can't mark a case as March of Dimes" gap — no new tracking model.
Patient lift is an accessibility/equipment item that uses this same picker — funded by March of Dimes, ODSP, or Hardship (e.g. Toronto residents), so its funding is chosen per case, not fixed.
sale_type_mapgap:x_fc_funding_sourcecurrently lackshardshipwhilex_fc_sale_typealready has it (sale_order.py:320) — addhardshipto the picker + asale_type_mapentry (accessibility_assessment.py:771), and review the map so every offered funding routes to a realx_fc_sale_type. MOD funding cap applies to MOD items — see Resolved decision 1 (§4).
3.5 ADP multi-device + combinations + scooter + home-access rule
Multi-device ADP order. Today one ADP device per order; the visit allows a valid combination of ADP devices for one client, all landing on the one ADP SO. Each ADP device is an item; the combination check runs across the visit's ADP items.
Device categories: Walker/Rollator · Manual Wheelchair · Power Wheelchair · Scooter (new).
Combination rules (confirmed):
| Combination | Allowed? |
|---|---|
| Any single device | ✓ |
| Walker + Manual Wheelchair | ✓ |
| Walker + Power Wheelchair | ✓ |
| Walker + Scooter | ✓ |
| Manual + Power Wheelchair | ✗ |
| Power Wheelchair + Scooter | ✗ |
| Manual Wheelchair + Scooter | ✗ |
| Two walkers / any duplicate | ✗ |
Rule in words: at most one "seated-mobility" device {manual wheelchair, power wheelchair, scooter}, optionally one walker/rollator alongside, no duplicates. Enforced when adding/saving an ADP device.
Scooter (new ADP type) fields: client_weight (exists), scooter type, maximum travel range, and the home-accessibility check (below). Gets its own measurement section in the ADP form, mirroring the rollator/wheelchair/powerchair sections.
Power-mobility home-accessibility hard rule. For scooter and power wheelchair, a required check: "Is the home accessible enough for the device to be used inside and outside the home independently — no lifting, not left outside/in the garage?" ADP will not fund power mobility a home can't accommodate. If the answer is No, the visit flags an accessibility need and prompts the rep to add an accessibility item (ramp / porch lift, typically March of Dimes) to remediate. This is the explicit bridge between the ADP power-mobility item and the accessibility/MOD upsell.
The power-wheelchair form is already well-optimized — do NOT change its fields. The only addition there is this home-accessibility warning. The new scooter type gets its own section (fields above); the manual-wheelchair and rollator sections are unchanged.
3.6 Funding-workflow grouping → one SO per workflow
On visit completion, group its assessments by funding workflow (x_fc_sale_type) and create one SO per group:
- All
march_of_dimesitems (stair lift + porch lift + tub cutout, or two stair lifts) → one MOD SO, multiple lines (funding permitting). - All ADP devices (the valid combination) → one ADP SO.
- Private / ODSP / WSIB / insurance → their own SO each.
- A separate SO appears only when the case type changes, never per-item within a funding.
Refactor the two per-model _create_draft_sale_order routines into a shared, group-aware builder that takes a set of same-funding assessments and produces one SO, branching on funding type to stamp the right starting status field (x_fc_adp_application_status for ADP, x_fc_mod_status for MOD, etc. — mirroring assessment.py:600-622) and the right links. Reuse the existing MOD/ADP/ODSP pipelines unchanged.
3.7 Emails
- Reuse
fusion.email.builder.mixinand the existing per-funding stage emails (they're keyed off SO type + status, so per-SO they keep working). - Move the completion send to per-SO inside the new builder (not per-assessment), and dedupe recipients, so a 3-item visit doesn't emit 3–6 completion emails.
- Fix the existing duplicate (authorizer gets two completion emails on backend ADP completion) as part of this.
- Make
enable_email_notificationsgating consistent across the sends the visit touches.
3.8 Reused vs net-new
- Reused, largely untouched: the 7 accessibility measurement forms + their JS/Python calc; the ADP Express form + Page-11 signature; the MOD/ADP/ODSP pipelines, views, wizards, and stage emails; the email branding mixin.
- Net-new: the
fusion.assessment.visitmodel + workspace UI; per-item funding selector on the accessibility forms; the group-aware SO builder + link-cardinality change; ADP multi-device + combination validation; scooter type + fields; power-mobility home-access rule + cross-sell flag; completion-email consolidation.
4. Resolved decisions
- MOD funding cap — documented rule, light-touch in v1. March of Dimes covers up to $15,000 per person, lifetime, income-gated: if the client's income is under that year's threshold (the threshold changes annually), MOD funds the full $15k; if over, MOD may deny or partially approve. v1: surface this cap as a reminder on MOD items and capture an "income under MOD threshold? (yes / no / unknown)" flag so the rep can judge — do not auto-compute lifetime used-vs-remaining across the client's prior MOD orders (the SO's existing
x_fc_mod_*approved/payment fields already record per-order amounts). Future: yearly-threshold config + automatic lifetime-remaining tracking + a hard warning. - No auto pricing / products in v1. The visit creates a draft SO per funding workflow and appends each assessment's specs to that SO's chatter (today's pattern); the sales rep builds the quotation lines manually. One SO can hold many items. No per-assessment-type product mapping. (Auto-pricing is a future expansion.)
- Patient-lift funding is chosen per case via the funding picker — March of Dimes, ODSP, or Hardship (e.g. Toronto residents) all fund it; it is not fixed (see §3.4).
- Power-wheelchair form unchanged — already well-optimized; the only addition is the home-accessibility warning (device usable inside and outside the home). The home-access rule applies to scooter (new type, new section) and power wheelchair (warning only).
5. Phasing
- Phase 1 — Funding correctness + visit backbone:
fusion.assessment.visit, link-cardinality change, funding selector on the accessibility forms (incl. Hardship; patient-lift routing), MOD $15k-cap reminder + income-threshold flag (informational), group-and-route to per-workflow draft SOs (specs to chatter, manual pricing) reusing existing pipelines, completion-email consolidation + duplicate fix. (Delivers the MOD-routing fix and the multi-SO split.) - Phase 2 — ADP expansion: multi-device ADP order + combination validation, scooter type + fields, power-mobility home-access hard rule + accessibility cross-sell prompt.
- Phase 3 — Seamless field UX: the full add-as-you-go workspace, measurement-first deferral, location tags, "same as previous", OT on-site sign-off polish.
- Later: product-line auto-pricing, MOD funding-cap tracking, voice/quick entry.
6. Risks (from investigation)
- Duplicate completion emails already live on the ADP backend path — fix before fan-out (§3.7).
- Scalar back-links + double-SO guards assume one SO per assessment; grouping breaks them — must move to
visit_id/ One2many and make the guard visit-aware. - Inconsistent
enable_email_notifications— template sends ignore the kill-switch; don't route new traffic through templates without honoring it. - Label drift
x_fc_funding_sourcevsx_fc_sale_type(insurance="Private Insurance" vs "Insurance";direct_private="Private Pay (Direct)" vs "Direct/Private") — keys match so routing works; align labels in any shared UI. - Unreachable funding types from accessibility:
sale_type_map(accessibility_assessment.py:771) covers 6 values; decide which funding types each assessment type may emit.
7. Files in scope
fusion_portal/models/assessment.py— ADP_create_draft_sale_order(:587), completion email (:847), multi-device + scooter + home-access.fusion_portal/models/accessibility_assessment.py— accessibility_create_draft_sale_order(:751),action_complete(:493), completion email (:624), funding routing.fusion_portal/models/sale_order.py— back-links (:37,:48) →visit_id/ One2many.fusion_portal/models/visit.py— newfusion.assessment.visit.fusion_portal/views/portal_accessibility_forms.xml+portal_assessment_express.xml— funding selector, scooter section, home-access check; workspace shell.fusion_portal/controllers/portal_main.py(/my/accessibility/save:2482) +portal_assessment.py— visit-aware save/group/route.fusion_claims/models/sale_order.py— reusex_fc_sale_type(:320),x_fc_mod_status(:438), stage emails (:6876,:9038,:10063); no pipeline rebuild.fusion_tasks/models/email_builder_mixin.py— reuse for any new visit emails.
Deployment note: fusion_portal is live on odoo-westin (westin-v19, container odoo-dev-app). Ship per the rename/deploy procedure (backup → code sync → -u fusion_portal → cache-bust → restart → verify).