chore: catálogo obligaciones, cierre automático, fixes SAT y facturación
- Catálogo de obligaciones fiscales expandido a 30 entradas con campo requierePago. - Soporte de frecuencia cuatrimestral en obligaciones y declaraciones. - Automatización de cierre de obligaciones fiscales desde Documentos › Declaraciones. - Nuevas tablas obligacion_evidencias, obligacion_periodos estados y declaracion_obligaciones. - Nuevo servicio obligacion-evidencias.service.ts y endpoints REST. - Refactor de declaraciones.service.ts para vincular obligaciones y crear evidencias. - Notificaciones por email para evidencias de obligaciones. - Adjuntar PDFs en correo de declaración subida. - Fix drill-down de CFDIs: carga completa al visualizar. - Fix sincronización SAT: tipos P/N, UUID case-insensitive, no reutilizar requestId. - Fix suscripciones pending en /configuracion/planes-despacho. - Fix sugerencias de Clave Producto SAT: importar catálogo y robustecer autocomplete. - Quitar toggle manual de completado en Configuración › Obligaciones fiscales › Tareas. - Scripts de soporte para Demo Ventas y utilerías (change-user-email, resend-welcome, import-clave-prod-serv). - Documentación de cambios en docs/CAMBIOS-2026-05-04.md.
This commit is contained in:
126
apps/api/scripts/fix-demo-carteras-asignaciones.ts
Normal file
126
apps/api/scripts/fix-demo-carteras-asignaciones.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* 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();
|
||||
});
|
||||
Reference in New Issue
Block a user