Suspension Treatment — Open Decision
Status: Unresolved as of 2026-05-04. We need to agree which dashboard views include suspended accounts and which don't, and apply the rule consistently.
The issue
Suspended accounts have ri.suspendTill set on the recurring invoice. A common pattern is suspendTill = 2100-12-31 (sentinel "indefinite suspension") — billing is paused, no invoices issued, no cash collected, but the line is still in the CRM as Fulfilled / Completed.
None of the bridge endpoints currently filter on suspendTill. They check firstInvoiceOn, expireOn, and dateOfCancellation, but a line that's been frozen indefinitely still passes the active-in-month test and contributes MRR.
Surfaced via Kinsella Storage (38029959) on 2026-05-04: signed Dec 2025, BizSite Network-M recurring set to suspendTill=2100-12-31 with no invoices ever issued, but nextInvoiceOn 2026-03-31 → counted as €24/mo "new client" sold by John Fownes in 2026. Not actually billing. Not a real €24.
Affected endpoints
All three bridges share the same active-in-month logic:
| Endpoint | Suspended currently treated as… | Should they be? |
|---|---|---|
/dashboard-revenue-bridge |
Active (counts toward opening/closing MRR, not lost) | ? |
/dashboard-am-bridge |
Active | ? |
/dashboard-sales-rep-bridge |
Active (credited as new/returned/increased sales) | ? |
/dashboard-am-portfolio |
Active | ? |
/dashboard-solution-portfolio |
Active | ? |
/dashboard-suspensions |
Explicitly listed (this is the suspension report) | n/a |
/dashboard-check-account |
Active line shown with suspendTill flag | Probably correct as-is |
The competing interpretations
- Cash / billing view: Exclude suspended. We're not invoicing, not collecting, not earning. Including them inflates MRR figures and hides churn risk.
- Customer view: Include suspended. They're still our customer on paper; the AM still owns them; the sales rep still closed the deal. Excluding them shifts the number from "what we have" to "what we're billing".
- Mixed view: Include in account counts (it's still an account), exclude from MRR (we're not billing it).
Open questions to agree
- What's the canonical definition of "MRR" in revenue-bridge?
- "Recurring billings issued this month" → exclude suspended.
- "Recurring contracts on the books" → include.
- When does a suspension become a churn event? Currently a suspended-then-cancelled account fires "lost" only on cancellation. Should the suspension itself fire a loss with a "suspended" status?
- Should sales reps be credited for sales that immediately suspended? The Kinsella case is interesting — John genuinely closed the deal, but no money flowed.
- AM-portfolio account counts — when an AM looks at "their book", do they expect suspended accounts in the count?
Practical impact (rough — needs measurement)
We don't currently have a clean count of suspended accounts in each bridge view. Quick check via /dashboard-suspensions:
curl -H "x-api-key: $KEY" \
https://fcr-dashboard-api.fcrmedia.workers.dev/dashboard-suspensions \
| jq '.accountCount, .totalSuspendedMRR'
This is probably a few percent of the book — material enough to notice in a corkscrew, not enough to break trend lines.
Recommendation
Hold a 15-min decision call covering:
- One canonical rule per endpoint family (bridges, portfolios)
- Whether to add a
suspendTillfilter to active-in-month or expose a?includeSuspended=trueflag - Whether suspension transitions need to be a first-class movement type in the bridges (alongside new/lost/increased/etc.)
Once decided: a single shared util applies the rule across all three bridges + portfolios. Revisit before the next financial reporting cycle.
Related
worker/src/handlers/revenue-bridge.js— active-in-month check (line ~157-174)worker/src/handlers/am-bridge.js— same checkworker/src/handlers/sales-rep-bridge.js— same checkworker/src/handlers/suspensions.js— the only endpoint that's currently aware of suspension state- Kinsella Storage 38029959 — canonical example for the signed-but-suspended-immediately pattern
FCR Dashboard documentation · generated from docs/ · keep counts verified, not guessed.