Alta global de tenant
Crear negocio
Que resuelve el alta de negocio
El boton Crear negocio en /super-admin/businesses permite
registrar un nuevo tenant MedSync desde Super Admin, con estado comercial inicial, plan,
trial real y, opcionalmente, un administrador inicial del negocio.
Esta accion no crea pacientes, expedientes, recetas, citas clinicas, documentos,
tratamientos ni odontograma. El alta prepara el tenant administrativo y configuracion
operativa minima para que pueda iniciar uso.
FrontendVistaBusinessList.vueEndpointAltaPOST /api/super-admin/businessesAuditoriaEventoSUPER_ADMIN_BUSINESS_CREATE Vista real de Negocios
La pantalla documentada es la vista Super Admin de negocios. En local puede verse como
http://localhost:8081/super-admin/businesses si el frontend esta levantado
en el puerto 8081. La ruta productiva dentro de la app es
/super-admin/businesses.
| Elemento | Archivo real | Responsabilidad |
|---|
| Ruta Vue | src/router/routes/super-admin.js | Declara path: ‘businesses’, nombre superAdminBusinesses, layout Super Admin y meta.role = ‘super_admin’. |
| Vista principal | src/views/super-admin/businesses/BusinessList.vue | Pinta filtros, tabla, acciones por fila, modal de alta, modal de edicion, acciones comerciales y licencia. |
| Layout | src/core/layouts/super-admin.vue | Muestra la navegacion lateral Super Admin y el item Negocios. |
| Servicio frontend | src/services/core/superAdmin/DASuperAdminService.js | Expone getBusinesses, createBusiness, updateBusiness y acciones comerciales/licencia. |
| Constantes API | src/services/common/apiurl.js | Define DAC_SUPER_ADMIN_BUSINESSES = “/api/super-admin/businesses”. |
De donde sale la informacion que llena la tabla
Al montar la vista, mounted() ejecuta fetchBusinesses(). Esa
funcion arma parametros con buildParams() y llama
DASuperAdminService.getBusinesses(params). El servicio construye el query
string y consume GET /api/super-admin/businesses.
GET /api/super-admin/businesses
?page=1
&pageSize=20
&search=
&active=
&status=
&subscriptionStatus=
&trialState=
&plan=
&sortBy=created_at
&sortDir=desc
El backend devuelve data y pagination. La vista asigna
this.businesses = payload.data y actualiza pagina, total y totalPages.
La tabla no filtra solo en memoria: busqueda, filtros, paginacion y ordenamiento son
server-side.
Filtros de la vista de Negocios
| Filtro UI | Parametro backend | Valores principales | Que hace |
|---|
| Buscar negocio | search | Texto libre | Busca negocios por datos administrativos soportados por el DAO. |
| Activo | active | Activo, inactivo, todos | Filtra por estado tecnico business.active. |
| Estado tecnico | status | active, inactive, suspended, trial | Filtra por campo legacy/técnico business.status. |
| Estado MedSync | subscriptionStatus | trial, active, past_due, suspended, cancelled | Filtra por estado comercial MedSync real. |
| Trial | trialState | active, expired, expiring_soon | Filtra trials vigentes, vencidos o por vencer. |
| Plan | plan | basic, pro, enterprise | Filtra por plan comercial base. |
| Por pagina | pageSize | 10, 20, 50, 100 | Controla paginacion server-side. |
Columnas de la tabla de Negocios
| Columna | Campo de response | Que significa | Ordenable |
|---|
| Nombre | name | Nombre administrativo del negocio o tenant. | Si |
| Estado tecnico | status, active | Estado legacy/técnico simple. No reemplaza el estado MedSync. | Si |
| Estado MedSync | subscription_status | Estado comercial real: trial, active, past_due, suspended o cancelled. | Si |
| Plan | plan, plan_label | Muestra label amigable: Individual, Profesional o Clinica, conservando codigo interno. | Si |
| Trial vence | trial_ends_at | Fecha de fin de trial si el negocio esta en prueba. | Si |
| Dias restantes | trial_days_remaining, is_trial_expired | Muestra dias restantes, trial vencido o no aplica. | No |
| Operativo | is_operational | Indica si el tenant puede operar segun reglas de acceso MedSync. | No |
| Activo | active | Estado tecnico del negocio. | Si |
| Usuarios totales | total_users | Cuentas de acceso asociadas al negocio. | Si |
| Usuarios activos | active_users | Cuentas activas asociadas al negocio. | Si |
| Ultima actividad | last_activity_at | Ultima actividad agregada conocida para el negocio. | Si |
| Creado / Actualizado | created_at, updated_at | Fechas administrativas del negocio. | Si |
| Acciones | Derivado de getBusinessActions(business) | Menu contextual por fila. | No |
Acciones de la tabla
Cada fila usa un menu contextual. La funcion getBusinessActions(business)
decide que opciones mostrar segun subscription_status. Al elegir una
accion, openBusinessAction(type, business) abre el modal correspondiente.
| Accion | Cuando aparece | Endpoint que consume | Que cambia |
|---|
| Editar datos | Siempre. | PATCH /api/super-admin/businesses/:id | Solo datos administrativos: nombre, slug, razon social, email, telefono, direccion. |
| Ver licencia | Siempre. | GET /api/super-admin/businesses/:id/license-summary | No modifica datos. Muestra plan, uso, limites, extras y excedidos. |
| Extender trial | Solo si subscription_status = trial. | PATCH /api/super-admin/businesses/:id/trial/extend | Aumenta trial_ends_at. Mantiene estado trial. |
| Activar negocio | Si esta en trial. | PATCH /api/super-admin/businesses/:id/subscription-status | Cambia subscription_status a active. |
| Reactivar negocio | Si esta suspended, cancelled o past_due. | PATCH /api/super-admin/businesses/:id/subscription-status | Cambia a active y limpia fechas de suspension/cancelacion segun backend. |
| Marcar pago pendiente | Si no esta past_due ni cancelled. | PATCH /api/super-admin/businesses/:id/subscription-status | Cambia a past_due. Actualmente bloquea tenant. |
| Suspender negocio | Si no esta suspended ni cancelled. | PATCH /api/super-admin/businesses/:id/subscription-status | Cambia a suspended, marca suspended_at y bloquea tenant. |
| Cancelar negocio | Si no esta cancelled. | PATCH /api/super-admin/businesses/:id/subscription-status | Cambia a cancelled, marca cancelled_at y bloquea tenant. |
| Cambiar plan | Siempre. | PATCH /api/super-admin/businesses/:id/plan | Cambia plan entre basic, pro y enterprise. |
Acciones y confirmaciones
| Accion | Confirmacion UI | Motivo | Despues de exito |
|---|
| Editar datos | Modal de confirmacion con campos modificados. | No aplica como obligatorio. | Snackbar y fetchBusinesses(). |
| Extender trial | Modal muestra fecha actual y estimada. | Opcional. | Refresca tabla y recalcula dias restantes. |
| Suspender | Modal advierte bloqueo tenant. | Obligatorio. | Chip cambia a Suspendido y is_operational debe quedar bloqueado. |
| Cancelar | Debe escribir CANCELAR. | Obligatorio. | Chip cambia a Cancelado y tenant queda bloqueado. |
| Pago pendiente | Modal advierte que bloquea tenant actualmente. | Obligatorio. | Chip cambia a Pago pendiente. |
| Activar / reactivar | Modal explicito. | Opcional. | Chip cambia a Activo si backend confirma. |
| Cambiar plan | Modal con selector basic/pro/enterprise. | Opcional. | Plan visual cambia y licencia usa nuevos limites base. |
| Editar extras licencia | Desde modal Ver licencia. | Obligatorio si algun extra es mayor a 0. | Refresca licencia y tabla. |
Flujo completo desde la UI
- Super Admin entra a
/super-admin/businesses. - Presiona el boton Crear negocio.
- La vista abre un modal persistente con datos de negocio, estado MedSync y admin inicial.
- El usuario llena campos y pulsa Revisar creacion.
- Frontend ejecuta validaciones locales y abre modal de confirmacion.
- Al confirmar,
confirmCreateBusiness() arma payload con buildCreatePayload(). - El servicio llama
DASuperAdminService.createBusiness(payload). - Backend valida token, sesion activa si aplica y rol
super_admin. - DAO valida negocio, estado MedSync, trial, plan y admin inicial.
- DAO ejecuta transaccion: inserta negocio, configuracion operativa minima y admin opcional.
- Backend registra auditoria administrativa.
- Frontend muestra snackbar, cierra modales, vuelve a pagina 1 y refresca la tabla.
Campos visibles en el modal
| Seccion | Campo | Uso | Validacion principal |
|---|
| Datos del negocio | Nombre del negocio | Nombre comercial del tenant. | Obligatorio. |
| Datos del negocio | Slug | Identificador amigable. Si va vacio, backend lo genera desde el nombre. | Backend normaliza a minusculas, numeros y guiones. |
| Datos del negocio | Razon social | Nombre legal o administrativo. | Opcional. |
| Datos del negocio | Telefono, correo, direccion | Datos administrativos de contacto del negocio. | Email debe tener formato valido si se captura. |
| Estado MedSync | Estado inicial | Define si inicia como trial o active. | Solo permite prueba gratis o activo. |
| Estado MedSync | Plan | Plan comercial base. | Solo basic, pro, enterprise. |
| Estado MedSync | Dias de prueba | Duracion inicial del trial. | Entero entre 1 y 365. Default UI: 60. |
| Admin inicial | Crear usuario administrador inicial | Permite crear la primera cuenta admin del tenant. | Opcional. |
| Admin inicial | Nombre, telefono, correo | Datos de acceso del admin inicial. | Nombre y telefono obligatorios si se crea admin. Email valido si existe. |
| Admin inicial | Contrasena temporal | Clave inicial del admin. | Minimo 8 caracteres y confirmacion exacta. |
| Admin inicial | Rol | La UI lo fija como admin. | No permite super_admin. |
Payload que envia el frontend
El frontend limpia strings vacios antes de enviar. Para trial agrega
trial_days; para active no lo necesita.
{
"business": {
"name": "Clinica Demo",
"slug": "clinica-demo",
"legalName": "Clinica Demo SA de CV",
"phone": "5555555555",
"email": "admin@demo.local",
"address": "Direccion administrativa",
"status": "active",
"active": true,
"plan": "basic",
"subscription_status": "trial",
"trial_days": 60
},
"adminUser": {
"create": true,
"name": "Admin Demo",
"email": "admin@demo.local",
"telephone": "5555555555",
"password": "********",
"role": "admin"
}
}
En el modal de confirmacion se muestra nombre, estado inicial, plan, dias de trial y
admin inicial. Nunca debe mostrar la contrasena.
Endpoint y cadena backend
| Capa | Archivo o funcion | Responsabilidad |
|---|
| Ruta | routes/mysql/super-admin.js | Declara POST /businesses con Token.Auth y requireRole(‘super_admin’). |
| Controller | SuperAdminController.createBusiness | Pasa body y actor del token al DAO. |
| DAO | SuperAdminDao.createBusiness | Valida, abre transaccion, inserta negocio/admin/configuracion y responde. |
| Auditoria | AdminAuditDao.record | Registra SUPER_ADMIN_BUSINESS_CREATE con metadata segura. |
Validaciones backend
| Validacion | Regla | Error esperado |
|---|
| Nombre | business.name obligatorio. | 400 |
| Estado MedSync inicial | Solo trial o active. | 400 |
| Dias trial | Si es trial: entero entre 1 y 365. Default: 60. | 400 |
| Fechas trial | Si se envian, deben ser fechas validas. | 400 |
| Slug | Se normaliza; si no puede generarse, falla. | 400 |
| Duplicado negocio | No puede existir mismo nombre o slug. | 409 |
| Admin inicial | Nombre, telefono y password valido si create=true. | 400 |
| Rol admin | Backend permite admin o business_admin; UI envia admin. | 400 si rol invalido. |
| Usuario admin duplicado | No puede existir telefono o correo activo igual. | 409 |
| Permisos | Sin token: no autorizado. Usuario no super_admin: prohibido. | 401 / 403 |
SQL principal: insertar negocio
El alta inserta el tenant en business. El estado tecnico queda
active, el estado comercial queda en subscription_status y el
plan queda en plan.
INSERT INTO business (
id_business,
name,
legal_name,
slug,
email,
phone,
address,
status,
subscription_status,
trial_starts_at,
trial_ends_at,
current_period_starts_at,
current_period_ends_at,
plan,
active,
created_at,
created_by
) VALUES (
:id_business,
:name,
:legal_name,
:slug,
:email,
:phone,
:address,
'active',
:subscription_status,
CASE WHEN :subscription_status = 'trial' THEN COALESCE(:trial_starts_at, NOW()) ELSE NULL END,
CASE
WHEN :subscription_status != 'trial' THEN NULL
WHEN :trial_ends_at IS NOT NULL THEN :trial_ends_at
ELSE DATE_ADD(COALESCE(:trial_starts_at, NOW()), INTERVAL :trial_days DAY)
END,
CASE WHEN :subscription_status = 'active' THEN NOW() ELSE NULL END,
NULL,
:plan,
:active,
NOW(),
:created_by
);
Si nace como trial
subscription_status = ‘trial’.trial_starts_at = NOW() si no se manda fecha.trial_ends_at = trial_starts_at + trial_days.current_period_starts_at = NULL.- El tenant opera mientras el trial este vigente.
Si nace como active
subscription_status = ‘active’.trial_starts_at = NULL.trial_ends_at = NULL.current_period_starts_at = NOW().- Se usa si ya es cliente pagado o debe operar sin prueba.
Configuracion operativa minima creada
Ademas de business, el DAO crea configuracion administrativa minima. Esto
prepara el tenant, pero no crea datos clinicos.
| Tabla | Que inserta | Para que sirve |
|---|
appointment_config | Registro default con duracion 30 y timezone America/Mexico_City. | Configuracion operativa base de agenda del negocio. |
ui_theme_configuration | Tema medical-blue si existe activo en catalogo. | Configuracion visual base del tenant. |
Aunque existe una configuracion de agenda, esta accion no crea citas reales ni datos de
pacientes.
SQL de configuracion minima
INSERT INTO appointment_config (
id_appointment_config,
id_business,
enabled,
default_appointment_duration,
timezone,
active,
created_at,
created_by
) VALUES (
:id_business_default,
:id_business,
1,
30,
'America/Mexico_City',
1,
NOW(),
:created_by
);
-- Si existe el tema activo
INSERT INTO ui_theme_configuration (
id_ui_theme_configuration,
id_business,
id_cat_ui_theme,
active,
created_by,
created_at
) VALUES (
UUID(),
:id_business,
:id_cat_ui_theme,
1,
:created_by,
NOW()
);
Admin inicial opcional
Si adminUser.create = true, el backend crea una cuenta en
login asociada al nuevo negocio. La contrasena se hashea con
encript() antes de guardar. El response nunca devuelve password ni hash.
INSERT INTO login (
id_login,
id_business,
id_rol,
name,
email,
telephone,
password,
locked,
active,
created_at,
created_by
) VALUES (
:id_login,
:id_business,
:id_rol,
:name,
:email,
:telephone,
:password_hash,
0,
1,
NOW(),
:created_by
);
Transaccion y consistencia
El alta usa una transaccion para los inserts criticos. Si falla validacion, duplicado,
rol, negocio o admin, se ejecuta rollback. La auditoria se registra despues del commit
siguiendo el patron del modulo.
flowchart TD
A[Confirmar crear negocio] --> B[POST /api/super-admin/businesses]
B --> C[Token.Auth]
C --> D[requireRole super_admin]
D --> E[SuperAdminController.createBusiness]
E --> F[SuperAdminDao.createBusiness]
F --> G[Validar payload]
G --> H{Valido?}
H -- No --> I[400 o 409 controlado]
H -- Si --> J[BEGIN transaction]
J --> K[INSERT business]
K --> L[INSERT appointment_config]
L --> M[INSERT ui_theme_configuration si aplica]
M --> N{Crear admin inicial?}
N -- Si --> O[Hash password + INSERT login]
N -- No --> P[SELECT business creado]
O --> P
P --> Q[COMMIT]
Q --> R[Auditoria SUPER_ADMIN_BUSINESS_CREATE]
R --> S[Response 201]
S --> T[Frontend refresca tabla]
Auditoria del alta
El evento registrado es SUPER_ADMIN_BUSINESS_CREATE. La metadata explica
que se creo, si hubo admin inicial, estado MedSync, trial, plan y configuracion default.
{
"created_admin": true,
"admin_login_id": "...",
"business_name": "Clinica Demo",
"business_status": "active",
"subscription_status": "trial",
"trial_days": 60,
"trial_starts_at": "...",
"trial_ends_at": "...",
"plan": "basic",
"default_operational_config": {
"appointment_config": true,
"ui_theme": true
}
}
No debe guardar password, hash, token, refresh token, pacientes, expedientes, recetas,
diagnosticos, notas medicas ni payloads clinicos.
Response esperado y efecto en UI
El backend responde 201. El wrapper HTTP del frontend trata respuestas
2xx como exito. Despues de crear, la tabla vuelve a pagina 1 y ejecuta de
nuevo GET /api/super-admin/businesses.
{
"success": true,
"message": "Negocio creado correctamente",
"data": {
"business": {
"id_business": "...",
"name": "Clinica Demo",
"slug": "clinica-demo",
"active": true,
"status": "active",
"subscription_status": "trial",
"trial_starts_at": "...",
"trial_ends_at": "...",
"plan": "basic",
"created_at": "..."
},
"adminUser": {
"id_login": "...",
"name": "Admin Demo",
"email": "admin@demo.local",
"telephone": "5555555555",
"role": "admin",
"active": true
}
}
}
Que se refleja despues del alta
| Lugar | Que cambia | Por que importa |
|---|
| Listado de negocios | Aparece el nuevo negocio con estado MedSync, plan, trial y operativo. | Confirma que el alta fue persistida. |
| Dashboard | Aumentan conteos de negocios, trial o active y posiblemente usuarios. | Permite validar impacto agregado. |
| Usuarios Super Admin | Si se creo admin inicial, aparece como usuario de acceso. | Confirma creacion de cuenta tenant. |
| Auditoria | Aparece SUPER_ADMIN_BUSINESS_CREATE. | Da trazabilidad administrativa. |
| Login tenant | El admin inicial puede iniciar sesion si el negocio esta operativo. | Valida punta a punta sin tocar datos clinicos. |
Privacidad y limites del alta
- No usa
applyTenantToBody(). - No usa
default-business. - No usa endpoints legacy
/api/business/create ni /api/user/create. - No crea pacientes.
- No crea expedientes ni historias clinicas.
- No crea recetas, citas, documentos clinicos, tratamientos ni odontograma.
- No devuelve password, hash, token ni refresh token.
- No permite crear
super_admin desde admin inicial.
Errores comunes al crear negocio
| Error | Causa probable | Accion |
|---|
400 | Nombre faltante, estado MedSync invalido, trial_days fuera de rango o rol invalido. | Revisar formulario y payload. |
401 | Token ausente, expirado o sesion cerrada. | Volver a iniciar sesion. |
403 | Usuario no es super_admin. | Validar rol y endpoint. |
409 | Negocio duplicado o admin inicial con telefono/correo activo existente. | Usar otro nombre, slug, telefono o correo. |
| Se creo pero UI mostro error | Wrapper HTTP no aceptaba 201 en versiones anteriores. | Confirmar que el wrapper acepte respuestas 2xx. |