feat: asignaciones obligaciones/tareas + fixes backend
- Migracion 046: tablas obligacion_asignaciones y tarea_asignaciones - Servicio y controller de asignaciones (CRUD + listados) - Fix: enviar correo welcome al invitar usuario nuevo - Fix: quitar JOIN users de queries tenant (usar Prisma en BD central) - Fix: req.params.obligacionId correcto en asignaciones controller - Fix: orden rutas estaticas antes de dinamicas en cartera.routes - Fix: owner/cfo ven todas las asignaciones en getAsignacionesPorSupervisor - Fix: validar que entidad pertenezca a cartera padre en subcartera - Nuevo endpoint GET /carteras/asignaciones/sin-asignar - Nuevo endpoint GET /tareas/mis-tareas
This commit is contained in:
@@ -17,6 +17,8 @@ export interface TareaCatalogo {
|
||||
active: boolean;
|
||||
orden: number;
|
||||
createdAt: Date;
|
||||
auxiliarAsignadoId?: string | null;
|
||||
auxiliarAsignadoNombre?: string | null;
|
||||
}
|
||||
|
||||
export interface TareaPeriodo {
|
||||
@@ -47,6 +49,8 @@ const ROW_TO_TAREA = (r: any): TareaCatalogo => ({
|
||||
active: r.active,
|
||||
orden: r.orden,
|
||||
createdAt: r.created_at,
|
||||
auxiliarAsignadoId: r.auxiliarAsignadoId ?? null,
|
||||
auxiliarAsignadoNombre: r.auxiliarAsignadoNombre ?? null,
|
||||
});
|
||||
|
||||
const ROW_TO_PERIODO = (r: any): TareaPeriodo => ({
|
||||
@@ -68,9 +72,13 @@ function sanitizeUuid(id: string): string {
|
||||
|
||||
export async function listTareas(pool: Pool, contribuyenteId: string): Promise<TareaCatalogo[]> {
|
||||
const { rows } = await pool.query(
|
||||
`SELECT * FROM tareas_catalogo
|
||||
WHERE contribuyente_id = $1 AND active = true
|
||||
ORDER BY orden, nombre`,
|
||||
`SELECT
|
||||
tc.*,
|
||||
ta.auxiliar_user_id AS "auxiliarAsignadoId"
|
||||
FROM tareas_catalogo tc
|
||||
LEFT JOIN tarea_asignaciones ta ON ta.tarea_id = tc.id
|
||||
WHERE tc.contribuyente_id = $1 AND tc.active = true
|
||||
ORDER BY tc.orden, tc.nombre`,
|
||||
[sanitizeUuid(contribuyenteId)],
|
||||
);
|
||||
return rows.map(ROW_TO_TAREA);
|
||||
@@ -272,6 +280,59 @@ export async function listTareasConPeriodoActual(
|
||||
return tareas.map(t => ({ ...t, periodoActual: periodos.get(t.id) ?? null }));
|
||||
}
|
||||
|
||||
export interface TareaConContribuyente extends TareaConPeriodo {
|
||||
contribuyenteRfc: string;
|
||||
contribuyenteRazonSocial: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lee tareas activas con periodo actual para una lista de contribuyentes.
|
||||
* Útil para la vista "Mis Tareas".
|
||||
*/
|
||||
export async function listTareasConPeriodoPorContribuyentes(
|
||||
pool: Pool,
|
||||
contribuyenteIds: string[],
|
||||
): Promise<TareaConContribuyente[]> {
|
||||
if (contribuyenteIds.length === 0) return [];
|
||||
|
||||
// Materializar periodos para cada contribuyente en paralelo
|
||||
await Promise.all(contribuyenteIds.map(id => materializarPeriodos(pool, id)));
|
||||
|
||||
const { rows: tareasRows } = await pool.query(
|
||||
`SELECT tc.*, c.entidad_id AS "contribuyenteId",
|
||||
c.rfc AS "contribuyenteRfc",
|
||||
COALESCE(r.razon_social, c.rfc) AS "contribuyenteRazonSocial"
|
||||
FROM tareas_catalogo tc
|
||||
JOIN contribuyentes c ON c.entidad_id = tc.contribuyente_id
|
||||
LEFT JOIN rfcs r ON UPPER(r.rfc) = UPPER(c.rfc)
|
||||
WHERE tc.contribuyente_id = ANY($1::uuid[]) AND tc.active = true
|
||||
ORDER BY c.rfc, tc.orden, tc.nombre`,
|
||||
[contribuyenteIds],
|
||||
);
|
||||
|
||||
if (tareasRows.length === 0) return [];
|
||||
|
||||
const tareaIds = tareasRows.map((r: any) => r.id);
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
const { rows: periodoRows } = await pool.query(
|
||||
`SELECT DISTINCT ON (tarea_id) *
|
||||
FROM tarea_periodos
|
||||
WHERE tarea_id = ANY($1::uuid[])
|
||||
AND (completada = false OR fecha_limite >= $2::date)
|
||||
ORDER BY tarea_id, fecha_limite ASC`,
|
||||
[tareaIds, today],
|
||||
);
|
||||
const periodos = new Map(periodoRows.map((r: any) => [r.tarea_id, ROW_TO_PERIODO(r)]));
|
||||
|
||||
return tareasRows.map((r: any) => ({
|
||||
...ROW_TO_TAREA(r),
|
||||
contribuyenteId: r.contribuyenteId,
|
||||
contribuyenteRfc: r.contribuyenteRfc,
|
||||
contribuyenteRazonSocial: r.contribuyenteRazonSocial,
|
||||
periodoActual: periodos.get(r.id) ?? null,
|
||||
}));
|
||||
}
|
||||
|
||||
// ─── Completar / descompletar periodo ───
|
||||
|
||||
const ROLES_SUPERVISOR = new Set(['owner', 'cfo', 'supervisor']);
|
||||
|
||||
Reference in New Issue
Block a user