Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { HTMLComponent } from '../../model/html-component.class';
import { generateBarcodeSVG } from '../../model/barcode';
import { Receta } from '../../../recetas/receta-schema';
import { recetarClient, isRecetarConfigured } from '../../../recetas/recetar-client';


export class RecetaMedicaComponent extends HTMLComponent {
Expand Down Expand Up @@ -119,10 +120,7 @@ del Ministerio de Salud de la Nación - RL-2025-24026558-APN-SSVEIYES#MS

if (tieneReceta) {
try {
const recetas: any[] = await Receta.find({
idPrestacion: this.prestacion._id.toString(),
idRegistro: this.registro.id,
}).sort({ fechaRegistro: -1 });
const recetas = await this.getRecetasCombinadas();

const recetaPorPrestacion = recetas.find(r =>
!['pendiente', 'eliminada'].includes(r.estadoActual?.tipo)
Expand All @@ -134,6 +132,9 @@ del Ministerio de Salud de la Nación - RL-2025-24026558-APN-SSVEIYES#MS
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}
if (!idReceta && recetaPorPrestacion?.idReceta) {
idReceta = recetaPorPrestacion.idReceta;
}
} catch (error) {
estadoReceta = null;
}
Expand All @@ -160,4 +161,23 @@ del Ministerio de Salud de la Nación - RL-2025-24026558-APN-SSVEIYES#MS
barcodeSVG: generateBarcodeSVG(finalIdReceta, 'code128')
};
}

private async getRecetasCombinadas(): Promise<any[]> {
const local: any[] = await Receta.find({
idPrestacion: this.prestacion._id.toString(),
idRegistro: this.registro.id,
}).sort({ fechaRegistro: -1 });

if (!isRecetarConfigured()) {
return local;
}

try {
const remotas: any[] = await recetarClient.getRecetasPorPrestacion(this.prestacion._id.toString());
const filtradas = remotas.filter(r => !r.idRegistro || r.idRegistro === this.registro.id);
return [...local, ...filtradas];
} catch (error) {
return local;
}
}
}
48 changes: 48 additions & 0 deletions modules/recetas/recetar-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const request = require('request');

export const isRecetarConfigured = (): boolean => !!process.env.RECETAR_API_URL;

const baseUrl = (): string => (process.env.RECETAR_API_URL || '').replace(/\/$/, '');
const token = (): string => process.env.RECETAR_APP_TOKEN || '';

function call(method: string, path: string, payload?: any, qs?: any): Promise<any> {
return new Promise((resolve, reject) => {
request({
method,
url: `${baseUrl()}${path}`,
qs,
body: payload,
json: true,
headers: { Authorization: `Bearer ${token()}` },
}, (error: any, response: any, body: any) => {
if (error) {
return reject(error);
}
if (response && response.statusCode >= 400) {
return reject(new Error(`RecetAr respondio ${response.statusCode}`));
}
resolve(body);
});
});
}

const unwrap = (body: any): any[] => (body && body.data) ? body.data : body;

export const recetarClient = {
ingest(dataReceta: Record<string, unknown>): Promise<any> {
return call('POST', '/modules/recetas', dataReceta);
},

async getRecetas(params: Record<string, unknown>): Promise<any[]> {
return unwrap(await call('GET', '/modules/recetas/filtros', undefined, params));
},

async getRecetasPorPrestacion(idPrestacion: string): Promise<any[]> {
return unwrap(await call('GET', `/modules/recetas/por-prestacion/${idPrestacion}`));
},

patchReceta(body: Record<string, unknown>): Promise<any> {
return call('PATCH', '/modules/recetas', body);
},
};
44 changes: 42 additions & 2 deletions modules/recetas/recetas.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Auth } from '../../auth/auth.class';
import { Receta } from './receta-schema';
import { buscarRecetas, getMotivosReceta, setEstadoDispensa, suspender, actualizarAppNotificada, cancelarDispensa, create, buscarRecetasPorProfesional, buscarRecetasConFiltros, verificarRecetaExistente } from './recetasController';
import { ParamsIncorrect } from './recetas.error';
import { recetarClient, isRecetarConfigured } from './recetar-client';

class RecetasResource extends ResourceBase {
Model = Receta;
Expand All @@ -28,7 +29,8 @@ class RecetasResource extends ResourceBase {

export const get = async (req, res) => {
const result = await buscarRecetas(req);
res.json(result);
const extra = await fetchRecetar(req);
res.json(extra ? mergeResult(result, extra) : result);
};

export const getMotivos = async (req, res) => {
Expand All @@ -43,9 +45,37 @@ export const getByProfesional = async (req, res) => {

export const getConFiltros = async (req, res) => {
const result = await buscarRecetasConFiltros(req);
res.json(result);
const extra = await fetchRecetar(req);
res.json(extra ? mergeResult(result, extra) : result);
};

// Lee las recetas nuevas desde RecetAr (fuente de verdad) para mergear con las historicas de ANDES.
async function fetchRecetar(req: any): Promise<any[] | null> {
if (!isRecetarConfigured()) {
return null;
}
const documento = req.query?.documento;
if (!documento) {
return null;
}
try {
const estado = req.query.estado || req.query.status;
return await recetarClient.getRecetas({ documento, estado, ambito: req.query.ambito });
} catch (err) {
return null;
}
}

function mergeResult(result: any, extra: any[]): any {
if (Array.isArray(result)) {
return [...extra, ...result];
}
if (result && Array.isArray(result.data)) {
return { ...result, data: [...extra, ...result.data] };
}
return result;
}

export const getVerificarReceta = async (req, res) => {
try {
const { documento, sexo, esMagistral, codigoFuente, codigoValor, conceptId } = req.query;
Expand Down Expand Up @@ -79,6 +109,16 @@ export const patch = async (req, res) => {
} else {
switch (operacion) {
case 'suspender':
if (isRecetarConfigured()) {
try {
const proxied = await recetarClient.patchReceta({ op: 'suspender', recetaId, motivo: req.body.motivo, observacion: req.body.observacion, sistema: app });
if (proxied) {
return res.status(200).json(proxied);
}
} catch (err) {
// fallback a la coleccion local de ANDES (historicas)
}
}
result = await suspender(recetaId, req);
break;
case 'dispensar':
Expand Down
7 changes: 6 additions & 1 deletion modules/rup/controllers/rup.events.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { EventCore } from '@andes/event-bus';
import { getProfesionActualizada, crearReceta } from '../../recetas/recetasController';
import { recetarClient, isRecetarConfigured } from '../../recetas/recetar-client';
import * as moment from 'moment';
import { Receta } from '../../recetas/receta-schema';
import { rupEventsLog as logger } from './rup.events.log';
Expand Down Expand Up @@ -87,7 +88,11 @@ EventCore.on('prestacion:receta:create', async ({ prestacion, registro }) => {
diagnostico: medicamento?.diagnostico || null,
};

await crearReceta(dataReceta, prestacion.createdBy);
if (isRecetarConfigured()) {
await recetarClient.ingest(dataReceta);
} else {
await crearReceta(dataReceta, prestacion.createdBy);
}
}
} catch (errorMedicamento) {
logger.error('prestacion:receta:create', { idRegistro, medicamento }, errorMedicamento);
Expand Down
Loading