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::timestamp, '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(); });