03 — Event catalog (Chargebee → domain)
Status: draft (MVP filled)
Rule: Source facts become domain events. Invoices do not invent contracts.
Spec: 08-mvp-spec.md · Examples: 07-asc606-worked-examples.md
When you change a row, update 04-engine-scenarios.md if behavior changes.
1. Domain operations (apply API)
| Operation |
Intent |
MVP |
open_contract |
Steps 1–4 |
yes |
amend_contract |
Modification |
prospective only |
record_billing |
Invoice → AR + deferred timing |
yes |
record_payment |
Cash application |
yes |
record_credit |
CN with named policy |
one default policy |
record_delivery |
Proportional / PIT |
later |
recognize |
Period Step 5 |
yes |
post_journals |
GL summary |
yes |
terminate |
Cancel remaining |
yes (policy TBD) |
2. Chargebee streams → events
Tap reference: ~/work/tap-chargebee streams. MVP uses a subset.
| Chargebee stream / fact |
Domain emit |
MVP |
Notes |
customers |
upsert Customer |
yes |
Identity only |
subscriptions created |
open_contract |
yes |
POs from subscription items + term |
subscriptions changed |
amend_contract |
yes |
Plan/qty/term; prospective |
subscriptions cancelled |
terminate |
yes |
Policy on remaining schedule |
contract_term (nested or related) |
feeds open/amend |
yes |
Service window for ratable PO |
items / item_prices / plans / addons |
product + list/sale hints |
yes |
Catalog for PO product_ref / SSP later |
invoices |
record_billing |
yes |
Never open_contract |
credit_notes |
record_credit |
yes |
Policy table |
transactions |
record_payment |
yes |
Link to invoice |
unbilled_charges |
optional billing signal |
later |
|
events |
realtime trigger |
later |
Extract can poll without this |
coupons / discounts on invoice |
TP / billing line attributes |
partial |
MVP: amount on invoice lines enough |
| ramps / IBM |
amend_contract |
later |
First-class if API exposes; else don’t infer via asof |
orders (CB shipping orders) |
usually ignore for SaaS RevRec |
no |
Not ASC PO by default |
3. Field-level sketch (MVP)
3.1 open_contract ← subscription created
| Domain field |
Chargebee source (typical) |
source_ref |
subscription.id |
customer_id |
subscription.customer_id |
currency |
subscription / customer currency |
service_start / service_end |
contract_term start/end, or current_term_start/end |
product_ref |
item_price_id / plan_id |
transaction_price |
known sale for term (from subscription item amounts or first invoice — prefer subscription/price, not inventing PO from invoice lines) |
method |
org/product rule default ratable |
Open product question (ADR later): if sale amount only appears on first invoice, may read invoice amount as TP input without treating invoice as contract creator.
3.2 record_billing ← invoice
| Domain field |
Chargebee source |
source_ref |
invoice.id |
date |
date / generated_at |
total / lines |
line_items amounts |
contract_link |
subscription_id on line or invoice |
customer_id |
customer_id |
Effect: AR + deferred identity — no new PO.
3.3 record_payment ← transaction
| Domain field |
Chargebee source |
source_ref |
transaction.id |
invoice_id |
linked invoice |
amount |
amount |
3.4 record_credit ← credit_note
| Domain field |
Chargebee source |
source_ref |
credit_note.id |
reason |
reason_code → policy id |
amount / lines |
line items |
invoice_link |
reference invoice |
4. Policies (named; not buried in joins)
| Policy ID |
Applies |
MVP default |
mod.upgrade |
mid-term up |
prospective |
mod.downgrade |
mid-term down |
prospective |
cn.refund_unrecog |
most CNs |
reduce unrecognized / deferred |
cn.reverse_recog |
return-like |
later |
cn.ar_only |
billing credit only |
later |
term.cancel |
cancellation |
TBD ADR |
recog.method |
PO |
ratable |
Map CB reason_code → policy id in config (steal ideas from Hotglue apply_accounting_treatment; do not steal its control flow).
5. Today vs Next (same CB data)
| CB data |
Current Hotglue→RevRec |
RevRec Next |
| Subscription + term |
Input to asof join with invoices |
Primary open_contract |
| Invoice |
Often creates OrderDetails via match |
record_billing only |
| Snapshots / history CSV |
Required for consistent rebuild |
Raw lake + ops state; replay events |
| CN reason codes |
Treatments inside contract rebuild |
record_credit + policy |
| Catalog |
Product sheet + synthetic products |
Product refs; SSP library later |
| Full closure reload |
Every job for touched subs |
Not required for apply |
6. Idempotency & identity
- Apply key:
(tenant_id, source='chargebee', source_event_id)
- Prefer CB resource id + updated_at/version or event id when using
events stream
- Always store
source_ref on domain entities
- Raw lake keeps verbatim payload
7. Explicit non-goals
merge_asof(invoice, contract_term) to create POs
- OrderDetails as system of record
- Full subscription history reload to “close” a sync
- Supporting every tap stream on day one
Checklist
- [ ] ADR: subscription = Step-1 contract?
- [ ] ADR: where TP comes from if not on subscription
- [ ] ADR:
term.cancel and CN overflow behavior
- [ ] Config map: CN reason_code → policy id
- [ ] Implement normalize functions per stream