08 — MVP product & engine spec
Status: draft spec (design lock candidate)
Scope: Thin engine that proves the simple product. Not full multi-PO/SSP/usage day one.
If this MVP covers a large merchant %, most Hotglue complexity was accidental.
1. Goals
| ID | Goal |
|---|---|
| G1 | Correct ASC 606 for single-PO ratable SaaS from Chargebee |
| G2 | Billing/AR sibling from invoices, payments, credit notes |
| G3 | Period close: recognize + deferred waterfall + exportable journals |
| G4 | Audit: every balance change tied to a source event id |
| G5 | Operable extract (owned), not Hotglue-as-architecture |
Non-goals for MVP: multi-PO SSP, usage variable consideration, proportional PS delivery, Hotglue CSV parity, Camunda, Catalyst/ERP.
2. Actors & systems
| Actor | Responsibility |
|---|---|
| Chargebee | Source of monetization facts |
| Extract | Pull → raw lake + cursors (Temporal) |
| Apply | Map raw → domain events → mutate ops DB |
| Close | Period recognize / lock / journal export |
| Serve | API + reports; ClickHouse for heavy analytics |
| Finance user | Close, recon, drill-down (UI later) |
3. Domain entities (MVP)
| Entity | Key fields (illustrative) |
|---|---|
Customer |
id, source_ref, currency |
Contract |
id, customer_id, source_ref (subscription id), status, start, end? |
PerformanceObligation |
id, contract_id, product_ref, method=ratable, service_start, service_end, allocated_amount |
RecognitionScheduleLine |
po_id, period, amount, status (open/recognized) |
BillingDocument |
id, type=invoice|credit_note, source_ref, customer_id, date, total |
BillingLine |
document_id, amount, optional po_id link |
CashApplication |
payment_ref, invoice_id, amount |
JournalBatch |
period, lines (account, amount, dims), status |
AppliedEvent |
tenant_id, source, source_event_id, type, applied_at — idempotency |
Balances derived or maintained: deferred liability, AR, revenue YTD — product choice; must be reconcilable to events.
4. Apply API (normative)
4.1 open_contract
Input: customer, subscription id, items (one plan for MVP), term start/end, TP (sale amount), method=ratable.
Effect: Contract + one PO + allocation 100% + schedule lines (monthly or daily — pick one in ADR).
Idempotent on subscription create event.
4.2 amend_contract (MVP: upgrade/downgrade prospective only)
Input: effective date, new TP for remaining term (or new price × remaining).
Effect: Close/recompute remaining schedule lines; past recognized unchanged.
Out of MVP: cumulative catch-up.
4.3 record_billing
Input: invoice id, lines, amounts, dates, link to contract/subscription.
Effect: BillingDocument + lines; update AR; set/increase deferred as needed per accounting identity.
Does not create POs.
4.4 record_payment
Input: transaction ↔ invoice.
Effect: Cash application; AR ↓.
4.5 record_credit (MVP: one policy)
Default policy cn.refund_unrecog: reduce deferred / remaining schedule; do not reverse already recognized unless CN exceeds unrecognized (then define overflow — TBD ADR).
Effect: BillingDocument credit + schedule adjustment + audit.
4.6 recognize(period)
Input: tenant, period (e.g. 2026-01).
Effect: Sum open schedule lines for period → revenue; deferred ↓; mark lines recognized.
Idempotent for period if not locked.
4.7 post_journals(period)
Input: period.
Effect: Summarized Dr/Cr batch (Revenue, Deferred, AR, Cash) suitable for GL export. Detail stays in subledger.
4.8 Cancel / terminate (MVP)
Remaining open schedule → zero or recognize catch-up per policy term.cancel (choose ADR: recognize remaining vs write off — product).
5. Accounting identities (MVP)
Prepaid annual (example A in 07):
On invoice (paid): Dr Cash 1200
Cr Deferred 1200
On recognize/mo: Dr Deferred 100
Cr Revenue 100
Invoice unpaid then paid: AR instead of Cash on bill; payment clears AR.
Engine must keep: ∑ recognized + deferred_remaining ≈ allocated TP (after amendments/credits per policy).
6. Chargebee coverage (MVP entity set)
| Must pull | Why |
|---|---|
customers |
Party |
subscriptions (+ nested/related contract term) |
Contract / term |
items / item_prices or plans |
Product + price |
invoices |
Billing |
credit_notes |
Credits |
transactions |
Payments |
Nice-to-have later: unbilled_charges, events (realtime), ramps/IBM if first-class in API.
Full mapping: 03-event-catalog.md.
7. Acceptance tests
Scenarios S01–S05 in 04-engine-scenarios.md must pass.
Shadow compare vs old path (optional): amounts by period/subscription within tolerance — not sheet shape.
8. Explicitly deferred (not “forgotten”)
| Feature | When to pull in |
|---|---|
| Multi-PO + SSP | Cohort evidence + ADR |
| Usage / variable consideration | Cohort + policy |
| Cumulative catch-up mods | Finance ask |
| Proportional delivery | PS merchants |
| Invoice-as-contract compatibility mode | Only if product requires |
| AC Premium full feature parity | After AR projector MVP |
9. Architecture binding
This spec assumes the four-layer design in 02-architecture.md and deep dive in 09-architecture-deep.md.
Tech opinions (Temporal, Python, ops DB, ClickHouse) are replaceable; the apply API and entities are not without a new ADR.