- CRITICAL: reconciliation upsert keyed on (service, partner, period) collided
when one customer has two deployments (two subs) in a period — the second
overwrote the first. Add external_subscription_id to the model + a
UNIQUE(service_id, external_subscription_id, period) constraint, and key the
upsert per subscription. New test proves two subs for one partner keep two rows.
- raise a clear error if the nexacloud service is missing (was a confusing
per-row failure).
- _fc_resolve_subscription: the integer fallback no longer reaches a different
service's tagged subscription (latent multi-service IDOR); live untagged subs
stay resolvable and the partner-link authz is unchanged.
Full suite green on odoo-trial.
_api_record_usage now resolves the target subscription via the source
app's own id (x_fc_nexacloud_subscription_id, scoped to the service)
before falling back to a direct Odoo sale.order id. This is what lets
NexaCloud push usage against the shadow subscriptions the importer
created from NexaCloud UUIDs — closing the flip-day mapping gap the
review flagged. Authz unchanged (partner must be linked to the service).
Add _match_api_key() class method to fusion.billing.service, with a
TDD test suite (TestServiceApiKey) covering key generation, hash storage,
positive match, and rejection of bad/inactive keys. Also fix
fcb_test_on_trial.sh to use --http-port 8070, as Odoo 19 forces
http_spawn() even under --no-http when --test-enable is set.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>