Form Template Field Mapping Extension¶
| Canonical | ../StructureDefinition/nexus-form-template-field-mapping |
|---|---|
| Status | draft · 1.26.0 |
| Base | Extension (constraint) |
| Context | DocumentReference (element) |
| Source | FSH · JSON |
Which fillable fields of a form template's file a chart value pre-fills. One rule per field:
the field's name in the file, verbatim, and the chart value that fills it, coded from the
form chart value vocabulary (patient.lastName, patient.birthDate.year, patient.address.city,
...). A form started from the template opens with each mapped field filled from the patient's
chart and every value still editable; a field the mapping does not name, or whose chart value
cannot be resolved, is left blank.
FSH
The authored source of truth: extensions/FormTemplateFieldMappingExtension.fsh (edit on GitHub). One FSH file may define several related artifacts.
// FormTemplateFieldMappingExtension.fsh
// Which fillable fields of a form template's file a chart value pre-fills, carried on the template.
//
// WHY. A form template is the tenant-wide blank of a fillable PDF. Filling one for a patient
// starts by copying the same demographics into the same boxes every time -- name, date of birth,
// sex, phone, address -- and those boxes have stable names inside the file. This extension lets a
// template say, once, which of its fields a chart value fills, so every form started from it opens
// with those fields already completed and only the clinical content left to write. The mapping
// belongs on the template rather than in a resource of its own because it is meaningful only
// against that file's field names, it is read exactly when the template is read, and nothing ever
// needs to search by it.
//
// KEYED BY FIELD NAME, NOT WIDGET. A fillable PDF names its fields (AcroForm field names) and
// draws each one with one or more widgets. Widget identities exist only while a viewer has the
// file open, and one name can be drawn on several pages -- the WSIB Form 8 repeats the patient's
// name on every page. A rule therefore names the field, verbatim, and applies to every widget that
// carries the name. Names are carried as they appear in the file, spaces and case included; a
// reader compares them exactly.
//
// HOW A VALUE LANDS. The chart value is resolved from the patient's record, then written according
// to what kind of field it lands on: a text field takes the value as text; a checkbox is checked
// when its export value equals the resolved code (`cbsex` with export values `male` / `female`
// against `patient.gender`); a choice field selects the option whose export or display value
// equals the resolved value (a province list against `ON`). Nothing matches, or the chart holds no
// value, and the field is left blank. A pre-filled value is a starting point, never a lock: every
// field stays editable in the form that is produced, and the filed form carries the values in its
// file, not this mapping.
//
// FIXED VALUES. A rule may carry a fixed value in place of a chart value: text written as it is,
// or -- for a checkbox, radio group or choice field -- the export value of the box or option to
// set. It lands by the same per-kind rules, so a fixed value and a chart value are told apart only
// by where they come from. Exactly one of the two is carried; a rule with both, or neither, is
// malformed.
//
// FILL TEMPLATES. The rules at the extension's top level are the DEFAULT fill template, which every
// form template has. Beside it a template may carry any number of NAMED fill templates, each a
// `template` child with a `name` and its own rules of the same shape -- so one blank can open with
// different pre-fills (a full intake, a quick referral) chosen when the form is started. Names are
// unique within a template, compared case-insensitively, and never "default", which names the
// top-level rules. A reader that knows nothing of named templates reads the default and is right.
Alias: $NexusFormTemplateFieldMapping = ../StructureDefinition/nexus-form-template-field-mapping
// What cardinality cannot state. `field 1..1` requires the element to be PRESENT; it does not stop
// it being the empty string, and an empty field name is a rule that binds nothing while passing
// every structural check.
//
// `value.ofType(string)`, NOT `valueString`: Extension.value[x] is a CHOICE, and the publisher
// rejects the shorthand -- an invariant whose expression fails to evaluate is not enforced at all.
Invariant: form-template-field-mapping-field-is-substantive
Description: "Every rule must name a non-empty fillable field -- an empty name satisfies the cardinality while binding nothing"
Severity: #error
Expression: "extension('rule').all(extension('field').value.ofType(string) != '')"
// A field mapped twice leaves which chart value wins to whoever reads the mapping, and two readers
// need not agree. Refused rather than resolved by rule order, because the rules have no order
// that anything else depends on.
//
// `isDistinct()` is set algebra, outside the FHIRPath subset the generated runtime validators can
// evaluate; this rule is enforced by the HL7 validator against the examples and by the writers,
// which refuse a duplicate before it reaches a store.
Invariant: form-template-field-mapping-fields-are-distinct
Description: "A fillable field is mapped at most once within a fill template -- two rules naming the same field would leave which value wins to the reader"
Severity: #error
Expression: "extension('rule').extension('field').value.ofType(string).isDistinct()"
// A rule fills its field from the chart OR with a fixed value. Both would leave which wins to the
// reader; neither is a rule that binds nothing while passing every cardinality check.
Invariant: form-template-field-mapping-rule-has-one-value
Description: "Every rule carries exactly one of a chart value and a fixed value"
Severity: #error
Expression: "extension('rule').all((extension('chartValue').exists() xor extension('staticValue').exists()))"
// The same rule shape applies inside each named fill template.
Invariant: form-template-field-mapping-template-rules-are-well-formed
Description: "Within a named fill template, every rule names a non-empty field, carries exactly one of a chart value and a fixed value, and no field is mapped twice"
Severity: #error
Expression: "extension('template').all(extension('rule').all(extension('field').value.ofType(string) != '' and (extension('chartValue').exists() xor extension('staticValue').exists())) and extension('rule').extension('field').value.ofType(string).isDistinct())"
// A name chooses one fill template, so two templates may not share one -- and none may take the
// default's, which names the top-level rules. Compared case-insensitively, the way the chart
// compares them, so "Default" cannot pose as a second default.
Invariant: form-template-field-mapping-template-names-are-distinct
Description: "Named fill templates carry distinct, non-empty names, none of them the default's"
Severity: #error
Expression: "extension('template').extension('name').value.ofType(string).lower().isDistinct() and extension('template').all(extension('name').value.ofType(string).trim() != '' and extension('name').value.ofType(string).lower().trim() != 'default')"
Extension: NexusFormTemplateFieldMapping
Id: nexus-form-template-field-mapping
Title: "Form Template Field Mapping"
Description: """
Which fillable fields of a form template's file a chart value pre-fills. One `rule` per field:
the field's name in the file, verbatim, and the chart value that fills it, coded from the
form chart value vocabulary (`patient.lastName`, `patient.birthDate.year`, `patient.address.city`,
...). A form started from the template opens with each mapped field filled from the patient's
chart and every value still editable; a field the mapping does not name, or whose chart value
cannot be resolved, is left blank.
Rules are keyed by field name because a fillable PDF may draw one named field on several pages,
and every widget carrying the name is filled. The mapping is carried only by a template (a
`DocumentReference` marked under `NamingSystem/nexus-form-template`); a filed form carries the
filled values in its own file and needs no mapping.
"""
* ^url = $NexusFormTemplateFieldMapping
* ^status = #draft
* ^context[0].type = #element
* ^context[0].expression = "DocumentReference"
* obeys form-template-field-mapping-field-is-substantive and form-template-field-mapping-fields-are-distinct and form-template-field-mapping-rule-has-one-value and form-template-field-mapping-template-rules-are-well-formed and form-template-field-mapping-template-names-are-distinct
* . ^short = "Which fillable fields of the template's file a chart value or a fixed value pre-fills, as the default fill template and any named ones"
* extension contains
rule 0..* MS and
template 0..* MS
* extension[rule] ^short = "One fillable field of the file and what pre-fills it -- the default fill template's rules"
* extension[rule].extension contains
field 1..1 MS and
chartValue 0..1 MS and
staticValue 0..1 MS
* extension[rule].extension[field].value[x] only string
* extension[rule].extension[field] ^short = "The field's name in the file, verbatim (an AcroForm field name; spaces and case kept)"
* extension[rule].extension[field] ^comment = "Compared exactly. A name that no field in the current file carries binds nothing and is not an error: the file may have been revised, and a reader simply finds nothing to fill."
* extension[rule].extension[chartValue].value[x] only code
* extension[rule].extension[chartValue].value[x] from NexusFormChartValueVS (extensible)
* extension[rule].extension[chartValue] ^short = "The chart value that pre-fills the field (patient.firstName, patient.birthDate.year, ...)"
* extension[rule].extension[chartValue] ^comment = "Extensible rather than required: the vocabulary is expected to grow, and a deployment may carry a key ahead of this specification listing it. A reader that does not know a key leaves the field blank -- an unfilled box, never a wrong value -- so an unknown key degrades safely. Carried instead of staticValue, never beside it."
* extension[rule].extension[staticValue].value[x] only string
* extension[rule].extension[staticValue] ^short = "The fixed value that pre-fills the field: text as written, or the export value of the box or option to set"
* extension[rule].extension[staticValue] ^comment = "Written into the field as it is, by the same per-kind rules a resolved chart value lands by: a text field takes it as text, a checkbox or radio group checks the box whose export value equals it, a choice field selects the option whose export value equals it. Carried instead of chartValue, never beside it."
* extension[template] ^short = "One named fill template beside the default: its name, and its own rules"
* extension[template] ^comment = "A form started from the template may be pre-filled from any one fill template, the default unless another is chosen. Names are unique within the template, compared case-insensitively, and never 'default'. A reader that knows only the top-level rules reads the default and ignores these."
* extension[template].extension contains
name 1..1 MS and
rule 0..* MS
* extension[template].extension[name].value[x] only string
* extension[template].extension[name] ^short = "The fill template's name, as the user gave it"
* extension[template].extension[rule] ^short = "One fillable field of the file and what pre-fills it, in this fill template"
* extension[template].extension[rule].extension contains
field 1..1 MS and
chartValue 0..1 MS and
staticValue 0..1 MS
* extension[template].extension[rule].extension[field].value[x] only string
* extension[template].extension[rule].extension[field] ^short = "The field's name in the file, verbatim"
* extension[template].extension[rule].extension[chartValue].value[x] only code
* extension[template].extension[rule].extension[chartValue].value[x] from NexusFormChartValueVS (extensible)
* extension[template].extension[rule].extension[chartValue] ^short = "The chart value that pre-fills the field"
* extension[template].extension[rule].extension[staticValue].value[x] only string
* extension[template].extension[rule].extension[staticValue] ^short = "The fixed value that pre-fills the field"