diff --git a/app/models/location.rb b/app/models/location.rb index 8fe580ee8..3c816dcd5 100644 --- a/app/models/location.rb +++ b/app/models/location.rb @@ -27,7 +27,52 @@ class Location < ApplicationRecord scope :active_in_2_weeks, -> { where(confirmed: true).and(started_in_2_weeks) } scope :confirmed, -> { where(confirmed: true) } scope :unconfirmed, -> { where.not(confirmed: true) } - scope :filter_by_status, ->(status, _user = nil) { where status: } + scope :filter_by_status, ->(statuses, _user = nil) { + filtered_records = all + scopes = [] + + statuses.each do |status| + if respond_to?(status, true) + status == "active" ? scopes << send("active_status") : scopes << send(status) + end + end + + filtered_records = filtered_records.left_outer_joins(:location_deactivation_periods).merge(scopes.reduce(&:or)) if scopes.any? + + filtered_records + } + + scope :incomplete, -> { + where(confirmed: false) + } + + scope :deactivated, -> { + merge(LocationDeactivationPeriod.deactivations_without_reactivation) + .where("location_deactivation_periods.deactivation_date <= ?", Time.zone.now) + } + + scope :deactivating_soon, -> { + merge(LocationDeactivationPeriod.deactivations_without_reactivation) + .where("location_deactivation_periods.deactivation_date > ?", Time.zone.now) + } + + scope :reactivating_soon, -> { + where.not("location_deactivation_periods.reactivation_date IS NULL") + .order("location_deactivation_periods.created_at DESC") + .where("location_deactivation_periods.reactivation_date > ?", Time.zone.now) + } + + scope :activating_soon, -> { + where("startdate > ?", Time.zone.now) + } + + scope :active_status, -> { + where.not(id: joins(:location_deactivation_periods).reactivating_soon.pluck(:id)) + .where.not(id: joins(:location_deactivation_periods).deactivated.pluck(:id)) + .where.not(id: incomplete.pluck(:id)) + .where.not(id: joins(:location_deactivation_periods).deactivating_soon.pluck(:id)) + .where.not(id: activating_soon.pluck(:id)) + } LOCAL_AUTHORITIES = LocalAuthority.all.map { |la| [la.name, la.code] }.to_h @@ -99,10 +144,6 @@ class Location < ApplicationRecord :active end - def self.filter_by_status(statuses, _user = nil) - where(id: all.select { |record| statuses.include?(record.status.to_s) }) - end - def active? status == :active end diff --git a/spec/models/location_spec.rb b/spec/models/location_spec.rb index 1fe359136..6636e30a8 100644 --- a/spec/models/location_spec.rb +++ b/spec/models/location_spec.rb @@ -930,6 +930,79 @@ RSpec.describe Location, type: :model do end end + describe "filter by status" do + let!(:incomplete_location) { FactoryBot.create(:location, :incomplete, startdate: Time.zone.local(2022, 4, 1)) } + let!(:active_location) { FactoryBot.create(:location, startdate: Time.zone.local(2022, 4, 1)) } + let(:deactivating_soon_location) { FactoryBot.create(:location, startdate: Time.zone.local(2022, 4, 1)) } + let(:deactivated_location) { FactoryBot.create(:location, startdate: Time.zone.local(2022, 4, 1)) } + let(:reactivating_soon_location) { FactoryBot.create(:location, startdate: Time.zone.local(2022, 4, 1)) } + let!(:activating_soon_location) { FactoryBot.create(:location, startdate: Time.zone.local(2022, 7, 7)) } + + before do + Timecop.freeze(2022, 6, 7) + FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 8, 8), location: deactivating_soon_location) + deactivating_soon_location.save! + FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 6), location: deactivated_location) + deactivated_location.save! + FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 7), reactivation_date: Time.zone.local(2022, 6, 8), location: reactivating_soon_location) + reactivating_soon_location.save! + end + + after do + Timecop.unfreeze + end + + context "when filtering by incomplete status" do + it "returns only incomplete locations" do + expect(described_class.filter_by_status(["incomplete"]).count).to eq(1) + expect(described_class.filter_by_status(["incomplete"]).first).to eq(incomplete_location) + end + end + + context "when filtering by active status" do + it "returns only active locations" do + expect(described_class.filter_by_status(["active"]).count).to eq(1) + expect(described_class.filter_by_status(["active"]).first).to eq(active_location) + end + end + + context "when filtering by deactivating_soon status" do + it "returns only deactivating_soon locations" do + expect(described_class.filter_by_status(["deactivating_soon"]).count).to eq(1) + expect(described_class.filter_by_status(["deactivating_soon"]).first).to eq(deactivating_soon_location) + end + end + + context "when filtering by deactivated status" do + it "returns only deactivated locations" do + expect(described_class.filter_by_status(["deactivated"]).count).to eq(1) + expect(described_class.filter_by_status(["deactivated"]).first).to eq(deactivated_location) + end + end + + context "when filtering by reactivating_soon status" do + it "returns only reactivating_soon locations" do + expect(described_class.filter_by_status(["reactivating_soon"]).count).to eq(1) + expect(described_class.filter_by_status(["reactivating_soon"]).first).to eq(reactivating_soon_location) + end + end + + context "when filtering by activating_soon status" do + it "returns only activating_soon locations" do + expect(described_class.filter_by_status(["activating_soon"]).count).to eq(1) + expect(described_class.filter_by_status(["activating_soon"]).first).to eq(activating_soon_location) + end + end + + context "when filtering by multiple statuses" do + it "returns only activating_soon locations" do + expect(described_class.filter_by_status(["deactivating_soon", "activating_soon"]).count).to eq(2) + expect(described_class.filter_by_status(["deactivating_soon", "activating_soon"])).to include(activating_soon_location) + expect(described_class.filter_by_status(["deactivating_soon", "activating_soon"])).to include(deactivating_soon_location) + end + end + end + describe "available_from" do context "when there is a startdate" do let(:location) { FactoryBot.build(:location, startdate: Time.zone.local(2022, 4, 6)) }