Files
Horux360/apps/api/prisma/seed.ts
Consultoria AS 0c4b1a43a9 feat(fase3): update sidebar navigation and seed with calendario_fiscal
- Add navigation items for Reportes, Calendario, Alertas, Usuarios
- Add calendario_fiscal table creation in seed
- Insert demo fiscal events for all 12 months

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 03:04:25 +00:00

242 lines
8.0 KiB
TypeScript

import { PrismaClient } from '@prisma/client';
import bcrypt from 'bcryptjs';
const prisma = new PrismaClient();
async function main() {
console.log('🌱 Seeding database...');
// Create demo tenant
const schemaName = 'tenant_ede123456ab1';
const tenant = await prisma.tenant.upsert({
where: { rfc: 'EDE123456AB1' },
update: {},
create: {
nombre: 'Empresa Demo SA de CV',
rfc: 'EDE123456AB1',
plan: 'professional',
schemaName,
cfdiLimit: 2000,
usersLimit: 10,
},
});
console.log('✅ Tenant created:', tenant.nombre);
// Create demo users
const passwordHash = await bcrypt.hash('demo123', 12);
const users = [
{ email: 'admin@demo.com', nombre: 'Admin Demo', role: 'admin' as const },
{ email: 'contador@demo.com', nombre: 'Contador Demo', role: 'contador' as const },
{ email: 'visor@demo.com', nombre: 'Visor Demo', role: 'visor' as const },
];
for (const userData of users) {
const user = await prisma.user.upsert({
where: { email: userData.email },
update: {},
create: {
tenantId: tenant.id,
email: userData.email,
passwordHash,
nombre: userData.nombre,
role: userData.role,
},
});
console.log(`✅ User created: ${user.email} (${user.role})`);
}
// Create tenant schema
await prisma.$executeRawUnsafe(`CREATE SCHEMA IF NOT EXISTS "${schemaName}"`);
// Create tables in tenant schema
await prisma.$executeRawUnsafe(`
CREATE TABLE IF NOT EXISTS "${schemaName}"."cfdis" (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
uuid_fiscal VARCHAR(36) UNIQUE NOT NULL,
tipo VARCHAR(20) NOT NULL,
serie VARCHAR(25),
folio VARCHAR(40),
fecha_emision TIMESTAMP NOT NULL,
fecha_timbrado TIMESTAMP NOT NULL,
rfc_emisor VARCHAR(13) NOT NULL,
nombre_emisor VARCHAR(300) NOT NULL,
rfc_receptor VARCHAR(13) NOT NULL,
nombre_receptor VARCHAR(300) NOT NULL,
subtotal DECIMAL(18,2) NOT NULL,
descuento DECIMAL(18,2) DEFAULT 0,
iva DECIMAL(18,2) DEFAULT 0,
isr_retenido DECIMAL(18,2) DEFAULT 0,
iva_retenido DECIMAL(18,2) DEFAULT 0,
total DECIMAL(18,2) NOT NULL,
moneda VARCHAR(3) DEFAULT 'MXN',
tipo_cambio DECIMAL(10,4) DEFAULT 1,
metodo_pago VARCHAR(3),
forma_pago VARCHAR(2),
uso_cfdi VARCHAR(4),
estado VARCHAR(20) DEFAULT 'vigente',
xml_url TEXT,
pdf_url TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
// Create IVA monthly table
await prisma.$executeRawUnsafe(`
CREATE TABLE IF NOT EXISTS "${schemaName}"."iva_mensual" (
id SERIAL PRIMARY KEY,
año INT NOT NULL,
mes INT NOT NULL,
iva_trasladado DECIMAL(18,2) NOT NULL,
iva_acreditable DECIMAL(18,2) NOT NULL,
iva_retenido DECIMAL(18,2) DEFAULT 0,
resultado DECIMAL(18,2) NOT NULL,
acumulado DECIMAL(18,2) NOT NULL,
estado VARCHAR(20) DEFAULT 'pendiente',
fecha_declaracion TIMESTAMP,
UNIQUE(año, mes)
)
`);
// Insert demo CFDIs
const cfdiTypes = ['ingreso', 'egreso'];
const rfcs = ['XAXX010101000', 'MEXX020202000', 'AAXX030303000', 'BBXX040404000'];
const nombres = ['Cliente Demo SA', 'Proveedor ABC', 'Servicios XYZ', 'Materiales 123'];
for (let i = 0; i < 50; i++) {
const tipo = cfdiTypes[i % 2];
const rfcIndex = i % 4;
const subtotal = Math.floor(Math.random() * 50000) + 1000;
const iva = subtotal * 0.16;
const total = subtotal + iva;
await prisma.$executeRawUnsafe(`
INSERT INTO "${schemaName}"."cfdis"
(uuid_fiscal, tipo, serie, folio, fecha_emision, fecha_timbrado,
rfc_emisor, nombre_emisor, rfc_receptor, nombre_receptor,
subtotal, iva, total, estado)
VALUES (
'${crypto.randomUUID()}',
'${tipo}',
'A',
'${1000 + i}',
NOW() - INTERVAL '${Math.floor(Math.random() * 180)} days',
NOW() - INTERVAL '${Math.floor(Math.random() * 180)} days',
'${tipo === 'ingreso' ? 'EDE123456AB1' : rfcs[rfcIndex]}',
'${tipo === 'ingreso' ? 'Empresa Demo SA de CV' : nombres[rfcIndex]}',
'${tipo === 'egreso' ? 'EDE123456AB1' : rfcs[rfcIndex]}',
'${tipo === 'egreso' ? 'Empresa Demo SA de CV' : nombres[rfcIndex]}',
${subtotal},
${iva},
${total},
'vigente'
)
ON CONFLICT (uuid_fiscal) DO NOTHING
`);
}
console.log('✅ Demo CFDIs created');
// Create IVA monthly records
let acumulado = 0;
for (let mes = 1; mes <= 6; mes++) {
const trasladado = Math.floor(Math.random() * 100000) + 150000;
const acreditable = Math.floor(Math.random() * 80000) + 120000;
const resultado = trasladado - acreditable;
acumulado += resultado;
await prisma.$executeRawUnsafe(`
INSERT INTO "${schemaName}"."iva_mensual"
(año, mes, iva_trasladado, iva_acreditable, resultado, acumulado, estado)
VALUES (2024, ${mes}, ${trasladado}, ${acreditable}, ${resultado}, ${acumulado},
'${mes <= 4 ? 'declarado' : 'pendiente'}')
ON CONFLICT (año, mes) DO NOTHING
`);
}
console.log('✅ IVA monthly records created');
// Create alerts table
await prisma.$executeRawUnsafe(`
CREATE TABLE IF NOT EXISTS "${schemaName}"."alertas" (
id SERIAL PRIMARY KEY,
tipo VARCHAR(50) NOT NULL,
titulo VARCHAR(200) NOT NULL,
mensaje TEXT NOT NULL,
prioridad VARCHAR(20) DEFAULT 'media',
fecha_vencimiento TIMESTAMP,
leida BOOLEAN DEFAULT FALSE,
resuelta BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
await prisma.$executeRawUnsafe(`
INSERT INTO "${schemaName}"."alertas" (tipo, titulo, mensaje, prioridad, fecha_vencimiento)
VALUES
('iva_favor', 'IVA a Favor Disponible', 'Tienes $43,582.40 de IVA a favor acumulado', 'media', NULL),
('declaracion', 'Declaración Mensual', 'La declaración mensual de IVA/ISR vence el 17 de febrero', 'alta', NOW() + INTERVAL '5 days'),
('discrepancia', 'CFDI con Discrepancia', 'Se encontraron 12 facturas con discrepancias', 'alta', NULL)
ON CONFLICT DO NOTHING
`);
console.log('✅ Alerts created');
// Create calendario_fiscal table
await prisma.$executeRawUnsafe(`
CREATE TABLE IF NOT EXISTS "${schemaName}"."calendario_fiscal" (
id SERIAL PRIMARY KEY,
titulo VARCHAR(200) NOT NULL,
descripcion TEXT,
tipo VARCHAR(20) NOT NULL,
fecha_limite TIMESTAMP NOT NULL,
recurrencia VARCHAR(20) DEFAULT 'mensual',
completado BOOLEAN DEFAULT FALSE,
notas TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
// Insert demo fiscal events for current year
const año = new Date().getFullYear();
const eventos = [
{ titulo: 'Declaración mensual IVA', tipo: 'declaracion', dia: 17 },
{ titulo: 'Declaración mensual ISR', tipo: 'declaracion', dia: 17 },
{ titulo: 'Pago provisional ISR', tipo: 'pago', dia: 17 },
{ titulo: 'DIOT', tipo: 'obligacion', dia: 17 },
];
for (let mes = 1; mes <= 12; mes++) {
for (const evento of eventos) {
const fechaLimite = new Date(año, mes - 1, evento.dia);
const completado = fechaLimite < new Date();
await prisma.$executeRawUnsafe(`
INSERT INTO "${schemaName}"."calendario_fiscal"
(titulo, descripcion, tipo, fecha_limite, recurrencia, completado)
VALUES ($1, $2, $3, $4, 'mensual', $5)
ON CONFLICT DO NOTHING
`, evento.titulo, `${evento.titulo} - ${mes}/${año}`, evento.tipo, fechaLimite.toISOString(), completado);
}
}
console.log('✅ Calendario fiscal created');
console.log('🎉 Seed completed successfully!');
console.log('\n📝 Demo credentials:');
console.log(' Admin: admin@demo.com / demo123');
console.log(' Contador: contador@demo.com / demo123');
console.log(' Visor: visor@demo.com / demo123');
}
main()
.catch((e) => {
console.error('Error seeding database:', e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});