From 15d97d1e01c49f9e9ee1f6d4f6fa885134cec6d6 Mon Sep 17 00:00:00 2001 From: ma7payne Date: Mon, 3 Nov 2025 14:35:21 -0300 Subject: [PATCH] feat(REC): Implementa filtros para listado de recetas --- modules/recetas/recetas.routes.ts | 8 ++- modules/recetas/recetasController.ts | 97 +++++++++++++++++++++++++++- 2 files changed, 103 insertions(+), 2 deletions(-) diff --git a/modules/recetas/recetas.routes.ts b/modules/recetas/recetas.routes.ts index e7e96a14f..c9d509912 100644 --- a/modules/recetas/recetas.routes.ts +++ b/modules/recetas/recetas.routes.ts @@ -2,7 +2,7 @@ import { asyncHandler, Request, Response } from '@andes/api-tool'; import { MongoQuery, ResourceBase } from '@andes/core'; import { Auth } from '../../auth/auth.class'; import { Receta } from './receta-schema'; -import { buscarRecetas, getMotivosReceta, setEstadoDispensa, suspender, actualizarAppNotificada, cancelarDispensa, create, buscarRecetasPorProfesional } from './recetasController'; +import { buscarRecetas, getMotivosReceta, setEstadoDispensa, suspender, actualizarAppNotificada, cancelarDispensa, create, buscarRecetasPorProfesional, buscarRecetasConFiltros } from './recetasController'; import { ParamsIncorrect } from './recetas.error'; class RecetasResource extends ResourceBase { @@ -41,6 +41,11 @@ export const getByProfesional = async (req, res) => { res.json(result); }; +export const getConFiltros = async (req, res) => { + const result = await buscarRecetasConFiltros(req); + res.json(result); +}; + export const patch = async (req, res) => { const operacion = req.body.op ? req.body.op.toLowerCase() : ''; let result, status; @@ -96,5 +101,6 @@ RecetasRouter.use(Auth.authenticate()); RecetasRouter.get('/recetas', authorizeByToken, asyncHandler(get)); RecetasRouter.get('/recetas/motivos', asyncHandler(getMotivos)); RecetasRouter.get('/recetas/profesional/:id', authorizeByToken,asyncHandler(getByProfesional)); +RecetasRouter.get('/recetas/filtros', authorizeByToken, asyncHandler(getConFiltros)); RecetasRouter.patch('/recetas', authorizeByToken, asyncHandler(patch)); RecetasRouter.post('/recetas', authorizeByToken, asyncHandler(post)); diff --git a/modules/recetas/recetasController.ts b/modules/recetas/recetasController.ts index 3778b6e4a..a8e6e63c7 100644 --- a/modules/recetas/recetasController.ts +++ b/modules/recetas/recetasController.ts @@ -123,7 +123,7 @@ export async function buscarRecetas(req) { const estadoDispensaArray = params.estadoDispensa.replace(/ /g, '').split(','); options['estadoDispensaActual.tipo'] = { $in: estadoDispensaArray }; } else { - options['estadoDispensaActual.tipo'] = 'sin-dispensa'; + options['estadoActual.tipo'] = null; } const estadoArray = params.estado ? params.estado.replace(/ /g, '').split(',') : []; const fechaFin = params.fechaFin ? moment(params.fechaFin).endOf('day').toDate() : moment().endOf('day').toDate(); @@ -160,6 +160,7 @@ export async function buscarRecetas(req) { } let recetas: any = await Receta.find(options); + if (!recetas.length) { return []; } @@ -184,6 +185,100 @@ export async function buscarRecetas(req) { return recetas; } catch (err) { await informarLog.error('buscarRecetas', { params, options }, err, req); + + return err; + } +} + +/** + * Busca recetas filtrando por rango de fechas y estado. + * Incluye estados de receta y de dispensa simultáneamente. + * + * Parámetros (req.query): + * - fechaInicio: fecha inicial del rango (opcional) + * - fechaFin: fecha final del rango (opcional) + * - estado: uno o varios estados separados por coma (opcional) + * + * Ejemplo de estados: 'pendiente,vigente,vencida,sin-dispensa,dispensada,dispensa-parcial' + */ +export async function buscarRecetasConFiltros(req) { + try { + const { fechaInicio, fechaFin, estado, documento, sexo } = req.query; + const filter: any = {}; + const statusVal = req.query.status; + const estadoVal = estado; + let estadoParam = null; + + if (estadoVal && statusVal) { + estadoParam = `${estadoVal},${statusVal}`; + } else { + estadoParam = estadoVal ?? statusVal; + } + if (!estadoParam || String(estadoParam).trim() === '') { + estadoParam = 'vigente'; + } + + // Validación mínima: al menos un filtro + if (!documento && !sexo && !estadoParam) { + throw new ParamsIncorrect(); + } + + // Filtro por rango de fechas sobre fechaRegistro + if (fechaInicio || fechaFin) { + filter['fechaRegistro'] = {}; + if (fechaInicio) { + filter['fechaRegistro'].$gte = moment(fechaInicio, 'DD-MM-YYYY').startOf('day').toDate(); + } + if (fechaFin) { + filter['fechaRegistro'].$lte = moment(fechaFin, 'DD-MM-YYYY').endOf('day').toDate(); + } + } else { + // Default: último mes hasta hoy si no se especifican fechas + filter['fechaRegistro'] = { + $gte: moment().subtract(1, 'months').startOf('day').toDate(), + $lte: moment().endOf('day').toDate() + }; + } + + // Filtro por estado aplicado tanto a receta como a dispensa + if (estadoParam) { + const estadosRaw = String(estadoParam).replace(/ /g, '').split(',').filter(Boolean); + const incluyeTodas = estadosRaw.some(e => e.toLowerCase() === 'todas'); + // Si llega "todas" (solo o incluido), no aplicar filtro de estado + if (!incluyeTodas) { + const estados = estadosRaw; // ya normalizado + if (estados.length) { + const or: any[] = []; + if (estados.length === 1) { + const val = estados[0]; + or.push({ 'estadoActual.tipo': val }); + or.push({ 'estadoDispensaActual.tipo': val }); + } else { + estados.forEach(val => { + or.push({ 'estadoActual.tipo': val }); + or.push({ 'estadoDispensaActual.tipo': val }); + }); + } + filter['$or'] = or; + } + } else { + // Forzar que se tome cualquier estadoActual.tipo + filter['estadoActual.tipo'] = { $exists: true }; + } + } + + // Filtros por datos del paciente + if (documento) { + filter['paciente.documento'] = documento; + } + if (sexo) { + filter['paciente.sexo'] = sexo; + } + + const recetas = await Receta.find(filter); + return recetas; + } catch (err) { + await informarLog.error('buscarRecetasConFiltros', { query: req.query }, err, req); return err; } }