Files
igny8/Final_Flow_Tenancy.md

19 KiB

Nice, this is exactly the sort of thing that cleans up the whole mental model.

Below is a single structured view of flows, split into tight tables and mini text diagrams so you can literally walk them in frontend and API console and tick them off.

I am describing how it is intended to work, aligned with current models and views.


0. Key objects involved

Area Model / Endpoint Notes
Plans Plan model with price, billing_cycle, included_credits, stripe_* fields Defines included monthly credits and Stripe product
Accounts Account with plan, credits, status (active, trial, suspended, cancelled) Tenant root, holds current credits
Subscription Subscription with current_period_start, current_period_end, status, cancel_at_period_end Mirrors Stripe subscription state
Auth endpoints /api/v1/auth/register/, /api/v1/auth/login/, /api/v1/auth/plans/ allowed without auth Used before tenant context exists
Account/Subscription APIs AccountsViewSet, SubscriptionsViewSet under /api/v1/auth/accounts/ and /api/v1/auth/subscriptions/ Manage accounts and subscriptions

Payment methods (current state)

  • Options shown: Stripe (placeholder), PayPal (placeholder), Bank Transfer (active).
  • Stripe/PayPal: keep UI/API options but mark as "coming soon"; no charge attempts yet.
  • Bank Transfer: the only active path; capture payer details + plan, mark subscription/account pending payment until confirmed manually.
  • No country-specific rules for any method; keep global defaults.

Flow F1 - Sign up, plan selection, initial credits

Text diagram

Marketing Site → App Auth
  1) User chooses plan on pricing page
  2) Frontend loads plans from API
  3) User fills register form with plan
  4) Backend creates User + Account + (optionally) Subscription
  5) Account gets plan and starting credits

Step table

Step Frontend surface Backend call Backend effect (DB) What to verify
1 Pricing page (public) GET /api/v1/auth/plans/ Reads active Plan records via PlanSerializer Plans visible match Plan rows (name, price, billing_cycle, included_credits)
2 Register page shows chosen plan no call or same GET /auth/plans/ - Selected plan id matches an active Plan row
3 Register submit POST /api/v1/auth/register/ (body contains user + account name + plan_id) Creates User, Account(plan=Plan) and possibly Subscription if Stripe created immediately In DB check: new User, new Account with plan_id set and status = trial or active as per your rule; account.owner = user
4 Initial credits seed same register flow or follow up billing hook Account.credits should be set to plan.get_effective_credits_per_month() (from included_credits or legacy credits_per_month) After signup, check Account.credits equals the plan credits for this plan
5 Payment method choice UI shows Stripe (placeholder), PayPal (placeholder), Bank Transfer (active) Record payment_method on Account/Subscription. For now only Bank Transfer can be marked paid; Stripe/PayPal stay "coming soon" UI labels make clear Stripe/PayPal are not yet active; bank transfer selection stores choice and blocks entry until marked paid

For validation you can drive this entirely with Postman and Django admin plus the app UI.


Flow F2 - Login, tenant context, subscription gate

This validates that a user can only enter the app when account and plan are healthy.

Diagram

Login form
  → POST /auth/login/
      → Validate password
      → Ensure User has Account
      → Ensure Account has active Plan
      → Issue JWT with account_id
  → All subsequent requests:
      → Middleware sets request.account from token
      → Permissions enforce tenant access

Step table

Step Frontend surface API Backend logic Verify
1 Login form POST /api/v1/auth/login/ Finds user by email, checks password Wrong password gives 401 with "Invalid credentials"
2 Account gate same login handler If user.account is missing, returns 403 "Account not configured" Create a user without account and confirm it cannot log in
3 Plan gate same If account.plan is missing or inactive, returns 402 "Active subscription required" Mark a plan inactive, try login, see 402
4 Token issue same Generates access and refresh tokens embedding user_id and account_id in JWT, and returns them with expiry timestamps Decode JWT and check account_id == Account.id
5 Tenant context per request Any app request after login AccountContextMiddleware reads JWT, fetches User and Account, sets request.account and blocks suspended / invalid accounts Hit a random endpoint, log request.account.id and compare to user.account.id

If this flow passes, your base tenancy gate is sound.


Flow F3 - Normal app workflow and credits consumption

Think of this as the day to day life of a tenant.

Subflow F3a - Site and sector inside an account

Step UI API DB objects Verify tenancy
1 Sites page GET /api/v1/auth/sites/ Returns Site filtered by account via AccountModelViewSet and SiteSerializer Confirm only sites with site.account_id = request.account.id show
2 Create site POST /api/v1/auth/sites/ Creates Site inheriting account from request.account, counts against plan.max_sites Try creating more sites than plan.max_sites and confirm it fails as per your rule
3 Add sectors POST /api/v1/auth/sectors/ or site detail → add sector Sector belongs to Site, and Site belongs to Account. Site.get_max_sectors_limit() uses account.plan.max_industries to enforce limit Adding sectors beyond limit returns validation error; each sector.site.account_id matches account

This sets the container for Planner / Writer.


Subflow F3b - Planner credits usage

Based on the Planner technical guide.

Diagram

Keywords → Auto cluster → Ideas

Each AI operation:
  - Calculate credits needed
  - Call CreditService to deduct from Account.credits
  - Call AICore with account context
  - Store results (clusters, ideas, tasks)

Table

Stage Frontend action Endpoint Backend behavior Credits check
1. Attach keyword Keywords page, "Add keyword" POST /v1/planner/keywords/ Validates seed keyword, site, sector, then creates Keyword using SiteSectorBaseModel so account is set from site No or minimal credits used, depending on your rule
2. Auto cluster Select keywords, "Auto Cluster" POST /v1/planner/keywords/auto_cluster/ auto_cluster loads keywords, instantiates ClusteringService, calls cluster_keywords(keyword_ids, account, sector_id) In ClusteringService step 2, it "Check credits - Calculate credits needed and deduct credits from account" . Validate that Account.credits decreases accordingly after clustering
3. Idea generation Clusters page, "Generate ideas" Usually POST /v1/planner/clusters/generate_ideas/ Service collects cluster data, calls AI via AICore(account=request.account) and creates ContentIdeas Confirm one AI call consumes the planned credits and created ideas are tied to same site/sector/account

To test: run each step for a known account and watch Account.credits in admin before and after.


Subflow F3c - Writer credits usage

Pattern mirrors Planner.

Stage Frontend Endpoint Backend behavior Credits check
1. Create task from idea Ideas page, "Send to Writer" POST /v1/writer/tasks/ Creates Writer Task linked to idea, site, sector, account No credits yet or minimal
2. Generate draft Writer task detail, "Generate Article" POST /v1/writer/content/generate/ or similar Calls AI via AICore(account=request.account), passes structured prompt, receives content, stores Content row Check that for each generation, Account.credits decreases by the expected credit cost per 1k tokens
3. Regenerate sections etc Same page e.g. POST /v1/writer/content/regenerate_section/ Same AI pipeline, smaller requests Verify smaller credit deductions and that usage is logged in any Usage / Billing tables if present

AICore always initialises with an account and logs cost per request based on tokens and MODEL_RATES . You can cross check AI cost vs credit deduction.


Flow F4 - Subscription renewal and monthly or annual cycle

The renewal flow is Stripe driven, mirrored in your Subscription and Account rows.

Diagram

Stripe billing cycle
  → Stripe renews subscription (card charged)
  → Stripe sends webhook to backend
  → Backend updates Subscription row
  → Backend resets/adjusts Account.credits based on Plan.included_credits

Step table

Step Source Backend effect What to verify
1 Stripe billing engine / bank transfer confirmation Stripe (later) marks subscription renewed; today bank transfer confirmation is recorded manually against the subscription For bank transfer, admin marks payment received and sets renewal date; Stripe dashboard flows remain future work
2 Stripe webhook or admin update Webhook (future) or admin update finds Subscription by payment method and updates status, current_period_start, current_period_end, cancel_at_period_end Subscription dates/status match payment confirmation; Stripe webhook path is stubbed until integration
3 Credits reset or top up Same handler/job Sets Account.credits to plan.get_effective_credits_per_month() or adds included credits, depending on your policy At renewal/confirmation, credits jump to intended value and usage across the new period subtracts from that
4 Account status update Same billing service Ensures Account.status switches from trial or past_due back to active After payment, login is allowed again and middleware does not block due to account status

You can simulate this by editing Subscription dates and status in admin if webhooks are not wired yet, then running the same code path as webhook.


Flow F5 - Cancellation and expiry

Short but necessary to validate health.

Scenario Backend state Effects to validate
User cancels at period end Subscription.cancel_at_period_end=True while status remains active until current_period_end App continues working, credits used until period end; no new credits after end date
Subscription ends or payment fails Subscription.status becomes canceled or past_due. Billing job sets Account.status to suspended or cancelled Login returns 402 or 403 according to your rule, Planner/Writer calls fail with proper error and do not consume credits

How to actually use this

You can turn this into a verification pass like:

  1. Pick a clean test user and Stripe test customer.
  2. Walk F1 then F2 in sequence and confirm all DB fields line up.
  3. Run through F3a, F3b, F3c and log credits before and after each AI-heavy action.
  4. Simulate renewal and cancellation via Stripe or DB and walk F4 and F5.