Browse Source

Cldc 1671 deactivate scheme (#980)

* feat: wip update scheme summary page

* feat: wip deactivate scheme schemes page

* feat: wip toggle active page

* feat: wip set deactivation_date to a datetime (to be more specific times later_

* Change conditional question controller to accommodate all models

* feat: add specific datetimes for deactivation

* feat: correct date and add notice

* feat: wip error behaviour

* feat: wip errors

* feat: wip errors refactoring

* feat: wip errors more refactoring

* refactor: linting

* feat: add second error in correct position and wip date range error

* feat: remove unneccessary db field

* feat: change type values to strings

* refactor: tidy up controller logic

* refactor: use same structure as locations deactivation

* feat: add general partially missing date input error

* feat: add tests ("updates existing scheme with valid deactivation date and renders scheme page" still wip) and add new partially nil date behaviour to locations controller

* feat: fix tests, add status tag behaviour and remove unnecessary nils

* refactor: linting

* refactor: erblinting

* refactor: remove redundant line

* refactor: respond to PR comments 1

* refactor: respond to PR comments 2

* refactor: respond to PR comments 3

* refactor: respond to PR comments 3 (locations side)

* fix: remove @locations in location model

* fix: remove @locations in location model

* fix: update status names

* feat: wip validation update

* feat: add validation to model layer

* feat: further separate scheme deactivation behaviour

* test: update tests to new flow

* feat: respond to pr comments and add dynamic success text

* feat: duplicate behaviour schemes -> locations (+ tests)

* refactor: linting

* refactor: typo and remove unnecessary line for this PR

* refactor: feature toggle simplification

* Refactor locations and schemes controller actions

- Rename confirmation partials to `deactivate_confirm.html.erb` so that they match the actions in which they belong to
- Make all deactivation date comparision UTC time

* feat: update deactivation_date validation and add tests

* refactor: linting

Co-authored-by: Kat <katrina@kosiak.co.uk>
Co-authored-by: James Rose <james@jbpr.net>
pull/1000/head
natdeanlewissoftwire 2 years ago committed by GitHub
parent
commit
87793f24bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 82
      app/controllers/locations_controller.rb
  2. 64
      app/controllers/schemes_controller.rb
  3. 2
      app/helpers/locations_helper.rb
  4. 26
      app/helpers/schemes_helper.rb
  5. 4
      app/helpers/tag_helper.rb
  6. 2
      app/models/form_handler.rb
  7. 35
      app/models/location.rb
  8. 2
      app/models/organisation.rb
  9. 68
      app/models/scheme.rb
  10. 9
      app/views/locations/deactivate_confirm.html.erb
  11. 6
      app/views/locations/show.html.erb
  12. 6
      app/views/locations/toggle_active.html.erb
  13. 2
      app/views/organisations/show.html.erb
  14. 19
      app/views/schemes/deactivate_confirm.html.erb
  15. 12
      app/views/schemes/show.html.erb
  16. 35
      app/views/schemes/toggle_active.html.erb
  17. 14
      config/initializers/feature_toggle.rb
  18. 18
      config/locales/en.yml
  19. 9
      config/routes.rb
  20. 5
      db/migrate/20221110163351_add_deactivation_date_to_schemes.rb
  21. 7
      db/schema.rb
  22. 2
      spec/features/schemes_spec.rb
  23. 6
      spec/helpers/locations_helper_spec.rb
  24. 27
      spec/helpers/schemes_helper_spec.rb
  25. 2
      spec/models/form_handler_spec.rb
  26. 12
      spec/models/location_spec.rb
  27. 44
      spec/models/scheme_spec.rb
  28. 32
      spec/requests/locations_controller_spec.rb
  29. 2
      spec/requests/organisations_controller_spec.rb
  30. 173
      spec/requests/schemes_controller_spec.rb

82
app/controllers/locations_controller.rb

@ -20,22 +20,35 @@ class LocationsController < ApplicationController
def show; end
def deactivate
def new_deactivation
if params[:location].blank?
render "toggle_active", locals: { action: "deactivate" }
elsif params[:location][:confirm].present? && params[:location][:deactivation_date].present?
confirm_deactivation
else
deactivation_date_errors
if @location.errors.present?
@location.deactivation_date_type = params[:location][:deactivation_date_type]
render "toggle_active", locals: { action: "deactivate" }, status: :unprocessable_entity
@location.run_deactivation_validations!
@location.deactivation_date = deactivation_date
@location.deactivation_date_type = params[:location][:deactivation_date_type]
if @location.valid?
redirect_to scheme_location_deactivate_confirm_path(@location, deactivation_date: @location.deactivation_date, deactivation_date_type: @location.deactivation_date_type)
else
render "toggle_active_confirm", locals: { action: "deactivate", deactivation_date: }
render "toggle_active", locals: { action: "deactivate" }, status: :unprocessable_entity
end
end
end
def deactivate_confirm
@deactivation_date = params[:deactivation_date]
@deactivation_date_type = params[:deactivation_date_type]
end
def deactivate
@location.run_deactivation_validations!
if @location.update!(deactivation_date:)
flash[:notice] = deactivate_success_notice
end
redirect_to scheme_location_path(@scheme, @location)
end
def reactivate
render "toggle_active", locals: { action: "reactivate" }
end
@ -148,13 +161,13 @@ private
end
def authenticate_action!
if %w[new edit update create index edit_name edit_local_authority deactivate].include?(action_name) && !((current_user.organisation == @scheme&.owning_organisation) || current_user.support?)
if %w[new edit update create index edit_name edit_local_authority new_deactivation deactivate_confirm deactivate].include?(action_name) && !((current_user.organisation == @scheme&.owning_organisation) || current_user.support?)
render_not_found and return
end
end
def location_params
required_params = params.require(:location).permit(:postcode, :name, :units, :type_of_unit, :add_another_location, :startdate, :mobility_type, :location_admin_district, :location_code).merge(scheme_id: @scheme.id)
required_params = params.require(:location).permit(:postcode, :name, :units, :type_of_unit, :add_another_location, :startdate, :mobility_type, :location_admin_district, :location_code, :deactivation_date).merge(scheme_id: @scheme.id)
required_params[:postcode] = PostcodeService.clean(required_params[:postcode]) if required_params[:postcode]
required_params
end
@ -167,48 +180,29 @@ private
location_params["location_admin_district"] != "Select an option"
end
def confirm_deactivation
if @location.update(deactivation_date: params[:location][:deactivation_date])
flash[:notice] = "#{@location.name || @location.postcode} has been deactivated"
end
redirect_to scheme_location_path(@scheme, @location)
end
def deactivation_date_errors
if params[:location][:deactivation_date].blank? && params[:location][:deactivation_date_type].blank?
@location.errors.add(:deactivation_date_type, message: I18n.t("validations.location.deactivation_date.not_selected"))
end
if params[:location][:deactivation_date_type] == "other"
day = params[:location]["deactivation_date(3i)"]
month = params[:location]["deactivation_date(2i)"]
year = params[:location]["deactivation_date(1i)"]
collection_start_date = FormHandler.instance.current_collection_start_date
if [day, month, year].any?(&:blank?)
{ day:, month:, year: }.each do |period, value|
@location.errors.add(:deactivation_date, message: I18n.t("validations.location.deactivation_date.not_entered", period: period.to_s)) if value.blank?
end
elsif !Date.valid_date?(year.to_i, month.to_i, day.to_i)
@location.errors.add(:deactivation_date, message: I18n.t("validations.location.deactivation_date.invalid"))
elsif !Date.new(year.to_i, month.to_i, day.to_i).between?(collection_start_date, Date.new(2200, 1, 1))
@location.errors.add(:deactivation_date, message: I18n.t("validations.location.deactivation_date.out_of_range", date: collection_start_date.to_formatted_s(:govuk_date)))
end
def deactivate_success_notice
case @location.status
when :deactivated
"#{@location.name} has been deactivated"
when :deactivating_soon
"#{@location.name} will deactivate on #{@location.deactivation_date.to_formatted_s(:govuk_date)}"
end
end
def deactivation_date
return if params[:location].blank?
collection_start_date = FormHandler.instance.current_collection_start_date
return collection_start_date if params[:location][:deactivation_date_type] == "default"
return params[:location][:deactivation_date] if params[:location][:deactivation_date_type].blank?
if params[:location].blank?
return
elsif params[:location][:deactivation_date_type] == "default"
return FormHandler.instance.current_collection_start_date
elsif params[:location][:deactivation_date].present?
return params[:location][:deactivation_date]
end
day = params[:location]["deactivation_date(3i)"]
month = params[:location]["deactivation_date(2i)"]
year = params[:location]["deactivation_date(1i)"]
return nil if [day, month, year].any?(&:blank?)
Date.new(year.to_i, month.to_i, day.to_i)
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
end

64
app/controllers/schemes_controller.rb

@ -21,6 +21,39 @@ class SchemesController < ApplicationController
render_not_found and return unless @scheme
end
def new_deactivation
if params[:scheme].blank?
render "toggle_active", locals: { action: "deactivate" }
else
@scheme.run_deactivation_validations = true
@scheme.deactivation_date = deactivation_date
@scheme.deactivation_date_type = params[:scheme][:deactivation_date_type]
if @scheme.valid?
redirect_to scheme_deactivate_confirm_path(@scheme, deactivation_date: @scheme.deactivation_date, deactivation_date_type: @scheme.deactivation_date_type)
else
render "toggle_active", locals: { action: "deactivate" }, status: :unprocessable_entity
end
end
end
def deactivate_confirm
@deactivation_date = params[:deactivation_date]
@deactivation_date_type = params[:deactivation_date_type]
end
def deactivate
@scheme.run_deactivation_validations!
if @scheme.update!(deactivation_date:)
flash[:notice] = deactivate_success_notice
end
redirect_to scheme_details_path(@scheme)
end
def reactivate
render "toggle_active", locals: { action: "reactivate" }
end
def new
@scheme = Scheme.new
end
@ -206,7 +239,8 @@ private
:support_type,
:arrangement_type,
:intended_stay,
:confirmed)
:confirmed,
:deactivation_date)
if arrangement_type_changed_to_different_org?(required_params)
required_params[:managing_organisation_id] = nil
@ -251,7 +285,7 @@ private
def authenticate_scope!
head :unauthorized and return unless current_user.data_coordinator? || current_user.support?
if %w[show locations primary_client_group confirm_secondary_client_group secondary_client_group support details check_answers edit_name].include?(action_name) && !((current_user.organisation == @scheme&.owning_organisation) || current_user.support?)
if %w[show locations primary_client_group confirm_secondary_client_group secondary_client_group support details check_answers edit_name deactivate].include?(action_name) && !((current_user.organisation == @scheme&.owning_organisation) || current_user.support?)
render_not_found and return
end
end
@ -259,4 +293,30 @@ private
def redirect_if_scheme_confirmed
redirect_to @scheme if @scheme.confirmed?
end
def deactivate_success_notice
case @scheme.status
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)}"
end
end
def deactivation_date
if params[:scheme].blank?
return
elsif params[:scheme][:deactivation_date_type] == "default"
return FormHandler.instance.current_collection_start_date
elsif params[:scheme][:deactivation_date].present?
return params[:scheme][:deactivation_date]
end
day = params[:scheme]["deactivation_date(3i)"]
month = params[:scheme]["deactivation_date(2i)"]
year = params[:scheme]["deactivation_date(1i)"]
return nil if [day, month, year].any?(&:blank?)
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
end

2
app/helpers/locations_helper.rb

@ -23,7 +23,7 @@ module LocationsHelper
resource.map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize) }
end
def display_attributes(location)
def display_location_attributes(location)
[
{ name: "Postcode", value: location.postcode },
{ name: "Local authority", value: location.location_admin_district },

26
app/helpers/schemes_helper.rb

@ -0,0 +1,26 @@
module SchemesHelper
def display_scheme_attributes(scheme)
base_attributes = [
{ name: "Scheme code", value: scheme.id_to_display },
{ name: "Name", value: scheme.service_name, edit: true },
{ name: "Confidential information", value: scheme.sensitive, edit: true },
{ name: "Type of scheme", value: scheme.scheme_type },
{ name: "Registered under Care Standards Act 2000", value: scheme.registered_under_care_act },
{ name: "Housing stock owned by", value: scheme.owning_organisation.name, edit: true },
{ name: "Support services provided by", value: scheme.arrangement_type },
{ name: "Organisation providing support", value: scheme.managing_organisation&.name },
{ name: "Primary client group", value: scheme.primary_client_group },
{ name: "Has another client group", value: scheme.has_other_client_group },
{ 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: "Status", value: scheme.status },
]
if scheme.arrangement_type_same?
base_attributes.delete({ name: "Organisation providing support", value: scheme.managing_organisation&.name })
end
base_attributes
end
end

4
app/helpers/tag_helper.rb

@ -7,7 +7,9 @@ module TagHelper
in_progress: "In progress",
completed: "Completed",
active: "Active",
incomplete: "Incomplete",
deactivating_soon: "Deactivating soon",
reactivating_soon: "Reactivating soon",
deactivated: "Deactivated",
}.freeze
@ -17,7 +19,9 @@ module TagHelper
in_progress: "blue",
completed: "green",
active: "green",
incomplete: "red",
deactivating_soon: "yellow",
reactivating_soon: "blue",
deactivated: "grey",
}.freeze

2
app/models/form_handler.rb

@ -50,7 +50,7 @@ class FormHandler
end
def current_collection_start_date
Time.utc(current_collection_start_year, 4, 1)
Time.zone.local(current_collection_start_year, 4, 1)
end
def form_name_from_start_year(year, type)

35
app/models/location.rb

@ -1,5 +1,6 @@
class Location < ApplicationRecord
validate :validate_postcode
validate :deactivation_date_errors
validates :units, :type_of_unit, :mobility_type, presence: true
belongs_to :scheme
has_many :lettings_logs, class_name: "LettingsLog"
@ -10,7 +11,7 @@ class Location < ApplicationRecord
auto_strip_attributes :name
attr_accessor :add_another_location, :deactivation_date_type
attr_accessor :add_another_location, :deactivation_date_type, :run_deactivation_validations
scope :search_by_postcode, ->(postcode) { where("REPLACE(postcode, ' ', '') ILIKE ?", "%#{postcode.delete(' ')}%") }
scope :search_by_name, ->(name) { where("name ILIKE ?", "%#{name}%") }
@ -375,7 +376,37 @@ class Location < ApplicationRecord
def status
return :active if deactivation_date.blank?
return :deactivating_soon if Time.zone.now < deactivation_date
return :deactivated if Time.zone.now >= deactivation_date
:deactivated
end
def active?
status == :active
end
def run_deactivation_validations!
@run_deactivation_validations = true
end
def implicit_run_deactivation_validations
deactivation_date.present? || @run_deactivation_validations
end
def deactivation_date_errors
return unless implicit_run_deactivation_validations
if deactivation_date.blank?
if deactivation_date_type.blank?
errors.add(:deactivation_date_type, message: I18n.t("validations.location.deactivation_date.not_selected"))
elsif deactivation_date_type == "other"
errors.add(:deactivation_date, message: I18n.t("validations.location.deactivation_date.invalid"))
end
else
collection_start_date = FormHandler.instance.current_collection_start_date
unless deactivation_date.between?(collection_start_date, Date.new(2200, 1, 1))
errors.add(:deactivation_date, message: I18n.t("validations.location.deactivation_date.out_of_range", date: collection_start_date.to_formatted_s(:govuk_date)))
end
end
end
private

2
app/models/organisation.rb

@ -77,7 +77,7 @@ class Organisation < ApplicationRecord
DISPLAY_PROVIDER_TYPE[provider_type.to_sym]
end
def display_attributes
def display_organisation_attributes
[
{ name: "Name", value: name, editable: true },
{ name: "Address", value: address_string, editable: true },

68
app/models/scheme.rb

@ -18,9 +18,12 @@ class Scheme < ApplicationRecord
}
validate :validate_confirmed
validate :deactivation_date_errors
auto_strip_attributes :service_name
attr_accessor :deactivation_date_type, :run_deactivation_validations
SENSITIVE = {
No: 0,
Yes: 1,
@ -153,29 +156,6 @@ class Scheme < ApplicationRecord
]
end
def display_attributes
base_attributes = [
{ name: "Scheme code", value: id_to_display },
{ name: "Name", value: service_name, edit: true },
{ name: "Confidential information", value: sensitive, edit: true },
{ name: "Type of scheme", value: scheme_type },
{ name: "Registered under Care Standards Act 2000", value: registered_under_care_act },
{ name: "Housing stock owned by", value: owning_organisation.name, edit: true },
{ name: "Support services provided by", value: arrangement_type },
{ name: "Organisation providing support", value: managing_organisation&.name },
{ name: "Primary client group", value: primary_client_group },
{ name: "Has another client group", value: has_other_client_group },
{ name: "Secondary client group", value: secondary_client_group },
{ name: "Level of support given", value: support_type },
{ name: "Intended length of stay", value: intended_stay },
]
if arrangement_type_same?
base_attributes.delete({ name: "Organisation providing support", value: managing_organisation&.name })
end
base_attributes
end
def synonyms
locations.map(&:postcode).join(",")
end
@ -219,7 +199,7 @@ class Scheme < ApplicationRecord
end
def validate_confirmed
required_attributes = attribute_names - %w[id created_at updated_at old_id old_visible_id confirmed end_date sensitive secondary_client_group total_units has_other_client_group]
required_attributes = attribute_names - %w[id created_at updated_at old_id old_visible_id confirmed end_date sensitive secondary_client_group total_units has_other_client_group deactivation_date deactivation_date_type]
if confirmed == true
required_attributes.any? do |attribute|
@ -230,4 +210,44 @@ class Scheme < ApplicationRecord
end
end
end
def available_from
created_at
end
def status
return :active if deactivation_date.blank?
return :deactivating_soon if Time.zone.now < deactivation_date
:deactivated
end
def active?
status == :active
end
def run_deactivation_validations!
@run_deactivation_validations = true
end
def implicit_run_deactivation_validations
deactivation_date.present? || @run_deactivation_validations
end
def deactivation_date_errors
return unless implicit_run_deactivation_validations
if deactivation_date.blank?
if deactivation_date_type.blank?
errors.add(:deactivation_date_type, message: I18n.t("validations.scheme.deactivation_date.not_selected"))
elsif deactivation_date_type == "other"
errors.add(:deactivation_date, message: I18n.t("validations.scheme.deactivation_date.invalid"))
end
else
collection_start_date = FormHandler.instance.current_collection_start_date
unless deactivation_date.between?(collection_start_date, Date.new(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
end
end

9
app/views/locations/toggle_active_confirm.html.erb → app/views/locations/deactivate_confirm.html.erb

@ -4,13 +4,14 @@
<% end %>
<h1 class="govuk-heading-l">
<span class="govuk-caption-l"><%= @location.postcode %></span>
<%= "This change will affect #{@location.lettings_logs.count} logs" %>
This change will affect <%= @location.lettings_logs.count %> logs
</h1>
<%= govuk_warning_text text: I18n.t("warnings.location.deactivation.review_logs") %>
<%= f.hidden_field :confirm, value: true %>
<%= f.hidden_field :deactivation_date, value: deactivation_date %>
<div class="govuk-button-group">
<%= f.hidden_field :deactivation_date, value: @deactivation_date %>
<%= f.hidden_field :deactivation_date_type, value: @deactivation_date_type %>
<div class="govuk-button-group">
<%= f.govuk_submit "Deactivate this location" %>
<%= govuk_button_link_to "Cancel", scheme_location_path(scheme_id: @scheme, id: @location.id), html: { method: :get }, secondary: true %>
<%= govuk_button_link_to "Cancel", scheme_location_path(@scheme, @location), html: { method: :get }, secondary: true %>
</div>
<% end %>

6
app/views/locations/show.html.erb

@ -13,7 +13,7 @@
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<%= govuk_summary_list do |summary_list| %>
<% display_attributes(@location).each do |attr| %>
<% display_location_attributes(@location).each do |attr| %>
<%= summary_list.row do |row| %>
<% row.key { attr[:name] } %>
<% row.value { attr[:name].eql?("Status") ? status_tag(attr[:value]) : details_html(attr) } %>
@ -24,8 +24,8 @@
</div>
</div>
<% if FeatureToggle.location_toggle_enabled? %>
<% if @location.status == :active %>
<%= govuk_button_link_to "Deactivate this location", scheme_location_deactivate_path(scheme_id: @scheme.id, location_id: @location.id), warning: true %>
<% if @location.active? %>
<%= govuk_button_link_to "Deactivate this location", scheme_location_new_deactivation_path(scheme_id: @scheme.id, location_id: @location.id), warning: true %>
<% else %>
<%= govuk_button_link_to "Reactivate this location", scheme_location_reactivate_path(scheme_id: @scheme.id, location_id: @location.id) %>
<% end %>

6
app/views/locations/toggle_active.html.erb

@ -4,11 +4,11 @@
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: scheme_location_path(scheme_id: @location.scheme.id, id: @location.id),
href: scheme_location_path(@location.scheme, @location),
) %>
<% end %>
<%= form_with model: @location, url: scheme_location_deactivate_path(scheme_id: @location.scheme.id, location_id: @location.id), method: "patch", local: true do |f| %>
<%= form_with model: @location, url: scheme_location_new_deactivation_path(scheme_id: @location.scheme.id, location_id: @location.id), method: "patch", local: true do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<% collection_start_date = FormHandler.instance.current_collection_start_date %>
@ -28,7 +28,7 @@
**basic_conditional_html_attributes({ "deactivation_date" => %w[other] }, "location") do %>
<%= f.govuk_date_field :deactivation_date,
legend: { text: "Date", size: "m" },
hint: { text: "For example, 27 3 2008" },
hint: { text: "For example, 27 3 2022" },
width: 20 %>
<% end %>
<% end %>

2
app/views/organisations/show.html.erb

@ -14,7 +14,7 @@
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<%= govuk_summary_list do |summary_list| %>
<% @organisation.display_attributes.each do |attr| %>
<% @organisation.display_organisation_attributes.each do |attr| %>
<% if can_edit_org?(current_user) && attr[:editable] %>
<%= summary_list.row do |row| %>
<% row.key { attr[:name].to_s.humanize } %>

19
app/views/schemes/deactivate_confirm.html.erb

@ -0,0 +1,19 @@
<% title = "Deactivate #{@scheme.service_name}" %>
<% content_for :title, title %>
<%= form_with model: @scheme, url: scheme_deactivate_path(@scheme), method: "patch", local: true do |f| %>
<% content_for :before_content do %>
<%= govuk_back_link(href: :back) %>
<% end %>
<h1 class="govuk-heading-l">
<span class="govuk-caption-l"><%= @scheme.service_name %></span>
This change will affect <%= @scheme.lettings_logs.count %> logs
</h1>
<%= govuk_warning_text text: I18n.t("warnings.scheme.deactivation.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 %>
<div class="govuk-button-group">
<%= f.govuk_submit "Deactivate this scheme" %>
<%= govuk_button_link_to "Cancel", scheme_details_path(@scheme), html: { method: :get }, secondary: true %>
</div>
<% end %>

12
app/views/schemes/show.html.erb

@ -15,12 +15,20 @@
<h2 class="govuk-visually-hidden">Scheme</h2>
<%= govuk_summary_list do |summary_list| %>
<% @scheme.display_attributes.each do |attr| %>
<% display_scheme_attributes(@scheme).each do |attr| %>
<% next if current_user.data_coordinator? && attr[:name] == ("Housing stock owned by") %>
<%= summary_list.row do |row| %>
<% row.key { attr[:name].eql?("Registered under Care Standards Act 2000") ? "Registered under Care Standards Act 2000" : attr[:name].to_s.humanize } %>
<% row.value { details_html(attr) } %>
<% row.value { attr[:name].eql?("Status") ? status_tag(attr[:value]) : details_html(attr) } %>
<% row.action(text: "Change", href: scheme_edit_name_path(scheme_id: @scheme.id)) if attr[:edit] %>
<% end %>
<% end %>
<% end %>
<% if FeatureToggle.scheme_toggle_enabled? %>
<% if @scheme.active? %>
<%= govuk_button_link_to "Deactivate this scheme", scheme_new_deactivation_path(@scheme), warning: true %>
<% else %>
<%= govuk_button_link_to "Reactivate this scheme", scheme_reactivate_path(@scheme) %>
<% end %>
<% end %>

35
app/views/schemes/toggle_active.html.erb

@ -0,0 +1,35 @@
<% title = "#{action.humanize} #{@scheme.service_name}" %>
<% content_for :title, title %>
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: scheme_details_path(@scheme),
) %>
<% end %>
<%= form_with model: @scheme, url: scheme_new_deactivation_path(@scheme), method: "patch", local: true do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<% collection_start_date = FormHandler.instance.current_collection_start_date %>
<%= f.govuk_error_summary %>
<%= f.govuk_radio_buttons_fieldset :deactivation_date_type,
legend: { text: I18n.t("questions.scheme.deactivation.apply_from") },
caption: { text: title },
hint: { text: I18n.t("hints.scheme.deactivation", date: collection_start_date.to_formatted_s(:govuk_date)) } do %>
<%= govuk_warning_text text: I18n.t("warnings.scheme.deactivation.existing_logs") %>
<%= f.govuk_radio_button :deactivation_date_type,
"default",
label: { text: "From the start of the current collection period (#{collection_start_date.to_formatted_s(:govuk_date)})" } %>
<%= f.govuk_radio_button :deactivation_date_type,
"other",
label: { text: "For tenancies starting after a certain date" },
**basic_conditional_html_attributes({ "deactivation_date" => %w[other] }, "scheme") do %>
<%= f.govuk_date_field :deactivation_date,
legend: { text: "Date", size: "m" },
hint: { text: "For example, 27 3 2022" },
width: 20 %>
<% end %>
<% end %>
<%= f.govuk_submit "Continue" %>
</div>
</div>
<% end %>

14
config/initializers/feature_toggle.rb

@ -4,21 +4,19 @@ class FeatureToggle
end
def self.sales_log_enabled?
return true unless Rails.env.production?
false
!Rails.env.production?
end
def self.managing_owning_enabled?
return true unless Rails.env.production?
!Rails.env.production?
end
false
def self.scheme_toggle_enabled?
!Rails.env.production?
end
def self.location_toggle_enabled?
return true unless Rails.env.production?
false
!Rails.env.production?
end
def self.managing_for_other_user_enabled?

18
config/locales/en.yml

@ -312,11 +312,16 @@ en:
declaration:
missing: "You must show the DLUHC privacy notice to the tenant before you can submit this log."
scheme:
deactivation_date:
not_selected: "Select one of the options"
invalid: "Enter a valid day, month and year"
out_of_range: "The date must be on or after the %{date}"
location:
deactivation_date:
not_selected: "Select one of the options"
not_entered: "Enter a %{period}"
invalid: "Enter a valid date"
invalid: "Enter a valid day, month and year"
out_of_range: "The date must be on or after the %{date}"
soft_validations:
@ -371,6 +376,9 @@ en:
mobility_type: "What are the mobility standards for the majority of units in this location?"
deactivation:
apply_from: "When should this change apply?"
scheme:
deactivation:
apply_from: "When should this change apply?"
descriptions:
location:
mobility_type:
@ -384,12 +392,18 @@ en:
name: "This is how you refer to this location within your organisation"
units: "A unit can be a bedroom in a shared house or flat, or a house with 4 bedrooms. Do not include bedrooms used for wardens, managers, volunteers or sleep-in staff."
deactivation: "If the date is before %{date}, select ‘From the start of the current collection period’ because the previous period has now closed."
scheme:
deactivation: "If the date is before %{date}, select ‘From the start of the current collection period’ because the previous period has now closed."
warnings:
location:
deactivation:
existing_logs: "It will not be possible to add logs with this location if their tenancy start date is on or after the date you enter. Any existing logs may be affected."
review_logs: "Your data providers will need to review these logs and answer a few questions again. We’ll email each log creator with a list of logs that need updating."
scheme:
deactivation:
existing_logs: "It will not be possible to add logs with this scheme if their tenancy start date is on or after the date you enter. Any existing logs may be affected."
review_logs: "Your data providers will need to review these logs and answer a few questions again. We’ll email each log creator with a list of logs that need updating."
test:
one_argument: "This is based on the tenant’s work situation: %{ecstat1}"

9
config/routes.rb

@ -49,12 +49,19 @@ Rails.application.routes.draw do
get "check-answers", to: "schemes#check_answers"
get "edit-name", to: "schemes#edit_name"
get "support-services-provider", to: "schemes#support_services_provider"
get "new-deactivation", to: "schemes#new_deactivation"
get "deactivate-confirm", to: "schemes#deactivate_confirm"
get "reactivate", to: "schemes#reactivate"
patch "new-deactivation", to: "schemes#new_deactivation"
patch "deactivate", to: "schemes#deactivate"
resources :locations do
get "edit-name", to: "locations#edit_name"
get "edit-local-authority", to: "locations#edit_local_authority"
get "deactivate", to: "locations#deactivate"
get "new-deactivation", to: "locations#new_deactivation"
get "deactivate-confirm", to: "locations#deactivate_confirm"
get "reactivate", to: "locations#reactivate"
patch "new-deactivation", to: "locations#new_deactivation"
patch "deactivate", to: "locations#deactivate"
end
end

5
db/migrate/20221110163351_add_deactivation_date_to_schemes.rb

@ -0,0 +1,5 @@
class AddDeactivationDateToSchemes < ActiveRecord::Migration[7.0]
def change
add_column :schemes, :deactivation_date, :datetime
end
end

7
db/schema.rb

@ -364,15 +364,15 @@ ActiveRecord::Schema[7.0].define(version: 2022_11_11_102656) 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"
@ -398,6 +398,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_11_11_102656) 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

2
spec/features/schemes_spec.rb

@ -779,7 +779,7 @@ RSpec.describe "Schemes scheme Features" do
it "allows to deactivate a location" do
click_link("Deactivate this location")
expect(page).to have_current_path("/schemes/#{scheme.id}/locations/#{location.id}/deactivate")
expect(page).to have_current_path("/schemes/#{scheme.id}/locations/#{location.id}/new-deactivation")
end
context "when I press the back button" do

6
spec/helpers/locations_helper_spec.rb

@ -47,7 +47,7 @@ RSpec.describe LocationsHelper do
end
end
describe "display_attributes" do
describe "display_location_attributes" do
let(:location) { FactoryBot.build(:location, startdate: Time.zone.local(2022, 8, 8)) }
it "returns correct display attributes" do
@ -63,12 +63,12 @@ RSpec.describe LocationsHelper do
{ name: "Status", value: :active },
]
expect(display_attributes(location)).to eq(attributes)
expect(display_location_attributes(location)).to eq(attributes)
end
it "displays created_at as availability date if startdate is not present" do
location.update!(startdate: nil)
availability_attribute = display_attributes(location).find { |x| x[:name] == "Availability" }[:value]
availability_attribute = display_location_attributes(location).find { |x| x[:name] == "Availability" }[:value]
expect(availability_attribute).to eq("Available from #{location.created_at.to_formatted_s(:govuk_date)}")
end

27
spec/helpers/schemes_helper_spec.rb

@ -0,0 +1,27 @@
require "rails_helper"
RSpec.describe SchemesHelper do
describe "display_scheme_attributes" do
let!(:scheme) { FactoryBot.create(:scheme, created_at: Time.zone.local(2022, 8, 8)) }
it "returns correct display attributes" do
attributes = [
{ name: "Scheme code", value: scheme.id_to_display },
{ name: "Name", value: scheme.service_name, edit: true },
{ name: "Confidential information", value: scheme.sensitive, edit: true },
{ name: "Type of scheme", value: scheme.scheme_type },
{ name: "Registered under Care Standards Act 2000", value: scheme.registered_under_care_act },
{ name: "Housing stock owned by", value: scheme.owning_organisation.name, edit: true },
{ name: "Support services provided by", value: scheme.arrangement_type },
{ name: "Primary client group", value: scheme.primary_client_group },
{ name: "Has another client group", value: scheme.has_other_client_group },
{ 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: "Status", value: :active },
]
expect(display_scheme_attributes(scheme)).to eq(attributes)
end
end
end

2
spec/models/form_handler_spec.rb

@ -120,7 +120,7 @@ RSpec.describe FormHandler do
end
it "returns the correct current start date" do
expect(form_handler.current_collection_start_date).to eq(Time.utc(2022, 4, 1))
expect(form_handler.current_collection_start_date).to eq(Time.zone.local(2022, 4, 1))
end
end

12
spec/models/location_spec.rb

@ -121,26 +121,38 @@ RSpec.describe Location, type: :model do
it "returns active if the location is not deactivated" do
location.deactivation_date = nil
location.deactivation_date_type = nil
location.save!
expect(location.status).to eq(:active)
end
it "returns deactivating soon if deactivation_date is in the future" do
location.deactivation_date = Time.zone.local(2022, 8, 8)
location.deactivation_date_type = "other"
location.save!
expect(location.status).to eq(:deactivating_soon)
end
it "returns deactivated if deactivation_date is in the past" do
location.deactivation_date = Time.zone.local(2022, 4, 8)
location.deactivation_date_type = "other"
location.save!
expect(location.status).to eq(:deactivated)
end
it "returns deactivated if deactivation_date is today" do
location.deactivation_date = Time.zone.local(2022, 6, 7)
location.deactivation_date_type = "other"
location.save!
expect(location.status).to eq(:deactivated)
end
end
describe "with deactivation_date (but no deactivation_date_type)" do
let(:location) { FactoryBot.create(:location, deactivation_date: Date.new(2022, 4, 1)) }
it "is valid" do
expect(location).to be_valid
end
end
end

44
spec/models/scheme_spec.rb

@ -91,4 +91,48 @@ RSpec.describe Scheme, type: :model do
end
end
end
describe "status" do
let(:scheme) { FactoryBot.build(:scheme) }
before 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
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 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)
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)
end
end
describe "with deactivation_date (but no deactivation_date_type)" do
let(:scheme) { FactoryBot.create(:scheme, deactivation_date: Date.new(2022, 4, 1)) }
it "is valid" do
expect(scheme).to be_valid
end
end
end

32
spec/requests/locations_controller_spec.rb

@ -1245,29 +1245,37 @@ RSpec.describe LocationsController, type: :request do
before do
Timecop.freeze(Time.utc(2022, 10, 10))
sign_in user
patch "/schemes/#{scheme.id}/locations/#{location.id}/deactivate", params:
patch "/schemes/#{scheme.id}/locations/#{location.id}/new-deactivation", params:
end
context "with default date" do
let(:params) { { location: { deactivation_date_type: "default" } } }
let(:params) { { location: { deactivation_date_type: "default", deactivation_date: } } }
it "renders the confirmation page" do
it "redirects to the confirmation page" do
follow_redirect!
expect(response).to have_http_status(:ok)
expect(page).to have_content("This change will affect #{location.lettings_logs.count} logs")
end
end
context "with other date" do
let(:params) { { location: { deactivation_date: "other", "deactivation_date(3i)": "10", "deactivation_date(2i)": "10", "deactivation_date(1i)": "2022" } } }
let(:params) { { location: { deactivation_date_type: "other", "deactivation_date(3i)": "10", "deactivation_date(2i)": "10", "deactivation_date(1i)": "2022" } } }
it "renders the confirmation page" do
it "redirects to the confirmation page" do
follow_redirect!
expect(response).to have_http_status(:ok)
expect(page).to have_content("This change will affect #{location.lettings_logs.count} logs")
end
end
context "when confirming deactivation" do
let(:params) { { location: { deactivation_date:, confirm: true } } }
let(:params) { { location: { deactivation_date:, confirm: true, deactivation_date_type: "other" } } }
before do
Timecop.freeze(Time.utc(2022, 10, 10))
sign_in user
patch "/schemes/#{scheme.id}/locations/#{location.id}/deactivate", params:
end
it "updates existing location with valid deactivation date and renders location page" do
follow_redirect!
@ -1310,7 +1318,7 @@ RSpec.describe LocationsController, type: :request do
it "displays page with an error message" do
expect(response).to have_http_status(:unprocessable_entity)
expect(page).to have_content(I18n.t("validations.location.deactivation_date.not_entered", period: "day"))
expect(page).to have_content(I18n.t("validations.location.deactivation_date.invalid"))
end
end
@ -1319,7 +1327,7 @@ RSpec.describe LocationsController, type: :request do
it "displays page with an error message" do
expect(response).to have_http_status(:unprocessable_entity)
expect(page).to have_content(I18n.t("validations.location.deactivation_date.not_entered", period: "month"))
expect(page).to have_content(I18n.t("validations.location.deactivation_date.invalid"))
end
end
@ -1328,7 +1336,7 @@ RSpec.describe LocationsController, type: :request do
it "displays page with an error message" do
expect(response).to have_http_status(:unprocessable_entity)
expect(page).to have_content(I18n.t("validations.location.deactivation_date.not_entered", period: "year"))
expect(page).to have_content(I18n.t("validations.location.deactivation_date.invalid"))
end
end
end
@ -1365,21 +1373,24 @@ RSpec.describe LocationsController, type: :request do
Timecop.freeze(Time.utc(2022, 10, 10))
sign_in user
location.deactivation_date = deactivation_date
location.deactivation_date_type = deactivation_date_type
location.save!
get "/schemes/#{scheme.id}/locations/#{location.id}"
end
context "with active location" do
let(:deactivation_date) { nil }
let(:deactivation_date_type) { nil }
it "renders deactivate this location" do
expect(response).to have_http_status(:ok)
expect(page).to have_link("Deactivate this location", href: "/schemes/#{scheme.id}/locations/#{location.id}/deactivate")
expect(page).to have_link("Deactivate this location", href: "/schemes/#{scheme.id}/locations/#{location.id}/new-deactivation")
end
end
context "with deactivated location" do
let(:deactivation_date) { Time.utc(2022, 10, 9) }
let(:deactivation_date_type) { "other" }
it "renders reactivate this location" do
expect(response).to have_http_status(:ok)
@ -1389,6 +1400,7 @@ RSpec.describe LocationsController, type: :request do
context "with location that's deactivating soon" do
let(:deactivation_date) { Time.utc(2022, 10, 12) }
let(:deactivation_date_type) { "other" }
it "renders reactivate this location" do
expect(response).to have_http_status(:ok)

2
spec/requests/organisations_controller_spec.rb

@ -59,7 +59,7 @@ RSpec.describe OrganisationsController, type: :request do
expect(page).to have_field("search", type: "search")
end
it "has hidden accebility field with description" do
it "has hidden accessibility field with description" do
expected_field = "<h2 class=\"govuk-visually-hidden\">Supported housing schemes</h2>"
expect(CGI.unescape_html(response.body)).to include(expected_field)
end

173
spec/requests/schemes_controller_spec.rb

@ -242,6 +242,50 @@ RSpec.describe SchemesController, type: :request do
expect(response).to have_http_status(:not_found)
end
end
context "when looking at scheme details" do
let(:user) { FactoryBot.create(:user, :data_coordinator) }
let!(:scheme) { FactoryBot.create(:scheme, owning_organisation: user.organisation) }
before do
Timecop.freeze(Time.utc(2022, 10, 10))
sign_in user
scheme.deactivation_date = deactivation_date
scheme.deactivation_date_type = deactivation_date_type
scheme.save!
get "/schemes/#{scheme.id}"
end
context "with active scheme" do
let(:deactivation_date) { nil }
let(:deactivation_date_type) { nil }
it "renders deactivate this scheme" do
expect(response).to have_http_status(:ok)
expect(page).to have_link("Deactivate this scheme", href: "/schemes/#{scheme.id}/new-deactivation")
end
end
context "with deactivated scheme" do
let(:deactivation_date) { Time.utc(2022, 10, 9) }
let(:deactivation_date_type) { "other" }
it "renders reactivate this scheme" do
expect(response).to have_http_status(:ok)
expect(page).to have_link("Reactivate this scheme", href: "/schemes/#{scheme.id}/reactivate")
end
end
context "with scheme that's deactivating soon" do
let(:deactivation_date) { Time.utc(2022, 10, 12) }
let(:deactivation_date_type) { "other" }
it "renders reactivate this scheme" do
expect(response).to have_http_status(:ok)
expect(page).to have_link("Reactivate this scheme", href: "/schemes/#{scheme.id}/reactivate")
end
end
end
end
context "when signed in as a support user" do
@ -1698,4 +1742,133 @@ RSpec.describe SchemesController, type: :request do
end
end
end
describe "#deactivate" do
context "when not signed in" do
it "redirects to the sign in page" do
patch "/schemes/1/new-deactivation"
expect(response).to redirect_to("/account/sign-in")
end
end
context "when signed in as a data provider" do
let(:user) { FactoryBot.create(:user) }
before do
sign_in user
patch "/schemes/1/new-deactivation"
end
it "returns 401 unauthorized" do
request
expect(response).to have_http_status(:unauthorized)
end
end
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(:deactivation_date) { Time.utc(2022, 10, 10) }
before do
Timecop.freeze(Time.utc(2022, 10, 10))
sign_in user
patch "/schemes/#{scheme.id}/new-deactivation", params:
end
context "with default date" do
let(:params) { { scheme: { deactivation_date_type: "default", deactivation_date: } } }
it "redirects to the confirmation page" do
follow_redirect!
expect(response).to have_http_status(:ok)
expect(page).to have_content("This change will affect #{scheme.lettings_logs.count} logs")
end
end
context "with other date" do
let(:params) { { scheme: { deactivation_date_type: "other", "deactivation_date(3i)": "10", "deactivation_date(2i)": "10", "deactivation_date(1i)": "2022" } } }
it "redirects to the confirmation page" do
follow_redirect!
expect(response).to have_http_status(:ok)
expect(page).to have_content("This change will affect #{scheme.lettings_logs.count} logs")
end
end
context "when confirming deactivation" do
let(:params) { { scheme: { deactivation_date:, confirm: true, deactivation_date_type: "other" } } }
before do
Timecop.freeze(Time.utc(2022, 10, 10))
sign_in user
patch "/schemes/#{scheme.id}/deactivate", params:
end
it "updates existing scheme with valid deactivation date and renders scheme page" do
follow_redirect!
follow_redirect!
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)
end
end
context "when the date is not selected" do
let(:params) { { scheme: { "deactivation_date": "" } } }
it "displays the new page with an error message" do
expect(response).to have_http_status(:unprocessable_entity)
expect(page).to have_content(I18n.t("validations.scheme.deactivation_date.not_selected"))
end
end
context "when invalid date is entered" do
let(:params) { { scheme: { deactivation_date_type: "other", "deactivation_date(3i)": "10", "deactivation_date(2i)": "44", "deactivation_date(1i)": "2022" } } }
it "displays the new page with an error message" do
expect(response).to have_http_status(:unprocessable_entity)
expect(page).to have_content(I18n.t("validations.scheme.deactivation_date.invalid"))
end
end
context "when the date is entered is before the beginning of current collection window" do
let(:params) { { scheme: { deactivation_date_type: "other", "deactivation_date(3i)": "10", "deactivation_date(2i)": "4", "deactivation_date(1i)": "2020" } } }
it "displays the new page with an error message" do
expect(response).to have_http_status(:unprocessable_entity)
expect(page).to have_content(I18n.t("validations.scheme.deactivation_date.out_of_range", date: "1 April 2022"))
end
end
context "when the day is not entered" do
let(:params) { { scheme: { deactivation_date_type: "other", "deactivation_date(3i)": "", "deactivation_date(2i)": "2", "deactivation_date(1i)": "2022" } } }
it "displays page with an error message" do
expect(response).to have_http_status(:unprocessable_entity)
expect(page).to have_content(I18n.t("validations.scheme.deactivation_date.invalid"))
end
end
context "when the month is not entered" do
let(:params) { { scheme: { deactivation_date_type: "other", "deactivation_date(3i)": "2", "deactivation_date(2i)": "", "deactivation_date(1i)": "2022" } } }
it "displays page with an error message" do
expect(response).to have_http_status(:unprocessable_entity)
expect(page).to have_content(I18n.t("validations.scheme.deactivation_date.invalid"))
end
end
context "when the year is not entered" do
let(:params) { { scheme: { deactivation_date_type: "other", "deactivation_date(3i)": "2", "deactivation_date(2i)": "2", "deactivation_date(1i)": "" } } }
it "displays page with an error message" do
expect(response).to have_http_status(:unprocessable_entity)
expect(page).to have_content(I18n.t("validations.scheme.deactivation_date.invalid"))
end
end
end
end
end

Loading…
Cancel
Save