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
77 changes: 77 additions & 0 deletions docs/adr-condicion-paciente-en-turnos-y-prestaciones.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit de docs: este ADR quedó en docs/adr-condicion-paciente-….md (root).

El de doble firma está en docs/rup/0001-….

¿Unificamos convención? Sugerencia: docs/adr/ o docs/rup/ + numeración global (0001, 0002) + tags en frontmatter.

tags:
- rup
- turnos
scope:
- api
- app
---

# ADR 0002: Condición del paciente en turnos y prestaciones

- **Estado:** Aceptado
- **Fecha:** 2026-10-30
- **Repos afectados:** `api`

> **Este documento es vivo.** Los cambios que afecten el almacenamiento o la validación de
> `condicionPaciente` en turnos o prestaciones deben actualizar este ADR.

## Contexto

Los flujos de turnos y prestaciones necesitan registrar la condición del paciente asociada
a la atención. El dato requiere un conjunto de valores controlado, debe poder omitirse y no
debe modificarse en una prestación que ya fue validada.

## Decisión

Agregar el campo opcional `condicionPaciente` a los modelos de turnos y prestaciones, con
estos valores permitidos:

- `ambulatorio`
- `internado`
- `guardia`

La lista de valores y su validación se centralizan en `modules/rup/condicion-paciente.ts`
para reutilizarlas en los flujos de ambos módulos. Los esquemas de persistencia limitan el
campo a esos valores.

Las rutas validan el dato antes de procesarlo:

- La creación de prestaciones y su actualización mediante `estadoPush` rechazan valores
inválidos. En `estadoPush`, no se permite cambiar la condición si la prestación ya está
validada.
- La asignación de turnos y la creación de sobreturnos rechazan condiciones inválidas. En
los sobreturnos, los valores vacíos o nulos se tratan como dato no informado.
- La asignación de un turno guarda la condición recibida. Al liberar o reutilizar un turno,
se limpia la condición anterior para evitar que se herede en otra atención.

## Alternativas consideradas

Los cambios revisados no documentan otras alternativas consideradas.

## Consecuencias

**Positivas**

- Prestaciones y turnos pueden guardar la condición sin hacer obligatorio el dato para
registros existentes o nuevas solicitudes que lo omitan.
- Los valores aceptados son consistentes entre validación de rutas y esquemas de persistencia.
- La condición no persiste accidentalmente al liberar o reutilizar un turno.

**Negativas / a vigilar**

- Los consumidores de estas rutas deben enviar uno de los valores definidos cuando informen
`condicionPaciente`.
- La condición de una prestación validada no puede modificarse mediante `estadoPush`.

## Alcance y puntos abiertos

El alcance documentado comprende la persistencia y validación de `condicionPaciente` en los
flujos de prestaciones y turnos descritos en este ADR. Los cambios TypeScript revisados no
identifican puntos abiertos adicionales.

## Historial de cambios del documento

| Fecha | Cambio |
|---|---|
| 2026-10-30 | Versión inicial, basada en los cambios TypeScript locales. |
5 changes: 5 additions & 0 deletions modules/rup/condicion-paciente.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const CONDICIONES_PACIENTE = ['ambulatorio', 'internado', 'guardia'];

export function condicionPacienteValida(condicionPaciente: any): boolean {
return typeof condicionPaciente === 'string' && CONDICIONES_PACIENTE.indexOf(condicionPaciente) !== -1;
}
3 changes: 3 additions & 0 deletions modules/rup/prestaciones.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import { IFinanciador } from '../../core-v2/mpi/financiador';
import { IOrganizacion } from '../../core/tm/interfaces/IOrganizacion';
import { ISnomedConcept } from './schemas/snomed-concept';

export type CondicionPaciente = 'ambulatorio' | 'internado' | 'guardia';

export interface IPrestacion {
id: ObjectId;
trackId?: ObjectId;
groupId?: ObjectId;
condicionPaciente?: CondicionPaciente;
paciente?: {
id: ObjectId;
nombre: string;
Expand Down
13 changes: 13 additions & 0 deletions modules/rup/routes/prestacion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { registrosProfundidad } from '../controllers/rup';
import { buscarYCrearSolicitudes } from '../controllers/solicitudes.controller';
import { IPrestacionDoc } from '../prestaciones.interface';
import { Prestacion } from '../schemas/prestacion';
import { condicionPacienteValida } from '../condicion-paciente';
import { Auth } from './../../../auth/auth.class';
import { parseDate } from './../../../shared/parse';
import { Agenda } from '../../turnos/schemas/agenda';
Expand Down Expand Up @@ -591,6 +592,9 @@ router.get('/prestaciones', async (req: any, res, next) => {
router.post('/prestaciones', async (req, res, next) => {
try {
const dto = parseDate(JSON.stringify(req.body));
if (dto.condicionPaciente !== undefined && !condicionPacienteValida(dto.condicionPaciente)) {
return next('Condición del paciente inválida');
}

if (dto.inicio === 'top') {
updateRegistroHistorialSolicitud(dto.solicitud, { op: 'creacion' });
Expand Down Expand Up @@ -638,6 +642,15 @@ router.patch('/prestaciones/:id', (req: Request, res, next) => {
}
break;
case 'estadoPush':
if (req.body.condicionPaciente !== undefined) {
if (data.estadoActual.tipo === 'validada') {
return next('Prestación validada, no se puede modificar.');
}
if (!condicionPacienteValida(req.body.condicionPaciente)) {
return next('Condición del paciente inválida');
}
data.condicionPaciente = req.body.condicionPaciente;
}
if (req.body.estado) {
// SI LA SOLICITUD FUE ANULADA NO SE PUEDE REALIZAR OTRA OPERACION
if (data.estadoActual.tipo === 'anulada') {
Expand Down
31 changes: 31 additions & 0 deletions modules/rup/schemas/prestacion.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Prestacion } from './prestacion';

describe('PrestacionSchema condicionPaciente', () => {
const solicitud = { fecha: new Date() };

it('permite omitir la condición en prestaciones existentes o sin informar', () => {
const prestacion = new Prestacion({ noNominalizada: false, solicitud });

expect(prestacion.validateSync()?.errors.condicionPaciente).toBeUndefined();
});

it.each(['ambulatorio', 'internado', 'guardia'])('acepta la condición %s', condicionPaciente => {
const prestacion = new Prestacion({
noNominalizada: false,
solicitud,
condicionPaciente
});

expect(prestacion.validateSync()?.errors.condicionPaciente).toBeUndefined();
});

it('rechaza valores fuera del conjunto permitido', () => {
const prestacion = new Prestacion({
noNominalizada: false,
solicitud,
condicionPaciente: 'desconocido'
});

expect(prestacion.validateSync()?.errors.condicionPaciente).toBeDefined();
});
});
11 changes: 10 additions & 1 deletion modules/rup/schemas/prestacion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import { PrestacionSolicitudHistorialschema } from './prestacion.solicitud.histo
import { SemanticTag } from './semantic-tag';
import { PacienteSubSchema } from '../../../core-v2/mpi/paciente/paciente.schema';

import { CONDICIONES_PACIENTE } from '../condicion-paciente';

export { CONDICIONES_PACIENTE };

export const PrestacionSchema = new Schema({
/**
* ID de un EVENTO externo (internacion, guardia, embarazo) para agrupar un conjunto de prestaciones referidas a ese evento
Expand All @@ -19,6 +23,12 @@ export const PrestacionSchema = new Schema({
*/
groupId: { required: false, type: Schema.Types.ObjectId },

condicionPaciente: {
type: String,
enum: CONDICIONES_PACIENTE,
required: false
},

inicio: {
type: String,
enum: ['top', 'agenda', 'fuera-agenda', 'internacion', 'servicio-intermedio', 'plan-indicaciones'],
Expand Down Expand Up @@ -373,4 +383,3 @@ PrestacionSchema.index(

export const Prestacion = model('prestacion', PrestacionSchema, 'prestaciones');
export const PrestacionHistorial = model('prestacion-historial', PrestacionHistorialSchema, 'prestacion-historial');

10 changes: 10 additions & 0 deletions modules/turnos/controller/agenda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { getAccesosVirtuales } from '../../turnos/controller/turnosController';
import * as cie10 from './../../../core/term/schemas/cie10';
import { ECLQueriesCtr } from './../../../core/tm/eclqueries.routes';
import { handleHttpRequest } from '../../../utils/requestHandler';
import { condicionPacienteValida } from '../../rup/condicion-paciente';

export async function getAgendaById(agendaId) {
return await Agenda.findById(agendaId);
Expand Down Expand Up @@ -135,6 +136,7 @@ export async function liberarTurno(req, data, turno) {
turno.tipoTurno = undefined;
turno.link = undefined;
turno.motivoConsulta = undefined;
turno.condicionPaciente = undefined;
turno.webexLinks = null;
turno.updatedAt = new Date();
turno.updatedBy = req.user.usuario || req.user;
Expand Down Expand Up @@ -434,6 +436,13 @@ export async function actualizarPaciente(pacienteTurno: any, req: any) {
}

// Agenda
export function condicionPacienteSobreturnoValida(sobreturno): boolean {
if (sobreturno && (sobreturno.condicionPaciente === null || sobreturno.condicionPaciente === '')) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

en turno.ts (condicionPacienteTurnoInvalida, ~L28) solo controla el null

delete sobreturno.condicionPaciente;
}
return !sobreturno || sobreturno.condicionPaciente === undefined || condicionPacienteValida(sobreturno.condicionPaciente);
}

export async function agregarSobreturno(req, data) {
const sobreturno = req.body.sobreturno;
if (sobreturno) {
Expand Down Expand Up @@ -1605,6 +1614,7 @@ export function agendaNueva(data, clon, req) {
turno.fechaHoraDacion = undefined;
turno.link = undefined;
turno.motivoConsulta = undefined;
turno.condicionPaciente = undefined;
turno.usuarioDacion = undefined;
turno.profesional = undefined;
turno.horaAsistencia = undefined;
Expand Down
3 changes: 3 additions & 0 deletions modules/turnos/routes/agenda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,9 @@ router.patch('/agenda/:id*?', (req, res, next) => {
}
break;
case 'agregarSobreturno':
if (!agendaCtrl.condicionPacienteSobreturnoValida(req.body.sobreturno)) {
return next({ status: 400, message: 'Condición del paciente inválida' });
}
event = { object: 'turno', accion: 'asignar', data: null };
event.data = await agendaCtrl.agregarSobreturno(req, data);
break;
Expand Down
13 changes: 13 additions & 0 deletions modules/turnos/routes/turno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,15 @@ import { Agenda } from '../schemas/agenda';
import { Constantes } from '../../constantes/constantes.schema';
import { ValidateDarTurno } from './../../../utils/validateDarTurno';
import { userScheduler } from '../../../config.private';
import { condicionPacienteValida } from '../../rup/condicion-paciente';

const router = express.Router();
const dbgTurno = debug('dbgTurno');

function condicionPacienteTurnoInvalida(condicionPaciente: any): boolean {
return condicionPaciente !== undefined && condicionPaciente !== null && !condicionPacienteValida(condicionPaciente);
}

router.get('/turno/:id*?', async (req, res, next) => {

try {
Expand Down Expand Up @@ -55,6 +60,9 @@ function getCarpeta(nroDocumento, idOrganizacion) {
}

router.patch('/turno/agenda/:idAgenda', async (req, res, next) => {
if (condicionPacienteTurnoInvalida(req.body.condicionPaciente)) {
return next({ status: 400, message: 'Condición del paciente inválida' });
}
const continues = ValidateDarTurno.checkTurno(req.body);

if (continues.valid) {
Expand Down Expand Up @@ -82,6 +90,7 @@ router.patch('/turno/agenda/:idAgenda', async (req, res, next) => {
nota: req.body.nota,
link: req.body.link,
motivoConsulta: req.body.motivoConsulta,
condicionPaciente: req.body.condicionPaciente || undefined,
paciente: req.body.paciente,
tipoPrestacion: req.body.tipoPrestacion,
updatedAt: fecha,
Expand Down Expand Up @@ -172,6 +181,9 @@ router.patch('/turno/agenda/:idAgenda', async (req, res, next) => {
*/

router.patch('/turno/:idTurno/bloque/:idBloque/agenda/:idAgenda/', async (req: any, res, next) => {
if (condicionPacienteTurnoInvalida(req.body.condicionPaciente)) {
return next({ status: 400, message: 'Condición del paciente inválida' });
}
const continues = ValidateDarTurno.checkTurno(req.body);
const pacienteTurno = req.body.paciente;

Expand Down Expand Up @@ -315,6 +327,7 @@ router.patch('/turno/:idTurno/bloque/:idBloque/agenda/:idAgenda/', async (req: a
update[etiquetaLink] = req.body.link;
update[etiquetaEmitidoPor] = req.body.emitidoPor ? req.body.emitidoPor : 'Gestión de pacientes';
update[etiquetaMotivoConsulta] = req.body.motivoConsulta;
update[bloqueTurno + '.condicionPaciente'] = req.body.condicionPaciente || undefined;
update[estadoFacturacion] = req.body.estadoFacturacion;

const etiquetaVideoConferencia = bloqueTurno + '.videoConferencia';
Expand Down
6 changes: 6 additions & 0 deletions modules/turnos/schemas/turno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as cie10 from '../../../core/term/schemas/cie10';
import { tipoPrestacionSchema } from '../../../core/tm/schemas/tipoPrestacion';
import { SnomedConcept } from '../../rup/schemas/snomed-concept';
import { PacienteSubSchema } from '../../../core-v2/mpi/paciente/paciente.schema';
import { CONDICIONES_PACIENTE } from '../../rup/condicion-paciente';

const turnoSchema = new mongoose.Schema({
horaInicio: Date,
Expand Down Expand Up @@ -64,6 +65,11 @@ const turnoSchema = new mongoose.Schema({
emitidoPor: String, // Agregamos para identificar desde donde se emitio el turno. Ej: appmobile.
paciente: PacienteSubSchema,
motivoConsulta: String,
condicionPaciente: {
type: String,
enum: [...CONDICIONES_PACIENTE, null],
required: false
},
tipoPrestacion: {
type: tipoPrestacionSchema
},
Expand Down
Loading