diff --git a/apps/api/src/modules/vitals/vitals.controller.ts b/apps/api/src/modules/vitals/vitals.controller.ts index 10746a0..e90e6c2 100644 --- a/apps/api/src/modules/vitals/vitals.controller.ts +++ b/apps/api/src/modules/vitals/vitals.controller.ts @@ -4,6 +4,8 @@ import { validateRequest } from "../../middlewares/validate.middleware"; import { calculateVitalsFlags } from "./vitals.flags"; import { VitalsModel } from "./models/vitals.model"; import { emitVitalsCreated } from "../ai/cds.events"; +import { EncounterModel } from "../encounters/models/encounter.model"; +import { PatientModel } from "../patients/models/patient.model"; import { CreateVitalsDto, EncounterVitalsParamsDto, @@ -17,6 +19,17 @@ const ALL_ROLES: Roles[] = Object.values(Roles); type CreateVitalsRequest = Request, unknown, CreateVitalsDto>; type EncounterVitalsRequest = Request; +const toAgeInYears = (dateOfBirth: Date) => { + const now = new Date(); + let age = now.getFullYear() - dateOfBirth.getFullYear(); + const monthDelta = now.getMonth() - dateOfBirth.getMonth(); + if (monthDelta < 0 || (monthDelta === 0 && now.getDate() < dateOfBirth.getDate())) { + age -= 1; + } + + return Math.max(age, 0); +}; + const toPayload = (doc: { _id: unknown; encounterId: string; @@ -60,8 +73,35 @@ router.post( }); } - const mockPatientAge = 45; - const flags = calculateVitalsFlags(mockPatientAge, { + const encounter = await EncounterModel.findOne({ + _id: req.body.encounterId, + clinicId, + }) + .select({ patientId: 1 }) + .lean(); + + if (!encounter) { + return res.status(404).json({ + error: "NotFound", + message: "Encounter not found", + }); + } + + const patient = await PatientModel.findOne({ + _id: encounter.patientId, + clinicId, + }) + .select({ dateOfBirth: 1 }) + .lean(); + + if (!patient?.dateOfBirth) { + return res.status(400).json({ + error: "BadRequest", + message: "Encounter patient record is required before saving vitals", + }); + } + + const flags = calculateVitalsFlags(toAgeInYears(new Date(patient.dateOfBirth)), { bpSystolic: req.body.bpSystolic, bpDiastolic: req.body.bpDiastolic, heartRate: req.body.heartRate, @@ -69,7 +109,7 @@ router.post( }); const created = await VitalsModel.create({ - encounterId: req.body.encounterId ?? "mock-enc-123", + encounterId: req.body.encounterId, authorId, clinicId, timestamp: new Date(), @@ -86,7 +126,7 @@ router.post( // Trigger CDS processing asynchronously so API response path stays fast. emitVitalsCreated({ clinicId, - encounterId: req.body.encounterId ?? "mock-enc-123", + encounterId: req.body.encounterId, vitalsId: String(created._id), }); diff --git a/apps/api/src/modules/vitals/vitals.validation.ts b/apps/api/src/modules/vitals/vitals.validation.ts index bef412b..cdd6e4e 100644 --- a/apps/api/src/modules/vitals/vitals.validation.ts +++ b/apps/api/src/modules/vitals/vitals.validation.ts @@ -1,7 +1,7 @@ import { z } from "zod"; export const createVitalsSchema = z.object({ - encounterId: z.string().trim().min(1).optional(), + encounterId: z.string().trim().min(1), bpSystolic: z.number().min(30).max(300), bpDiastolic: z.number().min(20).max(200), heartRate: z.number().min(20).max(260),