/** * Script: fix-demo-carteras-asignaciones * * Corrige la estructura de carteras de Demo Ventas para que las asignaciones * de obligaciones/tareas al auxiliar sean válidas: * - La cartera principal queda solo para el supervisor. * - Se crea una subcartera asignada al auxiliar. * - Los contribuyentes se mueven a la subcartera del auxiliar. * - Se mantiene la relación auxiliar → supervisor. * * Ejecución: * cd apps/api && npx tsx scripts/fix-demo-carteras-asignaciones.ts */ import { PrismaClient } from '@prisma/client'; import { tenantDb } from '../src/config/database.ts'; const prisma = new PrismaClient(); const DEMO_RFC = 'DEMO2501019X2'; async function main() { console.log('🔧 Corrigiendo carteras y asignaciones de Demo Ventas...\n'); const tenant = await prisma.tenant.findUnique({ where: { rfc: DEMO_RFC } }); if (!tenant) throw new Error(`Tenant ${DEMO_RFC} no encontrado`); const [supervisor, auxiliar] = await Promise.all([ prisma.user.findUnique({ where: { email: 'supervisor@horuxfin.com' } }), prisma.user.findUnique({ where: { email: 'auxiliar@horuxfin.com' } }), ]); if (!supervisor) throw new Error('Usuario supervisor no encontrado'); if (!auxiliar) throw new Error('Usuario auxiliar no encontrado'); const pool = await tenantDb.getPool(tenant.id, tenant.databaseName); const client = await pool.connect(); try { await client.query('BEGIN'); // Obtener cartera principal const { rows: [carteraPrincipal] } = await client.query<{ id: string }>(` SELECT id FROM carteras WHERE parent_id IS NULL ORDER BY created_at LIMIT 1 `); if (!carteraPrincipal) throw new Error('No existe cartera principal'); // Crear subcartera para el auxiliar const { rows: [subcartera] } = await client.query<{ id: string }>(` INSERT INTO carteras (supervisor_user_id, auxiliar_user_id, nombre, descripcion, parent_id) VALUES ($1, $2, $3, $4, $5) ON CONFLICT DO NOTHING RETURNING id `, [supervisor.id, auxiliar.id, 'Cartera Auxiliar Demo', 'RFCs asignados al auxiliar de demo', carteraPrincipal.id]); const subcarteraId = subcartera?.id; if (!subcarteraId) { // Si ya existía, recuperarla const { rows: [existing] } = await client.query<{ id: string }>(` SELECT id FROM carteras WHERE parent_id = $1 AND auxiliar_user_id = $2 LIMIT 1 `, [carteraPrincipal.id, auxiliar.id]); if (!existing) throw new Error('No se pudo crear ni recuperar la subcartera del auxiliar'); // Asegurar que tenga supervisor await client.query(`UPDATE carteras SET supervisor_user_id = $1 WHERE id = $2`, [supervisor.id, existing.id]); } const finalSubcarteraId = subcarteraId || (await client.query<{ id: string }>(`SELECT id FROM carteras WHERE parent_id = $1 AND auxiliar_user_id = $2 LIMIT 1`, [carteraPrincipal.id, auxiliar.id])).rows[0].id; console.log('✅ Subcartera del auxiliar creada/recuperada'); // Mover contribuyentes de la cartera principal a la subcartera del auxiliar const { rows: entidades } = await client.query<{ entidad_id: string }>(` SELECT entidad_id FROM cartera_entidades WHERE cartera_id = $1 `, [carteraPrincipal.id]); for (const e of entidades) { await client.query(` INSERT INTO cartera_entidades (cartera_id, entidad_id) VALUES ($1, $2) ON CONFLICT DO NOTHING `, [finalSubcarteraId, e.entidad_id]); } // Quitar contribuyentes de la cartera principal (ahora están en la subcartera) await client.query(`DELETE FROM cartera_entidades WHERE cartera_id = $1`, [carteraPrincipal.id]); // La cartera principal ya no tiene auxiliar asignado await client.query(`UPDATE carteras SET auxiliar_user_id = NULL WHERE id = $1`, [carteraPrincipal.id]); await client.query('COMMIT'); console.log(`✅ ${entidades.length} contribuyentes movidos a la subcartera del auxiliar`); console.log('✅ Cartera principal limpia (sin auxiliar)'); // 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 `, [auxiliar.id, supervisor.id]); console.log('✅ Relación auxiliar → supervisor registrada'); // Validar: el auxiliar debe ser elegible para todos los contribuyentes const { rows: elegibles } = await pool.query<{ entidad_id: string }>(` SELECT DISTINCT ce.entidad_id FROM carteras c JOIN cartera_entidades ce ON ce.cartera_id = c.id WHERE c.auxiliar_user_id = $1 `, [auxiliar.id]); console.log(`✅ Auxiliar elegible para ${elegibles.length} contribuyentes`); } catch (err) { await client.query('ROLLBACK'); throw err; } finally { client.release(); } console.log('\n🎉 Estructura de carteras corregida'); } main() .catch((e) => { console.error('\n❌ Error:', e); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); await tenantDb.shutdown(); });