From 1f5eaf0386e706345b57033f51dbb027ffa5a968 Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Wed, 27 May 2026 14:52:28 -0400 Subject: [PATCH] =?UTF-8?q?docs(billing):=20handoff=20update=20=E2=80=94?= =?UTF-8?q?=20sub-project=20#2=20complete=20(2a/2d=20shipped,=202b/2c=20co?= =?UTF-8?q?de-complete)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...26-05-27-fusion-billing-session-handoff.md | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/docs/superpowers/2026-05-27-fusion-billing-session-handoff.md b/docs/superpowers/2026-05-27-fusion-billing-session-handoff.md index 8a157a97..99388329 100644 --- a/docs/superpowers/2026-05-27-fusion-billing-session-handoff.md +++ b/docs/superpowers/2026-05-27-fusion-billing-session-handoff.md @@ -78,3 +78,51 @@ start with?** Cut each new feature branch from `main`, and land it before starting the next. For any cross-branch git surgery, use a **throwaway `git worktree`** — never switch the shared working dir's branch, because a concurrent session may be working on it. + +--- + +## UPDATE — sub-project #2 complete (2026-05-27, later session) + +All four chunks of #2 are now built. The brainstorm "which slice" question resolved to +2a-first; everything else followed. + +**Done + on `main` in `Odoo-Modules` (fully tested on odoo-trial, suite `FCB_EXIT=0`):** +- **2a — importer** (`fusion.billing.import.wizard`): read-only `psycopg2` reader split + from pure-Odoo writes; users→partners+links, plans→`cpu_seconds` charge catalog + (`plan_id` NULL), deployments→one **draft shadow** `sale.order` each with the flat price. + Shadow-safe by construction (draft + no token + NULL `plan_id`). Idempotent, dry-run, + Test-Connection guard, README runbook. +- **2d — reconciliation** (`fusion.billing.reconciliation`): `_compute_reconciliation` + + `_reconcile_rows` (Odoo flat+overage vs NexaCloud actual, status match/delta), reader for + NexaCloud usage+invoice actuals, "Run Reconciliation" button. **Upsert key is + `(service, external_subscription_id, period)`** — per subscription, so a customer with + two deployments doesn't collide. +- **/usage enabler**: `_api_record_usage` resolves a subscription by the source app's own + id (`x_fc_nexacloud_subscription_id`) so NexaCloud can push against shadow subs. +- Core-engine bug fixed in passing: `charge.price_per_unit` is now `Float(16,6)` and + `_compute_billable` keeps 6-dp precision (was `Monetary`/cent-rounded → would under-bill + sub-cent rates and drift from NexaCloud's 4-dp amounts). + +**Code-complete in `Nexa-Cloud` (feature-flagged, NOT deployed, NOT integration-tested):** +- **2b — usage push**: `services/odoo_billing_client.py` + a hook in `usage_metering.py` + posting cpu-seconds to Odoo `/usage`. **2c — control loop**: + `routers/odoo_billing.py` (`POST /api/v1/billing/webhooks/central`, HMAC-verified) + + `services/odoo_billing_integration.py` (suspend/restore/deprovision). All INERT unless + `ODOO_BILLING_ENABLED`. Implemented as NEW modules + edits to clean files only — + NexaCloud `main` had concurrent **Cursor uncommitted WIP** (`routers/billing.py`, + `scheduler.py`, `stripe_service.py`, `models/billing.py`, …) which was deliberately not + touched. Commits: `94542ec` + `956abb2` (only my files staged). + +**Remaining before go-live (gated on infra / ops you do):** +1. Grant the read-only DSN (`fusion_billing.nexacloud_dsn`) — see the module README — then + Test Connection → dry-run import → review → real import. +2. Run a dual-run cycle (Run Reconciliation), confirm all rows `match`. +3. **2c needs the Odoo side to actually EMIT** `invoice.payment_failed` / + `payment_succeeded` / `subscription.terminated` webhooks with `deployment_id` in the + payload — that emission isn't wired yet (it belongs to the live billing flow). The + NexaCloud receiver is built to that contract; confirm the payload shape when wiring it. +4. Integration-test + deploy the NexaCloud changes (no test harness in that repo). +5. The flip: set `charge.plan_id`, attach Stripe tokens, confirm the shadow subs. + +Specs/plans: `specs/2026-05-27-nexacloud-billing-importer-design.md`, +`specs/2026-05-27-nexacloud-reconciliation-design.md`, and the matching plans.