Browse Source

Merge branch 'main' into CLDC-1665-add-scheme-status

# Conflicts:
#	app/helpers/tag_helper.rb
CLDC-1665-add-scheme-status^2
natdeanlewissoftwire 3 years ago
parent
commit
8e22fd4597
  1. 2
      .rspec_parallel
  2. 2
      Gemfile
  3. 4
      Gemfile.lock
  4. 83
      app/controllers/locations_controller.rb
  5. 66
      app/controllers/schemes_controller.rb
  6. 6
      app/frontend/controllers/conditional_question_controller.js
  7. 6
      app/helpers/check_answers_helper.rb
  8. 14
      app/helpers/locations_helper.rb
  9. 10
      app/helpers/question_attribute_helper.rb
  10. 26
      app/helpers/schemes_helper.rb
  11. 8
      app/helpers/tag_helper.rb
  12. 2
      app/models/form/common/questions/created_by_id.rb
  13. 2
      app/models/form/common/questions/owning_organisation_id.rb
  14. 19
      app/models/form/lettings/pages/created_by.rb
  15. 30
      app/models/form/lettings/pages/housing_provider.rb
  16. 30
      app/models/form/lettings/pages/managing_organisation.rb
  17. 48
      app/models/form/lettings/questions/created_by_id.rb
  18. 68
      app/models/form/lettings/questions/housing_provider.rb
  19. 74
      app/models/form/lettings/questions/managing_organisation.rb
  20. 2
      app/models/form/lettings/questions/scheme_id.rb
  21. 38
      app/models/form/lettings/subsections/setup.rb
  22. 2
      app/models/form/question.rb
  23. 2
      app/models/form/sales/subsections/household_characteristics.rb
  24. 2
      app/models/form/sales/subsections/household_needs.rb
  25. 2
      app/models/form/sales/subsections/income_benefits_and_outgoings.rb
  26. 2
      app/models/form/sales/subsections/property_information.rb
  27. 6
      app/models/form/subsection.rb
  28. 4
      app/models/form_handler.rb
  29. 4
      app/models/lettings_log.rb
  30. 52
      app/models/location.rb
  31. 4
      app/models/log.rb
  32. 2
      app/models/organisation.rb
  33. 3
      app/models/organisation_relationship.rb
  34. 4
      app/models/sales_log.rb
  35. 68
      app/models/scheme.rb
  36. 3
      app/models/validations/financial_validations.rb
  37. 4
      app/models/validations/property_validations.rb
  38. 16
      app/models/validations/tenancy_validations.rb
  39. 8
      app/services/imports/import_service.rb
  40. 10
      app/services/imports/lettings_logs_field_import_service.rb
  41. 18
      app/services/imports/lettings_logs_import_service.rb
  42. 34
      app/services/imports/scheme_location_import_service.rb
  43. 2
      app/services/imports/user_import_service.rb
  44. 2
      app/views/form/_radio_question.html.erb
  45. 2
      app/views/form/_select_question.html.erb
  46. 17
      app/views/locations/deactivate_confirm.html.erb
  47. 2
      app/views/locations/edit.html.erb
  48. 2
      app/views/locations/edit_local_authority.html.erb
  49. 4
      app/views/locations/edit_name.html.erb
  50. 4
      app/views/locations/index.html.erb
  51. 2
      app/views/locations/new.html.erb
  52. 32
      app/views/locations/show.html.erb
  53. 39
      app/views/locations/toggle_active.html.erb
  54. 2
      app/views/organisations/show.html.erb
  55. 2
      app/views/schemes/check_answers.html.erb
  56. 19
      app/views/schemes/deactivate_confirm.html.erb
  57. 12
      app/views/schemes/show.html.erb
  58. 35
      app/views/schemes/toggle_active.html.erb
  59. 2
      config/database.yml
  60. 1
      config/environments/test.rb
  61. 12
      config/forms/2021_2022.json
  62. 20
      config/forms/2022_2023.json
  63. 18
      config/initializers/feature_toggle.rb
  64. 35
      config/locales/en.yml
  65. 21
      config/routes.rb
  66. 5
      db/migrate/20221109122033_add_deactivation_date_to_locations.rb
  67. 5
      db/migrate/20221110163351_add_deactivation_date_to_schemes.rb
  68. 13
      db/migrate/20221111102656_change_old_visible_id_type.rb
  69. 22
      db/schema.rb
  70. 42
      db/seeds.rb
  71. 2
      docs/form/builder.md
  72. 16
      docs/testing.md
  73. 2
      spec/factories/location.rb
  74. 31
      spec/features/lettings_log_spec.rb
  75. 46
      spec/features/schemes_spec.rb
  76. 7
      spec/fixtures/forms/2021_2022.json
  77. 2
      spec/fixtures/forms/2022_2023.json
  78. 2
      spec/fixtures/imports/logs/0b4a68df-30cc-474a-93c0-a56ce8fdad3b.xml
  79. 2
      spec/fixtures/imports/mgmtgroups/6d6d7618b58affe2a150a5ef2e9f4765fa6cd05d.xml
  80. 19
      spec/fixtures/imports/user/80d9b73aa1c88b6e5c36ee49be9050b923b4a1bb.xml
  81. 27
      spec/helpers/locations_helper_spec.rb
  82. 2
      spec/helpers/question_attribute_helper_spec.rb
  83. 27
      spec/helpers/schemes_helper_spec.rb
  84. 55
      spec/models/form/lettings/pages/created_by_spec.rb
  85. 128
      spec/models/form/lettings/pages/housing_provider_spec.rb
  86. 130
      spec/models/form/lettings/pages/managing_organisation_spec.rb
  87. 82
      spec/models/form/lettings/questions/created_by_id_spec.rb
  88. 117
      spec/models/form/lettings/questions/housing_provider_spec.rb
  89. 155
      spec/models/form/lettings/questions/managing_organisation_spec.rb
  90. 66
      spec/models/form/lettings/subsections/setup_spec.rb
  91. 2
      spec/models/form/sales/subsections/household_characteristics_spec.rb
  92. 2
      spec/models/form/sales/subsections/income_benefits_and_outgoings_spec.rb
  93. 12
      spec/models/form_handler_spec.rb
  94. 44
      spec/models/location_spec.rb
  95. 44
      spec/models/scheme_spec.rb
  96. 66
      spec/models/validations/financial_validations_spec.rb
  97. 11
      spec/models/validations/property_validations_spec.rb
  98. 48
      spec/models/validations/tenancy_validations_spec.rb
  99. 40
      spec/requests/form_controller_spec.rb
  100. 201
      spec/requests/locations_controller_spec.rb
  101. Some files were not shown because too many files have changed in this diff Show More

2
.rspec_parallel

@ -0,0 +1,2 @@
--format progress
--format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log

2
Gemfile

@ -68,6 +68,8 @@ group :development, :test do
gem "byebug", platforms: %i[mri mingw x64_mingw]
gem "dotenv-rails"
gem "pry-byebug"
gem "parallel_tests"
end
group :development do

4
Gemfile.lock

@ -247,6 +247,8 @@ GEM
globalid
paper_trail (>= 3.0.0)
parallel (1.22.1)
parallel_tests (4.0.0)
parallel
parser (3.1.2.1)
ast (~> 2.4.1)
pg (1.4.3)
@ -430,6 +432,7 @@ PLATFORMS
x86_64-darwin-19
x86_64-darwin-20
x86_64-darwin-21
x86_64-darwin-22
x86_64-linux
DEPENDENCIES
@ -456,6 +459,7 @@ DEPENDENCIES
overcommit (>= 0.37.0)
paper_trail
paper_trail-globalid
parallel_tests
pg (~> 1.1)
possessive
postcodes_io

83
app/controllers/locations_controller.rb

@ -18,14 +18,49 @@ class LocationsController < ApplicationController
@location = Location.new
end
def show; end
def new_deactivation
if params[:location].blank?
render "toggle_active", locals: { action: "deactivate" }
else
@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", 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
def create
if date_params_missing?(location_params) || valid_date_params?(location_params)
@location = Location.new(location_params)
if @location.save
if @location.location_admin_district.nil?
redirect_to(location_edit_local_authority_path(id: @scheme.id, location_id: @location.id, add_another_location: location_params[:add_another_location]))
redirect_to(scheme_location_edit_local_authority_path(scheme_id: @scheme.id, location_id: @location.id, add_another_location: location_params[:add_another_location]))
elsif location_params[:add_another_location] == "Yes"
redirect_to new_location_path(@scheme)
redirect_to new_scheme_location_path(@scheme)
else
check_answers_path = @scheme.confirmed? ? scheme_check_answers_path(@scheme, anchor: "locations") : scheme_check_answers_path(@scheme)
redirect_to check_answers_path
@ -69,17 +104,21 @@ class LocationsController < ApplicationController
case page
when "edit"
if @location.location_admin_district.nil?
redirect_to(location_edit_local_authority_path(id: @scheme.id, location_id: @location.id, add_another_location: location_params[:add_another_location]))
redirect_to(scheme_location_edit_local_authority_path(scheme_id: @scheme.id, location_id: @location.id, add_another_location: location_params[:add_another_location]))
elsif location_params[:add_another_location] == "Yes"
redirect_to(new_location_path(@location.scheme))
redirect_to(new_scheme_location_path(@location.scheme))
else
redirect_to(scheme_check_answers_path(@scheme, anchor: "locations"))
end
when "edit-name"
redirect_to(scheme_check_answers_path(@scheme, anchor: "locations"))
if @scheme.locations.count == @scheme.locations.active.count
redirect_to(scheme_location_path(@scheme, @location))
else
redirect_to(scheme_check_answers_path(@scheme, anchor: "locations"))
end
when "edit-local-authority"
if params[:add_another_location] == "Yes"
redirect_to(new_location_path(@location.scheme))
redirect_to(new_scheme_location_path(@location.scheme))
else
redirect_to(scheme_check_answers_path(@scheme, anchor: "locations"))
end
@ -107,7 +146,7 @@ private
def find_scheme
@scheme = if %w[new create index edit_name].include?(action_name)
Scheme.find(params[:id])
Scheme.find(params[:scheme_id])
else
@location&.scheme
end
@ -122,13 +161,13 @@ private
end
def authenticate_action!
if %w[new edit update create index edit_name edit_local_authority].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
@ -140,4 +179,30 @@ private
def valid_location_admin_district?(location_params)
location_params["location_admin_district"] != "Select an option"
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
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?)
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

66
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
@ -176,7 +209,7 @@ private
when "secondary-client-group"
scheme_support_path(@scheme)
when "support"
new_location_path
new_scheme_location_path(@scheme)
when "details"
if @scheme.arrangement_type_before_type_cast == "D"
scheme_primary_client_group_path(@scheme)
@ -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

6
app/frontend/controllers/conditional_question_controller.js

@ -10,14 +10,14 @@ export default class extends Controller {
const selectedValue = this.element.value
const dataInfo = JSON.parse(this.element.dataset.info)
const conditionalFor = dataInfo.conditional_questions
const logType = dataInfo.log_type
const type = dataInfo.type
Object.entries(conditionalFor).forEach(([targetQuestion, conditions]) => {
if (!conditions.map(String).includes(String(selectedValue))) {
const textNumericInput = document.getElementById(`${logType}-log-${targetQuestion.replaceAll('_', '-')}-field`)
const textNumericInput = document.getElementById(`${type}-${targetQuestion.replaceAll('_', '-')}-field`)
if (textNumericInput == null) {
const dateInputs = [1, 2, 3].map((idx) => {
return document.getElementById(`${logType}_log_${targetQuestion}_${idx}i`)
return document.getElementById(`${type.replaceAll('-', '_')}_${targetQuestion}_${idx}i`)
})
this.clearDateInputs(dateInputs)
} else {

6
app/helpers/check_answers_helper.rb

@ -18,14 +18,14 @@ module CheckAnswersHelper
def get_location_change_link_href_postcode(scheme, location)
if location.confirmed?
location_edit_name_path(id: scheme.id, location_id: location.id)
scheme_location_path(scheme_id: scheme.id, id: location.id)
else
location_edit_path(id: scheme.id, location_id: location.id)
edit_scheme_location_path(scheme_id: scheme.id, id: location.id)
end
end
def get_location_change_link_href_location_admin_district(scheme, location)
location_edit_local_authority_path(id: scheme.id, location_id: location.id)
scheme_location_edit_local_authority_path(scheme_id: scheme.id, location_id: location.id)
end
def any_questions_have_summary_card_number?(subsection, lettings_log)

14
app/helpers/locations_helper.rb

@ -22,4 +22,18 @@ module LocationsHelper
resource.map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize) }
end
def display_location_attributes(location)
[
{ name: "Postcode", value: location.postcode },
{ name: "Local authority", value: location.location_admin_district },
{ name: "Location name", value: location.name, edit: true },
{ name: "Total number of units at this location", value: location.units },
{ name: "Common type of unit", value: location.type_of_unit },
{ name: "Mobility type", value: location.mobility_type },
{ name: "Code", value: location.location_code },
{ name: "Availability", value: "Available from #{location.available_from.to_formatted_s(:govuk_date)}" },
{ name: "Status", value: location.status },
]
end
end

10
app/helpers/question_attribute_helper.rb

@ -7,6 +7,14 @@ module QuestionAttributeHelper
merge_controller_attributes(*attribs)
end
def basic_conditional_html_attributes(conditional_for, type)
{
"data-controller": "conditional-question",
"data-action": "click->conditional-question#displayConditional",
"data-info": { conditional_questions: conditional_for, type: }.to_json,
}
end
private
def numeric_question_html_attributes(question)
@ -27,7 +35,7 @@ private
{
"data-controller": "conditional-question",
"data-action": "click->conditional-question#displayConditional",
"data-info": { conditional_questions: question.conditional_for, log_type: question.form.type }.to_json,
"data-info": { conditional_questions: question.conditional_for, type: "#{question.form.type}-log" }.to_json,
}
end
end

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

8
app/helpers/tag_helper.rb

@ -8,8 +8,8 @@ module TagHelper
completed: "Completed",
active: "Active",
incomplete: "Incomplete",
deactivates_soon: "Deactivates soon",
reactivates_soon: "Reactivates soon",
deactivating_soon: "Deactivating soon",
reactivating_soon: "Reactivating soon",
deactivated: "Deactivated",
}.freeze
@ -20,8 +20,8 @@ module TagHelper
completed: "green",
active: "green",
incomplete: "red",
deactivates_soon: "yellow",
reactivates_soon: "blue",
deactivating_soon: "yellow",
reactivating_soon: "blue",
deactivated: "grey",
}.freeze

2
app/models/form/common/questions/created_by_id.rb

@ -19,7 +19,7 @@ class Form::Common::Questions::CreatedById < ::Form::Question
end
end
def displayed_answer_options(log)
def displayed_answer_options(log, _user = nil)
return answer_options unless log.owning_organisation
user_ids = log.owning_organisation.users.pluck(:id) + [""]

2
app/models/form/common/questions/owning_organisation_id.rb

@ -19,7 +19,7 @@ class Form::Common::Questions::OwningOrganisationId < ::Form::Question
end
end
def displayed_answer_options(_log)
def displayed_answer_options(_log, _user = nil)
answer_options
end

19
app/models/form/lettings/pages/created_by.rb

@ -0,0 +1,19 @@
class Form::Lettings::Pages::CreatedBy < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "created_by"
@header = ""
@description = ""
@subsection = subsection
end
def questions
@questions ||= [
Form::Lettings::Questions::CreatedById.new(nil, nil, self),
]
end
def routed_to?(_log, current_user)
!!current_user&.support?
end
end

30
app/models/form/lettings/pages/housing_provider.rb

@ -0,0 +1,30 @@
class Form::Lettings::Pages::HousingProvider < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "housing_provider"
@header = ""
@description = ""
@subsection = subsection
end
def questions
@questions ||= [
Form::Lettings::Questions::HousingProvider.new(nil, nil, self),
]
end
def routed_to?(log, current_user)
return false unless current_user
return true if current_user.support?
return true unless current_user.organisation.holds_own_stock?
housing_providers = current_user.organisation.housing_providers
return false if housing_providers.count.zero?
return true if housing_providers.count > 1
log.update!(owning_organisation: housing_providers.first)
false
end
end

30
app/models/form/lettings/pages/managing_organisation.rb

@ -0,0 +1,30 @@
class Form::Lettings::Pages::ManagingOrganisation < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "managing_organisation"
@header = ""
@description = ""
@subsection = subsection
end
def questions
@questions ||= [
Form::Lettings::Questions::ManagingOrganisation.new(nil, nil, self),
]
end
def routed_to?(log, current_user)
return false unless current_user
return true if current_user.support?
return true unless current_user.organisation.holds_own_stock?
managing_agents = current_user.organisation.managing_agents
return false if managing_agents.count.zero?
return true if managing_agents.count > 1
log.update!(managing_organisation: managing_agents.first)
false
end
end

48
app/models/form/lettings/questions/created_by_id.rb

@ -0,0 +1,48 @@
class Form::Lettings::Questions::CreatedById < ::Form::Question
def initialize(id, hsh, page)
super
@id = "created_by_id"
@check_answer_label = "Log owner"
@header = "Which user are you creating this log for?"
@hint_text = ""
@type = "select"
@page = page
end
def answer_options
answer_opts = { "" => "Select an option" }
return answer_opts unless ActiveRecord::Base.connected?
User.select(:id, :name, :email).each_with_object(answer_opts) do |user, hsh|
hsh[user.id] = "#{user.name} (#{user.email})"
hsh
end
end
def displayed_answer_options(log, _user = nil)
return answer_options unless log.owning_organisation
user_ids = log.owning_organisation.users.pluck(:id) + [""]
answer_options.select { |k, _v| user_ids.include?(k) }
end
def label_from_value(value)
return unless value
answer_options[value]
end
def hidden_in_check_answers?(_log, current_user)
!current_user.support?
end
def derived?
true
end
private
def selected_answer_option_is_derived?(_log)
true
end
end

68
app/models/form/lettings/questions/housing_provider.rb

@ -0,0 +1,68 @@
class Form::Lettings::Questions::HousingProvider < ::Form::Question
attr_accessor :current_user, :log
def initialize(id, hsh, page)
super
@id = "owning_organisation_id"
@check_answer_label = "Housing provider"
@header = "Which organisation owns this property?"
@type = "select"
@page = page
end
def answer_options
answer_opts = { "" => "Select an option" }
return answer_opts unless ActiveRecord::Base.connected?
return answer_opts unless current_user
if !current_user.support? && current_user.organisation.holds_own_stock?
answer_opts[current_user.organisation.id] = "#{current_user.organisation.name} (Your organisation)"
end
answer_opts.merge(housing_providers_answer_options)
end
def displayed_answer_options(log, user = nil)
@current_user = user
@log = log
answer_options
end
def label_from_value(value)
return unless value
answer_options[value]
end
def derived?
true
end
def hidden_in_check_answers?(_log, user = nil)
@current_user = user
return false unless @current_user
return false if @current_user.support?
housing_providers_answer_options.count < 2
end
def enabled
true
end
private
def selected_answer_option_is_derived?(_log)
true
end
def housing_providers_answer_options
if current_user.support?
Organisation
else
current_user.organisation.housing_providers
end.pluck(:id, :name).to_h
end
end

74
app/models/form/lettings/questions/managing_organisation.rb

@ -0,0 +1,74 @@
class Form::Lettings::Questions::ManagingOrganisation < ::Form::Question
attr_accessor :current_user, :log
def initialize(id, hsh, page)
super
@id = "managing_organisation_id"
@check_answer_label = "Managing agent"
@header = "Which organisation manages this letting?"
@type = "select"
@answer_options = answer_options
@page = page
end
def answer_options
opts = { "" => "Select an option" }
return opts unless ActiveRecord::Base.connected?
return opts unless current_user
return opts unless log
if current_user.support?
if log.owning_organisation.holds_own_stock?
opts[log.owning_organisation.id] = "#{log.owning_organisation.name} (Owning organisation)"
end
elsif current_user.organisation.holds_own_stock?
opts[current_user.organisation.id] = "#{current_user.organisation.name} (Your organisation)"
end
opts.merge(managing_organisations_answer_options)
end
def displayed_answer_options(log, user)
@current_user = user
@log = log
answer_options
end
def label_from_value(value)
return unless value
answer_options[value]
end
def derived?
true
end
def hidden_in_check_answers?(_log, user = nil)
@current_user = user
return false unless @current_user
return false if @current_user.support?
managing_organisations_answer_options.count < 2
end
def enabled
true
end
private
def selected_answer_option_is_derived?(_log)
true
end
def managing_organisations_answer_options
if current_user.support?
log.owning_organisation
else
current_user.organisation
end.managing_agents.pluck(:id, :name).to_h
end
end

2
app/models/form/lettings/questions/scheme_id.rb

@ -20,7 +20,7 @@ class Form::Lettings::Questions::SchemeId < ::Form::Question
end
end
def displayed_answer_options(lettings_log)
def displayed_answer_options(lettings_log, _user = nil)
organisation = lettings_log.owning_organisation || lettings_log.created_by&.organisation
schemes = organisation ? Scheme.select(:id).where(owning_organisation_id: organisation.id, confirmed: true) : Scheme.select(:id).where(confirmed: true)
filtered_scheme_ids = schemes.joins(:locations).merge(Location.where("startdate <= ? or startdate IS NULL", Time.zone.today)).map(&:id)

38
app/models/form/lettings/subsections/setup.rb

@ -8,8 +8,10 @@ class Form::Lettings::Subsections::Setup < ::Form::Subsection
def pages
@pages ||= [
Form::Common::Pages::Organisation.new(nil, nil, self),
Form::Common::Pages::CreatedBy.new(nil, nil, self),
organisation_page,
housing_provider_page,
managing_organisation_page,
created_by_page,
Form::Lettings::Pages::NeedsType.new(nil, nil, self),
Form::Lettings::Pages::Scheme.new(nil, nil, self),
Form::Lettings::Pages::Location.new(nil, nil, self),
@ -18,11 +20,7 @@ class Form::Lettings::Subsections::Setup < ::Form::Subsection
Form::Lettings::Pages::RentType.new(nil, nil, self),
Form::Lettings::Pages::TenantCode.new(nil, nil, self),
Form::Lettings::Pages::PropertyReference.new(nil, nil, self),
]
end
def applicable_questions(lettings_log)
questions.select { |q| support_only_questions.include?(q.id) } + super
].compact
end
def enabled?(_lettings_log)
@ -31,7 +29,29 @@ class Form::Lettings::Subsections::Setup < ::Form::Subsection
private
def support_only_questions
%w[owning_organisation_id created_by_id].freeze
def organisation_page
return if FeatureToggle.managing_for_other_user_enabled?
Form::Common::Pages::Organisation.new(nil, nil, self)
end
def housing_provider_page
return unless FeatureToggle.managing_for_other_user_enabled?
Form::Lettings::Pages::HousingProvider.new(nil, nil, self)
end
def managing_organisation_page
return unless FeatureToggle.managing_for_other_user_enabled?
Form::Lettings::Pages::ManagingOrganisation.new(nil, nil, self)
end
def created_by_page
if FeatureToggle.managing_for_other_user_enabled?
Form::Lettings::Pages::CreatedBy.new(nil, nil, self)
else
Form::Common::Pages::CreatedBy.new(nil, nil, self)
end
end
end

2
app/models/form/question.rb

@ -107,7 +107,7 @@ class Form::Question
false
end
def displayed_answer_options(log)
def displayed_answer_options(log, _current_user = nil)
answer_options.select do |_key, val|
!val.is_a?(Hash) || !val["depends_on"] || form.depends_on_met(val["depends_on"], log)
end

2
app/models/form/sales/subsections/household_characteristics.rb

@ -4,7 +4,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection
@id = "household_characteristics"
@label = "Household characteristics"
@section = section
@depends_on = [{ "setup" => "completed" }]
@depends_on = [{ "setup_completed?" => true }]
end
def pages

2
app/models/form/sales/subsections/household_needs.rb

@ -4,7 +4,7 @@ class Form::Sales::Subsections::HouseholdNeeds < ::Form::Subsection
@id = "household_needs"
@label = "Household needs"
@section = section
@depends_on = [{ "setup" => "completed" }]
@depends_on = [{ "setup_completed?" => true }]
end
def pages

2
app/models/form/sales/subsections/income_benefits_and_outgoings.rb

@ -4,7 +4,7 @@ class Form::Sales::Subsections::IncomeBenefitsAndOutgoings < ::Form::Subsection
@id = "income_benefits_and_outgoings"
@label = "Income, benefits and outgoings"
@section = section
@depends_on = [{ "setup" => "completed" }]
@depends_on = [{ "setup_completed?" => true }]
end
def pages

2
app/models/form/sales/subsections/property_information.rb

@ -4,7 +4,7 @@ class Form::Sales::Subsections::PropertyInformation < ::Form::Subsection
@id = "property_information"
@label = "Property information"
@section = section
@depends_on = [{ "setup" => "completed" }]
@depends_on = [{ "setup_completed?" => true }]
end
def pages

6
app/models/form/subsection.rb

@ -20,11 +20,7 @@ class Form::Subsection
def enabled?(log)
return true unless depends_on
depends_on.any? do |conditions_set|
conditions_set.all? do |subsection_id, dependent_status|
form.get_subsection(subsection_id).status(log) == dependent_status.to_sym
end
end
form.depends_on_met(depends_on, log)
end
def status(log)

4
app/models/form_handler.rb

@ -49,6 +49,10 @@ class FormHandler
today < window_end_date ? today.year - 1 : today.year
end
def current_collection_start_date
Time.zone.local(current_collection_start_year, 4, 1)
end
def form_name_from_start_year(year, type)
form_mappings = { 0 => "current_#{type}", 1 => "previous_#{type}", -1 => "next_#{type}" }
form_mappings[current_collection_start_year - year]

4
app/models/lettings_log.rb

@ -507,6 +507,10 @@ class LettingsLog < Log
form.get_question("rent_type", self)&.label_from_value(rent_type)
end
def non_location_setup_questions_completed?
[needstype, renewal, rent_type, startdate, owning_organisation_id, created_by_id].all?(&:present?)
end
private
PIO = PostcodeService.new

52
app/models/location.rb

@ -1,7 +1,9 @@
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"
has_paper_trail
@ -9,7 +11,7 @@ class Location < ApplicationRecord
auto_strip_attributes :name
attr_accessor :add_another_location
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}%") }
@ -359,14 +361,6 @@ class Location < ApplicationRecord
enum type_of_unit: TYPE_OF_UNIT
def display_attributes
[
{ name: "Location code ", value: location_code, suffix: false },
{ name: "Postcode", value: postcode, suffix: county },
{ name: "Type of unit", value: type_of_unit, suffix: false },
]
end
def postcode=(postcode)
if postcode
super UKPostcode.parse(postcode).to_s
@ -375,6 +369,46 @@ class Location < ApplicationRecord
end
end
def available_from
startdate || 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.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
PIO = PostcodeService.new

4
app/models/log.rb

@ -40,6 +40,10 @@ class Log < ApplicationRecord
ethnic_group == 17
end
def managing_organisation_provider_type
managing_organisation&.provider_type
end
private
def update_status!

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 },

3
app/models/organisation_relationship.rb

@ -2,6 +2,9 @@ class OrganisationRelationship < ApplicationRecord
belongs_to :child_organisation, class_name: "Organisation"
belongs_to :parent_organisation, class_name: "Organisation"
scope :owning, -> { where(relationship_type: OWNING) }
scope :managing, -> { where(relationship_type: MANAGING) }
OWNING = "owning".freeze
MANAGING = "managing".freeze
RELATIONSHIP_TYPE = {

4
app/models/sales_log.rb

@ -47,4 +47,8 @@ class SalesLog < Log
def completed?
status == "completed"
end
def setup_completed?
form.setup_sections.all? { |sections| sections.subsections.all? { |subsection| subsection.status(self) == :completed } }
end
end

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

3
app/models/validations/financial_validations.rb

@ -34,10 +34,12 @@ module Validations::FinancialValidations
if record.earnings.present? && record.incfreq.blank?
record.errors.add :incfreq, I18n.t("validations.financial.earnings.freq_missing")
record.errors.add :earnings, I18n.t("validations.financial.earnings.freq_missing")
end
if record.incfreq.present? && record.earnings.blank?
record.errors.add :earnings, I18n.t("validations.financial.earnings.earnings_missing")
record.errors.add :incfreq, I18n.t("validations.financial.earnings.earnings_missing")
end
end
@ -110,6 +112,7 @@ module Validations::FinancialValidations
if record.is_carehome?
period = record.form.get_question("period", record).label_from_value(record.period).downcase
if record.chcharge.blank?
record.errors.add :is_carehome, I18n.t("validations.financial.carehome.not_provided", period:)
record.errors.add :chcharge, I18n.t("validations.financial.carehome.not_provided", period:)
elsif !weekly_value_in_range(record, "chcharge", 10, 1000)
max_chcharge = record.weekly_to_value_per_period(1000)

4
app/models/validations/property_validations.rb

@ -38,6 +38,10 @@ module Validations::PropertyValidations
record.errors.add :rsnvac, I18n.t("validations.property.rsnvac.referral_invalid")
record.errors.add :referral, I18n.t("validations.household.referral.rsnvac_non_temp")
end
if record.renewal.present? && record.renewal.zero? && record.rsnvac == 14
record.errors.add :rsnvac, I18n.t("validations.property.rsnvac.not_a_renewal")
end
end
def validate_unitletas(record)

16
app/models/validations/tenancy_validations.rb

@ -5,7 +5,7 @@ module Validations::TenancyValidations
def validate_fixed_term_tenancy(record)
is_present = record.tenancylength.present?
is_in_range = record.tenancylength.to_i.between?(2, 99)
is_in_range = record.tenancylength.to_i.between?(min_tenancy_length(record), 99)
conditions = [
{
condition: !(record.is_secure_tenancy? || record.is_assured_shorthold_tenancy?) && is_present,
@ -13,11 +13,17 @@ module Validations::TenancyValidations
},
{
condition: (record.is_assured_shorthold_tenancy? && !is_in_range) && is_present,
error: I18n.t("validations.tenancy.length.shorthold"),
error: I18n.t(
"validations.tenancy.length.shorthold",
min_tenancy_length: min_tenancy_length(record),
),
},
{
condition: record.is_secure_tenancy? && (!is_in_range && is_present),
error: I18n.t("validations.tenancy.length.secure"),
error: I18n.t(
"validations.tenancy.length.secure",
min_tenancy_length: min_tenancy_length(record),
),
},
]
@ -41,4 +47,8 @@ module Validations::TenancyValidations
record.errors.add :hhmemb, I18n.t("validations.tenancy.joint_more_than_one_member")
end
end
def min_tenancy_length(record)
record.is_supported_housing? || record.renttype == 3 ? 1 : 2
end
end

8
app/services/imports/import_service.rb

@ -19,8 +19,12 @@ module Imports
end
end
def field_value(xml_document, namespace, field)
xml_document.at_xpath("//#{namespace}:#{field}")&.text
def field_value(xml_document, namespace, field, *args)
xml_document.at_xpath("//#{namespace}:#{field}", *args)&.text
end
def meta_field_value(xml_document, field)
field_value(xml_document, "meta", field, { "meta" => "http://data.gov.uk/core/metadata" })
end
def overridden?(xml_document, namespace, field)

10
app/services/imports/lettings_logs_field_import_service.rb

@ -16,8 +16,8 @@ module Imports
private
def update_lettings_allocation(xml_doc)
old_id = field_value(xml_doc, "meta", "document-id")
previous_status = field_value(xml_doc, "meta", "status")
old_id = meta_field_value(xml_doc, "document-id")
previous_status = meta_field_value(xml_doc, "status")
record = LettingsLog.find_by(old_id:)
if record.present? && previous_status.include?("submitted")
@ -46,11 +46,11 @@ module Imports
end
def update_major_repairs(xml_doc)
old_id = field_value(xml_doc, "meta", "document-id")
old_id = meta_field_value(xml_doc, "document-id")
record = LettingsLog.find_by(old_id:)
if record.present?
previous_status = field_value(xml_doc, "meta", "status")
previous_status = meta_field_value(xml_doc, "status")
major_repairs_date = compose_date(xml_doc, "MRCDAY", "MRCMONTH", "MRCYEAR")
major_repairs = if major_repairs_date.present? && previous_status.include?("submitted")
1
@ -69,7 +69,7 @@ module Imports
end
def update_tenant_code(xml_doc)
old_id = field_value(xml_doc, "meta", "document-id")
old_id = meta_field_value(xml_doc, "document-id")
record = LettingsLog.find_by(old_id:)
if record.present?

18
app/services/imports/lettings_logs_import_service.rb

@ -57,7 +57,7 @@ module Imports
def create_log(xml_doc)
attributes = {}
previous_status = field_value(xml_doc, "meta", "status")
previous_status = meta_field_value(xml_doc, "status")
# Required fields for status complete or logic to work
# Note: order matters when we derive from previous values (attributes parameter)
@ -191,9 +191,9 @@ module Imports
# Not specific to our form but required for consistency (present in import)
attributes["old_form_id"] = safe_string_as_integer(xml_doc, "FORM")
attributes["created_at"] = Time.zone.parse(field_value(xml_doc, "meta", "created-date"))
attributes["updated_at"] = Time.zone.parse(field_value(xml_doc, "meta", "modified-date"))
attributes["old_id"] = field_value(xml_doc, "meta", "document-id")
attributes["created_at"] = Time.zone.parse(meta_field_value(xml_doc, "created-date"))
attributes["updated_at"] = Time.zone.parse(meta_field_value(xml_doc, "modified-date"))
attributes["old_id"] = meta_field_value(xml_doc, "document-id")
# Required for our form invalidated questions (not present in import)
attributes["previous_la_known"] = attributes["prevloc"].nil? ? 0 : 1
@ -205,8 +205,8 @@ module Imports
# Supported Housing fields
if attributes["needstype"] == GN_SH[:supported_housing]
location_old_visible_id = safe_string_as_integer(xml_doc, "_1cschemecode")
scheme_old_visible_id = safe_string_as_integer(xml_doc, "_1cmangroupcode")
location_old_visible_id = string_or_nil(xml_doc, "_1cschemecode")
scheme_old_visible_id = string_or_nil(xml_doc, "_1cmangroupcode")
schemes = Scheme.where(old_visible_id: scheme_old_visible_id, owning_organisation_id: attributes["owning_organisation_id"])
location = Location.find_by(old_visible_id: location_old_visible_id, scheme: schemes)
@ -236,7 +236,7 @@ module Imports
attributes["net_income_value_check"] = 0
# Sets the log creator
owner_id = field_value(xml_doc, "meta", "owner-user-id").strip
owner_id = meta_field_value(xml_doc, "owner-user-id").strip
if owner_id.present?
user = LegacyUser.find_by(old_user_id: owner_id)&.user
@logger.warn "Missing user! We expected to find a legacy user with old_user_id #{owner_id}" unless user
@ -355,7 +355,7 @@ module Imports
end
def get_form_name_component(xml_doc, index)
form_name = field_value(xml_doc, "meta", "form-name")
form_name = meta_field_value(xml_doc, "form-name")
form_type_components = form_name.split("-")
form_type_components[index]
end
@ -399,7 +399,7 @@ module Imports
end
def find_organisation_id(xml_doc, id_field)
old_visible_id = unsafe_string_as_integer(xml_doc, id_field)
old_visible_id = string_or_nil(xml_doc, id_field)
organisation = Organisation.find_by(old_visible_id:)
raise "Organisation not found with legacy ID #{old_visible_id}" if organisation.nil?

34
app/services/imports/scheme_location_import_service.rb

@ -91,31 +91,25 @@ module Imports
attributes["units"] = safe_string_as_integer(xml_doc, "total-units")
attributes["type_of_unit"] = safe_string_as_integer(xml_doc, "unit-type")
attributes["location_old_id"] = string_or_nil(xml_doc, "id")
attributes["location_old_visible_id"] = safe_string_as_integer(xml_doc, "visible-id")
attributes["location_old_visible_id"] = string_or_nil(xml_doc, "visible-id")
attributes["scheme_old_id"] = string_or_nil(xml_doc, "mgmtgroup")
attributes
end
def add_location(scheme, attributes)
if attributes["end_date"].nil? || attributes["end_date"] >= Time.zone.now
begin
Location.create!(
name: attributes["location_name"],
postcode: attributes["postcode"],
mobility_type: attributes["mobility_type"],
units: attributes["units"],
type_of_unit: attributes["type_of_unit"],
old_visible_id: attributes["location_old_visible_id"],
old_id: attributes["location_old_id"],
startdate: attributes["start_date"],
scheme:,
)
rescue ActiveRecord::RecordNotUnique
@logger.warn("Location is already present with legacy ID #{attributes['location_old_id']}, skipping")
end
else
@logger.warn("Location with legacy ID #{attributes['location_old_id']} is expired (#{attributes['end_date']}), skipping")
end
Location.create!(
name: attributes["location_name"],
postcode: attributes["postcode"],
mobility_type: attributes["mobility_type"],
units: attributes["units"],
type_of_unit: attributes["type_of_unit"],
old_visible_id: attributes["location_old_visible_id"],
old_id: attributes["location_old_id"],
startdate: attributes["start_date"],
scheme:,
)
rescue ActiveRecord::RecordNotUnique
@logger.warn("Location is already present with legacy ID #{attributes['location_old_id']}, skipping")
end
def find_scheme_to_merge(attributes)

2
app/services/imports/user_import_service.rb

@ -45,7 +45,7 @@ module Imports
end
def user_field_value(xml_document, field)
field_value(xml_document, "user", field)
field_value(xml_document, "user", field, { "user" => "dclg:user" })
end
def role(field_value)

2
app/views/form/_radio_question.html.erb

@ -5,7 +5,7 @@
legend: legend(question, page_header, conditional),
hint: { text: question.hint_text&.html_safe } do %>
<% question.displayed_answer_options(@log).map do |key, options| %>
<% question.displayed_answer_options(@log, current_user).map do |key, options| %>
<% if key.starts_with?("divider") %>
<%= f.govuk_radio_divider %>
<% else %>

2
app/views/form/_select_question.html.erb

@ -1,7 +1,7 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.top_guidance? %>
<% selected = @log.public_send(question.id) || "" %>
<% answers = question.displayed_answer_options(@log).map { |key, value| OpenStruct.new(id: key, name: value.respond_to?(:service_name) ? value.service_name : nil, resource: value) } %>
<% answers = question.displayed_answer_options(@log, current_user).map { |key, value| OpenStruct.new(id: key, name: value.respond_to?(:service_name) ? value.service_name : nil, resource: value) } %>
<%= f.govuk_select(question.id.to_sym,
label: legend(question, page_header, conditional),
"data-controller": "accessible-autocomplete",

17
app/views/locations/deactivate_confirm.html.erb

@ -0,0 +1,17 @@
<%= form_with model: @location, url: scheme_location_deactivate_path(@location), 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"><%= @location.postcode %></span>
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 %>
<%= 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, @location), html: { method: :get }, secondary: true %>
</div>
<% end %>

2
app/views/locations/edit.html.erb

@ -7,7 +7,7 @@
) %>
<% end %>
<%= form_for(@location, method: :patch, url: location_path(@location)) do |f| %>
<%= form_for(@location, method: :patch, url: scheme_location_path(scheme_id: @scheme.id, id: @location.id)) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= f.govuk_error_summary %>

2
app/views/locations/edit_local_authority.html.erb

@ -5,7 +5,7 @@
) %>
<% end %>
<%= form_for(@location, method: :patch, url: location_path(location_id: @location.id, add_another_location: params[:add_another_location])) do |f| %>
<%= form_for(@location, method: :patch, url: scheme_location_path(scheme_id: @scheme.id, id: @location.id, add_another_location: params[:add_another_location])) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= f.govuk_error_summary %>

4
app/views/locations/edit_name.html.erb

@ -3,11 +3,11 @@
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: "/schemes/#{@scheme.id}/locations",
href: scheme_location_path(@scheme, @location),
) %>
<% end %>
<%= form_for(@location, method: :patch, url: location_path(location_id: @location.id)) do |f| %>
<%= form_for(@location, method: :patch, url: scheme_location_path(scheme_id: @scheme.id, id: @location.id)) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= f.govuk_error_summary %>

4
app/views/locations/index.html.erb

@ -52,7 +52,7 @@
<%= table.body do |body| %>
<%= body.row do |row| %>
<% row.cell(text: location.id) %>
<% row.cell(text: simple_format(location_cell_postcode(location, "/schemes/#{@scheme.id}/locations/#{location.id}/edit-name"), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
<% row.cell(text: simple_format(location_cell_postcode(location, "/schemes/#{@scheme.id}/locations/#{location.id}"), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
<% row.cell(text: location.units) %>
<% row.cell(text: simple_format("<span>#{location.type_of_unit}</span>")) %>
<% row.cell(text: location.mobility_type) %>
@ -62,6 +62,6 @@
<% end %>
<% end %>
<% end %>
<%= govuk_button_link_to "Add a location", new_location_path(id: @scheme.id), secondary: true %>
<%= govuk_button_link_to "Add a location", new_scheme_location_path(scheme_id: @scheme.id), secondary: true %>
<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "locations" } %>

2
app/views/locations/new.html.erb

@ -7,7 +7,7 @@
) %>
<% end %>
<%= form_for(@location, method: :post, url: locations_path) do |f| %>
<%= form_for(@location, method: :post, url: scheme_locations_path) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= f.govuk_error_summary %>

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

@ -0,0 +1,32 @@
<% title = @location.name %>
<% content_for :title, title %>
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: scheme_locations_path(@scheme),
) %>
<% end %>
<%= render partial: "organisations/headings", locals: { main: @location.postcode, sub: @location.name } %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<%= govuk_summary_list do |summary_list| %>
<% 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) } %>
<% row.action(text: "Change", href: scheme_location_edit_name_path(scheme_id: @scheme.id, location_id: @location.id)) if attr[:edit] %>
<% end %>
<% end %>
<% end %>
</div>
</div>
<% if FeatureToggle.location_toggle_enabled? %>
<% 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 %>
<% end %>

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

@ -0,0 +1,39 @@
<% title = "#{action.humanize} #{@location.postcode}" %>
<% content_for :title, title %>
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: scheme_location_path(@location.scheme, @location),
) %>
<% end %>
<%= 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 %>
<%= f.govuk_error_summary %>
<%= f.govuk_radio_buttons_fieldset :deactivation_date_type,
legend: { text: I18n.t("questions.location.deactivation.apply_from") },
caption: { text: "Deactivate #{@location.postcode}" },
hint: { text: I18n.t("hints.location.deactivation", date: collection_start_date.to_formatted_s(:govuk_date)) } do %>
<%= govuk_warning_text text: I18n.t("warnings.location.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] }, "location") 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 %>

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 } %>

2
app/views/schemes/check_answers.html.erb

@ -78,7 +78,7 @@
<% end %>
<% end %>
<% end %>
<%= govuk_button_link_to "Add a location", new_location_path(id: @scheme.id), secondary: true %>
<%= govuk_button_link_to "Add a location", new_scheme_location_path(scheme_id: @scheme.id), secondary: true %>
<% end %>
<% end %>
<%= f.hidden_field :page, value: "check-answers" %>

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 %>

2
config/database.yml

@ -66,7 +66,7 @@ staging:
# Do not set this db to the same as development or production.
test:
<<: *default
database: data_collector_test
database: data_collector_test<%= ENV['TEST_ENV_NUMBER'] %>
# As with config/credentials.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is

1
config/environments/test.rb

@ -1,4 +1,5 @@
require "active_support/core_ext/integer/time"
require "faker"
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that

12
config/forms/2021_2022.json

@ -10,7 +10,7 @@
"label": "Property information",
"depends_on": [
{
"setup": "completed"
"non_location_setup_questions_completed?": true
}
],
"pages": {
@ -952,7 +952,7 @@
"label": "Tenancy information",
"depends_on": [
{
"setup": "completed"
"non_location_setup_questions_completed?": true
}
],
"pages": {
@ -1133,7 +1133,7 @@
"label": "Household characteristics",
"depends_on": [
{
"setup": "completed"
"non_location_setup_questions_completed?": true
}
],
"pages": {
@ -5671,7 +5671,7 @@
"label": "Household needs",
"depends_on": [
{
"setup": "completed"
"non_location_setup_questions_completed?": true
}
],
"pages": {
@ -6060,7 +6060,7 @@
"label": "Household situation",
"depends_on": [
{
"setup": "completed"
"non_location_setup_questions_completed?": true
}
],
"pages": {
@ -7302,7 +7302,7 @@
"label": "Income, benefits and outgoings",
"depends_on": [
{
"setup": "completed"
"non_location_setup_questions_completed?": true
}
],
"pages": {

20
config/forms/2022_2023.json

@ -10,7 +10,7 @@
"label": "Property information",
"depends_on": [
{
"setup": "completed"
"non_location_setup_questions_completed?": true
}
],
"pages": {
@ -952,7 +952,7 @@
"label": "Tenancy information",
"depends_on": [
{
"setup": "completed"
"non_location_setup_questions_completed?": true
}
],
"pages": {
@ -1168,7 +1168,7 @@
"label": "Household characteristics",
"depends_on": [
{
"setup": "completed"
"non_location_setup_questions_completed?": true
}
],
"pages": {
@ -5670,7 +5670,7 @@
"label": "Household needs",
"depends_on": [
{
"setup": "completed"
"non_location_setup_questions_completed?": true
}
],
"pages": {
@ -6062,7 +6062,7 @@
"label": "Household situation",
"depends_on": [
{
"setup": "completed"
"non_location_setup_questions_completed?": true
}
],
"pages": {
@ -7070,7 +7070,7 @@
},
"depends_on": [
{
"managing_organisation.provider_type": "LA",
"managing_organisation_provider_type": "LA",
"needstype": 1,
"renewal": 0
}
@ -7127,7 +7127,7 @@
},
"depends_on": [
{
"managing_organisation.provider_type": "PRP",
"managing_organisation_provider_type": "PRP",
"needstype": 1,
"renewal": 0
}
@ -7184,7 +7184,7 @@
},
"depends_on": [
{
"managing_organisation.provider_type": "LA",
"managing_organisation_provider_type": "LA",
"needstype": 2,
"renewal": 0
}
@ -7244,7 +7244,7 @@
},
"depends_on": [
{
"managing_organisation.provider_type": "PRP",
"managing_organisation_provider_type": "PRP",
"needstype": 2,
"renewal": 0
}
@ -7261,7 +7261,7 @@
"label": "Income, benefits and outgoings",
"depends_on": [
{
"setup": "completed"
"non_location_setup_questions_completed?": true
}
],
"pages": {

18
config/initializers/feature_toggle.rb

@ -4,14 +4,22 @@ 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
def self.scheme_toggle_enabled?
!Rails.env.production?
end
def self.location_toggle_enabled?
!Rails.env.production?
end
false
def self.managing_for_other_user_enabled?
!Rails.env.production?
end
end

35
config/locales/en.yml

@ -141,6 +141,7 @@ en:
previous_let_social: "Property cannot have a previous let type if being let as social housing for the first time"
non_temp_accommodation: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as this accommodation is not temporary"
referral_invalid: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as a different source of referral for this letting"
not_a_renewal: "Reason for vacancy cannot be 'Renewal of fixed-term tenancy' if letting is not a renewal"
unittype_gn:
one_bedroom_bedsit: "A bedsit can only have one bedroom"
one_seven_bedroom_shared: "A shared house must have 1 to 7 bedrooms"
@ -301,8 +302,8 @@ en:
tenancy:
length:
fixed_term_not_required: "You must only answer the length of the tenancy if it's fixed-term"
shorthold: "Enter a tenancy length between 2 and 99 years for a Fixed Term – Assured Shorthold Tenancy (AST)"
secure: "Enter a tenancy length between 2 and 99 years (or don't specify the length) for a Secure (including flexible) tenancy"
shorthold: "Enter a tenancy length between %{min_tenancy_length} and 99 years for a tenancy of this type"
secure: "Enter a tenancy length between %{min_tenancy_length} and 99 years (or don't specify the length) for a tenancy of this type"
internal_transfer: "Answer must be secure tenancy as this tenancy is an internal transfer"
cannot_be_internal_transfer: "Answer cannot be internal transfer as this is not a secure tenancy"
not_joint: "This cannot be a joint tenancy as you've told us there's only one person in the household"
@ -311,6 +312,18 @@ 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"
invalid: "Enter a valid day, month and year"
out_of_range: "The date must be on or after the %{date}"
soft_validations:
net_income:
title_text: "Net income is outside the expected range based on the lead tenant’s working situation"
@ -361,6 +374,11 @@ en:
startdate: "When did the first property in this location become available under this scheme? (optional)"
add_another_location: "Do you want to add another location?"
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:
@ -373,6 +391,19 @@ en:
postcode: "For example, SW1P 4DF."
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}"

21
config/routes.rb

@ -49,13 +49,20 @@ 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"
member do
resources :locations do
get "edit-name", to: "locations#edit_name"
get "edit", to: "locations#edit"
get "edit-local-authority", to: "locations#edit_local_authority"
end
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 "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/20221109122033_add_deactivation_date_to_locations.rb

@ -0,0 +1,5 @@
class AddDeactivationDateToLocations < ActiveRecord::Migration[7.0]
def change
add_column :locations, :deactivation_date, :datetime
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

13
db/migrate/20221111102656_change_old_visible_id_type.rb

@ -0,0 +1,13 @@
class ChangeOldVisibleIdType < ActiveRecord::Migration[7.0]
def up
change_column :organisations, :old_visible_id, :string
change_column :schemes, :old_visible_id, :string
change_column :locations, :old_visible_id, :string
end
def down
change_column :organisations, :old_visible_id, :integer
change_column :schemes, :old_visible_id, :integer
change_column :locations, :old_visible_id, :integer
end
end

22
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_10_19_082625) do
ActiveRecord::Schema[7.0].define(version: 2022_11_11_102656) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -255,11 +255,12 @@ ActiveRecord::Schema[7.0].define(version: 2022_10_19_082625) do
t.integer "units"
t.integer "type_of_unit"
t.string "old_id"
t.integer "old_visible_id"
t.string "old_visible_id"
t.string "mobility_type"
t.datetime "startdate", precision: nil
t.string "location_admin_district"
t.boolean "confirmed"
t.datetime "deactivation_date"
t.index ["old_id"], name: "index_locations_on_old_id", unique: true
t.index ["scheme_id"], name: "index_locations_on_scheme_id"
end
@ -315,7 +316,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_10_19_082625) do
t.integer "supported_housing_units"
t.integer "unspecified_units"
t.string "old_org_id"
t.integer "old_visible_id"
t.string "old_visible_id"
t.index ["old_visible_id"], name: "index_organisations_on_old_visible_id", unique: true
end
@ -359,12 +360,6 @@ ActiveRecord::Schema[7.0].define(version: 2022_10_19_082625) do
t.integer "hholdcount"
t.integer "age3"
t.integer "age3_known"
t.integer "age4"
t.integer "age4_known"
t.integer "age5"
t.integer "age5_known"
t.integer "age6"
t.integer "age6_known"
t.string "la"
t.integer "la_known"
t.integer "income1"
@ -372,6 +367,12 @@ ActiveRecord::Schema[7.0].define(version: 2022_10_19_082625) do
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.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"
@ -394,9 +395,10 @@ ActiveRecord::Schema[7.0].define(version: 2022_10_19_082625) do
t.bigint "managing_organisation_id"
t.string "arrangement_type"
t.string "old_id"
t.integer "old_visible_id"
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

42
db/seeds.rb

@ -8,8 +8,8 @@
# rubocop:disable Rails/Output
unless Rails.env.test?
housing_provider = Organisation.find_or_create_by!(
name: "Housing Provider",
housing_provider1 = Organisation.find_or_create_by!(
name: "Housing Provider 1",
address_line1: "2 Marsham Street",
address_line2: "London",
postcode: "SW1P 4DF",
@ -18,8 +18,28 @@ unless Rails.env.test?
managing_agents_label: "None",
provider_type: "LA",
)
managing_agent = Organisation.find_or_create_by!(
name: "Managing Agent",
housing_provider2 = Organisation.find_or_create_by!(
name: "Housing Provider 2",
address_line1: "2 Marsham Street",
address_line2: "London",
postcode: "SW1P 4DF",
holds_own_stock: true,
other_stock_owners: "None",
managing_agents_label: "None",
provider_type: "LA",
)
managing_agent1 = Organisation.find_or_create_by!(
name: "Managing Agent 1",
address_line1: "2 Marsham Street",
address_line2: "London",
postcode: "SW1P 4DF",
holds_own_stock: true,
other_stock_owners: "None",
managing_agents_label: "None",
provider_type: "LA",
)
managing_agent2 = Organisation.find_or_create_by!(
name: "Managing Agent 2",
address_line1: "2 Marsham Street",
address_line2: "London",
postcode: "SW1P 4DF",
@ -49,11 +69,21 @@ unless Rails.env.test?
OrganisationRelationship.create!(
child_organisation: org,
parent_organisation: housing_provider,
parent_organisation: housing_provider1,
relationship_type: OrganisationRelationship::OWNING,
)
OrganisationRelationship.create!(
child_organisation: org,
parent_organisation: housing_provider2,
relationship_type: OrganisationRelationship::OWNING,
)
OrganisationRelationship.create!(
child_organisation: managing_agent,
child_organisation: managing_agent1,
parent_organisation: org,
relationship_type: OrganisationRelationship::MANAGING,
)
OrganisationRelationship.create!(
child_organisation: managing_agent2,
parent_organisation: org,
relationship_type: OrganisationRelationship::MANAGING,
)

2
docs/form/builder.md

@ -9,7 +9,7 @@ nav_order: 1
The setup this log section is treated slightly differently from the rest of the form. It is more accurately viewed as providing metadata about the form than as being part of the form itself. It also needs to know far more about the application specific context than other parts of the form such as who the current user is, what organisation they’re part of and what role they have etc.
As a result it’s not modelled as part of the config but rather as code. It still uses the same [Form Runner](runner) components though.
As a result it’s not modelled as part of the config but rather as code. It still uses the same [Form Runner](/form/runner) components though.
## Features the Form Config supports

16
docs/testing.md

@ -15,3 +15,19 @@ nav_order: 4
- Feature specs are generally written sparingly as they’re also the slowest, where possible a request spec is preferred as this still tests a large surface area (route, controller, model, view) without the performance impact. They are not suitable for tests that need to run JavaScript or test that a specific set of interaction events that trigger a specific set of requests (with high confidence).
- Test data is created with [FactoryBot](https://github.com/thoughtbot/factory_bot) where ever possible
## Parallel testing
- The RSpec test suite can be ran in parallel in local development for quicker turnaround times
- Setup with the following:
```sh
bundle exec rake parallel:setup
```
- Run with:
```sh
RAILS_ENV=test bundle exec rake parallel:spec
```

2
spec/factories/location.rb

@ -17,7 +17,7 @@ FactoryBot.define do
units { 20 }
mobility_type { "A" }
scheme { FactoryBot.create(:scheme, :export) }
old_visible_id { 111 }
old_visible_id { "111" }
end
end
end

31
spec/features/lettings_log_spec.rb

@ -3,10 +3,10 @@ require "rails_helper"
RSpec.describe "Lettings Log Features" do
context "when searching for specific logs" do
context "when I am signed in and there are logs in the database" do
let(:user) { FactoryBot.create(:user, last_sign_in_at: Time.zone.now) }
let!(:log_to_search) { FactoryBot.create(:lettings_log, owning_organisation: user.organisation) }
let!(:same_organisation_log) { FactoryBot.create(:lettings_log, owning_organisation: user.organisation) }
let!(:another_organisation_log) { FactoryBot.create(:lettings_log) }
let(:user) { create(:user, last_sign_in_at: Time.zone.now) }
let!(:log_to_search) { create(:lettings_log, owning_organisation: user.organisation) }
let!(:same_organisation_log) { create(:lettings_log, owning_organisation: user.organisation) }
let!(:another_organisation_log) { create(:lettings_log) }
before do
visit("/lettings-logs")
@ -58,7 +58,7 @@ RSpec.describe "Lettings Log Features" do
end
context "when the signed is user is a Support user" do
let(:support_user) { FactoryBot.create(:user, :support, last_sign_in_at: Time.zone.now) }
let(:support_user) { create(:user, :support, last_sign_in_at: Time.zone.now) }
let(:devise_notify_mailer) { DeviseNotifyMailer.new }
let(:notify_client) { instance_double(Notifications::Client) }
let(:mfa_template_id) { User::MFA_TEMPLATE_ID }
@ -77,26 +77,29 @@ RSpec.describe "Lettings Log Features" do
click_button("Submit")
end
context "when completing the setup lettings log section" do
context "when completing the setup lettings log section", :aggregate_failure do
it "includes the organisation and created by questions" do
visit("/lettings-logs")
click_button("Create a new lettings log")
click_link("Set up this lettings log")
select(support_user.organisation.name, from: "lettings-log-owning-organisation-id-field")
click_button("Save and continue")
select("#{support_user.organisation.name} (Owning organisation)", from: "lettings-log-managing-organisation-id-field")
click_button("Save and continue")
select(support_user.name, from: "lettings-log-created-by-id-field")
click_button("Save and continue")
log_id = page.current_path.scan(/\d/).join
visit("lettings-logs/#{log_id}/setup/check-answers")
expect(page).to have_content("Owning organisation #{support_user.organisation.name}")
expect(page).to have_content("User #{support_user.name}")
expect(page).to have_content("You have answered 2 of 8 questions")
expect(page).to have_content("Housing provider #{support_user.organisation.name}")
expect(page).to have_content("Managing agent #{support_user.organisation.name}")
expect(page).to have_content("Log owner #{support_user.name}")
expect(page).to have_content("You have answered 3 of 9 questions")
end
end
end
context "when the signed is user is not a Support user" do
let(:support_user) { FactoryBot.create(:user) }
let(:user) { create(:user) }
let(:devise_notify_mailer) { DeviseNotifyMailer.new }
let(:notify_client) { instance_double(Notifications::Client) }
@ -105,8 +108,8 @@ RSpec.describe "Lettings Log Features" do
allow(devise_notify_mailer).to receive(:notify_client).and_return(notify_client)
allow(notify_client).to receive(:send_email).and_return(true)
visit("/account/sign-in")
fill_in("user[email]", with: support_user.email)
fill_in("user[password]", with: support_user.password)
fill_in("user[email]", with: user.email)
fill_in("user[password]", with: user.password)
click_button("Sign in")
end
@ -118,8 +121,8 @@ RSpec.describe "Lettings Log Features" do
log_id = page.current_path.scan(/\d/).join
expect(page).to have_current_path("/lettings-logs/#{log_id}/needs-type")
visit("lettings-logs/#{log_id}/setup/check-answers")
expect(page).not_to have_content("Owning organisation #{support_user.organisation.name}")
expect(page).not_to have_content("User #{support_user.name}")
expect(page).not_to have_content("Owning organisation #{user.organisation.name}")
expect(page).not_to have_content("User #{user.name}")
expect(page).to have_content("You have answered 0 of 6 questions")
end
end

46
spec/features/schemes_spec.rb

@ -692,7 +692,7 @@ RSpec.describe "Schemes scheme Features" do
context "when I click to see individual scheme" do
let(:scheme) { schemes.first }
let!(:location) { FactoryBot.create(:location, scheme:) }
let!(:location) { FactoryBot.create(:location, startdate: Time.zone.local(2022, 4, 4), scheme:) }
before do
click_link(scheme.service_name)
@ -757,11 +757,31 @@ RSpec.describe "Schemes scheme Features" do
click_link(location.postcode)
end
it "shows available fields to edit" do
expect(page).to have_current_path("/schemes/#{scheme.id}/locations/#{location.id}/edit-name")
it "displays details about the selected location" do
expect(page).to have_current_path("/schemes/#{scheme.id}/locations/#{location.id}")
expect(page).to have_content(location.postcode)
expect(page).to have_content(location.location_admin_district)
expect(page).to have_content(location.name)
expect(page).to have_content(location.units)
expect(page).to have_content(location.type_of_unit)
expect(page).to have_content(location.mobility_type)
expect(page).to have_content(location.location_code)
expect(page).to have_content("Available from 4 April 2022")
expect(page).to have_content("Active")
end
it "only allows to edit the location name" do
assert_selector "a", text: "Change", count: 1
click_link("Change")
expect(page).to have_content "Location name for #{location.postcode}"
end
it "allows to deactivate a location" do
click_link("Deactivate this location")
expect(page).to have_current_path("/schemes/#{scheme.id}/locations/#{location.id}/new-deactivation")
end
context "when I press the back button" do
before do
click_link "Back"
@ -775,14 +795,26 @@ RSpec.describe "Schemes scheme Features" do
context "and I change the location name" do
before do
fill_in "location-name-field", with: "NewName"
click_button "Save and continue"
click_link("Change")
end
it "returns to locations check your answers page and shows the new name" do
expect(page).to have_content location.id
fill_in "location-name-field", with: "NewName"
click_button "Save and continue"
expect(page).to have_content location.postcode
expect(page).to have_content "NewName"
expect(page.current_url.split("/").last).to eq("check-answers#locations")
expect(page).to have_current_path("/schemes/#{scheme.id}/locations/#{location.id}")
end
context "when I press the back button" do
before do
click_link "Back"
end
it "I see location details" do
expect(page).to have_content location.name
expect(page).to have_current_path("/schemes/#{scheme.id}/locations/#{location.id}")
end
end
end
end

7
spec/fixtures/forms/2021_2022.json vendored

@ -1099,9 +1099,10 @@
"label": "Declaration",
"depends_on": [
{
"household_characteristics": "completed",
"household_needs": "completed",
"property_information": "completed"
"armedforces": 1
},
{
"armedforces": 3
}
],
"pages": {

2
spec/fixtures/forms/2022_2023.json vendored

@ -47,7 +47,7 @@
},
"depends_on": [
{
"setup": "completed"
"non_location_setup_questions_completed?": true
}
]
}

2
spec/fixtures/imports/logs/0b4a68df-30cc-474a-93c0-a56ce8fdad3b.xml vendored

@ -19,7 +19,7 @@
<FORM>123456</FORM>
<Landlord source-value="2">2 Local Authority</Landlord>
<Group>
<_1cmangroupcode>123</_1cmangroupcode>
<_1cmangroupcode>0123</_1cmangroupcode>
<_1cschemecode>10</_1cschemecode>
<schPC/>
<Q1e>3 No</Q1e>

2
spec/fixtures/imports/mgmtgroups/6d6d7618b58affe2a150a5ef2e9f4765fa6cd05d.xml vendored

@ -5,5 +5,5 @@
<mgmtgroup:agent>456</mgmtgroup:agent>
<mgmtgroup:institution>7c5bd5fb549c09z2c55d9cb90d7ba84927e64618</mgmtgroup:institution>
<mgmtgroup:status>Approved</mgmtgroup:status>
<mgmtgroup:visible-id>123</mgmtgroup:visible-id>
<mgmtgroup:visible-id>0123</mgmtgroup:visible-id>
</mgmtgroup:mgmtgroup>

19
spec/fixtures/imports/user/80d9b73aa1c88b6e5c36ee49be9050b923b4a1bb.xml vendored

@ -0,0 +1,19 @@
<user xmlns="dclg:user">
<id>80d9b73aa1c88b6e5c36ee49be9050b923b4a1bb</id>
<user-name>Jane Doe</user-name>
<institution>7c5bd5fb549c09a2c55d7cb90d7ba84927e64618</institution>
<user-stamp>1158 1158</user-stamp>
<active>true</active>
<user-type>Data Provider</user-type>
<association-site-id />
<e-core-download>false</e-core-download>
<raw-data-download>false</raw-data-download>
<licence>false</licence>
<contact-priority-id>None</contact-priority-id>
<email>jane.doe@gov.uk</email>
<password>REDACTED</password>
<full-name>Jane Doe</full-name>
<telephone-no>01111 000000</telephone-no>
<deleted>false</deleted>
<date-deactivated>2021-09-28Z</date-deactivated>
</user>

27
spec/helpers/locations_helper_spec.rb

@ -46,4 +46,31 @@ RSpec.describe LocationsHelper do
expect(selection_options(%w[example])).to eq([OpenStruct.new(id: "example", name: "Example")])
end
end
describe "display_location_attributes" do
let(:location) { FactoryBot.build(:location, startdate: Time.zone.local(2022, 8, 8)) }
it "returns correct display attributes" do
attributes = [
{ name: "Postcode", value: location.postcode },
{ name: "Local authority", value: location.location_admin_district },
{ name: "Location name", value: location.name, edit: true },
{ name: "Total number of units at this location", value: location.units },
{ name: "Common type of unit", value: location.type_of_unit },
{ name: "Mobility type", value: location.mobility_type },
{ name: "Code", value: location.location_code },
{ name: "Availability", value: "Available from 8 August 2022" },
{ name: "Status", value: :active },
]
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_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
end
end

2
spec/helpers/question_attribute_helper_spec.rb

@ -48,7 +48,7 @@ RSpec.describe QuestionAttributeHelper do
"data-action": "input->numeric-question#calculateFields click->conditional-question#displayConditional",
"data-target": "lettings-log-#{question.result_field.to_s.dasherize}-field",
"data-calculated": question.fields_to_add.to_json,
"data-info": { conditional_questions: question.conditional_for, log_type: "lettings" }.to_json,
"data-info": { conditional_questions: question.conditional_for, type: "lettings-log" }.to_json,
}
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

55
spec/models/form/lettings/pages/created_by_spec.rb

@ -0,0 +1,55 @@
require "rails_helper"
RSpec.describe Form::Lettings::Pages::CreatedBy, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
let(:form) { instance_double(Form) }
let(:lettings_log) { instance_double(LettingsLog) }
describe "#routed_to?" do
context "when nil" do
it "is not shown" do
expect(page.routed_to?(nil, nil)).to eq(false)
end
end
context "when support" do
it "is shown" do
expect(page.routed_to?(nil, create(:user, :support))).to eq(true)
end
end
context "when not support" do
it "is not shown" do
expect(page.routed_to?(nil, create(:user, :data_coordinator))).to eq(false)
end
end
end
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[created_by_id])
end
it "has the correct id" do
expect(page.id).to eq("created_by")
end
it "has the correct header" do
expect(page.header).to eq("")
end
it "has the correct description" do
expect(page.description).to eq("")
end
it "has the correct depends_on" do
expect(page.depends_on).to be nil
end
end

128
spec/models/form/lettings/pages/housing_provider_spec.rb

@ -0,0 +1,128 @@
require "rails_helper"
RSpec.describe Form::Lettings::Pages::HousingProvider, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
let(:form) { instance_double(Form) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[owning_organisation_id])
end
it "has the correct id" do
expect(page.id).to eq("housing_provider")
end
it "has the correct header" do
expect(page.header).to eq("")
end
it "has the correct description" do
expect(page.description).to eq("")
end
it "has the correct depends_on" do
expect(page.depends_on).to be nil
end
describe "#routed_to?" do
let(:log) { create(:lettings_log, owning_organisation_id: nil) }
context "when user nil" do
it "is not shown" do
expect(page.routed_to?(log, nil)).to eq(false)
end
it "does not update owning_organisation_id" do
expect { page.routed_to?(log, nil) }.not_to change(log.reload, :owning_organisation).from(nil)
end
end
context "when support" do
let(:user) { create(:user, :support) }
it "is shown" do
expect(page.routed_to?(log, user)).to eq(true)
end
it "does not update owning_organisation_id" do
expect { page.routed_to?(log, user) }.not_to change(log.reload, :owning_organisation).from(nil)
end
end
context "when not support" do
context "when does not hold own stock" do
let(:user) do
create(:user, :data_coordinator, organisation: create(:organisation, holds_own_stock: false))
end
it "is shown" do
expect(page.routed_to?(log, user)).to eq(true)
end
it "does not update owning_organisation_id" do
expect { page.routed_to?(log, user) }.not_to change(log.reload, :owning_organisation).from(nil)
end
end
context "when holds own stock" do
let(:user) do
create(:user, :data_coordinator, organisation: create(:organisation, holds_own_stock: true))
end
context "with 0 housing_providers" do
it "is not shown" do
expect(page.routed_to?(log, user)).to eq(false)
end
it "does not update owning_organisation_id" do
expect { page.routed_to?(log, user) }.not_to change(log.reload, :owning_organisation).from(nil)
end
end
context "with >1 housing_providers" do
before do
create(:organisation_relationship, :owning, child_organisation: user.organisation)
create(:organisation_relationship, :owning, child_organisation: user.organisation)
end
it "is shown" do
expect(page.routed_to?(log, user)).to eq(true)
end
it "does not update owning_organisation_id" do
expect { page.routed_to?(log, user) }.not_to change(log.reload, :owning_organisation).from(nil)
end
end
context "with 1 housing_providers" do
let(:housing_provider) { create(:organisation) }
before do
create(
:organisation_relationship,
:owning,
child_organisation: user.organisation,
parent_organisation: housing_provider,
)
end
it "is not shown" do
expect(page.routed_to?(log, user)).to eq(false)
end
it "updates owning_organisation_id" do
expect { page.routed_to?(log, user) }.to change(log.reload, :owning_organisation).from(nil).to(housing_provider)
end
end
end
end
end
end

130
spec/models/form/lettings/pages/managing_organisation_spec.rb

@ -0,0 +1,130 @@
require "rails_helper"
RSpec.describe Form::Lettings::Pages::ManagingOrganisation, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
let(:form) { instance_double(Form) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[managing_organisation_id])
end
it "has the correct id" do
expect(page.id).to eq("managing_organisation")
end
it "has the correct header" do
expect(page.header).to eq("")
end
it "has the correct description" do
expect(page.description).to eq("")
end
it "has the correct depends_on" do
expect(page.depends_on).to be nil
end
describe "#routed_to?" do
let(:log) { create(:lettings_log) }
let(:organisation) { create(:organisation) }
context "when user nil" do
it "is not shown" do
expect(page.routed_to?(log, nil)).to eq(false)
end
it "does not update managing_organisation_id" do
expect { page.routed_to?(log, nil) }.not_to change(log.reload, :managing_organisation)
end
end
context "when support" do
let(:organisation) { create(:organisation) }
let(:user) { create(:user, :support, organisation:) }
it "is shown" do
expect(page.routed_to?(log, user)).to eq(true)
end
it "does not update managing_organisation_id" do
expect { page.routed_to?(log, user) }.not_to change(log.reload, :managing_organisation)
end
end
context "when not support" do
context "when does not hold own stock" do
let(:user) do
create(:user, :data_coordinator, organisation: create(:organisation, holds_own_stock: false))
end
it "is shown" do
expect(page.routed_to?(log, user)).to eq(true)
end
it "does not update managing_organisation_id" do
expect { page.routed_to?(log, user) }.not_to change(log.reload, :managing_organisation)
end
end
context "when holds own stock" do
let(:user) do
create(:user, :data_coordinator, organisation: create(:organisation, holds_own_stock: true))
end
context "with 0 managing_agents" do
it "is not shown" do
expect(page.routed_to?(log, user)).to eq(false)
end
it "does not update managing_organisation_id" do
expect { page.routed_to?(log, user) }.not_to change(log.reload, :managing_organisation)
end
end
context "with >1 managing_agents" do
before do
create(:organisation_relationship, :managing, parent_organisation: user.organisation)
create(:organisation_relationship, :managing, parent_organisation: user.organisation)
end
it "is shown" do
expect(page.routed_to?(log, user)).to eq(true)
end
it "does not update managing_organisation_id" do
expect { page.routed_to?(log, user) }.not_to change(log.reload, :managing_organisation)
end
end
context "with 1 managing_agents" do
let(:managing_agent) { create(:organisation) }
before do
create(
:organisation_relationship,
:managing,
child_organisation: managing_agent,
parent_organisation: user.organisation,
)
end
it "is not shown" do
expect(page.routed_to?(log, user)).to eq(false)
end
it "updates managing_organisation_id" do
expect { page.routed_to?(log, user) }.to change(log.reload, :managing_organisation).to(managing_agent)
end
end
end
end
end
end

82
spec/models/form/lettings/questions/created_by_id_spec.rb

@ -0,0 +1,82 @@
require "rails_helper"
RSpec.describe Form::Lettings::Questions::CreatedById, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
let(:subsection) { instance_double(Form::Subsection) }
let(:form) { instance_double(Form) }
let(:user_1) { FactoryBot.create(:user, name: "first user") }
let(:user_2) { FactoryBot.create(:user, name: "second user") }
let!(:expected_answer_options) do
{
"" => "Select an option",
user_1.id => "#{user_1.name} (#{user_1.email})",
user_2.id => "#{user_2.name} (#{user_2.email})",
}
end
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("created_by_id")
end
it "has the correct header" do
expect(question.header).to eq("Which user are you creating this log for?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Log owner")
end
it "has the correct type" do
expect(question.type).to eq("select")
end
it "has the correct hint_text" do
expect(question.hint_text).to eq("")
end
it "has the correct answer options" do
expect(question.answer_options).to eq(expected_answer_options)
end
it "is marked as derived" do
expect(question.derived?).to be true
end
context "when the current user is support" do
let(:support_user) { FactoryBot.build(:user, :support) }
it "is shown in check answers" do
expect(question.hidden_in_check_answers?(nil, support_user)).to be false
end
end
context "when the current user is not support" do
let(:user) { FactoryBot.build(:user) }
it "is not shown in check answers" do
expect(question.hidden_in_check_answers?(nil, user)).to be true
end
end
context "when the owning organisation is already set" do
let(:lettings_log) { FactoryBot.create(:lettings_log, owning_organisation: user_2.organisation) }
let(:expected_answer_options) do
{
"" => "Select an option",
user_2.id => "#{user_2.name} (#{user_2.email})",
}
end
it "only displays users that belong to that organisation" do
expect(question.displayed_answer_options(lettings_log)).to eq(expected_answer_options)
end
end
end

117
spec/models/form/lettings/questions/housing_provider_spec.rb

@ -0,0 +1,117 @@
require "rails_helper"
RSpec.describe Form::Lettings::Questions::HousingProvider, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
let(:subsection) { instance_double(Form::Subsection) }
let(:form) { instance_double(Form) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("owning_organisation_id")
end
it "has the correct header" do
expect(question.header).to eq("Which organisation owns this property?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Housing provider")
end
it "has the correct type" do
expect(question.type).to eq("select")
end
it "has the correct hint_text" do
expect(question.hint_text).to be_nil
end
describe "answer options" do
let(:options) { { "" => "Select an option" } }
context "when current_user nil" do
it "shows default options" do
expect(question.answer_options).to eq(options)
end
end
context "when user not support and owns own stock" do
let(:user) { create(:user, :data_coordinator, organisation: create(:organisation, holds_own_stock: true)) }
let(:options) do
{
"" => "Select an option",
user.organisation.id => "#{user.organisation.name} (Your organisation)",
}
end
before do
question.current_user = user
end
it "shows housing providers with own org at the top" do
expect(question.answer_options).to eq(options)
end
end
context "when user support" do
before do
question.current_user = create(:user, :support)
end
let(:expected_opts) do
Organisation.all.each_with_object(options) do |organisation, hsh|
hsh[organisation.id] = organisation.name
hsh
end
end
it "shows all orgs" do
expect(question.answer_options).to eq(expected_opts)
end
end
end
it "is marked as derived" do
expect(question.derived?).to be true
end
describe "#hidden_in_check_answers?" do
context "when housing providers < 2" do
context "when not support user" do
let(:user) { create(:user) }
it "is hidden in check answers" do
expect(question.hidden_in_check_answers?(nil, user)).to be true
end
end
context "when support" do
let(:user) { create(:user, :support) }
it "is not hiddes in check answers" do
expect(question.hidden_in_check_answers?(nil, user)).to be false
end
end
end
context "when housing providers >= 2" do
let(:user) { create(:user) }
before do
create(:organisation_relationship, :owning, child_organisation: user.organisation)
create(:organisation_relationship, :owning, child_organisation: user.organisation)
end
it "is not hidden in check answers" do
expect(question.hidden_in_check_answers?(nil, user)).to be false
end
end
end
end

155
spec/models/form/lettings/questions/managing_organisation_spec.rb

@ -0,0 +1,155 @@
require "rails_helper"
RSpec.describe Form::Lettings::Questions::ManagingOrganisation, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
let(:subsection) { instance_double(Form::Subsection) }
let(:form) { instance_double(Form) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("managing_organisation_id")
end
it "has the correct header" do
expect(question.header).to eq("Which organisation manages this letting?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Managing agent")
end
it "has the correct type" do
expect(question.type).to eq("select")
end
it "has the correct hint_text" do
expect(question.hint_text).to be_nil
end
describe "#displayed_answer_options" do
let(:options) { { "" => "Select an option" } }
context "when current_user nil" do
let(:log) { create(:lettings_log) }
it "shows default options" do
expect(question.displayed_answer_options(log, nil)).to eq(options)
end
end
context "when log nil" do
let(:user) { create(:user) }
it "shows default options" do
expect(question.displayed_answer_options(nil, user)).to eq(options)
end
end
context "when user not support and owns own stock" do
let(:user) { create(:user, :data_coordinator, organisation: create(:organisation, holds_own_stock: true)) }
let(:log) { create(:lettings_log) }
let!(:org_rel1) { create(:organisation_relationship, :managing, parent_organisation: user.organisation) }
let!(:org_rel2) { create(:organisation_relationship, :managing, parent_organisation: user.organisation) }
let(:options) do
{
"" => "Select an option",
user.organisation.id => "#{user.organisation.name} (Your organisation)",
org_rel1.child_organisation.id => org_rel1.child_organisation.name,
org_rel2.child_organisation.id => org_rel2.child_organisation.name,
}
end
it "shows managing agents with own org at the top" do
expect(question.displayed_answer_options(log, user)).to eq(options)
end
end
context "when support user and org does not own own stock" do
let(:user) { create(:user, :support) }
let(:log_owning_org) { create(:organisation, holds_own_stock: false) }
let(:log) { create(:lettings_log, owning_organisation: log_owning_org) }
let!(:org_rel1) { create(:organisation_relationship, :managing, parent_organisation: log_owning_org) }
let!(:org_rel2) { create(:organisation_relationship, :managing, parent_organisation: log_owning_org) }
let(:options) do
{
"" => "Select an option",
org_rel1.child_organisation.id => org_rel1.child_organisation.name,
org_rel2.child_organisation.id => org_rel2.child_organisation.name,
}
end
it "shows owning org managing agents with hint" do
expect(question.displayed_answer_options(log, user)).to eq(options)
end
end
context "when support user and org does own stock" do
let(:user) { create(:user, :support) }
let(:log_owning_org) { create(:organisation, holds_own_stock: true) }
let(:log) { create(:lettings_log, owning_organisation: log_owning_org) }
let!(:org_rel1) { create(:organisation_relationship, :managing, parent_organisation: log_owning_org) }
let!(:org_rel2) { create(:organisation_relationship, :managing, parent_organisation: log_owning_org) }
let(:options) do
{
"" => "Select an option",
log_owning_org.id => "#{log_owning_org.name} (Owning organisation)",
org_rel1.child_organisation.id => org_rel1.child_organisation.name,
org_rel2.child_organisation.id => org_rel2.child_organisation.name,
}
end
it "shows owning org managing agents
" do
expect(question.displayed_answer_options(log, user)).to eq(options)
end
end
end
it "is marked as derived" do
expect(question.derived?).to be true
end
describe "#hidden_in_check_answers?" do
context "when housing providers < 2" do
context "when not support user" do
let(:user) { create(:user) }
it "is hidden in check answers" do
expect(question.hidden_in_check_answers?(nil, user)).to be true
end
end
context "when support" do
let(:user) { create(:user, :support) }
it "is not hiddes in check answers" do
expect(question.hidden_in_check_answers?(nil, user)).to be false
end
end
end
context "when managing agents >= 2" do
let(:user) { create(:user) }
before do
create(:organisation_relationship, :managing, parent_organisation: user.organisation)
create(:organisation_relationship, :managing, parent_organisation: user.organisation)
end
it "is not hidden in check answers" do
expect(question.hidden_in_check_answers?(nil, user)).to be false
end
end
end
end

66
spec/models/form/lettings/subsections/setup_spec.rb

@ -13,16 +13,19 @@ RSpec.describe Form::Lettings::Subsections::Setup, type: :model do
it "has correct pages" do
expect(setup.pages.map(&:id)).to eq(
%w[organisation
created_by
needs_type
scheme
location
renewal
tenancy_start_date
rent_type
tenant_code
property_reference],
%w[
housing_provider
managing_organisation
created_by
needs_type
scheme
location
renewal
tenancy_start_date
rent_type
tenant_code
property_reference
],
)
end
@ -33,4 +36,47 @@ RSpec.describe Form::Lettings::Subsections::Setup, type: :model do
it "has the correct label" do
expect(setup.label).to eq("Set up this lettings log")
end
context "when not production" do
it "has correct pages" do
expect(setup.pages.map(&:id)).to eq(
%w[
housing_provider
managing_organisation
created_by
needs_type
scheme
location
renewal
tenancy_start_date
rent_type
tenant_code
property_reference
],
)
end
end
context "when production" do
before do
allow(Rails.env).to receive(:production?).and_return(true)
end
it "has the correct pages" do
expect(setup.pages.map(&:id)).to eq(
%w[
organisation
created_by
needs_type
scheme
location
renewal
tenancy_start_date
rent_type
tenant_code
property_reference
],
)
end
end
end

2
spec/models/form/sales/subsections/household_characteristics_spec.rb

@ -53,6 +53,6 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
end
it "has correct depends on" do
expect(household_characteristics.depends_on).to eq([{ "setup" => "completed" }])
expect(household_characteristics.depends_on).to eq([{ "setup_completed?" => true }])
end
end

2
spec/models/form/sales/subsections/income_benefits_and_outgoings_spec.rb

@ -28,6 +28,6 @@ RSpec.describe Form::Sales::Subsections::IncomeBenefitsAndOutgoings, type: :mode
end
it "has correct depends on" do
expect(subsection.depends_on).to eq([{ "setup" => "completed" }])
expect(subsection.depends_on).to eq([{ "setup_completed?" => true }])
end
end

12
spec/models/form_handler_spec.rb

@ -27,13 +27,13 @@ RSpec.describe FormHandler do
it "is able to load a current lettings form" do
form = form_handler.get_form("current_lettings")
expect(form).to be_a(Form)
expect(form.pages.count).to eq(45)
expect(form.pages.count).to eq(46)
end
it "is able to load a next lettings form" do
form = form_handler.get_form("next_lettings")
expect(form).to be_a(Form)
expect(form.pages.count).to eq(12)
expect(form.pages.count).to eq(13)
end
end
@ -49,13 +49,13 @@ RSpec.describe FormHandler do
it "is able to load a current lettings form" do
form = form_handler.get_form("current_lettings")
expect(form).to be_a(Form)
expect(form.pages.count).to eq(12)
expect(form.pages.count).to eq(13)
end
it "is able to load a previous lettings form" do
form = form_handler.get_form("previous_lettings")
expect(form).to be_a(Form)
expect(form.pages.count).to eq(45)
expect(form.pages.count).to eq(46)
end
it "is able to load a current sales form" do
@ -118,6 +118,10 @@ RSpec.describe FormHandler do
it "returns the correct next sales form name" do
expect(form_handler.form_name_from_start_year(2023, "sales")).to eq("next_sales")
end
it "returns the correct current start date" do
expect(form_handler.current_collection_start_date).to eq(Time.zone.local(2022, 4, 1))
end
end
context "with the date before 1st of April" do

44
spec/models/location_spec.rb

@ -111,4 +111,48 @@ RSpec.describe Location, type: :model do
end
end
end
describe "status" do
let(:location) { FactoryBot.build(:location) }
before do
Timecop.freeze(2022, 6, 7)
end
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

66
spec/models/validations/financial_validations_spec.rb

@ -18,6 +18,8 @@ RSpec.describe Validations::FinancialValidations do
financial_validator.validate_net_income(record)
expect(record.errors["incfreq"])
.to include(match I18n.t("validations.financial.earnings.freq_missing"))
expect(record.errors["earnings"])
.to include(match I18n.t("validations.financial.earnings.freq_missing"))
end
it "when income frequency is provided it validates that earnings must be provided" do
@ -26,6 +28,8 @@ RSpec.describe Validations::FinancialValidations do
financial_validator.validate_net_income(record)
expect(record.errors["earnings"])
.to include(match I18n.t("validations.financial.earnings.earnings_missing"))
expect(record.errors["incfreq"])
.to include(match I18n.t("validations.financial.earnings.earnings_missing"))
end
end
@ -828,6 +832,11 @@ RSpec.describe Validations::FinancialValidations do
financial_validator.validate_rent_amount(record)
expect(record.errors["brent"])
.to include(match I18n.t("validations.financial.brent.below_hard_min"))
%w[beds la postcode_known scheme_id location_id rent_type needstype period].each do |field|
expect(record.errors[field])
.to include(match I18n.t("validations.financial.brent.#{field}.below_hard_min"))
end
end
it "validates hard max for general needs" do
@ -842,22 +851,11 @@ RSpec.describe Validations::FinancialValidations do
financial_validator.validate_rent_amount(record)
expect(record.errors["brent"])
.to include(match I18n.t("validations.financial.brent.above_hard_max"))
expect(record.errors["beds"])
.to include(match I18n.t("validations.financial.brent.beds.above_hard_max"))
expect(record.errors["la"])
.to include(match I18n.t("validations.financial.brent.la.above_hard_max"))
expect(record.errors["postcode_known"])
.to include(match I18n.t("validations.financial.brent.postcode_known.above_hard_max"))
expect(record.errors["scheme_id"])
.to include(match I18n.t("validations.financial.brent.scheme_id.above_hard_max"))
expect(record.errors["location_id"])
.to include(match I18n.t("validations.financial.brent.location_id.above_hard_max"))
expect(record.errors["rent_type"])
.to include(match I18n.t("validations.financial.brent.rent_type.above_hard_max"))
expect(record.errors["needstype"])
.to include(match I18n.t("validations.financial.brent.needstype.above_hard_max"))
expect(record.errors["period"])
.to include(match I18n.t("validations.financial.brent.period.above_hard_max"))
%w[beds la postcode_known scheme_id location_id rent_type needstype period].each do |field|
expect(record.errors[field])
.to include(match I18n.t("validations.financial.brent.#{field}.above_hard_max"))
end
end
it "validates hard max for supported housing" do
@ -871,22 +869,11 @@ RSpec.describe Validations::FinancialValidations do
financial_validator.validate_rent_amount(record)
expect(record.errors["brent"])
.to include(match I18n.t("validations.financial.brent.above_hard_max"))
expect(record.errors["beds"])
.to include(match I18n.t("validations.financial.brent.beds.above_hard_max"))
expect(record.errors["la"])
.to include(match I18n.t("validations.financial.brent.la.above_hard_max"))
expect(record.errors["postcode_known"])
.to include(match I18n.t("validations.financial.brent.postcode_known.above_hard_max"))
expect(record.errors["scheme_id"])
.to include(match I18n.t("validations.financial.brent.scheme_id.above_hard_max"))
expect(record.errors["location_id"])
.to include(match I18n.t("validations.financial.brent.location_id.above_hard_max"))
expect(record.errors["rent_type"])
.to include(match I18n.t("validations.financial.brent.rent_type.above_hard_max"))
expect(record.errors["needstype"])
.to include(match I18n.t("validations.financial.brent.needstype.above_hard_max"))
expect(record.errors["period"])
.to include(match I18n.t("validations.financial.brent.period.above_hard_max"))
%w[beds la postcode_known scheme_id location_id rent_type needstype period].each do |field|
expect(record.errors[field])
.to include(match I18n.t("validations.financial.brent.#{field}.above_hard_max"))
end
end
it "validates hard max for correct collection year" do
@ -900,14 +887,11 @@ RSpec.describe Validations::FinancialValidations do
financial_validator.validate_rent_amount(record)
expect(record.errors["brent"])
.to include(match I18n.t("validations.financial.brent.above_hard_max"))
expect(record.errors["beds"])
.to include(match I18n.t("validations.financial.brent.beds.above_hard_max"))
expect(record.errors["la"])
.to include(match I18n.t("validations.financial.brent.la.above_hard_max"))
expect(record.errors["rent_type"])
.to include(match I18n.t("validations.financial.brent.rent_type.above_hard_max"))
expect(record.errors["needstype"])
.to include(match I18n.t("validations.financial.brent.needstype.above_hard_max"))
%w[beds la postcode_known scheme_id location_id rent_type needstype period].each do |field|
expect(record.errors[field])
.to include(match I18n.t("validations.financial.brent.#{field}.above_hard_max"))
end
end
it "does not error if some of the fields are missing" do
@ -1010,6 +994,8 @@ RSpec.describe Validations::FinancialValidations do
financial_validator.validate_care_home_charges(record)
expect(record.errors["chcharge"])
.to include(match I18n.t("validations.financial.carehome.not_provided", period: "every 4 weeks"))
expect(record.errors["is_carehome"])
.to include(match I18n.t("validations.financial.carehome.not_provided", period: "every 4 weeks"))
end
end

11
spec/models/validations/property_validations_spec.rb

@ -233,6 +233,17 @@ RSpec.describe Validations::PropertyValidations do
property_validator.validate_rsnvac(record)
expect(record.errors["rsnvac"]).to be_empty
end
context "when the letting is not a renewal" do
it "validates that the reason for vacancy is not renewal" do
record.first_time_property_let_as_social_housing = 0
record.renewal = 0
record.rsnvac = 14
property_validator.validate_rsnvac(record)
expect(record.errors["rsnvac"])
.to include(match I18n.t("validations.property.rsnvac.not_a_renewal"))
end
end
end
context "when the property has been let before" do

48
spec/models/validations/tenancy_validations_spec.rb

@ -4,7 +4,7 @@ RSpec.describe Validations::TenancyValidations do
subject(:tenancy_validator) { validator_class.new }
let(:validator_class) { Class.new { include Validations::TenancyValidations } }
let(:record) { FactoryBot.create(:lettings_log, startdate: Time.zone.local(2021, 5, 1)) }
let(:record) { FactoryBot.create(:lettings_log, startdate: Time.zone.local(2021, 5, 1), needstype: 1, rent_type: 1) }
describe "fixed term tenancy validations" do
context "when fixed term tenancy" do
@ -21,11 +21,16 @@ RSpec.describe Validations::TenancyValidations do
end
context "when type of tenancy is assured shorthold" do
let(:expected_error) { I18n.t("validations.tenancy.length.shorthold") }
let(:expected_error) do
I18n.t(
"validations.tenancy.length.shorthold",
min_tenancy_length: 2,
)
end
before { record.tenancy = 4 }
context "when tenancy length is greater than 1" do
context "when tenancy length is less than 2" do
it "adds an error" do
record.tenancylength = 1
tenancy_validator.validate_fixed_term_tenancy(record)
@ -34,7 +39,7 @@ RSpec.describe Validations::TenancyValidations do
end
end
context "when tenancy length is less than 100" do
context "when tenancy length is greater than 99" do
it "adds an error" do
record.tenancylength = 100
tenancy_validator.validate_fixed_term_tenancy(record)
@ -64,11 +69,16 @@ RSpec.describe Validations::TenancyValidations do
context "when the collection start year is before 2022" do
context "when type of tenancy is secure" do
let(:expected_error) { I18n.t("validations.tenancy.length.secure") }
let(:expected_error) do
I18n.t(
"validations.tenancy.length.secure",
min_tenancy_length: 2,
)
end
before { record.tenancy = 1 }
context "when tenancy length is greater than 1" do
context "when tenancy length is less than 2" do
it "adds an error" do
record.tenancylength = 1
tenancy_validator.validate_fixed_term_tenancy(record)
@ -77,7 +87,7 @@ RSpec.describe Validations::TenancyValidations do
end
end
context "when tenancy length is less than 100" do
context "when tenancy length is greater than 99" do
it "adds an error" do
record.tenancylength = 100
tenancy_validator.validate_fixed_term_tenancy(record)
@ -107,14 +117,19 @@ RSpec.describe Validations::TenancyValidations do
end
context "when the collection start year is 2022 or later" do
let(:record) { FactoryBot.create(:lettings_log, startdate: Time.zone.local(2022, 5, 1)) }
let(:record) { FactoryBot.create(:lettings_log, startdate: Time.zone.local(2022, 5, 1), needstype: 1, rent_type: 1) }
context "when type of tenancy is Secure - fixed term" do
let(:expected_error) { I18n.t("validations.tenancy.length.secure") }
let(:expected_error) do
I18n.t(
"validations.tenancy.length.secure",
min_tenancy_length: 2,
)
end
before { record.tenancy = 6 }
context "when tenancy length is greater than 1" do
context "when tenancy length is less than 2" do
it "adds an error" do
record.tenancylength = 1
tenancy_validator.validate_fixed_term_tenancy(record)
@ -123,7 +138,7 @@ RSpec.describe Validations::TenancyValidations do
end
end
context "when tenancy length is less than 100" do
context "when tenancy length is greater than 99" do
it "adds an error" do
record.tenancylength = 100
tenancy_validator.validate_fixed_term_tenancy(record)
@ -152,11 +167,16 @@ RSpec.describe Validations::TenancyValidations do
end
context "when type of tenancy is Secure - lifetime" do
let(:expected_error) { I18n.t("validations.tenancy.length.secure") }
let(:expected_error) do
I18n.t(
"validations.tenancy.length.secure",
min_tenancy_length: 2,
)
end
before { record.tenancy = 7 }
context "when tenancy length is greater than 1" do
context "when tenancy length is less than 2" do
it "adds an error" do
record.tenancylength = 1
tenancy_validator.validate_fixed_term_tenancy(record)
@ -165,7 +185,7 @@ RSpec.describe Validations::TenancyValidations do
end
end
context "when tenancy length is less than 100" do
context "when tenancy length is greater than 99" do
it "adds an error" do
record.tenancylength = 100
tenancy_validator.validate_fixed_term_tenancy(record)

40
spec/requests/form_controller_spec.rb

@ -2,18 +2,18 @@ require "rails_helper"
RSpec.describe FormController, type: :request do
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:user) { FactoryBot.create(:user) }
let(:user) { create(:user) }
let(:organisation) { user.organisation }
let(:other_organisation) { FactoryBot.create(:organisation) }
let(:other_organisation) { create(:organisation) }
let!(:unauthorized_lettings_log) do
FactoryBot.create(
create(
:lettings_log,
owning_organisation: other_organisation,
managing_organisation: other_organisation,
)
end
let(:setup_complete_lettings_log) do
FactoryBot.create(
create(
:lettings_log,
:about_completed,
status: 1,
@ -23,7 +23,7 @@ RSpec.describe FormController, type: :request do
)
end
let(:completed_lettings_log) do
FactoryBot.create(
create(
:lettings_log,
:completed,
owning_organisation: organisation,
@ -39,7 +39,7 @@ RSpec.describe FormController, type: :request do
context "when a user is not signed in" do
let!(:lettings_log) do
FactoryBot.create(
create(
:lettings_log,
owning_organisation: organisation,
managing_organisation: organisation,
@ -68,7 +68,7 @@ RSpec.describe FormController, type: :request do
context "when a user is signed in" do
let!(:lettings_log) do
FactoryBot.create(
create(
:lettings_log,
owning_organisation: organisation,
managing_organisation: organisation,
@ -83,8 +83,8 @@ RSpec.describe FormController, type: :request do
describe "GET" do
context "with form pages" do
context "when forms exist for multiple years" do
let(:lettings_log_year_1) { FactoryBot.create(:lettings_log, startdate: Time.zone.local(2021, 5, 1), owning_organisation: organisation, created_by: user) }
let(:lettings_log_year_2) { FactoryBot.create(:lettings_log, :about_completed, startdate: Time.zone.local(2022, 5, 1), owning_organisation: organisation, created_by: user) }
let(:lettings_log_year_1) { create(:lettings_log, startdate: Time.zone.local(2021, 5, 1), owning_organisation: organisation, created_by: user) }
let(:lettings_log_year_2) { create(:lettings_log, :about_completed, startdate: Time.zone.local(2022, 5, 1), owning_organisation: organisation, created_by: user) }
it "displays the correct question details for each lettings log based on form year" do
get "/lettings-logs/#{lettings_log_year_1.id}/tenant-code-test", headers: headers, params: {}
@ -110,11 +110,11 @@ RSpec.describe FormController, type: :request do
context "when viewing the setup section schemes page" do
context "when the user is support" do
let(:user) { FactoryBot.create(:user, :support) }
let(:user) { create(:user, :support) }
context "when organisation and user have not been selected yet" do
let(:lettings_log) do
FactoryBot.create(
create(
:lettings_log,
owning_organisation: nil,
managing_organisation: nil,
@ -124,7 +124,7 @@ RSpec.describe FormController, type: :request do
end
before do
locations = FactoryBot.create_list(:location, 5)
locations = create_list(:location, 5)
locations.each { |location| location.scheme.update!(arrangement_type: "The same organisation that owns the housing stock", managing_organisation_id: location.scheme.owning_organisation_id) }
end
@ -147,7 +147,7 @@ RSpec.describe FormController, type: :request do
context "when no other sections are enabled" do
let(:lettings_log_2022) do
FactoryBot.create(
create(
:lettings_log,
startdate: Time.zone.local(2022, 12, 1),
owning_organisation: organisation,
@ -194,17 +194,17 @@ RSpec.describe FormController, type: :request do
context "when viewing a user dependent page" do
context "when the dependency is met" do
let(:user) { FactoryBot.create(:user, :support) }
let(:user) { create(:user, :support) }
it "routes to the page" do
get "/lettings-logs/#{lettings_log.id}/organisation"
get "/lettings-logs/#{lettings_log.id}/created-by"
expect(response).to have_http_status(:ok)
end
end
context "when the dependency is not met" do
it "redirects to the tasklist page" do
get "/lettings-logs/#{lettings_log.id}/organisation"
get "/lettings-logs/#{lettings_log.id}/created-by"
expect(response).to redirect_to("/lettings-logs/#{lettings_log.id}")
end
end
@ -213,10 +213,10 @@ RSpec.describe FormController, type: :request do
describe "Submit Form" do
context "with a form page" do
let(:user) { FactoryBot.create(:user) }
let(:user) { create(:user) }
let(:organisation) { user.organisation }
let(:lettings_log) do
FactoryBot.create(
create(
:lettings_log,
owning_organisation: organisation,
managing_organisation: organisation,
@ -527,9 +527,9 @@ RSpec.describe FormController, type: :request do
context "with lettings logs that are not owned or managed by your organisation" do
let(:answer) { 25 }
let(:other_organisation) { FactoryBot.create(:organisation) }
let(:other_organisation) { create(:organisation) }
let(:unauthorized_lettings_log) do
FactoryBot.create(
create(
:lettings_log,
owning_organisation: other_organisation,
managing_organisation: other_organisation,

201
spec/requests/locations_controller_spec.rb

@ -597,7 +597,7 @@ RSpec.describe LocationsController, type: :request do
it "updates existing location for scheme with valid params and redirects to correct page" do
follow_redirect!
expect(response).to have_http_status(:ok)
expect(page).to have_content("Locations")
expect(page).to have_content("Test")
end
it "updates existing location for scheme with valid params" do
@ -736,7 +736,7 @@ RSpec.describe LocationsController, type: :request do
it "updates existing location for scheme with valid params and redirects to correct page" do
follow_redirect!
expect(response).to have_http_status(:ok)
expect(page).to have_content("Locations")
expect(page).to have_content("Test")
end
it "updates existing location for scheme with valid params" do
@ -1212,4 +1212,201 @@ RSpec.describe LocationsController, 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/locations/1/deactivate"
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/locations/1/deactivate"
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!(:location) { FactoryBot.create(:location, scheme:) }
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}/locations/#{location.id}/new-deactivation", params:
end
context "with default date" do
let(:params) { { location: { 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 #{location.lettings_logs.count} logs")
end
end
context "with other date" do
let(:params) { { location: { 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 #{location.lettings_logs.count} logs")
end
end
context "when confirming deactivation" do
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!
expect(response).to have_http_status(:ok)
expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success")
location.reload
expect(location.deactivation_date).to eq(deactivation_date)
end
end
context "when the date is not selected" do
let(:params) { { location: { "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.location.deactivation_date.not_selected"))
end
end
context "when invalid date is entered" do
let(:params) { { location: { 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.location.deactivation_date.invalid"))
end
end
context "when the date is entered is before the beginning of current collection window" do
let(:params) { { location: { 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.location.deactivation_date.out_of_range", date: "1 April 2022"))
end
end
context "when the day is not entered" do
let(:params) { { location: { 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.location.deactivation_date.invalid"))
end
end
context "when the month is not entered" do
let(:params) { { location: { 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.location.deactivation_date.invalid"))
end
end
context "when the year is not entered" do
let(:params) { { location: { 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.location.deactivation_date.invalid"))
end
end
end
end
describe "#show" do
context "when not signed in" do
it "redirects to the sign in page" do
get "/schemes/1/locations/1"
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
get "/schemes/1/locations/1"
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!(:location) { FactoryBot.create(:location, scheme:) }
before 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}/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)
expect(page).to have_link("Reactivate this location", href: "/schemes/#{scheme.id}/locations/#{location.id}/reactivate")
end
end
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)
expect(page).to have_link("Reactivate this location", href: "/schemes/#{scheme.id}/locations/#{location.id}/reactivate")
end
end
end
end
end

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save