SMART PH4H
0.9.9 - release

This page is part of the PH4H (v0.9.9: Releases Draft) based on FHIR (HL7® FHIR® Standard) R4. This is the current published version in its permanent home (it will always be available at this URL). For a full list of available versions, see the Directory of published versions

StructureMap: MedicationOverviewLMToMedicationOverviewBundle

Official URL: http://smart.who.int/ph4h/StructureMap/MedicationOverviewLMToMedicationOverviewBundle Version: 0.9.9
Draft as of 2026-07-09 Computable Name: MedicationOverviewLMToMedicationOverviewBundle

map "http://smart.who.int/ph4h/StructureMap/MedicationOverviewLMToMedicationOverviewBundle" = "MedicationOverviewLMToMedicationOverviewBundle"

uses "https://profiles.ihe.net/PHARM/MEOW/StructureDefinition/MedicationOverviewLM" alias MedicationOverviewLM as source
uses "https://profiles.ihe.net/PHARM/MEOW/StructureDefinition/PatientLM" alias PatientLM as source
uses "https://profiles.ihe.net/PHARM/MEOW/StructureDefinition/MedicationTreatmentLineLM" alias MedicationTreatmentLineLM as source
uses "https://profiles.ihe.net/PHARM/MEOW/StructureDefinition/PractitionerLM" alias PractitionerLM as source
uses "https://profiles.ihe.net/PHARM/MEOW/StructureDefinition/MedicationOverview" alias MedicationOverviewBundle as target
uses "https://profiles.ihe.net/PHARM/MEOW/StructureDefinition/MedicationOverviewComposition" alias MedicationOverviewComposition as target
uses "https://profiles.ihe.net/PHARM/MEOW/StructureDefinition/MedicationTreatmentLine" alias MedicationTreatmentLine as target
uses "http://hl7.org/fhir/StructureDefinition/Patient" alias Patient as target
uses "http://hl7.org/fhir/StructureDefinition/Practitioner" alias Practitioner as target

// ---------------------------------------------------------------------------
// Top-level group: assemble MedicationOverview Bundle (document)
// ---------------------------------------------------------------------------
group MedicationOverviewLMToMedicationOverviewBundle(source src : MedicationOverviewLM, target tgt : MedicationOverviewBundle) {
  src -> tgt.type = 'document' "bundleType";
  src -> tgt.timestamp = (now()) "bundleTimestamp";
  // Generate fixed UUIDs once; reuse them across entries and Reference targets.
  src ->  uuid() as bid,  uuid() as cid,  uuid() as pid then {
    // Bundle.identifier (UUID identifier per RFC 4122)
    src ->  tgt.identifier as id,  id.system = 'urn:ietf:rfc:4122',  id.value = bid "bundleIdentifier";
    // Composition entry. The Composition group ALSO emits the treatment-line bundle
    // entries (passing tgt in), so each section.entry reference can share the same
    // UUID as its bundle entry — making the lines reachable from the Composition.
    src ->  tgt.entry as ce,  ce.fullUrl = append('urn:uuid:', cid),  ce.resource = create('MedicationOverviewComposition') as comp then {
      src then MedicationOverviewLMToComposition(src, comp, pid, tgt) "compositionContent";
    } "compositionEntry";
    // Patient entry
    src.patient as pref ->  tgt.entry as pe,  pe.fullUrl = append('urn:uuid:', pid),  pe.resource = create('Patient') as pat then {
      pref then PatientLMToPatient(pref, pat) "patientContent";
    } "patientEntry";
  } "setEntries";
}

// ---------------------------------------------------------------------------
// MedicationOverviewLM -> MedicationOverviewComposition
// Receives the Patient entry's UUID (pid) so subject can reference it.
// ---------------------------------------------------------------------------
group MedicationOverviewLMToComposition(source src : MedicationOverviewLM, target tgt : MedicationOverviewComposition, source pid, target bundle : MedicationOverviewBundle) {
  src -> tgt.status = 'final' "compStatus";
  // Composition.type — LOINC 56445-0 "Medication summary Document"
  src ->  create('Coding') as cc,  cc.system = 'http://loinc.org',  cc.code = '56445-0',  cc.display = 'Medication summary Document',  create('CodeableConcept') as ct,  ct.coding = cc,  tgt.type = ct "compType";
  // Composition.subject -> Patient entry by uuid
  src ->  create('Reference') as subj,  subj.reference = append('urn:uuid:', pid),  tgt.subject = subj "compSubject";
  src -> tgt.date = (now()) "compDate";
  src -> tgt.title = 'Medication Overview' "compTitle";
  // Composition.author is 1..* (required). The Min source carries no author, so
  // emit a display-only placeholder Reference to satisfy the cardinality.
  // TODO: replace with the real authoring Organization/Device once the source
  // (or a config value) provides it.
  src ->  create('Reference') as au,  au.display = 'Medication Overview generator',  tgt.author = au "compAuthor";
  // Section: MedRecordTreatmentLine
  src -> tgt.section as sect then {
    src -> sect.title = 'Medication Treatment Lines' "sectTitle";
    src ->  create('Coding') as scc,  scc.system = 'http://loinc.org',  scc.code = '10160-0',  scc.display = 'History of Medication use Narrative',  create('CodeableConcept') as sc,  sc.coding = scc,  sect.code = sc "sectCode";
    // One UUID per line, shared by the section.entry reference AND the bundle entry,
    // so each treatment line is reachable by forward traversal from the Composition
    // (mirrors the ICVP ICVPLMToIPS pattern). This is why the group receives the
    // Bundle: the line entries are created here, not in the top-level group.
    src.medicationTreatmentLine as lref -> uuid() as tid then {
      lref ->  sect.entry as se,  se.reference = append('urn:uuid:', tid) "sectEntry";
      lref ->  bundle.entry as te,  te.fullUrl = append('urn:uuid:', tid),  te.resource = create('MedicationTreatmentLine') as ms then {
        lref then MedicationTreatmentLineLMToMedicationStatement(lref, ms, pid) "lineContent";
      } "lineEntry";
    } "treatmentLines";
  } "sectionTreatmentLines";
  // Attester from verification BackboneElement
  src.verification as v -> tgt.attester as att then {
    v -> att.mode = 'professional' "attMode";
    v.verificationTime as vt -> att.time = vt "attTime";
    // practicioner is Reference(PractitionerLM) in the LM — copy the reference
    // as-is ((pr.name.text) was invalid: References have no name).
    v.practicioner as pr -> att.party = pr "attParty";
  } "attester";
  // Comment -> R4 Composition has NO `note` element (R5-only). Use the
  // cross-version extension, mirroring the r4 profile's approach for adherence.
  src.comment as cm ->  create('Extension') as ext,  ext.url = 'http://hl7.org/fhir/5.0/StructureDefinition/extension-Composition.note',  create('Annotation') as nt then {
    cm.commentText as ctxt -> nt.text = ctxt "noteText";
    cm.date as d -> nt.time = d "noteTime";
    // author is a Reference in the LM; Annotation.author[x] resolves by type.
    cm.author as a -> nt.author = a "noteAuthor";
    cm ->  ext.value = nt,  tgt.extension = ext "noteAssign";
  } "noteFromComment";
}

// ---------------------------------------------------------------------------
// PatientLM -> FHIR Patient
// ---------------------------------------------------------------------------
group PatientLMToPatient(source src : PatientLM, target tgt : Patient) {
  src.identifier as id -> tgt.identifier = id "patIdentifier";
  src.name as n -> tgt.name = n "patName";
  // gender: navigate instead of a FHIRPath evaluate — validator can't scope
  // rule variables inside (…) expressions.
  src.gender as g then {
    g.coding as gc then {
      gc.code as gcode -> tgt.gender = gcode "patGenderCode";
    } "patGenderCoding";
  } "patGender";
  // dateOfBirth is dateTime in LM; birthDate is date — silent truncation expected.
  src.dateOfBirth as d -> tgt.birthDate = d "patBirthDate";
}

// ---------------------------------------------------------------------------
// MedicationTreatmentLineLM -> MedicationTreatmentLine (profile on MedicationStatement, R4)
// Receives the Patient entry's UUID (pid) so MS.subject can reference it.
// ---------------------------------------------------------------------------
group MedicationTreatmentLineLMToMedicationStatement(source src : MedicationTreatmentLineLM, target tgt : MedicationTreatmentLine, source pid) {
  src.identifier as id -> tgt.identifier = id;
  // subject -> Patient entry by uuid (MS.subject is 1..1)
  src ->  create('Reference') as subj,  subj.reference = append('urn:uuid:', pid),  tgt.subject = subj "subject";
  src.status as st -> tgt.status = st;
  // medication[x] choice -> R4 medication[x] (no CodeableReference in R4;
  // the engine resolves medicationReference/medicationCodeableConcept by value type)
  src.medication : Reference as mr -> tgt.medication = mr "medicationRef";
  src.medication : CodeableConcept as mc -> tgt.medication = mc "medicationCC";
  // effective[x] on R4 MedicationStatement — address the choice by base name.
  src.effectivePeriod as ep -> tgt.effective = ep;
  src.recordingMetadata as rm then {
    rm.recordedTime as rt -> tgt.dateAsserted = rt "dateAsserted";
    rm.recorder as rc -> tgt.informationSource = rc "informationSource";
  };
  // reason -> R4 reasonCode (0..* CodeableConcept; no CodeableReference in R4)
  src.indication as ind -> tgt.reasonCode = ind "reasonIndication";
  src.indicationText as it ->  create('CodeableConcept') as cc,  cc.text = it,  tgt.reasonCode = cc "reasonIndicationText";
  // intendedUse: no R4 MedicationStatement element and no extension slice in the
  // r4 MEOW profile — intentionally not mapped (stays LM-only).
  src.usageInstructions as ui -> tgt.dosage = ui "dosage";
  src.preparationInstructions as pi ->  create('Dosage') as dos,  dos.text = pi,  tgt.dosage = dos "prepInstructions";
  // note = comment
  src.comment as cm -> create('Annotation') as nt then {
    cm.commentText as ct -> nt.text = ct "lineNoteText";
    cm.date as d -> nt.time = d "lineNoteTime";
    cm.author as a -> nt.author = a "lineNoteAuthor";
    cm -> tgt.note = nt "lineNoteAssign";
  } "lineNote";
  // adherence -> R4 has no MS.adherence; the r4 profile slices the R5
  // cross-version extension (1..1 in the profile, sub-extensions code/reason).
  src.treatmentStatus as ts ->  create('Extension') as ext,  ext.url = 'http://hl7.org/fhir/5.0/StructureDefinition/extension-MedicationStatement.adherence',  create('Extension') as e1,  e1.url = 'code',  e1.value = ts,  ext.extension = e1,  tgt.extension = ext "extAdherence";
  src.treatmentStatusReasonCode as tsr ->  create('Extension') as ext,  ext.url = 'http://hl7.org/fhir/5.0/StructureDefinition/extension-MedicationStatement.adherence',  create('Extension') as e1,  e1.url = 'reason',  e1.value = tsr,  ext.extension = e1,  tgt.extension = ext "extAdherenceReason";
  src.category as cat -> tgt.category = cat;
  src.derivedFrom as df then {
    df.request as rq -> tgt.derivedFrom = rq "derivedFromRequest";
    df.statement as stm -> tgt.derivedFrom = stm "derivedFromStatement";
    df.dispensation as dsp -> tgt.derivedFrom = dsp "derivedFromDispensation";
    df.administration as adm -> tgt.derivedFrom = adm "derivedFromAdministration";
    df.other as oth -> tgt.derivedFrom = oth "derivedFromOther";
  };
  // Extension: artifact-version
  src.version as v ->  create('Extension') as ext,  ext.url = 'http://hl7.org/fhir/StructureDefinition/artifact-version',  ext.value = v,  tgt.extension = ext "extVersion";
  // Extension: VerificationInformation
  src.verificationInformation as vi -> create('Extension') as ext then {
    vi -> ext.url = 'https://profiles.ihe.net/PHARM/MEOW/StructureDefinition/ihe-ext-medicationstatement-verificationinformation' "viUrl";
    vi.verifier as vr ->  create('Extension') as e1,  e1.url = 'verifier',  e1.value = vr,  ext.extension = e1 "viVerifier";
    vi.verificationTime as vt ->  create('Extension') as e2,  e2.url = 'verificationTime',  e2.value = vt,  ext.extension = e2 "viTime";
    vi -> tgt.extension = ext "viAssign";
  } "extVerification";
  // Extension: Substitution
  src.substitution as sub -> create('Extension') as ext then {
    sub -> ext.url = 'https://profiles.ihe.net/PHARM/MEOW/StructureDefinition/ihe-ext-medicationstatement-substitution' "subUrl";
    sub.substitutionType as t ->  create('Extension') as e1,  e1.url = 'substitutionType',  e1.value = t,  ext.extension = e1 "subType";
    sub.substitutionTypeReason as r ->  create('Extension') as e2,  e2.url = 'substitutionTypeReason',  e2.value = r,  ext.extension = e2 "subReason";
    sub.substitutionTypeReasonText as rt ->  create('Extension') as e3,  e3.url = 'substitutionTypeReasonText',  e3.value = rt,  ext.extension = e3 "subReasonText";
    sub -> tgt.extension = ext "subAssign";
  } "extSubstitution";
}

// ---------------------------------------------------------------------------
// PractitionerLM -> FHIR Practitioner
// ---------------------------------------------------------------------------
group PractitionerLMToPractitioner(source src : PractitionerLM, target tgt : Practitioner) {
  src.identifier as id -> tgt.identifier = id "practIdentifier";
  src.name as n -> tgt.name = n "practName";
}