Plan · Modelo core
Propósito
Plan define el nivel de suscripción de una organización SaaS. Actúa como configuración base de módulos: determina qué funcionalidades (SaaSModule) están disponibles de serie para las organizaciones del tier. Introducido en la migración 0013_module_gating, centraliza la lógica de acceso a funcionalidades evitando hardcoding por organización.
Contrato
Campos (core/models.py:27-47):
| Campo | Tipo | Notas |
|---|---|---|
slug | SlugField(50, unique) | Identificador canónico: starter, pro, custom |
name | CharField(100) | Nombre legible |
description | TextField(blank) | Descripción opcional |
modules | M2M(SaaSModule, related_name="plans", blank) | Módulos incluidos por defecto en el plan |
sort_order | IntegerField(default=0) | Orden en listados |
Tiers definidos (constante PLAN_CHOICES, no campo):
"starter"— Rack design essentials; solo móduloracks."pro"— Gestión completa; incluyeracks,network-maps,network-management,network-observatory."custom"— Base Pro + módulos premium à la carte (wireless-monitor,ups-monitor,digital-signage) víaOrganization.extra_modules. Tras0014, hereda los 4 módulos base de Pro.
Meta: ordering = ["sort_order"]. Sin verbose_name explícito.
Dependencias entrantes
| Archivo | Uso |
|---|---|
core/models.py:63 (Organization) | FK plan con SET_NULL, null=True |
core/api/signup.py | Resuelve Plan.objects.filter(slug="starter").first(); asigna max_racks=10 |
core/admin.py (OrganizationAdmin.plan_modules_display) | Itera Plan.objects.prefetch_related("modules") para mapa JS |
core/admin.py (PlanAdmin) | filter_horizontal = ("modules",), columnas module_count y org_count |
Dependencias salientes
SaaSModule— M2M víamodules. Inverse:SaaSModule.plans.core/migrations/0013_module_gating— crea los 3 planes semilla y asigna organizaciones existentes al plancustomcon todos los módulos no-core enextra_modules.core/migrations/0014_fix_custom_plan_modules— corrigecustompara incluir los 4 módulos base de Pro.
Ejemplos
# Signup self-service (core/api/signup.py)
PLAN_MAX_RACKS = {"starter": 10, "pro": 200, "custom": 200}
plan = Plan.objects.filter(slug="starter").first()
org = Organization.objects.create(
name=org_name,
plan=plan,
max_racks=PLAN_MAX_RACKS.get(plan.slug, 10) if plan else 10,
)
# Sync extra_modules = core_modules ∪ plan_modules
core_pks = set(SaaSModule.objects.filter(is_core=True).values_list("pk", flat=True))
plan_pks = set(plan.modules.values_list("pk", flat=True))
org.extra_modules.set(core_pks | plan_pks)
# Admin — mapa de módulos por plan (core/admin.py)
for plan in Plan.objects.prefetch_related("modules").all():
mod_names = sorted(set(core_names) | set(plan.modules.values_list("name", flat=True)))
plan_data[str(plan.pk)] = {"label": ", ".join(mod_names), ...}
Related
entity--core--model--saasmodule— módulos individuales que componen un plan.entity--core--model--organization— tenant SaaS que consume el plan vía FK.concept--saas--module-gating— lógicaget_active_modules()que combina core + plan + extras.
Véase también
- [[entity—core—model—saasmodule]]
- [[entity—core—model—organization]]
- [[concept—saas—module-gating]]
Referenciado desde
- Agente · dev-core
- Billing · Stripe PR-2 — webhook receiver + estado de suscripción
- Estrategia SaaS 2026 — CreaRack Pro
- Guía de Superusuario y Administración SaaS
- Modelo InvitationCode — código invite-only para registro F1
- Module Gating · SaaSModule + Plan en lugar de feature flags genéricos
- Módulos SaaS y plan gating
- Organization · Modelo core
- SaaSModule · Modelo core