import { useCallback } from "react"; import type { SaveTransport } from "../utils/saveOperation"; import type { ServiceRequest } from "fhir/r4"; import { useHttpService } from "../services/HttpService.tsx"; import { environment } from "../environments/environment.ts"; import type { IValueSet } from "../services"; import type { DiagnosticOrderKind } from "../utils/diagnosticOrderQueue"; /** * Placing a lab order. * * IT IS THE SAME ROUTE A REFERRAL USES, and the category is what tells them apart. `POST * /R4/ServiceRequest` did referral work unconditionally until the server learned to branch: a * referral state, a Task with kind `referral`, and a place in the coordinator's outbound queue. An * order posted without the category would land there carrying a forward-only state machine nobody * could walk back. * * ONLY THE SNOMED CODING COUNTS on the server -- `category.text` is ignored deliberately, so that a * typo cannot choose which branch of that handler runs. */ /** * SNOMED CT, the codes FHIR's own ServiceRequest category examples use. The server reads ONLY the * coding, so a typo in a display string cannot change which branch of that handler runs. */ const CATEGORY_BY_KIND: Record = { laboratory: { coding: [{ system: "http://snomed.info/sct", code: "108252007", display: "Laboratory procedure" }], }, imaging: { coding: [{ system: "http://snomed.info/sct", code: "363679005", display: "Imaging" }], }, }; export type DiagnosticOrderPriority = "routine" | "urgent" | "stat"; export interface PlaceDiagnosticOrderRequest { patientId: string; /** * Which kind of order this is, and therefore which state machine it runs on and which queue column * it appears in. It decides the CATEGORY, which is the whole discriminator on the server. */ kind: DiagnosticOrderKind; /** What was ordered. Free text, because the lookup may be unavailable -- see LabTestPicker. */ testText: string; /** Present only when a code was actually chosen; a result is matched back through it later. */ coding?: IValueSet; /** * The visit this order came out of, and the one link that cannot be recovered later. A lab drawn * on the DAY of a visit is not evidence it was ordered at one. * * Undefined is correct and ordinary: an order placed from a chart opened directly did not come out * of a visit, and stamping one would be a lie the visit container would then repeat. The server * validates any encounter it is given against the patient and refuses a mismatch. */ encounterId?: string; priority: DiagnosticOrderPriority; /** The free-text reason, kept alongside a coded diagnosis when both are given. */ reason?: string; /** The ICD-10-CM diagnosis the order answers - the medical-necessity code a claim carries. */ diagnosis?: IValueSet; } export const usePlaceDiagnosticOrder = () => { const { post } = useHttpService(); return useCallback( // RETURNS THE CREATED RESOURCE, not just its id. The handler stamps the opening state onto // the order before persisting it, so the 201 body is the server's own version -- which is // what lets a caller show the new order without re-reading it. See the screen for why that // matters. async (request: PlaceDiagnosticOrderRequest, transport?: SaveTransport): Promise => post( // ON THE CLINICAL API, NOT THE BASE ONE. ServiceRequest is declared only in // 03-fhir-clinical.yaml; the base gateway has no such resource. Sending it there is not a // permission failure but a route that does not exist, and API Gateway answers an unmatched // resource with 403 and no CORS headers -- so it surfaces as a forbidden OPTIONS preflight // and reads exactly like an authorization problem, which is the wrong thing to look at. // // The patient goes in the QUERY STRING as well as the body, and that is not redundancy: this // route is gated on patient access and that gate runs in an API Gateway request authorizer, // which never receives a body. Without it the call is refused before a handler sees it, and // the browser is told only that it is not authorized. `${environment.fhirClinicalApi}/ServiceRequest?subject=${encodeURIComponent(`Patient/${request.patientId}`)}`, { resourceType: "ServiceRequest", status: "active", intent: "order", category: [CATEGORY_BY_KIND[request.kind]], code: { text: request.testText, ...(request.coding ? { coding: [{ system: request.coding.system, code: request.coding.code, display: request.coding.display }] } : {}), }, subject: { reference: `Patient/${request.patientId}` }, ...(request.encounterId ? { encounter: { reference: `Encounter/${request.encounterId}` } } : {}), priority: request.priority, // One reasonCode carrying both: the coding for anything downstream that matches on it, // the text for the human who typed it. Absent entirely when neither was given. ...(request.diagnosis || request.reason ? { reasonCode: [ { ...(request.diagnosis ? { coding: [{ system: request.diagnosis.system, code: request.diagnosis.code, display: request.diagnosis.display }] } : {}), ...(request.reason ? { text: request.reason } : {}), }, ], } : {}), }, transport, ), [post], ); };