Patient Profile¶
| Canonical | ../StructureDefinition/nexus-emr-core-patient |
|---|---|
| Status | draft · 1.26.0 |
| Base | Patient (constraint) |
| Source | FSH · JSON |
Generic Nexus EMR core profile for patient demographic and administrative information, based on CA Core+. Designed to support a broad range of integration needs.
FSH
The authored source of truth: resources/StructureDefinition-nexus-emr-core-patient.fsh (edit on GitHub). One FSH file may define several related artifacts.
// NexusEmrCorePatient.fsh
// Defines the generic Nexus EMR Patient profile
// based on FHIR R4 and CA Core+.
Alias: $NexusEmrCorePatient = ../StructureDefinition/nexus-emr-core-patient
Alias: $CABirthSex = http://hl7.org/fhir/ca/baseline/StructureDefinition/ext-patientbirthsex
Alias: $CAIdentifierVersion = http://hl7.org/fhir/ca/baseline/StructureDefinition/ext-identifierversion
// Ontario CA-eReC reason-for-no-HCN (package ca.on.erec.r4). Adopted rather than re-minted -- see
// the identifier section below. Note the canonical is on the Infoway io/CA-eReC path while its
// terminology is on ontariohealth.ca; both are Ontario's, not ours.
Alias: $CAeReCReasonForNoHCN = http://fhir.infoway-inforoute.ca/io/CA-eReC/StructureDefinition/CA-eReC-reason-for-no-HCN-EXT
// patient self identifier rule
// patient: a source/raw identifier SHOULD have a system matching the Nexus EMR patient identifier pattern (recommended, not enforced)
// patient name constraint - CA Baseline ipa-pat-2: family, given, or text (or a data-absent-reason)
Invariant: patient-name-family-or-given
Description: "Patient.name: at least one name SHALL carry a family name, a given name, or text -- or a data-absent-reason extension (CA Baseline ipa-pat-2). Ontario's stricter 'family required' is applied as an edge transform on submission, not in the core profile."
Severity: #warning
Expression: "name.where(family.exists() or given.exists() or text.exists()).exists() or name.extension.where(url = 'http://hl7.org/fhir/StructureDefinition/data-absent-reason').exists()"
// Address.country must be ISO 3166-1 alpha-2, not alpha-3 and not a country name. Stated as an
// invariant IN ADDITION to the required binding on address.country, because the observed
// production bug is specifically a 3-letter code ("CAN") reaching consumers that then do
// positional string surgery on it -- and a demoted binding violation reads as generic
// "not in value set" noise, whereas this fires with the actual diagnosis. Warning severity:
// the binding is what enforces; this is what explains.
Invariant: patient-address-country-alpha2
Description: "Address.country SHOULD be an ISO 3166-1 alpha-2 code ('CA'), not alpha-3 ('CAN') and not a country name ('Canada'). Alpha-3 is an edge/submission form only and must not be stored."
Severity: #warning
Expression: "country.empty() or country.matches('^[A-Za-z]{2}$')"
// Indigenous identity: the not-answered values (NullFlavor/DataAbsentReason members of the mirror VS)
// are mutually exclusive with any named selection and with each other. Root-level because a
// per-instance constraint cannot see its sibling instances. Warning severity: the app never
// produces a violation; external data that does still ingests, flagged.
Invariant: patient-indigenous-identity-absence-exclusive
Description: "If a not-answered value (Asked But Declined / Do not know/ Not applicable) is recorded, it SHOULD be the only instance of the extension."
Severity: #warning
Expression: "extension.where(url = 'http://fhir.infoway-inforoute.ca/ca-ext/StructureDefinition/patient-indigenousIdentity').value.ofType(CodeableConcept).coding.where(system = 'http://terminology.hl7.org/CodeSystem/v3-NullFlavor' or system = 'http://terminology.hl7.org/CodeSystem/data-absent-reason').exists() implies extension.where(url = 'http://fhir.infoway-inforoute.ca/ca-ext/StructureDefinition/patient-indigenousIdentity').count() = 1"
// Indigenous identity: one coding per answer instance (the app writes exactly one; sibling codings would
// be translations, which this field does not use) and no duplicate answers. system|code pairs,
// not bare codes — bare codes false-positive across systems.
Invariant: patient-indigenous-identity-instances-clean
Description: "Each instance SHOULD carry exactly one coding, and no two instances SHOULD repeat the same system|code answer."
Severity: #warning
Expression: "extension.where(url = 'http://fhir.infoway-inforoute.ca/ca-ext/StructureDefinition/patient-indigenousIdentity').value.ofType(CodeableConcept).all(coding.count() = 1) and extension.where(url = 'http://fhir.infoway-inforoute.ca/ca-ext/StructureDefinition/patient-indigenousIdentity').value.ofType(CodeableConcept).coding.select(system & '|' & code).isDistinct()"
// Indian registration number: 9 or 10 digits when present. The value may legitimately be
// absent (data-absent-reason) while other card facets — validity period, card-type extension —
// are recorded, so the rule binds only a carried value. Warning severity: the app rejects a
// malformed number at entry; external data that carries one still ingests, flagged.
Invariant: patient-indian-registration-number-format
Description: "An Indian registration number identifier's value, when carried, SHOULD be 9 or 10 digits."
Severity: #warning
Expression: "identifier.where(system = 'https://fhir.infoway-inforoute.ca/NamingSystem/ca-indigenous-northern-affairs-number').all(value.empty() or value.matches('^[0-9]{9,10}$'))"
// Racialized group: the not-answered values (NullFlavor/DataAbsentReason members of the mirror VS)
// are mutually exclusive with any named selection and with each other. Root-level because a
// per-instance constraint cannot see its sibling instances. Warning severity: the app never
// produces a violation; external data that does still ingests, flagged.
Invariant: patient-racialized-group-absence-exclusive
Description: "If a not-answered value (Asked But Declined / Do not know / Another race category) is recorded, it SHOULD be the only instance of the extension."
Severity: #warning
Expression: "extension.where(url = 'http://fhir.infoway-inforoute.ca/ca-ext/StructureDefinition/patient-racializedGroup').value.ofType(CodeableConcept).coding.where(system = 'http://terminology.hl7.org/CodeSystem/v3-NullFlavor' or system = 'http://terminology.hl7.org/CodeSystem/data-absent-reason').exists() implies extension.where(url = 'http://fhir.infoway-inforoute.ca/ca-ext/StructureDefinition/patient-racializedGroup').count() = 1"
// Racialized group: one coding per answer instance (the app writes exactly one; sibling codings would
// be translations, which this field does not use) and no duplicate answers. system|code pairs,
// not bare codes — bare codes false-positive across systems.
Invariant: patient-racialized-group-instances-clean
Description: "Each instance SHOULD carry exactly one coding, and no two instances SHOULD repeat the same system|code answer."
Severity: #warning
Expression: "extension.where(url = 'http://fhir.infoway-inforoute.ca/ca-ext/StructureDefinition/patient-racializedGroup').value.ofType(CodeableConcept).all(coding.count() = 1) and extension.where(url = 'http://fhir.infoway-inforoute.ca/ca-ext/StructureDefinition/patient-racializedGroup').value.ofType(CodeableConcept).coding.select(system & '|' & code).isDistinct()"
// Profile Definition
Profile: NexusEmrCorePatient
Parent: Patient
Id: nexus-emr-core-patient
Title: "Patient Profile"
Description: "Generic Nexus EMR core profile for patient demographic and administrative information, based on CA Core+. Designed to support a broad range of integration needs."
* ^url = $NexusEmrCorePatient
* ^status = #draft
* obeys patient-name-family-or-given
* obeys patient-indigenous-identity-absence-exclusive
* obeys patient-indigenous-identity-instances-clean
* obeys patient-racialized-group-absence-exclusive
* obeys patient-racialized-group-instances-clean
* obeys patient-indian-registration-number-format
// THE effective date for this profile (see the EffectiveDate / NoEffectiveDate
// RuleSets): one date concept per resource, declared here rather than guessed.
* insert NoEffectiveDate
// Salient fields — the reader/render surface (the AI chart + the facade's salient accessors).
// The primary name is name.first() (faithful to the retired renderer, robust to names with no use).
* insert Salient(given_name, [[name.first().given.first()]], string)
* insert Salient(middle_name, [[name.first().given.tail().join(' ')]], string)
* insert Salient(family_name, [[name.first().family]], string)
* insert Salient(gender, gender, code)
* insert Salient(birth_date, birthDate, dateTime)
* insert Salient(phone, [[telecom.where(system='phone').first().value]], string)
* insert Salient(email, [[telecom.where(system='email').first().value]], string)
// SMART on FHIR exposure -- read and write, but NOT delete. An app may maintain demographics;
// deleting the patient destroys the compartment root every other resource hangs off, which is a
// data-loss event rather than a workflow step.
* insert SmartExposedNoDelete(patient)
* . ^short = "Nexus EMR Patient Core"
* . ^definition = """
Represents patient demographic and administrative information, capturing key details
like identifiers, name, gender, birth date, and contact information using standard
Nexus EMR references. Uses instance-specific identifiers for tracking patients
across systems.
"""
* meta.source 0..1 MS
* meta.source ^short = "Identifies EMR instance & pipeline version the resource came from"
* meta.source ^definition = "A URI that identifies the EMR pipeline and version from which this resource originated. This tells you which EMR instance (recommend using the instance identifier), and the version of the pipeline code/transformations."
// optional id
* id 0..1 MS
* id ^short = "Logical id of this artifact"
* id ^definition = "The logical id of the resource, used to reference this Patient resource in transactions and other operations. Assigned by the server and should not be changed by clients."
// Identifier requirements with slicing
* identifier 0..* MS
* identifier.system 1..1
* identifier.value 1..1
* identifier ^short = "Patient identifiers (Nexus EMR ID recommended)"
* identifier ^definition = """
Patient identifiers. Identifier is optional (0..*); patient identity rests on the FHIR logical `id`
plus any business identifiers present. Types include:
- **Recommended**: a Nexus EMR system identifier
- **Optional**: Provincial/Territorial Health Numbers (PHN/JHN)
- **Optional**: Indian Act registration / status number, under the Canadian URI Registry system
`https://fhir.infoway-inforoute.ca/NamingSystem/ca-indigenous-northern-affairs-number` —
validity period carries the card's issue/expiry, the IndianStatusCardTypeExtension the
presented document form, and a data-absent-reason on `value` stands in while card facets are
recorded before the number
- **Optional**: Raw codes from source EMR systems
- **Optional**: Medical Record Numbers (MRN), other local identifiers
A source system's own identifier SHOULD be carried alongside this one, flagged `use = secondary`. See [Carrying the raw code](../guide/patterns/raw-codes.md).
"""
// Identifier slicing setup.
// Discriminate the jurisdictional slice by `type` only: the slice fixes
// type.coding.code = #JHN but fixes no `system` pattern (system is bound to a
// ValueSet, which a #pattern discriminator cannot evaluate), so a `system`
// discriminator left slice membership ambiguous. Type alone is unambiguous.
* identifier ^slicing.discriminator[0].type = #pattern
* identifier ^slicing.discriminator[0].path = "type"
* identifier ^slicing.rules = #open
* identifier ^slicing.description = "Slice identifiers by type"
// -----------------------------------------
// Reason for no health card number -- ADOPTED FROM ONTARIO (CA-eReC), not minted here
// -----------------------------------------
// Why this element exists: Ontario eReferral requires that Patient.identifier either carry the
// health card number OR a documented reason for its absence. This is the "or" half.
//
// Why Ontario's extension rather than a Nexus one: a Nexus-canonical equivalent was drafted and
// then withdrawn in favour of this. CA-eReC is one of the specs the future Ontario "Union IG"
// consolidates, and adopting the jurisdictional canonical now makes later conformance a
// re-badging exercise instead of a remodelling one. Same principle as the health-card version
// code, which uses the CA Baseline canonical rather than a local invention.
//
// ⚠ NOTE THE CONTEXT. Ontario declares this extension on `Patient.identifier`, not on Patient,
// so it rides ON an identifier rather than standing alone. When the patient genuinely has no
// health card there is no jurisdictional identifier to host it, so it is carried on another
// identifier the patient does have (in practice the MRN slice). That is Ontario's implicit model
// and the reason this is on the base `identifier` element rather than on `identifier[jurisdictional]`.
//
// ⚠ ITS VALUE SET IS THINNER THAN THE OPERATIONAL NEED. Ontario's value set is ONE clinical code
// (`no-health-card-number-reason#no-HC`, "does not have a health card") plus two data-absent-reason
// codes (`not-asked`, `asked-declined`). It cannot distinguish newborn-awaiting-registration from
// refugee-on-IFHP from card-not-presented -- all of which drive different front-desk action. The
// binding is EXTENSIBLE, so a clinic-specific reason can be carried as an additional coding
// alongside the Ontario code; do that rather than reintroducing a competing local extension.
* identifier.extension contains
$CAeReCReasonForNoHCN named reasonForNoHCN 0..1 MS
* identifier.extension[reasonForNoHCN] ^short = "Why the patient has no health card number (Ontario CA-eReC extension)"
* identifier.extension[reasonForNoHCN] ^definition = """
The documented reason the patient has no provincial/territorial health card number, using
Ontario's CA-eReC `reason-for-no-HCN` extension.
Ontario eReferral requires `Patient.identifier` to carry either the health card number or a
documented reason for its absence; this is that reason. Present at most once per patient.
Because the extension's declared context is `Patient.identifier`, it is carried on an identifier
the patient DOES have (typically the MRN slice) when no jurisdictional health number exists --
there is no jurisdictional identifier repeat to attach it to in exactly the case it describes.
SHOULD be absent when `identifier[jurisdictional]` is populated: carrying both is contradictory,
and a reader encountering both should trust the identifier. It also says nothing about WHICH
jurisdiction is missing -- a patient with an Alberta PHN and no Ontario one is an out-of-province
patient, not a patient without a health card.
"""
* identifier.extension[reasonForNoHCN] ^comment = "Binding is extensible and Ontario's value set carries only one clinical code (#no-HC) plus data-absent-reason #not-asked / #asked-declined. Where a more specific local reason matters (newborn awaiting registration, refugee/IFHP, card not presented), carry it as an ADDITIONAL coding beside the Ontario code -- do not mint a competing extension."
* identifier contains
jurisdictional 0..* MS and
mrn 0..1 MS
// Jurisdictional identifier slice (optional, multiple allowed for multi-provincial patients)
* identifier[jurisdictional] ^short = "Provincial/Territorial Health Number"
* identifier[jurisdictional] ^definition = "Provincial or Territorial Health Number (PHN/JHN) from Canadian jurisdictions"
* identifier[jurisdictional].type 1..1
// patternCodeableConcept ON type -- required for the `type` pattern-discriminator to be
// evaluable (fixing type.coding.* alone is not visible to the slice matcher).
* identifier[jurisdictional].type = http://terminology.hl7.org/CodeSystem/v2-0203#JHN "Jurisdictional health number"
* identifier[jurisdictional].type ^short = "Type or jurisdiction of the health card, which drives format validation"
* identifier[jurisdictional].type ^comment = "Slice membership is discriminated by `type`, so a jurisdictional PHN MUST carry the v2-0203 #JHN type coding. Ingest/edge transforms should stamp this type on every provincial/territorial health number; a PHN with only system+value falls outside this slice and its constraints (system binding, healthCardVersion) do not apply."
* identifier[jurisdictional].type.coding 1..1
* identifier[jurisdictional].type.coding.system = "http://terminology.hl7.org/CodeSystem/v2-0203" (exactly)
* identifier[jurisdictional].type.coding.code = #JHN
* identifier[jurisdictional].type.coding.display = "Jurisdictional health number"
* identifier[jurisdictional].system 1..1
* identifier[jurisdictional].system from $CanadianPHNSystems (required)
* identifier[jurisdictional].system ^short = "PHN system from Canadian PHN systems ValueSet"
* identifier[jurisdictional].system ^definition = "Must be one of the defined Canadian PHN system URIs (e.g., ca-ab-patient-healthcare-id for Alberta)"
* identifier[jurisdictional].value 1..1
* identifier[jurisdictional].value ^short = "The actual PHN value"
* identifier[jurisdictional].use = #official
// Health-card version code (e.g. Ontario/Quebec version letters). Internal representation is
// the CA Baseline ext-identifierversion (valueString), chosen for cross-jurisdiction support
// (QC as well as ON). Ontario's ehealthontario.ca ext-id-health-card-version-code is applied as
// an edge transform on Ontario submission, not stored internally.
* identifier[jurisdictional].extension contains
$CAIdentifierVersion named healthCardVersion 0..1 MS
* identifier[jurisdictional].extension[healthCardVersion] ^short = "Health card version code (CA Baseline ext-identifierversion)"
// MRN / Chart Number slice (identifier type MR, per HL7 v2-0203): the patient's
// local Medical Record Number / chart number from the source EMR.
* identifier[mrn] ^short = "Medical Record Number (chart number)"
* identifier[mrn] ^definition = "The patient's local Medical Record Number / chart number from the source EMR."
* identifier[mrn].type 1..1
// patternCodeableConcept ON type -- see jurisdictional slice above.
* identifier[mrn].type = http://terminology.hl7.org/CodeSystem/v2-0203#MR "Medical record number"
* identifier[mrn].type.coding 1..1
* identifier[mrn].type.coding.system = "http://terminology.hl7.org/CodeSystem/v2-0203" (exactly)
* identifier[mrn].type.coding.code = #MR
* identifier[mrn].type.coding.display = "Medical record number"
* identifier[mrn].system 1..1
* identifier[mrn].system = "../NamingSystem/patient-mrn" (exactly)
* identifier[mrn].value 1..1
// Narrative element with human-readable summary
* text 0..1
* text ^short = "Narrative"
* text ^definition = "A human-readable narrative summary of the resource for human interpretation."
// Active Status: Whether this patient's record is in active use
* active 0..1 MS
* active ^short = "Whether this patient record is in active use"
* active ^definition = "Indicates whether this patient record is in active use. Many systems use this property to mark as non-active patients who have not been seen for a period of time, died, or left the practice. When absent, the patient record is assumed to be active."
// At least one name, Family/Given required.
* name 1..* MS
* name ^short = "A name associated with the patient"
* name ^definition = "The patient's name(s). At least one name must be provided with either a family or given name. Multiple names can be recorded to capture legal names, preferred names, previous names, and aliases."
* name.family 0..1 MS
* name.family ^short = "Family name (often called 'Surname')"
* name.family ^definition = "The part of a name that links to the genealogy. In some cultures (e.g. Eritrea) the family name of a child is the given name of their father."
* name.given 0..* MS
* name.given ^short = "Given names (not always 'first')"
* name.given ^definition = "Given name(s) including first name, middle names, initials. The first given name is typically used as the primary given name."
* name.prefix 0..* MS
* name.prefix ^short = "Honorific prefix(es) (e.g. Dr., Mr., Ms.)"
* name.prefix ^definition = "Part of the name that is acquired as a title due to academic, legal, employment, or nobility status, etc., and that appears at the start of the name (standard R4 HumanName.prefix)."
* name.suffix 0..* MS
* name.suffix ^short = "Name suffix(es) (e.g. Jr., Sr.)"
* name.suffix ^definition = "Part of the name that is acquired as a title due to academic, legal, employment, or nobility status, etc., and that appears at the end of the name (standard R4 HumanName.suffix)."
// Telecom
* telecom 0..* MS
* telecom ^short = "Contact details for the patient"
* telecom ^definition = "Contact details (telephone, email, etc.) for the patient. May include phone numbers, email addresses, and other electronic communication methods. Systems should capture the use (home, work, mobile) and preference ranking when available."
// Gender identity + pronouns (HL7 R4 extensions), birth sex (CA Baseline).
* extension contains
http://hl7.org/fhir/StructureDefinition/individual-genderIdentity named genderIdentity 0..1 MS and
http://hl7.org/fhir/StructureDefinition/individual-pronouns named pronouns 0..* MS and
$CABirthSex named birthSex 0..1 MS and
PatientRecordInactiveReasonExtension named recordInactiveReason 0..1 and
$CaIndigenousIdentity named indigenousIdentity 0..* MS and
$CaRacializedGroup named racializedGroup 0..* MS and
IndianActRegistrationStatusExtension named indianActRegistrationStatus 0..1 and
FirstNationRegistryGroupExtension named firstNationRegistryGroup 0..1 and
FirstNationCommunityExtension named firstNationCommunity 0..1 and
http://hl7.org/fhir/StructureDefinition/patient-interpreterRequired named interpreterRequired 0..1 MS
// Wired in explicitly (2026-08-06): the extension existed as declared intent (heritage
// record-status migration) but no profile referenced it. Not must-support — nothing
// consumes it yet; the slice makes the intent legible instead of dangling.
* extension[interpreterRequired] ^short = "Whether this patient needs an interpreter"
* extension[interpreterRequired] ^definition = """
Whether an interpreter is required to communicate with the patient. HL7's own extension, adopted
here rather than restated: this specification declares no address of its own for it.
Read `Patient.communication` for WHICH language; this element says only that help is needed.
"""
* extension[interpreterRequired] ^comment = """
**Absence means NOT RECORDED, not "no interpreter needed".** Producers in this estate write the
extension only when the answer is true, so a patient with no extension is one nobody was asked
about. Treating absence as a negative answer asserts a determination that was never made -- and the
population it would silently mislabel is exactly the one the field exists to find.
"""
* extension[recordInactiveReason] ^short = "Why a heritage patient record is inactive"
* extension[indigenousIdentity] ^short = "The Indigenous identity a patient self-identifies with"
* extension[indigenousIdentity] ^comment = "Repeatable, with one answer per instance. Declining to answer must be non-blocking."
* extension[indigenousIdentity] ^definition = "Self-identified Indigenous identity, carried on CA Core+ patient-indigenousIdentity (reused, not minted — review finding 2026-08-13; the extension is draft in the 1.2.0-dft-ballot package, pinned in the IG dependencies). Repetition is one answer per extension INSTANCE. UNLIKE genderIdentity/pronouns, the absence concepts are first-class VALUES of this field per the source composition — readers must not filter the NullFlavor/DataAbsentReason systems out. MS: reporting-bound (ON RFP 3.2.1.16). WHEN AGGREGATING, absence concepts are not identities: exclude by SYSTEM (`v3-NullFlavor`, `data-absent-reason`), never by a hand-copied code list -- that is the same test the absence-exclusive invariant uses, so the rule stays true if the source composition adds a concept. Displaying them and counting them are different jobs: a decline is a real recorded answer to show, and not a group to tally."
// The CA Core+ extension binds its Infoway ValueSet at `preferred`; the slice tightens to
// the pinned enumerated mirror at `extensible` so membership cannot drift under us.
* extension[indigenousIdentity].valueCodeableConcept from NexusEmrIndigenousIdentityVS (extensible)
* extension[racializedGroup] ^short = "The racialized group a patient self-identifies with"
* extension[racializedGroup] ^comment = "Repeatable, with one answer per instance."
* extension[racializedGroup] ^definition = "Self-identified racialized group, carried on CA Core+ patient-racializedGroup (reused, not minted; NOT ethnicity — CA Baseline ext-ethnicity is a distinct concept). Repetition is one answer per extension INSTANCE. Absence concepts are first-class VALUES per the source composition — readers must not filter the NullFlavor/DataAbsentReason systems out. MS: reporting-bound (ON RFP 3.2.1.16). WHEN AGGREGATING, absence concepts are not identities: exclude by SYSTEM (`v3-NullFlavor`, `data-absent-reason`), never by a hand-copied code list -- that is the same test the absence-exclusive invariant uses, so the rule stays true if the source composition adds a concept. Displaying them and counting them are different jobs: a decline is a real recorded answer to show, and not a group to tally."
* extension[racializedGroup].valueCodeableConcept from NexusEmrRacializedGroupVS (extensible)
// The Indigenous profile fields: registration status and First Nation affiliation as flat
// extensions, and the status card as the Indian registration number IDENTIFIER (system
// https://fhir.infoway-inforoute.ca/NamingSystem/ca-indigenous-northern-affairs-number, the
// Canadian URI Registry's official URI; issue/expiry as the identifier's validity period; the
// presented document form as the IndianStatusCardTypeExtension riding on it). Every field is
// independently recordable — the app enforces no cross-field dependency, a recorded expiry never
// implies a registration-status change, and a card recorded before its number carries a
// data-absent-reason on the identifier's required value element. They record what the patient or
// their documentation STATES; nothing here is verified against the Indian Register. Not
// must-support: the surface is an opt-in clinic feature, not reporting-bound.
* extension[indianActRegistrationStatus] ^short = "Indian Act registration status, as stated"
* extension[firstNationRegistryGroup] ^short = "Registry group (band) the registration is held under"
* extension[firstNationCommunity] ^short = "First Nation community the patient affiliates with"
* extension[genderIdentity] ^short = "Gender identity (distinct from administrative gender)"
* extension[genderIdentity] ^definition = "The patient's gender identity, distinct from administrative gender and birth sex. Uses the HL7 individual-genderIdentity extension."
// Bound locally because the inherited binding does not describe the data. The HL7 extension
// binds hl7.org/fhir/ValueSet/gender-identity (preferred), and this EMR's roster is BROADER:
// 10 of its 13 concepts are Canada Health Infoway namespace (pan-Canadian GSSO) and are not
// members of that value set. Leaving it unbound put the real terminology governance in
// frontend TypeScript, on a must-support element.
//
// ⚠ NOTE THE PATH. individual-genderIdentity is a COMPLEX extension: the concept lives in a
// `value` sub-extension (1..1, CodeableConcept), alongside optional `period` and `comment`
// sub-extensions, and `Extension.value[x]` on the root is 0..0. Binding
// `extension[genderIdentity].value[x]` is therefore invalid, not merely unidiomatic -- SUSHI
// rejects it. The frontend reads the complex form correctly (with a legacy simple-form
// fallback for pre-R5 data), so the code was right and only the IG statement was missing.
* extension[genderIdentity].extension[value].value[x] from NexusEmrGenderIdentityVS (required)
* extension[genderIdentity] ^comment = "For 'asked but declined' or 'unknown', place a data-absent-reason extension on the value element -- those are absence reasons, not gender identities, and are deliberately not members of NexusEmrGenderIdentityVS. Same rule as pronouns below."
* extension[pronouns] ^short = "Personal pronouns"
* extension[pronouns] ^definition = "The patient's personal pronouns. Uses the HL7 individual-pronouns extension."
// Extensible, not required, unlike gender identity: the HL7 extension's own binding is only an
// EXAMPLE binding, pronoun sets are open-ended in a way the gender-identity concept list is
// not, and a patient supplying a set outside the LOINC answer list should be recordable.
// Same complex-extension shape as genderIdentity above -- the concept is in the `value`
// sub-extension, not on the extension root.
* extension[pronouns].extension[value].value[x] from NexusEmrPronounsVS (extensible)
* extension[pronouns] ^comment = "For 'asked but declined', place a data-absent-reason extension on the value element rather than using a data-absent-reason coding as the pronoun value."
* extension[birthSex] ^short = "Sex assigned at birth (CA Baseline)"
* extension[birthSex] ^definition = "Sex assigned at birth, distinct from administrative gender and gender identity. Uses the CA Baseline ext-patientbirthsex extension -- a known Canadian must-support."
// Gender: Administrative gender
// if missing, unknown (but know this can cause problems for patient matching)
* gender 0..1 MS
* gender ^short = "Administrative gender (male | female | other | unknown)"
* gender ^definition = "Administrative gender used for identification and administrative purposes. When absent, gender is unknown. Note that this may cause challenges with patient matching algorithms. This is distinct from birth sex and clinical sex parameters which may be captured in extensions."
* gender ^binding.strength = #required
* gender ^binding.valueSet = "http://hl7.org/fhir/ValueSet/administrative-gender"
// Birth Date - if missing, unknown (but know this can cause problems for patient matching)
* birthDate 0..1 MS
* birthDate ^short = "The date of birth for the patient"
* birthDate ^definition = "The date of birth for the patient. When absent, birth date is unknown. Note that missing birth dates may impact patient matching accuracy and age-based clinical decision support. Partial dates (year or year-month only) are permitted when full date is unknown."
// Deceased Status - if missing, assumed to be alive.
* deceased[x] 0..1 MS
* deceased[x] ^short = "Indicates if the patient is deceased"
* deceased[x] ^definition = "Indicates if the patient is deceased or not. Can be a boolean (true if deceased) or the actual date/time of death. When absent, the patient is assumed to be alive. Systems should update this field promptly when death information is received."
// Address
* address 0..* MS
* address ^short = "Addresses for the patient"
* address ^definition = "Patient addresses including home, temporary, billing addresses. Should follow Canadian address formatting standards including valid province codes and postal codes in A1A 1A1 format. Multiple addresses can be recorded with different uses (home, work, temp, billing)."
* address.postalCode 0..1 MS
* address.postalCode ^definition = "The postal code for the patient's address. Should be in A1A 1A1 format."
// state / country -- bound, because the previous "valid province codes" prose above was not
// computable and the divergence it permitted is already realized in production data. Both
// elements are primitive `string` in R4, which IS bindable.
//
// The IG's internal representation is the BARE two-letter subdivision code for state
// (`ON`, `QC`) and ISO 3166-1 **alpha-2** for country (`CA`, `US`). That is what the address
// UI writes, and what Ontario PCR eligibility is gated on. ISO **alpha-3** ("CAN") is an edge
// form only: convert on submission, do not store it -- the same posture this profile already
// takes on Ontario's health-card-version code.
//
// ⚠ Do not cross the two axes: `CA` means Canada on `country` and California on `state`.
* address.state 0..1 MS
* address.state from NexusEmrProvinceStateVS (required)
* address.state ^short = "Province / territory / state (bare two-letter subdivision code)"
* address.state ^definition = """
The province, territory or state, as a bare two-letter subdivision code (ISO 3166-2 suffix form,
which matches the Canada Post / USPS abbreviation): `ON`, not `CA-ON`, and not `Ontario`.
**The binding is `required`, which is a deliberate departure from this IG's usual lenient posture.**
The reason is that the alternative is silent corruption rather than a clean failure: consumers gate
jurisdictional behaviour on this value (Ontario PCR eligibility is gated on `ON`) and do positional
string surgery on it, which has produced malformed outbound jurisdictional HL7 in production. A
closed roster makes that fail loudly instead.
**⚠ HERITAGE EMR SITUATION -- UNRESOLVED as of IG 1.12.0.** This binding was validated against the
IG's own example instances and the synthetic corpus only. **Heritage-EMR data has NOT been
surveyed.** Heritage EMRs store province as an unconstrained free-text column typed by front-desk
staff over decades, with no validation at entry -- the same systems demonstrably emit uncoded
free-text on adjacent fields (the enrolment/roster status is carried as an uncoded `valueString`).
So spelled-out province names ("Ontario"), lowercase codes, and trailing whitespace are all
plausible in existing records.
Where they exist, a `required` binding **rejects real patients** rather than merely flagging them --
strictly worse than the outbound corruption it was added to prevent. The distribution has not been
measured, so this is an open risk, not a closed decision.
Implementers migrating a heritage corpus SHOULD profile their actual `state` value distribution
before enforcing, and MAY treat this binding as `extensible` during migration, relying on the
`patient-address-country-alpha2` warning invariant and an equivalent state-shape check instead.
Normalizing at ingest (uppercase, trim, map full names to codes) is the intended long-term fix.
This caveat comes out once the heritage-data survey is done and the decision is recorded.
"""
* address.state ^comment = "Consumers gate jurisdictional behaviour on this value (e.g. Ontario PCR eligibility on `ON`), so a spelled-out province name is not merely untidy -- it silently fails those gates. See the definition for the unresolved legacy-data migration caveat on the `required` binding."
* address.country 0..1 MS
* address.country from http://hl7.org/fhir/ValueSet/iso3166-1-2 (required)
* address.country ^short = "Country as ISO 3166-1 alpha-2 (`CA`, `US`)"
* address.country ^definition = """
The country, as an ISO 3166-1 alpha-2 code. R4's element comment permits 3-letter codes generally;
this profile pins alpha-2 as the stored form to end the alpha-2/alpha-3 split that has produced
malformed outbound values (`"Ca-io"` and `"CANio"` have both been observed on outbound
jurisdictional HL7, from consumers doing positional string surgery on inconsistent input).
Alpha-3 (`CAN`) and country names (`Canada`) are NOT conformant. Where a downstream interface
requires alpha-3, convert at that edge -- the same posture this profile takes on the Ontario
health-card version code.
**⚠ HERITAGE EMR SITUATION -- UNRESOLVED as of IG 1.12.0.** As with `address.state`, this `required`
binding was validated against the IG's own examples and the synthetic corpus only. **Heritage-EMR
data has NOT been surveyed**, and alpha-3 (`CAN`) or spelled-out country values (`Canada`) are
plausible in existing records -- one in-repo converter already writes alpha-3, which is how the
malformed outbound values above were produced in the first place, so the inconsistency demonstrably
exists inside our own stack and not only upstream of it.
Survey the corpus before enforcing, and treat the binding as `extensible` during migration if it is
dirty -- the `patient-address-country-alpha2` invariant (`#warning`) still reports the specific
alpha-3 case either way. Normalizing at ingest is the intended long-term fix. This caveat comes out
once the heritage-data survey is done and the decision is recorded.
"""
* address.country ^comment = "Alpha-3 ('CAN') and country names ('Canada') are NOT conformant here. Where a downstream interface requires alpha-3, convert at that edge. See the definition for the unresolved legacy-data migration caveat on the `required` binding."
// Both bindings are `required` deliberately, against this IG's usually-lenient posture: the
// realized failure mode is downstream consumers doing positional string surgery on
// inconsistent values and emitting corrupt jurisdictional HL7 ("Ca-io", "CANio"). A closed
// roster is the only thing that makes that fail loudly instead of silently. The invariant
// below names the specific observed bug, so the diagnostic stays legible even where a
// validator is run lenient and binding violations are demoted to warnings.
* address obeys patient-address-country-alpha2
// Marital Status
* maritalStatus 0..1 MS
* maritalStatus ^short = "Marital status with raw code support"
* maritalStatus ^definition = """
The patient's most recent marital (civil) status.
RECOMMENDED: Standard terminology codes (v3 Marital Status) when available
OPTIONAL: Raw EMR codes for source system preservation
Used for administrative and billing purposes, may impact insurance coverage and next-of-kin determinations.
"""
// Multiple Birth
* multipleBirth[x] 0..1
* multipleBirth[x] ^short = "Whether patient is part of multiple birth"
* multipleBirth[x] ^definition = "Indicates whether the patient is part of a multiple birth (twins, triplets, etc.) and if so, the birth order. Can be boolean (true if multiple birth) or integer (birth order: 1, 2, 3, etc.). Important for pediatric care and family medical history."
// Communication: for language preference.
* communication 0..* MS
* communication ^short = "Language communication capabilities"
* communication ^definition = "Languages which may be used to communicate with the patient about their health. Important for ensuring effective communication, interpreter services, and meeting Canadian bilingual healthcare requirements."
* communication.language 1..1 MS
* communication.language ^short = "Language with raw code support"
* communication.language ^definition = """
The language for communication with the patient.
RECOMMENDED: ISO 639 language codes (e.g., en, fr) or BCP 47 codes
OPTIONAL: Raw EMR codes for source system preservation
Should include relevant languages such as English (en), French (fr), and indigenous languages.
The preferred language should be marked with communication.preferred = true.
"""
// General Practitioner: Reference PractitionerRole.
* generalPractitioner 0..* MS
* generalPractitioner only Reference(NexusEmrCorePractitionerRole or NexusEmrCorePractitioner)
* generalPractitioner ^short = "Patient's primary care provider(s)"
* generalPractitioner ^definition = "Reference to the patient's primary care provider(s), typically their family physician or primary care team. References a Practitioner -- the primary provider identity in this IG -- or a PractitionerRole where the role/organization binding is part of the assertion. Multiple providers can be listed for team-based care models."
// Primary Care Attachment: the patient's formal roster/enrollment state, distinct
// from generalPractitioner (local clinical responsibility / MRP). The enrolled
// provider may be local or remote. See the rostering design doc.
* extension contains NexusEmrPrimaryCareAttachment named primaryCareAttachment 0..1 MS
* extension[primaryCareAttachment] ^short = "Current primary-care roster/enrollment/attachment"
// (Reason for no health card is modelled on Patient.identifier, using Ontario's CA-eReC
// extension -- see the identifier section above.)
// Managing Organization: Reference Organization.
* managingOrganization 1..1
* managingOrganization only Reference(NexusEmrCoreOrganization)
* managingOrganization ^short = "Organization that maintains the patient record"
* managingOrganization ^definition = "The organization that has primary responsibility for maintaining this patient record in their EMR system. This is typically the primary care clinic or practice where the patient is rostered. Required to establish data ownership and governance."
// Alias for the Canadian PHN Systems ValueSet
Alias: $CanadianPHNSystems = ../ValueSet/canadian-phn-systems
// CodeSystem for Canadian Provincial Health Number Systems URIs
// Defines the standard naming systems for provincial and territorial health numbers across Canada
CodeSystem: CanadianPHNSystemsCS
Id: canadian-phn-systems
Title: "Canadian Provincial Health Number Systems CodeSystem"
Description: "URIs for provincial and territorial health number naming systems across Canada"
* ^status = #active
* ^experimental = false
* ^caseSensitive = true
* ^content = #complete
* ^url = "../CodeSystem/canadian-phn-systems"
// Codes ARE the full Infoway PHN system URIs. `Patient.identifier[jurisdictional].system`
// is a uri element bound (required) to this ValueSet; the code a uri binding matches is the
// literal uri string, so the codes must be the full URLs that real instances carry -- not the
// short slugs, which no conformant instance ever puts in `system`.
* #"https://fhir.infoway-inforoute.ca/NamingSystem/ca-ab-patient-healthcare-id" "Alberta Health Care Number"
* #"https://fhir.infoway-inforoute.ca/NamingSystem/ca-bc-patient-healthcare-id" "British Columbia PHN"
* #"https://fhir.infoway-inforoute.ca/NamingSystem/ca-mb-patient-healthcare-id" "Manitoba Health Registration Number"
* #"https://fhir.infoway-inforoute.ca/NamingSystem/ca-nb-patient-healthcare-id" "New Brunswick Medicare Number"
* #"https://fhir.infoway-inforoute.ca/NamingSystem/ca-nl-patient-healthcare-id" "Newfoundland and Labrador MCP Number"
* #"https://fhir.infoway-inforoute.ca/NamingSystem/ca-ns-patient-healthcare-id" "Nova Scotia Health Card Number"
* #"https://fhir.infoway-inforoute.ca/NamingSystem/ca-nt-patient-healthcare-id" "Northwest Territories Health Care Number"
* #"https://fhir.infoway-inforoute.ca/NamingSystem/ca-nu-patient-healthcare-id" "Nunavut Health Care Number"
* #"https://fhir.infoway-inforoute.ca/NamingSystem/ca-on-patient-hcn" "Ontario Health Card Number"
* #"https://fhir.infoway-inforoute.ca/NamingSystem/ca-pe-patient-healthcare-id" "Prince Edward Island Health Number"
* #"https://fhir.infoway-inforoute.ca/NamingSystem/ca-qc-patient-healthcare-id" "Quebec Health Insurance Number"
* #"https://fhir.infoway-inforoute.ca/NamingSystem/ca-sk-patient-healthcare-id" "Saskatchewan Health Services Number"
* #"https://fhir.infoway-inforoute.ca/NamingSystem/ca-yt-patient-healthcare-id" "Yukon Health Care Number"
ValueSet: CanadianPHNSystems
Id: canadian-phn-systems
Title: "Canadian Provincial Health Number Systems"
Description: "Naming systems for provincial and territorial health numbers across Canada, based on Infoway standards"
* ^status = #active
* ^experimental = false
* ^jurisdiction = urn:iso:std:iso:3166#CA "Canada"
* ^url = "../ValueSet/canadian-phn-systems"
* include codes from system ../CodeSystem/canadian-phn-systems