diff --git a/app/controllers/locations_controller.rb b/app/controllers/locations_controller.rb index 50102ee17..876f0e405 100644 --- a/app/controllers/locations_controller.rb +++ b/app/controllers/locations_controller.rb @@ -269,7 +269,7 @@ private if params[:location_deactivation_period].blank? return elsif params[:location_deactivation_period]["#{key}_type".to_sym] == "default" - return FormHandler.instance.current_collection_start_date + return FormHandler.instance.start_date_of_earliest_open_collection_period elsif params[:location_deactivation_period][key.to_sym].present? return params[:location_deactivation_period][key.to_sym] end diff --git a/app/controllers/schemes_controller.rb b/app/controllers/schemes_controller.rb index f248bffe5..e60620bc5 100644 --- a/app/controllers/schemes_controller.rb +++ b/app/controllers/schemes_controller.rb @@ -296,7 +296,7 @@ private if params[:scheme_deactivation_period].blank? return elsif params[:scheme_deactivation_period]["#{key}_type".to_sym] == "default" - return FormHandler.instance.current_collection_start_date + return FormHandler.instance.start_date_of_earliest_open_collection_period elsif params[:scheme_deactivation_period][key.to_sym].present? return params[:scheme_deactivation_period][key.to_sym] end diff --git a/app/models/form_handler.rb b/app/models/form_handler.rb index ab22e6bc5..ee84c3e32 100644 --- a/app/models/form_handler.rb +++ b/app/models/form_handler.rb @@ -77,6 +77,10 @@ class FormHandler form_mappings[current_collection_start_year - year] end + def start_date_of_earliest_open_collection_period + in_crossover_period? ? previous_collection_start_date : current_collection_start_date + end + def in_crossover_period?(now: Time.zone.now) lettings_in_crossover_period?(now:) || sales_in_crossover_period?(now:) end diff --git a/app/models/location_deactivation_period.rb b/app/models/location_deactivation_period.rb index dcf347d24..c9a24bdc9 100644 --- a/app/models/location_deactivation_period.rb +++ b/app/models/location_deactivation_period.rb @@ -1,4 +1,6 @@ class LocationDeactivationPeriodValidator < ActiveModel::Validator + include CollectionTimeHelper + def validate(record) location = record.location recent_deactivation = location.location_deactivation_periods.deactivations_without_reactivation.first @@ -16,7 +18,7 @@ class LocationDeactivationPeriodValidator < ActiveModel::Validator elsif record.reactivation_date_type == "other" record.errors.add(:reactivation_date, message: I18n.t("validations.location.toggle_date.invalid")) end - elsif !record.reactivation_date.between?(location.available_from, Time.zone.local(2200, 1, 1)) + elsif record.reactivation_date.before? location.available_from record.errors.add(:reactivation_date, message: I18n.t("validations.location.toggle_date.out_of_range", date: location.available_from.to_formatted_s(:govuk_date))) elsif record.reactivation_date < recent_deactivation.deactivation_date record.errors.add(:reactivation_date, message: I18n.t("validations.location.reactivation.before_deactivation", date: recent_deactivation.deactivation_date.to_formatted_s(:govuk_date))) @@ -32,10 +34,10 @@ class LocationDeactivationPeriodValidator < ActiveModel::Validator end elsif location.location_deactivation_periods.any? { |period| period.reactivation_date.present? && record.deactivation_date.between?(period.deactivation_date, period.reactivation_date - 1.day) } record.errors.add(:deactivation_date, message: I18n.t("validations.location.deactivation.during_deactivated_period")) - else - unless record.deactivation_date.between?(location.available_from, Time.zone.local(2200, 1, 1)) - record.errors.add(:deactivation_date, message: I18n.t("validations.location.toggle_date.out_of_range", date: location.available_from.to_formatted_s(:govuk_date))) - end + elsif record.deactivation_date.before? FormHandler.instance.start_date_of_earliest_open_collection_period + record.errors.add(:deactivation_date, message: I18n.t("validations.location.toggle_date.out_of_range", date: FormHandler.instance.start_date_of_earliest_open_collection_period.to_formatted_s(:govuk_date))) + elsif record.deactivation_date.before? location.available_from + record.errors.add(:deactivation_date, message: I18n.t("validations.location.toggle_date.before_creation", date: location.available_from.to_formatted_s(:govuk_date))) end end end diff --git a/app/models/scheme_deactivation_period.rb b/app/models/scheme_deactivation_period.rb index f716cbc32..01aafbcb4 100644 --- a/app/models/scheme_deactivation_period.rb +++ b/app/models/scheme_deactivation_period.rb @@ -1,4 +1,6 @@ class SchemeDeactivationPeriodValidator < ActiveModel::Validator + include CollectionTimeHelper + def validate(record) scheme = record.scheme recent_deactivation = scheme.scheme_deactivation_periods.deactivations_without_reactivation.first @@ -32,10 +34,10 @@ class SchemeDeactivationPeriodValidator < ActiveModel::Validator end elsif scheme.scheme_deactivation_periods.any? { |period| period.reactivation_date.present? && record.deactivation_date.between?(period.deactivation_date, period.reactivation_date - 1.day) } record.errors.add(:deactivation_date, message: I18n.t("validations.scheme.deactivation.during_deactivated_period")) - else - unless record.deactivation_date.between?(scheme.available_from, Time.zone.local(2200, 1, 1)) - record.errors.add(:deactivation_date, message: I18n.t("validations.scheme.toggle_date.out_of_range", date: scheme.available_from.to_formatted_s(:govuk_date))) - end + elsif record.deactivation_date.before? FormHandler.instance.start_date_of_earliest_open_collection_period + record.errors.add(:deactivation_date, message: I18n.t("validations.scheme.toggle_date.out_of_range", date: FormHandler.instance.start_date_of_earliest_open_collection_period.to_formatted_s(:govuk_date))) + elsif record.deactivation_date.before? scheme.available_from + record.errors.add(:deactivation_date, message: I18n.t("validations.scheme.toggle_date.before_creation", date: scheme.available_from.to_formatted_s(:govuk_date))) end end end diff --git a/app/views/locations/toggle_active.html.erb b/app/views/locations/toggle_active.html.erb index 5c030de89..7b995b2b5 100644 --- a/app/views/locations/toggle_active.html.erb +++ b/app/views/locations/toggle_active.html.erb @@ -11,16 +11,16 @@ <%= form_with model: @location_deactivation_period, url: toggle_location_form_path(action, @location), method: "patch", local: true do |f| %>
- <% collection_start_date = FormHandler.instance.earliest_open_collection_start_date(now: @location.available_from) %> + <% start_date = FormHandler.instance.earliest_open_collection_start_date(now: @location.available_from) %> <%= f.govuk_error_summary %> <%= f.govuk_radio_buttons_fieldset date_type_question(action), legend: { text: I18n.t("questions.location.toggle_active.apply_from") }, caption: { text: title }, - hint: { text: I18n.t("hints.location.toggle_active", date: collection_start_date.to_formatted_s(:govuk_date)) } do %> + hint: { text: I18n.t("hints.location.toggle_active", date: start_date.to_formatted_s(:govuk_date)) } do %> <%= govuk_warning_text text: I18n.t("warnings.location.#{action}.existing_logs") %> <%= f.govuk_radio_button date_type_question(action), "default", - label: { text: "From the start of the current collection period (#{collection_start_date.to_formatted_s(:govuk_date)})" } %> + label: { text: "From the start of the open collection period (#{start_date.to_formatted_s(:govuk_date)})" } %> <%= f.govuk_radio_button date_type_question(action), "other", diff --git a/app/views/schemes/toggle_active.html.erb b/app/views/schemes/toggle_active.html.erb index 1b7507375..f2ecc4a1a 100644 --- a/app/views/schemes/toggle_active.html.erb +++ b/app/views/schemes/toggle_active.html.erb @@ -11,16 +11,16 @@ <%= form_with model: @scheme_deactivation_period, url: toggle_scheme_form_path(action, @scheme), method: "patch", local: true do |f| %>
- <% collection_start_date = FormHandler.instance.current_collection_start_date %> + <% start_date = FormHandler.instance.start_date_of_earliest_open_collection_period %> <%= f.govuk_error_summary %> <%= f.govuk_radio_buttons_fieldset date_type_question(action), legend: { text: I18n.t("questions.scheme.toggle_active.apply_from") }, caption: { text: title }, - hint: { text: I18n.t("hints.scheme.toggle_active", date: collection_start_date.to_formatted_s(:govuk_date)) } do %> + hint: { text: I18n.t("hints.scheme.toggle_active", date: start_date.to_formatted_s(:govuk_date)) } do %> <%= govuk_warning_text text: I18n.t("warnings.scheme.#{action}.existing_logs") %> <%= f.govuk_radio_button date_type_question(action), "default", - label: { text: "From the start of the current collection period (#{collection_start_date.to_formatted_s(:govuk_date)})" } %> + label: { text: "From the start of the open collection period (#{start_date.to_formatted_s(:govuk_date)})" } %> <%= f.govuk_radio_button date_type_question(action), "other", label: { text: "For tenancies starting after a certain date" }, diff --git a/config/locales/en.yml b/config/locales/en.yml index b6735e3b5..1e7ed58af 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -456,6 +456,7 @@ en: toggle_date: not_selected: "Select one of the options" invalid: "Enter a valid day, month and year" + before_creation: "The scheme cannot be deactivated before %{date}, the start of the collection year when it was created" out_of_range: "The date must be on or after the %{date}" reactivation: before_deactivation: "This scheme was deactivated on %{date}. The reactivation date must be on or after deactivation date" @@ -474,6 +475,7 @@ en: toggle_date: not_selected: "Select one of the options" invalid: "Enter a valid day, month and year" + before_creation: "The location cannot be deactivated before %{date}, the date when it was first available" out_of_range: "The date must be on or after the %{date}" reactivation: before_deactivation: "This location was deactivated on %{date}. The reactivation date must be on or after deactivation date" @@ -594,10 +596,10 @@ en: postcode: "For example, SW1P 4DF." name: "This is how you refer to this location within your organisation" units: "A unit is the space being let. For example, the property might be a block of flats and the unit would be the specific flat being let. A unit can also be a bedroom in a shared house or flat. Do not include spaces used for staff." - toggle_active: "If the date is before %{date}, select ‘From the start of the current collection period’ because the previous period has now closed." + toggle_active: "If the date is before %{date}, select ‘From the start of the open collection period’ because the previous period has now closed." startdate: "For example, 27 3 2021" scheme: - toggle_active: "If the date is before %{date}, select ‘From the start of the current collection period’ because the previous period has now closed." + toggle_active: "If the date is before %{date}, select ‘From the start of the open collection period’ because the previous period has now closed." bulk_upload: needstype: "General needs housing includes both self-contained and shared housing without support or specific adaptations. Supported housing can include direct access hostels, group homes, residential care and nursing homes." offered: "Do not include the offer that led to this letting. This is after the last tenancy ended. If the property is being offered for let for the first time, enter 0." diff --git a/spec/features/schemes_spec.rb b/spec/features/schemes_spec.rb index f43dc67eb..1e45634c6 100644 --- a/spec/features/schemes_spec.rb +++ b/spec/features/schemes_spec.rb @@ -742,7 +742,7 @@ RSpec.describe "Schemes scheme Features" do expect(page).to have_current_path("/schemes/#{scheme.id}/locations") end - context "when location is incative" do + context "when location is inactive" do context "and I click to view the location" do before do click_link(deactivated_location.postcode) @@ -766,7 +766,7 @@ RSpec.describe "Schemes scheme Features" do expect(page).to have_current_path("/schemes/#{scheme.id}/locations/#{deactivated_location.id}/new-reactivation") expect(page).to have_content("Reactivate #{deactivated_location.name}") expect(page).to have_content("You’ll be able to add logs with this location if their tenancy start date is on or after the date you enter.") - expect(page).to have_content("If the date is before 1 April 2022, select ‘From the start of the current collection period’ because the previous period has now closed.") + expect(page).to have_content("If the date is before 1 April 2022, select ‘From the start of the open collection period’ because the previous period has now closed.") end context "when I press the back button" do diff --git a/spec/models/location_deactivation_period_spec.rb b/spec/models/location_deactivation_period_spec.rb new file mode 100644 index 000000000..4e0bb3384 --- /dev/null +++ b/spec/models/location_deactivation_period_spec.rb @@ -0,0 +1,83 @@ +require "rails_helper" + +RSpec.describe LocationDeactivationPeriod do + let(:validator) { LocationDeactivationPeriodValidator.new } + let(:location) { FactoryBot.create(:location, startdate: now - 2.years) } + let(:record) { FactoryBot.create(:location_deactivation_period, deactivation_date: now, location:) } + + describe "#validate" do + around do |example| + Timecop.freeze(now) do + example.run + end + end + + context "when not in a crossover period" do + let(:now) { Time.utc(2023, 3, 1) } + + context "with a deactivation date before the current collection period" do + it "adds an error" do + record.deactivation_date = now - 1.year + location.location_deactivation_periods.clear + validator.validate(record) + expect(record.errors[:deactivation_date]).to include "The date must be on or after the 1 April 2022" + end + end + + context "with a deactivation date in the current collection period" do + it "does not add an error" do + record.deactivation_date = now - 1.day + location.location_deactivation_periods.clear + validator.validate(record) + expect(record.errors).to be_empty + end + end + end + + context "when in a crossover period" do + let(:now) { Time.utc(2023, 5, 1) } + + context "with a deactivation date before the previous collection period" do + it "does not add an error" do + record.deactivation_date = now - 2.years + location.location_deactivation_periods.clear + validator.validate(record) + expect(record.errors[:deactivation_date]).to include "The date must be on or after the 1 April 2022" + end + end + + context "with a deactivation date in the previous collection period" do + it "does not add an error" do + record.deactivation_date = now - 1.year + location.location_deactivation_periods.clear + validator.validate(record) + expect(record.errors).to be_empty + end + end + + context "with a deactivation date in the current collection period" do + it "does not add an error" do + record.deactivation_date = now - 1.day + location.location_deactivation_periods.clear + validator.validate(record) + expect(record.errors).to be_empty + end + end + + context "but the location was created in the current collection period" do + let(:location) { FactoryBot.create(:location, startdate:) } + let(:startdate) { now - 2.days } + + context "with a deactivation date in the previous collection period" do + it "adds an error" do + record.deactivation_date = now - 1.year + location.location_deactivation_periods.clear + validator.validate(record) + start_date = startdate.to_formatted_s(:govuk_date) + expect(record.errors[:deactivation_date]).to include "The location cannot be deactivated before #{start_date}, the date when it was first available" + end + end + end + end + end +end diff --git a/spec/models/scheme_deactivation_period_spec.rb b/spec/models/scheme_deactivation_period_spec.rb new file mode 100644 index 000000000..eb46ee62f --- /dev/null +++ b/spec/models/scheme_deactivation_period_spec.rb @@ -0,0 +1,68 @@ +require "rails_helper" + +RSpec.describe SchemeDeactivationPeriod do + let(:validator) { SchemeDeactivationPeriodValidator.new } + let(:scheme) { FactoryBot.create(:scheme, created_at: now - 2.years) } + let(:record) { FactoryBot.create(:scheme_deactivation_period, deactivation_date: now, scheme:) } + + describe "#validate" do + around do |example| + Timecop.freeze(now) do + example.run + end + end + + context "when not in a crossover period" do + let(:now) { Time.utc(2023, 3, 1) } + + context "with a deactivation date before the current collection period" do + it "adds an error" do + record.deactivation_date = now - 1.year + scheme.scheme_deactivation_periods.clear + validator.validate(record) + expect(record.errors[:deactivation_date]).to include("The date must be on or after the 1 April 2022") + end + end + + context "with a deactivation date in the current collection period" do + it "does not add an error" do + record.deactivation_date = now - 1.day + scheme.scheme_deactivation_periods.clear + validator.validate(record) + expect(record.errors[:deactivation_date]).to be_empty + end + end + end + + context "when in a crossover period" do + let(:now) { Time.utc(2023, 5, 1) } + + context "with a deactivation date before the previous collection period" do + it "does not add an error" do + record.deactivation_date = now - 2.years + scheme.scheme_deactivation_periods.clear + validator.validate(record) + expect(record.errors[:deactivation_date]).to include("The date must be on or after the 1 April 2022") + end + end + + context "with a deactivation date in the previous collection period" do + it "does not add an error" do + record.deactivation_date = now - 1.year + scheme.scheme_deactivation_periods.clear + validator.validate(record) + expect(record.errors[:deactivation_date]).to be_empty + end + end + + context "with a deactivation date in the current collection period" do + it "does not add an error" do + record.deactivation_date = now - 1.day + scheme.scheme_deactivation_periods.clear + validator.validate(record) + expect(record.errors[:deactivation_date]).to be_empty + end + end + end + end +end