diff --git a/app/forms/hub/zip_code_routing_form.rb b/app/forms/hub/zip_code_routing_form.rb
index d91d0e8a4a..c80ea77642 100644
--- a/app/forms/hub/zip_code_routing_form.rb
+++ b/app/forms/hub/zip_code_routing_form.rb
@@ -22,15 +22,9 @@ def vita_partner_id
end
def unused_zipcode
- existing = VitaPartnerZipCode.includes(:vita_partner).find_by(zip_code: @params[:zip_code])
+ existing = VitaPartnerZipCode.find_by(zip_code: @params[:zip_code], vita_partner_id: vita_partner.id)
if existing.present?
- if existing.vita_partner == vita_partner
- errors.add(:zip_code, I18n.t("hub.zip_codes.already_applied", zip_code: existing.zip_code))
- else
- params = { anchor: "zip-code-routing-form", id: existing.vita_partner.id }
- path = existing.vita_partner.organization? ? edit_hub_organization_path(params) : edit_hub_site_path(params)
- errors.add(:zip_code, I18n.t("hub.zip_codes.already_taken", vita_partner_name: existing.vita_partner.name, vita_partner_path: path, zip_code: existing.zip_code))
- end
+ errors.add(:zip_code, I18n.t("hub.zip_codes.already_applied", zip_code: existing.zip_code))
end
end
diff --git a/app/models/vita_partner_zip_code.rb b/app/models/vita_partner_zip_code.rb
index 0e7cacd1f5..1879343f17 100644
--- a/app/models/vita_partner_zip_code.rb
+++ b/app/models/vita_partner_zip_code.rb
@@ -11,7 +11,7 @@
# Indexes
#
# index_vita_partner_zip_codes_on_vita_partner_id (vita_partner_id)
-# index_vita_partner_zip_codes_on_zip_code (zip_code) UNIQUE
+# index_vita_partner_zip_codes_on_zip_code (zip_code)
#
# Foreign Keys
#
@@ -22,7 +22,7 @@ class VitaPartnerZipCode < ApplicationRecord
belongs_to :organization, optional: true, foreign_key: 'vita_partner_id', class_name: 'Organization'
belongs_to :site, optional: true, foreign_key: 'vita_partner_id', class_name: 'Site'
validate :record_of_zip_code
- validates :zip_code, uniqueness: true
+ validates :zip_code, uniqueness: { scope: :vita_partner_id }
def city_state
ZipCodes.details(zip_code)[:name]
diff --git a/app/services/partner_routing_service.rb b/app/services/partner_routing_service.rb
index fa9b68cb50..6d31b8c20e 100644
--- a/app/services/partner_routing_service.rb
+++ b/app/services/partner_routing_service.rb
@@ -103,7 +103,8 @@ def vita_partner_from_zip_code
eligible_with_capacity = Organization.with_capacity.joins(:serviced_zip_codes).
where(vita_partner_zip_codes: { zip_code: @zip_code })
- vita_partner = eligible_with_capacity.first
+
+ vita_partner = eligible_with_capacity.sample
if vita_partner.present?
@routing_method = :zip_code
diff --git a/config/locales/en.yml b/config/locales/en.yml
index a155f9bf67..42ba822d3c 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -1396,7 +1396,6 @@ en:
waiting_on: Waiting on
zip_codes:
already_applied: "%{zip_code} is already routed to this partner."
- already_taken: "%{zip_code} is already routed to %{vita_partner_name}."
confirm: Are you sure you want to delete %{code} from %{name}?
success: Added %{code} to %{name}.
invitations:
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 12c14ec914..21c3c3284d 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -1393,7 +1393,6 @@ es:
waiting_on: En espera
zip_codes:
already_applied: "%{zip_code} ya está enrutado a este socio."
- already_taken: "%{zip_code} ya está enrutado a %{vita_partner_name}."
confirm: "¿Estás segura de que quieres eliminar? %{code} de %{name}?"
success: Se agregó %{code} a %{name}.
invitations:
diff --git a/db/migrate/20251119181821_remove_zipcode_indexing_to_partner.rb b/db/migrate/20251119181821_remove_zipcode_indexing_to_partner.rb
new file mode 100644
index 0000000000..5ae76adca9
--- /dev/null
+++ b/db/migrate/20251119181821_remove_zipcode_indexing_to_partner.rb
@@ -0,0 +1,13 @@
+class RemoveZipcodeIndexingToPartner < ActiveRecord::Migration[7.1]
+ disable_ddl_transaction!
+
+ def up
+ remove_index :vita_partner_zip_codes, :zip_code
+ add_index :vita_partner_zip_codes, :zip_code, algorithm: :concurrently
+ end
+
+ def down
+ remove_index :vita_partner_zip_codes, :zip_code
+ add_index :vita_partner_zip_codes, :zip_code, unique: true, algorithm: :concurrently
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index dc35d2b4fe..08c6162793 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.1].define(version: 2025_09_26_151436) do
+ActiveRecord::Schema[7.1].define(version: 2025_11_19_181821) do
# These are extensions that must be enabled in order to support this database
enable_extension "citext"
enable_extension "plpgsql"
@@ -2906,7 +2906,7 @@
t.bigint "vita_partner_id", null: false
t.string "zip_code", null: false
t.index ["vita_partner_id"], name: "index_vita_partner_zip_codes_on_vita_partner_id"
- t.index ["zip_code"], name: "index_vita_partner_zip_codes_on_zip_code", unique: true
+ t.index ["zip_code"], name: "index_vita_partner_zip_codes_on_zip_code"
end
create_table "vita_partners", force: :cascade do |t|
diff --git a/spec/controllers/hub/zip_codes_controller_spec.rb b/spec/controllers/hub/zip_codes_controller_spec.rb
index 6a62ce8b79..7ca9cded31 100644
--- a/spec/controllers/hub/zip_codes_controller_spec.rb
+++ b/spec/controllers/hub/zip_codes_controller_spec.rb
@@ -41,16 +41,39 @@
end
end
- context "when the code already exists" do
+ context "when the code already exists for a different partner" do
before do
create :vita_partner_zip_code, zip_code: zip_code, vita_partner: create(:organization)
end
- it "does not make a new source parameter" do
+ it "creates a new vita partner zip code for this partner" do
+ expect {
+ post :create, params: params, format: :js, xhr: true
+ }.to change(vita_partner.serviced_zip_codes, :count).by 1
+ end
+
+ it "increases the total count of VitaPartnerZipCodes" do
+ expect {
+ post :create, params: params, format: :js, xhr: true
+ }.to change(VitaPartnerZipCode, :count).by 1
+ end
+ end
+
+ context "when the code already exists for the same partner" do
+ before do
+ create :vita_partner_zip_code, zip_code: zip_code, vita_partner: vita_partner
+ end
+
+ it "does not create a duplicate zip code for the same partner" do
expect {
post :create, params: params, format: :js, xhr: true
}.not_to change(VitaPartnerZipCode, :count)
end
+
+ it "shows an error" do
+ post :create, params: params, format: :js, xhr: true
+ expect(flash.now[:alert]).to be_present
+ end
end
end
diff --git a/spec/factories/vita_partner_zip_codes.rb b/spec/factories/vita_partner_zip_codes.rb
index 1f1e3a953b..2496eab2df 100644
--- a/spec/factories/vita_partner_zip_codes.rb
+++ b/spec/factories/vita_partner_zip_codes.rb
@@ -11,7 +11,7 @@
# Indexes
#
# index_vita_partner_zip_codes_on_vita_partner_id (vita_partner_id)
-# index_vita_partner_zip_codes_on_zip_code (zip_code) UNIQUE
+# index_vita_partner_zip_codes_on_zip_code (zip_code)
#
# Foreign Keys
#
diff --git a/spec/features/hub/clients_searching_sorting_and_filtering_spec.rb b/spec/features/hub/clients_searching_sorting_and_filtering_spec.rb
index e5101e9406..34bb8ad5b0 100644
--- a/spec/features/hub/clients_searching_sorting_and_filtering_spec.rb
+++ b/spec/features/hub/clients_searching_sorting_and_filtering_spec.rb
@@ -41,6 +41,9 @@
expect(page).to have_text "All Clients"
+ expect(page).to have_css('.client-table')
+ expect(page).to have_css('.client-row', count: 4)
+
# Default sort order
expected_rows = [
{
diff --git a/spec/forms/hub/zip_code_routing_form_spec.rb b/spec/forms/hub/zip_code_routing_form_spec.rb
index d5db28c070..dd3ed26606 100644
--- a/spec/forms/hub/zip_code_routing_form_spec.rb
+++ b/spec/forms/hub/zip_code_routing_form_spec.rb
@@ -22,19 +22,6 @@
expect(subject.errors[:zip_code]).to include "94606 is already routed to this partner."
end
end
-
- context "when it belongs to a different vita partner" do
- let(:other_vita_partner) { create :organization, name: "Oregano Org" }
- before do
- create :vita_partner_zip_code, vita_partner: other_vita_partner, zip_code: zip_code
- end
-
- it "is invalid with appropriate message" do
- expect(subject.valid?).to eq false
- expect(subject.errors[:zip_code]).to include "94606 is already routed to Oregano Org."
- end
- end
-
end
context "when the zip code is not valid" do
diff --git a/spec/models/vita_partner_zip_code_spec.rb b/spec/models/vita_partner_zip_code_spec.rb
index 94c5a38c85..bb62076ee3 100644
--- a/spec/models/vita_partner_zip_code_spec.rb
+++ b/spec/models/vita_partner_zip_code_spec.rb
@@ -11,7 +11,7 @@
# Indexes
#
# index_vita_partner_zip_codes_on_vita_partner_id (vita_partner_id)
-# index_vita_partner_zip_codes_on_zip_code (zip_code) UNIQUE
+# index_vita_partner_zip_codes_on_zip_code (zip_code)
#
# Foreign Keys
#
@@ -61,7 +61,7 @@
it "is not valid" do
new_record = described_class.new(zip_code: existing_record.zip_code, vita_partner: create(:organization))
- expect(new_record).not_to be_valid
+ expect(new_record).to be_valid
end
end
diff --git a/spec/services/partner_routing_service_spec.rb b/spec/services/partner_routing_service_spec.rb
index 818e19793f..52cb40b49c 100644
--- a/spec/services/partner_routing_service_spec.rb
+++ b/spec/services/partner_routing_service_spec.rb
@@ -359,5 +359,32 @@
end
end
end
+
+ context "when multiple Vita Partners serve the same zip code" do
+ let(:vita_partner_2) { create :organization, name: "Partner 2" }
+ before do
+ create :vita_partner_zip_code, zip_code: "94606", vita_partner: vita_partner_2
+ end
+
+ subject { PartnerRoutingService.new(zip_code: "94606") }
+
+ it "routes to one of the partners with capacity" do
+ result = subject.determine_partner
+
+ expect([vita_partner, vita_partner_2]).to include(result)
+ expect(subject.routing_method).to eq :zip_code
+ end
+
+ context "when one partner is at capacity" do
+ before do
+ vita_partner.update(capacity_limit: 0)
+ end
+
+ it "routes only to partners with capacity" do
+ expect(subject.determine_partner).to eq vita_partner_2
+ expect(subject.routing_method).to eq :zip_code
+ end
+ end
+ end
end
end