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
485 changes: 485 additions & 0 deletions jobs/jobRecetaControl.ts

Large diffs are not rendered by default.

115 changes: 115 additions & 0 deletions modules/recetas/receta-control-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { AuditPlugin } from '@andes/mongoose-plugin-audit';
import { Schema, SchemaTypes, model } from 'mongoose';


export const identificadorSubschema = new Schema({
_id: false,
nombre: {
type: String,
required: false
},
conceptId: {
type: String,
required: false
},
codigo: [{
_id: false,
fuente: {
type: String,
required: false
},
valor: {
type: String,
required: false
}
}]
}, { _id: false });

export const recetaControlSchema = new Schema({
idPrestacion: {
type: SchemaTypes.ObjectId,
required: true
},
idRegistro: {
type: SchemaTypes.ObjectId,
required: true
},
idPaciente: {
type: SchemaTypes.ObjectId,
required: true
},
tipo: {
type: String,
enum: ['medicamento', 'magistral', 'dispositivo', 'nutricion'],
required: true
},
nombre: {
type: String,
required: false
},
identificador: {
type: identificadorSubschema,
required: false
},
tratamientoProlongado: {
type: Boolean,
default: false,
required: true
},
cantTotalRecetas: {
type: Number,
default: 1,
required: true
},
intento: {
type: Number,
default: 0,
required: true
},
estado: {
type: String,
enum: ['pendiente', 'incompleto', 'completo', 'suspendido', 'error'],
default: 'pendiente',
required: true
},
recetas: [{
_id: false,
receta_id: {
type: SchemaTypes.ObjectId,
required: false
},
ordenTratamiento: {
type: Number,
required: true
},
origen: {
type: String,
enum: ['andes', 'recetar'],
required: false
},
fecha: {
type: Date,
default: Date.now
}
}],
ultimoError: {
type: SchemaTypes.Mixed,
required: false
},
fechaUltimoIntento: {
type: Date,
required: false
}
});

recetaControlSchema.plugin(AuditPlugin);

recetaControlSchema.index({ idPrestacion: 1, idRegistro: 1 });
recetaControlSchema.index({ idPaciente: 1 });
recetaControlSchema.index({ estado: 1, createdAt: 1 });
recetaControlSchema.index({ 'identificador.conceptId': 1 });
recetaControlSchema.index({ 'identificador.codigo.valor': 1 });

export const RecetaControl = model('recetaControl', recetaControlSchema, 'recetaControl');


12 changes: 11 additions & 1 deletion modules/recetas/recetaLogs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const updateLog = new Logger({
application: 'andes',
bucketBy: 'h',
bucketSize: 100,
expiredAt: '6 M'
expiredAt: '3 M'
});

export const informarLog = new Logger({
Expand Down Expand Up @@ -38,5 +38,15 @@ export const jobsLog = new Logger({
application: 'andes',
bucketBy: 'h',
bucketSize: 100,
expiredAt: '3 M'
});

export const preCargaLog = new Logger({
connection: Connections.logs,
module: 'recetas',
type: 'precarga',
application: 'andes',
bucketBy: 'h',
bucketSize: 100,
expiredAt: '6 M'
});
71 changes: 71 additions & 0 deletions modules/recetas/recetas.helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
export interface CodigoPrescripcion {
fuente: string;
valor: string;
}

export interface IdentificadorPrescripcion {
nombre: string;
conceptId: string;
codigo: CodigoPrescripcion[];
}

/**
* Resuelve y normaliza la identificación de una prescripción (medicamento, magistral, insumo, dispositivo, nutrición).
* Extrae nombre descriptivo, conceptId de SNOMED/ANDES y códigos externos/magistrales.
*/
export function resolverIdentificador(prescripcion: any, tipo?: string): IdentificadorPrescripcion {
if (!prescripcion) {
return null;
}
const esMagistral = tipo === 'magistral' || !!prescripcion.esMagistral;
const esMedicamento = tipo === 'medicamento';

// 1. Nombre
const nombre = esMagistral
? (prescripcion.magistral?.nombre || prescripcion.concepto?.nombre || prescripcion.concepto?.term || prescripcion.nombre)
: (esMedicamento
? (prescripcion.concepto?.term || prescripcion.generico?.term || prescripcion.nombre)
: (prescripcion.generico?.nombre || prescripcion.nombre));

// 2. conceptId de ANDES (si es de SNOMED o concepto ANDES)
const conceptId = prescripcion.concepto?.conceptId
|| prescripcion.generico?.conceptId
|| (prescripcion.concepto?._id ? String(prescripcion.concepto._id) : null);

// 3. Códigos (magistrales, insumos con [{ fuente, valor }], o código alfanumérico externo)
let codigosRaw = prescripcion.magistral?.codigo
|| prescripcion.concepto?.codigo
|| prescripcion.generico?.codigo
|| prescripcion.codigo
|| prescripcion.insumo?.codigo
|| [];

if (!Array.isArray(codigosRaw)) {
if (codigosRaw && typeof codigosRaw === 'object' && (codigosRaw.fuente || codigosRaw.valor)) {
codigosRaw = [codigosRaw];
} else if (typeof codigosRaw === 'string' && codigosRaw.trim()) {
codigosRaw = [{ fuente: prescripcion.fuente || 'externo', valor: codigosRaw.trim() }];
} else {
codigosRaw = [];
}
}

const codigo: CodigoPrescripcion[] = codigosRaw.map((c: any) => ({
fuente: c.fuente || 'externo',
valor: c.valor || c.codigo || (typeof c === 'string' ? c : '')
})).filter((c: any) => c.valor);

// Si viene código externo en prescripcion.origenExterno y no está en codigo
if (prescripcion.origenExterno?.id && !codigo.some((c: any) => c.valor === prescripcion.origenExterno.id)) {
codigo.push({
fuente: prescripcion.origenExterno.app || 'recetar',
valor: String(prescripcion.origenExterno.id)
});
}

return {
nombre,
conceptId,
codigo
};
}
Loading
Loading