Update: nueva version Horux Despachos
This commit is contained in:
135
apps/api/scripts/bootstrap-horux360-admin.ts
Normal file
135
apps/api/scripts/bootstrap-horux360-admin.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* 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 = 'enterprise' as const;
|
||||
const CFDI_LIMIT = -1; // ilimitado
|
||||
const USERS_LIMIT = 10;
|
||||
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} (cfdi: ${CFDI_LIMIT}, users: ${USERS_LIMIT})`);
|
||||
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,
|
||||
cfdiLimit: CFDI_LIMIT,
|
||||
usersLimit: USERS_LIMIT,
|
||||
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());
|
||||
Reference in New Issue
Block a user