Task 30 of self-serve signup Phase 2. Super-admins can now manage Stripe
IDs, display names, prices, and public/archived flags via the existing
admin plan-limits endpoints.
- GET /admin/plan-limits now outer-joins plan_billing and returns
merged PlanLimitWithBillingResponse rows. Plans without a
plan_billing row return None for the billing fields.
- PUT /admin/plan-limits accepts the new optional billing fields and
upserts plan_billing in the same transaction. If no plan_billing
row exists for the plan and the body includes any billing field, a
row is created (display_name defaults to plan.capitalize() when
omitted; display_name is never NULLed out on an existing row).
- After commit, the handler queries account_ids on the affected plan
and calls BillingService.invalidate_billing_cache(account_ids).
This is a no-op stub today (logs only) — there's no in-process
billing cache yet. TODO comment marks the wire-up point.
- 3 new integration tests cover GET-with-billing-present, PUT creating
a plan_billing row, and the invalidation hook being awaited with a
list of account_ids.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds complete super_admin panel with 9 pages and account owner categories page.
Backend includes 5 new DB tables, ~25 API endpoints, settings manager with
in-memory cache, and 29 integration tests. Frontend includes reusable admin
components (DataTable, Pagination, ActionMenu, etc.) with code-split lazy loading.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>