/** * Script: seed-demo-obligaciones-tareas * * Crea obligaciones fiscales y tareas recurrentes para todos los contribuyentes * del tenant Demo Ventas. Además asigna el usuario auxiliar a las tareas y * obligaciones, y lo vincula a la cartera principal. * * Ejecución: * cd apps/api && npx tsx scripts/seed-demo-obligaciones-tareas.ts */ import { PrismaClient } from '@prisma/client'; import { tenantDb } from '../src/config/database.ts'; import { seedTareasDefault, materializarPeriodos } from '../src/services/tareas.service.ts'; const prisma = new PrismaClient(); const DEMO_RFC = 'DEMO2501019X2'; const OBLIGACIONES = [ { id: 'isr-provisional', nombre: 'Pago provisional de ISR', fundamento: 'Art. 14 LISR', frecuencia: 'mensual', fechaLimite: 'Día 17 del mes siguiente', categoria: 'Federal mensual' }, { id: 'iva-mensual', nombre: 'Pago mensual definitivo de IVA', fundamento: 'Art. 5-D LIVA', frecuencia: 'mensual', fechaLimite: 'Día 17 del mes siguiente', categoria: 'Federal mensual' }, { id: 'ret-isr-honorarios', nombre: 'Retenciones de ISR por honorarios y arrendamiento a PF', fundamento: 'Art. 106/116 LISR', frecuencia: 'mensual', fechaLimite: 'Día 17 del mes siguiente', categoria: 'Federal mensual' }, { id: 'diot', nombre: 'DIOT (Declaración Informativa de Operaciones con Terceros)', fundamento: 'Art. 32 LIVA', frecuencia: 'mensual', fechaLimite: 'Último día del mes siguiente', categoria: 'Informativa mensual' }, { id: 'imss-cuotas', nombre: 'Cuotas obrero-patronales IMSS', fundamento: 'LSS', frecuencia: 'mensual', fechaLimite: 'Día 17 del mes siguiente', categoria: 'Seguridad social' }, { id: 'anual-isr-pm', nombre: 'Declaración Anual de ISR PM', fundamento: 'Art. 76 LISR', frecuencia: 'anual', fechaLimite: '31 de marzo', categoria: 'Anual' }, { id: 'isn', nombre: 'ISN - Impuesto Sobre Nómina', fundamento: 'Ley estatal', frecuencia: 'mensual', fechaLimite: 'Varía por estado (CDMX día 17)', categoria: 'Estatal' }, { id: 'isrtp', nombre: 'Impuesto sobre remuneración al trabajo', fundamento: 'Ley estatal', frecuencia: 'mensual', fechaLimite: 'Día 10 del mes siguiente', categoria: 'Estatal' }, ]; async function main() { console.log('🌱 Sembrando obligaciones y tareas en Demo Ventas...\n'); const tenant = await prisma.tenant.findUnique({ where: { rfc: DEMO_RFC } }); if (!tenant) throw new Error(`Tenant ${DEMO_RFC} no encontrado`); const auxUser = await prisma.user.findUnique({ where: { email: 'auxiliar@horuxfin.com' } }); if (!auxUser) throw new Error('Usuario auxiliar no encontrado'); const supervisorUser = await prisma.user.findUnique({ where: { email: 'supervisor@horuxfin.com' } }); if (!supervisorUser) throw new Error('Usuario supervisor no encontrado'); const pool = await tenantDb.getPool(tenant.id, tenant.databaseName); const { rows: contribuyentes } = await pool.query<{ id: string; rfc: string }>(` SELECT entidad_id AS id, rfc FROM contribuyentes ORDER BY rfc `); if (contribuyentes.length === 0) throw new Error('No hay contribuyentes en el tenant demo'); for (const c of contribuyentes) { // Obligaciones fiscales (idempotente: evita duplicados por contribuyente + catalogo_id) let obligacionesCreadas = 0; for (const o of OBLIGACIONES) { const { rows: existing } = await pool.query( `SELECT 1 FROM obligaciones_contribuyente WHERE contribuyente_id = $1 AND catalogo_id = $2 LIMIT 1`, [c.id, o.id], ); if (existing.length > 0) continue; await pool.query(` INSERT INTO obligaciones_contribuyente ( contribuyente_id, catalogo_id, nombre, fundamento, frecuencia, fecha_limite, categoria, activa, es_recomendada ) VALUES ($1, $2, $3, $4, $5, $6, $7, true, true) `, [c.id, o.id, o.nombre, o.fundamento, o.frecuencia, o.fechaLimite, o.categoria]); obligacionesCreadas++; } console.log(`✅ ${c.rfc}: ${obligacionesCreadas} obligaciones creadas`); // Tareas default const tareasCreadas = await seedTareasDefault(pool, c.id); if (tareasCreadas > 0) { await materializarPeriodos(pool, c.id); console.log(`✅ ${c.rfc}: ${tareasCreadas} tareas creadas y periodos materializados`); } else { console.log(`ℹ️ ${c.rfc}: tareas default ya existían`); } } // Asignar auxiliar a todas las obligaciones y tareas activas await pool.query(` INSERT INTO obligacion_asignaciones (obligacion_id, auxiliar_user_id, asignado_por) SELECT oc.id, $1, $2 FROM obligaciones_contribuyente oc WHERE oc.activa = true ON CONFLICT (obligacion_id) DO UPDATE SET auxiliar_user_id = EXCLUDED.auxiliar_user_id, asignado_por = EXCLUDED.asignado_por `, [auxUser.id, supervisorUser.id]); console.log('✅ Auxiliar asignado a obligaciones'); await pool.query(` INSERT INTO tarea_asignaciones (tarea_id, auxiliar_user_id, asignado_por) SELECT tc.id, $1, $2 FROM tareas_catalogo tc WHERE tc.active = true ON CONFLICT (tarea_id) DO UPDATE SET auxiliar_user_id = EXCLUDED.auxiliar_user_id, asignado_por = EXCLUDED.asignado_por `, [auxUser.id, supervisorUser.id]); console.log('✅ Auxiliar asignado a tareas'); // Asignar auxiliar a la cartera principal await pool.query(` UPDATE carteras SET auxiliar_user_id = $1 WHERE parent_id IS NULL `, [auxUser.id]); console.log('✅ Auxiliar asignado a la cartera principal'); // Asegurar relación auxiliar-supervisor await pool.query(` INSERT INTO auxiliar_supervisores (auxiliar_user_id, supervisor_user_id) VALUES ($1, $2) ON CONFLICT (auxiliar_user_id) DO UPDATE SET supervisor_user_id = EXCLUDED.supervisor_user_id `, [auxUser.id, supervisorUser.id]); console.log('✅ Relación auxiliar → supervisor registrada'); console.log('\n🎉 Obligaciones y tareas listas en Demo Ventas'); } main() .catch((e) => { console.error('\n❌ Error:', e); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); await tenantDb.shutdown(); });