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:
124
apps/api/scripts/seed-demo-obligaciones-tareas.ts
Normal file
124
apps/api/scripts/seed-demo-obligaciones-tareas.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* 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();
|
||||
});
|
||||
Reference in New Issue
Block a user