From 752103404fd38357a93763491dd1f793c962e520 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Thu, 17 Nov 2022 12:50:36 +0000 Subject: [PATCH] Multiple scheme deactivations (#1004) * Remove deactivation date from schemes and add scheme deactivation periods table * Update affected logs when a scheme gets deactivated * Update status method * Display availability timeline * Update flash notice message --- app/controllers/schemes_controller.rb | 8 ++- app/helpers/schemes_helper.rb | 11 ++- app/models/lettings_log.rb | 2 +- app/models/scheme.rb | 10 +-- app/models/scheme_deactivation_period.rb | 3 + ...20221117103841_add_scheme_deactivations.rb | 10 +++ ...5_remove_deactivation_date_from_schemes.rb | 13 ++++ db/schema.rb | 18 +++-- spec/factories/scheme_deactivation_period.rb | 5 ++ spec/helpers/schemes_helper_spec.rb | 25 ++++++- spec/models/scheme_spec.rb | 68 +++++++++++++------ spec/requests/schemes_controller_spec.rb | 42 +++++++++--- 12 files changed, 169 insertions(+), 46 deletions(-) create mode 100644 app/models/scheme_deactivation_period.rb create mode 100644 db/migrate/20221117103841_add_scheme_deactivations.rb create mode 100644 db/migrate/20221117103855_remove_deactivation_date_from_schemes.rb create mode 100644 spec/factories/scheme_deactivation_period.rb diff --git a/app/controllers/schemes_controller.rb b/app/controllers/schemes_controller.rb index 6766ffe85..208394942 100644 --- a/app/controllers/schemes_controller.rb +++ b/app/controllers/schemes_controller.rb @@ -44,7 +44,7 @@ class SchemesController < ApplicationController def deactivate @scheme.run_deactivation_validations! - if @scheme.update!(deactivation_date:) + if @scheme.scheme_deactivation_periods.create!(deactivation_date:) && update_affected_logs flash[:notice] = deactivate_success_notice end redirect_to scheme_details_path(@scheme) @@ -299,7 +299,7 @@ private when :deactivated "#{@scheme.service_name} has been deactivated" when :deactivating_soon - "#{@scheme.service_name} will deactivate on #{@scheme.deactivation_date.to_formatted_s(:govuk_date)}" + "#{@scheme.service_name} will deactivate on #{deactivation_date.to_time.to_formatted_s(:govuk_date)}" end end @@ -319,4 +319,8 @@ private Time.zone.local(year.to_i, month.to_i, day.to_i) if Date.valid_date?(year.to_i, month.to_i, day.to_i) end + + def update_affected_logs + @scheme.lettings_logs.filter_by_before_startdate(deactivation_date.to_time).update!(location: nil, scheme: nil) + end end diff --git a/app/helpers/schemes_helper.rb b/app/helpers/schemes_helper.rb index 6ef82c570..6d12f6675 100644 --- a/app/helpers/schemes_helper.rb +++ b/app/helpers/schemes_helper.rb @@ -14,7 +14,7 @@ module SchemesHelper { name: "Secondary client group", value: scheme.secondary_client_group }, { name: "Level of support given", value: scheme.support_type }, { name: "Intended length of stay", value: scheme.intended_stay }, - { name: "Availability", value: "Available from #{scheme.available_from.to_formatted_s(:govuk_date)}" }, + { name: "Availability", value: scheme_availability(scheme) }, ] if FeatureToggle.scheme_toggle_enabled? @@ -26,4 +26,13 @@ module SchemesHelper end base_attributes end + + def scheme_availability(scheme) + availability = "Active from #{scheme.available_from.to_formatted_s(:govuk_date)}" + scheme.scheme_deactivation_periods.each do |deactivation| + availability << " to #{(deactivation.deactivation_date - 1.day).to_formatted_s(:govuk_date)}\nDeactivated on #{deactivation.deactivation_date.to_formatted_s(:govuk_date)}" + availability << "\nActive from #{deactivation.reactivation_date.to_formatted_s(:govuk_date)}" if deactivation.reactivation_date.present? + end + availability + end end diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb index 574694f51..fb17bb613 100644 --- a/app/models/lettings_log.rb +++ b/app/models/lettings_log.rb @@ -45,7 +45,7 @@ class LettingsLog < Log .or(filter_by_postcode(param)) .or(filter_by_id(param)) } - scope :filter_by_before_startdate, ->(date) { left_joins(:location).where("lettings_logs.startdate >= ?", date) } + scope :filter_by_before_startdate, ->(date) { where("lettings_logs.startdate >= ?", date) } AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze OPTIONAL_FIELDS = %w[first_time_property_let_as_social_housing tenancycode propcode].freeze diff --git a/app/models/scheme.rb b/app/models/scheme.rb index 5de804c58..3dfe1ff55 100644 --- a/app/models/scheme.rb +++ b/app/models/scheme.rb @@ -3,6 +3,7 @@ class Scheme < ApplicationRecord belongs_to :managing_organisation, optional: true, class_name: "Organisation" has_many :locations, dependent: :delete_all has_many :lettings_logs, class_name: "LettingsLog", dependent: :delete_all + has_many :scheme_deactivation_periods, class_name: "SchemeDeactivationPeriod" has_paper_trail @@ -22,7 +23,7 @@ class Scheme < ApplicationRecord auto_strip_attributes :service_name - attr_accessor :deactivation_date_type, :run_deactivation_validations + attr_accessor :deactivation_date_type, :deactivation_date, :run_deactivation_validations SENSITIVE = { No: 0, @@ -216,8 +217,9 @@ class Scheme < ApplicationRecord end def status - return :active if deactivation_date.blank? - return :deactivating_soon if Time.zone.now < deactivation_date + recent_deactivation = scheme_deactivation_periods.deactivations_without_reactivation.first + return :active if recent_deactivation.blank? + return :deactivating_soon if Time.zone.now < recent_deactivation.deactivation_date :deactivated end @@ -245,7 +247,7 @@ class Scheme < ApplicationRecord end else collection_start_date = FormHandler.instance.current_collection_start_date - unless deactivation_date.between?(collection_start_date, Date.new(2200, 1, 1)) + unless deactivation_date.between?(collection_start_date, Time.zone.local(2200, 1, 1)) errors.add(:deactivation_date, message: I18n.t("validations.scheme.deactivation_date.out_of_range", date: collection_start_date.to_formatted_s(:govuk_date))) end end diff --git a/app/models/scheme_deactivation_period.rb b/app/models/scheme_deactivation_period.rb new file mode 100644 index 000000000..5caba5c7a --- /dev/null +++ b/app/models/scheme_deactivation_period.rb @@ -0,0 +1,3 @@ +class SchemeDeactivationPeriod < ApplicationRecord + scope :deactivations_without_reactivation, -> { where(reactivation_date: nil) } +end diff --git a/db/migrate/20221117103841_add_scheme_deactivations.rb b/db/migrate/20221117103841_add_scheme_deactivations.rb new file mode 100644 index 000000000..b43faba31 --- /dev/null +++ b/db/migrate/20221117103841_add_scheme_deactivations.rb @@ -0,0 +1,10 @@ +class AddSchemeDeactivations < ActiveRecord::Migration[7.0] + def change + create_table :scheme_deactivation_periods do |t| + t.datetime :deactivation_date + t.datetime :reactivation_date + t.belongs_to :scheme + t.timestamps + end + end +end diff --git a/db/migrate/20221117103855_remove_deactivation_date_from_schemes.rb b/db/migrate/20221117103855_remove_deactivation_date_from_schemes.rb new file mode 100644 index 000000000..e5ce5c709 --- /dev/null +++ b/db/migrate/20221117103855_remove_deactivation_date_from_schemes.rb @@ -0,0 +1,13 @@ +class RemoveDeactivationDateFromSchemes < ActiveRecord::Migration[7.0] + def up + change_table :schemes, bulk: true do |t| + t.remove :deactivation_date + end + end + + def down + change_table :schemes, bulk: true do |t| + t.column :deactivation_date, :datetime + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 2644c8b0b..f2760b505 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.0].define(version: 2022_11_15_113437) do +ActiveRecord::Schema[7.0].define(version: 2022_11_17_103855) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -372,20 +372,29 @@ ActiveRecord::Schema[7.0].define(version: 2022_11_15_113437) do t.integer "la_known" t.integer "income1" t.integer "income1nk" - t.integer "details_known_2" - t.integer "details_known_3" - t.integer "details_known_4" t.integer "age4" t.integer "age4_known" t.integer "age5" t.integer "age5_known" t.integer "age6" t.integer "age6_known" + t.integer "details_known_2" + t.integer "details_known_3" + t.integer "details_known_4" t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id" t.index ["managing_organisation_id"], name: "index_sales_logs_on_managing_organisation_id" t.index ["owning_organisation_id"], name: "index_sales_logs_on_owning_organisation_id" end + create_table "scheme_deactivation_periods", force: :cascade do |t| + t.datetime "deactivation_date" + t.datetime "reactivation_date" + t.bigint "scheme_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["scheme_id"], name: "index_scheme_deactivation_periods_on_scheme_id" + end + create_table "schemes", force: :cascade do |t| t.string "service_name" t.bigint "owning_organisation_id", null: false @@ -406,7 +415,6 @@ ActiveRecord::Schema[7.0].define(version: 2022_11_15_113437) do t.string "old_visible_id" t.integer "total_units" t.boolean "confirmed" - t.datetime "deactivation_date" t.index ["managing_organisation_id"], name: "index_schemes_on_managing_organisation_id" t.index ["owning_organisation_id"], name: "index_schemes_on_owning_organisation_id" end diff --git a/spec/factories/scheme_deactivation_period.rb b/spec/factories/scheme_deactivation_period.rb new file mode 100644 index 000000000..c073bc68a --- /dev/null +++ b/spec/factories/scheme_deactivation_period.rb @@ -0,0 +1,5 @@ +FactoryBot.define do + factory :scheme_deactivation_period do + reactivation_date { nil } + end +end diff --git a/spec/helpers/schemes_helper_spec.rb b/spec/helpers/schemes_helper_spec.rb index 46dfbd7c2..1924a6cc5 100644 --- a/spec/helpers/schemes_helper_spec.rb +++ b/spec/helpers/schemes_helper_spec.rb @@ -18,10 +18,33 @@ RSpec.describe SchemesHelper do { name: "Secondary client group", value: scheme.secondary_client_group }, { name: "Level of support given", value: scheme.support_type }, { name: "Intended length of stay", value: scheme.intended_stay }, - { name: "Availability", value: "Available from 8 August 2022" }, + { name: "Availability", value: "Active from 8 August 2022" }, { name: "Status", value: :active }, ] expect(display_scheme_attributes(scheme)).to eq(attributes) end + + context "when viewing availability" do + context "with are no deactivations" do + it "displays created_at as availability date" do + availability_attribute = display_scheme_attributes(scheme).find { |x| x[:name] == "Availability" }[:value] + + expect(availability_attribute).to eq("Active from #{scheme.created_at.to_formatted_s(:govuk_date)}") + end + end + + context "with previous deactivations" do + before do + scheme.scheme_deactivation_periods << FactoryBot.create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 8, 10), reactivation_date: Time.zone.local(2022, 9, 1)) + scheme.scheme_deactivation_periods << FactoryBot.create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 9, 15), reactivation_date: nil) + end + + it "displays the timeline of availability" do + availability_attribute = display_scheme_attributes(scheme).find { |x| x[:name] == "Availability" }[:value] + + expect(availability_attribute).to eq("Active from 8 August 2022 to 9 August 2022\nDeactivated on 10 August 2022\nActive from 1 September 2022 to 14 September 2022\nDeactivated on 15 September 2022") + end + end + end end end diff --git a/spec/models/scheme_spec.rb b/spec/models/scheme_spec.rb index b9e8fd886..59ac34de4 100644 --- a/spec/models/scheme_spec.rb +++ b/spec/models/scheme_spec.rb @@ -99,32 +99,56 @@ RSpec.describe Scheme, type: :model do Timecop.freeze(2022, 6, 7) end - it "returns active if the scheme is not deactivated" do - scheme.deactivation_date = nil - scheme.deactivation_date_type = nil - scheme.save! - expect(scheme.status).to eq(:active) - end + context "when there have not been any previous deactivations" do + it "returns active if the scheme is not deactivated" do + expect(scheme.status).to eq(:active) + end - it "returns deactivating soon if deactivation_date is in the future" do - scheme.deactivation_date = Time.zone.local(2022, 8, 8) - scheme.deactivation_date_type = "other" - scheme.save! - expect(scheme.status).to eq(:deactivating_soon) - end + it "returns deactivating soon if deactivation_date is in the future" do + scheme.scheme_deactivation_periods << FactoryBot.create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 8, 8)) + scheme.save! + expect(scheme.status).to eq(:deactivating_soon) + end - it "returns deactivated if deactivation_date is in the past" do - scheme.deactivation_date = Time.zone.local(2022, 4, 8) - scheme.deactivation_date_type = "other" - scheme.save! - expect(scheme.status).to eq(:deactivated) + it "returns deactivated if deactivation_date is in the past" do + scheme.scheme_deactivation_periods << FactoryBot.create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 6)) + scheme.save! + expect(scheme.status).to eq(:deactivated) + end + + it "returns deactivated if deactivation_date is today" do + scheme.scheme_deactivation_periods << FactoryBot.create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 7)) + scheme.save! + expect(scheme.status).to eq(:deactivated) + end end - it "returns deactivated if deactivation_date is today" do - scheme.deactivation_date = Time.zone.local(2022, 6, 7) - scheme.deactivation_date_type = "other" - scheme.save! - expect(scheme.status).to eq(:deactivated) + context "when there have been previous deactivations" do + before do + scheme.scheme_deactivation_periods << FactoryBot.create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), reactivation_date: Time.zone.local(2022, 6, 5)) + end + + it "returns active if the scheme has no relevant deactivation records" do + expect(scheme.status).to eq(:active) + end + + it "returns deactivating soon if deactivation_date is in the future" do + scheme.scheme_deactivation_periods << FactoryBot.create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 8, 8)) + scheme.save! + expect(scheme.status).to eq(:deactivating_soon) + end + + it "returns deactivated if deactivation_date is in the past" do + scheme.scheme_deactivation_periods << FactoryBot.create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 6)) + scheme.save! + expect(scheme.status).to eq(:deactivated) + end + + it "returns deactivated if deactivation_date is today" do + scheme.scheme_deactivation_periods << FactoryBot.create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 7)) + scheme.save! + expect(scheme.status).to eq(:deactivated) + end end end diff --git a/spec/requests/schemes_controller_spec.rb b/spec/requests/schemes_controller_spec.rb index 5ad18663e..237df9785 100644 --- a/spec/requests/schemes_controller_spec.rb +++ b/spec/requests/schemes_controller_spec.rb @@ -246,19 +246,18 @@ RSpec.describe SchemesController, type: :request do context "when looking at scheme details" do let(:user) { FactoryBot.create(:user, :data_coordinator) } let!(:scheme) { FactoryBot.create(:scheme, owning_organisation: user.organisation) } + let(:add_deactivations) { scheme.scheme_deactivation_periods << scheme_deactivation_period } before do Timecop.freeze(Time.utc(2022, 10, 10)) sign_in user - scheme.deactivation_date = deactivation_date - scheme.deactivation_date_type = deactivation_date_type + add_deactivations scheme.save! get "/schemes/#{scheme.id}" end context "with active scheme" do - let(:deactivation_date) { nil } - let(:deactivation_date_type) { nil } + let(:add_deactivations) {} it "renders deactivate this scheme" do expect(response).to have_http_status(:ok) @@ -267,8 +266,7 @@ RSpec.describe SchemesController, type: :request do end context "with deactivated scheme" do - let(:deactivation_date) { Time.utc(2022, 10, 9) } - let(:deactivation_date_type) { "other" } + let(:scheme_deactivation_period) { FactoryBot.create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 10, 9)) } it "renders reactivate this scheme" do expect(response).to have_http_status(:ok) @@ -277,8 +275,7 @@ RSpec.describe SchemesController, type: :request do end context "with scheme that's deactivating soon" do - let(:deactivation_date) { Time.utc(2022, 10, 12) } - let(:deactivation_date_type) { "other" } + let(:scheme_deactivation_period) { FactoryBot.create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 10, 12)) } it "renders reactivate this scheme" do expect(response).to have_http_status(:ok) @@ -1768,8 +1765,10 @@ RSpec.describe SchemesController, type: :request do context "when signed in as a data coordinator" do let(:user) { FactoryBot.create(:user, :data_coordinator) } let!(:scheme) { FactoryBot.create(:scheme, owning_organisation: user.organisation) } - let(:startdate) { Time.utc(2021, 1, 2) } + let!(:location) { FactoryBot.create(:location, scheme:) } let(:deactivation_date) { Time.utc(2022, 10, 10) } + let!(:lettings_log) { FactoryBot.create(:lettings_log, :sh, location:, scheme:, startdate:, owning_organisation: user.organisation) } + let(:startdate) { Time.utc(2022, 10, 11) } before do Timecop.freeze(Time.utc(2022, 10, 10)) @@ -1812,7 +1811,30 @@ RSpec.describe SchemesController, type: :request do expect(response).to have_http_status(:ok) expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success") scheme.reload - expect(scheme.deactivation_date).to eq(deactivation_date) + expect(scheme.scheme_deactivation_periods.count).to eq(1) + expect(scheme.scheme_deactivation_periods.first.deactivation_date).to eq(deactivation_date) + end + + context "and a log startdate is after scheme deactivation date" do + it "clears the scheme and scheme answers" do + expect(lettings_log.scheme).to eq(scheme) + expect(lettings_log.scheme).to eq(scheme) + lettings_log.reload + expect(lettings_log.scheme).to eq(nil) + expect(lettings_log.scheme).to eq(nil) + end + end + + context "and a log startdate is before scheme deactivation date" do + let(:startdate) { Time.utc(2022, 10, 9) } + + it "does not update the log" do + expect(lettings_log.scheme).to eq(scheme) + expect(lettings_log.scheme).to eq(scheme) + lettings_log.reload + expect(lettings_log.scheme).to eq(scheme) + expect(lettings_log.scheme).to eq(scheme) + end end end