Skip to content

Commit 5bf320c

Browse files
authored
Route clients who have a specific language need to partners with that language capability (#6103)
1 parent f3da613 commit 5bf320c

18 files changed

+332
-83
lines changed

app/controllers/questions/interview_scheduling_controller.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ def edit
88
super
99
end
1010

11+
private
12+
13+
def after_update_success
14+
if current_intake.client.routing_method.blank? || current_intake.client.routing_method_at_capacity?
15+
PartnerRoutingService.update_intake_partner(current_intake)
16+
end
17+
end
18+
1119
def tracking_data
1220
{}
1321
end

app/controllers/questions/personal_info_controller.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@ def after_update_success
3333
new_intake = @form.intake
3434
session[:intake_id] = new_intake.id
3535
new_intake.set_navigator(session[:navigator])
36-
37-
if new_intake.client.routing_method.blank? || new_intake.client.routing_method_at_capacity?
38-
PartnerRoutingService.update_intake_partner(new_intake)
39-
end
4036
end
4137

4238
def form_params

app/lib/navigation/gyr_question_navigation.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ class GyrQuestionNavigation
1717

1818
# Contact information and preferences
1919
Questions::PersonalInfoController, # creates Intake record and Client record, if triage was skipped
20+
Questions::InterviewSchedulingController,
2021
Questions::AtCapacityController, # possible off-boarding when at capacity
2122
Questions::SsnItinController,
2223
Questions::BacktaxesController,
2324
Questions::StartWithCurrentYearController,
2425
Questions::ReturningClientController, # possible off-boarding from flow
25-
Questions::InterviewSchedulingController,
2626
Questions::NotificationPreferenceController,
2727
Questions::PhoneNumberCanReceiveTextsController,
2828
Questions::CellPhoneNumberController,

app/models/organization.rb

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,56 @@ class Organization < VitaPartner
7474
)
7575
end
7676

77-
def language_offerings
78-
all_offerings = Rails.cache.fetch('airtable_language_offerings', expires_in: 1.hour) do
77+
scope :with_language_capability, ->(locale) do
78+
language = locale_to_full_lang(locale)&.downcase
79+
if language.blank? || language == "english"
80+
all
81+
else
82+
names = lang_to_names_index_cached[language]
83+
names.present? ? where(name: names) : none
84+
end
85+
end
86+
87+
def self.locale_to_full_lang(locale)
88+
locale_code = locale.to_s.strip.downcase
89+
languages_hash = I18n.backend.translations[I18n.locale][:general][:language_options]
90+
languages_hash[locale_code.to_sym] # returns the full language name
91+
end
92+
93+
def self.all_language_offerings
94+
# English is not listed in the airtable but implied in all orgs' language offerings
95+
Rails.cache.fetch('airtable_language_offerings', expires_in: 1.hour) do
7996
Airtable::Organization.language_offerings
8097
end
81-
all_offerings[name] || []
98+
end
99+
100+
def self.language_options_list
101+
Rails.cache.fetch('airtable_lang_options_list', expires_in: 1.hour) do
102+
Organization.all_language_offerings.values.flatten.uniq.sort.reject { |k| k == "Other" }.unshift("English")
103+
end
104+
end
105+
106+
def language_offerings
107+
Organization.all_language_offerings[name] || []
108+
end
109+
110+
def self.lang_to_names_index
111+
# cross-request cache
112+
Rails.cache.fetch('airtable_lang_to_names', expires_in: 1.hour) do
113+
lang_to_org_names_hash = {}
114+
all_language_offerings.each do |org_name, langs|
115+
Array(langs).each do |l|
116+
key = l.to_s.strip.downcase
117+
(lang_to_org_names_hash[key] ||= []) << org_name
118+
end
119+
end
120+
lang_to_org_names_hash
121+
end
122+
end
123+
124+
def self.lang_to_names_index_cached
125+
# stores in cache per request
126+
RequestStore.store[:lang_to_names] ||= lang_to_names_index
82127
end
83128

84129
def at_capacity?

app/models/vita_partner.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ class VitaPartner < ApplicationRecord
5252
greetable_organizations.or(greetable_sites)
5353
}
5454

55+
scope :with_language_capability, -> (language) do
56+
org_ids = Organization.with_language_capability(language).pluck(:id)
57+
58+
# Organizations with language capability and Sites under those Orgs
59+
where(id: org_ids).or(where(type: Site::TYPE, parent_organization_id: org_ids))
60+
end
61+
5562
scope :with_capacity, -> {
5663
org_ids = Organization.with_capacity.pluck(:id)
5764

app/services/partner_routing_service.rb

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ def determine_partner
2727
end
2828
end
2929

30+
set_base_vita_partners(language_routing: true)
31+
3032
from_itin_enabled = vita_partner_from_itin_enabled if @intake.present? && @intake.itin_applicant?
3133
return from_itin_enabled if from_itin_enabled.present?
3234

@@ -45,12 +47,38 @@ def determine_partner
4547
from_national_routing = route_to_national_overflow_partner
4648
return from_national_routing if from_national_routing.present?
4749

50+
# If previous steps result in no vita-partner with the intake's language preference,
51+
# then we repeat the zip-code, state and national-overflow routing steps without factoring in the language preference
52+
set_base_vita_partners(language_routing: false)
53+
from_zip_code = vita_partner_from_zip_code if @zip_code.present?
54+
return from_zip_code if from_zip_code.present?
55+
56+
from_state_routing = vita_partner_from_state if @zip_code.present?
57+
return from_state_routing if from_state_routing.present?
58+
59+
from_national_routing = route_to_national_overflow_partner
60+
return from_national_routing if from_national_routing.present?
61+
4862
@routing_method = :at_capacity
4963
return
5064
end
5165

5266
private
5367

68+
def set_base_vita_partners(language_routing:)
69+
@base_orgs = if language_routing
70+
Organization.with_language_capability(@intake&.preferred_interview_language)
71+
else
72+
Organization
73+
end
74+
75+
@base_vita_partners = if language_routing
76+
VitaPartner.with_language_capability(@intake&.preferred_interview_language)
77+
else
78+
VitaPartner
79+
end
80+
end
81+
5482
def previous_year_partner
5583
return false unless @intake
5684

@@ -101,8 +129,8 @@ def vita_partner_from_itin_enabled
101129
def vita_partner_from_zip_code
102130
return unless @zip_code.present?
103131

104-
eligible_with_capacity = Organization.with_capacity.joins(:serviced_zip_codes).
105-
where(vita_partner_zip_codes: { zip_code: @zip_code })
132+
eligible_with_capacity = @base_orgs.with_capacity.joins(:serviced_zip_codes)
133+
.where(vita_partner_zip_codes: { zip_code: @zip_code })
106134

107135
vita_partner = eligible_with_capacity.sample
108136

@@ -119,7 +147,7 @@ def vita_partner_from_state
119147
in_state_routing_fractions = StateRoutingFraction.joins(:state_routing_target)
120148
.where(state_routing_targets: { state_abbreviation: state })
121149
# get state routing fractions associated with organizations that have capacity
122-
organization_ids_with_capacity = Organization.with_capacity.pluck('id')
150+
organization_ids_with_capacity = @base_orgs.with_capacity.pluck('id')
123151
with_capacity_organization_fractions = in_state_routing_fractions
124152
.joins(:organization)
125153
.where(organization: organization_ids_with_capacity)
@@ -142,7 +170,7 @@ def vita_partner_from_state
142170
end
143171

144172
def route_to_national_overflow_partner
145-
national_overflow_locations = VitaPartner.with_capacity.where(national_overflow_location: true).order(Arel.sql("RANDOM()")).limit(1)
173+
national_overflow_locations = @base_vita_partners.with_capacity.where(national_overflow_location: true).order(Arel.sql("RANDOM()")).limit(1)
146174
vita_partner = national_overflow_locations&.first
147175
if vita_partner.present?
148176
@routing_method = :national_overflow

config/locales/en.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,12 +484,25 @@ en:
484484
itin: ITIN
485485
language: Language
486486
language_options:
487+
ar: Arabic
487488
de: German
488489
en: English
489490
es: Spanish
490491
fa: Farsi
491492
fr: French
493+
gu: Gujarati
494+
hi: Hindi
495+
ht: Haitian Creole
496+
it: Italian
497+
ja: Japanese
498+
ko: Korean
499+
pl: Polish
500+
pt: Portuguese
492501
ru: Russian
502+
te: Telugu
503+
tgl: Tagalog
504+
vi: Vietnamese
505+
yi: Yiddish
493506
zh: Mandarin
494507
languages: Languages
495508
last_four_ssn: Last 4 of SSN/ITIN

config/locales/es.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,12 +485,25 @@ es:
485485
itin: Número de Identificación Personal (ITIN)
486486
language: Idioma
487487
language_options:
488+
ar: Árabe
488489
de: Alemán
489490
en: Inglés
490491
es: Español
491492
fa: Farsi
492493
fr: Francés
494+
gu: Guyaratí
495+
hi: Hindi
496+
ht: Criollo haitiano
497+
it: Italiano
498+
ja: Japonés
499+
ko: Coreano
500+
pl: Polaco
501+
pt: Portugués
493502
ru: Ruso
503+
te: Telugu
504+
tgl: Tagalo
505+
vi: Vietnamita
506+
yi: Yídish
494507
zh: Mandarín
495508
languages: Idiomas
496509
last_four_ssn: Últimos 4 de SSN)/ITIN

spec/controllers/questions/interview_scheduling_controller_spec.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,42 @@
3939
)
4040
end
4141
end
42+
43+
context "routing the client when routing service returns nil and routing_method is at_capacity" do
44+
let!(:organization_router) { double }
45+
let(:params) do
46+
{
47+
interview_scheduling_form: {
48+
interview_timing_preference: "After 11am, before 6pm",
49+
preferred_interview_language: "es",
50+
preferred_written_language: "en"
51+
}
52+
}
53+
end
54+
55+
before do
56+
allow(PartnerRoutingService).to receive(:new).and_return organization_router
57+
allow(organization_router).to receive(:determine_partner).and_return nil
58+
allow(organization_router).to receive(:routing_method).and_return :at_capacity
59+
end
60+
61+
it "saves routing method to at capacity, does not set a vita partner, does not create tax returns" do
62+
post :update, params: params
63+
64+
intake = Intake.last
65+
expect(intake.client.routing_method).to eq("at_capacity")
66+
expect(intake.client.vita_partner).to eq nil
67+
expect(PartnerRoutingService).to have_received(:new).with(
68+
{
69+
intake: intake,
70+
source_param: intake.source,
71+
zip_code: intake.zip_code
72+
}
73+
)
74+
expect(organization_router).to have_received(:determine_partner)
75+
expect(intake.tax_returns.count).to eq 0
76+
expect(response).to redirect_to Questions::AtCapacityController.to_path_helper
77+
end
78+
end
4279
end
4380
end

spec/controllers/questions/personal_info_controller_spec.rb

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@
3434
end
3535

3636
context "with correct params and not at capacity" do
37-
it "directs to ssn page" do
37+
it "directs to interview scheduling page" do
3838
post :update, params: params
39-
expect(response).to redirect_to Questions::SsnItinController.to_path_helper
39+
expect(response).to redirect_to Questions::InterviewSchedulingController.to_path_helper
4040
end
4141
end
4242

@@ -90,34 +90,6 @@
9090
end
9191
end
9292

93-
context "routing the client when routing service returns nil and routing_method is at_capacity" do
94-
let!(:organization_router) { double }
95-
96-
before do
97-
allow(PartnerRoutingService).to receive(:new).and_return organization_router
98-
allow(organization_router).to receive(:determine_partner).and_return nil
99-
allow(organization_router).to receive(:routing_method).and_return :at_capacity
100-
end
101-
102-
it "saves routing method to at capacity, does not set a vita partner, does not create tax returns" do
103-
post :update, params: params
104-
105-
intake = Intake.last
106-
expect(intake.client.routing_method).to eq("at_capacity")
107-
expect(intake.client.vita_partner).to eq nil
108-
expect(PartnerRoutingService).to have_received(:new).with(
109-
{
110-
intake: intake,
111-
source_param: intake.source,
112-
zip_code: "80309"
113-
}
114-
)
115-
expect(organization_router).to have_received(:determine_partner)
116-
expect(intake.tax_returns.count).to eq 0
117-
expect(response).to redirect_to Questions::AtCapacityController.to_path_helper
118-
end
119-
end
120-
12193
it "sends an event to mixpanel without PII" do
12294
post :update, params: params
12395

0 commit comments

Comments
 (0)