44 lines
1.3 KiB
Python
44 lines
1.3 KiB
Python
"""
|
|
app/tenancy/context.py
|
|
----------------------
|
|
Lightweight, detached descriptor for the resolved tenant. Populated by the
|
|
resolver while a control-plane session is open, then carried on `g.tenant`
|
|
for the lifetime of the request. Holds no live ORM object — safe to use after
|
|
the control session closes.
|
|
|
|
MT-5: plan feature flags and quota limits are included so quota.py and
|
|
gates.py can read them from g.tenant without a second control-DB round-trip.
|
|
"""
|
|
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class TenantContext:
|
|
id: int
|
|
slug: str
|
|
name: str
|
|
plan_id: int
|
|
db_uri: str
|
|
|
|
# MT-5: plan fields (None = unlimited / not loaded)
|
|
plan_code: Optional[str] = None
|
|
|
|
# Quota limits (None = unlimited)
|
|
max_users: Optional[int] = None
|
|
max_facilities: Optional[int] = None
|
|
max_inspections_month: Optional[int] = None
|
|
max_issues_month: Optional[int] = None
|
|
|
|
# Feature gates
|
|
allow_mobile_api: bool = True
|
|
allow_scheduled_reports: bool = True
|
|
allow_branding: bool = True
|
|
allow_custom_domain: bool = True
|
|
|
|
# MT-8: billing state (None = billing not configured / legacy tenant)
|
|
subscription_status: Optional[str] = None # trial|active|past_due|cancelled
|
|
trial_ends_at: Optional[datetime] = None
|