Search in sources :

Example 1 with ClaimEntry

use of org.mitre.synthea.world.concepts.Claim.ClaimEntry in project synthea by synthetichealth.

the class BB2RIFExporter method exportInpatient.

/**
 * Export inpatient claims details for a single person.
 * @param person the person to export
 * @param startTime earliest claim date to export
 * @param stopTime end time of simulation
 * @throws IOException if something goes wrong
 */
private void exportInpatient(Person person, long startTime, long stopTime) throws IOException {
    HashMap<INPATIENT, String> fieldValues = new HashMap<>();
    boolean previousEmergency = false;
    for (HealthRecord.Encounter encounter : person.record.encounters) {
        if (encounter.stop < startTime) {
            continue;
        }
        boolean isInpatient = encounter.type.equals(EncounterType.INPATIENT.toString());
        boolean isEmergency = encounter.type.equals(EncounterType.EMERGENCY.toString());
        boolean isVA = (ProviderType.VETERAN == encounter.provider.type);
        // IHS facilities have valid 6 digit id, IHS centers don't
        boolean isIHSCenter = (ProviderType.IHS == encounter.provider.type) && encounter.provider.id.length() != 6;
        if (isVA || isIHSCenter || !(isInpatient || isEmergency)) {
            previousEmergency = false;
            continue;
        }
        long claimId = BB2RIFExporter.claimId.getAndDecrement();
        int claimGroupId = BB2RIFExporter.claimGroupId.getAndDecrement();
        long fiDocId = BB2RIFExporter.fiDocCntlNum.getAndDecrement();
        fieldValues.clear();
        staticFieldConfig.setValues(fieldValues, INPATIENT.class, person);
        // The REQUIRED fields
        fieldValues.put(INPATIENT.BENE_ID, (String) person.attributes.get(BB2_BENE_ID));
        fieldValues.put(INPATIENT.CLM_ID, "" + claimId);
        fieldValues.put(INPATIENT.CLM_GRP_ID, "" + claimGroupId);
        fieldValues.put(INPATIENT.FI_DOC_CLM_CNTL_NUM, "" + fiDocId);
        fieldValues.put(INPATIENT.CLM_FROM_DT, bb2DateFromTimestamp(encounter.start));
        fieldValues.put(INPATIENT.CLM_ADMSN_DT, bb2DateFromTimestamp(encounter.start));
        fieldValues.put(INPATIENT.CLM_THRU_DT, bb2DateFromTimestamp(encounter.stop));
        fieldValues.put(INPATIENT.NCH_BENE_DSCHRG_DT, bb2DateFromTimestamp(encounter.stop));
        fieldValues.put(INPATIENT.NCH_WKLY_PROC_DT, bb2DateFromTimestamp(ExportHelper.nextFriday(encounter.stop)));
        fieldValues.put(INPATIENT.PRVDR_NUM, encounter.provider.id);
        fieldValues.put(INPATIENT.AT_PHYSN_NPI, encounter.clinician.npi);
        fieldValues.put(INPATIENT.ORG_NPI_NUM, encounter.provider.npi);
        fieldValues.put(INPATIENT.OP_PHYSN_NPI, encounter.clinician.npi);
        fieldValues.put(INPATIENT.CLM_PMT_AMT, String.format("%.2f", encounter.claim.getTotalClaimCost()));
        if (encounter.claim.payer == Payer.getGovernmentPayer(HealthInsuranceModule.MEDICARE)) {
            fieldValues.put(INPATIENT.NCH_PRMRY_PYR_CLM_PD_AMT, "0");
        } else {
            fieldValues.put(INPATIENT.NCH_PRMRY_PYR_CLM_PD_AMT, String.format("%.2f", encounter.claim.getCoveredCost()));
        }
        fieldValues.put(INPATIENT.PRVDR_STATE_CD, locationMapper.getStateCode(encounter.provider.state));
        // PTNT_DSCHRG_STUS_CD: 1=home, 2=transfer, 3=SNF, 20=died, 30=still here
        String field = null;
        String patientStatus = null;
        if (encounter.ended) {
            // TODO 2=transfer if the next encounter is also inpatient
            field = "1";
            // discharged
            patientStatus = "A";
        } else {
            // the patient is still here
            field = "30";
            // still a patient
            patientStatus = "C";
        }
        if (!person.alive(encounter.stop)) {
            // the patient died before the encounter ended
            field = "20";
            // died
            patientStatus = "B";
        }
        fieldValues.put(INPATIENT.PTNT_DSCHRG_STUS_CD, field);
        fieldValues.put(INPATIENT.NCH_PTNT_STATUS_IND_CD, patientStatus);
        fieldValues.put(INPATIENT.CLM_TOT_CHRG_AMT, String.format("%.2f", encounter.claim.getTotalClaimCost()));
        if (isEmergency) {
            // emergency
            field = "1";
        } else if (previousEmergency) {
            // urgent
            field = "2";
        } else {
            // elective
            field = "3";
        }
        fieldValues.put(INPATIENT.CLM_IP_ADMSN_TYPE_CD, field);
        fieldValues.put(INPATIENT.NCH_BENE_IP_DDCTBL_AMT, String.format("%.2f", encounter.claim.getDeductiblePaid()));
        fieldValues.put(INPATIENT.NCH_BENE_PTA_COINSRNC_LBLTY_AM, String.format("%.2f", encounter.claim.getCoinsurancePaid()));
        fieldValues.put(INPATIENT.NCH_IP_NCVRD_CHRG_AMT, String.format("%.2f", encounter.claim.getPatientCost()));
        fieldValues.put(INPATIENT.NCH_IP_TOT_DDCTN_AMT, String.format("%.2f", encounter.claim.getPatientCost()));
        int days = (int) ((encounter.stop - encounter.start) / (1000 * 60 * 60 * 24));
        fieldValues.put(INPATIENT.CLM_UTLZTN_DAY_CNT, "" + days);
        if (days > 60) {
            field = "" + (days - 60);
        } else {
            field = "0";
        }
        fieldValues.put(INPATIENT.BENE_TOT_COINSRNC_DAYS_CNT, field);
        if (days > 60) {
            // days outlier
            field = "1";
        } else if (encounter.claim.getTotalClaimCost() > 100_000) {
            // cost outlier
            field = "2";
        } else {
            // no outlier
            field = "0";
        }
        fieldValues.put(INPATIENT.CLM_DRG_OUTLIER_STAY_CD, field);
        fieldValues.put(INPATIENT.REV_CNTR_TOT_CHRG_AMT, String.format("%.2f", encounter.claim.getCoveredCost()));
        fieldValues.put(INPATIENT.REV_CNTR_NCVRD_CHRG_AMT, String.format("%.2f", encounter.claim.getPatientCost()));
        // OPTIONAL FIELDS
        fieldValues.put(INPATIENT.RNDRNG_PHYSN_NPI, encounter.clinician.npi);
        String icdReasonCode = null;
        if (encounter.reason != null) {
            // values into the principle diagnoses code.
            if (conditionCodeMapper.canMap(encounter.reason.code)) {
                icdReasonCode = conditionCodeMapper.map(encounter.reason.code, person, true);
                fieldValues.put(INPATIENT.PRNCPAL_DGNS_CD, icdReasonCode);
                fieldValues.put(INPATIENT.ADMTG_DGNS_CD, icdReasonCode);
            }
        }
        // Use the active condition diagnoses to enter mapped values
        // into the diagnoses codes.
        List<String> presentOnAdmission = getDiagnosesCodes(person, encounter.start);
        List<String> mappedDiagnosisCodes = getDiagnosesCodes(person, encounter.stop);
        boolean noDiagnoses = mappedDiagnosisCodes.isEmpty();
        if (!noDiagnoses) {
            int smallest = Math.min(mappedDiagnosisCodes.size(), BB2RIFStructure.inpatientDxFields.length);
            for (int i = 0; i < smallest; i++) {
                INPATIENT[] dxField = BB2RIFStructure.inpatientDxFields[i];
                String dxCode = mappedDiagnosisCodes.get(i);
                fieldValues.put(dxField[0], dxCode);
                // 0=ICD10
                fieldValues.put(dxField[1], "0");
                String present = presentOnAdmission.contains(dxCode) ? "Y" : "N";
                fieldValues.put(dxField[2], present);
            }
            if (!fieldValues.containsKey(INPATIENT.PRNCPAL_DGNS_CD)) {
                fieldValues.put(INPATIENT.PRNCPAL_DGNS_CD, mappedDiagnosisCodes.get(0));
            }
        }
        if (fieldValues.containsKey(INPATIENT.PRNCPAL_DGNS_CD)) {
            String icdCode = fieldValues.get(INPATIENT.PRNCPAL_DGNS_CD);
            // Add a DRG code, if applicable
            if (drgCodeMapper.canMap(icdCode)) {
                fieldValues.put(INPATIENT.CLM_DRG_CD, drgCodeMapper.map(icdCode, person));
            }
            // Check for external code...
            setExternalCode(person, fieldValues, INPATIENT.PRNCPAL_DGNS_CD, INPATIENT.ICD_DGNS_E_CD1, INPATIENT.ICD_DGNS_E_VRSN_CD1, INPATIENT.CLM_E_POA_IND_SW1, presentOnAdmission);
            setExternalCode(person, fieldValues, INPATIENT.PRNCPAL_DGNS_CD, INPATIENT.FST_DGNS_E_CD, INPATIENT.FST_DGNS_E_VRSN_CD);
        }
        // Use the procedures in this encounter to enter mapped values
        boolean noProcedures = false;
        if (!encounter.procedures.isEmpty()) {
            List<HealthRecord.Procedure> mappableProcedures = new ArrayList<>();
            List<String> mappedProcedureCodes = new ArrayList<>();
            for (HealthRecord.Procedure procedure : encounter.procedures) {
                for (HealthRecord.Code code : procedure.codes) {
                    if (conditionCodeMapper.canMap(code.code)) {
                        mappableProcedures.add(procedure);
                        mappedProcedureCodes.add(conditionCodeMapper.map(code.code, person, true));
                        // take the first mappable code for each procedure
                        break;
                    }
                }
            }
            if (!mappableProcedures.isEmpty()) {
                int smallest = Math.min(mappableProcedures.size(), BB2RIFStructure.inpatientPxFields.length);
                for (int i = 0; i < smallest; i++) {
                    INPATIENT[] pxField = BB2RIFStructure.inpatientPxFields[i];
                    fieldValues.put(pxField[0], mappedProcedureCodes.get(i));
                    // 0=ICD10
                    fieldValues.put(pxField[1], "0");
                    fieldValues.put(pxField[2], bb2DateFromTimestamp(mappableProcedures.get(i).start));
                }
            } else {
                noProcedures = true;
            }
        }
        if (icdReasonCode == null && noDiagnoses && noProcedures) {
            // skip this encounter
            continue;
        }
        previousEmergency = isEmergency;
        synchronized (rifWriters.getOrCreateWriter(INPATIENT.class)) {
            int claimLine = 1;
            for (ClaimEntry lineItem : encounter.claim.items) {
                String hcpcsCode = null;
                if (lineItem.entry instanceof HealthRecord.Procedure) {
                    for (HealthRecord.Code code : lineItem.entry.codes) {
                        if (hcpcsCodeMapper.canMap(code.code)) {
                            hcpcsCode = hcpcsCodeMapper.map(code.code, person, true);
                            // take the first mappable code for each procedure
                            break;
                        }
                    }
                    fieldValues.remove(INPATIENT.REV_CNTR_NDC_QTY);
                    fieldValues.remove(INPATIENT.REV_CNTR_NDC_QTY_QLFR_CD);
                } else if (lineItem.entry instanceof HealthRecord.Medication) {
                    HealthRecord.Medication med = (HealthRecord.Medication) lineItem.entry;
                    if (med.administration) {
                        // Administration of medication
                        hcpcsCode = "T1502";
                        // 1 Unit
                        fieldValues.put(INPATIENT.REV_CNTR_NDC_QTY, "1");
                        // Unit
                        fieldValues.put(INPATIENT.REV_CNTR_NDC_QTY_QLFR_CD, "UN");
                    }
                }
                if (hcpcsCode == null) {
                    continue;
                }
                fieldValues.put(INPATIENT.CLM_LINE_NUM, Integer.toString(claimLine++));
                fieldValues.put(INPATIENT.HCPCS_CD, hcpcsCode);
                fieldValues.put(INPATIENT.REV_CNTR_UNIT_CNT, "" + Integer.max(1, days));
                fieldValues.put(INPATIENT.REV_CNTR_RATE_AMT, String.format("%.2f", (lineItem.cost / Integer.max(1, days))));
                fieldValues.put(INPATIENT.REV_CNTR_TOT_CHRG_AMT, String.format("%.2f", lineItem.cost));
                fieldValues.put(INPATIENT.REV_CNTR_NCVRD_CHRG_AMT, String.format("%.2f", lineItem.copay + lineItem.deductible + lineItem.pocket));
                if (lineItem.pocket == 0 && lineItem.deductible == 0) {
                    // Not subject to deductible or coinsurance
                    fieldValues.put(INPATIENT.REV_CNTR_DDCTBL_COINSRNC_CD, "3");
                } else if (lineItem.pocket > 0 && lineItem.deductible > 0) {
                    // Subject to deductible and coinsurance
                    fieldValues.put(INPATIENT.REV_CNTR_DDCTBL_COINSRNC_CD, "0");
                } else if (lineItem.pocket == 0) {
                    // Not subject to deductible
                    fieldValues.put(INPATIENT.REV_CNTR_DDCTBL_COINSRNC_CD, "1");
                } else {
                    // Not subject to coinsurance
                    fieldValues.put(INPATIENT.REV_CNTR_DDCTBL_COINSRNC_CD, "2");
                }
                rifWriters.writeValues(INPATIENT.class, fieldValues);
            }
            if (claimLine == 1) {
                // If claimLine still equals 1, then no line items were successfully added.
                // Add a single top-level entry.
                fieldValues.put(INPATIENT.CLM_LINE_NUM, Integer.toString(claimLine));
                // HCPCS 99221: "Inpatient hospital visits: Initial and subsequent"
                fieldValues.put(INPATIENT.HCPCS_CD, "99221");
                rifWriters.writeValues(INPATIENT.class, fieldValues);
            }
        }
    }
}
Also used : Medication(org.mitre.synthea.world.concepts.HealthRecord.Medication) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) ArrayList(java.util.ArrayList) ClaimEntry(org.mitre.synthea.world.concepts.Claim.ClaimEntry) HealthRecord(org.mitre.synthea.world.concepts.HealthRecord) Code(org.mitre.synthea.world.concepts.HealthRecord.Code) Medication(org.mitre.synthea.world.concepts.HealthRecord.Medication) INPATIENT(org.mitre.synthea.export.BB2RIFStructure.INPATIENT)

Example 2 with ClaimEntry

use of org.mitre.synthea.world.concepts.Claim.ClaimEntry in project synthea by synthetichealth.

the class BB2RIFExporter method exportHome.

/**
 * Export Home Health Agency visits for a single person.
 * @param person the person to export
 * @param startTime earliest claim date to export
 * @param stopTime end time of simulation
 * @throws IOException if something goes wrong
 */
private void exportHome(Person person, long startTime, long stopTime) throws IOException {
    HashMap<HHA, String> fieldValues = new HashMap<>();
    int homeVisits = 0;
    for (HealthRecord.Encounter encounter : person.record.encounters) {
        if (encounter.stop < startTime) {
            continue;
        }
        boolean isVA = (ProviderType.VETERAN == encounter.provider.type);
        boolean isHome = encounter.type.equals(EncounterType.HOME.toString());
        // IHS facilities have valid 6 digit id, IHS centers don't
        boolean isIHSCenter = (ProviderType.IHS == encounter.provider.type) && encounter.provider.id.length() != 6;
        if (isVA || isIHSCenter || !isHome) {
            continue;
        }
        homeVisits += 1;
        long claimId = BB2RIFExporter.claimId.getAndDecrement();
        int claimGroupId = BB2RIFExporter.claimGroupId.getAndDecrement();
        long fiDocId = BB2RIFExporter.fiDocCntlNum.getAndDecrement();
        fieldValues.clear();
        staticFieldConfig.setValues(fieldValues, HHA.class, person);
        // The REQUIRED fields
        fieldValues.put(HHA.BENE_ID, (String) person.attributes.get(BB2_BENE_ID));
        fieldValues.put(HHA.CLM_ID, "" + claimId);
        fieldValues.put(HHA.CLM_GRP_ID, "" + claimGroupId);
        fieldValues.put(HHA.FI_DOC_CLM_CNTL_NUM, "" + fiDocId);
        fieldValues.put(HHA.CLM_FROM_DT, bb2DateFromTimestamp(encounter.start));
        fieldValues.put(HHA.CLM_ADMSN_DT, bb2DateFromTimestamp(encounter.start));
        fieldValues.put(HHA.CLM_THRU_DT, bb2DateFromTimestamp(encounter.stop));
        fieldValues.put(HHA.NCH_WKLY_PROC_DT, bb2DateFromTimestamp(ExportHelper.nextFriday(encounter.stop)));
        fieldValues.put(HHA.PRVDR_NUM, encounter.provider.id);
        fieldValues.put(HHA.ORG_NPI_NUM, encounter.provider.npi);
        fieldValues.put(HHA.AT_PHYSN_NPI, encounter.clinician.npi);
        fieldValues.put(HHA.RNDRNG_PHYSN_NPI, encounter.clinician.npi);
        fieldValues.put(HHA.CLM_PMT_AMT, String.format("%.2f", encounter.claim.getCoveredCost()));
        if (encounter.claim.payer == Payer.getGovernmentPayer(HealthInsuranceModule.MEDICARE)) {
            fieldValues.put(HHA.NCH_PRMRY_PYR_CLM_PD_AMT, "0");
        } else {
            fieldValues.put(HHA.NCH_PRMRY_PYR_CLM_PD_AMT, String.format("%.2f", encounter.claim.getCoveredCost()));
        }
        fieldValues.put(HHA.PRVDR_STATE_CD, locationMapper.getStateCode(encounter.provider.state));
        fieldValues.put(HHA.CLM_TOT_CHRG_AMT, String.format("%.2f", encounter.claim.getTotalClaimCost()));
        fieldValues.put(HHA.CLM_HHA_TOT_VISIT_CNT, "" + homeVisits);
        int days = (int) ((encounter.stop - encounter.start) / (1000 * 60 * 60 * 24));
        if (days <= 0) {
            days = 1;
        }
        fieldValues.put(HHA.REV_CNTR_UNIT_CNT, "" + days);
        fieldValues.put(HHA.REV_CNTR_RATE_AMT, String.format("%.2f", (encounter.claim.getTotalClaimCost() / days)));
        fieldValues.put(HHA.REV_CNTR_PMT_AMT_AMT, String.format("%.2f", encounter.claim.getCoveredCost()));
        fieldValues.put(HHA.REV_CNTR_TOT_CHRG_AMT, String.format("%.2f", encounter.claim.getTotalClaimCost()));
        fieldValues.put(HHA.REV_CNTR_NCVRD_CHRG_AMT, String.format("%.2f", encounter.claim.getPatientCost()));
        if (encounter.reason != null) {
            // values into the principle diagnoses code.
            if (conditionCodeMapper.canMap(encounter.reason.code)) {
                String icdCode = conditionCodeMapper.map(encounter.reason.code, person, true);
                fieldValues.put(HHA.PRNCPAL_DGNS_CD, icdCode);
            }
        }
        // PTNT_DSCHRG_STUS_CD: 1=home, 2=transfer, 3=SNF, 20=died, 30=still here
        String dischargeStatus = null;
        if (encounter.ended) {
            // TODO 2=transfer if the next encounter is also inpatient
            dischargeStatus = "1";
        } else {
            // the patient is still here
            dischargeStatus = "30";
        }
        if (!person.alive(encounter.stop)) {
            // the patient died before the encounter ended
            dischargeStatus = "20";
        }
        fieldValues.put(HHA.PTNT_DSCHRG_STUS_CD, dischargeStatus);
        // Use the active condition diagnoses to enter mapped values
        // into the diagnoses codes.
        List<String> mappedDiagnosisCodes = getDiagnosesCodes(person, encounter.stop);
        if (mappedDiagnosisCodes.isEmpty()) {
            // skip this encounter
            continue;
        }
        int smallest = Math.min(mappedDiagnosisCodes.size(), BB2RIFStructure.homeDxFields.length);
        for (int i = 0; i < smallest; i++) {
            HHA[] dxField = BB2RIFStructure.homeDxFields[i];
            fieldValues.put(dxField[0], mappedDiagnosisCodes.get(i));
            // 0=ICD10
            fieldValues.put(dxField[1], "0");
        }
        if (!fieldValues.containsKey(HHA.PRNCPAL_DGNS_CD)) {
            fieldValues.put(HHA.PRNCPAL_DGNS_CD, mappedDiagnosisCodes.get(0));
        }
        // Check for external code...
        setExternalCode(person, fieldValues, HHA.PRNCPAL_DGNS_CD, HHA.ICD_DGNS_E_CD1, HHA.ICD_DGNS_E_VRSN_CD1);
        setExternalCode(person, fieldValues, HHA.PRNCPAL_DGNS_CD, HHA.FST_DGNS_E_CD, HHA.FST_DGNS_E_VRSN_CD);
        synchronized (rifWriters.getOrCreateWriter(HHA.class)) {
            int claimLine = 1;
            for (ClaimEntry lineItem : encounter.claim.items) {
                String hcpcsCode = null;
                if (lineItem.entry instanceof HealthRecord.Procedure) {
                    for (HealthRecord.Code code : lineItem.entry.codes) {
                        if (hcpcsCodeMapper.canMap(code.code)) {
                            hcpcsCode = hcpcsCodeMapper.map(code.code, person, true);
                            // take the first mappable code for each procedure
                            break;
                        }
                    }
                    fieldValues.remove(HHA.REV_CNTR_NDC_QTY);
                    fieldValues.remove(HHA.REV_CNTR_NDC_QTY_QLFR_CD);
                } else if (lineItem.entry instanceof HealthRecord.Medication) {
                    HealthRecord.Medication med = (HealthRecord.Medication) lineItem.entry;
                    if (med.administration) {
                        // Administration of medication
                        hcpcsCode = "T1502";
                        // 1 Unit
                        fieldValues.put(HHA.REV_CNTR_NDC_QTY, "1");
                        // Unit
                        fieldValues.put(HHA.REV_CNTR_NDC_QTY_QLFR_CD, "UN");
                    }
                }
                if (hcpcsCode == null) {
                    continue;
                }
                fieldValues.put(HHA.CLM_LINE_NUM, Integer.toString(claimLine++));
                fieldValues.put(HHA.REV_CNTR_DT, bb2DateFromTimestamp(lineItem.entry.start));
                fieldValues.put(HHA.HCPCS_CD, hcpcsCode);
                fieldValues.put(HHA.REV_CNTR_RATE_AMT, String.format("%.2f", (lineItem.cost / Integer.max(1, days))));
                fieldValues.put(HHA.REV_CNTR_PMT_AMT_AMT, String.format("%.2f", lineItem.coinsurance + lineItem.payer));
                fieldValues.put(HHA.REV_CNTR_TOT_CHRG_AMT, String.format("%.2f", lineItem.cost));
                fieldValues.put(HHA.REV_CNTR_NCVRD_CHRG_AMT, String.format("%.2f", lineItem.copay + lineItem.deductible + lineItem.pocket));
                if (lineItem.pocket == 0 && lineItem.deductible == 0) {
                    // Not subject to deductible or coinsurance
                    fieldValues.put(HHA.REV_CNTR_DDCTBL_COINSRNC_CD, "3");
                } else if (lineItem.pocket > 0 && lineItem.deductible > 0) {
                    // Subject to deductible and coinsurance
                    fieldValues.put(HHA.REV_CNTR_DDCTBL_COINSRNC_CD, "0");
                } else if (lineItem.pocket == 0) {
                    // Not subject to deductible
                    fieldValues.put(HHA.REV_CNTR_DDCTBL_COINSRNC_CD, "1");
                } else {
                    // Not subject to coinsurance
                    fieldValues.put(HHA.REV_CNTR_DDCTBL_COINSRNC_CD, "2");
                }
                rifWriters.writeValues(HHA.class, fieldValues);
            }
            if (claimLine == 1) {
                // If claimLine still equals 1, then no line items were successfully added.
                // Add a single top-level entry.
                fieldValues.put(HHA.CLM_LINE_NUM, Integer.toString(claimLine));
                fieldValues.put(HHA.REV_CNTR_DT, bb2DateFromTimestamp(encounter.start));
                // home health visit
                fieldValues.put(HHA.HCPCS_CD, "T1021");
                rifWriters.writeValues(HHA.class, fieldValues);
            }
        }
    }
}
Also used : HHA(org.mitre.synthea.export.BB2RIFStructure.HHA) Medication(org.mitre.synthea.world.concepts.HealthRecord.Medication) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) ClaimEntry(org.mitre.synthea.world.concepts.Claim.ClaimEntry) HealthRecord(org.mitre.synthea.world.concepts.HealthRecord) Code(org.mitre.synthea.world.concepts.HealthRecord.Code) Medication(org.mitre.synthea.world.concepts.HealthRecord.Medication)

Example 3 with ClaimEntry

use of org.mitre.synthea.world.concepts.Claim.ClaimEntry in project synthea by synthetichealth.

the class BB2RIFExporter method exportSNF.

/**
 * Export Home Health Agency visits for a single person.
 * @param person the person to export
 * @param startTime earliest claim date to export
 * @param stopTime end time of simulation
 * @throws IOException if something goes wrong
 */
private void exportSNF(Person person, long startTime, long stopTime) throws IOException {
    HashMap<SNF, String> fieldValues = new HashMap<>();
    boolean previousEmergency;
    boolean previousUrgent;
    for (HealthRecord.Encounter encounter : person.record.encounters) {
        if (encounter.stop < startTime) {
            continue;
        }
        boolean isVA = (ProviderType.VETERAN == encounter.provider.type);
        // IHS facilities have valid 6 digit id, IHS centers don't
        boolean isIHSCenter = (ProviderType.IHS == encounter.provider.type) && encounter.provider.id.length() != 6;
        boolean isSNF = encounter.type.equals(EncounterType.SNF.toString());
        if (isVA || isIHSCenter || !isSNF) {
            continue;
        }
        previousEmergency = encounter.type.equals(EncounterType.EMERGENCY.toString());
        previousUrgent = encounter.type.equals(EncounterType.URGENTCARE.toString());
        long claimId = BB2RIFExporter.claimId.getAndDecrement();
        int claimGroupId = BB2RIFExporter.claimGroupId.getAndDecrement();
        long fiDocId = BB2RIFExporter.fiDocCntlNum.getAndDecrement();
        fieldValues.clear();
        staticFieldConfig.setValues(fieldValues, SNF.class, person);
        // The REQUIRED Fields
        fieldValues.put(SNF.BENE_ID, (String) person.attributes.get(BB2_BENE_ID));
        fieldValues.put(SNF.CLM_ID, "" + claimId);
        fieldValues.put(SNF.CLM_GRP_ID, "" + claimGroupId);
        fieldValues.put(SNF.FI_DOC_CLM_CNTL_NUM, "" + fiDocId);
        fieldValues.put(SNF.CLM_FROM_DT, bb2DateFromTimestamp(encounter.start));
        fieldValues.put(SNF.CLM_ADMSN_DT, bb2DateFromTimestamp(encounter.start));
        fieldValues.put(SNF.CLM_THRU_DT, bb2DateFromTimestamp(encounter.stop));
        fieldValues.put(SNF.NCH_WKLY_PROC_DT, bb2DateFromTimestamp(ExportHelper.nextFriday(encounter.stop)));
        fieldValues.put(SNF.PRVDR_NUM, encounter.provider.id);
        fieldValues.put(SNF.ORG_NPI_NUM, encounter.provider.npi);
        fieldValues.put(SNF.AT_PHYSN_NPI, encounter.clinician.npi);
        fieldValues.put(SNF.OP_PHYSN_NPI, encounter.clinician.npi);
        fieldValues.put(SNF.RNDRNG_PHYSN_NPI, encounter.clinician.npi);
        fieldValues.put(SNF.CLM_PMT_AMT, String.format("%.2f", encounter.claim.getCoveredCost()));
        if (encounter.claim.payer == Payer.getGovernmentPayer(HealthInsuranceModule.MEDICARE)) {
            fieldValues.put(SNF.NCH_PRMRY_PYR_CLM_PD_AMT, "0");
        } else {
            fieldValues.put(SNF.NCH_PRMRY_PYR_CLM_PD_AMT, String.format("%.2f", encounter.claim.getCoveredCost()));
        }
        fieldValues.put(SNF.PRVDR_STATE_CD, locationMapper.getStateCode(encounter.provider.state));
        fieldValues.put(SNF.CLM_TOT_CHRG_AMT, String.format("%.2f", encounter.claim.getTotalClaimCost()));
        if (previousEmergency) {
            fieldValues.put(SNF.CLM_IP_ADMSN_TYPE_CD, "1");
        } else if (previousUrgent) {
            fieldValues.put(SNF.CLM_IP_ADMSN_TYPE_CD, "2");
        } else {
            fieldValues.put(SNF.CLM_IP_ADMSN_TYPE_CD, "3");
        }
        fieldValues.put(SNF.NCH_BENE_IP_DDCTBL_AMT, String.format("%.2f", encounter.claim.getDeductiblePaid()));
        fieldValues.put(SNF.NCH_BENE_PTA_COINSRNC_LBLTY_AM, String.format("%.2f", encounter.claim.getCoinsurancePaid()));
        fieldValues.put(SNF.NCH_IP_NCVRD_CHRG_AMT, String.format("%.2f", encounter.claim.getPatientCost()));
        fieldValues.put(SNF.NCH_IP_TOT_DDCTN_AMT, String.format("%.2f", encounter.claim.getDeductiblePaid() + encounter.claim.getCoinsurancePaid()));
        int days = (int) ((encounter.stop - encounter.start) / (1000 * 60 * 60 * 24));
        if (days <= 0) {
            days = 1;
        }
        fieldValues.put(SNF.CLM_UTLZTN_DAY_CNT, "" + days);
        // first 21 days no coinsurance
        int coinDays = days - 21;
        if (coinDays < 0) {
            coinDays = 0;
        }
        fieldValues.put(SNF.BENE_TOT_COINSRNC_DAYS_CNT, "" + coinDays);
        fieldValues.put(SNF.REV_CNTR_UNIT_CNT, "" + days);
        fieldValues.put(SNF.REV_CNTR_RATE_AMT, String.format("%.2f", (encounter.claim.getTotalClaimCost() / days)));
        fieldValues.put(SNF.REV_CNTR_TOT_CHRG_AMT, String.format("%.2f", encounter.claim.getTotalClaimCost()));
        fieldValues.put(SNF.REV_CNTR_NCVRD_CHRG_AMT, String.format("%.2f", encounter.claim.getPatientCost()));
        // OPTIONAL CODES
        if (encounter.reason != null) {
            // values into the principle diagnoses code.
            if (conditionCodeMapper.canMap(encounter.reason.code)) {
                String icdCode = conditionCodeMapper.map(encounter.reason.code, person, true);
                fieldValues.put(SNF.PRNCPAL_DGNS_CD, icdCode);
                fieldValues.put(SNF.ADMTG_DGNS_CD, icdCode);
                if (drgCodeMapper.canMap(icdCode)) {
                    fieldValues.put(SNF.CLM_DRG_CD, drgCodeMapper.map(icdCode, person));
                }
            }
        }
        // PTNT_DSCHRG_STUS_CD: 1=home, 2=transfer, 3=SNF, 20=died, 30=still here
        // NCH_PTNT_STUS_IND_CD: A = Discharged, B = Died, C = Still a patient
        String dischargeStatus = null;
        String patientStatus = null;
        String dischargeDate = null;
        if (encounter.ended) {
            // TODO 2=transfer if the next encounter is also inpatient
            dischargeStatus = "1";
            // discharged
            patientStatus = "A";
            dischargeDate = bb2DateFromTimestamp(ExportHelper.nextFriday(encounter.stop));
        } else {
            // the patient is still here
            dischargeStatus = "30";
            // still a patient
            patientStatus = "C";
        }
        if (!person.alive(encounter.stop)) {
            // the patient died before the encounter ended
            dischargeStatus = "20";
            // died
            patientStatus = "B";
            dischargeDate = bb2DateFromTimestamp(ExportHelper.nextFriday(encounter.stop));
        }
        fieldValues.put(SNF.PTNT_DSCHRG_STUS_CD, dischargeStatus);
        fieldValues.put(SNF.NCH_PTNT_STATUS_IND_CD, patientStatus);
        if (dischargeDate != null) {
            fieldValues.put(SNF.NCH_BENE_DSCHRG_DT, dischargeDate);
        }
        // Use the active condition diagnoses to enter mapped values
        // into the diagnoses codes.
        List<String> mappedDiagnosisCodes = getDiagnosesCodes(person, encounter.stop);
        boolean noDiagnoses = mappedDiagnosisCodes.isEmpty();
        if (!noDiagnoses) {
            int smallest = Math.min(mappedDiagnosisCodes.size(), BB2RIFStructure.snfDxFields.length);
            for (int i = 0; i < smallest; i++) {
                SNF[] dxField = BB2RIFStructure.snfDxFields[i];
                fieldValues.put(dxField[0], mappedDiagnosisCodes.get(i));
                // 0=ICD10
                fieldValues.put(dxField[1], "0");
            }
            if (!fieldValues.containsKey(SNF.PRNCPAL_DGNS_CD)) {
                fieldValues.put(SNF.PRNCPAL_DGNS_CD, mappedDiagnosisCodes.get(0));
                fieldValues.put(SNF.ADMTG_DGNS_CD, mappedDiagnosisCodes.get(0));
            }
        }
        // Check for external code...
        setExternalCode(person, fieldValues, SNF.PRNCPAL_DGNS_CD, SNF.ICD_DGNS_E_CD1, SNF.ICD_DGNS_E_VRSN_CD1);
        setExternalCode(person, fieldValues, SNF.PRNCPAL_DGNS_CD, SNF.FST_DGNS_E_CD, SNF.FST_DGNS_E_VRSN_CD);
        // Use the procedures in this encounter to enter mapped values
        boolean noProcedures = false;
        if (!encounter.procedures.isEmpty()) {
            List<HealthRecord.Procedure> mappableProcedures = new ArrayList<>();
            List<String> mappedProcedureCodes = new ArrayList<>();
            for (HealthRecord.Procedure procedure : encounter.procedures) {
                for (HealthRecord.Code code : procedure.codes) {
                    if (conditionCodeMapper.canMap(code.code)) {
                        mappableProcedures.add(procedure);
                        mappedProcedureCodes.add(conditionCodeMapper.map(code.code, person, true));
                        // take the first mappable code for each procedure
                        break;
                    }
                }
            }
            if (!mappableProcedures.isEmpty()) {
                int smallest = Math.min(mappableProcedures.size(), BB2RIFStructure.snfPxFields.length);
                for (int i = 0; i < smallest; i++) {
                    SNF[] pxField = BB2RIFStructure.snfPxFields[i];
                    fieldValues.put(pxField[0], mappedProcedureCodes.get(i));
                    // 0=ICD10
                    fieldValues.put(pxField[1], "0");
                    fieldValues.put(pxField[2], bb2DateFromTimestamp(mappableProcedures.get(i).start));
                }
            } else {
                noProcedures = true;
            }
        }
        if (noDiagnoses && noProcedures) {
            // skip this encounter
            continue;
        }
        synchronized (rifWriters.getOrCreateWriter(SNF.class)) {
            int claimLine = 1;
            for (ClaimEntry lineItem : encounter.claim.items) {
                String hcpcsCode = null;
                if (lineItem.entry instanceof HealthRecord.Procedure) {
                    for (HealthRecord.Code code : lineItem.entry.codes) {
                        if (hcpcsCodeMapper.canMap(code.code)) {
                            hcpcsCode = hcpcsCodeMapper.map(code.code, person, true);
                            // take the first mappable code for each procedure
                            break;
                        }
                    }
                    fieldValues.remove(SNF.REV_CNTR_NDC_QTY);
                    fieldValues.remove(SNF.REV_CNTR_NDC_QTY_QLFR_CD);
                } else if (lineItem.entry instanceof HealthRecord.Medication) {
                    HealthRecord.Medication med = (HealthRecord.Medication) lineItem.entry;
                    if (med.administration) {
                        // Administration of medication
                        hcpcsCode = "T1502";
                        // 1 Unit
                        fieldValues.put(SNF.REV_CNTR_NDC_QTY, "1");
                        // Unit
                        fieldValues.put(SNF.REV_CNTR_NDC_QTY_QLFR_CD, "UN");
                    }
                }
                if (hcpcsCode == null) {
                    continue;
                }
                fieldValues.put(SNF.CLM_LINE_NUM, Integer.toString(claimLine++));
                fieldValues.put(SNF.HCPCS_CD, hcpcsCode);
                fieldValues.put(SNF.REV_CNTR_RATE_AMT, String.format("%.2f", (lineItem.cost / Integer.max(1, days))));
                fieldValues.put(SNF.REV_CNTR_TOT_CHRG_AMT, String.format("%.2f", lineItem.cost));
                fieldValues.put(SNF.REV_CNTR_NCVRD_CHRG_AMT, String.format("%.2f", lineItem.copay + lineItem.deductible + lineItem.pocket));
                if (lineItem.pocket == 0 && lineItem.deductible == 0) {
                    // Not subject to deductible or coinsurance
                    fieldValues.put(SNF.REV_CNTR_DDCTBL_COINSRNC_CD, "3");
                } else if (lineItem.pocket > 0 && lineItem.deductible > 0) {
                    // Subject to deductible and coinsurance
                    fieldValues.put(SNF.REV_CNTR_DDCTBL_COINSRNC_CD, "0");
                } else if (lineItem.pocket == 0) {
                    // Not subject to deductible
                    fieldValues.put(SNF.REV_CNTR_DDCTBL_COINSRNC_CD, "1");
                } else {
                    // Not subject to coinsurance
                    fieldValues.put(SNF.REV_CNTR_DDCTBL_COINSRNC_CD, "2");
                }
                rifWriters.writeValues(SNF.class, fieldValues);
            }
            if (claimLine == 1) {
                // If claimLine still equals 1, then no line items were successfully added.
                // Add a single top-level entry.
                fieldValues.put(SNF.CLM_LINE_NUM, Integer.toString(claimLine));
                // G0299: "direct skilled nursing services of a registered nurse (RN) in the home health
                // or hospice setting"
                fieldValues.put(SNF.HCPCS_CD, "G0299");
                rifWriters.writeValues(SNF.class, fieldValues);
            }
        }
    }
}
Also used : Medication(org.mitre.synthea.world.concepts.HealthRecord.Medication) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) ArrayList(java.util.ArrayList) SNF(org.mitre.synthea.export.BB2RIFStructure.SNF) ClaimEntry(org.mitre.synthea.world.concepts.Claim.ClaimEntry) HealthRecord(org.mitre.synthea.world.concepts.HealthRecord) Code(org.mitre.synthea.world.concepts.HealthRecord.Code) Medication(org.mitre.synthea.world.concepts.HealthRecord.Medication)

Example 4 with ClaimEntry

use of org.mitre.synthea.world.concepts.Claim.ClaimEntry in project synthea by synthetichealth.

the class BB2RIFExporter method exportDME.

/**
 * Export DME details for a single person.
 * @param person the person to export
 * @param startTime earliest claim date to export
 * @param stopTime end time of simulation
 * @throws IOException if something goes wrong
 */
private void exportDME(Person person, long startTime, long stopTime) throws IOException {
    HashMap<DME, String> fieldValues = new HashMap<>();
    for (HealthRecord.Encounter encounter : person.record.encounters) {
        if (encounter.stop < startTime) {
            continue;
        }
        boolean isVA = (ProviderType.VETERAN == encounter.provider.type);
        // IHS facilities have valid 6 digit id, IHS centers don't
        boolean isIHSCenter = (ProviderType.IHS == encounter.provider.type) && encounter.provider.id.length() != 6;
        if (isVA || isIHSCenter) {
            continue;
        }
        long claimId = BB2RIFExporter.claimId.getAndDecrement();
        int claimGroupId = BB2RIFExporter.claimGroupId.getAndDecrement();
        long carrClmId = BB2RIFExporter.carrClmCntlNum.getAndDecrement();
        double latestHemoglobin = 0;
        for (HealthRecord.Observation observation : encounter.observations) {
            if (observation.containsCode("718-7", "http://loinc.org")) {
                latestHemoglobin = (double) observation.value;
            }
        }
        fieldValues.clear();
        staticFieldConfig.setValues(fieldValues, DME.class, person);
        // complex fields that could not easily be set using cms_field_values.tsv
        fieldValues.put(DME.CLM_ID, "" + claimId);
        fieldValues.put(DME.CLM_GRP_ID, "" + claimGroupId);
        fieldValues.put(DME.CARR_CLM_CNTL_NUM, "" + carrClmId);
        fieldValues.put(DME.BENE_ID, (String) person.attributes.get(BB2_BENE_ID));
        fieldValues.put(DME.LINE_HCT_HGB_RSLT_NUM, "" + latestHemoglobin);
        fieldValues.put(DME.CARR_NUM, getCarrier(encounter.provider.state, CARRIER.CARR_NUM));
        fieldValues.put(DME.NCH_WKLY_PROC_DT, bb2DateFromTimestamp(ExportHelper.nextFriday(encounter.stop)));
        fieldValues.put(DME.PRVDR_NUM, encounter.provider.id);
        fieldValues.put(DME.PRVDR_NPI, encounter.provider.npi);
        fieldValues.put(DME.RFR_PHYSN_NPI, encounter.clinician.npi);
        fieldValues.put(DME.PRVDR_SPCLTY, ClinicianSpecialty.getCMSProviderSpecialtyCode((String) encounter.clinician.attributes.get(Clinician.SPECIALTY)));
        fieldValues.put(DME.PRVDR_STATE_CD, locationMapper.getStateCode(encounter.provider.state));
        fieldValues.put(DME.TAX_NUM, bb2TaxId((String) encounter.clinician.attributes.get(Person.IDENTIFIER_SSN)));
        fieldValues.put(DME.DMERC_LINE_PRCNG_STATE_CD, locationMapper.getStateCode((String) person.attributes.get(Person.STATE)));
        fieldValues.put(DME.LINE_1ST_EXPNS_DT, bb2DateFromTimestamp(encounter.start));
        fieldValues.put(DME.LINE_LAST_EXPNS_DT, bb2DateFromTimestamp(encounter.stop));
        fieldValues.put(DME.LINE_SRVC_CNT, "" + encounter.claim.items.size());
        // OPTIONAL
        if (encounter.reason != null) {
            // values into the principle diagnoses code.
            if (conditionCodeMapper.canMap(encounter.reason.code)) {
                String icdCode = conditionCodeMapper.map(encounter.reason.code, person, true);
                fieldValues.put(DME.PRNCPAL_DGNS_CD, icdCode);
                fieldValues.put(DME.LINE_ICD_DGNS_CD, icdCode);
            }
        }
        // Use the active condition diagnoses to enter mapped values
        // into the diagnoses codes.
        List<String> mappedDiagnosisCodes = getDiagnosesCodes(person, encounter.stop);
        if (mappedDiagnosisCodes.isEmpty()) {
            // skip this encounter
            continue;
        }
        int smallest = Math.min(mappedDiagnosisCodes.size(), BB2RIFStructure.dmeDxFields.length);
        for (int i = 0; i < smallest; i++) {
            DME[] dxField = BB2RIFStructure.dmeDxFields[i];
            fieldValues.put(dxField[0], mappedDiagnosisCodes.get(i));
            // 0=ICD10
            fieldValues.put(dxField[1], "0");
        }
        if (!fieldValues.containsKey(DME.PRNCPAL_DGNS_CD)) {
            fieldValues.put(DME.PRNCPAL_DGNS_CD, mappedDiagnosisCodes.get(0));
            fieldValues.put(DME.LINE_ICD_DGNS_CD, mappedDiagnosisCodes.get(0));
        }
        // preprocess some subtotals...
        ClaimEntry subTotals = (encounter.claim).new ClaimEntry(null);
        for (ClaimEntry lineItem : encounter.claim.items) {
            if (lineItem.entry instanceof Device || lineItem.entry instanceof Supply) {
                subTotals.addCosts(lineItem);
            }
        }
        fieldValues.put(DME.CARR_CLM_CASH_DDCTBL_APLD_AMT, String.format("%.2f", subTotals.deductible));
        fieldValues.put(DME.CARR_CLM_PRMRY_PYR_PD_AMT, String.format("%.2f", subTotals.coinsurance + subTotals.payer));
        fieldValues.put(DME.NCH_CARR_CLM_ALOWD_AMT, String.format("%.2f", subTotals.cost - subTotals.adjustment));
        fieldValues.put(DME.NCH_CARR_CLM_SBMTD_CHRG_AMT, String.format("%.2f", subTotals.cost));
        fieldValues.put(DME.NCH_CLM_PRVDR_PMT_AMT, String.format("%.2f", subTotals.coinsurance + subTotals.payer));
        fieldValues.put(DME.CLM_PMT_AMT, String.format("%.2f", subTotals.coinsurance + subTotals.payer));
        synchronized (rifWriters.getOrCreateWriter(DME.class)) {
            int lineNum = 1;
            // Now generate the line items...
            for (ClaimEntry lineItem : encounter.claim.items) {
                if (!(lineItem.entry instanceof Device || lineItem.entry instanceof Supply)) {
                    continue;
                }
                if (lineItem.entry instanceof Supply) {
                    Supply supply = (Supply) lineItem.entry;
                    fieldValues.put(DME.DMERC_LINE_MTUS_CNT, "" + supply.quantity);
                } else {
                    fieldValues.put(DME.DMERC_LINE_MTUS_CNT, "");
                }
                if (!dmeCodeMapper.canMap(lineItem.entry.codes.get(0).code)) {
                    System.err.println(" *** Possibly Missing DME Code: " + lineItem.entry.codes.get(0).code + " " + lineItem.entry.codes.get(0).display);
                    continue;
                }
                fieldValues.put(DME.CLM_FROM_DT, bb2DateFromTimestamp(lineItem.entry.start));
                fieldValues.put(DME.CLM_THRU_DT, bb2DateFromTimestamp(lineItem.entry.start));
                String hcpcsCode = dmeCodeMapper.map(lineItem.entry.codes.get(0).code, person);
                fieldValues.put(DME.HCPCS_CD, hcpcsCode);
                if (betosCodeMapper.canMap(hcpcsCode)) {
                    fieldValues.put(DME.BETOS_CD, betosCodeMapper.map(hcpcsCode, person));
                } else {
                    fieldValues.put(DME.BETOS_CD, "");
                }
                fieldValues.put(DME.LINE_CMS_TYPE_SRVC_CD, dmeCodeMapper.map(lineItem.entry.codes.get(0).code, DME.LINE_CMS_TYPE_SRVC_CD.toString().toLowerCase(), person));
                fieldValues.put(DME.LINE_BENE_PTB_DDCTBL_AMT, String.format("%.2f", lineItem.deductible));
                fieldValues.put(DME.LINE_COINSRNC_AMT, String.format("%.2f", lineItem.getCoinsurancePaid()));
                fieldValues.put(DME.LINE_BENE_PMT_AMT, String.format("%.2f", lineItem.copay + lineItem.deductible + lineItem.pocket));
                fieldValues.put(DME.LINE_PRVDR_PMT_AMT, String.format("%.2f", lineItem.coinsurance + lineItem.payer));
                fieldValues.put(DME.LINE_SBMTD_CHRG_AMT, String.format("%.2f", lineItem.cost));
                fieldValues.put(DME.LINE_ALOWD_CHRG_AMT, String.format("%.2f", lineItem.cost - lineItem.adjustment));
                fieldValues.put(DME.LINE_PRMRY_ALOWD_CHRG_AMT, String.format("%.2f", lineItem.cost - lineItem.adjustment));
                fieldValues.put(DME.LINE_NCH_PMT_AMT, String.format("%.2f", lineItem.coinsurance + lineItem.payer));
                // set the line number and write out field values
                fieldValues.put(DME.LINE_NUM, Integer.toString(lineNum++));
                rifWriters.writeValues(DME.class, fieldValues);
            }
        }
    }
}
Also used : HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) Device(org.mitre.synthea.world.concepts.HealthRecord.Device) Supply(org.mitre.synthea.world.concepts.HealthRecord.Supply) ClaimEntry(org.mitre.synthea.world.concepts.Claim.ClaimEntry) HealthRecord(org.mitre.synthea.world.concepts.HealthRecord) DME(org.mitre.synthea.export.BB2RIFStructure.DME)

Example 5 with ClaimEntry

use of org.mitre.synthea.world.concepts.Claim.ClaimEntry in project synthea by synthetichealth.

the class BB2RIFExporter method exportOutpatient.

/**
 * Export outpatient claims details for a single person.
 * @param person the person to export
 * @param startTime earliest claim date to export
 * @param stopTime end time of simulation
 * @throws IOException if something goes wrong
 */
private void exportOutpatient(Person person, long startTime, long stopTime) throws IOException {
    HashMap<OUTPATIENT, String> fieldValues = new HashMap<>();
    for (HealthRecord.Encounter encounter : person.record.encounters) {
        if (encounter.stop < startTime) {
            continue;
        }
        boolean isAmbulatory = encounter.type.equals(EncounterType.AMBULATORY.toString());
        boolean isOutpatient = encounter.type.equals(EncounterType.OUTPATIENT.toString());
        boolean isUrgent = encounter.type.equals(EncounterType.URGENTCARE.toString());
        boolean isPrimary = (ProviderType.PRIMARY == encounter.provider.type);
        // IHS facilities have valid 6 digit id, IHS centers don't
        boolean isIHSCenter = (ProviderType.IHS == encounter.provider.type) && encounter.provider.id.length() != 6;
        boolean isVA = (ProviderType.VETERAN == encounter.provider.type);
        if (isVA || isIHSCenter || isPrimary || isUrgent || !(isAmbulatory || isOutpatient)) {
            continue;
        }
        long claimId = BB2RIFExporter.claimId.getAndDecrement();
        int claimGroupId = BB2RIFExporter.claimGroupId.getAndDecrement();
        long fiDocId = BB2RIFExporter.fiDocCntlNum.getAndDecrement();
        staticFieldConfig.setValues(fieldValues, OUTPATIENT.class, person);
        // The REQUIRED fields
        fieldValues.put(OUTPATIENT.BENE_ID, (String) person.attributes.get(BB2_BENE_ID));
        fieldValues.put(OUTPATIENT.CLM_ID, "" + claimId);
        fieldValues.put(OUTPATIENT.CLM_GRP_ID, "" + claimGroupId);
        fieldValues.put(OUTPATIENT.FI_DOC_CLM_CNTL_NUM, "" + fiDocId);
        fieldValues.put(OUTPATIENT.CLM_FROM_DT, bb2DateFromTimestamp(encounter.start));
        fieldValues.put(OUTPATIENT.CLM_THRU_DT, bb2DateFromTimestamp(encounter.stop));
        fieldValues.put(OUTPATIENT.NCH_WKLY_PROC_DT, bb2DateFromTimestamp(ExportHelper.nextFriday(encounter.stop)));
        fieldValues.put(OUTPATIENT.PRVDR_NUM, encounter.provider.id);
        fieldValues.put(OUTPATIENT.AT_PHYSN_NPI, encounter.clinician.npi);
        fieldValues.put(OUTPATIENT.RNDRNG_PHYSN_NPI, encounter.clinician.npi);
        fieldValues.put(OUTPATIENT.ORG_NPI_NUM, encounter.provider.npi);
        fieldValues.put(OUTPATIENT.OP_PHYSN_NPI, encounter.clinician.npi);
        fieldValues.put(OUTPATIENT.CLM_PMT_AMT, String.format("%.2f", encounter.claim.getTotalClaimCost()));
        if (encounter.claim.payer == Payer.getGovernmentPayer(HealthInsuranceModule.MEDICARE)) {
            fieldValues.put(OUTPATIENT.NCH_PRMRY_PYR_CLM_PD_AMT, "0");
        } else {
            fieldValues.put(OUTPATIENT.NCH_PRMRY_PYR_CLM_PD_AMT, String.format("%.2f", encounter.claim.getCoveredCost()));
        }
        fieldValues.put(OUTPATIENT.PRVDR_STATE_CD, locationMapper.getStateCode(encounter.provider.state));
        // PTNT_DSCHRG_STUS_CD: 1=home, 2=transfer, 3=SNF, 20=died, 30=still here
        String field = null;
        if (encounter.ended) {
            field = "1";
        } else {
            // the patient is still here
            field = "30";
        }
        if (!person.alive(encounter.stop)) {
            // the patient died before the encounter ended
            field = "20";
        }
        fieldValues.put(OUTPATIENT.PTNT_DSCHRG_STUS_CD, field);
        fieldValues.put(OUTPATIENT.CLM_TOT_CHRG_AMT, String.format("%.2f", encounter.claim.getTotalClaimCost()));
        fieldValues.put(OUTPATIENT.CLM_OP_PRVDR_PMT_AMT, String.format("%.2f", encounter.claim.getTotalClaimCost()));
        fieldValues.put(OUTPATIENT.REV_CNTR_TOT_CHRG_AMT, String.format("%.2f", encounter.claim.getCoveredCost()));
        fieldValues.put(OUTPATIENT.REV_CNTR_NCVRD_CHRG_AMT, String.format("%.2f", encounter.claim.getPatientCost()));
        fieldValues.put(OUTPATIENT.NCH_BENE_PTB_DDCTBL_AMT, String.format("%.2f", encounter.claim.getDeductiblePaid()));
        fieldValues.put(OUTPATIENT.REV_CNTR_CASH_DDCTBL_AMT, String.format("%.2f", encounter.claim.getDeductiblePaid()));
        fieldValues.put(OUTPATIENT.REV_CNTR_COINSRNC_WGE_ADJSTD_C, String.format("%.2f", encounter.claim.getCoinsurancePaid()));
        fieldValues.put(OUTPATIENT.REV_CNTR_PMT_AMT_AMT, String.format("%.2f", encounter.claim.getTotalClaimCost()));
        fieldValues.put(OUTPATIENT.REV_CNTR_PRVDR_PMT_AMT, String.format("%.2f", encounter.claim.getTotalClaimCost()));
        fieldValues.put(OUTPATIENT.REV_CNTR_PTNT_RSPNSBLTY_PMT, String.format("%.2f", encounter.claim.getDeductiblePaid() + encounter.claim.getCoinsurancePaid()));
        fieldValues.put(OUTPATIENT.REV_CNTR_RDCD_COINSRNC_AMT, String.format("%.2f", encounter.claim.getCoinsurancePaid()));
        String icdReasonCode = null;
        if (encounter.reason != null) {
            // values into the principle diagnoses code.
            if (conditionCodeMapper.canMap(encounter.reason.code)) {
                icdReasonCode = conditionCodeMapper.map(encounter.reason.code, person, true);
                fieldValues.put(OUTPATIENT.PRNCPAL_DGNS_CD, icdReasonCode);
            }
        }
        // Use the active condition diagnoses to enter mapped values
        // into the diagnoses codes.
        List<String> mappedDiagnosisCodes = getDiagnosesCodes(person, encounter.stop);
        boolean noDiagnoses = mappedDiagnosisCodes.isEmpty();
        if (!noDiagnoses) {
            int smallest = Math.min(mappedDiagnosisCodes.size(), BB2RIFStructure.outpatientDxFields.length);
            for (int i = 0; i < smallest; i++) {
                OUTPATIENT[] dxField = BB2RIFStructure.outpatientDxFields[i];
                fieldValues.put(dxField[0], mappedDiagnosisCodes.get(i));
                // 0=ICD10
                fieldValues.put(dxField[1], "0");
            }
            if (!fieldValues.containsKey(OUTPATIENT.PRNCPAL_DGNS_CD)) {
                fieldValues.put(OUTPATIENT.PRNCPAL_DGNS_CD, mappedDiagnosisCodes.get(0));
            }
        }
        // Check for external code...
        setExternalCode(person, fieldValues, OUTPATIENT.PRNCPAL_DGNS_CD, OUTPATIENT.ICD_DGNS_E_CD1, OUTPATIENT.ICD_DGNS_E_VRSN_CD1);
        setExternalCode(person, fieldValues, OUTPATIENT.PRNCPAL_DGNS_CD, OUTPATIENT.FST_DGNS_E_CD, OUTPATIENT.FST_DGNS_E_VRSN_CD);
        // Use the procedures in this encounter to enter mapped values
        boolean noProcedures = false;
        if (!encounter.procedures.isEmpty()) {
            List<HealthRecord.Procedure> mappableProcedures = new ArrayList<>();
            List<String> mappedProcedureCodes = new ArrayList<>();
            for (HealthRecord.Procedure procedure : encounter.procedures) {
                for (HealthRecord.Code code : procedure.codes) {
                    if (conditionCodeMapper.canMap(code.code)) {
                        mappableProcedures.add(procedure);
                        mappedProcedureCodes.add(conditionCodeMapper.map(code.code, person, true));
                        // take the first mappable code for each procedure
                        break;
                    }
                }
            }
            if (!mappableProcedures.isEmpty()) {
                int smallest = Math.min(mappableProcedures.size(), BB2RIFStructure.outpatientPxFields.length);
                for (int i = 0; i < smallest; i++) {
                    OUTPATIENT[] pxField = BB2RIFStructure.outpatientPxFields[i];
                    fieldValues.put(pxField[0], mappedProcedureCodes.get(i));
                    // 0=ICD10
                    fieldValues.put(pxField[1], "0");
                    fieldValues.put(pxField[2], bb2DateFromTimestamp(mappableProcedures.get(i).start));
                }
            } else {
                noProcedures = true;
            }
        }
        if (icdReasonCode == null && noDiagnoses && noProcedures) {
            // skip this encounter
            continue;
        }
        synchronized (rifWriters.getOrCreateWriter(OUTPATIENT.class)) {
            int claimLine = 1;
            for (ClaimEntry lineItem : encounter.claim.items) {
                String hcpcsCode = null;
                if (lineItem.entry instanceof HealthRecord.Procedure) {
                    for (HealthRecord.Code code : lineItem.entry.codes) {
                        if (hcpcsCodeMapper.canMap(code.code)) {
                            hcpcsCode = hcpcsCodeMapper.map(code.code, person, true);
                            // take the first mappable code for each procedure
                            break;
                        }
                    }
                    fieldValues.remove(OUTPATIENT.REV_CNTR_IDE_NDC_UPC_NUM);
                    fieldValues.remove(OUTPATIENT.REV_CNTR_NDC_QTY);
                    fieldValues.remove(OUTPATIENT.REV_CNTR_NDC_QTY_QLFR_CD);
                } else if (lineItem.entry instanceof HealthRecord.Medication) {
                    HealthRecord.Medication med = (HealthRecord.Medication) lineItem.entry;
                    if (med.administration) {
                        // Administration of medication
                        hcpcsCode = "T1502";
                        String ndcCode = medicationCodeMapper.map(med.codes.get(0).code, person);
                        fieldValues.put(OUTPATIENT.REV_CNTR_IDE_NDC_UPC_NUM, ndcCode);
                        // 1 Unit
                        fieldValues.put(OUTPATIENT.REV_CNTR_NDC_QTY, "1");
                        // Unit
                        fieldValues.put(OUTPATIENT.REV_CNTR_NDC_QTY_QLFR_CD, "UN");
                    }
                }
                if (hcpcsCode == null) {
                    continue;
                }
                fieldValues.put(OUTPATIENT.CLM_LINE_NUM, Integer.toString(claimLine++));
                fieldValues.put(OUTPATIENT.REV_CNTR_DT, bb2DateFromTimestamp(lineItem.entry.start));
                fieldValues.put(OUTPATIENT.HCPCS_CD, hcpcsCode);
                fieldValues.put(OUTPATIENT.REV_CNTR_RATE_AMT, String.format("%.2f", (lineItem.cost)));
                fieldValues.put(OUTPATIENT.REV_CNTR_PMT_AMT_AMT, String.format("%.2f", lineItem.coinsurance + lineItem.payer));
                fieldValues.put(OUTPATIENT.REV_CNTR_TOT_CHRG_AMT, String.format("%.2f", lineItem.cost));
                fieldValues.put(OUTPATIENT.REV_CNTR_NCVRD_CHRG_AMT, String.format("%.2f", lineItem.copay + lineItem.deductible + lineItem.pocket));
                rifWriters.writeValues(OUTPATIENT.class, fieldValues);
            }
            if (claimLine == 1) {
                // If claimLine still equals 1, then no line items were successfully added.
                // Add a single top-level entry.
                fieldValues.put(OUTPATIENT.CLM_LINE_NUM, Integer.toString(claimLine));
                fieldValues.put(OUTPATIENT.REV_CNTR_DT, bb2DateFromTimestamp(encounter.start));
                // 99241: "Office consultation for a new or established patient"
                fieldValues.put(OUTPATIENT.HCPCS_CD, "99241");
                rifWriters.writeValues(OUTPATIENT.class, fieldValues);
            }
        }
    }
}
Also used : Medication(org.mitre.synthea.world.concepts.HealthRecord.Medication) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) ArrayList(java.util.ArrayList) ClaimEntry(org.mitre.synthea.world.concepts.Claim.ClaimEntry) HealthRecord(org.mitre.synthea.world.concepts.HealthRecord) OUTPATIENT(org.mitre.synthea.export.BB2RIFStructure.OUTPATIENT) Code(org.mitre.synthea.world.concepts.HealthRecord.Code) Medication(org.mitre.synthea.world.concepts.HealthRecord.Medication)

Aggregations

HashMap (java.util.HashMap)7 LinkedHashMap (java.util.LinkedHashMap)7 ClaimEntry (org.mitre.synthea.world.concepts.Claim.ClaimEntry)7 HealthRecord (org.mitre.synthea.world.concepts.HealthRecord)7 Code (org.mitre.synthea.world.concepts.HealthRecord.Code)6 Medication (org.mitre.synthea.world.concepts.HealthRecord.Medication)6 ArrayList (java.util.ArrayList)3 CARRIER (org.mitre.synthea.export.BB2RIFStructure.CARRIER)1 DME (org.mitre.synthea.export.BB2RIFStructure.DME)1 HHA (org.mitre.synthea.export.BB2RIFStructure.HHA)1 HOSPICE (org.mitre.synthea.export.BB2RIFStructure.HOSPICE)1 INPATIENT (org.mitre.synthea.export.BB2RIFStructure.INPATIENT)1 OUTPATIENT (org.mitre.synthea.export.BB2RIFStructure.OUTPATIENT)1 SNF (org.mitre.synthea.export.BB2RIFStructure.SNF)1 Device (org.mitre.synthea.world.concepts.HealthRecord.Device)1 Supply (org.mitre.synthea.world.concepts.HealthRecord.Supply)1