From 9b0cbac406e60a7a93e499f269d2102d3adbf59c Mon Sep 17 00:00:00 2001 From: Manny Dinssa <44172848+Dinssa@users.noreply.github.com> Date: Thu, 25 Jul 2024 15:26:30 +0100 Subject: [PATCH] CLDC 2954 handle schemes with only deactivated locations (#2510) --- app/models/location.rb | 27 ++++--- app/models/scheme.rb | 10 +++ app/models/validations/setup_validations.rb | 19 ++--- app/models/validations/shared_validations.rb | 9 +++ app/views/schemes/_scheme_list.html.erb | 7 +- app/views/schemes/show.html.erb | 3 + config/locales/en.yml | 7 +- .../validations/setup_validations_spec.rb | 79 +++++++++++++++++++ 8 files changed, 139 insertions(+), 22 deletions(-) diff --git a/app/models/location.rb b/app/models/location.rb index dfcbef41b..2b652c659 100644 --- a/app/models/location.rb +++ b/app/models/location.rb @@ -22,7 +22,6 @@ class Location < ApplicationRecord scope :search_by_name, ->(name) { where("name ILIKE ?", "%#{name}%") } scope :search_by, ->(param) { search_by_name(param).or(search_by_postcode(param)) } scope :started, -> { where("locations.startdate <= ?", Time.zone.today).or(where(startdate: nil)) } - scope :active, -> { where(confirmed: true).and(started) } scope :started_in_2_weeks, -> { where("locations.startdate <= ?", Time.zone.today + 2.weeks).or(where(startdate: nil)) } scope :active_in_2_weeks, -> { where(confirmed: true).and(started_in_2_weeks) } scope :confirmed, -> { where(confirmed: true) } @@ -62,25 +61,25 @@ class Location < ApplicationRecord merge(Organisation.filter_by_inactive) } - scope :deactivated_directly, lambda { + scope :deactivated_directly, lambda { |date = Time.zone.now| merge(LocationDeactivationPeriod.deactivations_without_reactivation) - .where("location_deactivation_periods.deactivation_date <= ?", Time.zone.now) + .where("location_deactivation_periods.deactivation_date <= ?", date) } - scope :deactivating_soon, lambda { + scope :deactivating_soon, lambda { |date = Time.zone.now| merge(LocationDeactivationPeriod.deactivations_without_reactivation) - .where("location_deactivation_periods.deactivation_date > ?", Time.zone.now) + .where("location_deactivation_periods.deactivation_date > ?", date) .where.not(id: joins(scheme: [:owning_organisation]).deactivated_by_organisation.pluck(:id)) } - scope :reactivating_soon, lambda { + scope :reactivating_soon, lambda { |date = Time.zone.now| where.not("location_deactivation_periods.reactivation_date IS NULL") - .where("location_deactivation_periods.reactivation_date > ?", Time.zone.now) + .where("location_deactivation_periods.reactivation_date > ?", date) .where.not(id: joins(scheme: [:owning_organisation]).deactivated_by_organisation.pluck(:id)) } - scope :activating_soon, lambda { - where("locations.startdate > ?", Time.zone.now) + scope :activating_soon, lambda { |date = Time.zone.now| + where("locations.startdate > ?", date) } scope :active_status, lambda { @@ -92,6 +91,14 @@ class Location < ApplicationRecord .where.not(id: activating_soon.pluck(:id)) } + scope :active, lambda { |date = Time.zone.now| + where.not(id: joins(:location_deactivation_periods).reactivating_soon(date).pluck(:id)) + .where.not(id: joins(scheme: [:owning_organisation]).deactivated_by_organisation.pluck(:id)) + .where.not(id: joins(:location_deactivation_periods).deactivated_directly(date).pluck(:id)) + .where.not(id: incomplete.pluck(:id)) + .where.not(id: activating_soon(date).pluck(:id)) + } + scope :visible, -> { where(discarded_at: nil) } LOCAL_AUTHORITIES = LocalAuthority.all.map { |la| [la.name, la.code] }.to_h @@ -157,9 +164,9 @@ class Location < ApplicationRecord return :incomplete unless confirmed return :deactivated if scheme.owning_organisation.status_at(date) == :deactivated || open_deactivation&.deactivation_date.present? && date >= open_deactivation.deactivation_date + return :activating_soon if startdate.present? && date < startdate return :deactivating_soon if open_deactivation&.deactivation_date.present? && date < open_deactivation.deactivation_date return :reactivating_soon if last_deactivation_before(date)&.reactivation_date.present? && date < last_deactivation_before(date).reactivation_date - return :activating_soon if startdate.present? && date < startdate :active end diff --git a/app/models/scheme.rb b/app/models/scheme.rb index 5b95ed98a..07ec14731 100644 --- a/app/models/scheme.rb +++ b/app/models/scheme.rb @@ -295,6 +295,16 @@ class Scheme < ApplicationRecord status == :active end + def has_active_locations? + locations.active.exists? + end + + def has_active_locations_on_date?(date) + return false unless date + + locations.active(date).exists? + end + def reactivating_soon? status == :reactivating_soon end diff --git a/app/models/validations/setup_validations.rb b/app/models/validations/setup_validations.rb index b06217ad5..528126ab7 100644 --- a/app/models/validations/setup_validations.rb +++ b/app/models/validations/setup_validations.rb @@ -72,15 +72,6 @@ module Validations::SetupValidations end end - def validate_location(record) - location_during_startdate_validation(record) - - if record.location&.status == :incomplete - record.errors.add :location_id, :incomplete, message: I18n.t("validations.setup.location.incomplete") - record.errors.add :scheme_id, :incomplete, message: I18n.t("validations.setup.location.incomplete") - end - end - def validate_scheme_has_confirmed_locations_validation(record) return unless record.scheme @@ -95,6 +86,16 @@ module Validations::SetupValidations end scheme_during_startdate_validation(record) + tenancy_startdate_with_scheme_locations(record) + end + + def validate_location(record) + location_during_startdate_validation(record) + + if record.location&.status == :incomplete + record.errors.add :location_id, :incomplete, message: I18n.t("validations.setup.location.incomplete") + record.errors.add :scheme_id, :incomplete, message: I18n.t("validations.setup.location.incomplete") + end end def validate_managing_organisation_data_sharing_agremeent_signed(record) diff --git a/app/models/validations/shared_validations.rb b/app/models/validations/shared_validations.rb index f3b0a9c34..ab9f9d7a8 100644 --- a/app/models/validations/shared_validations.rb +++ b/app/models/validations/shared_validations.rb @@ -87,6 +87,7 @@ module Validations::SharedValidations def scheme_during_startdate_validation(record) scheme_inactive_status = inactive_status(record.startdate, record.scheme) + if scheme_inactive_status.present? date, scope, deactivation_date = scheme_inactive_status.values_at(:date, :scope, :deactivation_date) record.errors.add :startdate, I18n.t("validations.setup.startdate.scheme.#{scope}.startdate", name: record.scheme.service_name, date:, deactivation_date:) @@ -112,6 +113,14 @@ module Validations::SharedValidations { scope: status, date: date&.to_formatted_s(:govuk_date), deactivation_date: closest_reactivation&.deactivation_date&.to_formatted_s(:govuk_date) } end + def tenancy_startdate_with_scheme_locations(record) + return if record.scheme.blank? || record.startdate.blank? + return if record.scheme.has_active_locations_on_date?(record.startdate) + + record.errors.add :startdate, I18n.t("validations.setup.startdate.scheme.locations_inactive.startdate", name: record.scheme.service_name) + record.errors.add :scheme_id, I18n.t("validations.setup.startdate.scheme.locations_inactive.scheme_id", name: record.scheme.service_name) + end + def shared_validate_partner_count(record, max_people) return if record.form.start_year_after_2024? diff --git a/app/views/schemes/_scheme_list.html.erb b/app/views/schemes/_scheme_list.html.erb index 83908d85b..967295236 100644 --- a/app/views/schemes/_scheme_list.html.erb +++ b/app/views/schemes/_scheme_list.html.erb @@ -26,7 +26,12 @@ <% row.with_cell(text: scheme.owning_organisation&.name) %> <% row.with_cell(text: scheme.id_to_display) %> <% row.with_cell(text: scheme.locations.visible&.count) %> - <% row.with_cell(text: status_tag_from_resource(scheme)) %> + <% row.with_cell do %> + <%= status_tag_from_resource(scheme) %> + <% if scheme.active? && !scheme.has_active_locations? %> + <%= content_tag(:div, "No currently active locations", class: "app-!-colour-muted", style: "margin-top: 10px") %> + <% end %> + <% end %> <% end %> <% end %> <% end %> diff --git a/app/views/schemes/show.html.erb b/app/views/schemes/show.html.erb index 1aefadef5..6cefa5847 100644 --- a/app/views/schemes/show.html.erb +++ b/app/views/schemes/show.html.erb @@ -26,6 +26,9 @@
Status
<%= details_html({ name: "Status", value: status_tag_from_resource(@scheme), id: "status" }) %> + <% if @scheme.confirmed? && @scheme.active? && !@scheme.has_active_locations? %> + No currently active locations + <% end %> <% if @scheme.confirmed? && @scheme.locations.confirmed.none? && LocationPolicy.new(current_user, @scheme.locations.new).create? %> Complete this scheme by adding a location using the <%= govuk_link_to("‘locations’ tab", scheme_locations_path(@scheme)) %>. <% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 326e92732..a70e7082b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -286,8 +286,8 @@ en: location: deactivated: - startdate: "The location %{postcode} was deactivated on %{date} and was not available on the day you entered. Select another location or edit the tenancy start date" - location_id: "The location %{postcode} was deactivated on %{date} and was not available on the day you entered. Select another location or edit the tenancy start date" + startdate: "The location %{postcode} is inactive on this date. Enter another date or choose another location." + location_id: "This location is not active on the tenancy start date. Choose another location or edit the tenancy start date." activating_soon: startdate: "The location %{postcode} is not available until %{date}. Enter a tenancy start date after %{date}" location_id: "The location %{postcode} is not available until %{date}. Select another location or edit the tenancy start date" @@ -301,6 +301,9 @@ en: reactivating_soon: startdate: "The scheme %{name} is not available until %{date}. Enter a tenancy start date after %{date}" scheme_id: "The scheme %{name} is not available until %{date}. Select another scheme or edit the tenancy start date" + locations_inactive: + startdate: "The scheme %{name} has no locations that are active on this date. Enter another date or choose another scheme." + scheme_id: "The scheme %{name} has no locations that are active on this date. Enter another date or choose another scheme." owning_organisation: invalid: "Please select the owning organisation or managing organisation that you belong to" data_sharing_agreement_not_signed: "The organisation must accept the Data Sharing Agreement before it can be selected as the owning organisation." diff --git a/spec/models/validations/setup_validations_spec.rb b/spec/models/validations/setup_validations_spec.rb index 14a9b45ae..884c4e22e 100644 --- a/spec/models/validations/setup_validations_spec.rb +++ b/spec/models/validations/setup_validations_spec.rb @@ -512,6 +512,60 @@ RSpec.describe Validations::SetupValidations do end end + context "with a scheme with no locations active on the start date & no location set" do + let(:scheme) { create(:scheme) } + let(:location) { create(:location, scheme:) } + + before do + location_deactivation_period = build(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), location:) + location_deactivation_period.save!(validate: false) + location.reload + end + + it "produces error when scheme does not have any active locations on the tenancy start date" do + record.startdate = Time.zone.local(2022, 7, 5) + record.scheme = scheme + setup_validator.validate_scheme(record) + expect(record.errors["startdate"]).to include(match I18n.t("validations.setup.startdate.scheme.locations_inactive.startdate", name: scheme.service_name)) + expect(record.errors["scheme_id"]).to include(match I18n.t("validations.setup.startdate.scheme.locations_inactive.startdate", name: scheme.service_name)) + end + + it "produces no error when scheme has active locations on the tenancy start date" do + record.startdate = Time.zone.local(2022, 6, 1) + record.scheme = scheme + setup_validator.validate_scheme(record) + expect(record.errors["startdate"]).to be_empty + end + end + + context "with a scheme with no locations active on the start date & location also set" do + let(:scheme) { create(:scheme) } + let(:location) { create(:location, scheme:) } + + before do + location_deactivation_period = build(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), location:) + location_deactivation_period.save!(validate: false) + location.reload + end + + it "produces error when scheme does not have any active locations on the tenancy start date" do + record.startdate = Time.zone.local(2022, 7, 5) + record.scheme = scheme + record.location = location + setup_validator.validate_scheme(record) + expect(record.errors["startdate"]).to include(match I18n.t("validations.setup.startdate.scheme.locations_inactive.startdate", name: scheme.service_name)) + expect(record.errors["startdate"]).not_to include(match I18n.t("validations.setup.startdate.location.deactivated.startdate", postcode: location.postcode)) + end + + it "produces no error when scheme has active locations on the tenancy start date" do + record.startdate = Time.zone.local(2022, 6, 1) + record.scheme = scheme + record.location = location + setup_validator.validate_scheme(record) + expect(record.errors["startdate"]).to be_empty + end + end + context "with an incomplete scheme" do let(:scheme) { create(:scheme, :incomplete) } @@ -657,6 +711,31 @@ RSpec.describe Validations::SetupValidations do expect(record.errors["location_id"]).to be_empty end end + + context "with the chosen location inactive on the tenancy start date" do + let(:scheme) { create(:scheme) } + let(:location) { create(:location, scheme:) } + + before do + location_deactivation_period = build(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), location:) + location_deactivation_period.save!(validate: false) + location.reload + end + + it "produces the location error when the chosen location is inactive on the tenancy start date" do + record.startdate = Time.zone.local(2022, 7, 5) + record.location = location + setup_validator.validate_location(record) + expect(record.errors["startdate"]).to include(match I18n.t("validations.setup.startdate.location.deactivated.startdate", postcode: location.postcode)) + end + + it "produces no error when the chosen location is active on the tenancy start date" do + record.startdate = Time.zone.local(2022, 6, 1) + record.location = location + setup_validator.validate_location(record) + expect(record.errors["startdate"]).to be_empty + end + end end describe "#validate_organisation" do