diff --git a/app/models/signup_round.rb b/app/models/signup_round.rb index 0e9ff52982a..6c00d3968be 100644 --- a/app/models/signup_round.rb +++ b/app/models/signup_round.rb @@ -41,7 +41,12 @@ class SignupRound < ApplicationRecord scope :reverse_chronological, -> { order("start DESC NULLS LAST") } def self.execute_currently_due_rounds! - currently_due.chronological.includes(:convention).find_each(&:execute!) + # can't use find_each with ordered scopes so we'll roll our own + round_ids = currently_due.chronological.pluck(:id) + round_ids.in_groups_of(100) do |batch| + rounds = SignupRound.where(id: batch).includes(:convention) + rounds.sort_by { |round| batch.index(round.id) }.each(&:execute!) + end end def execute! diff --git a/db/migrate/20251109200750_enforce_ranked_choice_order_for_ranked_choice_signup_rounds.rb b/db/migrate/20251109200750_enforce_ranked_choice_order_for_ranked_choice_signup_rounds.rb new file mode 100644 index 00000000000..9cebe966064 --- /dev/null +++ b/db/migrate/20251109200750_enforce_ranked_choice_order_for_ranked_choice_signup_rounds.rb @@ -0,0 +1,13 @@ +class EnforceRankedChoiceOrderForRankedChoiceSignupRounds < ActiveRecord::Migration[8.0] + def change + reversible { |dir| dir.up { execute <<~SQL } } + UPDATE signup_rounds + SET ranked_choice_order = 'asc' + WHERE automation_action = 'execute_ranked_choice' AND ranked_choice_order IS NULL + SQL + + add_check_constraint :signup_rounds, + "automation_action != 'execute_ranked_choice' OR ranked_choice_order IS NOT NULL", + name: "require_order_for_ranked_choice_rounds" + end +end diff --git a/db/structure.sql b/db/structure.sql index 93a8c2f0236..e94bd92efcf 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -2665,7 +2665,8 @@ CREATE TABLE public.signup_rounds ( ranked_choice_order text, executed_at timestamp without time zone, automation_action text, - CONSTRAINT chk_rails_4c92d587c4 CHECK (((maximum_event_signups = ANY (ARRAY['not_yet'::text, 'not_now'::text, 'unlimited'::text])) OR ((maximum_event_signups)::integer >= 1))) + CONSTRAINT chk_rails_4c92d587c4 CHECK (((maximum_event_signups = ANY (ARRAY['not_yet'::text, 'not_now'::text, 'unlimited'::text])) OR ((maximum_event_signups)::integer >= 1))), + CONSTRAINT require_order_for_ranked_choice_rounds CHECK (((automation_action <> 'execute_ranked_choice'::text) OR (ranked_choice_order IS NOT NULL))) ); @@ -6134,6 +6135,7 @@ ALTER TABLE ONLY public.cms_files_pages SET search_path TO "$user", public; INSERT INTO "schema_migrations" (version) VALUES +('20251109200750'), ('20251001173716'), ('20250906190727'), ('20250324175507'), diff --git a/test/models/signup_round_test.rb b/test/models/signup_round_test.rb index 00ffd7e4a8e..6a7f7f7fea3 100644 --- a/test/models/signup_round_test.rb +++ b/test/models/signup_round_test.rb @@ -42,7 +42,13 @@ class SignupRoundTest < ActiveSupport::TestCase mock_service = Minitest::Mock.new mock_service.expect :call!, nil convention = FactoryBot.create(:convention, signup_automation_mode: "ranked_choice") - signup_round = FactoryBot.create(:signup_round, convention:, automation_action: "execute_ranked_choice") + signup_round = + FactoryBot.create( + :signup_round, + convention:, + automation_action: "execute_ranked_choice", + ranked_choice_order: "asc" + ) ExecuteRankedChoiceSignupRoundService.stub :new, mock_service do signup_round.execute!