From aa737d974eb2f48eaa6cbcd910a4d647842c3af0 Mon Sep 17 00:00:00 2001 From: Sai Praveen Gudimetla Date: Mon, 30 Dec 2024 14:36:12 -0600 Subject: [PATCH 1/2] update logic to fetch slcsp values based on member add/drop (#166) --- .../data_stores/contract_holder_subject.rb | 9 +- .../aca_individuals/insurance_policy.rb | 4 +- .../aca_individuals/insurance_policy_spec.rb | 120 ++++++++++++++++++ 3 files changed, 128 insertions(+), 5 deletions(-) diff --git a/app/models/data_stores/contract_holder_subject.rb b/app/models/data_stores/contract_holder_subject.rb index 5984c6b4..f79e401b 100644 --- a/app/models/data_stores/contract_holder_subject.rb +++ b/app/models/data_stores/contract_holder_subject.rb @@ -16,9 +16,12 @@ class ContractHolderSubject index({ primary_person_hbx_id: 1 }) index({ status: 1 }) - index({ 'request_event.timestamp': 1 }, { name: "request_event_timestamp_index" }) - index({ 'response_event.timestamp': 1 }, { name: "response_event_timestamp_index" }) - index({ 'transmit_events.timestamp': 1 }, { name: "transmit_events_timestamp_index" }) + index({ request_event: 1 }, { name: "request_event" }) + index({ response_event: 1 }, { name: "response_event" }) + index({ transmit_events: 1 }, { name: "transmit_events" }) + index({ "response_event.body" => 1 }) + index({ "response_event.status" => 1 }) + index({ contract_holder_sync_id: 1, "response_event.body" => 1, "response_event.status" => 1 }) scope :by_primary_hbx_id, ->(hbx_id) { where(primary_person_hbx_id: hbx_id) } diff --git a/app/models/insurance_policies/aca_individuals/insurance_policy.rb b/app/models/insurance_policies/aca_individuals/insurance_policy.rb index e776b9ca..2e9212c2 100644 --- a/app/models/insurance_policies/aca_individuals/insurance_policy.rb +++ b/app/models/insurance_policies/aca_individuals/insurance_policy.rb @@ -202,10 +202,10 @@ def fetch_slcsp_from_tax_household(tax_household, enr_thhs) return 0.0 unless enr_thhs.any? { |enr_thh| enr_thh.tax_household.is_aqhp == true } tax_filer = fetch_tax_filer(tax_household) - enr_thh_for_month = enr_thhs.detect do |enr_thh| + enr_thh_for_month = enr_thhs.select do |enr_thh| enr_thh.tax_household.is_aqhp && enr_thh.tax_household.tax_household_members.map(&:person_id).include?(tax_filer&.person_id) - end + end.max_by { |enr_thh| enr_thh.enrolled_members_tax_household_members.count } return 0.0 if enr_thh_for_month.blank? 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 79e4179c..da6f063b 100644 --- a/spec/models/insurance_policies/aca_individuals/insurance_policy_spec.rb +++ b/spec/models/insurance_policies/aca_individuals/insurance_policy_spec.rb @@ -367,5 +367,125 @@ aqhp_enrollment_tax_household.applied_aptc)).to eq "400.00" end end + + context "when there is a dependent add mid term" do + let(:enrollment_subscriber) { FactoryBot.build(:enrolled_member, person: subscriber_person) } + let(:enrollment_dependent) { FactoryBot.build(:enrolled_member, person: dependent_person) } + let!(:enrollment_1) do + FactoryBot.create(:enrollment, start_on: Date.new(year, 1, 1), + effectuated_on: Date.new(year, 1, 1), + end_on: Date.new(year, 1, 23), + insurance_policy: insurance_policy, + subscriber: enrollment_subscriber) + end + let!(:enrollment_2) do + FactoryBot.create(:enrollment, start_on: Date.new(year, 1, 24), + effectuated_on: Date.new(year, 1, 24), + end_on: Date.new(year, 1, 31), + insurance_policy: insurance_policy, + subscriber: enrollment_subscriber, + dependents: [enrollment_dependent]) + end + + let!(:aqhp_tax_household_1) { FactoryBot.create(:tax_household, is_aqhp: true) } + let!(:aqhp_tax_household_2) { FactoryBot.create(:tax_household, is_aqhp: true) } + let!(:aqhp_thh_sub_tax_household_member) do + FactoryBot.create(:tax_household_member, tax_household: aqhp_tax_household_1, person: subscriber_person, + is_tax_filer: true) + end + let!(:aqhp_thh_sub_tax_household_member_2) do + FactoryBot.create(:tax_household_member, tax_household: aqhp_tax_household_2, person: subscriber_person, + is_tax_filer: true) + end + let!(:aqhp_thh_sub_tax_household_member_3) do + FactoryBot.create(:tax_household_member, tax_household: aqhp_tax_household_2, person: dependent_person, + is_tax_filer: false) + end + let!(:aqhp_enrollment_tax_household_1) do + FactoryBot.create(:enrollments_tax_households, enrollment_id: enrollment_1.id, + tax_household_id: aqhp_tax_household_1.id, + household_benchmark_ehb_premium: 400.0) + end + let!(:enr_members_thhm_1) do + FactoryBot.create(:enrolled_members_tax_household_members, enrollments_tax_households: aqhp_enrollment_tax_household_1) + end + + let!(:aqhp_enrollment_tax_household_2) do + FactoryBot.create(:enrollments_tax_households, enrollment_id: enrollment_2.id, + tax_household_id: aqhp_tax_household_2.id, + household_benchmark_ehb_premium: 500.0) + end + let!(:enr_members_thhm_2) do + FactoryBot.create(:enrolled_members_tax_household_members, enrollments_tax_households: aqhp_enrollment_tax_household_2) + end + let!(:enr_members_thhm_3) do + FactoryBot.create(:enrolled_members_tax_household_members, enrollments_tax_households: aqhp_enrollment_tax_household_2) + end + + it "should return member added slcsp premium" do + expect(insurance_policy.fetch_slcsp_premium([enrollment_1, enrollment_2], 1, aqhp_tax_household_2, + aqhp_enrollment_tax_household_2.applied_aptc)).to eq "500.00" + end + end + + context "when there is a dependent drop mid term" do + let(:enrollment_subscriber) { FactoryBot.build(:enrolled_member, person: subscriber_person) } + let(:enrollment_dependent) { FactoryBot.build(:enrolled_member, person: dependent_person) } + let!(:enrollment_1) do + FactoryBot.create(:enrollment, start_on: Date.new(year, 1, 1), + effectuated_on: Date.new(year, 1, 1), + end_on: Date.new(year, 1, 23), + insurance_policy: insurance_policy, + subscriber: enrollment_subscriber, + dependents: [enrollment_dependent]) + end + let!(:enrollment_2) do + FactoryBot.create(:enrollment, start_on: Date.new(year, 1, 24), + effectuated_on: Date.new(year, 1, 24), + end_on: Date.new(year, 1, 31), + insurance_policy: insurance_policy, + subscriber: enrollment_subscriber) + end + + let!(:aqhp_tax_household_1) { FactoryBot.create(:tax_household, is_aqhp: true) } + let!(:aqhp_tax_household_2) { FactoryBot.create(:tax_household, is_aqhp: true) } + let!(:aqhp_thh_sub_tax_household_member) do + FactoryBot.create(:tax_household_member, tax_household: aqhp_tax_household_1, person: subscriber_person, + is_tax_filer: true) + end + let!(:aqhp_thh_sub_tax_household_member_2) do + FactoryBot.create(:tax_household_member, tax_household: aqhp_tax_household_1, person: dependent_person, + is_tax_filer: false) + end + let!(:aqhp_thh_sub_tax_household_member_3) do + FactoryBot.create(:tax_household_member, tax_household: aqhp_tax_household_2, person: subscriber_person, + is_tax_filer: true) + end + let!(:aqhp_enrollment_tax_household_1) do + FactoryBot.create(:enrollments_tax_households, enrollment_id: enrollment_1.id, + tax_household_id: aqhp_tax_household_1.id, + household_benchmark_ehb_premium: 500.0) + end + let!(:enr_members_thhm_1) do + FactoryBot.create(:enrolled_members_tax_household_members, enrollments_tax_households: aqhp_enrollment_tax_household_1) + end + let!(:enr_members_thhm_2) do + FactoryBot.create(:enrolled_members_tax_household_members, enrollments_tax_households: aqhp_enrollment_tax_household_1) + end + + let!(:aqhp_enrollment_tax_household_2) do + FactoryBot.create(:enrollments_tax_households, enrollment_id: enrollment_2.id, + tax_household_id: aqhp_tax_household_2.id, + household_benchmark_ehb_premium: 400.0) + end + let!(:enr_members_thhm_3) do + FactoryBot.create(:enrolled_members_tax_household_members, enrollments_tax_households: aqhp_enrollment_tax_household_2) + end + + it "should not return member dropped slcsp premium" do + expect(insurance_policy.fetch_slcsp_premium([enrollment_1, enrollment_2], 1, aqhp_tax_household_2, + aqhp_enrollment_tax_household_2.applied_aptc)).to eq "500.00" + end + end end end From 3212189e62d75b9687a0f41510db639eb1bc304e Mon Sep 17 00:00:00 2001 From: Sai Praveen Gudimetla Date: Thu, 2 Jan 2025 14:23:53 -0600 Subject: [PATCH 2/2] populate covered individuls properly when they switch from UQHP to AQHP (#167) --- .../construct_cv3_payload.rb | 4 +- .../construct_cv3_payload_spec.rb | 107 +++++++++++++++--- 2 files changed, 93 insertions(+), 18 deletions(-) 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 348afe42..66f5e354 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 @@ -498,7 +498,7 @@ def fetch_member_ehb_premium(enrolled_member, premium_schedule, insurance_policy def get_enrolled_members_by_tax_household_for(enrollments_for_month, tax_household) enrs_thhs = fetch_enrollments_tax_households(enrollments_for_month) - valid_enr_thh = if enrs_thhs.size > 1 + valid_enr_thh = if enrs_thhs.size > 1 && tax_household.is_aqhp enrs_thhs.select do |enr_thh| valid_enrollment_tax_household?(enr_thh, tax_household) && enr_thh.tax_household.is_aqhp end @@ -563,7 +563,7 @@ def fetch_thh_members_from_enr_thhs(enr_thhs, tax_household) return tax_household.tax_household_members unless tax_household.is_aqhp enr_thhs_for_month = enr_thhs.select do |enr_thh| - enr_thh.tax_household.is_aqhp && valid_enrollment_tax_household?(enr_thh, tax_household) + valid_enrollment_tax_household?(enr_thh, tax_household) end enrolled_thh_members_person_ids = enr_thhs_for_month&.flat_map(&:tax_household) &.flat_map(&:tax_household_members)&.uniq(&:person_id) 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 5e0dc6ff..289ab8fb 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 @@ -407,19 +407,9 @@ let!(:premium_schedule_2_enrollment_1) do FactoryBot.create(:premium_schedule, enrolled_member: enrollment_1.dependents.first) end - let!(:aqhp_tax_household_1) { FactoryBot.create(:tax_household, is_aqhp: false) } let!(:aqhp_tax_household_2) { FactoryBot.create(:tax_household, is_aqhp: true) } let!(:aqhp_tax_household_3) { FactoryBot.create(:tax_household, is_aqhp: true) } - let!(:uqp_thh_1_sub_tax_household_member) do - FactoryBot.create(:tax_household_member, tax_household: aqhp_tax_household_1, person: subscriber_person, - is_tax_filer: true) - end - let!(:uqhp_thh_1_dep_tax_household_member) do - FactoryBot.create(:tax_household_member, tax_household: aqhp_tax_household_1, person: dependent_person, - is_tax_filer: false) - end - let!(:aqhp_thh_2_sub_tax_household_member) do FactoryBot.create(:tax_household_member, tax_household: aqhp_tax_household_2, person: subscriber_person, is_tax_filer: true) @@ -430,10 +420,6 @@ is_tax_filer: true) end - let!(:uqhp_enrollment_tax_household_1) do - FactoryBot.create(:enrollments_tax_households, enrollment_id: enrollment_1.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_1.id, tax_household_id: aqhp_tax_household_2.id) end @@ -452,8 +438,8 @@ end it "should return correct members for each tax_household" do - expect(@result[:aptc_csr_tax_households].first[:covered_individuals].count).to eq 1 - expect(@result[:aptc_csr_tax_households].first[:covered_individuals].count).to eq 1 + expect(@result[:aptc_csr_tax_households][0][:covered_individuals].count).to eq 1 + expect(@result[:aptc_csr_tax_households][1][:covered_individuals].count).to eq 1 end end @@ -577,6 +563,95 @@ end end + context 'UQHP mid month member drop' do + let(:subscriber_person) { FactoryBot.create(:people_person) } + let(:dependent_person) { FactoryBot.create(:people_person) } + let!(:glue_subscriber_person) { FactoryBot.create(:person, authority_member_id: subscriber_person.hbx_id) } + let!(:glue_dependent_person) { FactoryBot.create(:person, authority_member_id: dependent_person.hbx_id) } + let(:enrollment_1_subscriber) { FactoryBot.build(:enrolled_member, person: subscriber_person) } + let(:enrollment_2_subscriber) { FactoryBot.build(:enrolled_member, person: subscriber_person) } + let(:enrollment_2_dependents) { FactoryBot.build(:enrolled_member, person: dependent_person) } + let(:product) do + FactoryBot.create(:insurance_product, ehb: 0.9959) + end + let(:insurance_policy) do + FactoryBot.create(:insurance_policy, + insurance_product: product, + start_on: Date.new(year, 1, 1), + end_on: Date.new(year, 12, 31)) + end + + let!(:enrollment_1) do + FactoryBot.create(:enrollment, start_on: Date.new(year, 1, 1), + effectuated_on: Date.new(year, 1, 1), + end_on: Date.new(year, 7, 23), + created_at: Time.now, + insurance_policy: insurance_policy, + subscriber: enrollment_1_subscriber, + dependents: [enrollment_2_dependents]) + end + let!(:enrollment_2) do + FactoryBot.create(:enrollment, start_on: Date.new(year, 7, 24), + effectuated_on: Date.new(year, 7, 24), + created_at: Time.now, + insurance_policy: insurance_policy, + subscriber: enrollment_2_subscriber) + end + let!(:premium_schedule_enrollment) do + FactoryBot.create(:premium_schedule, enrolled_member: enrollment_1.subscriber, + premium_amount: 100.0, benchmark_ehb_premium_amount: 100.0) + end + let!(:premium_schedule_2_enrollment) do + FactoryBot.create(:premium_schedule, enrolled_member: enrollment_1.dependents.first, + premium_amount: 50.0, benchmark_ehb_premium_amount: 50.0) + end + let!(:premium_schedule_3_enrollment) do + FactoryBot.create(:premium_schedule, enrolled_member: enrollment_2.subscriber, + premium_amount: 100.0, benchmark_ehb_premium_amount: 100.0) + end + + let!(:uqhp_tax_household_1) { FactoryBot.create(:tax_household, is_aqhp: false) } + let!(:uqhp_tax_household_2) { FactoryBot.create(:tax_household, is_aqhp: false) } + + let!(:uqhp_thh_1_sub_tax_household_member) do + FactoryBot.create(:tax_household_member, tax_household: uqhp_tax_household_1, person: subscriber_person, + is_tax_filer: true) + end + let!(:uqhp_thh_1_dep_tax_household_member) do + FactoryBot.create(:tax_household_member, tax_household: uqhp_tax_household_1, person: dependent_person, + is_tax_filer: false) + end + let!(:uqhp_thh_2_dep_tax_household_member) do + FactoryBot.create(:tax_household_member, tax_household: uqhp_tax_household_2, person: subscriber_person, + is_tax_filer: true) + end + let!(:uqhp_enrollment_tax_household_1) do + FactoryBot.create(:enrollments_tax_households, enrollment_id: enrollment_1.id, tax_household_id: uqhp_tax_household_1.id) + end + let!(:uqhp_enrollment_tax_household_2) do + FactoryBot.create(:enrollments_tax_households, enrollment_id: enrollment_2.id, tax_household_id: uqhp_tax_household_2.id) + end + + before :each do + @result_call = subject.call({ insurance_policy: insurance_policy }) + @result = @result_call.value! + end + + it "should should publish the event" do + expect(@result_call.success?).to be_truthy + expect(@result[:aptc_csr_tax_households].first[:covered_individuals].count).to eq 2 + end + + it "should round down ehb premium value" do + july_coverage_info = @result[:aptc_csr_tax_households].first[:months_of_year][6][:coverage_information] + premium = july_coverage_info.dig(:total_premium, :cents) + expect(Money.new(premium).to_f).to eq 135.35 + jan_coverage_info = @result[:aptc_csr_tax_households].first[:months_of_year][0][:coverage_information] + premium = jan_coverage_info.dig(:total_premium, :cents) + expect(Money.new(premium).to_f).to eq 148.00 + end + end + context 'rounding down member premiums' do let(:subscriber_person) { FactoryBot.create(:people_person) } let!(:glue_subscriber_person) { FactoryBot.create(:person, authority_member_id: subscriber_person.hbx_id) }