From 22cefea4dcf4c379f0eab6ef631e1f300aed8eb5 Mon Sep 17 00:00:00 2001 From: Brian Cragun Date: Thu, 27 Jan 2022 11:36:44 -0700 Subject: [PATCH 1/2] IN1.49 to Patient Ident; limit RelPers creation Signed-off-by: Brian Cragun --- .../hl7/data/SimpleDataTypeMapper.java | 1 + .../hl7/data/SimpleDataValueResolver.java | 6 + .../hl7/codesystem/v2ToFhirMapping.yml | 51 +++++- src/main/resources/hl7/message/DFT_P03.yml | 1 + src/main/resources/hl7/resource/Coverage.yml | 10 +- src/main/resources/hl7/resource/Patient.yml | 34 +++- .../segments/Hl7FinancialInsuranceTest.java | 151 ++++++++++++++---- 7 files changed, 214 insertions(+), 40 deletions(-) diff --git a/src/main/java/io/github/linuxforhealth/hl7/data/SimpleDataTypeMapper.java b/src/main/java/io/github/linuxforhealth/hl7/data/SimpleDataTypeMapper.java index 88722d11..c252d1bb 100644 --- a/src/main/java/io/github/linuxforhealth/hl7/data/SimpleDataTypeMapper.java +++ b/src/main/java/io/github/linuxforhealth/hl7/data/SimpleDataTypeMapper.java @@ -68,6 +68,7 @@ public enum SimpleDataTypeMapper { DOC_REF_DOC_STATUS(SimpleDataValueResolver.DOC_REF_DOC_STATUS_CODE_FHIR), POLICYHOLDER_RELATIONSHIP(SimpleDataValueResolver.POLICYHOLDER_RELATIONSHIP), SUBSCRIBER_RELATIONSHIP(SimpleDataValueResolver.SUBSCRIBER_RELATIONSHIP), + RELATED_PERSON_NEEDED(SimpleDataValueResolver.RELATED_PERSON_NEEDED), UNIT_SYSTEM(SimpleDataValueResolver.UNIT_SYSTEM), ENCOUNTER_MODE_ARRIVAL_DISPLAY(SimpleDataValueResolver.ENCOUNTER_MODE_ARRIVAL_DISPLAY); diff --git a/src/main/java/io/github/linuxforhealth/hl7/data/SimpleDataValueResolver.java b/src/main/java/io/github/linuxforhealth/hl7/data/SimpleDataValueResolver.java index 74b0f397..72551e9f 100644 --- a/src/main/java/io/github/linuxforhealth/hl7/data/SimpleDataValueResolver.java +++ b/src/main/java/io/github/linuxforhealth/hl7/data/SimpleDataValueResolver.java @@ -379,6 +379,12 @@ public class SimpleDataValueResolver { } }; + // Maps from IN1.17 or IN2.72 to a boolean string TRUE if RelatedPerson should be created + public static final ValueExtractor RELATED_PERSON_NEEDED = (Object value) -> { + String val = Hl7DataHandlerUtil.getStringValue(value); + return getFHIRCode(val, "RelatedPersonNeeded"); + }; + public static final ValueExtractor MARITAL_STATUS = (Object value) -> { String val = Hl7DataHandlerUtil.getStringValue(value); String text = Hl7DataHandlerUtil.getOriginalDisplayText(value); diff --git a/src/main/resources/hl7/codesystem/v2ToFhirMapping.yml b/src/main/resources/hl7/codesystem/v2ToFhirMapping.yml index 05326509..4469f136 100644 --- a/src/main/resources/hl7/codesystem/v2ToFhirMapping.yml +++ b/src/main/resources/hl7/codesystem/v2ToFhirMapping.yml @@ -496,4 +496,53 @@ SubscriberRelationship: 18: parent # Parent 19: other # Grandparent - +# Indicates whether a relatedPerson should be created for Coverage +# Only a TRUE will cause relatedPerson to be created. At this time FALSE and no match are equivalent +RelatedPersonNeeded: +# Direction: Insured to Patient +# Codes from IN1.17 (values from table 0063) + BRO: TRUE +# CGV: Care giver: no relatedPerson + CHD: TRUE +# DEP: Handicapped dependent : no relatedPerson + DOM: TRUE + EXF: TRUE + FCH: TRUE +# FND: Friend: no relatedPerson + FTH: TRUE + GCH: TRUE +# GRD: Guardian : no relatedPerson + GRP: TRUE + MTH: TRUE + NCH: TRUE +# NON: None: no relatedPerson +# OTH: Other: no relatedPerson + PAR: TRUE + SCH: TRUE +# SEL: Self: no relatedPerson + SIB: TRUE + SIS: TRUE + SPO: TRUE +# UNK: Unknown : no relatedPerson +# WRD: Ward of the court : no relatedPerson +# - - - - - +# Codes from IN2.72 (values from table 0344) must be REVERSED for RelatedPerson.relationship. + 01: # Patient is insured : no relatedPerson + 02: TRUE # Spouse : Spouse + 03: TRUE # Natural child financial responsibility : Parent + 04: TRUE # Natural child no financial responsibility : Parent + 05: TRUE # Step child : Parent + 06: TRUE # Foster child : Foster Parent +# 07: Ward of the court: no relatedPerson +# 08: Employee: no relatedPerson +# 09: Unknown: no relatedPerson +# 10: Handicapped dependent input: no relatedPerson +# 11: Organ donor : no relatedPerson +# 12: Cadaver donor : no relatedPerson + 13: TRUE # Grandchild : Grandparent + 14: TRUE # Niece/Nephew : Extended +# 15: Injured planitiff : no relatedPerson +# 16: Sponsored dependent : no relatedPerson + 17: TRUE # Minor dependent of a minor dependant input (grandchild dependent) : Grandparent + 18: TRUE # Parent : Child + 19: TRUE # Grandparent : Grandchild \ No newline at end of file diff --git a/src/main/resources/hl7/message/DFT_P03.yml b/src/main/resources/hl7/message/DFT_P03.yml index f9ca8488..bd19ef41 100644 --- a/src/main/resources/hl7/message/DFT_P03.yml +++ b/src/main/resources/hl7/message/DFT_P03.yml @@ -25,6 +25,7 @@ resources: additionalSegments: - PD1 - MSH + - INSURANCE.IN1 - resourceName: Encounter segment: PV1 diff --git a/src/main/resources/hl7/resource/Coverage.yml b/src/main/resources/hl7/resource/Coverage.yml index cb99b0d9..3e1cb1dd 100644 --- a/src/main/resources/hl7/resource/Coverage.yml +++ b/src/main/resources/hl7/resource/Coverage.yml @@ -158,19 +158,19 @@ policyHolder: orgIdSystem: String, IN2.69.6 orgIdTypeCode: String, IN2.69.7 -# If the subscriber is not SEL (self), then create the related person +# If the subscriber is in the list of relationship codes needing a relatedPerson, create one subscriber_1: - condition: $relatedRelationshipStr NOT_NULL && $relatedRelationshipStr NOT_EQUALS SEL && $relatedRelationshipStr NOT_EQUALS 01 + condition: $createRelatedPerson NOT_NULL && $createRelatedPerson EQUALS TRUE valueOf: resource/RelatedPerson expressionType: reference vars: - relatedRelationshipStr: String, IN1.17 | IN2.72 + createRelatedPerson: RELATED_PERSON_NEEDED, IN1.17 | IN2.72 # Related person gets many values from scope, so they do not need to be passed in # IN1 and sub-fields # $Patient -# If the subscriber is SEL (self), then reference the patient -# NOT_NULL check is not needed because we have values here, and NOT_NULL is otherwise caught by logic of subscriber_1 +# If the subscriber is SEL (self), do not create a relatedPerson, but reference the patient +# NOT_NULL check is not needed because we have positive values and condition will not fire without them subscriber_2: condition: $relatedRelationshipStr EQUALS SEL || $relatedRelationshipStr EQUALS 01 valueOf: datatype/Reference diff --git a/src/main/resources/hl7/resource/Patient.yml b/src/main/resources/hl7/resource/Patient.yml index 9fc5fe12..e3fbce31 100644 --- a/src/main/resources/hl7/resource/Patient.yml +++ b/src/main/resources/hl7/resource/Patient.yml @@ -10,6 +10,7 @@ id: type: STRING valueOf: UUID.randomUUID() expressionType: JEXL + identifier_1: valueOf: datatype/Identifier_SystemID generateList: true @@ -17,6 +18,7 @@ identifier_1: specs: PID.3 vars: assignerSystem: String, PID.3.4 + # Gets the SSN from PID.19, formats and adds it as an ID identifier_2: condition: $valueIn NOT_NULL @@ -30,6 +32,7 @@ identifier_2: code: SS display: Social Security number # There is no text for PID.19 + identifier_3: condition: $valueIn NOT_NULL valueOf: datatype/Identifier_var @@ -44,6 +47,7 @@ identifier_3: code: DL display: Driver's license number # There is no text for PID.20 + identifier_4: condition: $mrgIdentifier NOT_NULL valueOf: datatype/Identifier_SystemID @@ -55,21 +59,36 @@ identifier_4: mrgIdentifier: MRG.1 # Add the old MR # from the MRG segment +identifier_5: + condition: $valueIn NOT_NULL + valueOf: datatype/Identifier_var + generateList: true + expressionType: resource + specs: IN1.49 + vars: + valueIn: String, IN1.49.1 + systemCX: IN1.49.4 + code: IN1.49.5 + constants: + system: http://terminology.hl7.org/CodeSystem/v2-0203 + name: valueOf: datatype/HumanName generateList: true expressionType: resource specs: PID.5 -# There is no authority for PID.20 + gender: type: ADMINISTRATIVE_GENDER valueOf: PID.8 expressionType: HL7Spec + address: valueOf: datatype/Address generateList: true expressionType: resource specs: PID.11 + telecom_1: condition: $pid14 NOT_NULL valueOf: datatype/ContactPoint @@ -80,7 +99,7 @@ telecom_1: pid14: PID.14 constants: use: work -# The yaml is processed in reverse order, therefore + telecom_2: condition: $pid13 NOT_NULL valueOf: datatype/ContactPoint @@ -91,11 +110,14 @@ telecom_2: pid13: PID.13 constants: use: home + +# The yaml is processed in reverse order, therefore # Put the PID.13 last in yaml so it is first to be processed birthDate: type: DATE valueOf: PID.7 expressionType: HL7Spec + multipleBirthBoolean_1: condition: $multBool NOT_NULL && $multInt NULL type: BOOLEAN @@ -104,6 +126,7 @@ multipleBirthBoolean_1: vars: multBool: PID.24 multInt: PID.25 + multipleBirthBoolean_2: condition: $multBool EQUALS N type: BOOLEAN @@ -112,6 +135,7 @@ multipleBirthBoolean_2: vars: multBool: String, PID.24 multInt: PID.25 + multipleBirthInteger_1: condition: $multBool NULL && $multInt NOT_NULL type: INTEGER @@ -120,6 +144,7 @@ multipleBirthInteger_1: vars: multBool: String, PID.24 multInt: PID.25 + multipleBirthInteger_2: condition: $multBool EQUALS Y && $multInt NOT_NULL type: INTEGER @@ -128,6 +153,7 @@ multipleBirthInteger_2: vars: multBool: String, PID.24 multInt: PID.25 + deceasedBoolean: condition: $deceasedBool NOT_NULL && $deceasedDateTime NULL type: BOOLEAN @@ -136,6 +162,7 @@ deceasedBoolean: vars: deceasedBool: PID.30 deceasedDateTime: PID.29 + deceasedDateTime: condition: $deceasedDateTime NOT_NULL type: DATE_TIME @@ -143,6 +170,7 @@ deceasedDateTime: expressionType: HL7Spec vars: deceasedDateTime: PID.29 + maritalStatus: valueOf: datatype/CodeableConcept expressionType: resource @@ -150,6 +178,7 @@ maritalStatus: vars: coding: MARITAL_STATUS, PID.16 text: String, PID.16.2 + generalPractitioner: condition: $practitionerVal NOT_NULL valueOf: resource/Practitioner @@ -158,6 +187,7 @@ generalPractitioner: specs: PD1.4 vars: practitionerVal: PD1.4 + extension: generateList: true expressionType: nested diff --git a/src/test/java/io/github/linuxforhealth/hl7/segments/Hl7FinancialInsuranceTest.java b/src/test/java/io/github/linuxforhealth/hl7/segments/Hl7FinancialInsuranceTest.java index f88e8a2d..1144c46a 100644 --- a/src/test/java/io/github/linuxforhealth/hl7/segments/Hl7FinancialInsuranceTest.java +++ b/src/test/java/io/github/linuxforhealth/hl7/segments/Hl7FinancialInsuranceTest.java @@ -51,8 +51,8 @@ void testBasicInsuranceCoverageFields() throws IOException { // FT1.7 is required transaction code (currently not used) + "FT1||||20201231145045||CG|FAKE|||||||||||||||||||||||||||||||||||||\n" // IN1 Segment is split and concatenated for easier understanding. (| precedes numbered field.) - // IN1.2.1, IN1.2.3 to Identifier XV 1 - // IN1.2.4, IN1.2.6 to Identifier XV 2 + // IN1.2.1, IN1.2.3 to Coverage.identifier XV 1 + // IN1.2.4, IN1.2.6 to Coverage.identifier XV 2 + "IN1|1|Value1^^System3^Value4^^System6" // Thorough organization testing. // IN1.3 to Organization Identifier @@ -99,15 +99,16 @@ void testBasicInsuranceCoverageFields() throws IOException { // IN1.23 through IN1.34 NOT REFERENCED // IN1.35 to Organization.identifier + "|20201231145045|20211231145045|||||||||5|||||||||||||COMPANYPLANCODE35" - // IN1.36 to Identifier MB and Identifier SN + // IN1.36 to Coverage.identifier MB and Coverage.identifier SN // IN1.36 also to subscriberId - // IN1.46 to Identifier XV 3 - // IN1.47 through IN1.53 NOT REFERENCED - + "|MEMBER36||||||||||Value46|||||||\n" - // IN2.6 is purposely empty so will not create an MC identifier - // IN2.8 is purposely empty so will not create an MA identifier + // IN1.46 to Coverage.identifier XV 3 + // IN1.49 to PatientCoverage.identifier + // IN1.53 through IN1.53 NOT REFERENCED + + "|MEMBER36||||||||||Value46||PatientId48|PatientId49.1^^^System49.4^TypeCode49.5|PatientId50|||\n" + // IN2.6 is purposely empty so will not create an MC Coverage.identifier + // IN2.8 is purposely empty so will not create an MA Coverage.identifier // IN2.25 to new PayorId Organization - // IN2.61 is purposely empty (primary to IN1.36) so IN1.36 will be used as the MB identifier + // IN2.61 is purposely empty (primary to IN1.36) so IN1.36 will be used as the MB Coverage.identifier + "IN2|||||||||||||||||||||||||IdValue25.1^^^IdSystem25.4^IdType25.5^^20201231145045^20211231145045|||||||||||||||||||||||||||||||||||||||||||" // IN2.69 to new PolicyHolder Organization Name and ID // IN2.72 is purposely empty (backup to IN1.17) so no RelatedPerson is created. @@ -122,6 +123,17 @@ void testBasicInsuranceCoverageFields() throws IOException { assertThat(patients).hasSize(1); // From PID Patient patient = (Patient) patients.get(0); String patientId = patient.getId(); + assertThat(patient.getIdentifier()).hasSize(2); // From PID.3 and IN1.49 + Identifier patientIdentifier = patient.getIdentifier().get(0); + assertThat(patientIdentifier.getValue()).isEqualTo("MR1"); // PID.3.1 + assertThat(patientIdentifier.getSystem()).isEqualTo("urn:id:XYZ"); // PID.3.4 + DatatypeUtils.checkCommonCodeableConceptAssertions(patientIdentifier.getType(), "MR", "Medical record number", + "http://terminology.hl7.org/CodeSystem/v2-0203", null); // PID.3.5 + patientIdentifier = patient.getIdentifier().get(1); + assertThat(patientIdentifier.getValue()).isEqualTo("PatientId49.1"); // IN1.49.1 + assertThat(patientIdentifier.getSystem()).isEqualTo("urn:id:System49.4"); // IN1.49.4 + DatatypeUtils.checkCommonCodeableConceptAssertions(patientIdentifier.getType(), "TypeCode49.5", null, + "http://terminology.hl7.org/CodeSystem/v2-0203", null); // IN1.49.5 List organizations = ResourceUtils.getResourceList(e, ResourceType.Organization); assertThat(organizations).hasSize(3); // From IN1.3 creates Payor, IN2.25 to new PayorId Organization, IN2.69 creates new PolicyHolder Organization Name @@ -310,8 +322,8 @@ void testInsuranceCoverageByRelatedFields(String messageType) throws IOException // FT1.7 is required transaction code (currently not used) + "FT1||||20201231145045||CG|FAKE|||||||||||||||||||||||||||||||||||||\n" // IN1 Segment is split and concatenated for easier understanding. (| precedes numbered field.) - // IN1.2.1, IN1.2.3 to Identifier XV 1 - // IN1.2.4, IN1.2.6 to Identifier XV 2 + // IN1.2.1, IN1.2.3 to Coverage.identifier XV 1 + // IN1.2.4, IN1.2.6 to Coverage.identifier XV 2 + "IN1|1|Value1^^System3^Value4^^System6" // Minimal Organization. Required for Payor, which is required. // Organization deep test in testBasicInsuranceCoverageFields @@ -331,9 +343,9 @@ void testInsuranceCoverageByRelatedFields(String messageType) throws IOException // IN1.22 purposely empty to show that IN1.1 is secondary for Coverage.order // IN1.23 through IN1.35 NOT REFERENCED + "|DoeFake^Judy^^^Rev.|PAR|19780429|19 Rose St^^Faketown^CA^ZIP5||||||||||||||||" - // IN1.36 purposely present, used by SN identifier, but is ignored by MB identifier because IN2.61 takes priority + // IN1.36 purposely present, used by SN Coverage.identifier, but is ignored by MB Coverage.identifier because IN2.61 takes priority // IN1.43 to RelatedPerson.gender - // IN1.46 to Identifier XV 3 + // IN1.46 to Coverage.identifier XV 3 // IN1.49 to RelatedPerson.identifier // IN1.49.1 to RelatedPerson.identifier.value // IN1.49.4 to RelatedPerson.identifier.system @@ -342,11 +354,11 @@ void testInsuranceCoverageByRelatedFields(String messageType) throws IOException // IN1.50 through IN1.53 NOT REFERENCED + "|MEMBER36|||||||F|||Value46|||J494949^^^Large HMO^XX||||\n" // IN2.2 to RelatedPerson.identifier (SSN) - // IN2.6 to Identifier 5: MC Patient's Medicare number - // IN2.8 to Identifier 5: MA Patient Medicaid number + // IN2.6 to Coverage.identifier MC Patient's Medicare number + // IN2.8 to Coverage.identifier MA Patient Medicaid number // IN2.9 through IN2.60 not used + "IN2||777-88-9999||||MEDICARE06||MEDICAID08|| |||||||||| ||||||||||| |||||||||| |||||||||| ||||||||||" - // IN2.61 to Identifier MB; takes priority over IN1.36 + // IN2.61 to Coverage.identifier MB; takes priority over IN1.36 // IN2.63 to RelatedPerson.telecom // IN2.63.1 to Organization Contact telecom .value (ONLY when XTN.5-XTN.7 are empty. See rules in getFormattedTelecomNumberValue.) // IN2.63.2 is not mapped. @@ -514,11 +526,11 @@ void testInsuranceCoverageOfSelfAndTenant() throws IOException { // FT1.7 is required transaction code (currently not used) + "FT1||||20201231145045||CG|FAKE|||||||||||||||||||||||||||||||||||||\n" // IN1 Segment is split and concatenated for easier understanding. (| precedes numbered field.) - // IN1.2.1, IN1.2.3 to Identifier XV 1 - // IN1.2.4, IN1.2.6 to Identifier XV 2 + // IN1.2.1, IN1.2.3 to Coverage.identifier XV 1 + // IN1.2.4, IN1.2.6 to Coverage.identifier XV 2 + "IN1|1|Value1^^System3^Value4^^System6" // Minimal Organization to test TENANT prepend. - // IN1.3 to Organization Identifier + // IN1.3 to Organization identifier // IN1.3.1 to Organization Identifier.value // IN1.3.4 to Organization Identifier.system, will be prepended by TENANT from options // IN1.4 to Organization Name @@ -528,14 +540,14 @@ void testInsuranceCoverageOfSelfAndTenant() throws IOException { // IN1.17 to Coverage.relationship. SEL (self) should create relationship of ONESELF, and reference to patient. // IN1.18 through IN1.35 NOT REFERENCED + "||SEL||||||||||||||||||" - // IN1.36 to Identifier MB and Identifier SN - // IN1.46 to Identifier XV 3 + // IN1.36 to Coverage.identifier MB and Coverage.identifier SN + // IN1.46 to Coverage.identifier XV 3 // IN1.47 through IN1.53 NOT REFERENCED + "|MEMBER36||||||||||Value46|||||||\n" - // IN2.6 is purposely empty so will not create an MC identifier - // IN2.8 is purposely empty so will not create an MA identifier + // IN2.6 is purposely empty so will not create an MC Coverage.identifier + // IN2.8 is purposely empty so will not create an MA Coverage.identifier // IN2.25 to new PayorId Organization - // IN2.61 is purposely empty (primary to IN1.36) so IN1.36 will be used as the MB identifier + // IN2.61 is purposely empty (primary to IN1.36) so IN1.36 will be used as the MB Coverage.identifier + "IN2|||||||||||||||||||||||||IdValue25.1^^^IdSystem25.4^IdType25.5^^20201231145045^20211231145045|||||||||||||||||||||||||||||||||||||||||||" // IN2.69 to new PolicyHolder Organization Name and ID // IN2.72 is purposely empty (backup to IN1.17) so no RelatedPerson is created. @@ -642,7 +654,7 @@ void testFailingOrganizationTelecom() throws IOException { // FT1.7 is required transaction code (currently not used) + "FT1||||20201231145045||CG|FAKE|||||||||||||||||||||||||||||||||||||\n" // These fields in IN1 with these values cause the failure, leaving some of the field empty may be contribute. - // IN1.2 to Identifier XV 1 + // IN1.2 to Coverage.identifier XV 1 // IN1.3 to Organization Identifier // IN1.4 to Organization Name // IN1.7 to Organization Telecom @@ -699,8 +711,8 @@ void testInsuranceCoverageFromIN2() throws IOException { // FT1.7 is required transaction code (currently not used) + "FT1||||20201231145045||CG|FAKE|||||||||||||||||||||||||||||||||||||\n" // IN1 Segment is split and concatenated for easier understanding. (| precedes numbered field.) - // IN1.2.1, IN1.2.3 to Identifier XV 1 - // IN1.2.4, IN1.2.6 to Identifier XV 2 + // IN1.2.1, IN1.2.3 to Coverage.identifier XV 1 + // IN1.2.4, IN1.2.6 to Coverage.identifier XV 2 + "IN1|1|Value1^^System3^Value4^^System6" // Minimal Organization. Required for Payor, which is required. // Organization deep test in testBasicInsuranceCoverageFields @@ -717,7 +729,7 @@ void testInsuranceCoverageFromIN2() throws IOException { // IN1.17 purposely empty to validate IN2.72 works as secondary // IN1.18 through IN1.35 NOT REFERENCED + "|DoeFake^Judy^^^Rev.|||||||||||||||||||" - // IN1.36 to Identifier MB and Identifier SN + // IN1.36 to Coverage.identifier MB and Coverage.identifier SN // IN1.37 through IN1.53 NOT REFERENCED + "|MEMBER36|||||||||||||||||\n" // IN2.1 through IN2.71 not used @@ -797,8 +809,8 @@ void testInsuranceCoverageFromIN2Self() throws IOException { // FT1.7 is required transaction code (currently not used) + "FT1||||20201231145045||CG|FAKE|||||||||||||||||||||||||||||||||||||\n" // IN1 Segment is split and concatenated for easier understanding. (| precedes numbered field.) - // IN1.2.1, IN1.2.3 to Identifier XV 1 - // IN1.2.4, IN1.2.6 to Identifier XV 2 + // IN1.2.1, IN1.2.3 to Coverage.identifier XV 1 + // IN1.2.4, IN1.2.6 to Coverage.identifier XV 2 + "IN1|1|Value1^^System3^Value4^^System6" // Minimal Organization // IN1.3 to Organization Identifier @@ -809,7 +821,7 @@ void testInsuranceCoverageFromIN2Self() throws IOException { // IN1.17 empty to verify that IN2.72 works as backup for IN1.17 // IN1.18 through IN1.35 NOT REFERENCED + "||||||||||||||||||||" - // IN1.36 to Identifier MB and Identifier SN + // IN1.36 to Coverage.identifier MB and Coverage.identifier SN // IN1.37 through IN1.53 NOT REFERENCED + "|MEMBER36|||||||||||||||||\n" // IN2.1 through IN2.71 NOT REFERENCED @@ -834,7 +846,7 @@ void testInsuranceCoverageFromIN2Self() throws IOException { // Check organization Id's assertThat(org.getIdentifier()).hasSize(1); - // Org identifiers checked deeply in other tests + // Organization identifiers checked deeply in other tests List coverages = ResourceUtils.getResourceList(e, ResourceType.Coverage); assertThat(coverages).hasSize(1); // From IN1 segment @@ -864,4 +876,79 @@ void testInsuranceCoverageFromIN2Self() throws IOException { assertThat(e).hasSize(4); } + @Test + // Tests non-related subscriber, employer. + void testInsuranceCoverageOfWorkersComp() throws IOException { + + String hl7message = "MSH|^~\\&|TEST|TEST|||20220101120000||DFT^P03|1234|P|2.6\n" + // + "EVN||20210407191342||||||\n" + + "PID|||workers_comp^^^XYZ^MR||DOE^JANE^|||F||||||||||||||||||||||\n" + + "PV1||I||||||||||||||||||||||||||||||||||||||||||\n" + // FT1 added for completeness; required in specification, but not used (ignored) by templates + // FT1.4 is required transaction date (currently not used) + // FT1.6 is required transaction type (currently not used) + // FT1.7 is required transaction code (currently not used) + + "FT1||||20201231145045||CG|FAKE|||||||||||||||||||||||||||||||||||||\n" + // IN1 Segment is split and concatenated for easier understanding. (| precedes numbered field.) + // IN1.2.1, IN1.2.3 to Coverage.identifier XV 1 + // IN1.2.4, IN1.2.6 to Coverage.identifier XV 2 + + "IN1|1|Value1^^System3^Value4^^System6" + // Minimal Organization + // IN1.3 to Organization Identifier + // INI.4 to Organization Name (required to inflate organization) + // IN1.5 through 15 NOT REFERENCED (Tested in testBasicInsuranceCoverageFields) + + "|IdValue1^^^IdSystem4^^^^|Large Blue Organization|||||||||||" + // IN1.16 empty because there is no related person (IN2.72 is self) + // IN1.17 is non-related value means "Employer" + // IN1.18 through IN1.35 NOT REFERENCED + + "||EMR||||||||||||||||||" + // IN1.36 to Coverage.identifier MB and Coverage.identifier SN + // IN1.37 through IN1.53 NOT REFERENCED + + "|MEMBER36|||||||||||||||||\n"; + + List e = ResourceUtils.createFHIRBundleFromHL7MessageReturnEntryList(hl7message); + + List encounters = ResourceUtils.getResourceList(e, ResourceType.Encounter); + assertThat(encounters).hasSize(1); // From PV1 + + List patients = ResourceUtils.getResourceList(e, ResourceType.Patient); + assertThat(patients).hasSize(1); // From PID + Patient patient = (Patient) patients.get(0); + String patientId = patient.getId(); + + List organizations = ResourceUtils.getResourceList(e, ResourceType.Organization); + assertThat(organizations).hasSize(1); // From Payor created by IN1 + Organization org = (Organization) organizations.get(0); + + // Check organization Id's + assertThat(org.getIdentifier()).hasSize(1); + // Org identifiers checked deeply in other tests + + List coverages = ResourceUtils.getResourceList(e, ResourceType.Coverage); + assertThat(coverages).hasSize(1); // From IN1 segment + Coverage coverage = (Coverage) coverages.get(0); + + // Confirm Coverage Identifiers + assertThat(coverage.getIdentifier()).hasSize(4); // XV, XV, MB, SN + // Coverage Identifiers deep check in testBasicInsuranceCoverageFields + + // Because the relationship is EMR (Employer), no subscriber is created (subscriber can't be an Organization) + assertThat(coverage.hasSubscriber()).isFalse(); + // Confirm Coverage Beneficiary references to Patient, and Payor references to Organization + assertThat(coverage.getBeneficiary().getReference()).isEqualTo(patientId); + assertThat(coverage.getPayorFirstRep().getReference()).isEqualTo(organizations.get(0).getId()); + + // Expect no RelatedPerson because IN1.17 was EMR + List relatedPersons = ResourceUtils.getResourceList(e, ResourceType.RelatedPerson); + assertThat(relatedPersons).isEmpty(); // No related person should be created because IN2.72 was 01 (self) + + // Check coverage.relationship (from SubscriberRelationship mapping) + DatatypeUtils.checkCommonCodeableConceptAssertions(coverage.getRelationship(), "EMR", + null, null, null); // IN1.17, because it is not mapped there is no system or display. + + // Confirm there are no unaccounted for resources + // Expected: Coverage, Organization, Patient, Encounter + assertThat(e).hasSize(4); + } + } From 8c0d5b6ee26aa11ea6fc8278c98a7d7bb3aef05d Mon Sep 17 00:00:00 2001 From: Brian Cragun Date: Thu, 27 Jan 2022 12:17:19 -0700 Subject: [PATCH 2/2] Code review fixes. Signed-off-by: Brian Cragun --- .../segments/Hl7FinancialInsuranceTest.java | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/test/java/io/github/linuxforhealth/hl7/segments/Hl7FinancialInsuranceTest.java b/src/test/java/io/github/linuxforhealth/hl7/segments/Hl7FinancialInsuranceTest.java index 1144c46a..4d4db53e 100644 --- a/src/test/java/io/github/linuxforhealth/hl7/segments/Hl7FinancialInsuranceTest.java +++ b/src/test/java/io/github/linuxforhealth/hl7/segments/Hl7FinancialInsuranceTest.java @@ -51,8 +51,8 @@ void testBasicInsuranceCoverageFields() throws IOException { // FT1.7 is required transaction code (currently not used) + "FT1||||20201231145045||CG|FAKE|||||||||||||||||||||||||||||||||||||\n" // IN1 Segment is split and concatenated for easier understanding. (| precedes numbered field.) - // IN1.2.1, IN1.2.3 to Coverage.identifier XV 1 - // IN1.2.4, IN1.2.6 to Coverage.identifier XV 2 + // IN1.2.1, IN1.2.3 to first XV Coverage.identifier + // IN1.2.4, IN1.2.6 to second XV Coverage.identifier + "IN1|1|Value1^^System3^Value4^^System6" // Thorough organization testing. // IN1.3 to Organization Identifier @@ -101,10 +101,9 @@ void testBasicInsuranceCoverageFields() throws IOException { + "|20201231145045|20211231145045|||||||||5|||||||||||||COMPANYPLANCODE35" // IN1.36 to Coverage.identifier MB and Coverage.identifier SN // IN1.36 also to subscriberId - // IN1.46 to Coverage.identifier XV 3 + // IN1.46 to third XV Coverage.identifier // IN1.49 to PatientCoverage.identifier - // IN1.53 through IN1.53 NOT REFERENCED - + "|MEMBER36||||||||||Value46||PatientId48|PatientId49.1^^^System49.4^TypeCode49.5|PatientId50|||\n" + + "|MEMBER36||||||||||Value46|||PatientId49.1^^^System49.4^TypeCode49.5||||\n" // IN2.6 is purposely empty so will not create an MC Coverage.identifier // IN2.8 is purposely empty so will not create an MA Coverage.identifier // IN2.25 to new PayorId Organization @@ -322,8 +321,8 @@ void testInsuranceCoverageByRelatedFields(String messageType) throws IOException // FT1.7 is required transaction code (currently not used) + "FT1||||20201231145045||CG|FAKE|||||||||||||||||||||||||||||||||||||\n" // IN1 Segment is split and concatenated for easier understanding. (| precedes numbered field.) - // IN1.2.1, IN1.2.3 to Coverage.identifier XV 1 - // IN1.2.4, IN1.2.6 to Coverage.identifier XV 2 + // IN1.2.1, IN1.2.3 to first XV Coverage.identifier + // IN1.2.4, IN1.2.6 to second XV Coverage.identifier + "IN1|1|Value1^^System3^Value4^^System6" // Minimal Organization. Required for Payor, which is required. // Organization deep test in testBasicInsuranceCoverageFields @@ -345,7 +344,7 @@ void testInsuranceCoverageByRelatedFields(String messageType) throws IOException + "|DoeFake^Judy^^^Rev.|PAR|19780429|19 Rose St^^Faketown^CA^ZIP5||||||||||||||||" // IN1.36 purposely present, used by SN Coverage.identifier, but is ignored by MB Coverage.identifier because IN2.61 takes priority // IN1.43 to RelatedPerson.gender - // IN1.46 to Coverage.identifier XV 3 + // IN1.46 to third XV Coverage.identifier // IN1.49 to RelatedPerson.identifier // IN1.49.1 to RelatedPerson.identifier.value // IN1.49.4 to RelatedPerson.identifier.system @@ -526,8 +525,8 @@ void testInsuranceCoverageOfSelfAndTenant() throws IOException { // FT1.7 is required transaction code (currently not used) + "FT1||||20201231145045||CG|FAKE|||||||||||||||||||||||||||||||||||||\n" // IN1 Segment is split and concatenated for easier understanding. (| precedes numbered field.) - // IN1.2.1, IN1.2.3 to Coverage.identifier XV 1 - // IN1.2.4, IN1.2.6 to Coverage.identifier XV 2 + // IN1.2.1, IN1.2.3 to first XV Coverage.identifier + // IN1.2.4, IN1.2.6 to second XV Coverage.identifier + "IN1|1|Value1^^System3^Value4^^System6" // Minimal Organization to test TENANT prepend. // IN1.3 to Organization identifier @@ -541,7 +540,7 @@ void testInsuranceCoverageOfSelfAndTenant() throws IOException { // IN1.18 through IN1.35 NOT REFERENCED + "||SEL||||||||||||||||||" // IN1.36 to Coverage.identifier MB and Coverage.identifier SN - // IN1.46 to Coverage.identifier XV 3 + // IN1.46 to third XV Coverage.identifier // IN1.47 through IN1.53 NOT REFERENCED + "|MEMBER36||||||||||Value46|||||||\n" // IN2.6 is purposely empty so will not create an MC Coverage.identifier @@ -654,7 +653,7 @@ void testFailingOrganizationTelecom() throws IOException { // FT1.7 is required transaction code (currently not used) + "FT1||||20201231145045||CG|FAKE|||||||||||||||||||||||||||||||||||||\n" // These fields in IN1 with these values cause the failure, leaving some of the field empty may be contribute. - // IN1.2 to Coverage.identifier XV 1 + // IN1.2 to first XV Coverage.identifier // IN1.3 to Organization Identifier // IN1.4 to Organization Name // IN1.7 to Organization Telecom @@ -711,8 +710,8 @@ void testInsuranceCoverageFromIN2() throws IOException { // FT1.7 is required transaction code (currently not used) + "FT1||||20201231145045||CG|FAKE|||||||||||||||||||||||||||||||||||||\n" // IN1 Segment is split and concatenated for easier understanding. (| precedes numbered field.) - // IN1.2.1, IN1.2.3 to Coverage.identifier XV 1 - // IN1.2.4, IN1.2.6 to Coverage.identifier XV 2 + // IN1.2.1, IN1.2.3 to first XV Coverage.identifier + // IN1.2.4, IN1.2.6 to second XV Coverage.identifier + "IN1|1|Value1^^System3^Value4^^System6" // Minimal Organization. Required for Payor, which is required. // Organization deep test in testBasicInsuranceCoverageFields @@ -809,8 +808,8 @@ void testInsuranceCoverageFromIN2Self() throws IOException { // FT1.7 is required transaction code (currently not used) + "FT1||||20201231145045||CG|FAKE|||||||||||||||||||||||||||||||||||||\n" // IN1 Segment is split and concatenated for easier understanding. (| precedes numbered field.) - // IN1.2.1, IN1.2.3 to Coverage.identifier XV 1 - // IN1.2.4, IN1.2.6 to Coverage.identifier XV 2 + // IN1.2.1, IN1.2.3 to first XV Coverage.identifier + // IN1.2.4, IN1.2.6 to second XV Coverage.identifier + "IN1|1|Value1^^System3^Value4^^System6" // Minimal Organization // IN1.3 to Organization Identifier @@ -890,8 +889,8 @@ void testInsuranceCoverageOfWorkersComp() throws IOException { // FT1.7 is required transaction code (currently not used) + "FT1||||20201231145045||CG|FAKE|||||||||||||||||||||||||||||||||||||\n" // IN1 Segment is split and concatenated for easier understanding. (| precedes numbered field.) - // IN1.2.1, IN1.2.3 to Coverage.identifier XV 1 - // IN1.2.4, IN1.2.6 to Coverage.identifier XV 2 + // IN1.2.1, IN1.2.3 to first XV Coverage.identifier + // IN1.2.4, IN1.2.6 to second XV Coverage.identifier + "IN1|1|Value1^^System3^Value4^^System6" // Minimal Organization // IN1.3 to Organization Identifier