use of org.hl7.fhir.r4.model.ExplanationOfBenefit.TotalComponent in project beneficiary-fhir-data by CMSgov.
the class R4ExplanationOfBenefitResourceProviderIT method assertEobEquals.
/**
* Compares two ExplanationOfBenefit objects in detail while working around serialization issues
* like comparing "0" and "0.0" or creation differences like using "Quantity" vs "SimpleQuantity"
*
* @param expected the expected
* @param actual the actual
*/
private static void assertEobEquals(ExplanationOfBenefit expected, ExplanationOfBenefit actual) {
// ID in the bundle will have `ExplanationOfBenefit/` in front, so make sure the last bit
// matches up
assertTrue(actual.getId().endsWith(expected.getId()));
// Clean them out so we can do a deep compare later
actual.setId("");
expected.setId("");
// If there are any contained resources, they might have lastupdate times in them too
assertEquals(expected.getContained().size(), actual.getContained().size());
for (int i = 0; i < expected.getContained().size(); i++) {
Resource expectedContained = expected.getContained().get(i);
Resource actualContained = actual.getContained().get(i);
expectedContained.getMeta().setLastUpdated(null);
actualContained.getMeta().setLastUpdated(null);
// TODO: HAPI seems to be inserting the `#` in the ID of the contained element.
// This is incorrect according to the FHIR spec:
// https://build.fhir.org/references.html#contained
// This works around that problem
assertEquals("#" + expectedContained.getId(), actualContained.getId());
expectedContained.setId("");
actualContained.setId("");
}
// Dates are not easy to compare so just make sure they are there
assertNotNull(actual.getMeta().getLastUpdated());
// We compared all of meta that we care about so get it out of the way
expected.getMeta().setLastUpdated(null);
actual.getMeta().setLastUpdated(null);
// Created is required, but can't be compared
assertNotNull(actual.getCreated());
expected.setCreated(null);
actual.setCreated(null);
// Extensions may have `valueMoney` elements
assertEquals(expected.getExtension().size(), actual.getExtension().size());
for (int i = 0; i < expected.getExtension().size(); i++) {
Extension expectedEx = expected.getExtension().get(i);
Extension actualEx = actual.getExtension().get(i);
// We have to deal with Money objects separately
if (expectedEx.hasValue() && expectedEx.getValue() instanceof Money) {
assertTrue(actualEx.getValue() instanceof Money);
assertCurrencyEquals((Money) expectedEx.getValue(), (Money) actualEx.getValue());
// Now remove since we validated so we can compare the rest directly
expectedEx.setValue(null);
actualEx.setValue(null);
}
}
// SupportingInfo can have `valueQuantity` that has the 0 vs 0.0 issue
assertEquals(expected.getSupportingInfo().size(), actual.getSupportingInfo().size());
for (int i = 0; i < expected.getSupportingInfo().size(); i++) {
SupportingInformationComponent expectedSup = expected.getSupportingInfo().get(i);
SupportingInformationComponent actualSup = actual.getSupportingInfo().get(i);
// We have to deal with Money objects separately
if (expectedSup.hasValueQuantity()) {
assertTrue(actualSup.hasValueQuantity());
assertEquals(expectedSup.getValueQuantity().getValue().floatValue(), actualSup.getValueQuantity().getValue().floatValue(), 0.0);
// Now remove since we validated so we can compare the rest directly
expectedSup.setValue(null);
actualSup.setValue(null);
}
}
// line items
assertEquals(expected.getItem().size(), actual.getItem().size());
for (int i = 0; i < expected.getItem().size(); i++) {
ItemComponent expectedItem = expected.getItem().get(i);
ItemComponent actualItem = actual.getItem().get(i);
// Compare value directly because SimpleQuantity vs Quantity can't be compared
assertEquals(expectedItem.getQuantity().getValue().floatValue(), actualItem.getQuantity().getValue().floatValue(), 0.0);
expectedItem.setQuantity(null);
actualItem.setQuantity(null);
assertEquals(expectedItem.getAdjudication().size(), actualItem.getAdjudication().size());
for (int j = 0; j < expectedItem.getAdjudication().size(); j++) {
AdjudicationComponent expectedAdj = expectedItem.getAdjudication().get(j);
AdjudicationComponent actualAdj = actualItem.getAdjudication().get(j);
// Here is where we start getting into trouble with "0" vs "0.0", so we do this manually
if (expectedAdj.hasAmount()) {
assertNotNull(actualAdj.getAmount());
assertCurrencyEquals(expectedAdj.getAmount(), actualAdj.getAmount());
} else {
// If expected doesn't have an amount, actual shouldn't
assertFalse(actualAdj.hasAmount());
}
// We just checked manually, so null them out and check the rest of the fields
expectedAdj.setAmount(null);
actualAdj.setAmount(null);
}
}
// Total has the same problem with values
assertEquals(expected.getTotal().size(), actual.getTotal().size());
for (int i = 0; i < expected.getTotal().size(); i++) {
TotalComponent expectedTot = expected.getTotal().get(i);
TotalComponent actualTot = actual.getTotal().get(i);
if (expectedTot.hasAmount()) {
assertNotNull(actualTot.getAmount());
assertCurrencyEquals(expectedTot.getAmount(), actualTot.getAmount());
} else {
// If expected doesn't have an amount, actual shouldn't
assertFalse(actualTot.hasAmount());
}
expectedTot.setAmount(null);
actualTot.setAmount(null);
}
// Benefit Balance Financial components
assertEquals(expected.getBenefitBalance().size(), actual.getBenefitBalance().size());
for (int i = 0; i < expected.getBenefitBalance().size(); i++) {
BenefitBalanceComponent expectedBen = expected.getBenefitBalance().get(i);
BenefitBalanceComponent actualBen = actual.getBenefitBalance().get(i);
assertEquals(expectedBen.getFinancial().size(), actualBen.getFinancial().size());
for (int j = 0; j < expectedBen.getFinancial().size(); j++) {
BenefitComponent expectedFinancial = expectedBen.getFinancial().get(j);
BenefitComponent actualFinancial = actualBen.getFinancial().get(j);
// Are we dealing with Money?
if (expectedFinancial.hasUsedMoney()) {
assertNotNull(actualFinancial.getUsedMoney());
assertCurrencyEquals(expectedFinancial.getUsedMoney(), actualFinancial.getUsedMoney());
// Clean up
expectedFinancial.setUsed(null);
actualFinancial.setUsed(null);
}
}
}
// As does payment
if (expected.hasPayment()) {
assertTrue(actual.hasPayment());
assertCurrencyEquals(expected.getPayment().getAmount(), actual.getPayment().getAmount());
} else {
// If expected doesn't have an amount, actual shouldn't
assertFalse(actual.hasPayment());
}
expected.getPayment().setAmount(null);
actual.getPayment().setAmount(null);
// Now for the grand finale, we can do an `equalsDeep` on the rest
assertTrue(expected.equalsDeep(actual));
}
use of org.hl7.fhir.r4.model.ExplanationOfBenefit.TotalComponent in project beneficiary-fhir-data by CMSgov.
the class SNFClaimTransformerV2Test method shouldHaveClmTotChrgAmtTotal.
@Test
public void shouldHaveClmTotChrgAmtTotal() {
// Only one so just pull it directly and compare
TotalComponent total = eob.getTotalFirstRep();
TotalComponent compare = new TotalComponent().setCategory(new CodeableConcept().setCoding(Arrays.asList(new Coding("http://terminology.hl7.org/CodeSystem/adjudication", "submitted", "Submitted Amount"), new Coding("https://bluebutton.cms.gov/resources/codesystem/adjudication", "https://bluebutton.cms.gov/resources/variables/clm_tot_chrg_amt", "Claim Total Charge Amount")))).setAmount(new Money().setValue(5555.03).setCurrency(TransformerConstants.CODED_MONEY_USD));
assertTrue(compare.equalsDeep(total));
}
use of org.hl7.fhir.r4.model.ExplanationOfBenefit.TotalComponent in project synthea by synthetichealth.
the class FhirR4 method explanationOfBenefit.
/**
* Create an explanation of benefit resource for each claim, detailing insurance
* information.
*
* @param personEntry Entry for the person
* @param bundle The Bundle to add to
* @param encounterEntry The current Encounter
* @param claimEntry the Claim object
* @param person the person the health record belongs to
* @param encounter the current Encounter as an object
* @return the added entry
*/
private static BundleEntryComponent explanationOfBenefit(BundleEntryComponent personEntry, Bundle bundle, BundleEntryComponent encounterEntry, Person person, BundleEntryComponent claimEntry, Encounter encounter) {
ExplanationOfBenefit eob = new ExplanationOfBenefit();
eob.setStatus(org.hl7.fhir.r4.model.ExplanationOfBenefit.ExplanationOfBenefitStatus.ACTIVE);
eob.setType(new CodeableConcept().addCoding(new Coding().setSystem("http://terminology.hl7.org/CodeSystem/claim-type").setCode("professional").setDisplay("Professional")));
eob.setUse(Use.CLAIM);
eob.setOutcome(RemittanceOutcome.COMPLETE);
org.hl7.fhir.r4.model.Encounter encounterResource = (org.hl7.fhir.r4.model.Encounter) encounterEntry.getResource();
// according to CMS guidelines claims have 12 months to be
// billed, so we set the billable period to 1 year after
// services have ended (the encounter ends).
Calendar cal = Calendar.getInstance();
cal.setTime(encounterResource.getPeriod().getEnd());
cal.add(Calendar.YEAR, 1);
Period billablePeriod = new Period().setStart(encounterResource.getPeriod().getEnd()).setEnd(cal.getTime());
eob.setBillablePeriod(billablePeriod);
// cost is hardcoded to be USD in claim so this should be fine as well
Money totalCost = new Money();
totalCost.setCurrency("USD");
totalCost.setValue(encounter.claim.getTotalClaimCost());
TotalComponent total = eob.addTotal();
total.setAmount(totalCost);
Code submitted = new Code("http://terminology.hl7.org/CodeSystem/adjudication", "submitted", "Submitted Amount");
total.setCategory(mapCodeToCodeableConcept(submitted, "http://terminology.hl7.org/CodeSystem/adjudication"));
// Set References
eob.setPatient(new Reference(personEntry.getFullUrl()));
if (USE_US_CORE_IG) {
eob.setFacility(encounterResource.getLocationFirstRep().getLocation());
}
ServiceRequest referral = (ServiceRequest) new ServiceRequest().setStatus(ServiceRequest.ServiceRequestStatus.COMPLETED).setIntent(ServiceRequest.ServiceRequestIntent.ORDER).setSubject(new Reference(personEntry.getFullUrl())).setId("referral");
CodeableConcept primaryCareRole = new CodeableConcept().addCoding(new Coding().setCode("primary").setSystem("http://terminology.hl7.org/CodeSystem/claimcareteamrole").setDisplay("Primary Care Practitioner"));
Reference providerReference = new Reference().setDisplay("Unknown");
if (encounter.clinician != null) {
String practitionerFullUrl = TRANSACTION_BUNDLE ? ExportHelper.buildFhirNpiSearchUrl(encounter.clinician) : findPractitioner(encounter.clinician, bundle);
if (practitionerFullUrl != null) {
providerReference = new Reference(practitionerFullUrl);
}
} else if (encounter.provider != null) {
String providerUrl = TRANSACTION_BUNDLE ? ExportHelper.buildFhirSearchUrl("Location", encounter.provider.getResourceLocationID()) : findProviderUrl(encounter.provider, bundle);
if (providerUrl != null) {
providerReference = new Reference(providerUrl);
}
}
eob.setProvider(providerReference);
eob.addCareTeam(new ExplanationOfBenefit.CareTeamComponent().setSequence(1).setProvider(providerReference).setRole(primaryCareRole));
referral.setRequester(providerReference);
referral.addPerformer(providerReference);
eob.addContained(referral);
eob.setReferral(new Reference().setReference("#referral"));
// Get the insurance info at the time that the encounter occurred.
Payer payer = encounter.claim.payer;
Coverage coverage = new Coverage();
coverage.setId("coverage");
coverage.setStatus(CoverageStatus.ACTIVE);
coverage.setType(new CodeableConcept().setText(payer.getName()));
coverage.setBeneficiary(new Reference(personEntry.getFullUrl()));
coverage.addPayor(new Reference().setDisplay(payer.getName()));
eob.addContained(coverage);
ExplanationOfBenefit.InsuranceComponent insuranceComponent = new ExplanationOfBenefit.InsuranceComponent();
insuranceComponent.setFocal(true);
insuranceComponent.setCoverage(new Reference("#coverage").setDisplay(payer.getName()));
eob.addInsurance(insuranceComponent);
eob.setInsurer(new Reference().setDisplay(payer.getName()));
org.hl7.fhir.r4.model.Claim claim = (org.hl7.fhir.r4.model.Claim) claimEntry.getResource();
eob.addIdentifier().setSystem("https://bluebutton.cms.gov/resources/variables/clm_id").setValue(claim.getId());
// Hardcoded group id
eob.addIdentifier().setSystem("https://bluebutton.cms.gov/resources/identifier/claim-group").setValue("99999999999");
eob.setClaim(new Reference().setReference(claimEntry.getFullUrl()));
eob.setCreated(encounterResource.getPeriod().getEnd());
eob.setType(claim.getType());
List<ExplanationOfBenefit.DiagnosisComponent> eobDiag = new ArrayList<>();
for (org.hl7.fhir.r4.model.Claim.DiagnosisComponent claimDiagnosis : claim.getDiagnosis()) {
ExplanationOfBenefit.DiagnosisComponent diagnosisComponent = new ExplanationOfBenefit.DiagnosisComponent();
diagnosisComponent.setDiagnosis(claimDiagnosis.getDiagnosis());
diagnosisComponent.getType().add(new CodeableConcept().addCoding(new Coding().setCode("principal").setSystem("http://terminology.hl7.org/CodeSystem/ex-diagnosistype")));
diagnosisComponent.setSequence(claimDiagnosis.getSequence());
diagnosisComponent.setPackageCode(claimDiagnosis.getPackageCode());
eobDiag.add(diagnosisComponent);
}
eob.setDiagnosis(eobDiag);
List<ExplanationOfBenefit.ProcedureComponent> eobProc = new ArrayList<>();
for (ProcedureComponent proc : claim.getProcedure()) {
ExplanationOfBenefit.ProcedureComponent p = new ExplanationOfBenefit.ProcedureComponent();
p.setDate(proc.getDate());
p.setSequence(proc.getSequence());
p.setProcedure(proc.getProcedure());
}
eob.setProcedure(eobProc);
List<ExplanationOfBenefit.ItemComponent> eobItem = new ArrayList<>();
double totalPayment = 0;
// Get all the items info from the claim
for (ItemComponent item : claim.getItem()) {
ExplanationOfBenefit.ItemComponent itemComponent = new ExplanationOfBenefit.ItemComponent();
itemComponent.setSequence(item.getSequence());
itemComponent.setQuantity(item.getQuantity());
itemComponent.setUnitPrice(item.getUnitPrice());
itemComponent.setCareTeamSequence(item.getCareTeamSequence());
itemComponent.setDiagnosisSequence(item.getDiagnosisSequence());
itemComponent.setInformationSequence(item.getInformationSequence());
itemComponent.setNet(item.getNet());
itemComponent.setEncounter(item.getEncounter());
itemComponent.setServiced(encounterResource.getPeriod());
itemComponent.setCategory(new CodeableConcept().addCoding(new Coding().setSystem("https://bluebutton.cms.gov/resources/variables/line_cms_type_srvc_cd").setCode("1").setDisplay("Medical care")));
itemComponent.setProductOrService(item.getProductOrService());
// Location of service, can use switch statement based on
// encounter type
String code;
String display;
CodeableConcept location = new CodeableConcept();
EncounterType encounterType = EncounterType.fromString(encounter.type);
switch(encounterType) {
case AMBULATORY:
code = "21";
display = "Inpatient Hospital";
break;
case EMERGENCY:
code = "20";
display = "Urgent Care Facility";
break;
case INPATIENT:
code = "21";
display = "Inpatient Hospital";
break;
case URGENTCARE:
code = "20";
display = "Urgent Care Facility";
break;
case WELLNESS:
code = "19";
display = "Off Campus-Outpatient Hospital";
break;
default:
code = "21";
display = "Inpatient Hospital";
}
location.addCoding().setCode(code).setSystem("http://terminology.hl7.org/CodeSystem/ex-serviceplace").setDisplay(display);
itemComponent.setLocation(location);
// Adjudication
if (item.hasNet()) {
// Assume that the patient has already paid deductible and
// has 20/80 coinsurance
ExplanationOfBenefit.AdjudicationComponent coinsuranceAmount = new ExplanationOfBenefit.AdjudicationComponent();
coinsuranceAmount.getCategory().getCoding().add(new Coding().setCode("https://bluebutton.cms.gov/resources/variables/line_coinsrnc_amt").setSystem("https://bluebutton.cms.gov/resources/codesystem/adjudication").setDisplay("Line Beneficiary Coinsurance Amount"));
coinsuranceAmount.getAmount().setValue(// 20% coinsurance
0.2 * item.getNet().getValue().doubleValue()).setCurrency("USD");
ExplanationOfBenefit.AdjudicationComponent lineProviderAmount = new ExplanationOfBenefit.AdjudicationComponent();
lineProviderAmount.getCategory().getCoding().add(new Coding().setCode("https://bluebutton.cms.gov/resources/variables/line_prvdr_pmt_amt").setSystem("https://bluebutton.cms.gov/resources/codesystem/adjudication").setDisplay("Line Provider Payment Amount"));
lineProviderAmount.getAmount().setValue(0.8 * item.getNet().getValue().doubleValue()).setCurrency("USD");
// assume the allowed and submitted amounts are the same for now
ExplanationOfBenefit.AdjudicationComponent submittedAmount = new ExplanationOfBenefit.AdjudicationComponent();
submittedAmount.getCategory().getCoding().add(new Coding().setCode("https://bluebutton.cms.gov/resources/variables/line_sbmtd_chrg_amt").setSystem("https://bluebutton.cms.gov/resources/codesystem/adjudication").setDisplay("Line Submitted Charge Amount"));
submittedAmount.getAmount().setValue(item.getNet().getValue()).setCurrency("USD");
ExplanationOfBenefit.AdjudicationComponent allowedAmount = new ExplanationOfBenefit.AdjudicationComponent();
allowedAmount.getCategory().getCoding().add(new Coding().setCode("https://bluebutton.cms.gov/resources/variables/line_alowd_chrg_amt").setSystem("https://bluebutton.cms.gov/resources/codesystem/adjudication").setDisplay("Line Allowed Charge Amount"));
allowedAmount.getAmount().setValue(item.getNet().getValue()).setCurrency("USD");
ExplanationOfBenefit.AdjudicationComponent indicatorCode = new ExplanationOfBenefit.AdjudicationComponent();
indicatorCode.getCategory().getCoding().add(new Coding().setCode("https://bluebutton.cms.gov/resources/variables/line_prcsg_ind_cd").setSystem("https://bluebutton.cms.gov/resources/codesystem/adjudication").setDisplay("Line Processing Indicator Code"));
// assume deductible is 0
ExplanationOfBenefit.AdjudicationComponent deductibleAmount = new ExplanationOfBenefit.AdjudicationComponent();
deductibleAmount.getCategory().getCoding().add(new Coding().setCode("https://bluebutton.cms.gov/resources/variables/line_bene_ptb_ddctbl_amt").setSystem("https://bluebutton.cms.gov/resources/codesystem/adjudication").setDisplay("Line Beneficiary Part B Deductible Amount"));
deductibleAmount.getAmount().setValue(0).setCurrency("USD");
List<ExplanationOfBenefit.AdjudicationComponent> adjudicationComponents = new ArrayList<>();
adjudicationComponents.add(coinsuranceAmount);
adjudicationComponents.add(lineProviderAmount);
adjudicationComponents.add(submittedAmount);
adjudicationComponents.add(allowedAmount);
adjudicationComponents.add(deductibleAmount);
adjudicationComponents.add(indicatorCode);
itemComponent.setAdjudication(adjudicationComponents);
// the total payment is what the insurance ends up paying
totalPayment += 0.8 * item.getNet().getValue().doubleValue();
}
eobItem.add(itemComponent);
}
eob.setItem(eobItem);
// This will throw a validation error no matter what. The
// payment section is required, and it requires a value.
// The validator will complain that if there is a value, the payment
// needs a code, but it will also complain if there is a code.
// There is no way to resolve this error.
Money payment = new Money();
payment.setValue(totalPayment).setCurrency("USD");
eob.setPayment(new ExplanationOfBenefit.PaymentComponent().setAmount(payment));
return newEntry(person, bundle, eob);
}
use of org.hl7.fhir.r4.model.ExplanationOfBenefit.TotalComponent in project beneficiary-fhir-data by CMSgov.
the class CarrierClaimTransformerV2Test method shouldHaveClmTotChrgAmtTotal.
@Test
public void shouldHaveClmTotChrgAmtTotal() {
// Only one so just pull it directly and compare
TotalComponent total = eob.getTotalFirstRep();
TotalComponent compare = new TotalComponent().setCategory(new CodeableConcept().setCoding(Arrays.asList(new Coding("http://hl7.org/fhir/us/carin-bb/CodeSystem/C4BBAdjudication", "priorpayerpaid", "Prior payer paid"), new Coding("https://bluebutton.cms.gov/resources/codesystem/adjudication", "https://bluebutton.cms.gov/resources/variables/clm_tot_chrg_amt", "Claim Total Charge Amount")))).setAmount(new Money().setValue(0).setCurrency(TransformerConstants.CODED_MONEY_USD));
assertTrue(compare.equalsDeep(total));
}
Aggregations