Centralize billing for all NexaSystems services (NexaCloud, NexaDesk, NexaMaps, custom apps, memberships) on the Odoo 19 Enterprise instance, replacing Lago. The module adds only the metering + integration layer; native sale_subscription / account_accountant / payment_stripe do all the financial work (invoicing, HST, dunning, portal, credit notes, Stripe). Includes: - Design spec (docs/superpowers/specs/2026-05-27-nexa-billing-centralized-design.md): 6 locked decisions, architecture, data model, usage engine, Lago-shaped API, webhook control loop, NexaCloud pilot, phased dual-run migration. - Module scaffold: 7 fusion.billing.* models (service, account.link, metric, charge, usage, webhook, reconciliation), bearer-auth API controller shell, security ACLs, README. Compiles on Odoo 19.0; engine/API bodies are stubs pending the implementation plan. - CLAUDE.md rule #15: no sale.subscription model in Odoo 19 — a subscription is a sale.order(is_subscription) + sale.subscription.plan (verified live). Task 0 verified: a single Stripe account is shared across NexaCloud and all Lago providers, so no Stripe account/card migration is required. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
71 lines
3.5 KiB
Markdown
71 lines
3.5 KiB
Markdown
# Fusion Centralized Billing (`fusion_centralize_billing`)
|
|
|
|
Centralized billing engine that makes this Odoo 19 **Enterprise** instance the single
|
|
billing brain for every NexaSystems service — **NexaCloud** hosting, **NexaDesk** chat,
|
|
**NexaMaps** API, custom apps, and memberships. It replaces Lago and absorbs NexaCloud's
|
|
home-grown Stripe billing into one customer ledger and one accounting system.
|
|
|
|
> **Design spec:** [`docs/superpowers/specs/2026-05-27-nexa-billing-centralized-design.md`](../docs/superpowers/specs/2026-05-27-nexa-billing-centralized-design.md)
|
|
>
|
|
> **Status:** **SCAFFOLD.** Models + security + the API auth shell are in place and the
|
|
> module installs. The usage engine, full inbound API, and webhook processor are stubs
|
|
> to be implemented from the writing-plans output.
|
|
|
|
## Why this module is small
|
|
|
|
We build **only** the metering + integration layer. Everything financial — recurring
|
|
invoicing, HST tax, proration, dunning, customer portal, credit notes, Stripe — is
|
|
**native Odoo Enterprise** (`sale_subscription`, `account_accountant`, `payment_stripe`),
|
|
already installed and running.
|
|
|
|
## Design decisions (locked)
|
|
|
|
1. Odoo fully replaces Lago (we build the metered-billing engine; Lago is decommissioned last).
|
|
2. One unified `res.partner` per client; **separate invoice per service**.
|
|
3. **Apps drive**, Odoo is the billing system of record — apps call the inbound API (as they call Lago today); Odoo bills and webhooks back.
|
|
4. Odoo owns the **billing catalog**; apps own **feature entitlements** (shared `plan_code`).
|
|
5. Pilot = **NexaCloud**, phased dual-run cutover.
|
|
6. **Aggregate-push** usage ingestion (periodic counters, not a raw-event firehose).
|
|
|
|
## Models (`fusion.billing.*`)
|
|
|
|
| Model | Purpose |
|
|
|---|---|
|
|
| `fusion.billing.service` | One source app; bearer API key (hashed) + webhook config. |
|
|
| `fusion.billing.account.link` | External account id → one `res.partner` (identity resolution). |
|
|
| `fusion.billing.metric` | Billable metric + aggregation (sum/max/last/unique). |
|
|
| `fusion.billing.charge` | Plan + metric → included quota + overage pricing. |
|
|
| `fusion.billing.usage` | Aggregated per-period usage rollups (idempotent). |
|
|
| `fusion.billing.webhook` | Outbound lifecycle event queue (HMAC + retry). |
|
|
| `fusion.billing.reconciliation` | Dual-run Odoo-vs-app delta during cutover. |
|
|
|
|
> **Odoo 19 note (verified):** a subscription is a `sale.order` with `is_subscription=True`
|
|
> (`plan_id` → `sale.subscription.plan`). There is **no** `sale.subscription` model.
|
|
> `fusion.billing.usage.subscription_id` therefore points at `sale.order`.
|
|
|
|
## Inbound API
|
|
|
|
Lago-shaped REST under `/api/billing/v1/*`, bearer auth. Endpoints mirror NexaDesk's
|
|
existing `lago-client.ts` so migration is a thin client swap. `/health` works today;
|
|
the rest return `501` until implemented.
|
|
|
|
## Relationship to `fusion_api`
|
|
|
|
`fusion_api` manages **outbound** provider keys (OpenAI, Maps, Twilio) + cost tracking —
|
|
i.e. COGS. This module tracks **customer** revenue. Complementary: feed `fusion_api`
|
|
cost into margin reporting; reuse its daily-rollup aggregation pattern.
|
|
|
|
## Dependencies
|
|
|
|
`account_accountant`, `sale_subscription`, `sale_management`, `payment_stripe`.
|
|
|
|
## Local dev
|
|
|
|
```bash
|
|
docker exec odoo-nexa-app odoo -d nexamain -u fusion_centralize_billing --stop-after-init
|
|
# tests (once added):
|
|
docker exec odoo-nexa-app odoo -d nexamain --test-enable --test-tags /fusion_centralize_billing -u fusion_centralize_billing --stop-after-init
|
|
```
|
|
|
|
Canadian English, CAD, HST via `account.tax`. New fields on native models use the `x_fc_*` prefix.
|