Merge remote-tracking branch 'origin/main' into wip/v140-E

This commit is contained in:
Marc-André Bombeck committed 2026-10-03 18:56:39 +02:00
commit f3ccc809a8
28 files changed
+891 -38

No files matched your search

+16
View File
@@ -33,6 +33,7 @@ import { withIdempotency } from "@/lib/idempotency";
import { enqueueReminderSatisfy } from "@/lib/jobs/reminder-satisfy";
import { emitDataArrival } from "@/lib/arrivals/emit-shared";
import { resolveOrMintBiomarker } from "@/lib/labs/biomarker-store";
import { sameLabUnit } from "@/lib/labs/unit-normalise";
import { parseReferenceRange } from "@/lib/labs/parse-reference-range";
import { serialiseLabResult } from "@/lib/labs/serialise";
import {
@@ -178,6 +179,21 @@ async function commitOcrRows(request: NextRequest) {
panel: row.panel ?? null,
});
// The marker's unit is the one the reading is stored under, so a row that
// states another unit cannot be written: the number would be kept under a
// unit it is not in. The review screen marks such a row before Save; this
// is the backstop for a client that did not, and for a marker whose unit
// changed between the scan and the commit. A freshly minted marker adopted
// this row's unit, so it cannot disagree.
if (
!isQualitative &&
row.unit !== undefined &&
!sameLabUnit(row.unit, biomarker.unit)
) {
skipped.push({ analyte: row.analyte.trim(), reason: "unit_mismatch" });
continue;
}
// Field-by-field — never spread the parsed row. The row stamps the resolved
// catalog name/unit/range as historical truth and keeps the FK.
const created = await prisma.labResult.create({
+27
View File
@@ -18,6 +18,7 @@ import { withIdempotency } from "@/lib/idempotency";
import { enqueueReminderSatisfy } from "@/lib/jobs/reminder-satisfy";
import { emitDataArrival } from "@/lib/arrivals/emit-shared";
import { resolveOrMintBiomarker } from "@/lib/labs/biomarker-store";
import { sameLabUnit } from "@/lib/labs/unit-normalise";
import {
type ResolvedBiomarker,
serialiseLabResult,
@@ -252,6 +253,32 @@ async function postLabResult(request: NextRequest) {
});
}
// The marker's unit is the one every reading is stored under, so a reading
// that states another unit cannot be written: the number would be kept and
// the label swapped, and nothing downstream could tell. Compared only when a
// unit was actually sent: on the `biomarkerId` path it may be omitted, and
// then the marker's unit is the stated one. A freshly minted marker adopted
// the reading's own unit, so it cannot disagree with it.
if (
!isQualitative &&
unit !== undefined &&
!sameLabUnit(unit, biomarker.unit)
) {
annotate({
action: { name: "labs.create.unit-mismatch" },
meta: { biomarkerId: biomarker.id },
});
return apiError(
`The reading is in ${unit}, but ${biomarker.name} is tracked in ${biomarker.unit}.`,
422,
{
errorCode: "labs.unit.mismatch",
markerUnit: biomarker.unit,
readingUnit: unit,
},
);
}
// Field-by-field assignment — never spread `parsed.data`. The row stamps the
// resolved name/unit/range as historical truth (so a later catalog edit does
// not silently rewrite a past reading) AND keeps the FK; reads resolve the