Files
HoruxDespachosNuevo/apps/api/scripts/bootstrap-horux360-admin.ts

132 lines
4.9 KiB
TypeScript

/**
* Bootstrap del tenant admin global (Horux 360 — HTS240708LJA) + usuarios staff.
*
* Crea:
* 1. Tenant Horux 360 (RFC HTS240708LJA, plan enterprise)
* 2. Carlos como owner del tenant + rol platform_admin
* 3. Ivan como contador del tenant + rol platform_ti (TI superset)
* 4. Suscripción authorized por 1 año
*
* Uso: `pnpm bootstrap:admin-global`
*
* Idempotente-ish: falla limpio si el tenant ya existe (RFC unique).
* Para re-ejecutar, borra el tenant y su BD manualmente antes.
*
* Requisitos previos:
* 1. `pnpm prisma migrate deploy` (schema central)
* 2. `pnpm db:seed` (catálogos SAT, regímenes, ISR, eventos fiscales, roles)
*
* Env vars opcionales (con defaults):
* HORUX_ADMIN_EMAIL (default: carlos@horuxfin.com)
* HORUX_ADMIN_NOMBRE (default: Carlos)
* HORUX_TI_EMAIL (default: ivan@horuxfin.com)
* HORUX_TI_NOMBRE (default: Ivan)
*/
import { prisma } from '../src/config/database.js';
import * as tenantsService from '../src/services/tenants.service.js';
import * as usuariosService from '../src/services/usuarios.service.js';
const RFC = 'HTS240708LJA';
const TENANT_NAME = 'Horux 360';
const PLAN = 'custom' as const;
const SUBSCRIPTION_YEARS = 1;
async function main() {
const adminEmail = process.env.HORUX_ADMIN_EMAIL || 'carlos@horuxfin.com';
const adminNombre = process.env.HORUX_ADMIN_NOMBRE || 'Carlos';
const tiEmail = process.env.HORUX_TI_EMAIL || 'ivan@horuxfin.com';
const tiNombre = process.env.HORUX_TI_NOMBRE || 'Ivan';
console.log(`Bootstrap del tenant admin global`);
console.log(` RFC: ${RFC}`);
console.log(` Nombre: ${TENANT_NAME}`);
console.log(` Admin: ${adminNombre} <${adminEmail}> (platform_admin)`);
console.log(` TI: ${tiNombre} <${tiEmail}> (platform_ti)`);
console.log(` Plan: ${PLAN} (sin cobro — admin global)`);
console.log('');
// 1. Crea tenant + BD provisionada + Carlos como owner + subscription pending
const { tenant, user: carlosUser, tempPassword: carlosPassword } = await tenantsService.createTenant({
nombre: TENANT_NAME,
rfc: RFC,
plan: PLAN,
adminEmail,
adminNombre,
amount: 0,
});
console.log(`✓ Tenant creado: ${tenant.id}`);
console.log(`✓ BD provisionada: ${tenant.databaseName}`);
console.log(`✓ Carlos creado (owner): ${carlosUser.email}`);
// 2. Asigna platform_admin a Carlos (no se hace automáticamente desde tenants.service)
const carlosFull = await prisma.user.findUnique({ where: { email: adminEmail } });
if (carlosFull) {
await prisma.userPlatformRole.upsert({
where: { userId_role: { userId: carlosFull.id, role: 'platform_admin' } },
update: {},
create: { userId: carlosFull.id, role: 'platform_admin' },
});
console.log(`✓ Carlos: rol platform_admin asignado`);
}
// 3. Crea Ivan como contador del tenant (membership) y le asigna platform_ti
const ivan = await usuariosService.inviteUsuario(tenant.id, {
email: tiEmail,
nombre: tiNombre,
role: 'contador',
});
console.log(`✓ Ivan creado: ${ivan.email} (membership contador)`);
await prisma.userPlatformRole.upsert({
where: { userId_role: { userId: ivan.id, role: 'platform_ti' } },
update: {},
create: { userId: ivan.id, role: 'platform_ti' },
});
console.log(`✓ Ivan: rol platform_ti asignado (superset, mismos permisos que admin)`);
// 4. Sube la subscription a 'authorized' con vigencia de 1 año
const existing = await prisma.subscription.findFirst({
where: { tenantId: tenant.id },
orderBy: { createdAt: 'desc' },
});
if (existing) {
const now = new Date();
const end = new Date(now);
end.setFullYear(end.getFullYear() + SUBSCRIPTION_YEARS);
await prisma.subscription.update({
where: { id: existing.id },
data: {
status: 'authorized',
currentPeriodStart: now,
currentPeriodEnd: end,
},
});
console.log(`✓ Suscripción marcada 'authorized' hasta ${end.toISOString().slice(0, 10)}`);
}
console.log('');
console.log('=== DONE ===');
console.log(`Credenciales temporales para primer login:`);
console.log(` Carlos (admin): ${adminEmail}`);
console.log(` Password: ${carlosPassword}`);
console.log('');
console.log(` Ivan (TI): ${tiEmail}`);
console.log(` Password: revisa el correo de bienvenida (inviteUsuario lo envía por email)`);
console.log('');
console.log('Próximos pasos manuales:');
console.log(` 1. Carlos login en /login con las credenciales de arriba`);
console.log(` 2. Cambiar el password desde /configuracion/seguridad`);
console.log(` 3. Verificar que Ivan recibió su correo de invitación`);
console.log(` 4. Subir FIEL en /configuracion/sat para habilitar sincronización`);
console.log(` 5. (Opcional) Configurar organización Facturapi en /configuracion`);
}
main()
.catch((err) => {
console.error('✗ Bootstrap falló:', err.message || err);
process.exit(1);
})
.finally(() => prisma.$disconnect());