Planes SaaS

Planes SaaS es el catálogo que Alex usa para lo que un tenant le paga a IVPrior (ADR-021 Stripe Billing). No es el checkout del consumidor (ADR-040 Connect). Crear o eliminar un plan acá MUST NOT emitir PlanChanged:v1 — ese evento es de la suscripción contratada del tenant.

Overview

Lista de planes SaaS

Figura: Operations → Plans con estado, intervalos, cantidad de tenants y acciones de fila.

Qué significa cada campo

Estado

  • Borrador — guardado; no aparece en precios públicos (GET /public/plans) ni en el alta. El toggle Publicar está apagado.
  • Publicado — el toggle Publicar está encendido y el checklist pasó. El plan sale en /pricing, S-105 #pricing y signup.
  • Eliminar (verbo de UI) — archiva. La fila sale de la lista por defecto. Las Subscription conservan planId.

Código

Se genera a partir del nombre al crear (LiteLITE). Se ve en la lista. No está en el formulario del wizard y no se puede editar después.

KPI / gate: Publicar exige al menos un flag de intervalo, precio > 0 y un Stripe Price ID pegado por cada intervalo activo, ≥1 bullet en inglés y ≥1 en español, y entitlements max_locations + max_users. Si ai_assistant > 0, hacen falta cupos mensuales de IA.

Flags de intervalo (monthlyEnabled / yearlyEnabled)

Son flags explícitos, no se infieren del Price ID. El toggle Monthly/Annual en una card aparece solo si ambos flags están on. Ahorro público = 12 × monthlyPrice − yearlyPrice si el anual es más barato.

Bullets vs entitlements

  • Bullets — copy de marketing en las cards públicas. Cada fila es un bullet. La columna Idiomas usa las mismas banderas circulares que el selector del header (EE. UU. = inglés, España = español). A color = hay texto en ese idioma; desaturada = falta la traducción. Nuevo bullet abre un modal que exige todos los idiomas. Un idioma nuevo es un campo más en ese modal, no una segunda lista.
  • Entitlements — se aplican (FEAT-019). max_locations es el tope de Location del Tenant (no restaurants). También max_users, ai_assistant (0/1) y cupos mensuales de IA si el asistente está activo. Ejemplo: un bullet puede decir “Locations ilimitadas” mientras max_locations es 5.

Destacado

Como máximo un plan Publicado es “Más popular”. La lista lo muestra como badge junto al nombre (no hay columna Popular). Publicar otro destacado quita el anterior sin diálogo extra.

Orden

La columna Orden va después de Tenants (antes de las acciones de la fila). Es la secuencia de las cards públicas (GET /public/plans ordena sortOrder ASC). Se mueve con las flechas arriba/abajo (hace falta platform.plans.update). El wizard ya no tiene campo Orden: un plan nuevo se agrega al final.

Eliminar vs 409

Sin suscripciones, archiva (204). Si algún tenant tiene ese planId, 409, el diálogo explica que las suscripciones bloquean el borrado y la fila permanece. Bulk es todo o nada: si un plan seleccionado tiene suscriptores, no se archiva ninguno. Trial y Enterprise son filas normales en esta pantalla — no hay tipo “Sistema”.

Permisos (platform.plans.*)

  • read — nav Operations → Plans, lista, form de solo lectura
  • create — Agregar plan, Duplicar
  • update — Guardar, Publicar, Despublicar, Más popular
  • delete — Eliminar fila y bulk

Sin read, el ítem de nav se oculta y la URL muestra forbidden.

Crear o editar un plan

  1. Abrí Operations → Plans.
  2. Agregar plan o ⋮ → Editar.
  3. Completá Identidad · Facturación · Marketing · Límites (pills clicables). Siguiente es outline, nunca el verde primario.
  4. Identidad tiene un toggle Publicar — no un segundo switch de “Autoservicio”. Apagarlo en un plan Publicado pide despublicar (las suscripciones se mantienen).
  5. En Marketing, los feature bullets son una tabla (buscar, exportar, paginar). Nuevo bullet abre un modal con todos los idiomas obligatorios.
  6. Encender Publicar (o Finish con el toggle on en un plan nuevo) corre el checklist. Un 422 muestra banner más campos inválidos.

Test del lector

Si /pricing sigue mostrando Lite/Plus/Premium después de despublicar Plus, el fetch del catálogo está stale o Plus no era el publicado — la fuente de verdad son las filas Published selfServe, no un enum del frontend.