Generated from CodeTour
Insurance Type
export interface EligibilitySubmission {
id?: number;
patient_info: PatientInfo;
insurance_id: string;
insurance_type?: string;
group_id: string | null;
payer?: Payer;
relationship_to_policy_holder: PolicyHolderRelationship;
policy_holder?: PatientInfo;
eligibility_state?: string;
}
src/react/src/ui/Covid/insuranceTypes.ts
Does most of the fetching from API/Context and sends values to inner Form as props
Expand Code
const InsuranceForm: React.FC<InsuranceFormProps> = (props) => {
const {
patientFirstName,
patientLastName,
patientBirthday,
patientGender,
patientGenderIdentity,
eligibilitySubmission,
kaiserInfo,
onSubmit,
onCancel,
formIsSubmitting = false,
isMinorForm,
allowSharingWithKaiser = true,
partnerName,
appointmentType = AppointmentType.MOLECULAR,
onsite,
collectStateId,
uninsuredAttestationIsRequired,
cdphContentIsVisible,
} = props;
const initialValues: EligibilitySubmission = eligibilitySubmission ?? {
patient_info: {
first_name: patientFirstName ?? '',
last_name: patientLastName ?? '',
birthday: toIsoDate(patientBirthday),
gender: patientGender !== 'O' ? patientGender : undefined,
gender_identity: (patientGenderIdentity as GenderIdentity) ?? '',
},
insurance_id: '',
group_id: '',
relationship_to_policy_holder: PolicyHolderRelationship.SELF,
};
const initialShareWithKaiser = kaiserInfo ?? {
agree_to_share_information_with_kaiser: allowSharingWithKaiser,
kaiser_medical_record_number: '',
};
const [insuranceCompanies, setInsuranceCompanies] = useState<Payer[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [createEncryptedContent] = useCreateEncryptedContent();
const [createReimbursementIdentifier] = useCreateReimbursementIdentifier();
const getInsuranceCompanies = useCallback(async () => {
// We want an error to throw if this request fails
const response = await APIClient.get('/api/v1/insurance_payers', {
params: { format: 'json', page_size: 2000, ordering: 'name' },
});
setInsuranceCompanies(response.results);
}, []);
useEffect(() => {
getInsuranceCompanies();
}, [getInsuranceCompanies]);
const checkEligibility = async (submission: {
eligibilitySubmission?: EligibilitySubmission;
kaiserInfo?: KaiserInfo;
stateIdSubmission?: StateIdSubmission;
}) => {
const { eligibilitySubmission, kaiserInfo, stateIdSubmission } = submission;
if (eligibilitySubmission) {
setIsLoading(true);
const url = '/api/v1/external_eligibility_submission';
const payload = {
...eligibilitySubmission,
};
try {
// TODO (lbaab): handle invalid eligibility states
if (payload.relationship_to_policy_holder === PolicyHolderRelationship.SELF) {
// The api expects policy holder not to be sent if the relationship is Self
delete payload.policy_holder;
}
const response: EligibilitySubmission = await APIClient.post(url, { json: payload });
setIsLoading(false);
await onSubmit?.({ eligibilitySubmission: response, kaiserInfo });
} catch (errors) {
requestErrorToast(errors);
setIsLoading(false);
}
} else if (stateIdSubmission) {
setIsLoading(true);
try {
const { id: encryptedContentId } = await createEncryptedContent(stateIdSubmission.idNumber);
const reimbursementIdentifier = await createReimbursementIdentifier({
identifierType: stateIdSubmission.idType,
identifierState: stateIdSubmission.state,
identifierId: encryptedContentId,
});
setIsLoading(false);
await onSubmit?.({ stateIdSubmission: reimbursementIdentifier });
} catch (errors) {
requestErrorToast(errors);
setIsLoading(false);
}
} else {
onSubmit?.({});
}
};
if (!insuranceCompanies) {
return null;
}
return (
<Form
eligibilitySubmission={initialValues}
insuranceCompanies={insuranceCompanies}
onSubmit={checkEligibility}
onCancel={onCancel}
isLoading={isLoading || formIsSubmitting}
kaiserInfo={initialShareWithKaiser}
hasInsurance
isMinorForm={isMinorForm}
allowSharingWithKaiser={allowSharingWithKaiser}
partnerName={partnerName}
appointmentType={appointmentType}
onsite={onsite}
uninsuredAttestationIsRequired={uninsuredAttestationIsRequired}
collectStateId={collectStateId}
cdphContentIsVisible={cdphContentIsVisible}
/>
);
};
src/react/src/ui/Covid/SignUp/components/InsuranceForm/InsuranceForm.tsx
Renders Formik form
Expand Code
const Form: React.FC<Props> = (props) => {
const {
insuranceCompanies,
eligibilitySubmission,
onSubmit,
onCancel,
hasInsurance,
kaiserInfo,
isMinorForm,
allowSharingWithKaiser = true,
partnerName,
appointmentType,
onsite,
uninsuredAttestationIsRequired = false,
collectStateId = false,
cdphContentIsVisible = false,
} = props;
const {
patient_info,
id,
group_id,
insurance_id,
relationship_to_policy_holder,
eligibility_state,
} = eligibilitySubmission;
const intl = useIntl();
const initialValues = {
id,
insurance_id,
group_id,
eligibility_state,
...getHasInsuranceInitialValues(hasInsurance),
...getInsurancePlanInitialValues(
eligibilitySubmission,
kaiserInfo,
relationship_to_policy_holder,
allowSharingWithKaiser
),
...getStateIdInitialValues(),
};
const { cdphInsuranceUsage } = getCdphInsuranceUsageContent(intl);
return (
<Formik
initialValues={initialValues}
onSubmit={(formikValues) => {
const {
has_insurance,
policy_holder: formikPolicyHolder,
payer,
agree_to_share_information_with_kaiser,
kaiser_medical_record_number,
insurance_type,
state_id_type,
state_id_state,
state_id_number,
...otherFormikValues
} = formikValues;
if (has_insurance === HasInsuranceOption.Yes) {
onSubmit({
eligibilitySubmission: {
patient_info,
policy_holder: {
...formikPolicyHolder,
birthday: formikPolicyHolder.birthday ? toIsoDate(formikPolicyHolder.birthday) : '',
},
payer: payer ? payer : undefined,
insurance_type,
...otherFormikValues,
},
kaiserInfo: {
agree_to_share_information_with_kaiser: isKaiser(payer)
? agree_to_share_information_with_kaiser
: undefined,
kaiser_medical_record_number:
isKaiser(payer) && agree_to_share_information_with_kaiser
? kaiser_medical_record_number
: undefined,
},
});
} else if (collectStateId && state_id_type && state_id_type !== StateIdType.None) {
onSubmit({
stateIdSubmission: {
idType: convertStateIdValueToReimbursementIdentifierType(state_id_type)!,
state: state_id_state!.id,
idNumber: state_id_number,
},
});
} else {
onSubmit({});
}
}}
validationSchema={Yup.object({
...getHasInsuranceValidationSchema(intl, isMinorForm, uninsuredAttestationIsRequired),
...getPlanInfoValidationSchema(intl, isMinorForm),
...getStateIdValidation(intl, collectStateId),
})}
>
{({ values }) => (
<FormikForm className={classes.Form}>
<HasInsuranceQuestion
onsite={onsite}
partnerName={partnerName}
isMinor={isMinorForm}
appointmentType={appointmentType}
uninsuredAttestationIsRequired={uninsuredAttestationIsRequired}
/>
{values.has_insurance === HasInsuranceOption.Yes && (
<InsurancePlanInfo
insuranceCompanies={insuranceCompanies}
isMinorForm={isMinorForm}
allowSharingWithKaiser={allowSharingWithKaiser}
partnerName={partnerName}
appointmentType={appointmentType}
/>
)}
{collectStateId &&
[HasInsuranceOption.YesButWontProvide, HasInsuranceOption.No].includes(
values.has_insurance
) && <StateIdForm />}
{cdphContentIsVisible && (
<>
<Spacer height={16} />
<Text>
<MarkdownContainer markdown={cdphInsuranceUsage} />
</Text>
</>
)}
<Spacer height={24} />
<FormikSubmitButton isLoading={props.isLoading} data-testid="SubmitButton">
<FormattedMessage
description="Covid sign up insurance collection continue button content"
defaultMessage="Continue"
/>
</FormikSubmitButton>
{onCancel && (
<>
<Spacer height={24} />
<ActionLink onClick={onCancel}>Cancel</ActionLink>
</>
)}
</FormikForm>
)}
</Formik>
);
};
src/react/src/ui/Covid/SignUp/components/InsuranceForm/Form.tsx
Sends data for COVID Activation, including insurance
export const useCovidActivationMutation = () => {
const { fetchUser } = useAuthContext();
return useMutation(
(covidActivationApiProps: CovidActivationApiProps) => {
const {
userProfile,
sampleIdentifier,
hasApprovedHipaaAuth,
consents,
population_short_name,
insuranceEligibilitySubmission,
kaiserInfo,
} = covidActivationApiProps;
return APIClient.post(COVID_ACTIVATION_URL, {
json: {
has_approved_hipaa_auth: hasApprovedHipaaAuth,
...userProfile,
...sampleIdentifier,
consent_type: 'covid',
consents,
population_short_name,
insurance_eligibility_submission: insuranceEligibilitySubmission,
kaiser_info: kaiserInfo,
},
});
},
{
throwOnError: true,
onSuccess: () => fetchUser(),
}
);
};
src/react/src/ui/Covid/SignUp/Activation/api.ts
Need to pinpoint when insurance is handled
Expand Code
def activate_covid_sample(request):
"""
Activates (and purchases if needed) a COVID sample in the user self-activation flow
"""
with handle_activation_errors():
consents = request.data.get('consents', [])
user, sample, _user_data, sample_data = _activation_details_from_request(
request, user_serializer_class=CovidKitRegistrationUserSerializer
)
if sample.is_activated():
raise DetailedBadRequestError(
{'kit_barcode': ["This sample has already been activated."]}
)
eligibility = get_activation_eligibility_for_sample(request.user, sample)
if not eligibility:
raise DetailedForbiddenRequestError(
{'user': ["You are not eligible to activate a COVID-19 test"]}
)
# Enforces that the population-specific ui flow matches the population of the eligibility used to activate
population_short_name = request.data.get('population_short_name')
if population_short_name:
request_population = Population.objects.get(
population_settings__short_name=population_short_name
)
if eligibility.population != request_population:
raise DetailedForbiddenRequestError(
{
'population_short_name': [
"Eligibility does not match the population selected for activation"
]
}
)
# For the COVID self-activation flow we assume the collection address was the one provided in the flow
sample.collection_country = 'US'
sample.collection_in_us = 'Y'
sample.collection_state = user.contact_address.state
# Update collection data will only update the sample type if it is not already a detailed swab type.
# (i.e., if a swab type is already rhinostic, it will not update to dry anterior nares.)
sample.update_collection_data(SampleType.COVID_DRY_ANTERIOR_NARES_SWAB)
insurance_eligibility_submission_data = request.data.get(
'insurance_eligibility_submission'
)
def create_physician_order():
# We assume the population has been set up with exactly one physician order template we can use for all orders
population_settings = eligibility.population.population_settings
physician_order_template = (
population_settings.physician_order_templates.get(
test_requested=TEST_NAME_COVID_19
)
)
patient_profile = PatientProfile.instantiate_from_user(user)
patient_profile.save()
physician_order = physician_order_template.create_physician_order(
patient_profile=patient_profile, sample=sample
)
if insurance_eligibility_submission_data:
insurance_eligibility_submission = (
InsuranceEligibilitySubmission.objects.get(
id=insurance_eligibility_submission_data.get('id')
)
)
_handle_insurance_eligibility_submission(
insurance_eligibility_submission,
physician_order,
user,
)
return physician_order
_handle_flow_specific_activation(eligibility, sample, create_physician_order)
kaiser_info = request.data.get('kaiser_info')
if kaiser_info and kaiser_info.get('agree_to_share_information_with_kaiser'):
kaiser_mrn = kaiser_info.get('kaiser_medical_record_number')
kit_order = sample.kit_orders.get(test_type=TEST_NAME_COVID_19)
_handle_kaiser_shared_provider(kit_order, kaiser_mrn)
create_covid_sign_up_custom_consents(consents, user=user)
consent_type = sample_data.get('consent_type')
assert consent_type == CovidConsent.type
consent_and_activate(sample, user, consent_type)
# COVID users should not receive discovery and marketing emails for now
user.emailsettings.unsubscribe_all()
user.emailsettings.save()
# We can not refresh from db since state field is protected
sample = Sample.objects.get(id=sample.id)
return Response(SampleSerializer(sample).data)
Reuses Covid Activation form
Expand Code
const InsurancePage = () => {
const { onSubmitPageForm } = useSubmitPageForm();
const { paths } = useFlowContext();
const { pathname } = useLocation();
const {
vaccinationAppointment,
vaccineFlowConfig,
appointmentIsPosting,
} = useRegistrationContext();
const {
first_name,
last_name,
birthday,
sex,
insurance_eligibility_submission,
appointment_type,
} = vaccinationAppointment;
if (!paths || !vaccineFlowConfig) {
return null;
}
if (!appointment_type) {
logger.logException(new Error('Unexpected: appointment_type is not set.'));
return <Redirect to={paths[0]} />;
}
const onSubmit = async (submission: { eligibilitySubmission?: EligibilitySubmission }) => {
onSubmitPageForm({
insurance_eligibility_submission: submission.eligibilitySubmission,
});
};
return (
<PageContainer helmetTitle={VACCINE_REGISTRATION_HELMET_TITLE}>
<LeftJustifiedLayout>
<SignUpForSomeoneElseContainer />
<Title>
<FormattedMessage
description="Vaccine registration insurance page title"
defaultMessage="Let us know about your health insurance."
/>
</Title>
<Spacer height={40} />
<VaccineInsuranceBillingInfoPane />
<Spacer height={48} />
<InsuranceForm
onSubmit={onSubmit}
formIsSubmitting={appointmentIsPosting}
patientFirstName={first_name}
patientLastName={last_name}
patientBirthday={fromIsoDate(birthday!)}
patientGender={sex}
eligibilitySubmission={insurance_eligibility_submission}
allowSharingWithKaiser={false}
partnerName={vaccineFlowConfig.population_settings.display_name}
appointmentType={appointment_type}
/>
<Spacer height={24} />
<ActionFooter backPath={getPreviousPath(paths, pathname)} />
</LeftJustifiedLayout>
</PageContainer>
);
};
src/react/src/ui/Vaccine/Registration/Pages/InsurancePage.tsx
Includes insurance info. Very hard to follow.
export function usePostVaccinationAppointment(
options?: MutationOptions<IVaccineAppointment, INewVaccineAppointment, ErrorPayloadWithDetail>
) {
return useMutation<IVaccineAppointment, INewVaccineAppointment, ErrorPayloadWithDetail>(
(appointment: INewVaccineAppointment) => {
return APIClient.post(VACCINE_APPOINTMENT_URL, {
json: {
// Walk-up appointments should default to a scheduled_for timestamp
// based off the time the appointment is submitted
scheduled_for: appointment.is_walk_up ? formatISO(new Date()) : undefined,
...appointment,
collection_site: appointment.collection_site_name,
},
});
},
options
);
}
src/react/src/ui/Vaccine/Registration/apis.ts
Not sure is this is right. Trying to verify that insurance passes through here.
With all the inheritence from code in the activation flow, this is kind of a nightmare to follow
def create(self, request, *, request_log):
claim_token = request.data.get('claim_token')
if not claim_token:
partner = request.data.get('partner')
if not partner:
raise DetailedBadRequestError(
"'partner' is required if 'claim_token' is not supplied."
)
population_settings = PopulationSettings.objects.get(
short_name__iexact=partner
)
claim = generate_claim_from_population(population_settings.population)
request.data['claim_token'] = claim.token
appointment = self._create_appointment(request)
request_log.link_entity(appointment)
serializer_class = self.get_serializer_class()
return Response(serializer_class(appointment).data)
Model that stores insurance ID
Question: are we changing relationship from other models to this to be 1:N, where it's currently 1:1? Or are we modifying this so that one record can have multiple insurances? I assume the first option.
Expand Code
class InsuranceEligibilitySubmission(TimeStampedModel):
# DEPRECATED(jjwon):
# The relations ``patient_info``, ``payer`` will be removed in a future
# release. Use ``submission.patient_info`` and ``submission.payer``,
# respectively, instead.
patient_info = models.ForeignKey(
'payments.InsurancePatientInfo',
related_name='eligibility_submission',
null=False,
on_delete=models.PROTECT,
)
payer = models.ForeignKey(
'payments.InsurancePayer', null=False, on_delete=models.PROTECT
)
policy_holder = models.ForeignKey(
'payments.InsurancePatientInfo',
related_name='policy_for_submission',
null=True,
blank=True,
on_delete=models.PROTECT,
)
insurance_id = models.CharField(max_length=50, null=False)
""" ID on the insurance card for patient """
insurance_type = models.TextField(
choices=INSURANCE_TYPE_CHOICES, null=True, blank=True
)
""" Type of insurance that the patient has (medicare, medicaid, commercial, unknown) """
relationship_to_policy_holder = models.CharField(
max_length=25, choices=POLICY_HOLDER_RELATIONSHIP_CHOICES, null=True, blank=True
)
estimated_oop_cost_cents = models.IntegerField(null=True)
patient_deductible_amount_cents = models.IntegerField(null=True)
patient_deductible_remaining_cents = models.IntegerField(null=True)
patient_coinsurance_percent = models.DecimalField(
max_digits=5, decimal_places=2, null=True, blank=True
)
patient_max_oop_cents = models.IntegerField(null=True)
eligibility_error_reason = models.CharField(
max_length=25, choices=ELIGIBILITY_ERROR_REASON_CHOICES, null=True
)
""" ERROR reason recoreded from api check of eligibility """
oop_check_reponse_messages = models.TextField(blank=True, null=True)
""" Deprecating in favor of better named api_response"""
api_response = models.TextField(blank=True)
""" Dump of complete api response for eligibility/oop call """
submitted_by = models.ForeignKey(
'users.ColorUser',
blank=True,
null=True,
related_name="eligibility_submissions",
on_delete=models.PROTECT,
)
""" Logged-in user who made eligibility submission """
met_oop_check_for_insurance = models.BooleanField(default=False)
""" Stored result of if at time of check this was a passing result """
overriden = models.NullBooleanField(default=False)
"""Whether this eligibility check was overriden."""
PRIVACY_SENSITIVE_FIELDS = ('insurance_id',)
group_id = models.CharField(max_length=50, null=True, blank=True)
""" ID on the insurance card for patient """
Not sure whether this class is involved in our task
class InsurancePatientInfo(TimeStampedModel, UserBasicPropertyMixin):
"""Patient information for an insurance claim submission."""
first_name = models.CharField(max_length=50, null=False)
"""The patient's first name."""
last_name = models.CharField(max_length=50, null=False)
"""The patient's first name."""
birthday = models.DateField(null=True)
"""The patient's birthday."""
gender = models.CharField(max_length=1, blank=True, null=True, choices=GENDER)
gender_identity = models.CharField(
max_length=50, blank=True, null=True, choices=GENDER_IDENTITY
)
"""The gender of the patient."""
insurance_id = models.CharField(max_length=50, blank=True, null=True)
""" ID on the insurance card for the patient """
PHI_FIELDS = (
'birthday',
'first_name',
'last_name',
)
An insurance claim submission, consisting of all information required to submit an insurance claim. Note that this does not represent an actual attempted insurance claim -- it just represents all of the information required to submit that insurance claim.
Expand Code
class InsuranceSubmission(TimeStampedModel):
"""
An insurance claim submission, consisting of all information required to
submit an insurance claim. Note that this does not represent an actual
attempted insurance claim -- it just represents all of the information
required to submit that insurance claim.
"""
physician_order = models.ForeignKey(
'ordering_physicians.PhysicianOrder',
related_name='attempted_insurance_submissions',
null=True,
on_delete=models.PROTECT,
)
"""
The physician order that this submission relates to.
When a ``PhysicianOrder`` is being placed, a provider may attempt to
place the order using insurance as a means of payment. This attribute
links the attempts with original order.
Note that if the submission was valid, an ``InsuranceClaim`` object will
be created, and the submission tied to that claim object has the correct
insurance information.
"""
patient_info = models.OneToOneField(
'payments.InsurancePatientInfo',
related_name='submission',
null=True,
on_delete=models.PROTECT,
)
"""Information about the patient for whom this medical procedure is for."""
payer = models.ForeignKey(
'payments.InsurancePayer', null=True, on_delete=models.PROTECT
)
"""The payer who the claim could be submitted for."""
successful_eligibility_submission = models.OneToOneField(
'payments.InsuranceEligibilitySubmission',
related_name='submission',
null=True,
on_delete=models.PROTECT,
)
"""The successful insurance eligibility submission."""
successful_criteria_submission = models.OneToOneField(
'payments.InsuranceCriteriaSubmission',
related_name='submission',
null=True,
on_delete=models.PROTECT,
)
"""The successful insurance criteria submission."""
# TODO: These fields are deprecated.
eligibility_submission = models.ForeignKey(
'payments.InsuranceEligibilitySubmission',
related_name='submissions',
null=True,
on_delete=models.PROTECT,
)
"""The corresponding insurance eligibility submission."""
criteria_submission = models.ForeignKey(
'payments.InsuranceCriteriaSubmission',
related_name='submissions',
null=True,
on_delete=models.PROTECT,
)
"""The corresponding insurance criteria submission."""