RevRec brainstorm · Supporting · ASC 606 domain notes

01 — Domain: ASC 606 first principles

Status: draft
Date: 2026-07-29
Canonical home: this file in revrec-next
Stance: Design from the accounting problem and the product promise — not from contracts.py, Hotglue joins, or OrderDetails sheet archaeology.
Companion product page: chargebee.com/revenue-recognition-software
Visual: ../visuals/revrec-asc606-first-principles.html
Extract plane: ~/work/temporal_poc (see 05-extract.md)
ADRs: 0001, 0002


0. Direct answer to your challenge

Are we assuming we need all this random merge / asof logic?

No. That was a bad assumption.

merge_asof, subscription-closure reloads, inventing future invoice-linked order lines from ramps, and pandas snapshot joins are not ASC 606. They are a reconstruction strategy: “we only trust Chargebee billing events, so we reverse-engineer a contract/PO view by joining invoices to contract terms on dates.”

ASC 606 needs a contract + performance obligations + recognition schedule.
Billing needs an AR / cash / invoice view.
Those are related but not the same object. Conflating them is why the ETL looks insane.

A greenfield system should model ASC 606 directly. Use Chargebee subscription / contract-term / catalog / amendment signals as contract inputs where possible. Treat invoices / CNs / payments as billing & consideration events — not as the thing that invents the contract via asof joins.


1. The problem RevRec is solving (from the product)

From Chargebee’s RevRec positioning:

Customer pain Product claim
Monetization changes (usage, credits, ramps, custom terms) create recognition obligations Finance can’t keep up with One rules engine across recurring / variable / hybrid
Manual recon across contracts, invoices, amendments Automated schedules as billing events occur
GL overloaded with detail Revenue subledger — detail in RevRec, summarized journals to GL
Slow close ASC 606 / IFRS 15 five-step automation + recon + posting
Need ARR/NRR/deferred waterfall + drill to contract/line Reporting on the subledger

Product shape (what Finance buys):

  1. ASC 606/IFRS 15 engine (five steps + modifications)
  2. Revenue subledger (schedules, deferred, unbilled, audit trail)
  3. AR view (invoices, payments, CNs, aging) — overlaps Accounting Premium
  4. Close + journal posting to GL
  5. Reporting (waterfall, SaaS metrics, drill-down)

Integrations (Chargebee Billing, Stripe, NS, …) are how data enters — not the definition of recognition.


2. ASC 606 in one page (the real domain)

Core principle: Recognize revenue to depict transfer of promised goods/services in an amount reflecting consideration the entity expects to be entitled to.

Five steps (the only mandatory “pipeline”)

Step Meaning Typical SaaS/subscription inputs
1. Contract Enforceable agreement; rights; payment terms; commercial substance; collectibility probable Customer + subscription / order / MSA+SOW; CB subscription + contract_term
2. Performance obligations Distinct promises (or a series of distinct services with same pattern) Plan/item access over term; setup; training; hardware; credits/material rights
3. Transaction price Expected consideration (fixed + estimated variable, constrained) List/sale amounts, discounts, expected usage, breakage, credits
4. Allocate Relative standalone selling prices (SSP) SSP library / observable prices / residual
5. Recognize When (or as) control transfers — over time or point in time Ratable over service period; PIT on delivery; proportional on units delivered

What the standard cares about that spreadsheets usually botch

What ASC 606 does not require

Those are implementation accidents of “billing-feed → fake sales order.”


3. How today’s RevRec product models it (clean mental model)

Chargebee’s own docs describe three transaction feeds into the recognition engine:

Feed Role in ASC 606 terms
OrderDetails Sales-order / obligation lines — what was sold, service window, amounts to allocate/recognize
BillingSchedule What was invoiced against those orders — billing / AR side
ProfessionalServicesDelivery Evidence of satisfaction for proportional / delivery-based POs

Plus config: Products, SSP library, Revenue rules (Ratable / Point-in-time / Proportional Performance).

So even in the current product, the intended architecture is:

Contract / order lines  →  allocate (SSP)  →  recognition schedule
        ↑                         ↑
   (sold promises)          (policy)

Invoices / payments     →  billing / AR / cash application
        ↑
   (consideration timing)

Hotglue Chargebee path tries to manufacture OrderDetails from invoices+subs so the engine has something to chew. That manufacturing step is where contracts.py lives — and where complexity exploded.

Invoice-as-contract / disable_contract_creation modes already admit a simpler world: billing lines ≈ order lines. That exists because many merchants don’t need full contract reconstruction.


4. Separation that should drive greenfield design

┌──────────────────────────────────────────────────────────┐
│ CONTRACT & RECOGNITION SUBLEDGER (ASC 606)               │
│  Contract → POs → TP → Allocation → Recognition schedule │
│  Modifications, SSP, deferred / unbilled, waterfall      │
└──────────────────────────────────────────────────────────┘
                         ▲
                         │ events (create/amend/cancel PO;
                         │         bill; pay; credit; deliver)
┌──────────────────────────────────────────────────────────┐
│ BILLING & CASH (AR)                                      │
│  Invoice, CN, payment, refund, dunning                   │
│  (Accounting Premium / AC_* lives mostly here)           │
└──────────────────────────────────────────────────────────┘
                         ▲
                         │
┌──────────────────────────────────────────────────────────┐
│ SOURCE FACTS (Chargebee canonical)                       │
│  Customer, subscription, contract_term, catalog,         │
│  invoice, CN, transaction, usage, …                      │
└──────────────────────────────────────────────────────────┘

Rule: Source facts update either the contract/recognition model or the billing/AR model (sometimes both, with explicit links) — never “one mega join that emits fake orders.”


5. When is recognition actually hard? (essential complexity)

Scenario Why ASC 606 needs real logic Still need asof-join ETL?
Single SaaS PO, ratable over known term, invoice aligns to term Almost mechanical No
Multi-PO bundle + discount (sub + implementation) SSP allocation No — need POs + SSP, not invoice asof
Mid-term upgrade / downgrade Modification accounting No — need amendment event + policy
Ramp / multi-year price schedule TP over periods; maybe series No — ramp is contract data, not invoice archaeology
Usage / variable consideration Estimate + constrain; true-up No — usage events + policy
Credit note Depends: variable consideration vs modification vs return Policy table — not reason-code → merge_asof spaghetti as the architecture
Free PO / material right Identify unstated PO; allocate Config + SSP — not billing joins
Proportional PS delivery Delivery evidence Delivery feed — already a first-class RevRec input

Essential complexity = policy + state machine on contracts/POs.
Accidental complexity = reconstructing POs from invoice time-series because the contract wasn’t modeled.


6. What a greenfield recognition engine looks like (no current constraints)

Entities (illustrative)

Operations (the real “API” of the domain)

  1. open_contract(...) — Steps 1–4
  2. amend_contract(...) — modification policy
  3. record_billing(...) — Steps 3 updates / AR / liability
  4. record_delivery(...) — for proportional / PIT
  5. recognize(period) — Step 5 close waterfalls
  6. post_journals(period) — subledger → GL

Source mapping (Chargebee-shaped, still first principles)

Chargebee fact Likely effect
Subscription created + items + contract_term open_contract / POs from catalog rules
Subscription changed (plan, quantity, term) amend_contract
Ramp created/updated Amend future PO amounts/periods
Invoice created record_billing (not “create order via asof”)
Credit note Policy: amend TP / reverse recognition / AR credit
Cancellation Amend / terminate remaining PO; catch-up per policy
Usage Variable consideration true-up

Invoices do not invent the contract. They move cash timing and often confirm collectibility / constraint.

Ops store vs ClickHouse

We keep an ops store because ASC 606 state is mutable and event-sourced, not because Hotglue used Redis. If you later prove a pure event-log + rebuild model, fine — still not “CH as merge-asof SoR.”


7. Implications for “boil the ocean”

Keep questioning Likely keep
OrderDetails as the truth format ASC 606 five-step domain model
Invoice→order asof reconstruction Explicit contract/PO from subscription/terms/CPQ
Pandas snapshot closure as architecture Event-sourced apply of source facts
Camunda/Hotglue Temporal extract + domain workflows
Assuming 234 ETL fixtures = product truth Golden accounting scenarios (modification types, multi-PO, usage)

Parity with today’s Hotglue output is a migration/compatibility goal, not the definition of correctness. Correctness is ASC 606 + audit trail + close.


8. Minimal viable recognition (prove the thesis)

Build the smallest engine that:

  1. Opens a contract from a CB subscription + one PO (plan) + term dates
  2. Allocates 100% TP (sale price)
  3. Recognizes ratably by day/month
  4. Applies an invoice as billing (deferred ↓ as recognized; AR as billed)
  5. Handles one upgrade (prospective) and one CN policy

If that covers a large % of Chargebee→RevRec merchants, the mega-join ETL was mostly accidental.
Complex SSP / multi-PO / usage then layer as explicit features — not as emergent pandas.


9. Open product questions (now grounded in ASC 606)

  1. For Chargebee-native merchants, is the subscription (+ contract_term) the Step-1 contract, or do we still need a synthetic “sales order” abstraction?
  2. What % of volume is single-PO ratable SaaS vs multi-element / usage / PS? (drives how much engine you build first)
  3. Are ramps / IBM first-class contract attributes in Billing today, or only recoverable via inference?
  4. Should Accounting Premium (AR subledger) and RevRec (revenue subledger) share one ledger core with two projections, or stay separate products with a shared extract?

10. Bottom line

Next: fill 03-event-catalog.md and 04-engine-scenarios.md. Architecture: 02-architecture.md.