diff --git a/app/helpers/date_helper.rb b/app/helpers/date_helper.rb new file mode 100644 index 00000000..87f52ff8 --- /dev/null +++ b/app/helpers/date_helper.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# Helper methods for date-related calculations +module DateHelper + def mid_month_end?(end_on, month) + ends_this_month = end_on.month == month + last_day_of_month = Date.new(end_on.year, month, -1) + (end_on.day != last_day_of_month.day) && ends_this_month + end + + def eom_end?(end_on, month) + ends_this_month = end_on.month == month + last_day_of_month = Date.new(end_on.year, month, -1) + (end_on == last_day_of_month) && ends_this_month + end +end diff --git a/app/models/insurance_policies/aca_individuals/enrollment.rb b/app/models/insurance_policies/aca_individuals/enrollment.rb index df3a0e2e..284019be 100644 --- a/app/models/insurance_policies/aca_individuals/enrollment.rb +++ b/app/models/insurance_policies/aca_individuals/enrollment.rb @@ -7,6 +7,7 @@ class Enrollment include Mongoid::Document include Mongoid::Timestamps include DomainModels::Domainable + include ::DateHelper has_many :enrollments_tax_households, class_name: 'InsurancePolicies::AcaIndividuals::EnrollmentsTaxHouseholds', @@ -59,15 +60,18 @@ def enrollment_end_on end_on.present? ? end_on : insurance_policy.start_on.end_of_year end - # rubocop:disable Metrics/AbcSize def pre_amt_tot_values(enrolled_thh_people, calendar_month) - if insurance_policy.term_for_np && insurance_policy.policy_end_on.month == calendar_month - format('%.2f', 0.0) - else - pre_amt_tot_month = enrolled_thh_people.map { |mem| mem.premium_schedule.premium_amount.to_f }.sum - pre_amt_tot_month = (pre_amt_tot_month * insurance_policy.insurance_product.ehb).to_f.round(2) - format('%.2f', pre_amt_tot_month) + if insurance_policy.term_for_np + end_on = insurance_policy.end_on + next_month = calendar_month == 12 ? 1 : calendar_month + 1 + # if npt policy ends on the last day of the month or mid month next month + # set to 0 as we are in the first or second (for uqhp) grace period month + gpm = eom_end?(end_on, calendar_month) || mid_month_end?(end_on, next_month) || mid_month_end?(end_on, calendar_month) + return format('%.2f', 0.0) if gpm end + pre_amt_tot_month = enrolled_thh_people.sum { |mem| mem.premium_schedule.premium_amount.to_f } + pre_amt_tot_month = (pre_amt_tot_month * insurance_policy.insurance_product.ehb).to_f.round(2) + format('%.2f', pre_amt_tot_month) end # Fetch eligible enrollees based on tax household members. @@ -99,7 +103,6 @@ def pediatric_dental_premium(enrollments_for_month, tax_household_members, calen health_enrolled_people: eligible_enrollees, month: calendar_month }).value!.to_f end - # rubocop:enable Metrics/AbcSize def enrolled_member_by_hbx_id(hbx_id) [[subscriber] + dependents].flatten.detect do |enrollee| diff --git a/app/models/insurance_policies/aca_individuals/insurance_policy.rb b/app/models/insurance_policies/aca_individuals/insurance_policy.rb index 2e9212c2..38f12c2a 100644 --- a/app/models/insurance_policies/aca_individuals/insurance_policy.rb +++ b/app/models/insurance_policies/aca_individuals/insurance_policy.rb @@ -7,6 +7,7 @@ class InsurancePolicy include Mongoid::Document include Mongoid::Timestamps include DomainModels::Domainable + include ::DateHelper has_many :enrollments, class_name: 'InsurancePolicies::AcaIndividuals::Enrollment' @@ -187,8 +188,16 @@ def fetch_enrollments_tax_households(enrs_for_month) end def fetch_slcsp_premium(enrs_for_month, calendar_month, tax_household = nil, aptc_tax_credit = nil) + if term_for_np + end_on = policy_end_on + next_month = calendar_month == 12 ? 1 : calendar_month + 1 + # if npt policy ends on the last day of the month or mid month next month + # set to 0 as we are in the first or second (for uqhp) grace period month + gpm = eom_end?(end_on, calendar_month) || mid_month_end?(end_on, next_month) || mid_month_end?(end_on, calendar_month) + return format('%.2f', 0.0) if gpm + end + return format('%.2f', 0.0) if aptc_tax_credit.blank? || aptc_tax_credit.to_f.zero? - return format('%.2f', 0.0) if term_for_np && policy_end_on.month == calendar_month enr_thhs = fetch_enrollments_tax_households(enrs_for_month) slcsp_premium = enr_thhs.map(&:household_benchmark_ehb_premium).compact.sum diff --git a/app/operations/insurance_policies/aca_individuals/insurance_policies/construct_cv3_payload.rb b/app/operations/insurance_policies/aca_individuals/insurance_policies/construct_cv3_payload.rb index 66f5e354..ff41d8a6 100644 --- a/app/operations/insurance_policies/aca_individuals/insurance_policies/construct_cv3_payload.rb +++ b/app/operations/insurance_policies/aca_individuals/insurance_policies/construct_cv3_payload.rb @@ -10,6 +10,7 @@ module InsurancePolicies # Operation to create insurance policy class ConstructCv3Payload include ::FloatHelper + include ::DateHelper send(:include, Dry::Monads[:result, :do]) def call(params) @@ -430,6 +431,9 @@ def construct_coverage_information(insurance_policy, covered_individuals, tax_ho next unless any_thh_members_enrolled?(tax_household, enrollments_for_month) next if enrollments_tax_household_for_month_empty?(enrollments_for_month, tax_household) + # if npt and mid month end date, do not include that month if aqhp + next if insurance_policy.term_for_np && mid_month_end?(insurance_policy.end_on, month) && tax_household.is_aqhp + if tax_household.is_aqhp && covered_individuals.present? update_covered_individuals_end_date(covered_individuals, enrollments_for_month, tax_household) end @@ -455,7 +459,15 @@ def construct_coverage_information(insurance_policy, covered_individuals, tax_ho end def calculate_ehb_premium_for(insurance_policy, tax_household, enrollments_for_month, calendar_month) - return format('%.2f', 0.0) if insurance_policy.term_for_np && insurance_policy.policy_end_on.month == calendar_month + + if insurance_policy.term_for_np + end_on = insurance_policy.end_on + next_month = calendar_month == 12 ? 1 : calendar_month + 1 + # if npt policy ends on the last day of the month or mid month next month + # set to 0 as we are in the first or second (for uqhp) grace period month + gpm = eom_end?(end_on, calendar_month) || mid_month_end?(end_on, next_month) || mid_month_end?(end_on, calendar_month) + return format('%.2f', 0.0) if gpm + end calender_month_begin = Date.new(insurance_policy.start_on.year, calendar_month, 1) calender_month_end = calender_month_begin.end_of_month diff --git a/spec/models/insurance_policies/aca_individuals/enrollment_spec.rb b/spec/models/insurance_policies/aca_individuals/enrollment_spec.rb index dfa2f26c..7a0ee634 100644 --- a/spec/models/insurance_policies/aca_individuals/enrollment_spec.rb +++ b/spec/models/insurance_policies/aca_individuals/enrollment_spec.rb @@ -12,6 +12,16 @@ let(:insurance_policy) do FactoryBot.create(:insurance_policy, start_on: Date.new(year, 1, 1), end_on: Date.new(year, 12, 31)) end + let(:insurance_policy_npt_eom) do + FactoryBot.create(:insurance_policy, start_on: Date.new(year, 1, 1), + end_on: Date.new(year, 5, 31), + term_for_np: true) + end + let(:insurance_policy_npt_mid_month) do + FactoryBot.create(:insurance_policy, start_on: Date.new(year, 1, 1), + end_on: Date.new(year, 3, 3), + term_for_np: true) + end let(:subscriber) { FactoryBot.build(:enrolled_member, person: subscriber_person) } let(:dependents) do [ @@ -29,6 +39,24 @@ dependents: dependents) end + let(:enrollment_npt_eom) do + FactoryBot.create(:enrollment, start_on: Date.new(year, 1, 1), + effectuated_on: Date.new(year, 1, 1), + end_on: Date.new(year, 5, 31), + insurance_policy: insurance_policy_npt_eom, + subscriber: subscriber, + dependents: dependents) + end + + let(:enrollment_npt_mid_month) do + FactoryBot.create(:enrollment, start_on: Date.new(year, 1, 1), + effectuated_on: Date.new(year, 1, 1), + end_on: Date.new(year, 5, 31), + insurance_policy: insurance_policy_npt_mid_month, + subscriber: subscriber, + dependents: dependents) + end + let(:tax_household_members) do [ double('InsurancePolicies::AcaIndividuals::TaxHouseholdMember', is_medicaid_chip_eligible: true, person: subscriber_person), @@ -91,4 +119,24 @@ end end end + + describe '#pre_amt_tot_values' do + context 'insurance_policy term_for_np is true and eom end date' do + it 'returns 0.00' do + expect(enrollment_npt_eom.pre_amt_tot_values([subscriber], 5)).to eq('0.00') + end + end + + context 'insurance_policy term_for_np is true and next month mid month end date' do + it 'returns 0.00' do + expect(enrollment_npt_mid_month.pre_amt_tot_values([subscriber], 2)).to eq('0.00') + end + end + + context 'insurance_policy term_for_np is true and mid month end date' do + it 'returns 0.00' do + expect(enrollment_npt_mid_month.pre_amt_tot_values([subscriber], 3)).to eq('0.00') + end + end + end end diff --git a/spec/models/insurance_policies/aca_individuals/insurance_policy_spec.rb b/spec/models/insurance_policies/aca_individuals/insurance_policy_spec.rb index da6f063b..ac24d5a5 100644 --- a/spec/models/insurance_policies/aca_individuals/insurance_policy_spec.rb +++ b/spec/models/insurance_policies/aca_individuals/insurance_policy_spec.rb @@ -7,6 +7,16 @@ let(:year) { Date.today.year } let(:insurance_policy) { FactoryBot.create(:insurance_policy, start_on: Date.new(year, 1, 1), end_on: Date.new(year, 12, 31)) } + let(:insurance_policy_npt_eom) do + FactoryBot.create(:insurance_policy, start_on: Date.new(year, 1, 1), + end_on: Date.new(year, 5, 31), + term_for_np: true) + end + let(:insurance_policy_npt_mid_month) do + FactoryBot.create(:insurance_policy, start_on: Date.new(year, 1, 1), + end_on: Date.new(year, 3, 3), + term_for_np: true) + end let(:subscriber) { FactoryBot.build(:enrolled_member, person: subscriber_person) } let(:dependents) { FactoryBot.build(:enrolled_member, person: dependent_person) } let!(:enrollment_1) do @@ -313,6 +323,20 @@ result = insurance_policy.applied_aptc_amount_for(enrollments_for_month, calendar_month, aqhp_tax_household_3) expect(result).to eq "0.00" end + + it "should return a value if on first grace period month with EOM end date" do + calendar_month = 5 + enrollments_for_month = insurance_policy_npt_eom.enrollments_for_month(calendar_month, year) + result = insurance_policy_npt_eom.applied_aptc_amount_for(enrollments_for_month, calendar_month, aqhp_tax_household_3) + expect(result).not_to eq nil + end + + it "should return a value if on first grace period month with next month mid month end date" do + calendar_month = 2 + enrollments_for_month = insurance_policy_npt_mid_month.enrollments_for_month(calendar_month, year) + result = insurance_policy_npt_mid_month.applied_aptc_amount_for(enrollments_for_month, calendar_month, aqhp_tax_household_3) + expect(result).not_to eq nil + end end context "#fetch_slcsp_premium" do @@ -340,6 +364,15 @@ expect(insurance_policy.fetch_slcsp_premium([enrollment], 1, aqhp_tax_household, aqhp_enrollment_tax_household.applied_aptc)).to eq "0.00" end + + it "should return 0 if in first or second (for uqhp) grace period month" do + result_eom = insurance_policy_npt_eom.fetch_slcsp_premium([enrollment], 5, aqhp_tax_household, 4.0) + result_next_month_mid_month = insurance_policy_npt_mid_month.fetch_slcsp_premium([enrollment], 2, aqhp_tax_household, 4.0) + result_mid_month = insurance_policy_npt_mid_month.fetch_slcsp_premium([enrollment], 3, aqhp_tax_household, 4.0) + expect(result_eom).to eq "0.00" + expect(result_next_month_mid_month).to eq "0.00" + expect(result_mid_month).to eq "0.00" + end end context "when aptc is non zero" do diff --git a/spec/operations/insurance_policies/aca_individuals/insurance_policies/construct_cv3_payload_spec.rb b/spec/operations/insurance_policies/aca_individuals/insurance_policies/construct_cv3_payload_spec.rb index 289ab8fb..07d095e2 100644 --- a/spec/operations/insurance_policies/aca_individuals/insurance_policies/construct_cv3_payload_spec.rb +++ b/spec/operations/insurance_policies/aca_individuals/insurance_policies/construct_cv3_payload_spec.rb @@ -500,6 +500,98 @@ end end + context "when on an npt policy" do + let(:enrollment_1_subscriber) { FactoryBot.build(:enrolled_member, person: subscriber_person) } + let(:enrollment_2_subscriber) { FactoryBot.build(:enrolled_member, person: subscriber_person) } + + let!(:aqhp_tax_household_1) { FactoryBot.create(:tax_household, is_aqhp: true) } + let!(:uqhp_tax_household_1) { FactoryBot.create(:tax_household, is_aqhp: false) } + + let!(:aqhp_tax_household_member_1) do + FactoryBot.create(:tax_household_member, tax_household: aqhp_tax_household_1, person: subscriber_person, + is_tax_filer: true) + end + + let!(:uqhp_tax_household_member_1) do + FactoryBot.create(:tax_household_member, tax_household: uqhp_tax_household_1, person: subscriber_person, + is_tax_filer: true) + end + + let(:npt_eom_insurance_policy) do + FactoryBot.create(:insurance_policy, start_on: Date.new(year, 1, 1), end_on: Date.new(year, 5, 31), term_for_np: true) + end + let(:enrollment_npt_eom) do + FactoryBot.create(:enrollment, start_on: Date.new(year, 1, 1), + effectuated_on: Date.new(year, 1, 1), + end_on: Date.new(year, 5, 31), + created_at: Time.now, + insurance_policy: npt_eom_insurance_policy, + subscriber: enrollment_1_subscriber) + end + + let(:npt_mid_month_insurance_policy) do + FactoryBot.create(:insurance_policy, start_on: Date.new(year, 1, 1), end_on: Date.new(year, 3, 3), term_for_np: true) + end + let(:enrollment_npt_mid_month) do + FactoryBot.create(:enrollment, start_on: Date.new(year, 1, 1), + effectuated_on: Date.new(year, 1, 1), + end_on: Date.new(year, 3, 3), + created_at: Time.now, + insurance_policy: npt_mid_month_insurance_policy, + subscriber: enrollment_1_subscriber) + end + + let(:npt_mid_month_uqhp_insurance_policy) do + FactoryBot.create(:insurance_policy, start_on: Date.new(year, 1, 1), end_on: Date.new(year, 3, 3), term_for_np: true) + end + + let(:enrollment_npt_mid_month_uqhp) do + FactoryBot.create(:enrollment, start_on: Date.new(year, 1, 1), + effectuated_on: Date.new(year, 1, 1), + end_on: Date.new(year, 3, 3), + created_at: Time.now, + insurance_policy: npt_mid_month_uqhp_insurance_policy, + subscriber: enrollment_2_subscriber) + end + + let!(:premium_schedule_1) { FactoryBot.create(:premium_schedule, enrolled_member: enrollment_npt_eom.subscriber) } + let!(:premium_schedule_2) { FactoryBot.create(:premium_schedule, enrolled_member: enrollment_npt_mid_month.subscriber) } + let!(:premium_schedule_3) { FactoryBot.create(:premium_schedule, enrolled_member: enrollment_npt_mid_month_uqhp.subscriber) } + + let!(:aqhp_enrollment_tax_household_1) do + FactoryBot.create(:enrollments_tax_households, enrollment_id: enrollment_npt_eom.id, + tax_household_id: aqhp_tax_household_1.id) + end + + let!(:aqhp_enrollment_tax_household_2) do + FactoryBot.create(:enrollments_tax_households, enrollment_id: enrollment_npt_mid_month.id, + tax_household_id: aqhp_tax_household_1.id) + end + + let!(:uqhp_enrollment_tax_household_1) do + FactoryBot.create(:enrollments_tax_households, enrollment_id: enrollment_npt_mid_month_uqhp.id, + tax_household_id: uqhp_tax_household_1.id) + end + + it "should return success on eom npt policies" do + result = subject.call({ insurance_policy: npt_eom_insurance_policy }) + expect(result.success?).to be_truthy + end + + it "should not return aptc_csr_household for month after GPM for mid month aqhp npt policies" do + result = subject.call({ insurance_policy: npt_mid_month_insurance_policy }) + expect(result.value![:aptc_csr_tax_households][0][:months_of_year].count).to eq 2 + expect(result.success?).to be_truthy + end + + it "should include mid month for uqhp npt policies" do + result = subject.call({ insurance_policy: npt_mid_month_uqhp_insurance_policy }) + expect(result.value![:aptc_csr_tax_households][0][:months_of_year].count).to eq 3 + print(result.value![:aptc_csr_tax_households][0][:months_of_year]) + expect(result.success?).to be_truthy + end + end + context 'rounding down total_ehb_premium' do let(:subscriber_person) { FactoryBot.create(:people_person) } let(:dependent_person) { FactoryBot.create(:people_person) }