Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions apps/bfd-model-idr/StructureDefinitions/Source/Provider.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@
}
]
},
{
"id": "Provider.PRVDR_LAST_OR_LGL_NAME",
"path": "Provider.PRVDR_LAST_OR_LGL_NAME",
"label": "Provider Last or Legal Name",
"min": 0,
"max": "1",
"type": [
{
"code": "string"
}
]
},
{
"id": "Provider.PRVDR_MDL_NAME",
"path": "Provider.PRVDR_MDL_NAME",
Expand Down Expand Up @@ -189,8 +201,8 @@
]
},
{
"id": "Provider.prvdr_zip",
"path": "Provider.prvdr_zip",
"id": "Provider.CLM_BLG_PRVDR_ZIP5_CD",
"path": "Provider.CLM_BLG_PRVDR_ZIP5_CD",
"label": "Billing Provider Zip 5",
"min": 0,
"max": "1",
Expand All @@ -199,6 +211,18 @@
"code": "string"
}
]
},
{
"id": "Provider.PRVDR_TAX_NUM",
"path": "Provider.PRVDR_TAX_NUM",
"label": "Provider Tax Number",
"min": 0,
"max": "1",
"type": [
{
"code": "string"
}
]
}
]
}
Expand Down
40 changes: 23 additions & 17 deletions apps/bfd-model-idr/augment_sample_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

prvdr_info_file = "sample-data/PRVDR_HSTRY_POC.csv"
df = pd.read_csv(prvdr_info_file, dtype={"PRVDR_SK": str})
df.head()

cur_sample = sys.argv[1]
cur_sample_data = {}
Expand All @@ -35,7 +34,7 @@
"CLM_LINE_PMD_UNIQ_TRKNG_NUM",
"CLM_LINE_PA_UNIQ_TRKNG_NUM",
]
npis_used = []
# npis_used = []
cur_sample_data["providerList"] = []
cur_careteam_sequence = 1
# we only use PRVDR_SRVC_PRVDR_NPI_NUM for part D events.
Expand All @@ -44,11 +43,9 @@

populate_fields_except_na = [
"PRVDR_LGL_NAME",
"PRVDR_OSCAR_NUM",
# "PRVDR_OSCAR_NUM", No longer pulling this from PRVDR, rather based upon submission.
"PRVDR_LAST_NAME",
"PRVDR_1ST_NAME",
"PRVDR_MDL_NAME",
"PRVDR_TYPE_CD",
]
provider_list = []

Expand All @@ -75,10 +72,11 @@ def convert_to_decimal(val: str | None) -> Decimal:
continue
provider_object = {}
provider_object["PRVDR_SK"] = cur_sample_data.get(column)
if provider_object.get("PRVDR_SK") in npis_used:
provider_object["isDuplicate"] = True
else:
npis_used.append(provider_object.get("PRVDR_SK"))
# Removing since we are not going to populate more than header-level providers using contained resources.
# if provider_object.get("PRVDR_SK") in npis_used:
# provider_object["isDuplicate"] = True
# else:
# npis_used.append(provider_object.get("PRVDR_SK"))

prvdr_hstry_for_npi = json.loads(
df[df["PRVDR_SK"] == str(provider_object.get("PRVDR_SK"))].iloc[0].to_json()
Expand All @@ -94,13 +92,14 @@ def convert_to_decimal(val: str | None) -> Decimal:
if prvdr_hstry_for_npi.get(fld)
}
)

""" we'll remove this...
if prvdr_hstry_for_npi.get("PRVDR_TXNMY_CMPST_CD"):
taxonomy_val = prvdr_hstry_for_npi.get("PRVDR_TXNMY_CMPST_CD")
taxonomy_codes = [
taxonomy_val[i : i + 10] for i in range(len(taxonomy_val), 10)
]
provider_object["taxonomyCodes"] = taxonomy_codes
"""

# assign care team type + sequence for header-level info
if len(header_columns.get(column)) > 0:
Expand All @@ -126,11 +125,18 @@ def convert_to_decimal(val: str | None) -> Decimal:

# We may want to remove this in the future, depending on requirements
# regarding address info.
if (
column == "PRVDR_BLG_PRVDR_NPI_NUM"
and "CLM_BLG_PRVDR_ZIP5_CD" in cur_sample_data
):
provider_object["prvdr_zip"] = cur_sample_data.get("CLM_BLG_PRVDR_ZIP5_CD")
if column in ("PRVDR_BLG_PRVDR_NPI_NUM", "PRVDR_SRVC_PRVDR_NPI_NUM"):
if "CLM_BLG_PRVDR_ZIP5_CD" in cur_sample_data:
provider_object["CLM_BLG_PRVDR_ZIP5_CD"] = cur_sample_data.get("CLM_BLG_PRVDR_ZIP5_CD")
if provider_object.get("NPI_TYPE") == 2:
provider_object["PRVDR_LAST_OR_LGL_NAME"] = provider_object.get("PRVDR_LGL_NAME")
else:
provider_object["PRVDR_LAST_OR_LGL_NAME"] = provider_object.get("PRVDR_LAST_NAME")
# don't worry about first name since we already pull it in above.
if "CLM_BLG_PRVDR_OSCAR_NUM" in cur_sample_data:
provider_object["PRVDR_OSCAR_NUM"] = cur_sample_data.get("CLM_BLG_PRVDR_OSCAR_NUM")
if "CLM_BLG_PRVDR_TAX_NUM" in cur_sample_data:
provider_object["PRVDR_TAX_NUM"] = cur_sample_data.get("CLM_BLG_PRVDR_TAX_NUM")

provider_list.append(provider_object)

Expand All @@ -150,7 +156,7 @@ def convert_to_decimal(val: str | None) -> Decimal:
if item.get(line_supporting_info_col):
item["SEQUENCE_INFO"] = supporting_info_seq
supporting_info_seq += 1

"""
for line_col in line_columns:
if line_col in item and item.get(line_col) not in npis_used:
npi = item.get(line_col)
Expand Down Expand Up @@ -206,7 +212,7 @@ def convert_to_decimal(val: str | None) -> Decimal:
]
careTeamSequence = matching_providers[0]
item["careTeamSequence"] = [careTeamSequence]

"""
# for part D claims, sum CLM_LINE_INGRDNT_CST_AMT, CLM_LINE_SRVC_CST_AMT, CLM_LINE_SLS_TAX_AMT,
# CLM_LINE_VCCN_ADMIN_FEE_AMT to set TOT_RX_CST_AMT
tot_rx_amt = sum(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2282,6 +2282,7 @@
- CCLF4.PRVDR_OSCAR_NUM
- CCLF4.CLM_BLG_PRVDR_OSCAR_NUM
fhirPath: ExplanationOfBenefit.contained.where(id = %root.provider.reference.substring(1)).identifier.where(system='http://terminology.hl7.org/NamingSystem/CCN').value
notes: This will sometimes be populated on Practitioners, even though individuals cannot be assigned an OSCAR number.
- inputPath: ExplanationOfBenefit-Institutional-Base.CLM_MDCR_INSTNL_BENE_PD_AMT
appliesTo:
- Outpatient
Expand Down
32 changes: 22 additions & 10 deletions apps/bfd-model-idr/maps/ExplanationOfBenefit-Base.map
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ group createEOBBase(source src: ExplanationOfBenefitBase, target tgt: BFDEOB){
src.providerList as provider where provider.isDuplicate.exists().not() and provider.careTeamType.exists().not() and provider.NPI_TYPE = 1 -> tgt.contained = create('Practitioner') as tgtPract then createPractitioner(provider, tgtPract) "Add Practitioner";
src.providerList as provider where provider.isDuplicate.exists().not() and provider.careTeamType.exists().not() and provider.NPI_TYPE = 2 -> tgt.contained = create('Organization') as tgtOrg then createOrg(provider, tgtOrg) "Add Organization";
} "Add billing provider";


//Partial for PAC data, complete for adjudicated. Of note, we do this by source. So even if final action on PAC source, still "partial"
src.CLM_SRC_ID as sourceSk where sourceSk = '20000' -> tgt.outcome = "complete" "Set NCH outcome to complete.";
Expand Down Expand Up @@ -592,13 +591,20 @@ group createOrg(source src: Provider, target tgt: Organization){
src -> tgtId.system = "http://hl7.org/fhir/sid/us-npi" "set NPI system url";
npi -> tgtId.value = npi "set NPI";
} "Create NPI identifier";
src.PRVDR_LGL_NAME as orgName -> tgt.name = orgName;

src.PRVDR_LAST_OR_LGL_NAME as orgName -> tgt.name = orgName;
src.PRVDR_OSCAR_NUM as ccn -> tgt.identifier = create('Identifier') as tgtId then{
src -> tgtId.system = "http://terminology.hl7.org/NamingSystem/CCN" "set ccn system url";
src -> tgtId.value = ccn "set ccn";
} "Add OSCAR/CCN num";

src.prvdr_zip as zip5 -> tgt.address = create('Address') as tgtAddress then{
src.PRVDR_TAX_NUM as taxId -> tgt.identifier = create('Identifier') as tgtId then{
src -> tgtId.type = cc("http://terminology.hl7.org/CodeSystem/v2-0203","TAX") "set type";
src -> tgtId.system = "urn:oid:2.16.840.1.113883.4.4" "add system";
taxId -> tgtId.value = taxId "set tax Id";
} "Add tax Id";

src.CLM_BLG_PRVDR_ZIP5_CD as zip5 -> tgt.address = create('Address') as tgtAddress then{
zip5-> tgtAddress.postalCode = zip5 "add zip code";
} "Add zip code";
}
Expand All @@ -616,17 +622,23 @@ group createPractitioner(source src: Provider, target tgt: Practitioner){
src.PRVDR_SK as npi -> tgtId.value = npi "set NPI";
} "Create NPI identifier";

src.PRVDR_LAST_NAME as familyName -> tgt.name = create('HumanName') as tgtName then {
src.PRVDR_OSCAR_NUM as ccn -> tgt.identifier = create('Identifier') as tgtId then{
src -> tgtId.system = "http://terminology.hl7.org/NamingSystem/CCN" "set ccn system url";
src -> tgtId.value = ccn "set ccn";
} "Add OSCAR/CCN num";

src.PRVDR_TAX_NUM as taxId -> tgt.identifier = create('Identifier') as tgtId then{
src -> tgtId.type = cc("http://terminology.hl7.org/CodeSystem/v2-0203","TAX") "set type";
src -> tgtId.system = "urn:oid:2.16.840.1.113883.4.4" "add system";
taxId -> tgtId.value = taxId "set tax Id";
} "Add tax Id";

src.PRVDR_LAST_OR_LGL_NAME as familyName -> tgt.name = create('HumanName') as tgtName then {
familyName -> tgtName.family = familyName "set family name";
src.PRVDR_1ST_NAME as firstName -> tgtName.given = firstName "set first name";
src.PRVDR_MDL_NAME as middleName -> tgtName.given = middleName "set middle name";
} "Add name";

src.taxonomyCodes as taxonomyCodes -> tgt.qualification = create('BackboneElement') as tgtBBElement then{
taxonomyCodes as taxCode -> tgtBBElement.code = cc("http://nucc.org/provider-taxonomy", taxCode) "Add qualification";
} "Add taxonomy info to Practitioner resource.";

src.prvdr_zip as zip5 -> tgt.address = create('Address') as tgtAddress then{
src.CLM_BLG_PRVDR_ZIP5_CD as zip5 -> tgt.address = create('Address') as tgtAddress then{
zip5-> tgtAddress.postalCode = zip5 "add zip code";
} "Add zip code";
}
Expand Down
60 changes: 60 additions & 0 deletions apps/bfd-model-idr/maps/ExplanationOfBenefit-Helper.map
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,66 @@ conceptmap "FinalActionIndicator" {
s:N == t:"cancelled"
}

conceptmap "FED_PRVDR_SPCLTY_TO_TAXONOMY" {
prefix s = "https://hl7.org/fhir/string"
prefix t = "http://nucc.org/provider-taxonomy"

s:"1" == t:"208D00000X"
s:"2" == t:"208600000X"
s:"3" == t:"207K00000X"
s:"4" == t:"207Y00000X"
s:"5" == t:"207L00000X"
s:"6" == t:"207RC0000X"
s:"7" == t:"207N00000X"
s:"8" == t:"207Q00000X"
s:"9" == t:"208VP0014X"
s:"10" == t:"207RG0100X"
s:"11" == t:"207R00000X"
s:"12" == t:"204D00000X"
s:"13" == t:"2084N0400X"
s:"14" == t:"207T00000X"
s:"63" == t:"335V00000X"
s:"64" == t:"231H00000X"
s:"65" == t:"225100000X"
s:"66" == t:"207RR0500X"
s:"67" == t:"225X00000X"
s:"68" == t:"103TC0700X"
s:"69" == t:"291U00000X"
s:"70" == t:"193200000X"
s:"71" == t:"133V00000X"
s:"72" == t:"208VP0000X"
s:"74" == t:"261QX0203X"
s:"76" == t:"2086S0129X"
s:"77" == t:"2086S0129X"
s:"78" == t:"2086S0129X"
s:"79" == t:"207RA0401X"
s:"80" == t:"1041C0700X"
s:"81" == t:"207RC0200X"
s:"82" == t:"207RH0000X"
s:"83" == t:"207RH0003X"
s:"84" == t:"2083P0901X"
s:"85" == t:"204E00000X"
s:"86" == t:"2084N0600X"
s:"89" == t:"364S00000X"
s:"90" == t:"207RX0202X"
s:"91" == t:"2086X0206X"
s:"92" == t:"2085R0001X"
s:"93" == t:"207P00000X"
s:"94" == t:"2085R0204X"
s:"96" == t:"156FX1800X"
s:"97" == t:"363A00000X"
s:"98" == t:"207VX0201X"
s:"A0" == t:"282N00000X"
s:"A1" == t:"314000000X"
s:"A2" == t:"313M00000X"
s:"A3" == t:"313M00000X"
s:"A4" == t:"251E00000X"
s:"A5" == t:"333600000X"
s:"A6" == t:"332B00000X"
s:"A7" == t:"332B00000X"
s:"A8" == t:"332B00000X"
}

conceptmap "CLM_LTST_CLM_IND" {
prefix s = "https://hl7.org/fhir/string"
prefix t = "http://hl7.org/fhir/explanationofbenefit-status"
Expand Down
3 changes: 3 additions & 0 deletions apps/bfd-model-idr/maps/ExplanationOfBenefit-Pharmacy.map
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ group createEOBPharmacy(source src: ExplanationOfBenefitPharmacy, target tgt: BF
src.providerList as provider where provider.isDuplicate.exists().not() and provider.careTeamType.exists().not() and provider.NPI_TYPE = 2 -> tgt.contained = create('Organization') as tgtOrg then createOrg(provider, tgtOrg) "Add Organization";
} "Add billing provider";

//QLFYR_TYPE_CD logic goes here pending CMS decision.


src.providerList as pList where pList.careTeamType.exists() -> tgt then {
pList where pList.NPI_TYPE = 1 and pList.isDuplicate.exists().not() -> tgt.contained = create('Practitioner') as tgtPract then createPractitioner(pList, tgtPract) "Add Practitioner";
pList where pList.NPI_TYPE = 2 and pList.isDuplicate.exists().not() -> tgt.contained = create('Organization') as tgtOrg then createOrg(pList, tgtOrg) "Add Organization";
Expand Down
1 change: 1 addition & 0 deletions apps/bfd-model-idr/sample-data/EOB-Base-Sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
"CLM_MDCR_COINSRNC_AMT":"9.82",
"CLM_NCVRD_CHRG_AMT":"11.50",
"CLM_BLOOD_LBLTY_AMT":"121.10",
"CLM_BLG_PRVDR_TAX_NUM": "765432",
"institutionalComponents":{"CLM_HIPPS_MODEL_BNDLD_PYMT_AMT":"44.11","CLM_MDCR_HHA_TOT_VISIT_CNT":"2","CLM_MDCR_HOSPC_PRD_CNT":"1","CLM_MDCR_INSTNL_PRMRY_PYR_AMT":"50.50","CLM_MDCR_IP_LRD_USE_CNT":"7","CLM_INSTNL_PER_DIEM_AMT":"5.00","CLM_INSTNL_CVRD_DAY_CNT":"30","CLM_HIPPS_UNCOMPD_CARE_AMT":"15.00","CLM_MDCR_IP_PPS_DSPRPRTNT_AMT":"15.11","CLM_MDCR_IP_PPS_DRG_WT_NUM":"1.08","CLM_INSTNL_MDCR_COINS_DAY_CNT":"3","CLM_INSTNL_NCVRD_DAY_CNT":"4","CLM_MDCR_IP_PPS_EXCPTN_AMT":"11.50","CLM_MDCR_IP_PPS_CPTL_FSP_AMT":"11.81","CLM_MDCR_IP_PPS_CPTL_IME_AMT":"14.11","CLM_MDCR_IP_PPS_OUTLIER_AMT":"40.11","CLM_MDCR_IP_PPS_CPTL_HRMLS_AMT":"13.31","CLM_MDCR_IP_PPS_CPTL_TOT_AMT":"11.11","CLM_INSTNL_DRG_OUTLIER_AMT":"15.89","CLM_MDCR_IP_BENE_DDCTBL_AMT":"19.11","CLM_INSTNL_PRFNL_AMT":"93.22","CLM_FINL_STDZD_PYMT_AMT":"10.00","CLM_HAC_RDCTN_PYMT_AMT":"10.01","CLM_HIPPS_MODEL_BNDLD_PMT_AMT":"10.02","CLM_HIPPS_READMSN_RDCTN_AMT":"10.03","CLM_HIPPS_VBP_AMT":"10.04","CLM_INSTNL_LOW_VOL_PMT_AMT":"10.05","CLM_MDCR_IP_1ST_YR_RATE_AMT":"10.06","CLM_MDCR_IP_SCND_YR_RATE_AMT":"10.07","CLM_PPS_MD_WVR_STDZD_VAL_AMT":"10.08","CLM_SITE_NTRL_CST_BSD_PYMT_AMT":"10.09","CLM_SITE_NTRL_IP_PPS_PYMT_AMT":"10.10","CLM_SS_OUTLIER_STD_PYMT_AMT":"10.11"},
"PRVDR_ATNDG_PRVDR_NPI_NUM":"1942945159",
"PRVDR_OPRTG_PRVDR_NPI_NUM":"1942945159",
Expand Down