MedicationStatement
→
drug_exposure
implemented
primary
Patient self-reported medication events. Each MedicationStatement produces one drug_exposure row with drug_type_concept_id defaulting to 44787730 (Patient Self-Reported Medication). This is the only FHIR medication resource with a normative HL7 IG FML mapping. Dynamic type concept adjustment is possible based on basedOn/partOf references (omoponfhir behavior).
Conversion profile
omop-medication-statement-drug-exposure
A FHIR instance converts to drug_exposure iff it validates against this profile.
| Path | Card | Type | Binding / Fixed | Comment |
|---|---|---|---|---|
| MedicationStatement.status | 1..*MS | fhir/medication-statement-statusrequired | ||
| MedicationStatement.medication[x] | 1..*MS | CodeableConcept | omop-drug-codesrequired | |
| MedicationStatement.subject | 1..1 | Reference | Required for drug_exposure.person_id. | |
| MedicationStatement.effective[x] | 1..1MS | Required for drug_exposure.drug_exposure_start_date / _end_date. May be dateTime or Period. |
ViewDefinition (Stage 1 flattener)
omop-medicationstatement-drug-exposure
20 columns · resource MedicationStatement
| column name | FHIRPath | type |
|---|---|---|
| id | MedicationStatement.id | id |
| drug_rxnorm | MedicationStatement.medicationCodeableConcept.coding.where(system='http://www.nlm.nih.gov/research/umls/rxnorm').first().code | code |
| drug_ndc | MedicationStatement.medicationCodeableConcept.coding.where(system='http://hl7.org/fhir/sid/ndc').first().code | code |
| drug_atc | MedicationStatement.medicationCodeableConcept.coding.where(system='http://www.whocc.no/atc').first().code | code |
| drug_snomed | MedicationStatement.medicationCodeableConcept.coding.where(system='http://snomed.info/sct').first().code | code |
| drug_text | MedicationStatement.medicationCodeableConcept.text | string |
| subject_id | MedicationStatement.subject | Reference(Patient) |
| drug_exposure_start_date | MedicationStatement.effectiveDateTime | dateTime |
| drug_exposure_start_datetime | MedicationStatement.effectiveDateTime | dateTime |
| drug_exposure_end_date | MedicationStatement.effectivePeriod.end | dateTime |
| drug_exposure_end_datetime | MedicationStatement.effectivePeriod.end | dateTime |
| verbatim_end_date | MedicationStatement.effectivePeriod.end | dateTime |
| stop_reason | MedicationStatement.statusReason | CodeableConcept |
| quantity | MedicationStatement.dosage[].doseAndRate[].doseQuantity.value | decimal |
| sig | MedicationStatement.dosage[].text | string |
| route | MedicationStatement.dosage[].route | CodeableConcept |
| route_text | MedicationStatement.dosage[].route.text | string |
| performer_id | MedicationStatement.informationSource | Reference(Practitioner) |
| encounter_id | MedicationStatement.context | Reference(Encounter) |
| dose_unit_text | MedicationStatement.dosage[].doseAndRate[].doseQuantity.unit | string |
Implementation:
src/mapper/medication-statement.tsFields (23)
-
drug_exposure_id← — integer · idPKrequiredSurrogate key. HL7 IG FML leaves this as a commented-out TODO. omoponfhir uses IdMapping.getOMOPfromFHIR().2 sources ▾
-
HL7 IG FML (normative) leaves drug_exposure_id mapping as a commented-out TODO — no implementation provided
- fhir-omop-ig(fml) refs/refs/fhir-omop-ig/input/maps/medication.fml:11-16 — person_id and drug_exposure_id commented out as TODO
-
omoponfhir uses a bidirectional IdMapping registry (getOMOPfromFHIR) to assign the surrogate key
- omoponfhir(java) refs/refs/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopMedicationStatement.java:657-660 — constructOmop() — IdMapping.getOMOPfromFHIR() at entry
-
-
person_id←MedicationStatement.subjectinteger · Reference(Patient)FK→PERSONrequiredResolve Patient reference. HL7 IG FML leaves this as a commented-out TODO. -
drug_concept_id←MedicationStatement.medication[x]integer · CodeableConcept/ReferenceFK→CONCEPTrequiredRxNorm / ATC / NDC mapped to OMOP standard concept. HL7 IG FML maps from medication.concept.coding.code. Placeholder: 0 when concept lookup unavailable. -
drug_exposure_start_date←MedicationStatement.effectiveDateTime | MedicationStatement.effectivePeriod.start | MedicationStatement.dateAsserteddate · dateTimerequiredtransform:effectiveDateTime -> effectivePeriod.start -> dateAsserted (tertiary fallback)Tertiary fallback chain unique to MedicationStatement. omoponfhir falls back to new Date() instead of dateAsserted.2 sources ▾
-
HL7 IG FML maps from effective[x] (DateTime or Period.start); no tertiary fallback defined
- fhir-omop-ig(fml) refs/refs/fhir-omop-ig/input/maps/medication.fml:24-26 — drug_exposure_start_date from effective[x]
-
This project falls back to dateAsserted as a tertiary source when no effective[x] is present
- omoponfhir(java) refs/refs/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopMedicationStatement.java:808-834 — Effective date handling (DateTime vs Period); falls back to new Date() rather than dateAsserted
-
-
drug_exposure_start_datetime←MedicationStatement.effectiveDateTime | MedicationStatement.effectivePeriod.start | MedicationStatement.dateAsserteddatetime · dateTimeFull ISO datetime. -
drug_exposure_end_date←MedicationStatement.effectivePeriod.enddate · dateTimerequiredtransform:effectivePeriod.end; fallback to start_date when effectiveDateTime is used (point in time) or period.end is absentWhen effectiveDateTime is used, end = start. When effectivePeriod has start but no end, end = start. -
drug_exposure_end_datetime←MedicationStatement.effectivePeriod.enddatetime · dateTimeFull ISO datetime. Null if no period end. -
verbatim_end_date←MedicationStatement.effectivePeriod.enddate · dateTimeRaw end date from source before any inference. HL7 IG FML maps effectivePeriod.end here. Most other implementations leave null.2 sources ▾
-
HL7 IG FML (normative) explicitly maps effectivePeriod.end to verbatim_end_date — MedicationStatement is the only medication resource where this field is populated
- fhir-omop-ig(fml) refs/refs/fhir-omop-ig/input/maps/medication.fml:31-32 — verbatim_end_date from effectivePeriod.end
-
omoponfhir and ETL-German leave verbatim_end_date null for MedicationStatement
- omoponfhir(java) refs/refs/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopMedicationStatement.java:808-834 — Effective date handling — verbatim_end_date not set
-
-
drug_type_concept_id← constant integerFK→CONCEPTrequiredmap:drug_type=44787730Default 44787730 (Patient Self-Reported Medication). omoponfhir dynamically adjusts based on basedOn/partOf references. ETL-German uses 32817 (CLAIM). HL7 IG FML derives from category.coding.code.3 sources ▾
-
omoponfhir dynamically adjusts the type concept based on basedOn/partOf references: MedicationRequest=38000177, MedicationAdministration=38000179, MedicationDispense=38000175
- omoponfhir(java) refs/refs/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopMedicationStatement.java:933-958 — Dynamic type concept from basedOn/partOf
-
HL7 IG FML derives drug_type_concept_id from category.coding.code rather than using a static constant
- fhir-omop-ig(fml) refs/refs/fhir-omop-ig/input/maps/medication.fml:34-38 — drug_type_concept_id from category.coding.code
-
ETL-German uses 32817 (CLAIM) statically for all medication resources
- ETL-German-FHIR-Core(java) refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/MedicationStatementMapper.java:600-600 — CONCEPT_CLAIM (32817) type concept
-
-
stop_reason←MedicationStatement.statusReasonvarchar(20) · CodeableConceptReason for discontinuation. omoponfhir truncates to 20 chars and only populates when status=stopped. HL7 IG FML maps from reason.concept.coding.code.2 sources ▾
-
omoponfhir reads statusReason only when status=stopped, then truncates the text to 20 chars to fit varchar(20)
- omoponfhir(java) refs/refs/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopMedicationStatement.java:711-748 — stop_reason from statusReason, truncated to 20 chars
-
HL7 IG FML maps stop_reason from reason.concept.coding.code — a different FHIR path than statusReason
- fhir-omop-ig(fml) refs/refs/fhir-omop-ig/input/maps/medication.fml:39-45 — stop_reason from reason.concept.coding.code
-
-
refills← — integerNot applicable to MedicationStatement (MedicationRequest-only). Leave null. -
quantity←MedicationStatement.dosage[].doseAndRate[].doseQuantity.valuefloat · decimalDose amount per administration. ETL-German computes mean of Range when doseRange is used.2 sources ▾
-
ETL-German computes arithmetic mean of doseRange.low and doseRange.high when a Range is used; creates one drug_exposure per dosage entry (fan-out)
- ETL-German-FHIR-Core(java) refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/MedicationStatementMapper.java:622-634 — Quantity from doseQuantity or Range mean
-
omoponfhir reads doseQuantity.value from the first dosage entry, no Range handling
- omoponfhir(java) refs/refs/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopMedicationStatement.java:885-931 — Dosage (quantity, unit, route)
-
-
days_supply← — integerNot directly available. Could be computed from period length / dosing frequency, but no reference implementation does this for MedicationStatement. -
sig←MedicationStatement.dosage[].text | MedicationStatement.dosage[].patientInstructionvarchar(MAX) · stringFree-text dosage instructions. -
route_concept_id←MedicationStatement.dosage[].routeinteger · CodeableConceptFK→CONCEPTSNOMED route code mapped to OMOP Route domain concept. omoponfhir performs full vocabulary lookup. ETL-German uses date-aware concept lookup. This project hardcodes null.2 sources ▾
-
omoponfhir performs a full vocabulary lookup via CodeableConceptUtil.getOmopConceptWithFhirConcept() against the OMOP concept table
- omoponfhir(java) refs/refs/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopMedicationStatement.java:885-931 — Dosage quantity, unit, route handling
-
ETL-German uses date-aware concept lookup (findOmopConcepts.getConcepts()) for SNOMED route codes
- ETL-German-FHIR-Core(java) refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/MedicationStatementMapper.java:610-621 — Route concept lookup with date-aware validity checking
-
-
route_source_value←MedicationStatement.dosage[].route.text | MedicationStatement.dosage[].route.coding[0].displayvarchar(50) · stringRaw route text. omoponfhir prefers route.text, falls back to coding[0].display. -
lot_number← — varchar(50)Vaccine-only field; not applicable to MedicationStatement. -
provider_id←MedicationStatement.informationSourceinteger · Reference(Practitioner)FK→PROVIDERReporter of the medication statement. omoponfhir filters to Practitioner-typed references only. ETL-German does not map provider for MedicationStatement.2 sources ▾
-
omoponfhir reads informationSource and filters to Practitioner-typed references only before resolving to provider_id
- omoponfhir(java) refs/refs/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopMedicationStatement.java:837-858 — Information source to provider, Practitioner filter
-
ETL-German does not map provider_id for MedicationStatement at all
- ETL-German-FHIR-Core(java) refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/MedicationStatementMapper.java:574-637 — setUpDrugExposure() — no provider_id mapping
-
-
visit_occurrence_id←MedicationStatement.contextinteger · Reference(Encounter)FK→VISIT_OCCURRENCEEncounter reference. R4 uses 'context'; renamed to 'encounter' in R5. -
visit_detail_id← — integerFK→VISIT_DETAILNot mapped. Null in all reviewed implementations. -
drug_source_value←MedicationStatement.medication[x].coding[best].codevarchar(50) · codeBest code by vocabulary priority. omoponfhir uses identifier.value as source value. ETL-German uses ATC code.2 sources ▾
-
omoponfhir uses MedicationStatement.identifier.value (business identifier) as the source value and truncates to 50 chars
- omoponfhir(java) refs/refs/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopMedicationStatement.java:751-805 — Medication resolution including source value extraction
-
ETL-German specifically uses ATC code as the source value for European data
- ETL-German-FHIR-Core(java) refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/MedicationMapper.java — 274 lines — ATC code extraction used as drug_source_value
-
-
drug_source_concept_id← constant integerFK→CONCEPT=0Source vocabulary concept. Placeholder: 0. -
dose_unit_source_value←MedicationStatement.dosage[].doseAndRate[].doseQuantity.unit | MedicationStatement.dosage[].doseAndRate[].doseQuantity.codevarchar(50) · stringRaw dose unit string. omoponfhir prefers .code, falls back to .unit.
Vocabularies
drug_type
| Source | Display | Concept ID | Concept Name |
|---|---|---|---|
| (default) | No basedOn / partOf reference | 44787730 | Patient Self-Reported Medication |
| basedOn -> MedicationRequest | basedOn references MedicationRequest | 38000177 | Prescription written |
| partOf -> MedicationAdministration | partOf references MedicationAdministration | 38000179 | Physician administered drug |
| partOf -> MedicationDispense | partOf references MedicationDispense | 38000175 | Prescription dispensed in pharmacy |
Edge Cases
status = entered-in-error
Skip -- do not create drug_exposure row. Consensus across all implementations.
status = stopped
omoponfhir populates stop_reason from statusReason and still creates the row. This project skips.
status = on-hold / intended / not-taken / unknown
This project skips. omoponfhir maps all statuses except entered-in-error.
effectiveDateTime (point in time)
Start and end date are the same. omoponfhir: setDrugExposureStartDate(date); setDrugExposureEndDate(date).
effectivePeriod with start but no end
End date = start date.
dateAsserted but no effective[x]
This project falls back to dateAsserted. omoponfhir sets new Date() (current time) if no date found.
basedOn references MedicationRequest
omoponfhir dynamically sets drug_type_concept_id to 38000177 (Prescription written). This project leaves at 44787730.
partOf references MedicationAdministration / MedicationDispense
omoponfhir dynamically sets drug_type_concept_id to 38000179 / 38000175 respectively. This project does not implement.
medicationReference to contained Medication
omoponfhir iterates contained[]. ETL-German pre-indexes via medication_id_map. This project skips (returns null).
Multiple dosage entries
This project takes first. ETL-German creates one drug_exposure per dosage entry (fan-out).
Multiple codings in medication[x]
This project uses selectBestCoding() by vocabulary priority. omoponfhir uses CodeableConceptUtil.searchConcept().
drug_source_value exceeds 50 chars
omoponfhir truncates medicationCodeableConcept.getText() to 50 chars. OMOP CDM field is varchar(50).
stop_reason exceeds 20 chars
omoponfhir truncates to 20. OMOP CDM field is varchar(20).
Reference Implementations
- fhir-omop-ig(fml) refs/refs/fhir-omop-ig/input/maps/medication.fml — 46 lines. Only normative mapping for any FHIR medication resource. Covers MedicationStatement only.
- omoponfhir(java) refs/refs/omoponfhir-omopv5-r4-mapping/src/main/java/edu/gatech/chai/omoponfhir/omopv5/r4/mapping/OmopMedicationStatement.java:1-983 — Most complete: RxNorm concept lookup, contained reference resolution, route concept lookup, dosage parsing, dynamic type concept from basedOn/partOf, stop_reason truncation to 20 chars.
- ETL-German-FHIR-Core(java) refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/MedicationStatementMapper.java:1-965 — ATC vocabulary, dose form and route mapping, Range mean quantity, fan-out per dosage entry. Type concept: 32817 (CLAIM).