fix(labs): drop the printed range text when an edit retires the conversion

Changing the value of a converted reading cleared sourceValue and
sourceUnit but kept the printed range text, which is written in the
report's unit. With the unit gone every reader presented it as the marker
unit's window, beside numbers in another unit. The text now goes with the
conversion record (a whole-form echo of it does not bring it back); the
bounds, already converted into the marker's unit, stay.
This commit is contained in:
Marc-André Bombeck committed 2026-10-14 19:49:33 +02:00
1 parent 530d1a6be2
commit 03e80c1a94
2 files changed
+99 -1

No files matched your search

+24 -1
View File
@@ -192,6 +192,9 @@ export const PUT = apiHandler(
const data: Record<string, unknown> = {};
if (d.panel !== undefined) data.panel = d.panel;
if (d.analyte !== undefined) data.analyte = d.analyte;
// Set when a changed value retires a conversion whose printed range text is
// in the report's unit (see below).
let dropPrintedText = false;
if (d.value !== undefined) {
data.value = d.value;
// A number the person typed is no longer the conversion of what the
@@ -205,6 +208,18 @@ export const PUT = apiHandler(
) {
data.sourceValue = null;
data.sourceUnit = null;
// The printed range text belonged to the printed value and is written
// in the report's unit. With `sourceUnit` gone nothing would say so,
// and every reader would present it as the marker unit's window, so
// it goes with the record. The bounds stay: they were converted with
// the value and are already in the marker's unit.
if (
existing.sourceUnit !== null &&
existing.sourceReferenceText !== null
) {
data.sourceReferenceText = null;
dropPrintedText = true;
}
}
}
// v1.18.9 — qualitative edit. A row stays single-typed: editing `value` on a
@@ -220,7 +235,15 @@ export const PUT = apiHandler(
if (d.sourceReferenceHigh !== undefined) {
data.sourceReferenceHigh = d.sourceReferenceHigh;
}
if (d.sourceReferenceText !== undefined) {
// A whole-form echo of the old printed text does not bring it back once the
// value change retired it; a text that differs is a new statement and wins.
if (
d.sourceReferenceText !== undefined &&
!(
dropPrintedText &&
d.sourceReferenceText === existing.sourceReferenceText
)
) {
data.sourceReferenceText = d.sourceReferenceText;
}
if (d.takenAt !== undefined) data.takenAt = d.takenAt;
@@ -297,6 +297,81 @@ describe("a printed reference range, end to end", () => {
expect(observations[0].valueQuantity?.unit).toBe("mg/dL");
});
it("drops the printed text with the conversion record when the value is edited", async () => {
const prisma = getPrismaClient();
const marker = await prisma.biomarker.create({
data: {
userId: USER_ID,
name: "Glucose",
unit: "mg/dL",
lowerBound: 70,
upperBound: 100,
},
});
const PRINTED_MMOL = "3,9 - 5,5 mmol/l";
const { POST } = await import("@/app/api/labs/route");
const created = await POST(
postReq({
biomarkerId: marker.id,
value: 5.6,
unit: "mmol/L",
sourceReferenceLow: 3.9,
sourceReferenceHigh: 5.5,
sourceReferenceText: PRINTED_MMOL,
takenAt: "2026-07-03T08:00:00.000Z",
}),
);
expect(created.status).toBe(201);
const id = (await created.json()).data.id as string;
// The person types a new number in mg/dL, and the form echoes the old
// printed text back with it.
const { PUT } = await import("@/app/api/labs/[id]/route");
const edited = await PUT(
new NextRequest(`http://localhost/api/labs/${id}`, {
method: "PUT",
headers: { "content-type": "application/json" },
body: JSON.stringify({ value: 104, sourceReferenceText: PRINTED_MMOL }),
}),
{ params: Promise.resolve({ id }) },
);
expect(edited.status).toBe(200);
const row = await prisma.labResult.findUniqueOrThrow({ where: { id } });
expect(row.sourceUnit).toBeNull();
expect(row.sourceValue).toBeNull();
expect(row.sourceReferenceText).toBeNull();
// The bounds were converted with the value and stay, in mg/dL.
expect(row.sourceReferenceLow).toBeCloseTo(3.9 * 18.0182, 6);
expect(row.sourceReferenceHigh).toBeCloseTo(5.5 * 18.0182, 6);
// No surface shows mmol/L text beside the mg/dL numbers.
const dto = await readDto("Glucose");
expect(dto.converted).toBe(false);
expect(dto.sourceReferenceText).toBeNull();
expect(dto.printedReferenceText).toBeNull();
expect(dto.referenceOrigin).toBe("source");
expect(dto.referenceHigh).toBeCloseTo(5.5 * 18.0182, 6);
const { loadLabResults } =
await import("@/lib/doctor-report/clinical-records");
const labs = await loadLabResults(
USER_ID,
new Date("2026-01-01T00:00:00.000Z"),
new Date("2026-12-31T23:59:59.000Z"),
);
expect(labs?.[0].sourceReferenceText).toBeNull();
const { labObservations } = await import("@/lib/fhir/resources/labs");
let seq = 0;
const observations = labObservations(
{ labResults: labs } as Parameters<typeof labObservations>[0],
() => `obs-${++seq}`,
);
expect(observations[0].referenceRange?.[0]?.text).toBeUndefined();
expect(observations[0].referenceRange?.[0]?.high?.unit).toBe("mg/dL");
});
it("keeps a window it cannot read as text and leaves the catalog in force", async () => {
const prisma = getPrismaClient();
await seedMarker();