Current section

Files

Jump to
hl7v2 lib hl7v2 message_definition.ex
Raw

lib/hl7v2/message_definition.ex

defmodule HL7v2.MessageDefinition do
@moduledoc """
Canonical message structure mappings and validation dispatch.
Maps HL7v2 trigger events to their canonical message structures (e.g.,
ADT^A04 → ADT_A01) and dispatches structural validation to
`HL7v2.Standard.MessageStructure` and `HL7v2.Validation.Structural`.
"""
# HL7 v2.5.1 canonical message structure map.
# Many trigger events share the same abstract message definition.
# If a {code, event} pair is not listed, the structure defaults to "CODE_EVENT".
@canonical_structures %{
# ADT aliases
{"ADT", "A04"} => "ADT_A01",
{"ADT", "A08"} => "ADT_A01",
{"ADT", "A13"} => "ADT_A01",
{"ADT", "A05"} => "ADT_A05",
{"ADT", "A14"} => "ADT_A05",
{"ADT", "A28"} => "ADT_A05",
{"ADT", "A31"} => "ADT_A05",
{"ADT", "A06"} => "ADT_A06",
{"ADT", "A07"} => "ADT_A06",
{"ADT", "A09"} => "ADT_A09",
{"ADT", "A10"} => "ADT_A09",
{"ADT", "A11"} => "ADT_A09",
{"ADT", "A12"} => "ADT_A12",
{"ADT", "A15"} => "ADT_A15",
{"ADT", "A16"} => "ADT_A16",
{"ADT", "A17"} => "ADT_A17",
{"ADT", "A19"} => "ADR_A19",
{"ADT", "A20"} => "ADT_A20",
{"ADT", "A21"} => "ADT_A21",
{"ADT", "A22"} => "ADT_A21",
{"ADT", "A23"} => "ADT_A21",
{"ADT", "A25"} => "ADT_A21",
{"ADT", "A26"} => "ADT_A21",
{"ADT", "A27"} => "ADT_A21",
{"ADT", "A29"} => "ADT_A21",
{"ADT", "A32"} => "ADT_A21",
{"ADT", "A33"} => "ADT_A21",
{"ADT", "A56"} => "ADT_A21",
{"ADT", "A60"} => "ADT_A21",
{"ADT", "A62"} => "ADT_A21",
{"ADT", "A24"} => "ADT_A24",
{"ADT", "A30"} => "ADT_A30",
{"ADT", "A34"} => "ADT_A30",
{"ADT", "A35"} => "ADT_A30",
{"ADT", "A36"} => "ADT_A30",
{"ADT", "A37"} => "ADT_A37",
{"ADT", "A38"} => "ADT_A38",
{"ADT", "A39"} => "ADT_A39",
{"ADT", "A40"} => "ADT_A39",
{"ADT", "A41"} => "ADT_A39",
{"ADT", "A42"} => "ADT_A39",
{"ADT", "A57"} => "ADT_A39",
{"ADT", "A43"} => "ADT_A43",
{"ADT", "A44"} => "ADT_A43",
{"ADT", "A45"} => "ADT_A45",
{"ADT", "A46"} => "ADT_A30",
{"ADT", "A47"} => "ADT_A30",
{"ADT", "A48"} => "ADT_A30",
{"ADT", "A49"} => "ADT_A30",
{"ADT", "A50"} => "ADT_A50",
{"ADT", "A51"} => "ADT_A50",
{"ADT", "A52"} => "ADT_A50",
{"ADT", "A53"} => "ADT_A50",
{"ADT", "A54"} => "ADT_A50",
{"ADT", "A55"} => "ADT_A50",
# BAR aliases
{"BAR", "P01"} => "BAR_P01",
{"BAR", "P02"} => "BAR_P02",
{"BAR", "P05"} => "BAR_P05",
{"BAR", "P06"} => "BAR_P06",
{"BAR", "P10"} => "BAR_P10",
{"BAR", "P12"} => "BAR_P12",
# Blood Bank
{"BPS", "O29"} => "BPS_O29",
{"BRP", "O30"} => "BRP_O30",
{"BRT", "O32"} => "BRT_O32",
{"BTS", "O31"} => "BTS_O31",
# Clinical Study — CRM C01-C08 share CRM_C01, CSU C09-C12 share CSU_C09
{"CRM", "C01"} => "CRM_C01",
{"CRM", "C02"} => "CRM_C01",
{"CRM", "C03"} => "CRM_C01",
{"CRM", "C04"} => "CRM_C01",
{"CRM", "C05"} => "CRM_C01",
{"CRM", "C06"} => "CRM_C01",
{"CRM", "C07"} => "CRM_C01",
{"CRM", "C08"} => "CRM_C01",
{"CSU", "C09"} => "CSU_C09",
{"CSU", "C10"} => "CSU_C09",
{"CSU", "C11"} => "CSU_C09",
{"CSU", "C12"} => "CSU_C09",
# DFT
{"DFT", "P03"} => "DFT_P03",
{"DFT", "P11"} => "DFT_P11",
# DOC
{"DOC", "T12"} => "DOC_T12",
# Equipment
{"EAC", "U07"} => "EAC_U07",
{"EAR", "U08"} => "EAR_U08",
{"ESR", "U02"} => "ESR_U02",
{"ESU", "U01"} => "ESU_U01",
{"INR", "U06"} => "INR_U06",
{"INU", "U05"} => "INU_U05",
{"LSU", "U12"} => "LSU_U12",
{"SSR", "U04"} => "SSR_U04",
{"SSU", "U03"} => "SSU_U03",
{"TCU", "U10"} => "TCU_U10",
# MDM
{"MDM", "T01"} => "MDM_T02",
{"MDM", "T02"} => "MDM_T02",
# Master File — MFN M03-M13 (not M02, M05 which have own) share MFN_M01
{"MFK", "M01"} => "MFK_M01",
{"MFK", "M02"} => "MFK_M01",
{"MFK", "M03"} => "MFK_M01",
{"MFK", "M04"} => "MFK_M01",
{"MFK", "M05"} => "MFK_M01",
{"MFK", "M06"} => "MFK_M01",
{"MFK", "M07"} => "MFK_M01",
{"MFK", "M08"} => "MFK_M01",
{"MFK", "M09"} => "MFK_M01",
{"MFK", "M10"} => "MFK_M01",
{"MFK", "M11"} => "MFK_M01",
{"MFK", "M12"} => "MFK_M01",
{"MFK", "M13"} => "MFK_M01",
{"MFN", "M01"} => "MFN_M01",
{"MFN", "M02"} => "MFN_M02",
{"MFN", "M03"} => "MFN_M01",
{"MFN", "M04"} => "MFN_M01",
{"MFN", "M05"} => "MFN_M05",
{"MFN", "M06"} => "MFN_M01",
{"MFN", "M07"} => "MFN_M01",
{"MFN", "M08"} => "MFN_M01",
{"MFN", "M09"} => "MFN_M01",
{"MFN", "M10"} => "MFN_M01",
{"MFN", "M11"} => "MFN_M01",
{"MFN", "M12"} => "MFN_M01",
{"MFN", "M13"} => "MFN_M01",
{"MFQ", "M01"} => "MFQ_M01",
# Network management
{"NMD", "N02"} => "NMD_N02",
{"NMQ", "N01"} => "NMQ_N01",
{"NMR", "N01"} => "NMR_N01",
# Orders
{"OMD", "O03"} => "OMD_O03",
{"OMG", "O19"} => "OMG_O19",
{"OML", "O21"} => "OML_O21",
{"OMP", "O09"} => "OMP_O09",
{"OMS", "O05"} => "OMS_O05",
{"ORA", "R33"} => "ORA_R33",
{"ORG", "O20"} => "ORG_O20",
{"ORL", "O22"} => "ORL_O22",
{"ORM", "O01"} => "ORM_O01",
{"ORP", "O10"} => "ORP_O10",
{"ORR", "O02"} => "ORR_O02",
{"ORU", "R30"} => "ORU_R30",
{"ORU", "R31"} => "ORU_R30",
{"OUL", "R21"} => "OUL_R21",
# Pathway/Problem/Goal — PPR PC1-PC3, PGL PC6-PC8, PPP PCB-PCD, PPG PCG-PCJ
{"PGL", "PC6"} => "PGL_PC6",
{"PGL", "PC7"} => "PGL_PC6",
{"PGL", "PC8"} => "PGL_PC6",
{"PPG", "PCG"} => "PPG_PCG",
{"PPG", "PCH"} => "PPG_PCG",
{"PPG", "PCJ"} => "PPG_PCG",
{"PPP", "PCB"} => "PPP_PCB",
{"PPP", "PCC"} => "PPP_PCB",
{"PPP", "PCD"} => "PPP_PCB",
{"PPR", "PC1"} => "PPR_PC1",
{"PPR", "PC2"} => "PPR_PC1",
{"PPR", "PC3"} => "PPR_PC1",
{"PPT", "PCL"} => "PPT_PCL",
# Product Experience
{"PEX", "P07"} => "PEX_P07",
{"PEX", "P08"} => "PEX_P07",
{"SUR", "P09"} => "SUR_P09",
# Personnel — PMU B01-B04 (B03 is delete, B01/B02/B04 share PMU_B01)
{"PMU", "B01"} => "PMU_B01",
{"PMU", "B02"} => "PMU_B01",
{"PMU", "B03"} => "PMU_B03",
{"PMU", "B04"} => "PMU_B01",
# Query
{"QBP", "Q21"} => "QBP_Q21",
{"QBP", "Q22"} => "QBP_Q21",
{"QBP", "Q23"} => "QBP_Q21",
{"QBP", "Q24"} => "QBP_Q21",
{"QBP", "Q25"} => "QBP_Q21",
{"QCK", "Q02"} => "QCK_Q02",
{"QCN", "J01"} => "QCN_J01",
{"RDY", "K15"} => "RDY_K15",
{"RSP", "K21"} => "RSP_K21",
{"RSP", "K22"} => "RSP_K21",
{"RSP", "K23"} => "RSP_K21",
{"RSP", "K24"} => "RSP_K21",
{"RSP", "K25"} => "RSP_K21",
{"RTB", "K13"} => "RTB_K13",
# Pharmacy
{"RAS", "O17"} => "RAS_O17",
{"RDE", "O11"} => "RDE_O11",
{"RDS", "O13"} => "RDS_O13",
{"RGV", "O15"} => "RGV_O15",
{"RRA", "O18"} => "RRA_O18",
{"RRD", "O14"} => "RRD_O14",
{"RRE", "O12"} => "RRE_O12",
{"RRG", "O16"} => "RRG_O16",
# Referral — REF/RRI I12-I15 share the same structures
{"REF", "I12"} => "REF_I12",
{"REF", "I13"} => "REF_I12",
{"REF", "I14"} => "REF_I12",
{"REF", "I15"} => "REF_I12",
{"RRI", "I12"} => "RRI_I12",
{"RRI", "I13"} => "RRI_I12",
{"RRI", "I14"} => "RRI_I12",
{"RRI", "I15"} => "RRI_I12",
# SIU S12-S26 (except S25) share SIU_S12
{"SIU", "S12"} => "SIU_S12",
{"SIU", "S13"} => "SIU_S12",
{"SIU", "S14"} => "SIU_S12",
{"SIU", "S15"} => "SIU_S12",
{"SIU", "S16"} => "SIU_S12",
{"SIU", "S17"} => "SIU_S12",
{"SIU", "S18"} => "SIU_S12",
{"SIU", "S19"} => "SIU_S12",
{"SIU", "S20"} => "SIU_S12",
{"SIU", "S21"} => "SIU_S12",
{"SIU", "S22"} => "SIU_S12",
{"SIU", "S23"} => "SIU_S12",
{"SIU", "S24"} => "SIU_S12",
{"SIU", "S26"} => "SIU_S12",
# SRM S01-S11 share SRM_S01, SRR S01-S11 share SRR_S01
{"SRM", "S01"} => "SRM_S01",
{"SRM", "S02"} => "SRM_S01",
{"SRM", "S03"} => "SRM_S01",
{"SRM", "S04"} => "SRM_S01",
{"SRM", "S05"} => "SRM_S01",
{"SRM", "S06"} => "SRM_S01",
{"SRM", "S07"} => "SRM_S01",
{"SRM", "S08"} => "SRM_S01",
{"SRM", "S09"} => "SRM_S01",
{"SRM", "S10"} => "SRM_S01",
{"SRM", "S11"} => "SRM_S01",
{"SRR", "S01"} => "SRR_S01",
{"SRR", "S02"} => "SRR_S01",
{"SRR", "S03"} => "SRR_S01",
{"SRR", "S04"} => "SRR_S01",
{"SRR", "S05"} => "SRR_S01",
{"SRR", "S06"} => "SRR_S01",
{"SRR", "S07"} => "SRR_S01",
{"SRR", "S08"} => "SRR_S01",
{"SRR", "S09"} => "SRR_S01",
{"SRR", "S10"} => "SRR_S01",
{"SRR", "S11"} => "SRR_S01",
# UDM
{"UDM", "Q05"} => "UDM_Q05",
# Vaccination
{"VXQ", "V01"} => "VXQ_V01",
{"VXR", "V03"} => "VXR_V03",
{"VXU", "V04"} => "VXU_V04"
}
@doc """
Returns the canonical message structure for a message code and trigger event.
Many HL7v2 trigger events share the same abstract message definition. For
example, ADT^A04, ADT^A08, and ADT^A13 all use the ADT_A01 structure.
Falls back to `"CODE_EVENT"` when no canonical mapping exists.
## Examples
iex> HL7v2.MessageDefinition.canonical_structure("ADT", "A28")
"ADT_A05"
iex> HL7v2.MessageDefinition.canonical_structure("ADT", "A01")
"ADT_A01"
iex> HL7v2.MessageDefinition.canonical_structure("ZZZ", "Z01")
"ZZZ_Z01"
"""
@spec canonical_structure(binary(), binary()) :: binary()
def canonical_structure(code, event) do
Map.get(@canonical_structures, {code, event}, "#{code}_#{event}")
end
@doc """
Validates segment presence/structure against the message definition.
Delegates to `HL7v2.Validation.Structural` for structures with group-aware
definitions. Returns a warning for unknown structures.
"""
@spec validate_structure(binary() | nil, [binary()]) :: :ok | {:error, [map()]}
def validate_structure(nil, _segment_ids), do: :ok
def validate_structure("", _segment_ids), do: :ok
def validate_structure(structure, segment_ids) do
case HL7v2.Standard.MessageStructure.get(structure) do
%{} = struct_def ->
case HL7v2.Validation.Structural.validate(struct_def, segment_ids) do
[] -> :ok
errors -> {:error, errors}
end
nil ->
{:error,
[
%{
level: :warning,
location: "message",
message:
"message structure #{structure} has no validation definition — structure not checked"
}
]}
end
end
end