Files
Odoo-Modules/fusion_claims/docs/superpowers/specs/2026-05-21-fusion-claims-dashboard-design.md
gsinghpal b2f483d67c docs(fusion_claims): add dashboard redesign spec
Action-oriented dashboard replacing the existing 4-panel HTML overview:
posting-week banner with live countdown, 3 KPI tiles, 8 funder hotlinks,
ADP + MOD workflow flag tiles, role-aware filtering, dark-mode aware SCSS.

Spec captures all design decisions from the brainstorm session; ready to
hand off to writing-plans.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 03:29:23 -04:00

22 KiB
Raw Blame History

Fusion Claims Dashboard — Design Spec

Date: 2026-05-21 Module: fusion_claims Status: Design approved, ready for implementation plan Replaces: the existing 4-panel HTML-field dashboard at models/dashboard.py + views/dashboard_views.xml


1. Purpose

Surface workflow flags, posting-week context, and per-funder hotlinks on a single dashboard so claims processors, sales reps, and managers can see at a glance what needs action today and how much money is in motion for the current ADP posting cycle.

The existing dashboard is a case-count overview. The new dashboard is action-oriented: "what's stuck, what's due this week, what should I be doing."

2. Audience and role behaviour

Single dashboard used by three personas, with auto-applied role filter:

  • Managers (in fusion_claims.group_fusion_claims_manager or sales_team.group_sale_manager) — see all cases.
  • Office staff — same as managers (they are typically in the manager group already, per the module's security model).
  • Sales reps (only in group_fusion_claims_user) — see only SOs where user_id = self.env.uid.

A small "Showing your cases" hint appears above the workflow tiles when the role filter is active (driven by computed is_manager).

3. Scope

In scope:

  • Posting-period banner with live countdown to submission cutoff
  • 3 KPI tiles: Ready to Claim, Claimed This Period, Total AR (ADP-portion)
  • 8 quick-action hotlinks: + ADP, + MOD, + ODSP, + WSIB, + Insurance, + MDC, + Hardship, + Private
  • "Your Activities" list (top 10 of current user's mail.activity)
  • Two bottleneck callouts: Approved without POD, Submitted with no ADP response > 14 days
  • ADP Pre-Approval workflow tiles (4): Waiting App, App Received, Ready Submission, Needs Correction
  • ADP Post-Approval workflow tiles (4): Approved, Ready Delivery, Ready Billing, On Hold
  • MOD workflow tiles (5): Awaiting Funding, Funding Approved, PCA Received, Project Complete, POD Submitted
  • Other-funder count cards (6): ODSP, WSIB, Insurance, MDC, Hardship, ACSD
  • Light + dark theme support via compile-time SCSS branching

Out of scope:

  • Charts / time-series graphs
  • The existing 4 configurable HTML panels (removed)
  • A "Recent Cases" power-user view (deferred — separate spec if needed)
  • Auto-refresh on window focus (manual reload only)
  • Per-user personalisation beyond the role filter (no saved layouts/filters)
  • Push notifications, email digests (out of scope, handled elsewhere)

4. Architecture

4.1 Implementation pattern

Hybrid: form-view shell + computed fields + small OWL widget for the live countdown.

Server-rendered Bootstrap-grid form view sits on top of a TransientModel with ~36 computed fields. One OWL field-widget handles the live deadline countdown (ticks every 60 seconds, swaps colour as deadline approaches).

The TransientModel name fusion.claims.dashboard is preserved — existing menu/action records continue to resolve. The model's internals are rewritten; old fields are dropped.

4.2 Files

File Action Purpose
models/dashboard.py Rewrite TransientModel with ~36 computed fields + role-filter helper + ~24 action methods
views/dashboard_views.xml Rewrite Form view: banner → KPIs → quick-actions → 2-column grid
static/src/scss/_fc_dashboard_tokens.scss New Colour palette tokens, compile-time @if $o-webclient-color-scheme == dark branch
static/src/scss/fc_dashboard.scss New Layout + section styles, references tokens
static/src/js/fc_posting_countdown.js New OWL field widget for live countdown (~60 lines)
static/src/xml/fc_posting_countdown.xml New OWL template (~10 lines)
__manifest__.py Edit Bump version (asset cache-bust), add SCSS to both web.assets_backend AND web.assets_web_dark, add JS+XML to backend

4.3 Layout

┌──────────────────────────────────────────────────────────────┐
│ BANNER: Posting Period: Mar 5  19  ·  [OWL: 3d to cutoff]   │
├──────────────────────────────────────────────────────────────┤
│ KPI TILES (3-up):  Ready  |  Claimed  |  Total AR            │
├──────────────────────────────────────────────────────────────┤
│ QUICK ACTIONS: + ADP  + MOD  + ODSP  + WSIB  + Ins  + ...   │
├────────────────────────┬─────────────────────────────────────┤
│ LEFT COLUMN            │ RIGHT COLUMN                        │
│  Your Activities       │  ADP Pre-Approval (4 tiles)         │
│  Bottlenecks           │  ADP Post-Approval (4 tiles)        │
│  Other Funders (6)     │  MOD (5 tiles)                      │
└────────────────────────┴─────────────────────────────────────┘

4.4 Data flow

  1. User clicks Dashboard menu.
  2. Existing action_fusion_claims_dashboard creates a fresh TransientModel record.
  3. Compute methods run (5 clusters — see §6).
  4. Form renders.
  5. OWL countdown widget tickets every 60 s, reading submission_deadline_dt from the rendered field, formatting it client-side.
  6. User clicks a tile → returns ir.actions.act_window opening a filtered sale.order list.
  7. User clicks a quick-action pill → returns ir.actions.act_window opening a fresh sale.order form with default_x_fc_sale_type in context.
  8. User clicks Refresh (form header button) → reloads the action.

5. Role filter

Central helper on fusion.claims.dashboard:

def _role_filter_domain(self):
    user = self.env.user
    if (user.has_group('fusion_claims.group_fusion_claims_manager')
        or user.has_group('sales_team.group_sale_manager')):
        return []
    return [('user_id', '=', user.id)]

Every count/sum compute method prepends _role_filter_domain() to its domain. For account.move based counts (KPIs), the filter is applied through x_fc_source_sale_order_id.user_id (the linked SO's salesperson) because invoices don't have their own user_id to filter on in this module.

is_manager (Boolean computed) exposed for the view to optionally show a "Showing your cases" hint.

6. Field inventory (≈36 fields)

6.1 Header / banner

Field Type Description
posting_period_label Char e.g. "Mar 5 Mar 19"
posting_period_start Date Start of current posting cycle
posting_period_end Date Start of next cycle (exclusive)
submission_deadline_dt Datetime Wed 18:00 of posting week, Toronto TZ
is_manager Boolean Drives role-hint visibility
is_pre_first_posting Boolean True if today < adp_posting_base_date

Derived from helpers already on adp.posting.schedule.mixin. Dashboard _inherit = ['adp.posting.schedule.mixin'].

6.2 KPI tiles

Field Type Source
kpi_ready_amount Monetary Sum of account.move.amount_total where x_fc_adp_billing_status='waiting' AND adp_exported=False, role-filtered via linked SO
kpi_ready_count Integer Same filter, count
kpi_claimed_amount Monetary Sum where x_fc_adp_billing_status in ('submitted','resubmitted') AND adp_export_date >= posting_period_start
kpi_claimed_count Integer Same filter, count
kpi_ar_amount Monetary Sum where move_type='out_invoice', state='posted', payment_state in ('not_paid','partial'), x_fc_invoice_type='adp'
kpi_ar_count Integer Same filter, count
currency_id Many2one Defaults to company_id.currency_id

6.3 Activities (left column)

Field Type Description
my_activities_count Integer mail.activity where user_id=current_user AND res_model in ('sale.order','account.move','fusion.technician.task')
my_activities_html Html Top 10 ordered by date_deadline asc, links via /odoo/<model>/<id>, overdue rows tinted

6.4 Bottlenecks (left column)

Field Type Domain
bottleneck_no_pod_count Integer ADP cases x_fc_adp_application_status in ('approved','approved_deduction') AND x_fc_proof_of_delivery=False
bottleneck_no_response_count Integer ADP cases x_fc_adp_application_status in ('submitted','resubmitted') AND x_fc_claim_submission_date < today - 14 days

6.5 Other funders (left column)

Each is an Integer count of active (non-terminal) cases:

Field Domain
count_odsp x_fc_sale_type in ('odsp','adp_odsp') excluding division-specific terminal states
count_wsib x_fc_sale_type='wsib' excluding case_closed, cancelled, denied
count_insurance x_fc_sale_type='insurance' excluding terminal states
count_mdc x_fc_sale_type='muscular_dystrophy' excluding terminal states
count_hardship x_fc_sale_type='hardship' excluding terminal states
count_acsd x_fc_client_type='ACS' excluding terminal states

6.6 ADP Pre-Approval (right column, 4 tiles)

Field Status filter
adp_waiting_app_count x_fc_adp_application_status in ('waiting_for_application','assessment_completed')
adp_app_received_count x_fc_adp_application_status='application_received'
adp_ready_submit_count x_fc_adp_application_status='ready_submission'
adp_needs_correction_count x_fc_adp_application_status='needs_correction' (rendered as urgent tile)

adp_waiting_app_count and adp_needs_correction_count are styled --urgent (red tint).

6.7 ADP Post-Approval (right column, 4 tiles)

Field Status filter
adp_approved_count x_fc_adp_application_status in ('approved','approved_deduction')
adp_ready_delivery_count x_fc_adp_application_status='ready_delivery'
adp_ready_bill_count x_fc_adp_application_status='ready_bill'
adp_on_hold_count x_fc_adp_application_status='on_hold' (rendered as urgent tile)

6.8 MOD (right column, 5 tiles)

Field Status filter
mod_awaiting_funding_count x_fc_mod_status='awaiting_funding'
mod_funding_approved_count x_fc_mod_status='funding_approved'
mod_pca_received_count x_fc_mod_status='contract_received'
mod_project_complete_count x_fc_mod_status='project_complete'
mod_pod_submitted_count x_fc_mod_status='pod_submitted'

7. Compute method clustering

Five compute methods, each owning a logical section so an expensive query in one cluster doesn't recompute the rest:

Method Fields populated
_compute_banner 6 banner fields
_compute_kpis 6 KPI fields + currency_id
_compute_activities 2 activity fields
_compute_workflow_counts 13 stage-tile fields (ADP + MOD)
_compute_secondary_counts 8 fields (bottlenecks + other funders)

All compute methods are bound to non-stored compute='_compute_*' fields (no @api.depends since TransientModel records are throwaway — every dashboard open is a fresh record). Counts use search_count() not search() to avoid loading recordsets.

8. Action methods (~24)

8.1 action_open_<bucket> (~16)

Thin wrappers returning ir.actions.act_window. Where the module already has per-stage actions (e.g. adp_claims_views.xml defines act_window_adp_ready_for_billing), reuse them via self.env.ref(...).read()[0]. Otherwise build the action inline.

Examples:

  • action_open_adp_waiting_app — opens SO list filtered to ('x_fc_adp_application_status', 'in', ['waiting_for_application', 'assessment_completed'])
  • action_open_bottleneck_no_pod — opens SO list filtered to approved-without-POD
  • action_open_my_activities — opens activity list filtered to current user

8.2 action_create_<funder>_so (8)

One per funder hotlink. Each opens a fresh sale.order form with default_x_fc_sale_type in context:

Method Context
action_create_adp_so {'default_x_fc_sale_type': 'adp'}
action_create_mod_so {'default_x_fc_sale_type': 'march_of_dimes'}
action_create_odsp_so {'default_x_fc_sale_type': 'odsp', 'default_x_fc_odsp_division': 'standard'}
action_create_wsib_so {'default_x_fc_sale_type': 'wsib'}
action_create_insurance_so {'default_x_fc_sale_type': 'insurance'}
action_create_mdc_so {'default_x_fc_sale_type': 'muscular_dystrophy'}
action_create_hardship_so {'default_x_fc_sale_type': 'hardship'}
action_create_private_so {'default_x_fc_sale_type': 'direct_private'}

User picks ODSP division on the SO form (we default to standard, they can change to sa_mobility or ontario_works).

9. Theming (SCSS structure)

9.1 File order

Tokens load first in each bundle. SCSS variables defined in _fc_dashboard_tokens.scss must be in scope when fc_dashboard.scss is compiled. Odoo concatenates SCSS within a bundle in registration order, so the manifest registration sequence is load-bearing — see §11.

9.2 _fc_dashboard_tokens.scss

Single source of truth. Define light values at top level, override with !global inside @if $o-webclient-color-scheme == dark. Token names use the $_fc-* convention (underscore prefix for "private" partials).

Light palette (22 tokens):

page-bg: #f7f7f8     card-bg: #ffffff      card-border: #d8dadd
text: #2b2b2b        text-muted: #6c7480

banner: linear-gradient(#eef2ff → #fce7f3)  border: #c7d2fe   text: #3730a3
deadline-text: #b91c1c

kpi-bg: #f0f4ff      kpi-border: #c7d2fe    kpi-num: #1e3a8a

action-bg: #ecfdf5   action-border: #6ee7b7  action-text: #047857

tile-bg: #f3f4f6     tile-border: #e5e7eb   tile-num: #111827

urgent-bg: #fee2e2   urgent-border: #fca5a5  urgent-num: #991b1b   urgent-text: #7f1d1d

activity-bg: #fefce8       activity-border: #fde047
bottleneck-bg: #fef2f2     bottleneck-border: #fecaca

Dark palette overrides (cool blue monochrome banner per Round 3 selection):

page-bg: #1a1d21     card-bg: #22262d      card-border: #3a3f47
text: #e5e7eb        text-muted: #9ca3af

banner: linear-gradient(#1e293b → #1e3a5f)  border: #3b82f6   text: #93c5fd
deadline-text: #fca5a5

kpi-bg: #1e293b      kpi-border: #334155    kpi-num: #93c5fd

action-bg: #064e3b   action-border: #047857  action-text: #6ee7b7

tile-bg: #2d3138     tile-border: #3a3f47   tile-num: #f3f4f6

urgent-bg: #4a1414   urgent-border: #7f1d1d  urgent-num: #fca5a5   urgent-text: #fecaca

activity-bg: #3a2e0a       activity-border: #854d0e
bottleneck-bg: #3a1414     bottleneck-border: #7f1d1d

9.3 fc_dashboard.scss

Layout file. Re-exports each token as a CSS custom property scoped under .o_fc_dashboard so dev-tools can inspect/tweak live, then uses both the SCSS variable (for compile-time work like darken()) and the CSS variable (for runtime). Section classes:

  • .o_fc_banner — gradient + border, flex-row with deadline countdown on the right
  • .o_fc_kpi (with .o_fc_kpi__num) — 3-up KPI tiles
  • .o_fc_pill — quick-action button pills
  • .o_fc_activities, .o_fc_bottleneck — left-column section backgrounds
  • .o_fc_tile, .o_fc_tile--urgent (with .o_fc_tile__num) — workflow stage tiles
  • .o_fc_countdown--info / .o_fc_countdown--warning / .o_fc_countdown--danger / .o_fc_countdown--muted — countdown widget colour levels (driven by OWL state)

9.4 Verification

After deploy, in odoo-shell:

env['ir.qweb']._get_asset_bundle('web.assets_backend').css()    # light bundle URL
env['ir.qweb']._get_asset_bundle('web.assets_web_dark').css()   # dark bundle URL

The two URLs must differ. If they're identical, the dark bundle didn't recompile — fix by deleting ir.attachment rows under /web/assets/% and restarting Odoo.

10. OWL countdown widget

10.1 Why a widget

The rest of the dashboard is fine being recomputed on page open — case counts move slowly. The countdown ("3 days 4 hours to cutoff") needs to tick without a page refresh, and its colour needs to shift as the deadline approaches (info → warning → danger).

10.2 Behaviour

  • Registered as a field widget under the name fc_posting_countdown.
  • Reads submission_deadline_dt from props.record.data.
  • Ticks every 60 seconds via setInterval. Cleared on onWillDestroy.
  • Four levels with auto-shift:
    • > 3 days remaininginfo (banner text colour)
    • 13 dayswarning (amber)
    • < 24 hoursdanger (urgent-num colour)
    • past deadlinemuted (text-muted colour), text reads "Cutoff passed"
  • Uses Luxon for date math (already loaded by Odoo).

10.3 Template

<templates xml:space="preserve">
    <t t-name="fusion_claims.PostingCountdown" owl="1">
        <span t-att-class="'o_fc_countdown o_fc_countdown--' + state.level"
              t-esc="state.text"/>
    </t>
</templates>

10.4 Use in form view

<field name="submission_deadline_dt"
       widget="fc_posting_countdown"
       nolabel="1"
       readonly="1"/>

11. Manifest changes

'version': '<bump minor>',   # e.g. 19.0.8.0.7 → 19.0.9.0.0 for asset cache-bust per CLAUDE.md §Asset Cache Busting

'data': [
    # ...existing entries (data files load order unchanged)...
    'views/dashboard_views.xml',  # rewritten
],

'assets': {
    'web.assets_backend': [
        # ...existing entries...
        'fusion_claims/static/src/scss/_fc_dashboard_tokens.scss',  # tokens FIRST
        'fusion_claims/static/src/scss/fc_dashboard.scss',
        'fusion_claims/static/src/js/fc_posting_countdown.js',
        'fusion_claims/static/src/xml/fc_posting_countdown.xml',
    ],
    'web.assets_web_dark': [
        'fusion_claims/static/src/scss/_fc_dashboard_tokens.scss',
        'fusion_claims/static/src/scss/fc_dashboard.scss',
        # No JS in dark bundle — Odoo loads JS once from backend.
    ],
},

Token file is registered before layout file in both bundles. JS+XML only in backend.

12. Edge cases

12.1 Pre-first-posting

If today < fusion_claims.adp_posting_base_date (default 2026-01-23), _get_current_posting_date() returns the base date itself. Treatment:

  • posting_period_label reads "Posting starts Jan 23".
  • submission_deadline_dt set to first Wednesday at 18:00.
  • KPI tiles all show $0 / 0 (no posting period to bill against yet).
  • is_pre_first_posting=True is exposed; view shows a one-line info note above the KPIs.

12.2 No invoices / empty system

All counts compute to 0. KPI tiles render $0.00. Activities section renders an empty-state message ("No activities assigned"). Bottleneck section hides itself when both counts are zero.

12.3 Sales rep with no assigned SOs

_role_filter_domain() returns [('user_id', '=', user.id)]. All counts → 0. The form still renders; "Showing your cases" hint plus an empty-state message ("You have no assigned cases").

12.4 Portal user accidentally clicks dashboard menu

The dashboard menu is already gated by groups_id on the existing menu item to fusion_claims.group_fusion_claims_user (internal users only). Confirm this is preserved in the rewritten dashboard_views.xml.

12.5 Currency mix

KPI sums assume a single company currency. currency_id defaults to company_id.currency_id. If invoices in another currency exist, they are summed in their own currency by Odoo's standard behaviour — out of scope to handle multi-currency for this dashboard. Document this limitation in the design note.

13. Decisions explicitly excluded

  • Auto-refresh on window focus — considered, dropped to keep scope tight. Manual refresh via form header button is sufficient.
  • The 4 configurable HTML panels from the existing dashboard — removed entirely. If a "Recent Cases" view is needed later, that's a separate spec.
  • Per-funder workflow tiles for ODSP / WSIB / Insurance / MDC / Hardship — those funders get a count card only, not a row of stage tiles. Decision: keep the dashboard focused on the two highest-volume funders (ADP, MOD).
  • Toggle between "My Cases" and "All Cases" — group-based auto-filter only. Sales reps see their cases, managers see everything, no switch.

14. Acceptance criteria

  1. Dashboard menu opens to a single page; old 4-panel UI gone.
  2. Banner shows current posting period and a live (ticking) countdown to Wed 6 PM cutoff.
  3. 3 KPI tiles render with correct dollar amounts for Ready / Claimed This Period / Total AR.
  4. 8 quick-action pills open a fresh SO form with the correct x_fc_sale_type pre-applied.
  5. All 17 workflow tiles show non-stale counts (verified by clicking a tile → resulting SO list count matches the tile number).
  6. Both bottleneck callouts compute and render; clicking opens the matching filtered SO list.
  7. Sales reps see only their own cases; managers see all.
  8. Light and dark themes render the dashboard without any invisible / low-contrast elements. Verified by:
    • Opening in light mode → no display:none-like artifacts, all text readable.
    • Switching to dark mode (user profile → Color Scheme → Dark → reload) → all colours shift to the dark palette, banner gradient is the cool blue monochrome.
  9. Asset bundles compile to distinct URLs in both themes (verified with the §9.4 snippet).
  10. No regression on existing dashboard menu item / action references — module loads cleanly, no XML resolution errors.

15. Open questions / non-decisions

None. All design choices are locked in. Implementation plan can proceed.