Subscriptions & Plans
One-line: a tenant’s plan decides which modules it can use; RBAC decides which of those a given user can touch. Two independent gates.
What it does
Every school is on a subscription plan. The plan carries a set of enabled feature keys (plans.features_json). A module is available to a tenant only if its key is in that set — regardless of what any user’s role permits. This is how NexSchool packages and prices modules per school.
Who uses it
- NexSchool operators assign and change a tenant’s plan from the panel.
- School admins experience the result: modules outside their plan simply don’t appear.
Key concepts
- Plan — a named bundle with
features_jsonlisting enabled feature keys. - Plan feature key — a string like
attendance,transport,hostel(defined incore/plan_features.py). - Subscription status — active / trial / suspended / cancelled; some routes require an active subscription.
- Two orthogonal gates — plan gate (
@require_plan_feature) and permission gate (@require_permission). A user needs both to use a feature.
The feature matrix
Plan-gated and implemented (each key backs a real module):
attendance · fees_management · notifications · schedule_management · student_management · teacher_management · class_management · timetable · transport · hostel
A missing key defaults to enabled, so adding a module and its key is a safe rollout. Always add a key alongside its module.
Not implemented — no working module behind them; intentionally out of scope until the pilot justifies them:
examinations · reports · assignments · library · inventory
These dead placeholder keys were removed from the optional-features list so the panel no longer offers toggles that do nothing.
How it works
1. Operator assigns a plan to the tenant (panel).
2. plans.features_json → the tenant's enabled_features.
3. Login/profile returns enabled_features to the client.
4. Backend: @require_plan_feature('x') blocks routes if 'x' not enabled.
5. Frontend: isFeatureEnabled('x') hides UI for disabled modules.
6. Subscription status may additionally gate routes (@require_active_subscription).Rules & edge cases
- Plan gate and RBAC are independent — having a permission doesn’t help if the plan lacks the feature, and vice versa.
- Missing feature key ⇒ treated as enabled (safe default for rollout).
- Tenant-scoped; each school’s plan is independent.
Where it lives
- Backend:
server/modules/subscription/,server/core/plan_features.py - Panel: plan assignment under the tenant management screens
- Related gate logic:
@require_plan_feature,isFeatureEnabled()/useFeatures()
Related
- Control plane · Auth & RBAC
- Roadmap — Razorpay/online payments is the commercial next step