diff --git a/app/controllers/schemes_controller.rb b/app/controllers/schemes_controller.rb index c0a36b920..9df4bebe7 100644 --- a/app/controllers/schemes_controller.rb +++ b/app/controllers/schemes_controller.rb @@ -51,17 +51,34 @@ class SchemesController < ApplicationController end def deactivate_confirm - @affected_logs = @scheme.lettings_logs.visible.after_date(params[:deactivation_date]) - if @affected_logs.count.zero? + @deactivation_date = Time.zone.parse(params[:deactivation_date]) + @affected_logs = @scheme.lettings_logs.visible.after_date(@deactivation_date) + @deactivation_date_type = params[:deactivation_date_type] + + scheme_locations = @scheme.locations.confirmed + + locations_active_on_deactivation_date, remaining_locations = scheme_locations.partition do |location| + location.status_at(@deactivation_date) == :active + end + + locations_deactivating_after_deactivation_date, remaining_locations = remaining_locations.partition do |location| + location.status_at(@deactivation_date) == :deactivating_soon || location.status_at(@deactivation_date) == :reactivating_soon + end + + locations_startdate_after_deactivation_date, = remaining_locations.partition do |location| + location.status_at(@deactivation_date) == :activating_soon + end + + @affected_locations = locations_active_on_deactivation_date + locations_deactivating_after_deactivation_date + locations_startdate_after_deactivation_date + + if @affected_logs.count.zero? && @affected_locations.count.zero? deactivate - else - @deactivation_date = params[:deactivation_date] - @deactivation_date_type = params[:deactivation_date_type] end end def deactivate - if @scheme.open_deactivation&.update!(deactivation_date: params[:deactivation_date]) || @scheme.scheme_deactivation_periods.create!(deactivation_date: params[:deactivation_date]) + deactivation_date = params[:deactivation_date] + if @scheme.open_deactivation&.update!(deactivation_date:) || @scheme.scheme_deactivation_periods.create!(deactivation_date:) logs = reset_location_and_scheme_for_logs! flash[:notice] = deactivate_success_notice @@ -370,4 +387,22 @@ private def session_filters filter_manager.session_filters end + + def deactivate_locations(deactivation_date) + # Add a deactivation period to @locations_without_deactivation_period + @locations_without_deactivation_period.each do |location| + LocationDeactivationPeriod.create!(location:, deactivation_date:) + end + + # Change the deactivation period for @locations_with_future_deactivation_period + @locations_with_future_deactivation_period.each do |location| + location_deactivation_period = location.location_deactivation_periods.find_by("deactivation_date > ?", Time.zone.now) + location_deactivation_period.update!(deactivation_date:) if location_deactivation_period + end + + # Clear the start date for @locations_with_future_start_date + @locations_with_future_start_date.each do |location| + location.update!(startdate: nil) + end + end end diff --git a/app/helpers/locations_helper.rb b/app/helpers/locations_helper.rb index f963c7040..7d2b6dee1 100644 --- a/app/helpers/locations_helper.rb +++ b/app/helpers/locations_helper.rb @@ -70,7 +70,7 @@ module LocationsHelper def toggle_location_link(location) return govuk_button_link_to "Deactivate this location", scheme_location_new_deactivation_path(location.scheme, location), warning: true if location.active? || location.deactivates_in_a_long_time? - return govuk_button_link_to "Reactivate this location", scheme_location_new_reactivation_path(location.scheme, location) if location.deactivated? + return govuk_button_link_to "Reactivate this location", scheme_location_new_reactivation_path(location.scheme, location) if location.deactivated? && !location.deactivated_by_scheme? end def delete_location_link(location) @@ -107,6 +107,14 @@ private periods << ActivePeriod.new(deactivation.reactivation_date, nil) end + if location.deactivated_by_scheme? || location.deactivating_soon_by_scheme? + scheme_periods = location.scheme.scheme_deactivation_periods.sort_by(&:deactivation_date) + scheme_periods.each do |scheme_period| + periods.last.to = scheme_period.deactivation_date + periods << ActivePeriod.new(scheme_period.reactivation_date, nil) + end + end + remove_overlapping_and_empty_periods(periods) end diff --git a/app/helpers/schemes_helper.rb b/app/helpers/schemes_helper.rb index f96f9e4c8..0e318d283 100644 --- a/app/helpers/schemes_helper.rb +++ b/app/helpers/schemes_helper.rb @@ -12,7 +12,7 @@ module SchemesHelper def toggle_scheme_link(scheme) return govuk_button_link_to "Deactivate this scheme", scheme_new_deactivation_path(scheme), warning: true if scheme.active? || scheme.deactivates_in_a_long_time? - return govuk_button_link_to "Reactivate this scheme", scheme_new_reactivation_path(scheme) if scheme.deactivated? + return govuk_button_link_to "Reactivate this scheme", scheme_new_reactivation_path(scheme) if scheme.deactivated? || scheme.deactivating_soon? end def delete_scheme_link(scheme) @@ -76,6 +76,15 @@ module SchemesHelper end end + def scheme_status_hint(scheme) + case scheme.status + when :deactivating_soon + "This scheme deactivates on #{scheme.last_deactivation_date.to_formatted_s(:govuk_date)}. Any locations you add will be deactivated on the same date. Reactivate the scheme to add locations active after this date." + when :deactivated + "This scheme deactivated on #{scheme.last_deactivation_date.to_formatted_s(:govuk_date)}. Any locations you add will be deactivated on the same date. Reactivate the scheme to add locations active after this date." + end + end + private ActivePeriod = Struct.new(:from, :to) diff --git a/app/models/location.rb b/app/models/location.rb index 8efa4ee28..917c4338c 100644 --- a/app/models/location.rb +++ b/app/models/location.rb @@ -40,6 +40,7 @@ class Location < ApplicationRecord filtered_records = filtered_records .left_outer_joins(:location_deactivation_periods) .joins(scheme: [:owning_organisation]) + .left_outer_joins(scheme: :scheme_deactivation_periods) .order("location_deactivation_periods.created_at DESC") .merge(scopes.reduce(&:or)) end @@ -55,12 +56,17 @@ class Location < ApplicationRecord scope :deactivated, lambda { deactivated_by_organisation .or(deactivated_directly) + .or(deactivated_by_scheme) } scope :deactivated_by_organisation, lambda { merge(Organisation.filter_by_inactive) } + scope :deactivated_by_scheme, lambda { + merge(Scheme.deactivated) + } + scope :deactivated_directly, lambda { |date = Time.zone.now| merge(LocationDeactivationPeriod.deactivations_without_reactivation) .where("location_deactivation_periods.deactivation_date <= ?", date) @@ -68,8 +74,13 @@ class Location < ApplicationRecord scope :deactivating_soon, lambda { |date = Time.zone.now| merge(LocationDeactivationPeriod.deactivations_without_reactivation) - .where("location_deactivation_periods.deactivation_date > ?", date) - .where.not(id: joins(scheme: [:owning_organisation]).deactivated_by_organisation.pluck(:id)) + .where("location_deactivation_periods.deactivation_date > ?", date) + .where.not(id: joins(scheme: [:owning_organisation]).deactivated_by_organisation.pluck(:id)) + .or(deactivating_soon_by_scheme) + } + + scope :deactivating_soon_by_scheme, lambda { + merge(Scheme.deactivating_soon) } scope :reactivating_soon, lambda { |date = Time.zone.now| @@ -84,17 +95,19 @@ class Location < ApplicationRecord scope :active_status, lambda { where.not(id: joins(:location_deactivation_periods).reactivating_soon.pluck(:id)) - .where.not(id: joins(scheme: [:owning_organisation]).deactivated_by_organisation.pluck(:id)) - .where.not(id: joins(:location_deactivation_periods).deactivated_directly.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)) + .where.not(id: joins(:location_deactivation_periods).deactivated_directly.pluck(:id)) + .where.not(id: joins(scheme: %i[owning_organisation scheme_deactivation_periods]).deactivated_by_scheme.pluck(:id)) + .where.not(id: joins(scheme: [:scheme_deactivation_periods]).deactivating_soon_by_scheme.pluck(:id)) + .where.not(id: joins(:location_deactivation_periods).deactivating_soon.pluck(:id)) + .where.not(id: incomplete.pluck(:id)) + .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: joins(scheme: %i[owning_organisation scheme_deactivation_periods]).deactivated_by_scheme.pluck(:id)) + .where.not(id: joins(scheme: [:scheme_deactivation_periods]).deactivating_soon_by_scheme.pluck(:id)) .where.not(id: incomplete.pluck(:id)) .where.not(id: activating_soon(date).pluck(:id)) } @@ -163,9 +176,9 @@ class Location < ApplicationRecord return :deleted if discarded_at.present? return :incomplete unless confirmed return :deactivated if scheme.owning_organisation.status_at(date) == :deactivated || - open_deactivation&.deactivation_date.present? && date >= open_deactivation.deactivation_date + open_deactivation&.deactivation_date.present? && date >= open_deactivation.deactivation_date || scheme.status_at(date) == :deactivated + return :deactivating_soon if open_deactivation&.deactivation_date.present? && date < open_deactivation.deactivation_date || scheme.status_at(date) == :deactivating_soon 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 :active @@ -187,6 +200,14 @@ class Location < ApplicationRecord status_at(6.months.from_now) == :deactivating_soon end + def deactivated_by_scheme? + status == :deactivated && scheme.status == :deactivated + end + + def deactivating_soon_by_scheme? + status == :deactivating_soon && scheme.status == :deactivating_soon + end + def validate_postcode if !postcode&.match(POSTCODE_REGEXP) error_message = I18n.t("validations.postcode") diff --git a/app/models/scheme.rb b/app/models/scheme.rb index 07ec14731..9d26b72c0 100644 --- a/app/models/scheme.rb +++ b/app/models/scheme.rb @@ -275,6 +275,10 @@ class Scheme < ApplicationRecord scheme_deactivation_periods.where("deactivation_date <= ?", date).order("created_at").last end + def last_deactivation_date + scheme_deactivation_periods.order(deactivation_date: :desc).first&.deactivation_date + end + def status @status ||= status_at(Time.zone.now) end @@ -313,6 +317,10 @@ class Scheme < ApplicationRecord status == :deactivated end + def deactivating_soon? + status == :deactivating_soon + end + def deactivates_in_a_long_time? status_at(6.months.from_now) == :deactivating_soon end diff --git a/app/views/locations/index.html.erb b/app/views/locations/index.html.erb index 78a362332..64d9bf286 100644 --- a/app/views/locations/index.html.erb +++ b/app/views/locations/index.html.erb @@ -56,10 +56,17 @@ <% end %> <% end %> + <% if status_hint_message = scheme_status_hint(@scheme) %> +
+ <%= status_hint_message %> +
+
+ <% end %> + <% if LocationPolicy.new(current_user, @scheme.locations.new).create? %> - <%= govuk_button_to "Add a location", scheme_locations_path(@scheme), method: "post", secondary: true %> + <%= govuk_button_to "Add a location", scheme_locations_path(@scheme), method: "post" %> <% end %> +
+ <%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "locations" } %> - -<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "locations" } %> diff --git a/app/views/schemes/deactivate_confirm.html.erb b/app/views/schemes/deactivate_confirm.html.erb index b5dda160e..b09784e5a 100644 --- a/app/views/schemes/deactivate_confirm.html.erb +++ b/app/views/schemes/deactivate_confirm.html.erb @@ -2,16 +2,41 @@ <% content_for :before_content do %> <%= govuk_back_link(href: :back) %> <% end %> -

- <%= @scheme.service_name %> - This change will affect <%= @affected_logs.count %> logs -

- <%= govuk_warning_text text: I18n.t("warnings.scheme.deactivate.review_logs") %> - <%= f.hidden_field :confirm, value: true %> - <%= f.hidden_field :deactivation_date, value: @deactivation_date %> - <%= f.hidden_field :deactivation_date_type, value: @deactivation_date_type %> -
- <%= f.govuk_submit "Deactivate this scheme" %> - <%= govuk_button_link_to "Cancel", scheme_details_path(@scheme), html: { method: :get }, secondary: true %> +
+
+ +

+ <%= @scheme.service_name %> + <% sentence_parts = [] %> + <% if @affected_logs.count > 0 %> + <% sentence_parts << pluralize(@affected_logs.count, 'log') %> + <% end %> + <% if @affected_locations.count > 0 %> + <% sentence_parts << pluralize(@affected_locations.count, 'location') %> + <% end %> + This change will affect <%= sentence_parts.join(' and ') %>. +

+ + <% if @affected_logs.count > 0 %> +

+ <%= pluralize(@affected_logs.count, 'existing log') %> using this scheme <%= @affected_logs.count == 1 ? 'has' : 'have' %> a tenancy start date after <%= @deactivation_date.to_formatted_s(:govuk_date) %>. +

+ <% end %> + + <%= govuk_warning_text text: I18n.t("warnings.scheme.deactivate.review_logs"), html_attributes: { class: "" } %> + +

+ This scheme has <%= pluralize(@affected_locations.count, 'location') %> active on <%= @deactivation_date.to_formatted_s(:govuk_date) %>. <%= @affected_locations.count == 1 ? 'This location' : 'These locations' %> will deactivate on that date. If the scheme is ever reactivated, <%= @affected_locations.count == 1 ? 'this location' : 'these locations' %> will reactivate as well. +

+
+ + <%= f.hidden_field :confirm, value: true %> + <%= f.hidden_field :deactivation_date, value: @deactivation_date %> + <%= f.hidden_field :deactivation_date_type, value: @deactivation_date_type %> +
+ <%= f.govuk_submit "Deactivate this scheme" %> + <%= govuk_button_link_to "Cancel", scheme_details_path(@scheme), html: { method: :get }, secondary: true %> +
+
<% end %> diff --git a/spec/views/schemes/deactivate_confirm.html.erb_spec.rb b/spec/views/schemes/deactivate_confirm.html.erb_spec.rb new file mode 100644 index 000000000..fd1f2a2ac --- /dev/null +++ b/spec/views/schemes/deactivate_confirm.html.erb_spec.rb @@ -0,0 +1,46 @@ +require "rails_helper" + +RSpec.describe "schemes/deactivate_confirm.html.erb", type: :view do + let(:scheme) { create(:scheme, service_name: "ABCScheme") } + let(:deactivation_date) { Time.zone.today + 1.month } + let(:affected_logs) { create_list(:lettings_log, 2, scheme:, status: 1) } + let(:affected_locations) { create_list(:location, 3, scheme:) } + let(:scheme_deactivation_period) { SchemeDeactivationPeriod.new } + + before do + assign(:scheme, scheme) + assign(:deactivation_date, deactivation_date) + assign(:affected_logs, affected_logs) + assign(:affected_locations, affected_locations) + assign(:scheme_deactivation_period, scheme_deactivation_period) + render + end + + it "displays the service name in the caption" do + expect(rendered).to have_css("span.govuk-caption-l", text: scheme.service_name) + end + + it "displays the correct heading" do + expect(rendered).to have_css("h1.govuk-heading-l", text: "This change will affect 2 logs and 3 locations.") + end + + it "displays the affected logs count" do + expect(rendered).to have_text("2 existing logs using this scheme have a tenancy start date after #{deactivation_date.to_formatted_s(:govuk_date)}.") + end + + it "displays the warning text" do + expect(rendered).to have_css(".govuk-warning-text", text: I18n.t("warnings.scheme.deactivate.review_logs")) + end + + it "displays the affected locations count" do + expect(rendered).to have_text("This scheme has 3 locations active on #{deactivation_date.to_formatted_s(:govuk_date)}.") + end + + it "renders the submit button" do + expect(rendered).to have_button("Deactivate this scheme") + end + + it "renders the cancel button" do + expect(rendered).to have_link("Cancel", href: scheme_details_path(scheme)) + end +end