@fuego-systems/core 0.1.14 → 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.d.ts +58 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +63 -2
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/plan-definition-group.d.ts +73 -1
- package/dist/plan-definition-group.d.ts.map +1 -1
- package/dist/plan-definition-group.js +150 -11
- package/dist/plan-definition-group.js.map +1 -1
- package/dist/practica-mapping.d.ts +108 -0
- package/dist/practica-mapping.d.ts.map +1 -0
- package/dist/practica-mapping.js +286 -0
- package/dist/practica-mapping.js.map +1 -0
- package/dist/practica-resolution.d.ts +117 -0
- package/dist/practica-resolution.d.ts.map +1 -0
- package/dist/practica-resolution.js +243 -0
- package/dist/practica-resolution.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MANDATORY_ADDON_OVERRIDE_ACTION_ID = void 0;
|
|
3
4
|
exports.isProtocolGroup = isProtocolGroup;
|
|
4
5
|
exports.getProtocolActions = getProtocolActions;
|
|
5
6
|
exports.getDefaultProtocol = getDefaultProtocol;
|
|
@@ -9,7 +10,28 @@ exports.buildProtocolGroup = buildProtocolGroup;
|
|
|
9
10
|
exports.configureProtocolGroup = configureProtocolGroup;
|
|
10
11
|
exports.resolveProtocolSelection = resolveProtocolSelection;
|
|
11
12
|
exports.readProtocolSelection = readProtocolSelection;
|
|
13
|
+
exports.readMandatoryAddonOverrides = readMandatoryAddonOverrides;
|
|
14
|
+
exports.resolveVariantMandatoryAddons = resolveVariantMandatoryAddons;
|
|
12
15
|
const constants_1 = require("./constants");
|
|
16
|
+
/**
|
|
17
|
+
* `action.id` of the per-variant group that overrides the order-set PD's
|
|
18
|
+
* default mandatory add-ons (the tipo-3 `getMandatoryAddonIds` path). It lives
|
|
19
|
+
* as a direct child of a protocol-group variant action, kept structurally
|
|
20
|
+
* separate from the variant's own add-on sub-actions (the tipo-2
|
|
21
|
+
* `resolveProtocolSelection` path) so the two never conflate. Its group node
|
|
22
|
+
* carries no `definitionCanonical`, so `resolveProtocolSelection`,
|
|
23
|
+
* `getProtocolActions` and friends skip it and never descend into it.
|
|
24
|
+
*/
|
|
25
|
+
exports.MANDATORY_ADDON_OVERRIDE_ACTION_ID = 'mandatory-addons';
|
|
26
|
+
/**
|
|
27
|
+
* FHIR-standard ActionType code system. Each override sub-action carries a
|
|
28
|
+
* `type.coding` from this system: `create` = add the add-on to the variant's
|
|
29
|
+
* mandatory set, `remove` = drop a PD-default mandatory add-on. `requiredBehavior`
|
|
30
|
+
* is not used for this because FHIR's value set has no "exclude" member.
|
|
31
|
+
*/
|
|
32
|
+
const ACTION_TYPE_SYSTEM = 'http://terminology.hl7.org/CodeSystem/action-type';
|
|
33
|
+
const ACTION_TYPE_ADD = 'create';
|
|
34
|
+
const ACTION_TYPE_REMOVE = 'remove';
|
|
13
35
|
// ─── Detection ───────────────────────────────────────────────────────────────
|
|
14
36
|
/**
|
|
15
37
|
* Returns `true` if the PlanDefinition is a protocol-group (wrapping multiple concrete PDs).
|
|
@@ -85,12 +107,14 @@ function getProtocolGroupHealthcareServiceReference(pd) {
|
|
|
85
107
|
function buildProtocolGroup(title, entries, existingId, hsReference) {
|
|
86
108
|
const actions = entries.map((e) => {
|
|
87
109
|
var _a;
|
|
88
|
-
const subActions = ((_a = e.addons) !== null && _a !== void 0 ? _a : []).map((addon) => buildAction(addon.id, addon.display, addon.ref, addon.code, addon.duration, addon.question, addon.label, addon.mandatory));
|
|
89
|
-
const action = buildAction(e.isDefault ? 'default' : e.id, e.display, e.ref, e.code, e.duration, e.question, e.label);
|
|
110
|
+
const subActions = ((_a = e.addons) !== null && _a !== void 0 ? _a : []).map((addon) => buildAction(addon.id, addon.display, addon.ref, addon.code, addon.duration, addon.question, addon.label, addon.mandatory, addon.praId));
|
|
111
|
+
const action = buildAction(e.isDefault ? 'default' : e.id, e.display, e.ref, e.code, e.duration, e.question, e.label, undefined, e.praId);
|
|
90
112
|
// Top-level actions use exactly-one selection behavior
|
|
91
113
|
action.selectionBehavior = 'exactly-one';
|
|
92
|
-
|
|
93
|
-
|
|
114
|
+
const overrideGroup = buildMandatoryAddonOverrideGroup(e.mandatoryAddonOverrides);
|
|
115
|
+
const childActions = overrideGroup ? [...subActions, overrideGroup] : subActions;
|
|
116
|
+
if (childActions.length > 0) {
|
|
117
|
+
action.action = childActions;
|
|
94
118
|
// When an action has sub-actions (addons), they use `any` grouping
|
|
95
119
|
action.groupingBehavior = 'logical-group';
|
|
96
120
|
}
|
|
@@ -151,8 +175,12 @@ function configureProtocolGroup(pd, selection) {
|
|
|
151
175
|
const isSelected = a.id === selection.actionId;
|
|
152
176
|
const subActions = ((_a = a.action) !== null && _a !== void 0 ? _a : []).map((sub) => {
|
|
153
177
|
var _a;
|
|
178
|
+
// The mandatory-addons override group is server-side configuration,
|
|
179
|
+
// not a selectable addon — never stamp it as prechecked, even if a
|
|
180
|
+
// (buggy) caller passes its id among the addonIds.
|
|
181
|
+
const isOverrideGroup = sub.id === exports.MANDATORY_ADDON_OVERRIDE_ACTION_ID;
|
|
154
182
|
const isMandatory = sub.requiredBehavior === 'must';
|
|
155
|
-
const isOn = isSelected && (isMandatory || addonSet.has((_a = sub.id) !== null && _a !== void 0 ? _a : ''));
|
|
183
|
+
const isOn = isSelected && !isOverrideGroup && (isMandatory || addonSet.has((_a = sub.id) !== null && _a !== void 0 ? _a : ''));
|
|
156
184
|
return {
|
|
157
185
|
...sub,
|
|
158
186
|
precheckBehavior: isOn ? 'yes' : 'no'
|
|
@@ -224,7 +252,7 @@ function readProtocolSelection(pd) {
|
|
|
224
252
|
if (prechecked === null || prechecked === void 0 ? void 0 : prechecked.id) {
|
|
225
253
|
const addonIds = ((_b = prechecked.action) !== null && _b !== void 0 ? _b : [])
|
|
226
254
|
.filter((sub) => {
|
|
227
|
-
if (!sub.id) {
|
|
255
|
+
if (!sub.id || sub.id === exports.MANDATORY_ADDON_OVERRIDE_ACTION_ID) {
|
|
228
256
|
return false;
|
|
229
257
|
}
|
|
230
258
|
if (sub.requiredBehavior === 'must') {
|
|
@@ -250,6 +278,12 @@ function getActionServiceCode(action) {
|
|
|
250
278
|
.find((c) => c.system === constants_1.SERVICE_TYPE_SYSTEM);
|
|
251
279
|
return (_b = coding === null || coding === void 0 ? void 0 : coding.code) !== null && _b !== void 0 ? _b : '';
|
|
252
280
|
}
|
|
281
|
+
function getActionPraId(action) {
|
|
282
|
+
var _a, _b;
|
|
283
|
+
return (_b = ((_a = action.code) !== null && _a !== void 0 ? _a : [])
|
|
284
|
+
.flatMap((cc) => { var _a; return (_a = cc.coding) !== null && _a !== void 0 ? _a : []; })
|
|
285
|
+
.find((c) => c.system === constants_1.EGES_PRACTICA_SYSTEM)) === null || _b === void 0 ? void 0 : _b.code;
|
|
286
|
+
}
|
|
253
287
|
function precheckToBool(value) {
|
|
254
288
|
if (value === 'yes') {
|
|
255
289
|
return true;
|
|
@@ -262,7 +296,7 @@ function precheckToBool(value) {
|
|
|
262
296
|
function extractActionInfo(a, isDefault) {
|
|
263
297
|
var _a, _b, _c, _d, _e, _f;
|
|
264
298
|
const subActions = ((_a = a.action) !== null && _a !== void 0 ? _a : [])
|
|
265
|
-
.filter((sub) => Boolean(sub.definitionCanonical))
|
|
299
|
+
.filter((sub) => Boolean(sub.definitionCanonical) && sub.id !== exports.MANDATORY_ADDON_OVERRIDE_ACTION_ID)
|
|
266
300
|
.map((sub) => extractActionInfo(sub, false));
|
|
267
301
|
return {
|
|
268
302
|
actionId: (_b = a.id) !== null && _b !== void 0 ? _b : '',
|
|
@@ -272,20 +306,125 @@ function extractActionInfo(a, isDefault) {
|
|
|
272
306
|
protocolRef: a.definitionCanonical,
|
|
273
307
|
protocolDisplay: (_f = a.title) !== null && _f !== void 0 ? _f : '',
|
|
274
308
|
protocolCode: getActionServiceCode(a),
|
|
309
|
+
praId: getActionPraId(a),
|
|
275
310
|
duration: a.timingDuration,
|
|
276
311
|
prechecked: precheckToBool(a.precheckBehavior),
|
|
277
312
|
mandatory: a.requiredBehavior === 'must' ? true : undefined,
|
|
278
|
-
addons: subActions
|
|
313
|
+
addons: subActions,
|
|
314
|
+
mandatoryAddonOverrides: readMandatoryAddonOverrides(a)
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Reads the per-variant mandatory-addon overrides out of a variant action's
|
|
319
|
+
* `mandatory-addons` group. Returns `[]` when the variant declares none.
|
|
320
|
+
*
|
|
321
|
+
* @param variantAction - a protocol-group top-level variant action
|
|
322
|
+
* @returns the variant's mandatory-addon overrides (empty when none)
|
|
323
|
+
*/
|
|
324
|
+
function readMandatoryAddonOverrides(variantAction) {
|
|
325
|
+
var _a, _b;
|
|
326
|
+
const group = ((_a = variantAction.action) !== null && _a !== void 0 ? _a : []).find((sub) => sub.id === exports.MANDATORY_ADDON_OVERRIDE_ACTION_ID);
|
|
327
|
+
const result = [];
|
|
328
|
+
for (const sub of (_b = group === null || group === void 0 ? void 0 : group.action) !== null && _b !== void 0 ? _b : []) {
|
|
329
|
+
if (!sub.definitionCanonical) {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
const action = overrideActionFromActionType(sub);
|
|
333
|
+
if (!action) {
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
result.push(sub.title !== undefined
|
|
337
|
+
? { ref: sub.definitionCanonical, action, display: sub.title }
|
|
338
|
+
: { ref: sub.definitionCanonical, action });
|
|
339
|
+
}
|
|
340
|
+
return result;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Resolves the effective mandatory add-on canonicals for a selected variant:
|
|
344
|
+
* the order-set PD defaults, plus the variant's `'add'` overrides, minus its
|
|
345
|
+
* `'remove'` overrides. Order-set defaults and overrides are matched by exact
|
|
346
|
+
* canonical string. Deduplicated; input order of the defaults is preserved,
|
|
347
|
+
* with any added canonicals appended.
|
|
348
|
+
*
|
|
349
|
+
* @param defaultRefs - canonicals of the order-set PD's default mandatory add-ons
|
|
350
|
+
* @param variantAction - the selected protocol-group variant action, or undefined
|
|
351
|
+
* @returns the resolved set of mandatory add-on canonicals
|
|
352
|
+
*/
|
|
353
|
+
function resolveVariantMandatoryAddons(defaultRefs, variantAction) {
|
|
354
|
+
const resolved = new Set(defaultRefs);
|
|
355
|
+
if (variantAction) {
|
|
356
|
+
for (const override of readMandatoryAddonOverrides(variantAction)) {
|
|
357
|
+
if (override.action === 'add') {
|
|
358
|
+
resolved.add(override.ref);
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
resolved.delete(override.ref);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return Array.from(resolved);
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Maps an override sub-action's `type.coding` (ActionType) back to add/remove.
|
|
369
|
+
*
|
|
370
|
+
* @param sub - an override group sub-action
|
|
371
|
+
* @returns `'add'` for `create`, `'remove'` for `remove`, else undefined
|
|
372
|
+
*/
|
|
373
|
+
function overrideActionFromActionType(sub) {
|
|
374
|
+
var _a, _b, _c;
|
|
375
|
+
const code = (_c = ((_b = (_a = sub.type) === null || _a === void 0 ? void 0 : _a.coding) !== null && _b !== void 0 ? _b : []).find((c) => c.system === ACTION_TYPE_SYSTEM)) === null || _c === void 0 ? void 0 : _c.code;
|
|
376
|
+
if (code === ACTION_TYPE_ADD) {
|
|
377
|
+
return 'add';
|
|
378
|
+
}
|
|
379
|
+
if (code === ACTION_TYPE_REMOVE) {
|
|
380
|
+
return 'remove';
|
|
381
|
+
}
|
|
382
|
+
return undefined;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Builds the `mandatory-addons` override group child for a variant, or returns
|
|
386
|
+
* `undefined` when the variant declares no overrides. Each override becomes a
|
|
387
|
+
* sub-action referencing the add-on's canonical, with `requiredBehavior`
|
|
388
|
+
* `'must'` (add) or `'prohibited'` (remove).
|
|
389
|
+
*
|
|
390
|
+
* @param overrides - the variant's mandatory-addon overrides (may be undefined)
|
|
391
|
+
* @returns the override group action, or undefined when there are no overrides
|
|
392
|
+
*/
|
|
393
|
+
function buildMandatoryAddonOverrideGroup(overrides) {
|
|
394
|
+
if (!overrides || overrides.length === 0) {
|
|
395
|
+
return undefined;
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
id: exports.MANDATORY_ADDON_OVERRIDE_ACTION_ID,
|
|
399
|
+
title: 'Adicionales obligatorios (override por variante)',
|
|
400
|
+
selectionBehavior: 'any',
|
|
401
|
+
action: overrides.map((o) => {
|
|
402
|
+
const sub = {
|
|
403
|
+
definitionCanonical: o.ref,
|
|
404
|
+
type: {
|
|
405
|
+
coding: [{ system: ACTION_TYPE_SYSTEM, code: o.action === 'add' ? ACTION_TYPE_ADD : ACTION_TYPE_REMOVE }]
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
if (o.display) {
|
|
409
|
+
sub.title = o.display;
|
|
410
|
+
}
|
|
411
|
+
return sub;
|
|
412
|
+
})
|
|
279
413
|
};
|
|
280
414
|
}
|
|
281
|
-
function buildAction(id, title, ref, code, duration, question, labelOverride, mandatory) {
|
|
415
|
+
function buildAction(id, title, ref, code, duration, question, labelOverride, mandatory, praId) {
|
|
282
416
|
const action = {
|
|
283
417
|
id,
|
|
284
418
|
title,
|
|
285
419
|
definitionCanonical: ref
|
|
286
420
|
};
|
|
287
|
-
if (code) {
|
|
288
|
-
action.code = [{
|
|
421
|
+
if (code || praId) {
|
|
422
|
+
action.code = [{
|
|
423
|
+
coding: [
|
|
424
|
+
...(code ? [{ system: constants_1.SERVICE_TYPE_SYSTEM, code }] : []),
|
|
425
|
+
...(praId ? [{ system: constants_1.EGES_PRACTICA_SYSTEM, code: praId }] : [])
|
|
426
|
+
]
|
|
427
|
+
}];
|
|
289
428
|
}
|
|
290
429
|
if (duration) {
|
|
291
430
|
action.timingDuration = duration;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan-definition-group.js","sourceRoot":"","sources":["../src/plan-definition-group.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Duration, PlanDefinition, PlanDefinitionAction } from '@medplum/fhirtypes'\nimport { PD_HEALTHCARE_SERVICE_EXTENSION_URL, PD_TYPE_PROTOCOL_GROUP, PD_TYPE_SYSTEM, SERVICE_TYPE_SYSTEM } from './constants'\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Information about a single action inside a protocol-group PD. */\nexport interface ProtocolActionInfo {\n readonly actionId: string\n readonly isDefault: boolean\n /** Short label for the variant (e.g. \"Helicoidal\") */\n readonly label: string\n /** User-facing question to select this variant */\n readonly question: string\n readonly protocolRef: string\n readonly protocolDisplay: string\n readonly protocolCode: string\n readonly duration?: Duration\n /**\n * Whether this action is pre-selected in a **configured** protocol-group.\n * Set by {@link configureProtocolGroup} via FHIR `precheckBehavior`.\n * `undefined` means the PD has not been configured (use `isDefault` as fallback).\n */\n readonly prechecked?: boolean\n /**\n * Only meaningful on addon (sub-action) entries. When `true`, the addon is\n * always included whenever its parent action is selected — it is not shown\n * as a togglable option in the consumer UI. Encoded in FHIR as\n * `PlanDefinitionAction.requiredBehavior === 'must'`.\n */\n readonly mandatory?: boolean\n /** Optional sub-actions (addons) with `any` selection behavior. */\n readonly addons: readonly ProtocolActionInfo[]\n}\n\n/** Entry used to build a protocol-group PD action. */\nexport interface ProtocolGroupActionEntry {\n readonly id: string\n readonly isDefault: boolean\n readonly label: string\n readonly question: string\n readonly ref: string\n readonly display: string\n readonly code: string\n readonly duration?: Duration\n /**\n * Only meaningful on addon entries. When `true`, the addon is always\n * included with the parent action and is hidden from the consumer UI.\n */\n readonly mandatory?: boolean\n /** Optional sub-action entries (addons). */\n readonly addons?: readonly ProtocolGroupActionEntry[]\n}\n\n// ─── Backward-compat aliases ────────────────────────────────────────────────\n\n/** @deprecated Use {@link ProtocolActionInfo} instead. */\nexport type ProtocolVariantInfo = ProtocolActionInfo\n\n/** @deprecated Use {@link ProtocolActionInfo} instead. */\nexport interface ProtocolDefaultInfo {\n readonly ref: string\n readonly display: string\n readonly code: string\n readonly duration?: Duration\n}\n\n// ─── Detection ───────────────────────────────────────────────────────────────\n\n/**\n * Returns `true` if the PlanDefinition is a protocol-group (wrapping multiple concrete PDs).\n *\n * @param pd - the PlanDefinition to check\n * @returns whether the PlanDefinition is a protocol-group\n */\nexport function isProtocolGroup (pd: PlanDefinition): boolean {\n return (pd.type?.coding ?? []).some(\n (c) => c.system === PD_TYPE_SYSTEM && c.code === PD_TYPE_PROTOCOL_GROUP\n )\n}\n\n// ─── Extraction ──────────────────────────────────────────────────────────────\n\n/**\n * Extracts all protocol actions from a protocol-group PD (preserving order).\n *\n * @param pd - the protocol-group PlanDefinition\n * @returns array of action info objects (the one with `isDefault === true` is the default)\n */\nexport function getProtocolActions (pd: PlanDefinition): ProtocolActionInfo[] {\n return (pd.action ?? [])\n .filter((a): a is PlanDefinitionAction & { definitionCanonical: string } =>\n Boolean(a.definitionCanonical)\n )\n .map((a) => extractActionInfo(a, a.id === 'default'))\n}\n\n/**\n * Extracts the default protocol action from a protocol-group PD.\n *\n * @param pd - the protocol-group PlanDefinition\n * @returns the default protocol info, or undefined if not found\n */\nexport function getDefaultProtocol (pd: PlanDefinition): ProtocolDefaultInfo | undefined {\n const action = (pd.action ?? []).find((a) => a.id === 'default')\n if (!action?.definitionCanonical) { return undefined }\n return {\n ref: action.definitionCanonical,\n display: action.title ?? '',\n code: getActionServiceCode(action),\n duration: action.timingDuration\n }\n}\n\n/**\n * Extracts the non-default variant actions from a protocol-group PD.\n *\n * @param pd - the protocol-group PlanDefinition\n * @returns array of variant info objects\n */\nexport function getProtocolVariants (pd: PlanDefinition): ProtocolActionInfo[] {\n return getProtocolActions(pd).filter((a) => !a.isDefault)\n}\n\n/**\n * Extracts the HealthcareService reference linked to a protocol-group PD.\n *\n * @param pd - the PlanDefinition to inspect\n * @returns the referenced HealthcareService, if present\n */\nexport function getProtocolGroupHealthcareServiceReference (pd: PlanDefinition): string | undefined {\n return pd.extension?.find((e) => e.url === PD_HEALTHCARE_SERVICE_EXTENSION_URL)?.valueReference?.reference\n}\n\n// ─── Construction ────────────────────────────────────────────────────────────\n\n/**\n * Builds a protocol-group PlanDefinition resource.\n *\n * @param title - human-readable title for the group (e.g. \"TAC de hombro — Protocolos\")\n * @param entries - protocol action entries (at least one should have `isDefault: true`)\n * @param existingId - optional ID to reuse when updating an existing group PD\n * @param hsReference - optional HealthcareService reference stored on the group PD\n * @returns the constructed protocol-group PlanDefinition\n */\nexport function buildProtocolGroup (\n title: string,\n entries: readonly ProtocolGroupActionEntry[],\n existingId?: string,\n hsReference?: string\n): PlanDefinition {\n const actions: PlanDefinitionAction[] = entries.map((e) => {\n const subActions = (e.addons ?? []).map((addon) =>\n buildAction(\n addon.id,\n addon.display,\n addon.ref,\n addon.code,\n addon.duration,\n addon.question,\n addon.label,\n addon.mandatory\n )\n )\n\n const action = buildAction(\n e.isDefault ? 'default' : e.id,\n e.display,\n e.ref,\n e.code,\n e.duration,\n e.question,\n e.label\n )\n\n // Top-level actions use exactly-one selection behavior\n action.selectionBehavior = 'exactly-one'\n\n if (subActions.length > 0) {\n action.action = subActions\n // When an action has sub-actions (addons), they use `any` grouping\n action.groupingBehavior = 'logical-group'\n }\n\n return action\n })\n\n const pd: PlanDefinition = {\n resourceType: 'PlanDefinition',\n status: 'active',\n title,\n type: {\n coding: [{ system: PD_TYPE_SYSTEM, code: PD_TYPE_PROTOCOL_GROUP }]\n },\n action: actions\n }\n\n if (hsReference) {\n pd.extension = [\n {\n url: PD_HEALTHCARE_SERVICE_EXTENSION_URL,\n valueReference: { reference: hsReference }\n }\n ]\n }\n\n if (existingId) {\n pd.id = existingId\n }\n\n return pd\n}\n\n// ─── Selection / Configuration ──────────────────────────────────────────────\n\n/**\n * A user's selection within a protocol-group PlanDefinition.\n *\n * Represents the patient's choices when configuring a HealthcareService:\n * one main action (from the `exactly-one` selection) plus zero or more\n * addon sub-actions.\n */\nexport interface ProtocolSelection {\n /** The `action.id` of the chosen top-level action. */\n readonly actionId: string\n /** The `action.id`s of toggled-on addon sub-actions within the selected action. */\n readonly addonIds: readonly string[]\n}\n\n/** Resolved booking parameters for a single protocol action. */\nexport interface ResolvedProtocolAction {\n /** The `definitionCanonical` reference to the order-set PlanDefinition. */\n readonly protocolRef: string\n /** The service-type code for slot searching / billing. */\n readonly serviceType: string\n /** Estimated duration for this action. */\n readonly duration?: Duration\n}\n\n/** Fully resolved booking parameters produced by {@link resolveProtocolSelection}. */\nexport interface ResolvedProtocol {\n /** Resolved main action. */\n readonly main: ResolvedProtocolAction\n /** Resolved addon actions (empty when none selected). */\n readonly addons: readonly ResolvedProtocolAction[]\n}\n\n/**\n * Returns a **configured** copy of a protocol-group PlanDefinition by stamping\n * `precheckBehavior` on every action according to the given selection.\n *\n * - The selected top-level action gets `precheckBehavior: \"yes\"`, all others `\"no\"`.\n * - Within the selected action, each addon sub-action is stamped according to\n * whether its id appears in `selection.addonIds`. **Mandatory addons**\n * (`requiredBehavior === 'must'`) are always stamped `\"yes\"` regardless of\n * the caller-provided ids.\n * - Unselected top-level actions have all their sub-actions stamped as `\"no\"`.\n *\n * The returned PD is a shallow copy — the original is never mutated.\n *\n * @param pd - a protocol-group PlanDefinition\n * @param selection - the user's action + addon selection\n * @returns a new PlanDefinition with `precheckBehavior` set on every action\n * @throws if the selected `actionId` is not found in the PD\n */\nexport function configureProtocolGroup (\n pd: PlanDefinition,\n selection: ProtocolSelection\n): PlanDefinition {\n const actions = pd.action ?? []\n const found = actions.some((a) => a.id === selection.actionId)\n if (!found) {\n throw new Error(`Action \"${selection.actionId}\" not found in protocol-group PlanDefinition`)\n }\n\n const addonSet = new Set(selection.addonIds)\n\n return {\n ...pd,\n action: actions.map((a) => {\n const isSelected = a.id === selection.actionId\n const subActions = (a.action ?? []).map((sub) => {\n const isMandatory = sub.requiredBehavior === 'must'\n const isOn = isSelected && (isMandatory || addonSet.has(sub.id ?? ''))\n return {\n ...sub,\n precheckBehavior: isOn ? 'yes' as const : 'no' as const\n }\n })\n\n return {\n ...a,\n precheckBehavior: isSelected ? 'yes' as const : 'no' as const,\n ...(a.action ? { action: subActions } : {})\n }\n })\n }\n}\n\n/**\n * Resolves a protocol-group PlanDefinition + selection into concrete booking\n * parameters (service-type codes, definitionCanonical references, durations).\n *\n * @param pd - a protocol-group PlanDefinition\n * @param selection - the user's action + addon selection\n * @returns the resolved main + addon protocol references and codes\n * @throws if the selected `actionId` is not found in the PD\n */\nexport function resolveProtocolSelection (\n pd: PlanDefinition,\n selection: ProtocolSelection\n): ResolvedProtocol {\n const actions = pd.action ?? []\n const selected = actions.find((a) => a.id === selection.actionId)\n if (!selected?.definitionCanonical) {\n throw new Error(`Action \"${selection.actionId}\" not found in protocol-group PlanDefinition`)\n }\n\n const addonSet = new Set(selection.addonIds)\n const resolvedAddons: ResolvedProtocolAction[] = (selected.action ?? [])\n .filter((sub): sub is PlanDefinitionAction & { definitionCanonical: string; id: string } => {\n if (!sub.id || !sub.definitionCanonical) { return false }\n const isMandatory = sub.requiredBehavior === 'must'\n return isMandatory || addonSet.has(sub.id)\n })\n .map((sub) => ({\n protocolRef: sub.definitionCanonical,\n serviceType: getActionServiceCode(sub),\n duration: sub.timingDuration\n }))\n\n return {\n main: {\n protocolRef: selected.definitionCanonical,\n serviceType: getActionServiceCode(selected),\n duration: selected.timingDuration\n },\n addons: resolvedAddons\n }\n}\n\n/**\n * Reads the current selection from a **configured** protocol-group PlanDefinition\n * (one whose actions have been stamped with `precheckBehavior` via\n * {@link configureProtocolGroup}).\n *\n * Falls back to `id === \"default\"` with mandatory addons of that default\n * action when no action has `precheckBehavior: \"yes\"`.\n *\n * @param pd - a (possibly configured) protocol-group PlanDefinition\n * @returns the selection encoded in the PD, or the default selection\n */\nexport function readProtocolSelection (pd: PlanDefinition): ProtocolSelection {\n const actions = pd.action ?? []\n\n // Look for an action explicitly marked as prechecked\n const prechecked = actions.find((a) => a.precheckBehavior === 'yes')\n if (prechecked?.id) {\n const addonIds = (prechecked.action ?? [])\n .filter((sub): sub is PlanDefinitionAction & { id: string } => {\n if (!sub.id) { return false }\n if (sub.requiredBehavior === 'must') { return true }\n return sub.precheckBehavior === 'yes'\n })\n .map((sub) => sub.id)\n return { actionId: prechecked.id, addonIds }\n }\n\n // Fallback: default action with its mandatory addons (if any)\n const defaultAction = actions.find((a) => a.id === 'default') ?? actions[0]\n const mandatoryAddonIds = (defaultAction?.action ?? [])\n .filter((sub): sub is PlanDefinitionAction & { id: string } =>\n Boolean(sub.id) && sub.requiredBehavior === 'must'\n )\n .map((sub) => sub.id)\n return { actionId: defaultAction?.id ?? '', addonIds: mandatoryAddonIds }\n}\n\n// ─── Internal helpers ───────────────────────────────────────────────────────\n\nfunction getActionServiceCode (action: PlanDefinitionAction): string {\n const coding = (action.code ?? [])\n .flatMap((cc) => cc.coding ?? [])\n .find((c) => c.system === SERVICE_TYPE_SYSTEM)\n return coding?.code ?? ''\n}\n\nfunction precheckToBool (value: string | undefined): boolean | undefined {\n if (value === 'yes') { return true }\n if (value === 'no') { return false }\n return undefined\n}\n\nfunction extractActionInfo (a: PlanDefinitionAction & { definitionCanonical: string }, isDefault: boolean): ProtocolActionInfo {\n const subActions = (a.action ?? [])\n .filter((sub): sub is PlanDefinitionAction & { definitionCanonical: string } =>\n Boolean(sub.definitionCanonical)\n )\n .map((sub) => extractActionInfo(sub, false))\n\n return {\n actionId: a.id ?? '',\n isDefault,\n label: a.textEquivalent ?? a.title ?? '',\n question: a.description ?? '',\n protocolRef: a.definitionCanonical,\n protocolDisplay: a.title ?? '',\n protocolCode: getActionServiceCode(a),\n duration: a.timingDuration,\n prechecked: precheckToBool(a.precheckBehavior),\n mandatory: a.requiredBehavior === 'must' ? true : undefined,\n addons: subActions\n }\n}\n\nfunction buildAction (\n id: string,\n title: string,\n ref: string,\n code: string,\n duration?: Duration,\n question?: string,\n labelOverride?: string,\n mandatory?: boolean\n): PlanDefinitionAction {\n const action: PlanDefinitionAction = {\n id,\n title,\n definitionCanonical: ref\n }\n if (code) {\n action.code = [{ coding: [{ system: SERVICE_TYPE_SYSTEM, code }] }]\n }\n if (duration) {\n action.timingDuration = duration\n }\n if (question) {\n action.description = question\n }\n if (labelOverride) {\n action.textEquivalent = labelOverride\n }\n if (mandatory) {\n action.requiredBehavior = 'must'\n }\n return action\n}\n"]}
|
|
1
|
+
{"version":3,"file":"plan-definition-group.js","sourceRoot":"","sources":["../src/plan-definition-group.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Duration, PlanDefinition, PlanDefinitionAction } from '@medplum/fhirtypes'\nimport { EGES_PRACTICA_SYSTEM, PD_HEALTHCARE_SERVICE_EXTENSION_URL, PD_TYPE_PROTOCOL_GROUP, PD_TYPE_SYSTEM, SERVICE_TYPE_SYSTEM } from './constants'\n\n/**\n * `action.id` of the per-variant group that overrides the order-set PD's\n * default mandatory add-ons (the tipo-3 `getMandatoryAddonIds` path). It lives\n * as a direct child of a protocol-group variant action, kept structurally\n * separate from the variant's own add-on sub-actions (the tipo-2\n * `resolveProtocolSelection` path) so the two never conflate. Its group node\n * carries no `definitionCanonical`, so `resolveProtocolSelection`,\n * `getProtocolActions` and friends skip it and never descend into it.\n */\nexport const MANDATORY_ADDON_OVERRIDE_ACTION_ID = 'mandatory-addons'\n\n/**\n * FHIR-standard ActionType code system. Each override sub-action carries a\n * `type.coding` from this system: `create` = add the add-on to the variant's\n * mandatory set, `remove` = drop a PD-default mandatory add-on. `requiredBehavior`\n * is not used for this because FHIR's value set has no \"exclude\" member.\n */\nconst ACTION_TYPE_SYSTEM = 'http://terminology.hl7.org/CodeSystem/action-type'\nconst ACTION_TYPE_ADD = 'create'\nconst ACTION_TYPE_REMOVE = 'remove'\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/**\n * How a variant alters one order-set default mandatory add-on:\n * - `'add'` forces the add-on mandatory for this variant even if the order-set\n * PD marks it optional (or does not list it as mandatory).\n * - `'remove'` drops an add-on that the order-set PD marks mandatory by default.\n */\nexport type MandatoryAddonOverrideAction = 'add' | 'remove'\n\n/**\n * A single per-variant override of the order-set's default mandatory add-on set\n * (the tipo-3 path). Referenced by the add-on's `ActivityDefinition` canonical\n * (`${EGES_PRACTICA_SYSTEM}/{pra_id}`) — the same canonical the order-set PD\n * uses — so add/remove resolve by exact canonical match against the defaults.\n */\nexport interface MandatoryAddonOverride {\n /** Add-on `ActivityDefinition` canonical, e.g. `${EGES_PRACTICA_SYSTEM}/{pra_id}`. */\n readonly ref: string\n /** Whether the variant adds or removes this add-on relative to the PD default. */\n readonly action: MandatoryAddonOverrideAction\n /** Optional human label, for the admin editor. */\n readonly display?: string\n}\n\n/** Information about a single action inside a protocol-group PD. */\nexport interface ProtocolActionInfo {\n readonly actionId: string\n readonly isDefault: boolean\n /** Short label for the variant (e.g. \"Helicoidal\") */\n readonly label: string\n /** User-facing question to select this variant */\n readonly question: string\n readonly protocolRef: string\n readonly protocolDisplay: string\n readonly protocolCode: string\n /**\n * EGES pra_id of the referenced práctica, when stamped on the action's\n * `code[]` (an `EGES_PRACTICA_SYSTEM` coding alongside the service-type\n * coding). Persisted by {@link buildProtocolGroup} so admin UIs can show\n * the id without re-reading each order-set PD.\n */\n readonly praId?: string\n readonly duration?: Duration\n /**\n * Whether this action is pre-selected in a **configured** protocol-group.\n * Set by {@link configureProtocolGroup} via FHIR `precheckBehavior`.\n * `undefined` means the PD has not been configured (use `isDefault` as fallback).\n */\n readonly prechecked?: boolean\n /**\n * Only meaningful on addon (sub-action) entries. When `true`, the addon is\n * always included whenever its parent action is selected — it is not shown\n * as a togglable option in the consumer UI. Encoded in FHIR as\n * `PlanDefinitionAction.requiredBehavior === 'must'`.\n */\n readonly mandatory?: boolean\n /** Optional sub-actions (addons) with `any` selection behavior. */\n readonly addons: readonly ProtocolActionInfo[]\n /**\n * Per-variant overrides of the order-set's default mandatory add-on set\n * (tipo-3 path). Empty when the variant inherits the PD defaults unchanged.\n * Only meaningful on top-level variant entries.\n */\n readonly mandatoryAddonOverrides: readonly MandatoryAddonOverride[]\n}\n\n/** Entry used to build a protocol-group PD action. */\nexport interface ProtocolGroupActionEntry {\n readonly id: string\n readonly isDefault: boolean\n readonly label: string\n readonly question: string\n readonly ref: string\n readonly display: string\n readonly code: string\n /** EGES pra_id of the referenced práctica, stamped on the action's `code[]`. */\n readonly praId?: string\n readonly duration?: Duration\n /**\n * Only meaningful on addon entries. When `true`, the addon is always\n * included with the parent action and is hidden from the consumer UI.\n */\n readonly mandatory?: boolean\n /** Optional sub-action entries (addons). */\n readonly addons?: readonly ProtocolGroupActionEntry[]\n /**\n * Per-variant overrides of the order-set's default mandatory add-on set\n * (tipo-3 path). Only meaningful on top-level variant entries; ignored on\n * addon entries.\n */\n readonly mandatoryAddonOverrides?: readonly MandatoryAddonOverride[]\n}\n\n// ─── Backward-compat aliases ────────────────────────────────────────────────\n\n/** @deprecated Use {@link ProtocolActionInfo} instead. */\nexport type ProtocolVariantInfo = ProtocolActionInfo\n\n/** @deprecated Use {@link ProtocolActionInfo} instead. */\nexport interface ProtocolDefaultInfo {\n readonly ref: string\n readonly display: string\n readonly code: string\n readonly duration?: Duration\n}\n\n// ─── Detection ───────────────────────────────────────────────────────────────\n\n/**\n * Returns `true` if the PlanDefinition is a protocol-group (wrapping multiple concrete PDs).\n *\n * @param pd - the PlanDefinition to check\n * @returns whether the PlanDefinition is a protocol-group\n */\nexport function isProtocolGroup (pd: PlanDefinition): boolean {\n return (pd.type?.coding ?? []).some(\n (c) => c.system === PD_TYPE_SYSTEM && c.code === PD_TYPE_PROTOCOL_GROUP\n )\n}\n\n// ─── Extraction ──────────────────────────────────────────────────────────────\n\n/**\n * Extracts all protocol actions from a protocol-group PD (preserving order).\n *\n * @param pd - the protocol-group PlanDefinition\n * @returns array of action info objects (the one with `isDefault === true` is the default)\n */\nexport function getProtocolActions (pd: PlanDefinition): ProtocolActionInfo[] {\n return (pd.action ?? [])\n .filter((a): a is PlanDefinitionAction & { definitionCanonical: string } =>\n Boolean(a.definitionCanonical)\n )\n .map((a) => extractActionInfo(a, a.id === 'default'))\n}\n\n/**\n * Extracts the default protocol action from a protocol-group PD.\n *\n * @param pd - the protocol-group PlanDefinition\n * @returns the default protocol info, or undefined if not found\n */\nexport function getDefaultProtocol (pd: PlanDefinition): ProtocolDefaultInfo | undefined {\n const action = (pd.action ?? []).find((a) => a.id === 'default')\n if (!action?.definitionCanonical) { return undefined }\n return {\n ref: action.definitionCanonical,\n display: action.title ?? '',\n code: getActionServiceCode(action),\n duration: action.timingDuration\n }\n}\n\n/**\n * Extracts the non-default variant actions from a protocol-group PD.\n *\n * @param pd - the protocol-group PlanDefinition\n * @returns array of variant info objects\n */\nexport function getProtocolVariants (pd: PlanDefinition): ProtocolActionInfo[] {\n return getProtocolActions(pd).filter((a) => !a.isDefault)\n}\n\n/**\n * Extracts the HealthcareService reference linked to a protocol-group PD.\n *\n * @param pd - the PlanDefinition to inspect\n * @returns the referenced HealthcareService, if present\n */\nexport function getProtocolGroupHealthcareServiceReference (pd: PlanDefinition): string | undefined {\n return pd.extension?.find((e) => e.url === PD_HEALTHCARE_SERVICE_EXTENSION_URL)?.valueReference?.reference\n}\n\n// ─── Construction ────────────────────────────────────────────────────────────\n\n/**\n * Builds a protocol-group PlanDefinition resource.\n *\n * @param title - human-readable title for the group (e.g. \"TAC de hombro — Protocolos\")\n * @param entries - protocol action entries (at least one should have `isDefault: true`)\n * @param existingId - optional ID to reuse when updating an existing group PD\n * @param hsReference - optional HealthcareService reference stored on the group PD\n * @returns the constructed protocol-group PlanDefinition\n */\nexport function buildProtocolGroup (\n title: string,\n entries: readonly ProtocolGroupActionEntry[],\n existingId?: string,\n hsReference?: string\n): PlanDefinition {\n const actions: PlanDefinitionAction[] = entries.map((e) => {\n const subActions = (e.addons ?? []).map((addon) =>\n buildAction(\n addon.id,\n addon.display,\n addon.ref,\n addon.code,\n addon.duration,\n addon.question,\n addon.label,\n addon.mandatory,\n addon.praId\n )\n )\n\n const action = buildAction(\n e.isDefault ? 'default' : e.id,\n e.display,\n e.ref,\n e.code,\n e.duration,\n e.question,\n e.label,\n undefined,\n e.praId\n )\n\n // Top-level actions use exactly-one selection behavior\n action.selectionBehavior = 'exactly-one'\n\n const overrideGroup = buildMandatoryAddonOverrideGroup(e.mandatoryAddonOverrides)\n const childActions = overrideGroup ? [...subActions, overrideGroup] : subActions\n\n if (childActions.length > 0) {\n action.action = childActions\n // When an action has sub-actions (addons), they use `any` grouping\n action.groupingBehavior = 'logical-group'\n }\n\n return action\n })\n\n const pd: PlanDefinition = {\n resourceType: 'PlanDefinition',\n status: 'active',\n title,\n type: {\n coding: [{ system: PD_TYPE_SYSTEM, code: PD_TYPE_PROTOCOL_GROUP }]\n },\n action: actions\n }\n\n if (hsReference) {\n pd.extension = [\n {\n url: PD_HEALTHCARE_SERVICE_EXTENSION_URL,\n valueReference: { reference: hsReference }\n }\n ]\n }\n\n if (existingId) {\n pd.id = existingId\n }\n\n return pd\n}\n\n// ─── Selection / Configuration ──────────────────────────────────────────────\n\n/**\n * A user's selection within a protocol-group PlanDefinition.\n *\n * Represents the patient's choices when configuring a HealthcareService:\n * one main action (from the `exactly-one` selection) plus zero or more\n * addon sub-actions.\n */\nexport interface ProtocolSelection {\n /** The `action.id` of the chosen top-level action. */\n readonly actionId: string\n /** The `action.id`s of toggled-on addon sub-actions within the selected action. */\n readonly addonIds: readonly string[]\n}\n\n/** Resolved booking parameters for a single protocol action. */\nexport interface ResolvedProtocolAction {\n /** The `definitionCanonical` reference to the order-set PlanDefinition. */\n readonly protocolRef: string\n /** The service-type code for slot searching / billing. */\n readonly serviceType: string\n /** Estimated duration for this action. */\n readonly duration?: Duration\n}\n\n/** Fully resolved booking parameters produced by {@link resolveProtocolSelection}. */\nexport interface ResolvedProtocol {\n /** Resolved main action. */\n readonly main: ResolvedProtocolAction\n /** Resolved addon actions (empty when none selected). */\n readonly addons: readonly ResolvedProtocolAction[]\n}\n\n/**\n * Returns a **configured** copy of a protocol-group PlanDefinition by stamping\n * `precheckBehavior` on every action according to the given selection.\n *\n * - The selected top-level action gets `precheckBehavior: \"yes\"`, all others `\"no\"`.\n * - Within the selected action, each addon sub-action is stamped according to\n * whether its id appears in `selection.addonIds`. **Mandatory addons**\n * (`requiredBehavior === 'must'`) are always stamped `\"yes\"` regardless of\n * the caller-provided ids.\n * - Unselected top-level actions have all their sub-actions stamped as `\"no\"`.\n *\n * The returned PD is a shallow copy — the original is never mutated.\n *\n * @param pd - a protocol-group PlanDefinition\n * @param selection - the user's action + addon selection\n * @returns a new PlanDefinition with `precheckBehavior` set on every action\n * @throws if the selected `actionId` is not found in the PD\n */\nexport function configureProtocolGroup (\n pd: PlanDefinition,\n selection: ProtocolSelection\n): PlanDefinition {\n const actions = pd.action ?? []\n const found = actions.some((a) => a.id === selection.actionId)\n if (!found) {\n throw new Error(`Action \"${selection.actionId}\" not found in protocol-group PlanDefinition`)\n }\n\n const addonSet = new Set(selection.addonIds)\n\n return {\n ...pd,\n action: actions.map((a) => {\n const isSelected = a.id === selection.actionId\n const subActions = (a.action ?? []).map((sub) => {\n // The mandatory-addons override group is server-side configuration,\n // not a selectable addon — never stamp it as prechecked, even if a\n // (buggy) caller passes its id among the addonIds.\n const isOverrideGroup = sub.id === MANDATORY_ADDON_OVERRIDE_ACTION_ID\n const isMandatory = sub.requiredBehavior === 'must'\n const isOn = isSelected && !isOverrideGroup && (isMandatory || addonSet.has(sub.id ?? ''))\n return {\n ...sub,\n precheckBehavior: isOn ? 'yes' as const : 'no' as const\n }\n })\n\n return {\n ...a,\n precheckBehavior: isSelected ? 'yes' as const : 'no' as const,\n ...(a.action ? { action: subActions } : {})\n }\n })\n }\n}\n\n/**\n * Resolves a protocol-group PlanDefinition + selection into concrete booking\n * parameters (service-type codes, definitionCanonical references, durations).\n *\n * @param pd - a protocol-group PlanDefinition\n * @param selection - the user's action + addon selection\n * @returns the resolved main + addon protocol references and codes\n * @throws if the selected `actionId` is not found in the PD\n */\nexport function resolveProtocolSelection (\n pd: PlanDefinition,\n selection: ProtocolSelection\n): ResolvedProtocol {\n const actions = pd.action ?? []\n const selected = actions.find((a) => a.id === selection.actionId)\n if (!selected?.definitionCanonical) {\n throw new Error(`Action \"${selection.actionId}\" not found in protocol-group PlanDefinition`)\n }\n\n const addonSet = new Set(selection.addonIds)\n const resolvedAddons: ResolvedProtocolAction[] = (selected.action ?? [])\n .filter((sub): sub is PlanDefinitionAction & { definitionCanonical: string; id: string } => {\n if (!sub.id || !sub.definitionCanonical) { return false }\n const isMandatory = sub.requiredBehavior === 'must'\n return isMandatory || addonSet.has(sub.id)\n })\n .map((sub) => ({\n protocolRef: sub.definitionCanonical,\n serviceType: getActionServiceCode(sub),\n duration: sub.timingDuration\n }))\n\n return {\n main: {\n protocolRef: selected.definitionCanonical,\n serviceType: getActionServiceCode(selected),\n duration: selected.timingDuration\n },\n addons: resolvedAddons\n }\n}\n\n/**\n * Reads the current selection from a **configured** protocol-group PlanDefinition\n * (one whose actions have been stamped with `precheckBehavior` via\n * {@link configureProtocolGroup}).\n *\n * Falls back to `id === \"default\"` with mandatory addons of that default\n * action when no action has `precheckBehavior: \"yes\"`.\n *\n * @param pd - a (possibly configured) protocol-group PlanDefinition\n * @returns the selection encoded in the PD, or the default selection\n */\nexport function readProtocolSelection (pd: PlanDefinition): ProtocolSelection {\n const actions = pd.action ?? []\n\n // Look for an action explicitly marked as prechecked\n const prechecked = actions.find((a) => a.precheckBehavior === 'yes')\n if (prechecked?.id) {\n const addonIds = (prechecked.action ?? [])\n .filter((sub): sub is PlanDefinitionAction & { id: string } => {\n if (!sub.id || sub.id === MANDATORY_ADDON_OVERRIDE_ACTION_ID) { return false }\n if (sub.requiredBehavior === 'must') { return true }\n return sub.precheckBehavior === 'yes'\n })\n .map((sub) => sub.id)\n return { actionId: prechecked.id, addonIds }\n }\n\n // Fallback: default action with its mandatory addons (if any)\n const defaultAction = actions.find((a) => a.id === 'default') ?? actions[0]\n const mandatoryAddonIds = (defaultAction?.action ?? [])\n .filter((sub): sub is PlanDefinitionAction & { id: string } =>\n Boolean(sub.id) && sub.requiredBehavior === 'must'\n )\n .map((sub) => sub.id)\n return { actionId: defaultAction?.id ?? '', addonIds: mandatoryAddonIds }\n}\n\n// ─── Internal helpers ───────────────────────────────────────────────────────\n\nfunction getActionServiceCode (action: PlanDefinitionAction): string {\n const coding = (action.code ?? [])\n .flatMap((cc) => cc.coding ?? [])\n .find((c) => c.system === SERVICE_TYPE_SYSTEM)\n return coding?.code ?? ''\n}\n\nfunction getActionPraId (action: PlanDefinitionAction): string | undefined {\n return (action.code ?? [])\n .flatMap((cc) => cc.coding ?? [])\n .find((c) => c.system === EGES_PRACTICA_SYSTEM)?.code\n}\n\nfunction precheckToBool (value: string | undefined): boolean | undefined {\n if (value === 'yes') { return true }\n if (value === 'no') { return false }\n return undefined\n}\n\nfunction extractActionInfo (a: PlanDefinitionAction & { definitionCanonical: string }, isDefault: boolean): ProtocolActionInfo {\n const subActions = (a.action ?? [])\n .filter((sub): sub is PlanDefinitionAction & { definitionCanonical: string } =>\n Boolean(sub.definitionCanonical) && sub.id !== MANDATORY_ADDON_OVERRIDE_ACTION_ID\n )\n .map((sub) => extractActionInfo(sub, false))\n\n return {\n actionId: a.id ?? '',\n isDefault,\n label: a.textEquivalent ?? a.title ?? '',\n question: a.description ?? '',\n protocolRef: a.definitionCanonical,\n protocolDisplay: a.title ?? '',\n protocolCode: getActionServiceCode(a),\n praId: getActionPraId(a),\n duration: a.timingDuration,\n prechecked: precheckToBool(a.precheckBehavior),\n mandatory: a.requiredBehavior === 'must' ? true : undefined,\n addons: subActions,\n mandatoryAddonOverrides: readMandatoryAddonOverrides(a)\n }\n}\n\n/**\n * Reads the per-variant mandatory-addon overrides out of a variant action's\n * `mandatory-addons` group. Returns `[]` when the variant declares none.\n *\n * @param variantAction - a protocol-group top-level variant action\n * @returns the variant's mandatory-addon overrides (empty when none)\n */\nexport function readMandatoryAddonOverrides (\n variantAction: PlanDefinitionAction\n): MandatoryAddonOverride[] {\n const group = (variantAction.action ?? []).find((sub) => sub.id === MANDATORY_ADDON_OVERRIDE_ACTION_ID)\n const result: MandatoryAddonOverride[] = []\n for (const sub of group?.action ?? []) {\n if (!sub.definitionCanonical) { continue }\n const action = overrideActionFromActionType(sub)\n if (!action) { continue }\n result.push(\n sub.title !== undefined\n ? { ref: sub.definitionCanonical, action, display: sub.title }\n : { ref: sub.definitionCanonical, action }\n )\n }\n return result\n}\n\n/**\n * Resolves the effective mandatory add-on canonicals for a selected variant:\n * the order-set PD defaults, plus the variant's `'add'` overrides, minus its\n * `'remove'` overrides. Order-set defaults and overrides are matched by exact\n * canonical string. Deduplicated; input order of the defaults is preserved,\n * with any added canonicals appended.\n *\n * @param defaultRefs - canonicals of the order-set PD's default mandatory add-ons\n * @param variantAction - the selected protocol-group variant action, or undefined\n * @returns the resolved set of mandatory add-on canonicals\n */\nexport function resolveVariantMandatoryAddons (\n defaultRefs: readonly string[],\n variantAction: PlanDefinitionAction | undefined\n): string[] {\n const resolved = new Set(defaultRefs)\n if (variantAction) {\n for (const override of readMandatoryAddonOverrides(variantAction)) {\n if (override.action === 'add') {\n resolved.add(override.ref)\n } else {\n resolved.delete(override.ref)\n }\n }\n }\n return Array.from(resolved)\n}\n\n/**\n * Maps an override sub-action's `type.coding` (ActionType) back to add/remove.\n *\n * @param sub - an override group sub-action\n * @returns `'add'` for `create`, `'remove'` for `remove`, else undefined\n */\nfunction overrideActionFromActionType (\n sub: PlanDefinitionAction\n): MandatoryAddonOverrideAction | undefined {\n const code = (sub.type?.coding ?? []).find((c) => c.system === ACTION_TYPE_SYSTEM)?.code\n if (code === ACTION_TYPE_ADD) { return 'add' }\n if (code === ACTION_TYPE_REMOVE) { return 'remove' }\n return undefined\n}\n\n/**\n * Builds the `mandatory-addons` override group child for a variant, or returns\n * `undefined` when the variant declares no overrides. Each override becomes a\n * sub-action referencing the add-on's canonical, with `requiredBehavior`\n * `'must'` (add) or `'prohibited'` (remove).\n *\n * @param overrides - the variant's mandatory-addon overrides (may be undefined)\n * @returns the override group action, or undefined when there are no overrides\n */\nfunction buildMandatoryAddonOverrideGroup (\n overrides: readonly MandatoryAddonOverride[] | undefined\n): PlanDefinitionAction | undefined {\n if (!overrides || overrides.length === 0) {\n return undefined\n }\n return {\n id: MANDATORY_ADDON_OVERRIDE_ACTION_ID,\n title: 'Adicionales obligatorios (override por variante)',\n selectionBehavior: 'any',\n action: overrides.map((o) => {\n const sub: PlanDefinitionAction = {\n definitionCanonical: o.ref,\n type: {\n coding: [{ system: ACTION_TYPE_SYSTEM, code: o.action === 'add' ? ACTION_TYPE_ADD : ACTION_TYPE_REMOVE }]\n }\n }\n if (o.display) {\n sub.title = o.display\n }\n return sub\n })\n }\n}\n\nfunction buildAction (\n id: string,\n title: string,\n ref: string,\n code: string,\n duration?: Duration,\n question?: string,\n labelOverride?: string,\n mandatory?: boolean,\n praId?: string\n): PlanDefinitionAction {\n const action: PlanDefinitionAction = {\n id,\n title,\n definitionCanonical: ref\n }\n if (code || praId) {\n action.code = [{\n coding: [\n ...(code ? [{ system: SERVICE_TYPE_SYSTEM, code }] : []),\n ...(praId ? [{ system: EGES_PRACTICA_SYSTEM, code: praId }] : [])\n ]\n }]\n }\n if (duration) {\n action.timingDuration = duration\n }\n if (question) {\n action.description = question\n }\n if (labelOverride) {\n action.textEquivalent = labelOverride\n }\n if (mandatory) {\n action.requiredBehavior = 'must'\n }\n return action\n}\n"]}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { ConceptMap, Range } from '@medplum/fhirtypes';
|
|
2
|
+
/** FHIR administrative gender codes accepted by mapping conditions. */
|
|
3
|
+
export type PracticaMappingSex = 'male' | 'female' | 'other' | 'unknown';
|
|
4
|
+
/**
|
|
5
|
+
* Conditions under which a mapping rule applies. All present conditions must
|
|
6
|
+
* match (logical AND); a rule with no conditions is an unconditional override.
|
|
7
|
+
*/
|
|
8
|
+
export interface PracticaMappingCondition {
|
|
9
|
+
/**
|
|
10
|
+
* Reference to an `InsurancePlan` (plan tier) or a payer `Organization`
|
|
11
|
+
* (obra social tier), e.g. `InsurancePlan/123` / `Organization/45`.
|
|
12
|
+
*/
|
|
13
|
+
readonly insuranceRef?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Patient age range; bounds inclusive, either side may be open. Each bound
|
|
16
|
+
* Quantity may carry a UCUM code (`a` years, `mo` months, `d` days) —
|
|
17
|
+
* absent unit means years — and each bound is evaluated in its own unit.
|
|
18
|
+
*/
|
|
19
|
+
readonly ageRange?: Range;
|
|
20
|
+
/** FHIR administrative gender. */
|
|
21
|
+
readonly sex?: PracticaMappingSex;
|
|
22
|
+
}
|
|
23
|
+
/** One práctica → práctica substitution rule from the mapping registry. */
|
|
24
|
+
export interface PracticaMappingRule {
|
|
25
|
+
/** EGES pra_id the rule applies to. */
|
|
26
|
+
readonly sourcePraId: number;
|
|
27
|
+
/** EGES pra_id substituted when the rule matches. */
|
|
28
|
+
readonly targetPraId: number;
|
|
29
|
+
readonly condition: PracticaMappingCondition;
|
|
30
|
+
/** Optional human label for the admin editor. */
|
|
31
|
+
readonly display?: string;
|
|
32
|
+
}
|
|
33
|
+
/** Units accepted on `ageRange` bounds (UCUM): years, months, days. */
|
|
34
|
+
export type PracticaAgeUnit = 'a' | 'mo' | 'd';
|
|
35
|
+
/**
|
|
36
|
+
* The patient's exact age at resolution time, floored per calendar unit, so
|
|
37
|
+
* `ageRange` bounds expressed in days, months, or years all compare exactly.
|
|
38
|
+
*/
|
|
39
|
+
export interface PatientAge {
|
|
40
|
+
/** Completed calendar years. */
|
|
41
|
+
readonly years: number;
|
|
42
|
+
/** Completed calendar months. */
|
|
43
|
+
readonly months: number;
|
|
44
|
+
/** Completed days. */
|
|
45
|
+
readonly days: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The patient-side facts a rule's conditions are matched against. Resolved
|
|
49
|
+
* once per booking (see `resolvePracticaMappingContext`).
|
|
50
|
+
*/
|
|
51
|
+
export interface PracticaMappingContext {
|
|
52
|
+
/** `InsurancePlan/{id}` reference of the patient's plan, when known. */
|
|
53
|
+
readonly insurancePlanRef?: string;
|
|
54
|
+
/** `Organization/{id}` reference of the plan's payer, when known. */
|
|
55
|
+
readonly insuranceOrgRef?: string;
|
|
56
|
+
/** Patient age at resolution time, when known. */
|
|
57
|
+
readonly age?: PatientAge;
|
|
58
|
+
readonly sex?: PracticaMappingSex;
|
|
59
|
+
}
|
|
60
|
+
/** Which condition dimensions a matching rule satisfied (for audit/UI). */
|
|
61
|
+
export type PracticaMappingMatchedCondition = 'insurance-plan' | 'insurance-org' | 'age' | 'sex';
|
|
62
|
+
/** A matched rule plus the dimensions it matched on. */
|
|
63
|
+
export interface PracticaMappingMatch {
|
|
64
|
+
readonly rule: PracticaMappingRule;
|
|
65
|
+
readonly matchedConditions: readonly PracticaMappingMatchedCondition[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Builds the singleton práctica-mapping ConceptMap from a list of rules.
|
|
69
|
+
* Rule order is preserved (it is the final precedence tie-break).
|
|
70
|
+
*
|
|
71
|
+
* @param rules - the mapping rules to serialize
|
|
72
|
+
* @param existingId - optional resource id to reuse when updating
|
|
73
|
+
* @returns the ConceptMap resource ready to be saved
|
|
74
|
+
*/
|
|
75
|
+
export declare function buildPracticaMappingConceptMap(rules: readonly PracticaMappingRule[], existingId?: string): ConceptMap;
|
|
76
|
+
/**
|
|
77
|
+
* Reads the mapping rules out of the práctica-mapping ConceptMap, preserving
|
|
78
|
+
* element/target order. Targets with a non-numeric code or an unreadable
|
|
79
|
+
* condition extension are skipped.
|
|
80
|
+
*
|
|
81
|
+
* @param conceptMap - the mapping registry ConceptMap (may be undefined)
|
|
82
|
+
* @returns the parsed rules (empty when the registry is absent or empty)
|
|
83
|
+
*/
|
|
84
|
+
export declare function readPracticaMappingRules(conceptMap: ConceptMap | undefined): PracticaMappingRule[];
|
|
85
|
+
/**
|
|
86
|
+
* Finds the winning rule for a source práctica under the given context.
|
|
87
|
+
*
|
|
88
|
+
* Precedence among matching rules:
|
|
89
|
+
* 1. Most matched condition dimensions wins.
|
|
90
|
+
* 2. Insurance tier breaks ties: a plan-tier match beats an obra-social-tier match.
|
|
91
|
+
* 3. Registry order breaks remaining ties (first rule wins).
|
|
92
|
+
*
|
|
93
|
+
* @param rules - all registry rules (in registry order)
|
|
94
|
+
* @param praId - the source pra_id being resolved
|
|
95
|
+
* @param context - the patient-side facts to match against
|
|
96
|
+
* @returns the winning match, or undefined when no rule applies (identity)
|
|
97
|
+
*/
|
|
98
|
+
export declare function matchPracticaMapping(rules: readonly PracticaMappingRule[], praId: number, context: PracticaMappingContext): PracticaMappingMatch | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* Computes a patient's exact age (completed years, months, and days) at the
|
|
101
|
+
* given date, so `ageRange` bounds in any unit compare correctly.
|
|
102
|
+
*
|
|
103
|
+
* @param birthDate - ISO date string (`YYYY-MM-DD`)
|
|
104
|
+
* @param at - the reference date (defaults to now)
|
|
105
|
+
* @returns the age, or undefined for an unparsable or future birth date
|
|
106
|
+
*/
|
|
107
|
+
export declare function computePatientAge(birthDate: string, at?: Date): PatientAge | undefined;
|
|
108
|
+
//# sourceMappingURL=practica-mapping.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"practica-mapping.d.ts","sourceRoot":"","sources":["../src/practica-mapping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAmE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAW5H,uEAAuE;AACvE,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAA;AAExE;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAA;IACzB,kCAAkC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,kBAAkB,CAAA;CAClC;AAED,2EAA2E;AAC3E,MAAM,WAAW,mBAAmB;IAClC,uCAAuC;IACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,qDAAqD;IACrD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,wBAAwB,CAAA;IAC5C,iDAAiD;IACjD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,uEAAuE;AACvE,MAAM,MAAM,eAAe,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,CAAA;AAE9C;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,gCAAgC;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,sBAAsB;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,wEAAwE;IACxE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAClC,qEAAqE;IACrE,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,kDAAkD;IAClD,QAAQ,CAAC,GAAG,CAAC,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,GAAG,CAAC,EAAE,kBAAkB,CAAA;CAClC;AAED,2EAA2E;AAC3E,MAAM,MAAM,+BAA+B,GAAG,gBAAgB,GAAG,eAAe,GAAG,KAAK,GAAG,KAAK,CAAA;AAEhG,wDAAwD;AACxD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAA;IAClC,QAAQ,CAAC,iBAAiB,EAAE,SAAS,+BAA+B,EAAE,CAAA;CACvE;AAID;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,SAAS,mBAAmB,EAAE,EACrC,UAAU,CAAC,EAAE,MAAM,GAClB,UAAU,CAkCZ;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAE,UAAU,EAAE,UAAU,GAAG,SAAS,GAAG,mBAAmB,EAAE,CA0BnG;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,SAAS,mBAAmB,EAAE,EACrC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,sBAAsB,GAC9B,oBAAoB,GAAG,SAAS,CAiBlC;AA6DD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAE,SAAS,EAAE,MAAM,EAAE,EAAE,GAAE,IAAiB,GAAG,UAAU,GAAG,SAAS,CAcnG"}
|