import type { CarePlan } from 'fhir/r4'; // Who may CHANGE a care plan, and what a reader at another practice is told. // // ONE RECORD, MANY PARTICIPANTS. A plan the patient's other clinician wrote is exactly what this product // exists to show, so it is never hidden. What it is not is theirs to edit. // // AND IT SAYS SO BEFORE ANYTHING IS TYPED. An editor that looks writable and refuses at save has already // cost the practitioner the work they entered, and it teaches them the product is unreliable rather than // that the plan belongs to somebody else. The reason is returned so a surface can render it beside a // disabled editor. export const ORGANIZATION_TAG_SYSTEM = 'https://akashichealthrecords.com/fhir/tags/organization'; export interface CarePlanAuthorshipContext { /** The practice the viewer is acting in. */ currentOrganizationId: string | undefined; /** Whether a collaboration team the viewer belongs to holds this plan. */ sharedWithMyCollaborationTeam?: boolean; } export interface CarePlanAuthorship { /** A plan is never hidden. This is here so a caller cannot accidentally treat absence as refusal. */ mayRead: true; mayEdit: boolean; isFromAnotherPractice: boolean; sharedWithMe: boolean; /** Why it cannot be edited, ready to render. `undefined` when it can. */ readOnlyReason?: string; } /** The practice that wrote this plan, or `undefined` when the tag is missing. */ export const carePlanOwningOrganizationId = (plan: CarePlan | undefined): string | undefined => plan?.meta?.tag?.find((tag) => tag.system === ORGANIZATION_TAG_SYSTEM)?.code; /** * What this viewer may do with this plan. * * THREE ANSWERS, AND THEY ARE THE SERVER'S. `evaluateCarePlanAuthorship` is the rule; this mirrors it * so a surface never hides an edit the server would allow: * * the plan names an owner and it is the viewer's may edit * the plan names an owner and it is somebody else's read-only, naming them * the plan names NO owner may edit * * THE THIRD ONE USED TO DISAGREE, and the disagreement is what this note replaces. This file used to * fail closed on an untagged plan, arguing that "treating 'no owner' as 'mine' would let any practice * change a record whose provenance was lost". The server argues the opposite and wins twice over. * * It wins on principle: it is the authorization boundary, and a client stricter than the boundary hides * work somebody may do - the quieter of the two failures, because nobody reports a button that was * never there. Where the boundary cannot be established there is nothing to enforce, and refusing * anyway renders absence as a denial. * * And it wins on the facts: an untagged plan cannot be created through this product. `POST /R4/CarePlan` * sets `requireOrganizationId`, and `addOrganizationContext` stamps the tag on BOTH its success and its * failure path - so the case this file was failing closed on is one the create path cannot produce. * Failing closed protected against a state that does not occur, at the cost of disagreeing with the * boundary about one that might. * * FAILING CLOSED WOULD NARROW EDITING ONLY, NEVER READING, and that much was always right: a record the * patient owns does not disappear because this client could not work out who wrote it. * * SHARING IS NOT AUTHORSHIP. `sharedWithMyCollaborationTeam` widens who can SEE a plan and is * deliberately absent from the edit decision - attaching a plan to a team changes visibility and never * ownership. */ export const carePlanAuthorship = ( plan: CarePlan | undefined, context: CarePlanAuthorshipContext, ): CarePlanAuthorship => { const owner = carePlanOwningOrganizationId(plan); const viewer = context.currentOrganizationId; const sharedWithMe = context.sharedWithMyCollaborationTeam === true; // No owner, nothing to enforce - the server's first answer, and now this one. if (!owner) { return { mayRead: true, mayEdit: true, isFromAnotherPractice: false, sharedWithMe }; } if (owner !== viewer) { return { mayRead: true, mayEdit: false, isFromAnotherPractice: true, sharedWithMe, readOnlyReason: 'This plan was written by another practice. You can read it, but only they can change it.', }; } return { mayRead: true, mayEdit: true, isFromAnotherPractice: false, sharedWithMe }; };