Browse Source

Merge branch 'main' into CLDC-1361-only-stock-owning-orgs-see-schemes

pull/838/head
Ted-U 3 years ago committed by GitHub
parent
commit
72512c9ffa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      Gemfile
  2. 36
      Gemfile.lock
  3. 37
      app/components/check_answers_summary_list_card_component.html.erb
  4. 18
      app/components/check_answers_summary_list_card_component.rb
  5. 2
      app/controllers/case_logs_controller.rb
  6. 7
      app/controllers/form_controller.rb
  7. 2
      app/controllers/locations_controller.rb
  8. 19
      app/controllers/schemes_controller.rb
  9. 8
      app/controllers/users_controller.rb
  10. 17
      app/helpers/check_answers_helper.rb
  11. 48
      app/models/case_log.rb
  12. 4
      app/models/derived_variables/case_log_variables.rb
  13. 3
      app/models/form/question.rb
  14. 3
      app/models/form/setup/pages/created_by.rb
  15. 3
      app/models/form/setup/pages/location.rb
  16. 3
      app/models/form/setup/pages/needs_type.rb
  17. 3
      app/models/form/setup/pages/organisation.rb
  18. 3
      app/models/form/setup/pages/property_reference.rb
  19. 3
      app/models/form/setup/pages/renewal.rb
  20. 3
      app/models/form/setup/pages/rent_type.rb
  21. 3
      app/models/form/setup/pages/scheme.rb
  22. 3
      app/models/form/setup/pages/tenancy_start_date.rb
  23. 3
      app/models/form/setup/pages/tenant_code.rb
  24. 7
      app/models/form/setup/subsections/setup.rb
  25. 4
      app/models/location.rb
  26. 29
      app/models/organisation.rb
  27. 4
      app/models/organisation_relationship.rb
  28. 6
      app/models/scheme.rb
  29. 36
      app/models/user.rb
  30. 4
      app/models/validations/date_validations.rb
  31. 11
      app/models/validations/soft_validations.rb
  32. 24
      app/services/archive_storage_service.rb
  33. 4
      app/services/imports/case_logs_import_service.rb
  34. 22
      app/services/imports/scheme_location_import_service.rb
  35. 2
      app/services/imports/user_import_service.rb
  36. 78
      app/services/s3_storage_service.rb
  37. 71
      app/services/storage_service.rb
  38. 2
      app/views/case_logs/edit.html.erb
  39. 14
      app/views/form/check_answers.html.erb
  40. 2
      app/views/form/review.html.erb
  41. 4
      app/views/locations/edit.html.erb
  42. 2
      app/views/organisations/show.html.erb
  43. 4
      app/views/schemes/_scheme_summary_list_row.html.erb
  44. 4
      app/views/schemes/check_answers.html.erb
  45. 2
      app/views/schemes/secondary_client_group.html.erb
  46. 6
      app/views/users/new.html.erb
  47. 432
      config/forms/2021_2022.json
  48. 436
      config/forms/2022_2023.json
  49. 2
      config/initializers/devise.rb
  50. 4
      config/locales/devise.en.yml
  51. 10
      config/locales/en.yml
  52. 1
      config/routes.rb
  53. 6
      db/migrate/20220720214646_add_delete_cascade_from_orgs_down.rb
  54. 6
      db/migrate/20220722133738_add_delete_caselogs_users.rb
  55. 7
      db/migrate/20220729110846_add_confirmed_location.rb
  56. 5
      db/migrate/20220808090330_add_major_repairs_date_value_check.rb
  57. 5
      db/migrate/20220808093035_add_void_date_value_check.rb
  58. 9
      db/migrate/20220810152340_add_parent_child_relationships.rb
  59. 16
      db/schema.rb
  60. 2
      lib/tasks/data_export.rake
  61. 4
      lib/tasks/data_import.rake
  62. 2
      lib/tasks/data_import_field.rake
  63. 32
      lib/tasks/full_import.rake
  64. 27
      spec/components/check_answers_summary_list_card_component_spec.rb
  65. 2
      spec/factories/case_log.rb
  66. 1
      spec/factories/location.rb
  67. 5
      spec/factories/organisation.rb
  68. 13
      spec/features/form/check_answers_page_spec.rb
  69. 23
      spec/features/form/checkboxes_spec.rb
  70. 33
      spec/features/form/page_routing_spec.rb
  71. 33
      spec/features/organisation_spec.rb
  72. 2
      spec/features/schemes_helpers.rb
  73. 96
      spec/features/schemes_spec.rb
  74. 4
      spec/fixtures/files/case_logs_download.csv
  75. 2
      spec/fixtures/files/case_logs_download_non_support.csv
  76. 33
      spec/fixtures/forms/2021_2022.json
  77. 0
      spec/fixtures/imports/dataprotect/7c5bd5fb549c09a2c55d7cb90d7ba84927e64618.xml
  78. 0
      spec/fixtures/imports/institution/7c5bd5fb549c09a2c55d7cb90d7ba84927e64618.xml
  79. 0
      spec/fixtures/imports/logs/00d2343e-d5fa-4c89-8400-ec3854b0f2b4.xml
  80. 0
      spec/fixtures/imports/logs/0b4a68df-30cc-474a-93c0-a56ce8fdad3b.xml
  81. 0
      spec/fixtures/imports/logs/0ead17cb-1668-442d-898c-0d52879ff592.xml
  82. 0
      spec/fixtures/imports/logs/166fc004-392e-47a8-acb8-1c018734882b.xml
  83. 0
      spec/fixtures/imports/logs/5ybz29dj-l33t-k1l0-hj86-n4k4ma77xkcd.xml
  84. 0
      spec/fixtures/imports/logs/893ufj2s-lq77-42m4-rty6-ej09gh585uy1.xml
  85. 0
      spec/fixtures/imports/mgmtgroups/6d6d7618b58affe2a150a5ef2e9f4765fa6cd05d.xml
  86. 0
      spec/fixtures/imports/rent-period/ebd22326d33e389e9f1bfd546979d2c05f9e68d6.xml
  87. 0
      spec/fixtures/imports/schemes/0ae7ad6dc0f1cf7ef33c18cc8c108bebc1b4923e.xml
  88. 0
      spec/fixtures/imports/schemes/0bb3836b70b4dd9903263d5a764a5c45b964a89d.xml
  89. 0
      spec/fixtures/imports/user/10c887710550844e2551b3e0fb88dc9b4a8a642b.xml
  90. 0
      spec/fixtures/imports/user/9ed81a262215a1634f0809effa683e38924d8bcb.xml
  91. 0
      spec/fixtures/imports/user/b7829b1a5dfb68bb1e01c08445830c0add40907c.xml
  92. 0
      spec/fixtures/imports/user/d4729b1a5dfb68bb1e01c08445830c0add40907c.xml
  93. 0
      spec/fixtures/imports/user/d6717836154cd9a58f9e2f1d3077e3ab81e07613.xml
  94. 0
      spec/fixtures/imports/user/fc7625a02b24ae16162aa63ae7cb33feeec0c373.xml
  95. 8
      spec/lib/tasks/data_export_spec.rb
  96. 18
      spec/lib/tasks/data_import_spec.rb
  97. 10
      spec/lib/tasks/date_import_field_spec.rb
  98. 86
      spec/lib/tasks/full_import_spec.rb
  99. 82
      spec/models/case_log_spec.rb
  100. 4
      spec/models/form/subsection_spec.rb
  101. Some files were not shown because too many files have changed in this diff Show More

2
Gemfile

@ -56,6 +56,8 @@ gem "sentry-rails"
gem "sentry-ruby" gem "sentry-ruby"
# Possessives in strings # Possessives in strings
gem "possessive" gem "possessive"
# Strip whitespace from active record attributes
gem "auto_strip_attributes"
group :development, :test do group :development, :test do
# Check gems for known vulnerabilities # Check gems for known vulnerabilities

36
Gemfile.lock

@ -81,9 +81,11 @@ GEM
addressable (2.8.0) addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0) public_suffix (>= 2.0.2, < 5.0)
ast (2.4.2) ast (2.4.2)
auto_strip_attributes (2.6.0)
activerecord (>= 4.0)
aws-eventstream (1.2.0) aws-eventstream (1.2.0)
aws-partitions (1.609.0) aws-partitions (1.615.0)
aws-sdk-core (3.131.3) aws-sdk-core (3.131.6)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0) aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
@ -107,7 +109,7 @@ GEM
parser (>= 2.4) parser (>= 2.4)
smart_properties smart_properties
bindex (0.8.1) bindex (0.8.1)
bootsnap (1.12.0) bootsnap (1.13.0)
msgpack (~> 1.2) msgpack (~> 1.2)
builder (3.2.4) builder (3.2.4)
bundler-audit (0.9.1) bundler-audit (0.9.1)
@ -143,9 +145,9 @@ GEM
diff-lcs (1.5.0) diff-lcs (1.5.0)
digest (3.1.0) digest (3.1.0)
docile (1.4.0) docile (1.4.0)
dotenv (2.7.6) dotenv (2.8.1)
dotenv-rails (2.7.6) dotenv-rails (2.8.1)
dotenv (= 2.7.6) dotenv (= 2.8.1)
railties (>= 3.2) railties (>= 3.2)
encryptor (3.0.0) encryptor (3.0.0)
erb_lint (0.1.3) erb_lint (0.1.3)
@ -156,14 +158,14 @@ GEM
rainbow rainbow
rubocop rubocop
smart_properties smart_properties
erubi (1.10.0) erubi (1.11.0)
excon (0.92.4) excon (0.92.4)
factory_bot (6.2.1) factory_bot (6.2.1)
activesupport (>= 5.0.0) activesupport (>= 5.0.0)
factory_bot_rails (6.2.0) factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0) factory_bot (~> 6.2.0)
railties (>= 5.0.0) railties (>= 5.0.0)
faker (2.21.0) faker (2.22.0)
i18n (>= 1.8.11, < 2) i18n (>= 1.8.11, < 2)
ffi (1.15.5) ffi (1.15.5)
globalid (1.0.0) globalid (1.0.0)
@ -209,7 +211,7 @@ GEM
method_source (1.0.0) method_source (1.0.0)
mini_mime (1.1.2) mini_mime (1.1.2)
minitest (5.16.2) minitest (5.16.2)
msgpack (1.5.3) msgpack (1.5.4)
net-imap (0.2.3) net-imap (0.2.3)
digest digest
net-protocol net-protocol
@ -249,7 +251,7 @@ GEM
parallel (1.22.1) parallel (1.22.1)
parser (3.1.2.0) parser (3.1.2.0)
ast (~> 2.4.1) ast (~> 2.4.1)
pg (1.4.1) pg (1.4.2)
possessive (1.0.1) possessive (1.0.1)
postcodes_io (0.4.0) postcodes_io (0.4.0)
excon (~> 0.39) excon (~> 0.39)
@ -373,14 +375,11 @@ GEM
rexml (~> 3.2, >= 3.2.5) rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0) rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0) websocket (~> 1.0)
sentry-rails (5.3.1) sentry-rails (5.4.1)
railties (>= 5.0) railties (>= 5.0)
sentry-ruby-core (~> 5.3.1) sentry-ruby (~> 5.4.1)
sentry-ruby (5.3.1) sentry-ruby (5.4.1)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
sentry-ruby-core (= 5.3.1)
sentry-ruby-core (5.3.1)
concurrent-ruby
simplecov (0.21.2) simplecov (0.21.2)
docile (~> 1.1) docile (~> 1.1)
simplecov-html (~> 0.11) simplecov-html (~> 0.11)
@ -390,7 +389,7 @@ GEM
smart_properties (1.17.0) smart_properties (1.17.0)
stimulus-rails (1.1.0) stimulus-rails (1.1.0)
railties (>= 6.0.0) railties (>= 6.0.0)
strscan (3.0.3) strscan (3.0.4)
thor (1.2.1) thor (1.2.1)
timecop (0.9.5) timecop (0.9.5)
timeout (0.3.0) timeout (0.3.0)
@ -408,7 +407,7 @@ GEM
activemodel (>= 6.0.0) activemodel (>= 6.0.0)
bindex (>= 0.4.0) bindex (>= 0.4.0)
railties (>= 6.0.0) railties (>= 6.0.0)
webmock (3.14.0) webmock (3.16.0)
addressable (>= 2.8.0) addressable (>= 2.8.0)
crack (>= 0.3.2) crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0) hashdiff (>= 0.4.0, < 2.0.0)
@ -428,6 +427,7 @@ PLATFORMS
x86_64-linux x86_64-linux
DEPENDENCIES DEPENDENCIES
auto_strip_attributes
aws-sdk-s3 aws-sdk-s3
bootsnap (>= 1.4.4) bootsnap (>= 1.4.4)
bundler-audit bundler-audit

37
app/components/check_answers_summary_list_card_component.html.erb

@ -0,0 +1,37 @@
<div class="x-govuk-summary-card govuk-!-margin-bottom-6">
<% if applicable_questions.first.check_answers_card_number != 0 %>
<div class="x-govuk-summary-card__header">
<% if applicable_questions.first.check_answers_card_number == 1 %>
<h3 class="x-govuk-summary-card__title">Lead tenant</h3>
<% end %>
<% if applicable_questions.first.check_answers_card_number > 1 %>
<h3 class="x-govuk-summary-card__title">Person <%= applicable_questions.first.check_answers_card_number %></h3>
<% end %>
</div>
<% end %>
<div class="x-govuk-summary-card__body">
<%= govuk_summary_list do |summary_list| %>
<% applicable_questions.each do |question| %>
<% summary_list.row do |row| %>
<% row.key { question.check_answer_label.to_s.presence || question.header.to_s } %>
<% row.value do %>
<span class="govuk-!-margin-right-4"><%= get_answer_label(question) %></span>
<% extra_value = question.get_extra_check_answer_value(case_log) %>
<% if extra_value %>
<span class="govuk-!-font-weight-regular app-!-colour-muted"><%= extra_value %></span>
<% end %>
<br>
<% question.get_inferred_answers(case_log).each do |inferred_answer| %>
<span class="govuk-!-font-weight-regular app-!-colour-muted"><%= inferred_answer %></span>
<% end %>
<% end %>
<% row.action(
text: question.action_text(case_log),
href: question.action_href(case_log, question.page.id),
visually_hidden_text: question.check_answer_label.to_s.downcase,
) %>
<% end %>
<% end %>
<% end %>
</div>
</div>

18
app/components/check_answers_summary_list_card_component.rb

@ -0,0 +1,18 @@
class CheckAnswersSummaryListCardComponent < ViewComponent::Base
attr_reader :questions, :case_log, :user
def initialize(questions:, case_log:, user:)
@questions = questions
@case_log = case_log
@user = user
super
end
def applicable_questions
questions.reject { |q| q.hidden_in_check_answers?(case_log, user) }
end
def get_answer_label(question)
question.answer_label(case_log).presence || "<span class=\"app-!-colour-muted\">You didn’t answer this question</span>".html_safe
end
end

2
app/controllers/case_logs_controller.rb

@ -22,7 +22,7 @@ class CaseLogsController < ApplicationController
end end
format.csv do format.csv do
send_data unpaginated_filtered_logs.to_csv, filename: "logs-#{Time.zone.now}.csv" send_data unpaginated_filtered_logs.to_csv(current_user), filename: "logs-#{Time.zone.now}.csv"
end end
end end
end end

7
app/controllers/form_controller.rb

@ -73,7 +73,7 @@ private
end end
if session["fields"] if session["fields"]
session["fields"].each do |field, value| session["fields"].each do |field, value|
unless @case_log.form.get_question(field, @case_log).type == "date" unless @case_log.form.get_question(field, @case_log)&.type == "date"
@case_log[field] = value @case_log[field] = value
end end
end end
@ -89,7 +89,7 @@ private
year = params["case_log"]["#{question.id}(1i)"] year = params["case_log"]["#{question.id}(1i)"]
next unless [day, month, year].any?(&:present?) next unless [day, month, year].any?(&:present?)
result[question.id] = if day.to_i.between?(1, 31) && month.to_i.between?(1, 12) && year.to_i.between?(2000, 2200) result[question.id] = if Date.valid_date?(year.to_i, month.to_i, day.to_i) && year.to_i.between?(2000, 2200)
Date.new(year.to_i, month.to_i, day.to_i) Date.new(year.to_i, month.to_i, day.to_i)
else else
Date.new(0, 1, 1) Date.new(0, 1, 1)
@ -161,10 +161,11 @@ private
def question_missing_response?(responses_for_page, question) def question_missing_response?(responses_for_page, question)
if %w[checkbox validation_override].include?(question.type) if %w[checkbox validation_override].include?(question.type)
question.answer_options.keys.reject { |x| x.match(/divider/) }.all? do |option| answered = question.answer_options.keys.reject { |x| x.match(/divider/) }.map do |option|
session["fields"][option] = @case_log[option] = params["case_log"][question.id].include?(option) ? 1 : 0 session["fields"][option] = @case_log[option] = params["case_log"][question.id].include?(option) ? 1 : 0
params["case_log"][question.id].exclude?(option) params["case_log"][question.id].exclude?(option)
end end
answered.all?
else else
session["fields"][question.id] = @case_log[question.id] = responses_for_page[question.id] session["fields"][question.id] = @case_log[question.id] = responses_for_page[question.id]
responses_for_page[question.id].nil? || responses_for_page[question.id].blank? responses_for_page[question.id].nil? || responses_for_page[question.id].blank?

2
app/controllers/locations_controller.rb

@ -56,7 +56,7 @@ class LocationsController < ApplicationController
when "edit" when "edit"
location_params[:add_another_location] == "Yes" ? redirect_to(new_location_path(@location.scheme)) : redirect_to(scheme_check_answers_path(@scheme, anchor: "locations")) location_params[:add_another_location] == "Yes" ? redirect_to(new_location_path(@location.scheme)) : redirect_to(scheme_check_answers_path(@scheme, anchor: "locations"))
when "edit-name" when "edit-name"
redirect_to(locations_path(@scheme)) redirect_to(scheme_check_answers_path(@scheme, anchor: "locations"))
end end
else else
render :edit, status: :unprocessable_entity render :edit, status: :unprocessable_entity

19
app/controllers/schemes_controller.rb

@ -5,9 +5,9 @@ class SchemesController < ApplicationController
before_action :authenticate_user! before_action :authenticate_user!
before_action :find_resource, except: %i[index] before_action :find_resource, except: %i[index]
before_action :authenticate_scope! before_action :authenticate_scope!
before_action :redirect_if_scheme_confirmed, only: %i[primary_client_group confirm_secondary_client_group secondary_client_group support details]
def index def index
flash[:notice] = "#{Scheme.find(params[:scheme_id].to_i).service_name} has been created." if params[:scheme_id]
redirect_to schemes_organisation_path(current_user.organisation) unless current_user.support? redirect_to schemes_organisation_path(current_user.organisation) unless current_user.support?
all_schemes = Scheme.all.order("service_name ASC") all_schemes = Scheme.all.order("service_name ASC")
@ -52,10 +52,19 @@ class SchemesController < ApplicationController
check_answers = params[:scheme][:check_answers] check_answers = params[:scheme][:check_answers]
page = params[:scheme][:page] page = params[:scheme][:page]
scheme_previously_confirmed = @scheme.confirmed?
validation_errors scheme_params validation_errors scheme_params
if @scheme.errors.empty? && @scheme.update(scheme_params) if @scheme.errors.empty? && @scheme.update(scheme_params)
if check_answers if scheme_params[:confirmed] == "true"
@scheme.locations.update!(confirmed: true)
flash[:notice] = if scheme_previously_confirmed
"#{@scheme.service_name} has been updated."
else
"#{@scheme.service_name} has been created."
end
redirect_to scheme_path(@scheme)
elsif check_answers
if confirm_secondary_page? page if confirm_secondary_page? page
redirect_to scheme_secondary_client_group_path(@scheme, check_answers: "true") redirect_to scheme_secondary_client_group_path(@scheme, check_answers: "true")
else else
@ -177,7 +186,7 @@ private
scheme_details_path(@scheme) scheme_details_path(@scheme)
end end
when "edit-name" when "edit-name"
scheme_path(@scheme) scheme_check_answers_path(@scheme)
when "check-answers" when "check-answers"
schemes_path(scheme_id: @scheme.id) schemes_path(scheme_id: @scheme.id)
end end
@ -246,4 +255,8 @@ private
render_not_found and return render_not_found and return
end end
end end
def redirect_if_scheme_confirmed
redirect_to @scheme if @scheme.confirmed?
end
end end

8
app/controllers/users_controller.rb

@ -58,7 +58,7 @@ class UsersController < ApplicationController
end end
elsif user_params.key?("password") elsif user_params.key?("password")
format_error_messages format_error_messages
@minimum_password_length = User.password_length.min @minimum_password_length = Devise.password_length.min
render "devise/passwords/edit", locals: { resource: @user, resource_name: "user" }, status: :unprocessable_entity render "devise/passwords/edit", locals: { resource: @user, resource_name: "user" }, status: :unprocessable_entity
else else
format_error_messages format_error_messages
@ -79,7 +79,6 @@ class UsersController < ApplicationController
redirect_to created_user_redirect_path redirect_to created_user_redirect_path
else else
unless @resource.errors[:organisation].empty? unless @resource.errors[:organisation].empty?
@resource.errors.add(:organisation_id, message: @resource.errors[:organisation])
@resource.errors.delete(:organisation) @resource.errors.delete(:organisation)
end end
render :new, status: :unprocessable_entity render :new, status: :unprocessable_entity
@ -87,7 +86,7 @@ class UsersController < ApplicationController
end end
def edit_password def edit_password
@minimum_password_length = User.password_length.min @minimum_password_length = Devise.password_length.min
render "devise/passwords/edit", locals: { resource: @user, resource_name: "user" } render "devise/passwords/edit", locals: { resource: @user, resource_name: "user" }
end end
@ -114,9 +113,6 @@ private
if user_params[:role].present? && !current_user.assignable_roles.key?(user_params[:role].to_sym) if user_params[:role].present? && !current_user.assignable_roles.key?(user_params[:role].to_sym)
@resource.errors.add :role, I18n.t("validations.role.invalid") @resource.errors.add :role, I18n.t("validations.role.invalid")
end end
if user_params[:role].empty?
@resource.errors.add :role, I18n.t("activerecord.errors.models.user.attributes.role.blank")
end
end end
def format_error_messages def format_error_messages

17
app/helpers/check_answers_helper.rb

@ -11,6 +11,23 @@ module CheckAnswersHelper
end end
end end
def can_change_scheme_answer?(attribute_name, scheme)
editable_attributes = current_user.support? ? ["Name", "Confidential information", "Housing stock owned by"] : ["Name", "Confidential information"]
!scheme.confirmed? || editable_attributes.include?(attribute_name)
end
def get_location_change_link_href(scheme, location)
if location.confirmed?
location_edit_name_path(id: scheme.id, location_id: location.id)
else
location_edit_path(id: scheme.id, location_id: location.id)
end
end
def any_questions_have_summary_card_number?(subsection, case_log)
subsection.applicable_questions(case_log).map(&:check_answers_card_number).compact.length.positive?
end
private private
def answered_questions_count(subsection, case_log, current_user) def answered_questions_count(subsection, case_log, current_user)

48
app/models/case_log.rb

@ -57,11 +57,13 @@ class CaseLog < ApplicationRecord
scope :filter_by_tenant_code, ->(tenant_code) { where("tenancycode ILIKE ?", "%#{tenant_code}%") } scope :filter_by_tenant_code, ->(tenant_code) { where("tenancycode ILIKE ?", "%#{tenant_code}%") }
scope :filter_by_propcode, ->(propcode) { where("propcode ILIKE ?", "%#{propcode}%") } scope :filter_by_propcode, ->(propcode) { where("propcode ILIKE ?", "%#{propcode}%") }
scope :filter_by_postcode, ->(postcode_full) { where("REPLACE(postcode_full, ' ', '') ILIKE ?", "%#{postcode_full.delete(' ')}%") } scope :filter_by_postcode, ->(postcode_full) { where("REPLACE(postcode_full, ' ', '') ILIKE ?", "%#{postcode_full.delete(' ')}%") }
scope :filter_by_location_postcode, ->(postcode_full) { left_joins(:location).where("REPLACE(locations.postcode, ' ', '') ILIKE ?", "%#{postcode_full.delete(' ')}%") }
scope :search_by, lambda { |param| scope :search_by, lambda { |param|
filter_by_id(param) filter_by_location_postcode(param)
.or(filter_by_tenant_code(param)) .or(filter_by_tenant_code(param))
.or(filter_by_propcode(param)) .or(filter_by_propcode(param))
.or(filter_by_postcode(param)) .or(filter_by_postcode(param))
.or(filter_by_id(param))
} }
AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze
@ -72,6 +74,7 @@ class CaseLog < ApplicationRecord
NUM_OF_WEEKS_FROM_PERIOD = { 2 => 26, 3 => 13, 4 => 12, 5 => 50, 6 => 49, 7 => 48, 8 => 47, 9 => 46, 1 => 52 }.freeze NUM_OF_WEEKS_FROM_PERIOD = { 2 => 26, 3 => 13, 4 => 12, 5 => 50, 6 => 49, 7 => 48, 8 => 47, 9 => 46, 1 => 52 }.freeze
SUFFIX_FROM_PERIOD = { 2 => "every 2 weeks", 3 => "every 4 weeks", 4 => "every month" }.freeze SUFFIX_FROM_PERIOD = { 2 => "every 2 weeks", 3 => "every 4 weeks", 4 => "every month" }.freeze
RETIREMENT_AGES = { "M" => 67, "F" => 60, "X" => 67 }.freeze RETIREMENT_AGES = { "M" => 67, "F" => 60, "X" => 67 }.freeze
CSV_FIELDS_TO_OMIT = %w[hhmemb net_income_value_check sale_or_letting first_time_property_let_as_social_housing renttype needstype postcode_known is_la_inferred totchild totelder totadult net_income_known is_carehome previous_la_known is_previous_la_inferred age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known letting_allocation_unknown details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 rent_type wrent wscharge wpschrge wsupchrg wtcharge wtshortfall rent_value_check old_form_id old_id retirement_value_check tshortfall_known pregnancy_value_check hhtype new_old vacdays].freeze
enum status: STATUS enum status: STATUS
def form def form
@ -424,18 +427,43 @@ class CaseLog < ApplicationRecord
[30, 31].any?(prevten) [30, 31].any?(prevten)
end end
def self.to_csv def owning_organisation_name
owning_organisation&.name
end
def managing_organisation_name
managing_organisation&.name
end
def created_by_name
created_by&.name
end
def self.to_csv(user = nil)
CSV.generate(headers: true) do |csv| CSV.generate(headers: true) do |csv|
csv << attribute_names + %w[unittype_sh] attributes = csv_attributes(user)
csv << attributes
all.find_each do |record| all.find_each do |record|
csv << record.attributes.merge({ "unittype_sh" => record.unittype_sh, "la" => record.la }).map do |att, val| csv << attributes.map do |att|
record.form.get_question(att, record)&.label_from_value(val) || val record.form.get_question(att, record)&.label_from_value(record.send(att)) || label_from_value(record.send(att))
end end
end end
end end
end end
def self.label_from_value(value)
return "Yes" if value == true
return "No" if value == false
value
end
def self.csv_attributes(user)
attributes = attribute_names - %w[owning_organisation_id managing_organisation_id created_by_id] + %w[unittype_sh owning_organisation_name managing_organisation_name created_by_name]
user.present? && !user.support? ? attributes - CSV_FIELDS_TO_OMIT : attributes
end
def soft_min_for_period def soft_min_for_period
soft_min = LaRentRange.find_by(start_year: collection_start_year, la:, beds:, lettype:).soft_min soft_min = LaRentRange.find_by(start_year: collection_start_year, la:, beds:, lettype:).soft_min
"#{soft_value_for_period(soft_min)} #{SUFFIX_FROM_PERIOD[period].presence || 'every week'}" "#{soft_value_for_period(soft_min)} #{SUFFIX_FROM_PERIOD[period].presence || 'every week'}"
@ -536,7 +564,9 @@ private
dependent_questions = { waityear: [{ key: :renewal, value: 0 }], dependent_questions = { waityear: [{ key: :renewal, value: 0 }],
homeless: [{ key: :renewal, value: 0 }], homeless: [{ key: :renewal, value: 0 }],
referral: [{ key: :renewal, value: 0 }], referral: [{ key: :renewal, value: 0 }],
underoccupation_benefitcap: [{ key: :renewal, value: 0 }] } underoccupation_benefitcap: [{ key: :renewal, value: 0 }],
wchair: [{ key: :needstype, value: 1 }],
location_id: [{ key: :needstype, value: 1 }] }
dependent_questions.each do |dependent, conditions| dependent_questions.each do |dependent, conditions|
condition_key = conditions.first[:key] condition_key = conditions.first[:key]

4
app/models/derived_variables/case_log_variables.rb

@ -8,7 +8,7 @@ module DerivedVariables::CaseLogVariables
def scheme_has_multiple_locations? def scheme_has_multiple_locations?
return false unless scheme return false unless scheme
@scheme_locations_count ||= scheme.locations.size @scheme_locations_count ||= scheme.locations.active.size
@scheme_locations_count > 1 @scheme_locations_count > 1
end end
@ -188,7 +188,7 @@ private
def reset_scheme_location! def reset_scheme_location!
self.location = nil self.location = nil
if scheme && scheme.locations.size == 1 if scheme && scheme.locations.active.size == 1
self.location = scheme.locations.first self.location = scheme.locations.first
end end
end end

3
app/models/form/question.rb

@ -3,7 +3,7 @@ class Form::Question
:type, :min, :max, :step, :width, :fields_to_add, :result_field, :type, :min, :max, :step, :width, :fields_to_add, :result_field,
:conditional_for, :readonly, :answer_options, :page, :check_answer_label, :conditional_for, :readonly, :answer_options, :page, :check_answer_label,
:inferred_answers, :hidden_in_check_answers, :inferred_check_answers_value, :inferred_answers, :hidden_in_check_answers, :inferred_check_answers_value,
:guidance_partial, :prefix, :suffix, :requires_js, :fields_added, :derived :guidance_partial, :prefix, :suffix, :requires_js, :fields_added, :derived, :check_answers_card_number
module GuidancePosition module GuidancePosition
TOP = 1 TOP = 1
@ -37,6 +37,7 @@ class Form::Question
@suffix = hsh["suffix"] @suffix = hsh["suffix"]
@requires_js = hsh["requires_js"] @requires_js = hsh["requires_js"]
@fields_added = hsh["fields_added"] @fields_added = hsh["fields_added"]
@check_answers_card_number = hsh["check_answers_card_number"]
end end
end end

3
app/models/form/setup/pages/created_by.rb

@ -4,12 +4,11 @@ class Form::Setup::Pages::CreatedBy < ::Form::Page
@id = "created_by" @id = "created_by"
@header = "" @header = ""
@description = "" @description = ""
@questions = questions
@subsection = subsection @subsection = subsection
end end
def questions def questions
[ @questions ||= [
Form::Setup::Questions::CreatedById.new(nil, nil, self), Form::Setup::Questions::CreatedById.new(nil, nil, self),
] ]
end end

3
app/models/form/setup/pages/location.rb

@ -3,7 +3,6 @@ class Form::Setup::Pages::Location < ::Form::Page
super("location", hsh, subsection) super("location", hsh, subsection)
@header = "" @header = ""
@description = "" @description = ""
@questions = questions
@depends_on = [{ @depends_on = [{
"supported_housing_schemes_enabled?" => true, "supported_housing_schemes_enabled?" => true,
"needstype" => 2, "needstype" => 2,
@ -12,7 +11,7 @@ class Form::Setup::Pages::Location < ::Form::Page
end end
def questions def questions
[ @questions ||= [
Form::Setup::Questions::LocationId.new(nil, nil, self), Form::Setup::Questions::LocationId.new(nil, nil, self),
] ]
end end

3
app/models/form/setup/pages/needs_type.rb

@ -4,13 +4,12 @@ class Form::Setup::Pages::NeedsType < ::Form::Page
@id = "needs_type" @id = "needs_type"
@header = "" @header = ""
@description = "" @description = ""
@questions = questions
@depends_on = [{ "supported_housing_schemes_enabled?" => true }] @depends_on = [{ "supported_housing_schemes_enabled?" => true }]
@subsection = subsection @subsection = subsection
end end
def questions def questions
[ @questions ||= [
Form::Setup::Questions::NeedsType.new(nil, nil, self), Form::Setup::Questions::NeedsType.new(nil, nil, self),
] ]
end end

3
app/models/form/setup/pages/organisation.rb

@ -4,12 +4,11 @@ class Form::Setup::Pages::Organisation < ::Form::Page
@id = "organisation" @id = "organisation"
@header = "" @header = ""
@description = "" @description = ""
@questions = questions
@subsection = subsection @subsection = subsection
end end
def questions def questions
[ @questions ||= [
Form::Setup::Questions::OwningOrganisationId.new(nil, nil, self), Form::Setup::Questions::OwningOrganisationId.new(nil, nil, self),
] ]
end end

3
app/models/form/setup/pages/property_reference.rb

@ -4,12 +4,11 @@ class Form::Setup::Pages::PropertyReference < ::Form::Page
@id = "property_reference" @id = "property_reference"
@header = "" @header = ""
@description = "" @description = ""
@questions = questions
@subsection = subsection @subsection = subsection
end end
def questions def questions
[ @questions ||= [
Form::Setup::Questions::PropertyReference.new(nil, nil, self), Form::Setup::Questions::PropertyReference.new(nil, nil, self),
] ]
end end

3
app/models/form/setup/pages/renewal.rb

@ -4,12 +4,11 @@ class Form::Setup::Pages::Renewal < ::Form::Page
@id = "renewal" @id = "renewal"
@header = "" @header = ""
@description = "" @description = ""
@questions = questions
@subsection = subsection @subsection = subsection
end end
def questions def questions
[ @questions ||= [
Form::Setup::Questions::Renewal.new(nil, nil, self), Form::Setup::Questions::Renewal.new(nil, nil, self),
] ]
end end

3
app/models/form/setup/pages/rent_type.rb

@ -3,13 +3,12 @@ class Form::Setup::Pages::RentType < ::Form::Page
super("rent_type", hsh, subsection) super("rent_type", hsh, subsection)
@header = "" @header = ""
@description = "" @description = ""
@questions = questions
@depends_on = [{ "supported_housing_schemes_enabled?" => true }] @depends_on = [{ "supported_housing_schemes_enabled?" => true }]
@derived = true @derived = true
end end
def questions def questions
[ @questions ||= [
Form::Setup::Questions::RentType.new(nil, nil, self), Form::Setup::Questions::RentType.new(nil, nil, self),
Form::Setup::Questions::IrproductOther.new(nil, nil, self), Form::Setup::Questions::IrproductOther.new(nil, nil, self),
] ]

3
app/models/form/setup/pages/scheme.rb

@ -3,7 +3,6 @@ class Form::Setup::Pages::Scheme < ::Form::Page
super("scheme", hsh, subsection) super("scheme", hsh, subsection)
@header = "" @header = ""
@description = "" @description = ""
@questions = questions
@depends_on = [{ @depends_on = [{
"supported_housing_schemes_enabled?" => true, "supported_housing_schemes_enabled?" => true,
"needstype" => 2, "needstype" => 2,
@ -11,7 +10,7 @@ class Form::Setup::Pages::Scheme < ::Form::Page
end end
def questions def questions
[ @questions ||= [
Form::Setup::Questions::SchemeId.new(nil, nil, self), Form::Setup::Questions::SchemeId.new(nil, nil, self),
] ]
end end

3
app/models/form/setup/pages/tenancy_start_date.rb

@ -3,12 +3,11 @@ class Form::Setup::Pages::TenancyStartDate < ::Form::Page
super super
@id = "tenancy_start_date" @id = "tenancy_start_date"
@description = "" @description = ""
@questions = questions
@subsection = subsection @subsection = subsection
end end
def questions def questions
[ @questions ||= [
Form::Setup::Questions::TenancyStartDate.new(nil, nil, self), Form::Setup::Questions::TenancyStartDate.new(nil, nil, self),
] ]
end end

3
app/models/form/setup/pages/tenant_code.rb

@ -4,12 +4,11 @@ class Form::Setup::Pages::TenantCode < ::Form::Page
@id = "tenant_code" @id = "tenant_code"
@header = "" @header = ""
@description = "" @description = ""
@questions = questions
@subsection = subsection @subsection = subsection
end end
def questions def questions
[ @questions ||= [
Form::Setup::Questions::TenantCode.new(nil, nil, self), Form::Setup::Questions::TenantCode.new(nil, nil, self),
] ]
end end

7
app/models/form/setup/subsections/setup.rb

@ -3,12 +3,11 @@ class Form::Subsections::Setup < ::Form::Subsection
super super
@id = "setup" @id = "setup"
@label = "Set up this lettings log" @label = "Set up this lettings log"
@pages = [pages]
@section = section @section = section
end end
def pages def pages
[ @pages ||= [
Form::Setup::Pages::Organisation.new(nil, nil, self), Form::Setup::Pages::Organisation.new(nil, nil, self),
Form::Setup::Pages::CreatedBy.new(nil, nil, self), Form::Setup::Pages::CreatedBy.new(nil, nil, self),
Form::Setup::Pages::NeedsType.new(nil, nil, self), Form::Setup::Pages::NeedsType.new(nil, nil, self),
@ -26,6 +25,10 @@ class Form::Subsections::Setup < ::Form::Subsection
questions.select { |q| support_only_questions.include?(q.id) } + super questions.select { |q| support_only_questions.include?(q.id) } + super
end end
def enabled?(_case_log)
true
end
private private
def support_only_questions def support_only_questions

4
app/models/location.rb

@ -7,11 +7,15 @@ class Location < ApplicationRecord
before_save :lookup_postcode!, if: :postcode_changed? before_save :lookup_postcode!, if: :postcode_changed?
auto_strip_attributes :name
attr_accessor :add_another_location attr_accessor :add_another_location
scope :search_by_postcode, ->(postcode) { where("REPLACE(postcode, ' ', '') ILIKE ?", "%#{postcode.delete(' ')}%") } scope :search_by_postcode, ->(postcode) { where("REPLACE(postcode, ' ', '') ILIKE ?", "%#{postcode.delete(' ')}%") }
scope :search_by_name, ->(name) { where("name ILIKE ?", "%#{name}%") } scope :search_by_name, ->(name) { where("name ILIKE ?", "%#{name}%") }
scope :search_by, ->(param) { search_by_name(param).or(search_by_postcode(param)) } scope :search_by, ->(param) { search_by_name(param).or(search_by_postcode(param)) }
scope :started, -> { where("startdate <= ?", Time.zone.today).or(where(startdate: nil)) }
scope :active, -> { where(confirmed: true).and(started) }
MOBILITY_TYPE = { MOBILITY_TYPE = {
"Wheelchair-user standard": "W", "Wheelchair-user standard": "W",

29
app/models/organisation.rb

@ -1,17 +1,23 @@
class Organisation < ApplicationRecord class Organisation < ApplicationRecord
has_many :users has_many :users, dependent: :delete_all
has_many :owned_case_logs, class_name: "CaseLog", foreign_key: "owning_organisation_id" has_many :owned_case_logs, class_name: "CaseLog", foreign_key: "owning_organisation_id", dependent: :delete_all
has_many :managed_case_logs, class_name: "CaseLog", foreign_key: "managing_organisation_id" has_many :managed_case_logs, class_name: "CaseLog", foreign_key: "managing_organisation_id"
has_many :data_protection_confirmations has_many :data_protection_confirmations
has_many :organisation_rent_periods has_many :organisation_rent_periods
has_many :owned_schemes, class_name: "Scheme", foreign_key: "owning_organisation_id" has_many :owned_schemes, class_name: "Scheme", foreign_key: "owning_organisation_id", dependent: :delete_all
has_many :managed_schemes, class_name: "Scheme", foreign_key: "managing_organisation_id" has_many :managed_schemes, class_name: "Scheme", foreign_key: "managing_organisation_id"
has_many :parent_organisation_relationships, foreign_key: :child_organisation_id, class_name: "OrganisationRelationship"
has_many :parent_organisations, through: :parent_organisation_relationships
has_many :child_organisation_relationships, foreign_key: :parent_organisation_id, class_name: "OrganisationRelationship"
has_many :child_organisations, through: :child_organisation_relationships
scope :search_by_name, ->(name) { where("name ILIKE ?", "%#{name}%") } scope :search_by_name, ->(name) { where("name ILIKE ?", "%#{name}%") }
scope :search_by, ->(param) { search_by_name(param) } scope :search_by, ->(param) { search_by_name(param) }
has_paper_trail has_paper_trail
auto_strip_attributes :name
PROVIDER_TYPE = { PROVIDER_TYPE = {
LA: 1, LA: 1,
PRP: 2, PRP: 2,
@ -63,15 +69,16 @@ class Organisation < ApplicationRecord
def display_attributes def display_attributes
[ [
{ name: "name", value: name, editable: true }, { name: "Name", value: name, editable: true },
{ name: "address", value: address_string, editable: true }, { name: "Address", value: address_string, editable: true },
{ name: "telephone_number", value: phone, editable: true }, { name: "Telephone_number", value: phone, editable: true },
{ name: "type", value: display_provider_type, editable: false }, { name: "Type of provider", value: display_provider_type, editable: false },
{ name: "rent_periods", value: rent_period_labels, editable: false, format: :bullet }, { name: "Registration number", value: housing_registration_no || "", editable: false },
{ name: "Rent_periods", value: rent_period_labels, editable: false, format: :bullet },
{ name: "Owns housing stock", value: holds_own_stock ? "Yes" : "No", editable: false }, { name: "Owns housing stock", value: holds_own_stock ? "Yes" : "No", editable: false },
{ name: "other_stock_owners", value: other_stock_owners, editable: false }, { name: "Other stock owners", value: other_stock_owners, editable: false },
{ name: "managing_agents", value: managing_agents, editable: false }, { name: "Managing agents", value: managing_agents, editable: false },
{ name: "data_protection_agreement", value: data_protection_agreement_string, editable: false }, { name: "Data protection agreement", value: data_protection_agreement_string, editable: false },
] ]
end end
end end

4
app/models/organisation_relationship.rb

@ -0,0 +1,4 @@
class OrganisationRelationship < ApplicationRecord
belongs_to :child_organisation, class_name: "Organisation"
belongs_to :parent_organisation, class_name: "Organisation"
end

6
app/models/scheme.rb

@ -1,8 +1,8 @@
class Scheme < ApplicationRecord class Scheme < ApplicationRecord
belongs_to :owning_organisation, class_name: "Organisation" belongs_to :owning_organisation, class_name: "Organisation"
belongs_to :managing_organisation, optional: true, class_name: "Organisation" belongs_to :managing_organisation, optional: true, class_name: "Organisation"
has_many :locations has_many :locations, dependent: :delete_all
has_many :case_logs has_many :case_logs, dependent: :delete_all
has_paper_trail has_paper_trail
@ -19,6 +19,8 @@ class Scheme < ApplicationRecord
validate :validate_confirmed validate :validate_confirmed
auto_strip_attributes :service_name
SENSITIVE = { SENSITIVE = {
No: 0, No: 0,
Yes: 1, Yes: 1,

36
app/models/user.rb

@ -1,16 +1,23 @@
class User < ApplicationRecord class User < ApplicationRecord
# Include default devise modules. Others available are: # Include default devise modules. Others available are:
# :omniauthable # :omniauthable
include Helpers::Email devise :database_authenticatable, :recoverable, :rememberable,
devise :database_authenticatable, :recoverable, :rememberable, :validatable,
:trackable, :lockable, :two_factor_authenticatable, :confirmable, :timeoutable :trackable, :lockable, :two_factor_authenticatable, :confirmable, :timeoutable
belongs_to :organisation # Marked as optional because we validate organisation_id below instead so that
has_many :owned_case_logs, through: :organisation # the error message is linked to the right field on the form
belongs_to :organisation, optional: true
has_many :owned_case_logs, through: :organisation, dependent: :delete_all
has_many :managed_case_logs, through: :organisation has_many :managed_case_logs, through: :organisation
validate :validate_email validates :name, presence: true
validates :name, :email, presence: true validates :email, presence: true
validates :email, uniqueness: { allow_blank: true, case_sensitive: true, if: :will_save_change_to_email? }
validates :email, format: { with: Devise.email_regexp, allow_blank: true, if: :will_save_change_to_email? }
validates :password, presence: { if: :password_required? }
validates :password, confirmation: { if: :password_required? }
validates :password, length: { within: Devise.password_length, allow_blank: true }
validates :organisation_id, presence: true
has_paper_trail ignore: %w[last_sign_in_at has_paper_trail ignore: %w[last_sign_in_at
current_sign_in_at current_sign_in_at
@ -27,6 +34,8 @@ class User < ApplicationRecord
has_one_time_password(encrypted: true) has_one_time_password(encrypted: true)
auto_strip_attributes :name
ROLES = { ROLES = {
data_provider: 1, data_provider: 1,
data_coordinator: 2, data_coordinator: 2,
@ -154,15 +163,12 @@ class User < ApplicationRecord
super && active? super && active?
end end
private protected
def validate_email # Checks whether a password is needed or not. For validations only.
unless email_valid?(email) # Passwords are always required if it's a new record, or if the password
if User.exists?(["email LIKE ?", "%#{email}%"]) # or confirmation are being set somewhere.
errors.add :email, I18n.t("activerecord.errors.models.user.attributes.email.taken") def password_required?
else !persisted? || !password.nil? || !password_confirmation.nil?
errors.add :email, I18n.t("activerecord.errors.models.user.attributes.email.invalid")
end
end
end end
end end

4
app/models/validations/date_validations.rb

@ -9,8 +9,8 @@ module Validations::DateValidations
record.errors.add :mrcdate, I18n.t("validations.property.mrcdate.not_first_let") record.errors.add :mrcdate, I18n.t("validations.property.mrcdate.not_first_let")
end end
if record["mrcdate"].present? && record["startdate"].present? && record["startdate"].to_date - record["mrcdate"].to_date > 730 if record["mrcdate"].present? && record["startdate"].present? && record["startdate"].to_date - record["mrcdate"].to_date > 3650
record.errors.add :mrcdate, I18n.t("validations.property.mrcdate.730_days_before_tenancy_start") record.errors.add :mrcdate, I18n.t("validations.property.mrcdate.ten_years_before_tenancy_start")
end end
end end

11
app/models/validations/soft_validations.rb

@ -62,6 +62,17 @@ module Validations::SoftValidations
end end
end end
TWO_YEARS_IN_DAYS = 730
TEN_YEARS_IN_DAYS = 3650
def major_repairs_date_in_soft_range?
mrcdate.present? && startdate.present? && mrcdate.between?(startdate.to_date - TEN_YEARS_IN_DAYS, startdate.to_date - TWO_YEARS_IN_DAYS)
end
def voiddate_in_soft_range?
voiddate.present? && startdate.present? && voiddate.between?(startdate.to_date - TEN_YEARS_IN_DAYS, startdate.to_date - TWO_YEARS_IN_DAYS)
end
private private
def details_known_or_lead_tenant?(tenant_number) def details_known_or_lead_tenant?(tenant_number)

24
app/services/archive_storage_service.rb

@ -0,0 +1,24 @@
class ArchiveStorageService < StorageService
MAX_SIZE = 50 * (1024**2) # 50MiB
def initialize(archive_io)
super()
@archive = Zip::File.open_buffer(archive_io)
end
def list_files(folder)
@archive.glob(File.join(folder, "*.*"))
.map(&:name)
end
def folder_present?(folder)
!list_files(folder).empty?
end
def get_file_io(file_name)
entry = @archive.get_entry(file_name)
raise "File too large to be extracted" if entry.size > MAX_SIZE
entry.get_input_stream
end
end

4
app/services/imports/case_logs_import_service.rb

@ -209,6 +209,8 @@ module Imports
# Soft validations can become required answers, set them to yes by default # Soft validations can become required answers, set them to yes by default
attributes["pregnancy_value_check"] = 0 attributes["pregnancy_value_check"] = 0
attributes["major_repairs_date_value_check"] = 0
attributes["void_date_value_check"] = 0
attributes["retirement_value_check"] = 0 attributes["retirement_value_check"] = 0
attributes["rent_value_check"] = 0 attributes["rent_value_check"] = 0
attributes["net_income_value_check"] = 0 attributes["net_income_value_check"] = 0
@ -273,7 +275,7 @@ module Imports
end end
def fields_not_present_in_softwire_data def fields_not_present_in_softwire_data
%w[majorrepairs illness_type_0 tshortfall_known pregnancy_value_check retirement_value_check rent_value_check net_income_value_check] %w[majorrepairs illness_type_0 tshortfall_known pregnancy_value_check retirement_value_check rent_value_check net_income_value_check major_repairs_date_value_check void_date_value_check]
end end
def check_status_completed(case_log, previous_status) def check_status_completed(case_log, previous_status)

22
app/services/imports/scheme_location_import_service.rb

@ -27,7 +27,7 @@ module Imports
}.freeze }.freeze
def create_scheme(source_scheme, attributes) def create_scheme(source_scheme, attributes)
Scheme.create!( scheme = Scheme.new(
scheme_type: attributes["scheme_type"], scheme_type: attributes["scheme_type"],
registered_under_care_act: attributes["registered_under_care_act"], registered_under_care_act: attributes["registered_under_care_act"],
support_type: attributes["support_type"], support_type: attributes["support_type"],
@ -36,7 +36,6 @@ module Imports
secondary_client_group: attributes["secondary_client_group"], secondary_client_group: attributes["secondary_client_group"],
sensitive: attributes["sensitive"], sensitive: attributes["sensitive"],
end_date: attributes["end_date"], end_date: attributes["end_date"],
confirmed: true,
# These values were set by the scheme import (management groups) # These values were set by the scheme import (management groups)
owning_organisation_id: source_scheme.owning_organisation_id, owning_organisation_id: source_scheme.owning_organisation_id,
managing_organisation_id: source_scheme.managing_organisation_id, managing_organisation_id: source_scheme.managing_organisation_id,
@ -45,10 +44,12 @@ module Imports
old_id: source_scheme.old_id, old_id: source_scheme.old_id,
old_visible_id: source_scheme.old_visible_id, old_visible_id: source_scheme.old_visible_id,
) )
confirm_scheme(scheme)
scheme.save! && scheme
end end
def update_scheme(scheme, attributes) def update_scheme(scheme, attributes)
scheme.update!( scheme.attributes = {
scheme_type: attributes["scheme_type"], scheme_type: attributes["scheme_type"],
registered_under_care_act: attributes["registered_under_care_act"], registered_under_care_act: attributes["registered_under_care_act"],
support_type: attributes["support_type"], support_type: attributes["support_type"],
@ -57,9 +58,18 @@ module Imports
secondary_client_group: attributes["secondary_client_group"], secondary_client_group: attributes["secondary_client_group"],
sensitive: attributes["sensitive"], sensitive: attributes["sensitive"],
end_date: attributes["end_date"], end_date: attributes["end_date"],
confirmed: true, }
) confirm_scheme(scheme)
scheme scheme.save! && scheme
end
def confirm_scheme(scheme)
scheme.confirmed = true
scheme.validate_confirmed
unless scheme.errors.empty?
scheme.confirmed = false
scheme.errors.clear
end
end end
def scheme_attributes(xml_doc) def scheme_attributes(xml_doc)

2
app/services/imports/user_import_service.rb

@ -13,8 +13,8 @@ module Imports
def create_user(xml_document) def create_user(xml_document)
organisation = Organisation.find_by(old_org_id: user_field_value(xml_document, "institution")) organisation = Organisation.find_by(old_org_id: user_field_value(xml_document, "institution"))
old_user_id = user_field_value(xml_document, "id") old_user_id = user_field_value(xml_document, "id")
name = user_field_value(xml_document, "full-name")
email = user_field_value(xml_document, "email").downcase.strip email = user_field_value(xml_document, "email").downcase.strip
name = user_field_value(xml_document, "full-name") || email
deleted = user_field_value(xml_document, "deleted") deleted = user_field_value(xml_document, "deleted")
if User.find_by(old_user_id:, organisation:) if User.find_by(old_user_id:, organisation:)

78
app/services/s3_storage_service.rb

@ -0,0 +1,78 @@
class S3StorageService < StorageService
attr_reader :configuration
def initialize(paas_config_service, paas_instance_name)
super()
@paas_config_service = paas_config_service
@paas_instance_name = (paas_instance_name || "").to_sym
@configuration = create_configuration
@client = create_client
end
def list_files(folder)
@client.list_objects_v2(bucket: @configuration.bucket_name, prefix: folder)
.flat_map { |response| response.contents.map(&:key) }
end
def folder_present?(folder)
response = @client.list_objects_v2(bucket: @configuration.bucket_name, prefix: folder, max_keys: 1)
response.key_count == 1
end
def get_file_io(file_name)
@client.get_object(bucket: @configuration.bucket_name, key: file_name)
.body
end
def write_file(file_name, data)
@client.put_object(
body: data,
bucket: @configuration.bucket_name,
key: file_name,
)
end
private
def create_configuration
unless @paas_config_service.config_present?
raise "No PaaS configuration present"
end
unless @paas_config_service.s3_buckets.key?(@paas_instance_name)
raise "#{@paas_instance_name} instance name could not be found"
end
bucket_config = @paas_config_service.s3_buckets[@paas_instance_name]
StorageConfiguration.new(bucket_config[:credentials])
end
def create_client
credentials =
Aws::Credentials.new(
@configuration.access_key_id,
@configuration.secret_access_key,
)
Aws::S3::Client.new(
region: @configuration.region,
credentials:,
)
end
end
class StorageConfiguration
attr_reader :access_key_id, :secret_access_key, :bucket_name, :region
def initialize(credentials)
@access_key_id = credentials[:aws_access_key_id]
@secret_access_key = credentials[:aws_secret_access_key]
@bucket_name = credentials[:bucket_name]
@region = credentials[:aws_region]
end
def ==(other)
@access_key_id == other.access_key_id &&
@secret_access_key == other.secret_access_key &&
@bucket_name == other.bucket_name &&
@region == other.region
end
end

71
app/services/storage_service.rb

@ -1,72 +1,17 @@
class StorageService class StorageService
attr_reader :configuration def list_files(_folder)
raise NotImplementedError
def initialize(paas_config_service, paas_instance_name)
@paas_config_service = paas_config_service
@paas_instance_name = (paas_instance_name || "").to_sym
@configuration = create_configuration
@client = create_client
end
def list_files(folder)
@client.list_objects_v2(bucket: @configuration.bucket_name, prefix: folder)
.flat_map { |response| response.contents.map(&:key) }
end end
def get_file_io(file_name) def folder_present?(_folder)
@client.get_object(bucket: @configuration.bucket_name, key: file_name) raise NotImplementedError
.body
end end
def write_file(file_name, data) def get_file_io(_file_name)
@client.put_object( raise NotImplementedError
body: data,
bucket: @configuration.bucket_name,
key: file_name,
)
end
private
def create_configuration
unless @paas_config_service.config_present?
raise "No PaaS configuration present"
end
unless @paas_config_service.s3_buckets.key?(@paas_instance_name)
raise "#{@paas_instance_name} instance name could not be found"
end
bucket_config = @paas_config_service.s3_buckets[@paas_instance_name]
StorageConfiguration.new(bucket_config[:credentials])
end
def create_client
credentials =
Aws::Credentials.new(
@configuration.access_key_id,
@configuration.secret_access_key,
)
Aws::S3::Client.new(
region: @configuration.region,
credentials:,
)
end
end
class StorageConfiguration
attr_reader :access_key_id, :secret_access_key, :bucket_name, :region
def initialize(credentials)
@access_key_id = credentials[:aws_access_key_id]
@secret_access_key = credentials[:aws_secret_access_key]
@bucket_name = credentials[:bucket_name]
@region = credentials[:aws_region]
end end
def ==(other) def write_file(_file_name, _data)
@access_key_id == other.access_key_id && raise NotImplementedError
@secret_access_key == other.secret_access_key &&
@bucket_name == other.bucket_name &&
@region == other.region
end end
end end

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

@ -29,7 +29,7 @@
<%= status_tag(@case_log.status) %> <%= status_tag(@case_log.status) %>
</p> </p>
<p class="govuk-body"> <p class="govuk-body">
You can <%= govuk_link_to "review and make changes to this log", "/logs/#{@case_log.id}/review" %> up to 3 months after the end of the current collection year, which closes on 31 March <%= @case_log.collection_start_year.present? ? @case_log.collection_start_year + 1 : "" %>. You can <%= govuk_link_to "review and make changes to this log", "/logs/#{@case_log.id}/review" %> until 2nd June <%= @case_log.collection_start_year.present? ? @case_log.collection_start_year + 1 : "" %>.
</p> </p>
<% end %> <% end %>
<%= render "tasklist" %> <%= render "tasklist" %>

14
app/views/form/check_answers.html.erb

@ -17,10 +17,16 @@
<% end %> <% end %>
<%= display_answered_questions_summary(subsection, @case_log, current_user) %> <%= display_answered_questions_summary(subsection, @case_log, current_user) %>
<%= render partial: "form/check_answers_summary_list", locals: { <% if any_questions_have_summary_card_number?(subsection, @case_log) %>
subsection:, <% subsection.applicable_questions(@case_log).group_by(&:check_answers_card_number).values.each do |question_group| %>
case_log: @case_log, <%= render CheckAnswersSummaryListCardComponent.new(questions: question_group, case_log: @case_log, user: current_user) %>
} %> <% end %>
<% else %>
<%= render partial: "form/check_answers_summary_list", locals: {
subsection:,
case_log: @case_log,
} %>
<% end %>
<%= form_with model: @case_log, method: "get" do |f| %> <%= form_with model: @case_log, method: "get" do |f| %>
<%= f.govuk_submit "Save and return to log" do %> <%= f.govuk_submit "Save and return to log" do %>

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

@ -11,7 +11,7 @@
<%= content_for(:title) %> <%= content_for(:title) %>
</h1> </h1>
<p class="govuk-body"> <p class="govuk-body">
You can review and make changes to this log up to 3 months after the end of the current collection year, which closes on 31 March <%= @case_log.collection_start_year.present? ? @case_log.collection_start_year + 1 : "" %>. You can review and make changes to this log until 2nd June <%= @case_log.collection_start_year.present? ? @case_log.collection_start_year + 1 : "" %>.
</p> </p>
<% @case_log.form.sections.map do |section| %> <% @case_log.form.sections.map do |section| %>
<h2 class="govuk-heading-m"><%= section.label %></h2> <h2 class="govuk-heading-m"><%= section.label %></h2>

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

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

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

@ -22,7 +22,7 @@
<% row.action( <% row.action(
visually_hidden_text: attr[:name].to_s.humanize.downcase, visually_hidden_text: attr[:name].to_s.humanize.downcase,
href: edit_organisation_path, href: edit_organisation_path,
html_attributes: { "data-qa": "change-#{attr[:name]}" }, html_attributes: { "data-qa": "change-#{attr[:name].downcase}" },
) %> ) %>
<% end %> <% end %>
<% else %> <% else %>

4
app/views/schemes/_scheme_summary_list_row.html.erb

@ -1,4 +1,4 @@
<div class="<%= "govuk-summary-list__row #{scheme.confirmed? && attribute[:name] != 'Name' ? 'govuk-summary-list__row--no-actions' : ''}" %>"> <div class="<%= "govuk-summary-list__row #{scheme.confirmed? && !can_change_scheme_answer?(attribute[:name], @scheme) ? 'govuk-summary-list__row--no-actions' : ''}" %>">
<dt class="govuk-summary-list__key"> <dt class="govuk-summary-list__key">
<%= attribute[:name].to_s %> <%= attribute[:name].to_s %>
</dt> </dt>
@ -14,7 +14,7 @@
<%= details_html(attribute) %> <%= details_html(attribute) %>
</dd> </dd>
<% end %> <% end %>
<% if !scheme.confirmed? || attribute[:name] == "Name" %> <% if can_change_scheme_answer?(attribute[:name], scheme) %>
<dd class="govuk-summary-list__actions"> <dd class="govuk-summary-list__actions">
<a class="govuk-link" href="<%= change_link %>">Change</a> <a class="govuk-link" href="<%= change_link %>">Change</a>
</dd> </dd>

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

@ -10,7 +10,7 @@
<dl class="govuk-summary-list"> <dl class="govuk-summary-list">
<% @scheme.check_details_attributes.each do |attr| %> <% @scheme.check_details_attributes.each do |attr| %>
<% next if current_user.data_coordinator? && attr[:name] == ("owned by") %> <% next if current_user.data_coordinator? && attr[:name] == ("owned by") %>
<%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_details_path(@scheme, check_answers: true) } %> <%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: @scheme.confirmed? ? scheme_edit_name_path(@scheme) : scheme_details_path(@scheme, check_answers: true) } %>
<% end %> <% end %>
<% if !@scheme.arrangement_type_same? %> <% if !@scheme.arrangement_type_same? %>
<% @scheme.check_support_services_provider_attributes.each do |attr| %> <% @scheme.check_support_services_provider_attributes.each do |attr| %>
@ -68,7 +68,7 @@
<%= table.body do |body| %> <%= table.body do |body| %>
<%= body.row do |row| %> <%= body.row do |row| %>
<% row.cell(text: location.id) %> <% row.cell(text: location.id) %>
<% row.cell(text: simple_format(location_cell(location, "/schemes/#{@scheme.id}/locations/#{location.id}/edit"), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %> <% row.cell(text: simple_format(location_cell(location, get_location_change_link_href(@scheme, location)), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
<% row.cell(text: location.units) %> <% row.cell(text: location.units) %>
<% row.cell(text: simple_format("<span>#{location.type_of_unit}</span>")) %> <% row.cell(text: simple_format("<span>#{location.type_of_unit}</span>")) %>
<% row.cell(text: location.mobility_type) %> <% row.cell(text: location.mobility_type) %>

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

@ -14,7 +14,7 @@
<%= render partial: "organisations/headings", locals: { main: "What is the other client group?", sub: @scheme.service_name } %> <%= render partial: "organisations/headings", locals: { main: "What is the other client group?", sub: @scheme.service_name } %>
<% secondary_client_group_selection = Scheme.secondary_client_groups.keys.excluding("Missing").map { |key, _| OpenStruct.new(id: key, name: key) } %> <% secondary_client_group_selection = Scheme.secondary_client_groups.keys.excluding("Missing", @scheme.primary_client_group).map { |key, _| OpenStruct.new(id: key, name: key) } %>
<%= f.govuk_collection_radio_buttons :secondary_client_group, <%= f.govuk_collection_radio_buttons :secondary_client_group,
secondary_client_group_selection, secondary_client_group_selection,
:id, :id,

6
app/views/users/new.html.erb

@ -37,11 +37,12 @@
answer_options, answer_options,
:id, :id,
:name, :name,
"data-controller": "accessible-autocomplete",
label: { text: "Organisation", size: "m" }, label: { text: "Organisation", size: "m" },
options: { disabled: [""], selected: @organisation_id ? answer_options.first : "" } %> options: { disabled: [""], selected: @organisation_id ? answer_options.first : "" } %>
<% end %> <% end %>
<% hints_for_roles = { data_provider: ["Default role for this organisation", "Can view and submit logs for this organisation"], data_coordinator: ["Can view and submit logs for this organisation and any of its managing agents", "Can manage details for this organisation", "Can manage users for this organisation"], support: nil } %> <% hints_for_roles = { data_provider: ["Can view and submit logs for this organisation"], data_coordinator: ["Can view and submit logs for this organisation and any of its managing agents", "Can manage details for this organisation", "Can manage users for this organisation"], support: nil } %>
<% roles_with_hints = current_user.assignable_roles.map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize, description: hints_for_roles[key.to_sym]) } %> <% roles_with_hints = current_user.assignable_roles.map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize, description: hints_for_roles[key.to_sym]) } %>
@ -52,7 +53,8 @@
lambda { |option| lambda { |option|
option.description&.map { |hint| content_tag(:li, hint) }&.reduce(:+) option.description&.map { |hint| content_tag(:li, hint) }&.reduce(:+)
}, },
legend: { text: "Role", size: "m" } %> legend: { text: "Role (optional)", size: "m" },
hint: { text: "You do not need to select a role if the user is a data protection officer only. You can tell us that this user is a data protection officer after you have invited them." } %>
<%= f.govuk_submit "Continue" %> <%= f.govuk_submit "Continue" %>
</div> </div>

432
config/forms/2021_2022.json

@ -34,7 +34,14 @@
"conditional_for": { "conditional_for": {
"postcode_full": [1] "postcode_full": [1]
}, },
"hidden_in_check_answers": true "hidden_in_check_answers": {
"depends_on": [{
"postcode_known": 0
},
{
"postcode_known": 1
}]
}
}, },
"postcode_full": { "postcode_full": {
"check_answer_label": "Postcode", "check_answer_label": "Postcode",
@ -764,6 +771,38 @@
} }
] ]
}, },
"void_date_value_check": {
"depends_on": [{ "voiddate_in_soft_range?": true }],
"title_text": {
"translation": "soft_validations.void_date.title_text"
},
"informative_text": {},
"questions": {
"void_date_value_check": {
"check_answer_label": "Void date confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"void_date_value_check": 0
},
{
"void_date_value_check": 1
}
]
},
"header": "Are you sure the time between these dates is correct?",
"type": "interruption_screen",
"answer_options": {
"0": {
"value": "Yes"
},
"1": {
"value": "No"
}
}
}
}
},
"new_build_handover_date": { "new_build_handover_date": {
"header": "", "header": "",
"description": "", "description": "",
@ -860,6 +899,38 @@
"rsnvac": 19 "rsnvac": 19
} }
] ]
},
"property_major_repairs_value_check": {
"depends_on": [{ "major_repairs_date_in_soft_range?": true }],
"title_text": {
"translation": "soft_validations.major_repairs_date.title_text"
},
"informative_text": {},
"questions": {
"major_repairs_date_value_check": {
"check_answer_label": "Major repairs date confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"major_repairs_date_value_check": 0
},
{
"major_repairs_date_value_check": 1
}
]
},
"header": "Are you sure the time between these dates is correct?",
"type": "interruption_screen",
"answer_options": {
"0": {
"value": "Yes"
},
"1": {
"value": "No"
}
}
}
}
} }
} }
}, },
@ -1056,6 +1127,7 @@
"header": "", "header": "",
"guidance_partial": "privacy_notice", "guidance_partial": "privacy_notice",
"check_answer_label": "Tenant has seen the privacy notice", "check_answer_label": "Tenant has seen the privacy notice",
"check_answers_card_number": 0,
"type": "checkbox", "type": "checkbox",
"answer_options": { "answer_options": {
"declaration": { "declaration": {
@ -1070,6 +1142,7 @@
"description": "", "description": "",
"questions": { "questions": {
"hhmemb": { "hhmemb": {
"check_answers_card_number": 0,
"check_answer_label": "Number of household members", "check_answer_label": "Number of household members",
"header": "How many people live in the household for this letting?", "header": "How many people live in the household for this letting?",
"hint_text": "You can provide details for a maximum of 8 people.", "hint_text": "You can provide details for a maximum of 8 people.",
@ -1122,7 +1195,11 @@
} }
}, },
"females_in_soft_age_range_in_pregnant_household_lead_hhmemb_value_check": { "females_in_soft_age_range_in_pregnant_household_lead_hhmemb_value_check": {
"depends_on": [{ "female_in_pregnant_household_in_soft_validation_range?": true }], "depends_on": [
{
"female_in_pregnant_household_in_soft_validation_range?": true
}
],
"title_text": { "title_text": {
"translation": "soft_validations.pregnancy.title", "translation": "soft_validations.pregnancy.title",
"arguments": [ "arguments": [
@ -1165,6 +1242,7 @@
"description": "", "description": "",
"questions": { "questions": {
"age1_known": { "age1_known": {
"check_answers_card_number": 1,
"header": "Do you know the lead tenant’s age?", "header": "Do you know the lead tenant’s age?",
"hint_text": "The ’lead’ or ’main’ tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The ’lead’ or ’main’ tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
"type": "radio", "type": "radio",
@ -1191,6 +1269,7 @@
} }
}, },
"age1": { "age1": {
"check_answers_card_number": 1,
"header": "Age", "header": "Age",
"check_answer_label": "Lead tenant’s age", "check_answer_label": "Lead tenant’s age",
"type": "numeric", "type": "numeric",
@ -1248,7 +1327,11 @@
} }
}, },
"females_in_soft_age_range_in_pregnant_household_lead_age_value_check": { "females_in_soft_age_range_in_pregnant_household_lead_age_value_check": {
"depends_on": [{ "female_in_pregnant_household_in_soft_validation_range?": true }], "depends_on": [
{
"female_in_pregnant_household_in_soft_validation_range?": true
}
],
"title_text": { "title_text": {
"translation": "soft_validations.pregnancy.title", "translation": "soft_validations.pregnancy.title",
"arguments": [ "arguments": [
@ -1292,6 +1375,7 @@
"questions": { "questions": {
"sex1": { "sex1": {
"check_answer_label": "Lead tenant’s gender identity", "check_answer_label": "Lead tenant’s gender identity",
"check_answers_card_number": 1,
"header": "Which of these best describes the lead tenant’s gender identity?", "header": "Which of these best describes the lead tenant’s gender identity?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
"type": "radio", "type": "radio",
@ -1356,7 +1440,11 @@
} }
}, },
"females_in_soft_age_range_in_pregnant_household_lead_value_check": { "females_in_soft_age_range_in_pregnant_household_lead_value_check": {
"depends_on": [{ "female_in_pregnant_household_in_soft_validation_range?": true }], "depends_on": [
{
"female_in_pregnant_household_in_soft_validation_range?": true
}
],
"title_text": { "title_text": {
"translation": "soft_validations.pregnancy.title", "translation": "soft_validations.pregnancy.title",
"arguments": [ "arguments": [
@ -1400,6 +1488,7 @@
"questions": { "questions": {
"ethnic_group": { "ethnic_group": {
"check_answer_label": "Lead tenant’s ethnic group", "check_answer_label": "Lead tenant’s ethnic group",
"check_answers_card_number": 1,
"header": "What is the lead tenant’s ethnic group?", "header": "What is the lead tenant’s ethnic group?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
"type": "radio", "type": "radio",
@ -1435,6 +1524,7 @@
"description": "", "description": "",
"questions": { "questions": {
"ethnic": { "ethnic": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s ethnic background", "check_answer_label": "Lead tenant’s ethnic background",
"header": "Which of the following best describes the lead tenant’s Arab background?", "header": "Which of the following best describes the lead tenant’s Arab background?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
@ -1456,6 +1546,7 @@
"description": "", "description": "",
"questions": { "questions": {
"ethnic": { "ethnic": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s ethnic background", "check_answer_label": "Lead tenant’s ethnic background",
"header": "Which of the following best describes the lead tenant’s Asian or Asian British background?", "header": "Which of the following best describes the lead tenant’s Asian or Asian British background?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
@ -1486,6 +1577,7 @@
"description": "", "description": "",
"questions": { "questions": {
"ethnic": { "ethnic": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s ethnic background", "check_answer_label": "Lead tenant’s ethnic background",
"header": "Which of the following best describes the lead tenant’s Black, African, Caribbean or Black British background?", "header": "Which of the following best describes the lead tenant’s Black, African, Caribbean or Black British background?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
@ -1510,6 +1602,7 @@
"description": "", "description": "",
"questions": { "questions": {
"ethnic": { "ethnic": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s ethnic background", "check_answer_label": "Lead tenant’s ethnic background",
"header": "Which of the following best describes the lead tenant’s Mixed or Multiple ethnic groups background?", "header": "Which of the following best describes the lead tenant’s Mixed or Multiple ethnic groups background?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
@ -1537,6 +1630,7 @@
"description": "", "description": "",
"questions": { "questions": {
"ethnic": { "ethnic": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s ethnic background", "check_answer_label": "Lead tenant’s ethnic background",
"header": "Which of the following best describes the lead tenant’s White background?", "header": "Which of the following best describes the lead tenant’s White background?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
@ -1564,6 +1658,7 @@
"description": "", "description": "",
"questions": { "questions": {
"national": { "national": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s nationality", "check_answer_label": "Lead tenant’s nationality",
"header": "What is the lead tenant’s nationality?", "header": "What is the lead tenant’s nationality?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
@ -1633,6 +1728,7 @@
"ecstat1": { "ecstat1": {
"check_answer_label": "Lead tenant’s working situation", "check_answer_label": "Lead tenant’s working situation",
"header": "Which of these best describes the lead tenant’s working situation?", "header": "Which of these best describes the lead tenant’s working situation?",
"check_answers_card_number": 1,
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -1703,8 +1799,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?", "header": "Are you sure this person is retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -1749,8 +1854,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?", "header": "Are you sure this person isn’t retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -1770,6 +1884,7 @@
"questions": { "questions": {
"details_known_2": { "details_known_2": {
"check_answer_label": "Details known for person 2", "check_answer_label": "Details known for person 2",
"check_answers_card_number": 2,
"header": "Do you know details for person 2?", "header": "Do you know details for person 2?",
"hint_text": "You must provide details for everyone in the household if you know them.", "hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio", "type": "radio",
@ -1813,6 +1928,7 @@
"questions": { "questions": {
"relat2": { "relat2": {
"check_answer_label": "Person 2’s relationship to the lead tenant", "check_answer_label": "Person 2’s relationship to the lead tenant",
"check_answers_card_number": 2,
"header": "What is person 2’s relationship to the lead tenant?", "header": "What is person 2’s relationship to the lead tenant?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -1848,6 +1964,7 @@
"questions": { "questions": {
"age2_known": { "age2_known": {
"header": "Do you know person 2’s age?", "header": "Do you know person 2’s age?",
"check_answers_card_number": 2,
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -1875,6 +1992,7 @@
"age2": { "age2": {
"header": "Age", "header": "Age",
"check_answer_label": "Person 2’s age", "check_answer_label": "Person 2’s age",
"check_answers_card_number": 2,
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"max": 120, "max": 120,
@ -1986,6 +2104,7 @@
"sex2": { "sex2": {
"check_answer_label": "Person 2’s gender identity", "check_answer_label": "Person 2’s gender identity",
"header": "Which of these best describes person 2’s gender identity?", "header": "Which of these best describes person 2’s gender identity?",
"check_answers_card_number": 2,
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -2107,6 +2226,7 @@
"questions": { "questions": {
"ecstat2": { "ecstat2": {
"check_answer_label": "Person 2’s working situation", "check_answer_label": "Person 2’s working situation",
"check_answers_card_number": 2,
"header": "Which of these best describes person 2’s working situation?", "header": "Which of these best describes person 2’s working situation?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -2197,8 +2317,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?", "header": "Are you sure this person is retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -2243,8 +2372,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?", "header": "Are you sure this person isn’t retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -2264,6 +2402,7 @@
"questions": { "questions": {
"details_known_3": { "details_known_3": {
"check_answer_label": "Details known for person 3", "check_answer_label": "Details known for person 3",
"check_answers_card_number": 3,
"header": "Do you know details for person 3?", "header": "Do you know details for person 3?",
"hint_text": "You must provide details for everyone in the household if you know them.", "hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio", "type": "radio",
@ -2304,6 +2443,7 @@
"questions": { "questions": {
"relat3": { "relat3": {
"check_answer_label": "Person 3’s relationship to the lead tenant", "check_answer_label": "Person 3’s relationship to the lead tenant",
"check_answers_card_number": 3,
"header": "What is person 3’s relationship to the lead tenant?", "header": "What is person 3’s relationship to the lead tenant?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -2339,6 +2479,7 @@
"questions": { "questions": {
"age3_known": { "age3_known": {
"header": "Do you know person 3’s age?", "header": "Do you know person 3’s age?",
"check_answers_card_number": 3,
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -2366,6 +2507,7 @@
"age3": { "age3": {
"header": "Age", "header": "Age",
"check_answer_label": "Person 3’s age", "check_answer_label": "Person 3’s age",
"check_answers_card_number": 3,
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"max": 120, "max": 120,
@ -2476,6 +2618,7 @@
"questions": { "questions": {
"sex3": { "sex3": {
"check_answer_label": "Person 3’s gender identity", "check_answer_label": "Person 3’s gender identity",
"check_answers_card_number": 3,
"header": "Which of these best describes person 3’s gender identity?", "header": "Which of these best describes person 3’s gender identity?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -2598,6 +2741,7 @@
"questions": { "questions": {
"ecstat3": { "ecstat3": {
"check_answer_label": "Person 3’s working situation", "check_answer_label": "Person 3’s working situation",
"check_answers_card_number": 3,
"header": "Which of these best describes person 3’s working situation?", "header": "Which of these best describes person 3’s working situation?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -2688,8 +2832,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?", "header": "Are you sure this person is retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -2734,8 +2887,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?", "header": "Are you sure this person isn’t retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -2755,6 +2917,7 @@
"questions": { "questions": {
"details_known_4": { "details_known_4": {
"check_answer_label": "Details known for person 4", "check_answer_label": "Details known for person 4",
"check_answers_card_number": 4,
"header": "Do you know details for person 4?", "header": "Do you know details for person 4?",
"hint_text": "You must provide details for everyone in the household if you know them.", "hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio", "type": "radio",
@ -2792,6 +2955,7 @@
"questions": { "questions": {
"relat4": { "relat4": {
"check_answer_label": "Person 4’s relationship to the lead tenant", "check_answer_label": "Person 4’s relationship to the lead tenant",
"check_answers_card_number": 4,
"header": "What is person 4’s relationship to the lead tenant?", "header": "What is person 4’s relationship to the lead tenant?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -2827,6 +2991,7 @@
"questions": { "questions": {
"age4_known": { "age4_known": {
"header": "Do you know person 4’s age?", "header": "Do you know person 4’s age?",
"check_answers_card_number": 4,
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -2854,6 +3019,7 @@
"age4": { "age4": {
"header": "Age", "header": "Age",
"check_answer_label": "Person 4’s age", "check_answer_label": "Person 4’s age",
"check_answers_card_number": 4,
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"max": 120, "max": 120,
@ -2964,6 +3130,7 @@
"questions": { "questions": {
"sex4": { "sex4": {
"check_answer_label": "Person 4’s gender identity", "check_answer_label": "Person 4’s gender identity",
"check_answers_card_number": 4,
"header": "Which of these best describes person 4’s gender identity?", "header": "Which of these best describes person 4’s gender identity?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -3086,6 +3253,7 @@
"questions": { "questions": {
"ecstat4": { "ecstat4": {
"check_answer_label": "Person 4’s working situation", "check_answer_label": "Person 4’s working situation",
"check_answers_card_number": 4,
"header": "Which of these best describes person 4’s working situation?", "header": "Which of these best describes person 4’s working situation?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -3176,8 +3344,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?", "header": "Are you sure this person is retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -3222,8 +3399,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?", "header": "Are you sure this person isn’t retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -3243,6 +3429,7 @@
"questions": { "questions": {
"details_known_5": { "details_known_5": {
"check_answer_label": "Details known for person 5", "check_answer_label": "Details known for person 5",
"check_answers_card_number": 5,
"header": "Do you know details for person 5?", "header": "Do you know details for person 5?",
"hint_text": "You must provide details for everyone in the household if you know them.", "hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio", "type": "radio",
@ -3277,6 +3464,7 @@
"questions": { "questions": {
"relat5": { "relat5": {
"check_answer_label": "Person 5’s relationship to the lead tenant", "check_answer_label": "Person 5’s relationship to the lead tenant",
"check_answers_card_number": 5,
"header": "What is person 5’s relationship to the lead tenant?", "header": "What is person 5’s relationship to the lead tenant?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -3312,6 +3500,7 @@
"questions": { "questions": {
"age5_known": { "age5_known": {
"header": "Do you know person 5’s age?", "header": "Do you know person 5’s age?",
"check_answers_card_number": 5,
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -3339,6 +3528,7 @@
"age5": { "age5": {
"header": "Age", "header": "Age",
"check_answer_label": "Person 5’s age", "check_answer_label": "Person 5’s age",
"check_answers_card_number": 5,
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"max": 120, "max": 120,
@ -3449,6 +3639,7 @@
"questions": { "questions": {
"sex5": { "sex5": {
"check_answer_label": "Person 5’s gender identity", "check_answer_label": "Person 5’s gender identity",
"check_answers_card_number": 5,
"header": "Which of these best describes person 5’s gender identity?", "header": "Which of these best describes person 5’s gender identity?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -3571,6 +3762,7 @@
"questions": { "questions": {
"ecstat5": { "ecstat5": {
"check_answer_label": "Person 5’s working situation", "check_answer_label": "Person 5’s working situation",
"check_answers_card_number": 5,
"header": "Which of these best describes person 5’s working situation?", "header": "Which of these best describes person 5’s working situation?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -3661,8 +3853,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?", "header": "Are you sure this person is retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -3707,8 +3908,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?", "header": "Are you sure this person isn’t retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -3728,6 +3938,7 @@
"questions": { "questions": {
"details_known_6": { "details_known_6": {
"check_answer_label": "Details known for person 6", "check_answer_label": "Details known for person 6",
"check_answers_card_number": 6,
"header": "Do you know details for person 6?", "header": "Do you know details for person 6?",
"hint_text": "You must provide details for everyone in the household if you know them.", "hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio", "type": "radio",
@ -3759,6 +3970,7 @@
"questions": { "questions": {
"relat6": { "relat6": {
"check_answer_label": "Person 6’s relationship to the lead tenant", "check_answer_label": "Person 6’s relationship to the lead tenant",
"check_answers_card_number": 6,
"header": "What is person 6’s relationship to the lead tenant?", "header": "What is person 6’s relationship to the lead tenant?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -3794,6 +4006,7 @@
"questions": { "questions": {
"age6_known": { "age6_known": {
"header": "Do you know person 6’s age?", "header": "Do you know person 6’s age?",
"check_answers_card_number": 6,
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -3821,6 +4034,7 @@
"age6": { "age6": {
"header": "Age", "header": "Age",
"check_answer_label": "Person 6’s age", "check_answer_label": "Person 6’s age",
"check_answers_card_number": 6,
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"max": 120, "max": 120,
@ -3931,6 +4145,7 @@
"questions": { "questions": {
"sex6": { "sex6": {
"check_answer_label": "Person 6’s gender identity", "check_answer_label": "Person 6’s gender identity",
"check_answers_card_number": 6,
"header": "Which of these best describes person 6’s gender identity?", "header": "Which of these best describes person 6’s gender identity?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -4053,6 +4268,7 @@
"questions": { "questions": {
"ecstat6": { "ecstat6": {
"check_answer_label": "Person 6’s working situation", "check_answer_label": "Person 6’s working situation",
"check_answers_card_number": 6,
"header": "Which of these best describes person 6’s working situation?", "header": "Which of these best describes person 6’s working situation?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -4143,8 +4359,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?", "header": "Are you sure this person is retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -4189,8 +4414,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?", "header": "Are you sure this person isn’t retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -4210,6 +4444,7 @@
"questions": { "questions": {
"details_known_7": { "details_known_7": {
"check_answer_label": "Details known for person 7", "check_answer_label": "Details known for person 7",
"check_answers_card_number": 7,
"header": "Do you know details for person 7?", "header": "Do you know details for person 7?",
"hint_text": "You must provide details for everyone in the household if you know them.", "hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio", "type": "radio",
@ -4238,6 +4473,7 @@
"questions": { "questions": {
"relat7": { "relat7": {
"check_answer_label": "Person 7’s relationship to the lead tenant", "check_answer_label": "Person 7’s relationship to the lead tenant",
"check_answers_card_number": 7,
"header": "What is person 7’s relationship to the lead tenant?", "header": "What is person 7’s relationship to the lead tenant?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -4273,6 +4509,7 @@
"questions": { "questions": {
"age7_known": { "age7_known": {
"header": "Do you know person 7’s age?", "header": "Do you know person 7’s age?",
"check_answers_card_number": 7,
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -4300,6 +4537,7 @@
"age7": { "age7": {
"header": "Age", "header": "Age",
"check_answer_label": "Person 7’s age", "check_answer_label": "Person 7’s age",
"check_answers_card_number": 7,
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"max": 120, "max": 120,
@ -4410,6 +4648,7 @@
"questions": { "questions": {
"sex7": { "sex7": {
"check_answer_label": "Person 7’s gender identity", "check_answer_label": "Person 7’s gender identity",
"check_answers_card_number": 7,
"header": "Which of these best describes person 7’s gender identity?", "header": "Which of these best describes person 7’s gender identity?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -4532,6 +4771,7 @@
"questions": { "questions": {
"ecstat7": { "ecstat7": {
"check_answer_label": "Person 7’s working situation", "check_answer_label": "Person 7’s working situation",
"check_answers_card_number": 7,
"header": "Which of these best describes person 7’s working situation?", "header": "Which of these best describes person 7’s working situation?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -4622,8 +4862,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?", "header": "Are you sure this person is retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -4668,8 +4917,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?", "header": "Are you sure this person isn’t retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -4689,6 +4947,7 @@
"questions": { "questions": {
"details_known_8": { "details_known_8": {
"check_answer_label": "Details known for person 8", "check_answer_label": "Details known for person 8",
"check_answers_card_number": 8,
"header": "Do you know details for person 8?", "header": "Do you know details for person 8?",
"hint_text": "You must provide details for everyone in the household if you know them.", "hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio", "type": "radio",
@ -4714,6 +4973,7 @@
"questions": { "questions": {
"relat8": { "relat8": {
"check_answer_label": "Person 8’s relationship to the lead tenant", "check_answer_label": "Person 8’s relationship to the lead tenant",
"check_answers_card_number": 8,
"header": "What is person 8’s relationship to the lead tenant?", "header": "What is person 8’s relationship to the lead tenant?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -4749,6 +5009,7 @@
"questions": { "questions": {
"age8_known": { "age8_known": {
"header": "Do you know person 8’s age?", "header": "Do you know person 8’s age?",
"check_answers_card_number": 8,
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -4776,6 +5037,7 @@
"age8": { "age8": {
"header": "Age", "header": "Age",
"check_answer_label": "Person 8’s age", "check_answer_label": "Person 8’s age",
"check_answers_card_number": 8,
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"max": 120, "max": 120,
@ -4886,6 +5148,7 @@
"questions": { "questions": {
"sex8": { "sex8": {
"check_answer_label": "Person 8’s gender identity", "check_answer_label": "Person 8’s gender identity",
"check_answers_card_number": 8,
"header": "Which of these best describes person 8’s gender identity?", "header": "Which of these best describes person 8’s gender identity?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -5008,6 +5271,7 @@
"questions": { "questions": {
"ecstat8": { "ecstat8": {
"check_answer_label": "Person 8’s working situation", "check_answer_label": "Person 8’s working situation",
"check_answers_card_number": 8,
"header": "Which of these best describes person 8’s working situation?", "header": "Which of these best describes person 8’s working situation?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -5098,8 +5362,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?", "header": "Are you sure this person is retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -5144,8 +5417,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?", "header": "Are you sure this person isn’t retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -5362,8 +5644,16 @@
}, },
"questions": { "questions": {
"pregnancy_value_check": { "pregnancy_value_check": {
"check_answer_label": "Pregnancy soft validation", "check_answer_label": "Pregnancy confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [{
"pregnancy_value_check": 0
},
{
"pregnancy_value_check": 1
}
]
},
"header": "Are you sure this is correct?", "header": "Are you sure this is correct?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -6856,8 +7146,17 @@
}, },
"questions": { "questions": {
"net_income_value_check": { "net_income_value_check": {
"check_answer_label": "Net income soft validation", "check_answer_label": "Net income confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"net_income_value_check": 0
},
{
"net_income_value_check": 1
}
]
},
"header": "Are you sure this is correct?", "header": "Are you sure this is correct?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -7760,8 +8059,17 @@
}, },
"questions": { "questions": {
"rent_value_check": { "rent_value_check": {
"check_answer_label": "Rent soft validation", "check_answer_label": "Total rent confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"rent_value_check": 0
},
{
"rent_value_check": 1
}
]
},
"header": "Are you sure this is correct?", "header": "Are you sure this is correct?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -7804,8 +8112,17 @@
}, },
"questions": { "questions": {
"rent_value_check": { "rent_value_check": {
"check_answer_label": "Rent soft validation", "check_answer_label": "Total rent confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"rent_value_check": 0
},
{
"rent_value_check": 1
}
]
},
"header": "Are you sure this is correct?", "header": "Are you sure this is correct?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -7824,7 +8141,7 @@
"description": "", "description": "",
"questions": { "questions": {
"hbrentshortfall": { "hbrentshortfall": {
"check_answer_label": "Outstanding amount for basic rent and charges", "check_answer_label": "Any outstanding amount for basic rent and charges",
"header": "After the household has received any housing-related benefits, will they still need to pay for rent and charges?", "header": "After the household has received any housing-related benefits, will they still need to pay for rent and charges?",
"hint_text": "Also known as the ‘outstanding amount’.", "hint_text": "Also known as the ‘outstanding amount’.",
"type": "radio", "type": "radio",
@ -7868,17 +8185,15 @@
} }
] ]
}, },
"outstanding_amount_known": { "outstanding_amount": {
"header": "", "header": "",
"description": "", "description": "",
"questions": { "questions": {
"tshortfall_known": { "tshortfall_known": {
"check_answer_label": "", "check_answer_label": "Do you know the outstanding amount?",
"header": "", "header": "Can you estimate the outstanding amount?",
"hint_text": "", "hint_text": "You only need to give an approximate figure.",
"hidden_in_check_answers": true,
"type": "radio", "type": "radio",
"derived": true,
"answer_options": { "answer_options": {
"0": { "0": {
"value": "Yes" "value": "Yes"
@ -7886,19 +8201,14 @@
"1": { "1": {
"value": "No" "value": "No"
} }
},
"conditional_for": {
"tshortfall": [0]
} }
} },
},
"depends_on": [false]
},
"outstanding_amount": {
"header": "",
"description": "",
"questions": {
"tshortfall": { "tshortfall": {
"check_answer_label": "Estimated outstanding amount", "check_answer_label": "Estimated outstanding amount",
"header": "What do you expect the outstanding amount to be?", "header": "Estimated outstanding amount",
"hint_text": "Give an estimated amount if you don’t know the exact figure.",
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"step": 0.01, "step": 0.01,

436
config/forms/2022_2023.json

@ -34,7 +34,14 @@
"conditional_for": { "conditional_for": {
"postcode_full": [1] "postcode_full": [1]
}, },
"hidden_in_check_answers": true "hidden_in_check_answers": {
"depends_on": [{
"postcode_known": 0
},
{
"postcode_known": 1
}]
}
}, },
"postcode_full": { "postcode_full": {
"check_answer_label": "Postcode", "check_answer_label": "Postcode",
@ -764,6 +771,38 @@
} }
] ]
}, },
"void_date_value_check": {
"depends_on": [{ "voiddate_in_soft_range?": true }],
"title_text": {
"translation": "soft_validations.void_date.title_text"
},
"informative_text": {},
"questions": {
"void_date_value_check": {
"check_answer_label": "Void date confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"void_date_value_check": 0
},
{
"void_date_value_check": 1
}
]
},
"header": "Are you sure the time between these dates is correct?",
"type": "interruption_screen",
"answer_options": {
"0": {
"value": "Yes"
},
"1": {
"value": "No"
}
}
}
}
},
"new_build_handover_date": { "new_build_handover_date": {
"header": "", "header": "",
"description": "", "description": "",
@ -860,6 +899,38 @@
"rsnvac": 19 "rsnvac": 19
} }
] ]
},
"property_major_repairs_value_check": {
"depends_on": [{ "major_repairs_date_in_soft_range?": true }],
"title_text": {
"translation": "soft_validations.major_repairs_date.title_text"
},
"informative_text": {},
"questions": {
"major_repairs_date_value_check": {
"check_answer_label": "Major repairs date confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"major_repairs_date_value_check": 0
},
{
"major_repairs_date_value_check": 1
}
]
},
"header": "Are you sure the time between these dates is correct?",
"type": "interruption_screen",
"answer_options": {
"0": {
"value": "Yes"
},
"1": {
"value": "No"
}
}
}
}
} }
} }
}, },
@ -1091,6 +1162,7 @@
"header": "", "header": "",
"guidance_partial": "privacy_notice", "guidance_partial": "privacy_notice",
"check_answer_label": "Tenant has seen the privacy notice", "check_answer_label": "Tenant has seen the privacy notice",
"check_answers_card_number": 0,
"type": "checkbox", "type": "checkbox",
"answer_options": { "answer_options": {
"declaration": { "declaration": {
@ -1105,6 +1177,7 @@
"description": "", "description": "",
"questions": { "questions": {
"hhmemb": { "hhmemb": {
"check_answers_card_number": 0,
"check_answer_label": "Number of household members", "check_answer_label": "Number of household members",
"header": "How many people live in the household for this letting?", "header": "How many people live in the household for this letting?",
"hint_text": "You can provide details for a maximum of 8 people.", "hint_text": "You can provide details for a maximum of 8 people.",
@ -1157,7 +1230,11 @@
} }
}, },
"females_in_soft_age_range_in_pregnant_household_lead_hhmemb_value_check": { "females_in_soft_age_range_in_pregnant_household_lead_hhmemb_value_check": {
"depends_on": [{ "female_in_pregnant_household_in_soft_validation_range?": true }], "depends_on": [
{
"female_in_pregnant_household_in_soft_validation_range?": true
}
],
"title_text": { "title_text": {
"translation": "soft_validations.pregnancy.title", "translation": "soft_validations.pregnancy.title",
"arguments": [ "arguments": [
@ -1200,6 +1277,7 @@
"description": "", "description": "",
"questions": { "questions": {
"age1_known": { "age1_known": {
"check_answers_card_number": 1,
"header": "Do you know the lead tenant’s age?", "header": "Do you know the lead tenant’s age?",
"hint_text": "The ’lead’ or ’main’ tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The ’lead’ or ’main’ tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
"type": "radio", "type": "radio",
@ -1226,6 +1304,7 @@
} }
}, },
"age1": { "age1": {
"check_answers_card_number": 1,
"header": "Age", "header": "Age",
"check_answer_label": "Lead tenant’s age", "check_answer_label": "Lead tenant’s age",
"type": "numeric", "type": "numeric",
@ -1283,7 +1362,11 @@
} }
}, },
"females_in_soft_age_range_in_pregnant_household_lead_age_value_check": { "females_in_soft_age_range_in_pregnant_household_lead_age_value_check": {
"depends_on": [{ "female_in_pregnant_household_in_soft_validation_range?": true }], "depends_on": [
{
"female_in_pregnant_household_in_soft_validation_range?": true
}
],
"title_text": { "title_text": {
"translation": "soft_validations.pregnancy.title", "translation": "soft_validations.pregnancy.title",
"arguments": [ "arguments": [
@ -1327,6 +1410,7 @@
"questions": { "questions": {
"sex1": { "sex1": {
"check_answer_label": "Lead tenant’s gender identity", "check_answer_label": "Lead tenant’s gender identity",
"check_answers_card_number": 1,
"header": "Which of these best describes the lead tenant’s gender identity?", "header": "Which of these best describes the lead tenant’s gender identity?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
"type": "radio", "type": "radio",
@ -1391,7 +1475,11 @@
} }
}, },
"females_in_soft_age_range_in_pregnant_household_lead_value_check": { "females_in_soft_age_range_in_pregnant_household_lead_value_check": {
"depends_on": [{ "female_in_pregnant_household_in_soft_validation_range?": true }], "depends_on": [
{
"female_in_pregnant_household_in_soft_validation_range?": true
}
],
"title_text": { "title_text": {
"translation": "soft_validations.pregnancy.title", "translation": "soft_validations.pregnancy.title",
"arguments": [ "arguments": [
@ -1435,6 +1523,7 @@
"questions": { "questions": {
"ethnic_group": { "ethnic_group": {
"check_answer_label": "Lead tenant’s ethnic group", "check_answer_label": "Lead tenant’s ethnic group",
"check_answers_card_number": 1,
"header": "What is the lead tenant’s ethnic group?", "header": "What is the lead tenant’s ethnic group?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
"type": "radio", "type": "radio",
@ -1470,6 +1559,7 @@
"description": "", "description": "",
"questions": { "questions": {
"ethnic": { "ethnic": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s ethnic background", "check_answer_label": "Lead tenant’s ethnic background",
"header": "Which of the following best describes the lead tenant’s Arab background?", "header": "Which of the following best describes the lead tenant’s Arab background?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
@ -1491,6 +1581,7 @@
"description": "", "description": "",
"questions": { "questions": {
"ethnic": { "ethnic": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s ethnic background", "check_answer_label": "Lead tenant’s ethnic background",
"header": "Which of the following best describes the lead tenant’s Asian or Asian British background?", "header": "Which of the following best describes the lead tenant’s Asian or Asian British background?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
@ -1521,6 +1612,7 @@
"description": "", "description": "",
"questions": { "questions": {
"ethnic": { "ethnic": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s ethnic background", "check_answer_label": "Lead tenant’s ethnic background",
"header": "Which of the following best describes the lead tenant’s Black, African, Caribbean or Black British background?", "header": "Which of the following best describes the lead tenant’s Black, African, Caribbean or Black British background?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
@ -1545,6 +1637,7 @@
"description": "", "description": "",
"questions": { "questions": {
"ethnic": { "ethnic": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s ethnic background", "check_answer_label": "Lead tenant’s ethnic background",
"header": "Which of the following best describes the lead tenant’s Mixed or Multiple ethnic groups background?", "header": "Which of the following best describes the lead tenant’s Mixed or Multiple ethnic groups background?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
@ -1572,6 +1665,7 @@
"description": "", "description": "",
"questions": { "questions": {
"ethnic": { "ethnic": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s ethnic background", "check_answer_label": "Lead tenant’s ethnic background",
"header": "Which of the following best describes the lead tenant’s White background?", "header": "Which of the following best describes the lead tenant’s White background?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
@ -1599,6 +1693,7 @@
"description": "", "description": "",
"questions": { "questions": {
"national": { "national": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s nationality", "check_answer_label": "Lead tenant’s nationality",
"header": "What is the lead tenant’s nationality?", "header": "What is the lead tenant’s nationality?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
@ -1632,6 +1727,7 @@
"ecstat1": { "ecstat1": {
"check_answer_label": "Lead tenant’s working situation", "check_answer_label": "Lead tenant’s working situation",
"header": "Which of these best describes the lead tenant’s working situation?", "header": "Which of these best describes the lead tenant’s working situation?",
"check_answers_card_number": 1,
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -1702,8 +1798,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?", "header": "Are you sure this person is retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -1718,7 +1823,9 @@
} }
}, },
"lead_tenant_over_retirement_value_check": { "lead_tenant_over_retirement_value_check": {
"depends_on": [{ "person_1_not_retired_over_soft_max_age?": true }], "depends_on": [
{ "person_1_not_retired_over_soft_max_age?": true }
],
"title_text": { "title_text": {
"translation": "soft_validations.retirement.max.title", "translation": "soft_validations.retirement.max.title",
"arguments": [ "arguments": [
@ -1746,8 +1853,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?", "header": "Are you sure this person isn’t retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -1767,6 +1883,7 @@
"questions": { "questions": {
"details_known_2": { "details_known_2": {
"check_answer_label": "Details known for person 2", "check_answer_label": "Details known for person 2",
"check_answers_card_number": 2,
"header": "Do you know details for person 2?", "header": "Do you know details for person 2?",
"hint_text": "You must provide details for everyone in the household if you know them.", "hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio", "type": "radio",
@ -1810,6 +1927,7 @@
"questions": { "questions": {
"relat2": { "relat2": {
"check_answer_label": "Person 2’s relationship to the lead tenant", "check_answer_label": "Person 2’s relationship to the lead tenant",
"check_answers_card_number": 2,
"header": "What is person 2’s relationship to the lead tenant?", "header": "What is person 2’s relationship to the lead tenant?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -1845,6 +1963,7 @@
"questions": { "questions": {
"age2_known": { "age2_known": {
"header": "Do you know person 2’s age?", "header": "Do you know person 2’s age?",
"check_answers_card_number": 2,
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -1872,6 +1991,7 @@
"age2": { "age2": {
"header": "Age", "header": "Age",
"check_answer_label": "Person 2’s age", "check_answer_label": "Person 2’s age",
"check_answers_card_number": 2,
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"max": 120, "max": 120,
@ -1983,6 +2103,7 @@
"sex2": { "sex2": {
"check_answer_label": "Person 2’s gender identity", "check_answer_label": "Person 2’s gender identity",
"header": "Which of these best describes person 2’s gender identity?", "header": "Which of these best describes person 2’s gender identity?",
"check_answers_card_number": 2,
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -2104,6 +2225,7 @@
"questions": { "questions": {
"ecstat2": { "ecstat2": {
"check_answer_label": "Person 2’s working situation", "check_answer_label": "Person 2’s working situation",
"check_answers_card_number": 2,
"header": "Which of these best describes person 2’s working situation?", "header": "Which of these best describes person 2’s working situation?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -2194,8 +2316,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?", "header": "Are you sure this person is retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -2240,8 +2371,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?", "header": "Are you sure this person isn’t retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -2261,6 +2401,7 @@
"questions": { "questions": {
"details_known_3": { "details_known_3": {
"check_answer_label": "Details known for person 3", "check_answer_label": "Details known for person 3",
"check_answers_card_number": 3,
"header": "Do you know details for person 3?", "header": "Do you know details for person 3?",
"hint_text": "You must provide details for everyone in the household if you know them.", "hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio", "type": "radio",
@ -2301,6 +2442,7 @@
"questions": { "questions": {
"relat3": { "relat3": {
"check_answer_label": "Person 3’s relationship to the lead tenant", "check_answer_label": "Person 3’s relationship to the lead tenant",
"check_answers_card_number": 3,
"header": "What is person 3’s relationship to the lead tenant?", "header": "What is person 3’s relationship to the lead tenant?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -2336,6 +2478,7 @@
"questions": { "questions": {
"age3_known": { "age3_known": {
"header": "Do you know person 3’s age?", "header": "Do you know person 3’s age?",
"check_answers_card_number": 3,
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -2363,6 +2506,7 @@
"age3": { "age3": {
"header": "Age", "header": "Age",
"check_answer_label": "Person 3’s age", "check_answer_label": "Person 3’s age",
"check_answers_card_number": 3,
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"max": 120, "max": 120,
@ -2473,6 +2617,7 @@
"questions": { "questions": {
"sex3": { "sex3": {
"check_answer_label": "Person 3’s gender identity", "check_answer_label": "Person 3’s gender identity",
"check_answers_card_number": 3,
"header": "Which of these best describes person 3’s gender identity?", "header": "Which of these best describes person 3’s gender identity?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -2595,6 +2740,7 @@
"questions": { "questions": {
"ecstat3": { "ecstat3": {
"check_answer_label": "Person 3’s working situation", "check_answer_label": "Person 3’s working situation",
"check_answers_card_number": 3,
"header": "Which of these best describes person 3’s working situation?", "header": "Which of these best describes person 3’s working situation?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -2685,8 +2831,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?", "header": "Are you sure this person is retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -2731,8 +2886,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?", "header": "Are you sure this person isn’t retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -2752,6 +2916,7 @@
"questions": { "questions": {
"details_known_4": { "details_known_4": {
"check_answer_label": "Details known for person 4", "check_answer_label": "Details known for person 4",
"check_answers_card_number": 4,
"header": "Do you know details for person 4?", "header": "Do you know details for person 4?",
"hint_text": "You must provide details for everyone in the household if you know them.", "hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio", "type": "radio",
@ -2789,6 +2954,7 @@
"questions": { "questions": {
"relat4": { "relat4": {
"check_answer_label": "Person 4’s relationship to the lead tenant", "check_answer_label": "Person 4’s relationship to the lead tenant",
"check_answers_card_number": 4,
"header": "What is person 4’s relationship to the lead tenant?", "header": "What is person 4’s relationship to the lead tenant?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -2824,6 +2990,7 @@
"questions": { "questions": {
"age4_known": { "age4_known": {
"header": "Do you know person 4’s age?", "header": "Do you know person 4’s age?",
"check_answers_card_number": 4,
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -2851,6 +3018,7 @@
"age4": { "age4": {
"header": "Age", "header": "Age",
"check_answer_label": "Person 4’s age", "check_answer_label": "Person 4’s age",
"check_answers_card_number": 4,
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"max": 120, "max": 120,
@ -2961,6 +3129,7 @@
"questions": { "questions": {
"sex4": { "sex4": {
"check_answer_label": "Person 4’s gender identity", "check_answer_label": "Person 4’s gender identity",
"check_answers_card_number": 4,
"header": "Which of these best describes person 4’s gender identity?", "header": "Which of these best describes person 4’s gender identity?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -3083,6 +3252,7 @@
"questions": { "questions": {
"ecstat4": { "ecstat4": {
"check_answer_label": "Person 4’s working situation", "check_answer_label": "Person 4’s working situation",
"check_answers_card_number": 4,
"header": "Which of these best describes person 4’s working situation?", "header": "Which of these best describes person 4’s working situation?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -3173,8 +3343,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?", "header": "Are you sure this person is retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -3219,8 +3398,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?", "header": "Are you sure this person isn’t retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -3240,6 +3428,7 @@
"questions": { "questions": {
"details_known_5": { "details_known_5": {
"check_answer_label": "Details known for person 5", "check_answer_label": "Details known for person 5",
"check_answers_card_number": 5,
"header": "Do you know details for person 5?", "header": "Do you know details for person 5?",
"hint_text": "You must provide details for everyone in the household if you know them.", "hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio", "type": "radio",
@ -3274,6 +3463,7 @@
"questions": { "questions": {
"relat5": { "relat5": {
"check_answer_label": "Person 5’s relationship to the lead tenant", "check_answer_label": "Person 5’s relationship to the lead tenant",
"check_answers_card_number": 5,
"header": "What is person 5’s relationship to the lead tenant?", "header": "What is person 5’s relationship to the lead tenant?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -3309,6 +3499,7 @@
"questions": { "questions": {
"age5_known": { "age5_known": {
"header": "Do you know person 5’s age?", "header": "Do you know person 5’s age?",
"check_answers_card_number": 5,
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -3336,6 +3527,7 @@
"age5": { "age5": {
"header": "Age", "header": "Age",
"check_answer_label": "Person 5’s age", "check_answer_label": "Person 5’s age",
"check_answers_card_number": 5,
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"max": 120, "max": 120,
@ -3446,6 +3638,7 @@
"questions": { "questions": {
"sex5": { "sex5": {
"check_answer_label": "Person 5’s gender identity", "check_answer_label": "Person 5’s gender identity",
"check_answers_card_number": 5,
"header": "Which of these best describes person 5’s gender identity?", "header": "Which of these best describes person 5’s gender identity?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -3568,6 +3761,7 @@
"questions": { "questions": {
"ecstat5": { "ecstat5": {
"check_answer_label": "Person 5’s working situation", "check_answer_label": "Person 5’s working situation",
"check_answers_card_number": 5,
"header": "Which of these best describes person 5’s working situation?", "header": "Which of these best describes person 5’s working situation?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -3658,8 +3852,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?", "header": "Are you sure this person is retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -3704,8 +3907,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?", "header": "Are you sure this person isn’t retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -3725,6 +3937,7 @@
"questions": { "questions": {
"details_known_6": { "details_known_6": {
"check_answer_label": "Details known for person 6", "check_answer_label": "Details known for person 6",
"check_answers_card_number": 6,
"header": "Do you know details for person 6?", "header": "Do you know details for person 6?",
"hint_text": "You must provide details for everyone in the household if you know them.", "hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio", "type": "radio",
@ -3756,6 +3969,7 @@
"questions": { "questions": {
"relat6": { "relat6": {
"check_answer_label": "Person 6’s relationship to the lead tenant", "check_answer_label": "Person 6’s relationship to the lead tenant",
"check_answers_card_number": 6,
"header": "What is person 6’s relationship to the lead tenant?", "header": "What is person 6’s relationship to the lead tenant?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -3791,6 +4005,7 @@
"questions": { "questions": {
"age6_known": { "age6_known": {
"header": "Do you know person 6’s age?", "header": "Do you know person 6’s age?",
"check_answers_card_number": 6,
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -3818,6 +4033,7 @@
"age6": { "age6": {
"header": "Age", "header": "Age",
"check_answer_label": "Person 6’s age", "check_answer_label": "Person 6’s age",
"check_answers_card_number": 6,
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"max": 120, "max": 120,
@ -3928,6 +4144,7 @@
"questions": { "questions": {
"sex6": { "sex6": {
"check_answer_label": "Person 6’s gender identity", "check_answer_label": "Person 6’s gender identity",
"check_answers_card_number": 6,
"header": "Which of these best describes person 6’s gender identity?", "header": "Which of these best describes person 6’s gender identity?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -4050,6 +4267,7 @@
"questions": { "questions": {
"ecstat6": { "ecstat6": {
"check_answer_label": "Person 6’s working situation", "check_answer_label": "Person 6’s working situation",
"check_answers_card_number": 6,
"header": "Which of these best describes person 6’s working situation?", "header": "Which of these best describes person 6’s working situation?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -4140,8 +4358,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?", "header": "Are you sure this person is retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -4186,8 +4413,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?", "header": "Are you sure this person isn’t retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -4207,6 +4443,7 @@
"questions": { "questions": {
"details_known_7": { "details_known_7": {
"check_answer_label": "Details known for person 7", "check_answer_label": "Details known for person 7",
"check_answers_card_number": 7,
"header": "Do you know details for person 7?", "header": "Do you know details for person 7?",
"hint_text": "You must provide details for everyone in the household if you know them.", "hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio", "type": "radio",
@ -4235,6 +4472,7 @@
"questions": { "questions": {
"relat7": { "relat7": {
"check_answer_label": "Person 7’s relationship to the lead tenant", "check_answer_label": "Person 7’s relationship to the lead tenant",
"check_answers_card_number": 7,
"header": "What is person 7’s relationship to the lead tenant?", "header": "What is person 7’s relationship to the lead tenant?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -4270,6 +4508,7 @@
"questions": { "questions": {
"age7_known": { "age7_known": {
"header": "Do you know person 7’s age?", "header": "Do you know person 7’s age?",
"check_answers_card_number": 7,
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -4297,6 +4536,7 @@
"age7": { "age7": {
"header": "Age", "header": "Age",
"check_answer_label": "Person 7’s age", "check_answer_label": "Person 7’s age",
"check_answers_card_number": 7,
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"max": 120, "max": 120,
@ -4407,6 +4647,7 @@
"questions": { "questions": {
"sex7": { "sex7": {
"check_answer_label": "Person 7’s gender identity", "check_answer_label": "Person 7’s gender identity",
"check_answers_card_number": 7,
"header": "Which of these best describes person 7’s gender identity?", "header": "Which of these best describes person 7’s gender identity?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -4529,6 +4770,7 @@
"questions": { "questions": {
"ecstat7": { "ecstat7": {
"check_answer_label": "Person 7’s working situation", "check_answer_label": "Person 7’s working situation",
"check_answers_card_number": 7,
"header": "Which of these best describes person 7’s working situation?", "header": "Which of these best describes person 7’s working situation?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -4619,8 +4861,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?", "header": "Are you sure this person is retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -4665,8 +4916,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?", "header": "Are you sure this person isn’t retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -4686,6 +4946,7 @@
"questions": { "questions": {
"details_known_8": { "details_known_8": {
"check_answer_label": "Details known for person 8", "check_answer_label": "Details known for person 8",
"check_answers_card_number": 8,
"header": "Do you know details for person 8?", "header": "Do you know details for person 8?",
"hint_text": "You must provide details for everyone in the household if you know them.", "hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio", "type": "radio",
@ -4711,6 +4972,7 @@
"questions": { "questions": {
"relat8": { "relat8": {
"check_answer_label": "Person 8’s relationship to the lead tenant", "check_answer_label": "Person 8’s relationship to the lead tenant",
"check_answers_card_number": 8,
"header": "What is person 8’s relationship to the lead tenant?", "header": "What is person 8’s relationship to the lead tenant?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -4746,6 +5008,7 @@
"questions": { "questions": {
"age8_known": { "age8_known": {
"header": "Do you know person 8’s age?", "header": "Do you know person 8’s age?",
"check_answers_card_number": 8,
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
@ -4773,6 +5036,7 @@
"age8": { "age8": {
"header": "Age", "header": "Age",
"check_answer_label": "Person 8’s age", "check_answer_label": "Person 8’s age",
"check_answers_card_number": 8,
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"max": 120, "max": 120,
@ -4883,6 +5147,7 @@
"questions": { "questions": {
"sex8": { "sex8": {
"check_answer_label": "Person 8’s gender identity", "check_answer_label": "Person 8’s gender identity",
"check_answers_card_number": 8,
"header": "Which of these best describes person 8’s gender identity?", "header": "Which of these best describes person 8’s gender identity?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -5005,6 +5270,7 @@
"questions": { "questions": {
"ecstat8": { "ecstat8": {
"check_answer_label": "Person 8’s working situation", "check_answer_label": "Person 8’s working situation",
"check_answers_card_number": 8,
"header": "Which of these best describes person 8’s working situation?", "header": "Which of these best describes person 8’s working situation?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -5095,8 +5361,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?", "header": "Are you sure this person is retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -5141,8 +5416,17 @@
}, },
"questions": { "questions": {
"retirement_value_check": { "retirement_value_check": {
"check_answer_label": "Retirement age soft validation", "check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?", "header": "Are you sure this person isn’t retired?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -5362,8 +5646,16 @@
}, },
"questions": { "questions": {
"pregnancy_value_check": { "pregnancy_value_check": {
"check_answer_label": "Pregnancy soft validation", "check_answer_label": "Pregnancy confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [{
"pregnancy_value_check": 0
},
{
"pregnancy_value_check": 1
}
]
},
"header": "Are you sure this is correct?", "header": "Are you sure this is correct?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -6811,8 +7103,17 @@
}, },
"questions": { "questions": {
"net_income_value_check": { "net_income_value_check": {
"check_answer_label": "Net income soft validation", "check_answer_label": "Net income confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"net_income_value_check": 0
},
{
"net_income_value_check": 1
}
]
},
"header": "Are you sure this is correct?", "header": "Are you sure this is correct?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -7712,8 +8013,17 @@
}, },
"questions": { "questions": {
"rent_value_check": { "rent_value_check": {
"check_answer_label": "Rent soft validation", "check_answer_label": "Total rent confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"rent_value_check": 0
},
{
"rent_value_check": 1
}
]
},
"header": "Are you sure this is correct?", "header": "Are you sure this is correct?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -7756,8 +8066,17 @@
}, },
"questions": { "questions": {
"rent_value_check": { "rent_value_check": {
"check_answer_label": "Rent soft validation", "check_answer_label": "Total rent confirmation",
"hidden_in_check_answers": true, "hidden_in_check_answers": {
"depends_on": [
{
"rent_value_check": 0
},
{
"rent_value_check": 1
}
]
},
"header": "Are you sure this is correct?", "header": "Are you sure this is correct?",
"type": "interruption_screen", "type": "interruption_screen",
"answer_options": { "answer_options": {
@ -7776,7 +8095,7 @@
"description": "", "description": "",
"questions": { "questions": {
"hbrentshortfall": { "hbrentshortfall": {
"check_answer_label": "Outstanding amount for basic rent and charges", "check_answer_label": "Any outstanding amount for basic rent and charges",
"header": "After the household has received any housing-related benefits, will they still need to pay for rent and charges?", "header": "After the household has received any housing-related benefits, will they still need to pay for rent and charges?",
"hint_text": "Also known as the ‘outstanding amount’.", "hint_text": "Also known as the ‘outstanding amount’.",
"type": "radio", "type": "radio",
@ -7812,17 +8131,15 @@
} }
] ]
}, },
"outstanding_amount_known": { "outstanding_amount": {
"header": "", "header": "",
"description": "", "description": "",
"questions": { "questions": {
"tshortfall_known": { "tshortfall_known": {
"check_answer_label": "", "check_answer_label": "Do you know the outstanding amount?",
"header": "", "header": "Can you estimate the outstanding amount?",
"hint_text": "", "hint_text": "You only need to give an approximate figure.",
"hidden_in_check_answers": true,
"type": "radio", "type": "radio",
"derived": true,
"answer_options": { "answer_options": {
"0": { "0": {
"value": "Yes" "value": "Yes"
@ -7830,19 +8147,14 @@
"1": { "1": {
"value": "No" "value": "No"
} }
},
"conditional_for": {
"tshortfall": [0]
} }
} },
},
"depends_on": [false]
},
"outstanding_amount": {
"header": "",
"description": "",
"questions": {
"tshortfall": { "tshortfall": {
"check_answer_label": "Estimated outstanding amount", "check_answer_label": "Estimated outstanding amount",
"header": "What do you expect the outstanding amount to be?", "header": "Estimated outstanding amount",
"hint_text": "Give an estimated amount if you don’t know the exact figure.",
"type": "numeric", "type": "numeric",
"min": 0, "min": 0,
"step": 0.01, "step": 0.01,

2
config/initializers/devise.rb

@ -184,7 +184,7 @@ Devise.setup do |config|
# Email regex used to validate email formats. It simply asserts that # Email regex used to validate email formats. It simply asserts that
# one (and only one) @ exists in the given string. This is mainly # one (and only one) @ exists in the given string. This is mainly
# to give user feedback and not to assert the e-mail validity. # to give user feedback and not to assert the e-mail validity.
config.email_regexp = /\A[^@\s]+@[^@\s]+\z/ config.email_regexp = URI::MailTo::EMAIL_REGEXP
# ==> Configuration for :timeoutable # ==> Configuration for :timeoutable
# The time you want to timeout the user session without activity. After this # The time you want to timeout the user session without activity. After this

4
config/locales/devise.en.yml

@ -9,10 +9,10 @@ en:
failure: failure:
already_authenticated: "You are already signed in" already_authenticated: "You are already signed in"
inactive: "Your account has not been activated yet" inactive: "Your account has not been activated yet"
invalid: "Incorrect %{authentication_keys} email or password" invalid: "Incorrect %{authentication_keys} or password"
locked: "Your account has been locked." locked: "Your account has been locked."
last_attempt: "You have one more attempt before your account is locked" last_attempt: "You have one more attempt before your account is locked"
not_found_in_database: "Incorrect %{authentication_keys} email or password" not_found_in_database: "Incorrect %{authentication_keys} or password"
timeout: "Your session expired. Sign in again to continue." timeout: "Your session expired. Sign in again to continue."
unauthenticated: "You need to sign in or sign up before continuing" unauthenticated: "You need to sign in or sign up before continuing"
unconfirmed: "You must confirm your email address before continuing" unconfirmed: "You must confirm your email address before continuing"

10
config/locales/en.yml

@ -78,8 +78,8 @@ en:
user: user:
attributes: attributes:
organisation_id: organisation_id:
blank: "Enter the existing organisation’s name" blank: "Select the user’s organisation"
invalid: "Enter the existing organisation’s name" invalid: "Select the user’s organisation"
name: name:
blank: "Enter a name" blank: "Enter a name"
email: email:
@ -122,7 +122,7 @@ en:
mrcdate: mrcdate:
before_tenancy_start: "Enter a major repairs date that is before the tenancy start date" before_tenancy_start: "Enter a major repairs date that is before the tenancy start date"
not_first_let: "Major repairs date must not be completed if the tenancy is a first let" not_first_let: "Major repairs date must not be completed if the tenancy is a first let"
730_days_before_tenancy_start: "Enter a major repairs completion date that is no more than 730 days before the tenancy start date" ten_years_before_tenancy_start: "Enter a major repairs completion date that is no more than 10 years before the tenancy start date"
void_date: void_date:
ten_years_before_tenancy_start: "Enter a void date must no more than 10 years before the tenancy start date" ten_years_before_tenancy_start: "Enter a void date must no more than 10 years before the tenancy start date"
before_tenancy_start: "Enter a void date must that is before the tenancy start date" before_tenancy_start: "Enter a void date must that is before the tenancy start date"
@ -319,6 +319,10 @@ en:
title: "You told us somebody in the household is pregnant" title: "You told us somebody in the household is pregnant"
no_females: "You also told us there are no female tenants living at the property." no_females: "You also told us there are no female tenants living at the property."
females_not_in_soft_age_range: "You also told us that any female tenants living at the property are in the following age ranges:<ul><li>11 to 16</li><li>50 to 65</li></ul>" females_not_in_soft_age_range: "You also told us that any female tenants living at the property are in the following age ranges:<ul><li>11 to 16</li><li>50 to 65</li></ul>"
major_repairs_date:
title_text: "You told us the time between the start of the tenancy and the major repairs completion date is more than 2 years"
void_date:
title_text: "You told us the time between the start of the tenancy and the void date is more than 2 years"
devise: devise:
two_factor_authentication: two_factor_authentication:

1
config/routes.rb

@ -48,6 +48,7 @@ Rails.application.routes.draw do
member do member do
resources :locations do resources :locations do
get "edit-name", to: "locations#edit_name" get "edit-name", to: "locations#edit_name"
get "edit", to: "locations#edit"
end end
end end
end end

6
db/migrate/20220720214646_add_delete_cascade_from_orgs_down.rb

@ -0,0 +1,6 @@
class AddDeleteCascadeFromOrgsDown < ActiveRecord::Migration[7.0]
def change
remove_foreign_key :schemes, :organisations, column: "owning_organisation_id"
add_foreign_key :schemes, :organisations, column: "owning_organisation_id", on_delete: :cascade
end
end

6
db/migrate/20220722133738_add_delete_caselogs_users.rb

@ -0,0 +1,6 @@
class AddDeleteCaselogsUsers < ActiveRecord::Migration[7.0]
def change
add_foreign_key :case_logs, :organisations, column: "owning_organisation_id", on_delete: :cascade
add_foreign_key :users, :organisations, on_delete: :cascade
end
end

7
db/migrate/20220729110846_add_confirmed_location.rb

@ -0,0 +1,7 @@
class AddConfirmedLocation < ActiveRecord::Migration[7.0]
def change
change_table :locations, bulk: true do |t|
t.column :confirmed, :boolean
end
end
end

5
db/migrate/20220808090330_add_major_repairs_date_value_check.rb

@ -0,0 +1,5 @@
class AddMajorRepairsDateValueCheck < ActiveRecord::Migration[7.0]
def change
add_column :case_logs, :major_repairs_date_value_check, :integer
end
end

5
db/migrate/20220808093035_add_void_date_value_check.rb

@ -0,0 +1,5 @@
class AddVoidDateValueCheck < ActiveRecord::Migration[7.0]
def change
add_column :case_logs, :void_date_value_check, :integer
end
end

9
db/migrate/20220810152340_add_parent_child_relationships.rb

@ -0,0 +1,9 @@
class AddParentChildRelationships < ActiveRecord::Migration[7.0]
def change
create_table :organisation_relationships do |t|
t.integer :child_organisation_id, foreign_key: true
t.integer :parent_organisation_id, foreign_key: true
t.timestamps
end
end
end

16
db/schema.rb

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2022_08_02_125711) do ActiveRecord::Schema[7.0].define(version: 2022_08_10_152340) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -200,6 +200,8 @@ ActiveRecord::Schema[7.0].define(version: 2022_08_02_125711) do
t.integer "vacdays" t.integer "vacdays"
t.bigint "scheme_id" t.bigint "scheme_id"
t.bigint "location_id" t.bigint "location_id"
t.integer "major_repairs_date_value_check"
t.integer "void_date_value_check"
t.index ["created_by_id"], name: "index_case_logs_on_created_by_id" t.index ["created_by_id"], name: "index_case_logs_on_created_by_id"
t.index ["location_id"], name: "index_case_logs_on_location_id" t.index ["location_id"], name: "index_case_logs_on_location_id"
t.index ["managing_organisation_id"], name: "index_case_logs_on_managing_organisation_id" t.index ["managing_organisation_id"], name: "index_case_logs_on_managing_organisation_id"
@ -250,6 +252,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_08_02_125711) do
t.string "mobility_type" t.string "mobility_type"
t.datetime "startdate", precision: nil t.datetime "startdate", precision: nil
t.string "location_admin_district" t.string "location_admin_district"
t.boolean "confirmed"
t.index ["old_id"], name: "index_locations_on_old_id", unique: true t.index ["old_id"], name: "index_locations_on_old_id", unique: true
t.index ["scheme_id"], name: "index_locations_on_scheme_id" t.index ["scheme_id"], name: "index_locations_on_scheme_id"
end end
@ -262,6 +265,13 @@ ActiveRecord::Schema[7.0].define(version: 2022_08_02_125711) do
t.boolean "empty_export", default: false, null: false t.boolean "empty_export", default: false, null: false
end end
create_table "organisation_relationships", force: :cascade do |t|
t.integer "child_organisation_id"
t.integer "parent_organisation_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "organisation_rent_periods", force: :cascade do |t| create_table "organisation_rent_periods", force: :cascade do |t|
t.bigint "organisation_id" t.bigint "organisation_id"
t.integer "rent_period" t.integer "rent_period"
@ -379,8 +389,10 @@ ActiveRecord::Schema[7.0].define(version: 2022_08_02_125711) do
end end
add_foreign_key "case_logs", "locations" add_foreign_key "case_logs", "locations"
add_foreign_key "case_logs", "organisations", column: "owning_organisation_id", on_delete: :cascade
add_foreign_key "case_logs", "schemes" add_foreign_key "case_logs", "schemes"
add_foreign_key "locations", "schemes" add_foreign_key "locations", "schemes"
add_foreign_key "schemes", "organisations", column: "managing_organisation_id" add_foreign_key "schemes", "organisations", column: "managing_organisation_id"
add_foreign_key "schemes", "organisations", column: "owning_organisation_id" add_foreign_key "schemes", "organisations", column: "owning_organisation_id", on_delete: :cascade
add_foreign_key "users", "organisations", on_delete: :cascade
end end

2
lib/tasks/data_export.rake

@ -4,7 +4,7 @@ namespace :core do
format = args[:format] format = args[:format]
full_update = args[:full_update].present? && args[:full_update] == "true" full_update = args[:full_update].present? && args[:full_update] == "true"
storage_service = StorageService.new(PaasConfigurationService.new, ENV["EXPORT_PAAS_INSTANCE"]) storage_service = S3StorageService.new(PaasConfigurationService.new, ENV["EXPORT_PAAS_INSTANCE"])
export_service = Exports::CaseLogExportService.new(storage_service) export_service = Exports::CaseLogExportService.new(storage_service)
if format.present? && format == "CSV" if format.present? && format == "CSV"

4
lib/tasks/data_import.rake

@ -1,11 +1,11 @@
namespace :core do namespace :core do
desc "Import data XMLs from Softwire system" desc "Import data XMLs from legacy CORE"
task :data_import, %i[type path] => :environment do |_task, args| task :data_import, %i[type path] => :environment do |_task, args|
type = args[:type] type = args[:type]
path = args[:path] path = args[:path]
raise "Usage: rake core:data_import['data_type', 'path/to/xml_files']" if path.blank? || type.blank? raise "Usage: rake core:data_import['data_type', 'path/to/xml_files']" if path.blank? || type.blank?
storage_service = StorageService.new(PaasConfigurationService.new, ENV["IMPORT_PAAS_INSTANCE"]) storage_service = S3StorageService.new(PaasConfigurationService.new, ENV["IMPORT_PAAS_INSTANCE"])
case type case type
when "organisation" when "organisation"

2
lib/tasks/data_import_field.rake

@ -5,7 +5,7 @@ namespace :core do
path = args[:path] path = args[:path]
raise "Usage: rake core:data_import_field['field','path/to/xml_files']" if path.blank? || field.blank? raise "Usage: rake core:data_import_field['field','path/to/xml_files']" if path.blank? || field.blank?
storage_service = StorageService.new(PaasConfigurationService.new, ENV["IMPORT_PAAS_INSTANCE"]) storage_service = S3StorageService.new(PaasConfigurationService.new, ENV["IMPORT_PAAS_INSTANCE"])
# We only allow a reduced list of known fields to be updatable # We only allow a reduced list of known fields to be updatable
case field case field

32
lib/tasks/full_import.rake

@ -0,0 +1,32 @@
Import = Struct.new("Import", :import_class, :import_method, :folder)
namespace :core do
desc "Import all data XMLs from legacy CORE"
task :full_import, %i[archive_path] => :environment do |_task, args|
archive_path = args[:archive_path]
raise "Usage: rake core:full_import['path/to/archive']" if archive_path.blank?
s3_service = S3StorageService.new(PaasConfigurationService.new, ENV["IMPORT_PAAS_INSTANCE"])
archive_io = s3_service.get_file_io(archive_path)
archive_service = ArchiveStorageService.new(archive_io)
import_list = [
Import.new(Imports::OrganisationImportService, :create_organisations, "institution"),
Import.new(Imports::SchemeImportService, :create_schemes, "mgmtgroups"),
Import.new(Imports::SchemeLocationImportService, :create_scheme_locations, "schemes"),
Import.new(Imports::UserImportService, :create_users, "user"),
Import.new(Imports::DataProtectionConfirmationImportService, :create_data_protection_confirmations, "dataprotect"),
Import.new(Imports::OrganisationRentPeriodImportService, :create_organisation_rent_periods, "rent-period"),
Import.new(Imports::CaseLogsImportService, :create_logs, "logs"),
]
import_list.each do |step|
if archive_service.folder_present?(step.folder)
Rails.logger.info("Start importing folder #{step.folder}")
step.import_class.new(archive_service).send(step.import_method, step.folder)
else
Rails.logger.info("#{step.folder} does not exist, skipping #{step.import_class}")
end
end
end
end

27
spec/components/check_answers_summary_list_card_component_spec.rb

@ -0,0 +1,27 @@
require "rails_helper"
RSpec.describe CheckAnswersSummaryListCardComponent, type: :component do
context "when given a set of questions" do
let(:user) { FactoryBot.build(:user) }
let(:case_log) { FactoryBot.build(:case_log, :completed, age2: 99) }
let(:subsection_id) { "household_characteristics" }
let(:subsection) { case_log.form.get_subsection(subsection_id) }
let(:questions) { subsection.applicable_questions(case_log) }
it "renders a summary list card for the answers to those questions" do
result = render_inline(described_class.new(questions:, case_log:, user:))
expect(result).to have_content(questions.first.answer_label(case_log))
end
it "applicable questions doesn't return questions that are hidden in check answers" do
summary_list = described_class.new(questions:, case_log:, user:)
expect(summary_list.applicable_questions.map(&:id).include?("retirement_value_check")).to eq(false)
end
it "has the correct answer label for a question" do
summary_list = described_class.new(questions:, case_log:, user:)
sex1_question = questions[2]
expect(summary_list.get_answer_label(sex1_question)).to eq("Female")
end
end
end

2
spec/factories/case_log.rb

@ -110,6 +110,8 @@ FactoryBot.define do
rp_dontknow { 0 } rp_dontknow { 0 }
tenancyother { nil } tenancyother { nil }
net_income_value_check { nil } net_income_value_check { nil }
void_date_value_check { 1 }
major_repairs_date_value_check { 1 }
net_income_known { 0 } net_income_known { 0 }
previous_la_known { 1 } previous_la_known { 1 }
property_owner_organisation { "Test" } property_owner_organisation { "Test" }

1
spec/factories/location.rb

@ -8,6 +8,7 @@ FactoryBot.define do
location_code { "E09000033" } location_code { "E09000033" }
location_admin_district { "Westminster" } location_admin_district { "Westminster" }
startdate { Faker::Date.between(from: 6.months.ago, to: Time.zone.today) } startdate { Faker::Date.between(from: 6.months.ago, to: Time.zone.today) }
confirmed { true }
scheme scheme
trait :export do trait :export do
postcode { "SW1A 2AA" } postcode { "SW1A 2AA" }

5
spec/factories/organisation.rb

@ -24,4 +24,9 @@ FactoryBot.define do
created_at { Time.zone.now } created_at { Time.zone.now }
updated_at { Time.zone.now } updated_at { Time.zone.now }
end end
factory :organisation_relationship do
child_organisation { FactoryBot.create(:organisation) }
parent_organisation { FactoryBot.create(:organisation) }
end
end end

13
spec/features/form/check_answers_page_spec.rb

@ -133,6 +133,19 @@ RSpec.describe "Form Check Answers Page" do
end end
end end
it "does not group questions into summary cards if the questions in the subsection don't have a check_answers_card_number attribute" do
visit("/logs/#{completed_case_log.id}/household-needs/check-answers")
assert_selector ".x-govuk-summary-card__title", count: 0
end
context "when the user is checking their answers for the household characteristics subsection" do
it "they see a seperate summary card for each member of the household" do
visit("/logs/#{completed_case_log.id}/#{subsection}/check-answers")
assert_selector ".x-govuk-summary-card__title", text: "Lead tenant", count: 1
assert_selector ".x-govuk-summary-card__title", text: "Person 2", count: 1
end
end
context "when viewing setup section answers" do context "when viewing setup section answers" do
before do before do
FactoryBot.create(:location, scheme:) FactoryBot.create(:location, scheme:)

23
spec/features/form/checkboxes_spec.rb

@ -38,4 +38,27 @@ RSpec.describe "Checkboxes" do
expect(case_log["housingneeds_h"]).to eq(1) expect(case_log["housingneeds_h"]).to eq(1)
end end
end end
context "when a checkbox question is submitted with invalid answers" do
before do
allow(case_log).to receive(:update).and_return(false)
end
it "shows an error summary" do
visit("/logs/#{id}/accessibility-requirements")
page.check("case-log-accessibility-requirements-housingneeds-a-field")
page.check("case-log-accessibility-requirements-housingneeds-c-field")
click_button("Save and continue")
expect(page).to have_title("Error")
end
it "persists the original selections" do
visit("/logs/#{id}/accessibility-requirements")
page.check("case-log-accessibility-requirements-housingneeds-a-field")
page.check("case-log-accessibility-requirements-housingneeds-c-field")
click_button("Save and continue")
expect(page).to have_checked_field("case-log-accessibility-requirements-housingneeds-a-field")
expect(page).to have_checked_field("case-log-accessibility-requirements-housingneeds-c-field")
end
end
end end

33
spec/features/form/page_routing_spec.rb

@ -111,4 +111,37 @@ RSpec.describe "Form Page Routing" do
expect(find_field("case_log[startdate(1i)]").value).to eq(nil) expect(find_field("case_log[startdate(1i)]").value).to eq(nil)
end end
end end
context "when completing the setup section" do
context "with a supported housing log" do
let(:case_log) do
FactoryBot.create(
:case_log,
owning_organisation: user.organisation,
managing_organisation: user.organisation,
needstype: 2,
)
end
context "with a scheme with only 1 active location" do
let(:scheme) { FactoryBot.create(:scheme, owning_organisation: user.organisation) }
let!(:active_location) { FactoryBot.create(:location, scheme:) }
before do
FactoryBot.create(:location, scheme:, startdate: Time.zone.today + 20.days)
visit("/logs/#{case_log.id}/scheme")
select(scheme.service_name, from: "case_log[scheme_id]")
click_button("Save and continue")
end
it "does not route to the scheme location question" do
expect(page).to have_current_path("/logs/#{case_log.id}/renewal")
end
it "infers the scheme location" do
expect(case_log.reload.location_id).to eq(active_location.id)
end
end
end
end
end end

33
spec/features/organisation_spec.rb

@ -252,6 +252,39 @@ RSpec.describe "User Features" do
end end
end end
end end
describe "delete cascade" do
context "when the organisation is deleted" do
let!(:organisation) { FactoryBot.create(:organisation) }
let!(:user) { FactoryBot.create(:user, :support, last_sign_in_at: Time.zone.now, organisation:) }
let!(:scheme_to_delete) { FactoryBot.create(:scheme, owning_organisation: user.organisation) }
let!(:log_to_delete) { FactoryBot.create(:case_log, owning_organisation: user.organisation) }
context "when organisation is deleted" do
it "child relationships ie logs, schemes and users are deleted too - application" do
organisation.destroy!
expect { organisation.reload }.to raise_error(ActiveRecord::RecordNotFound)
expect { CaseLog.find(log_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound)
expect { Scheme.find(scheme_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound)
expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound)
end
context "when the organisation is deleted" do
let!(:organisation) { FactoryBot.create(:organisation) }
let!(:user) { FactoryBot.create(:user, :support, last_sign_in_at: Time.zone.now, organisation:) }
let!(:scheme_to_delete) { FactoryBot.create(:scheme, owning_organisation: user.organisation) }
let!(:log_to_delete) { FactoryBot.create(:case_log, :in_progress, needstype: 1, owning_organisation: user.organisation) }
it "child relationships ie logs, schemes and users are deleted too - database" do
ActiveRecord::Base.connection.exec_query("DELETE FROM organisations WHERE id = #{organisation.id};")
expect { CaseLog.find(log_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound)
expect { Scheme.find(scheme_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound)
expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
end
end
end end
end end
end end

2
spec/features/schemes_helpers.rb

@ -44,7 +44,7 @@ module SchemesHelpers
end end
def fill_in_and_save_secondary_client_group def fill_in_and_save_secondary_client_group
choose "Homeless families with support needs" choose "Offenders and people at risk of offending"
click_button "Save and continue" click_button "Save and continue"
end end

96
spec/features/schemes_spec.rb

@ -284,13 +284,34 @@ RSpec.describe "Schemes scheme Features" do
expect(page).not_to have_button("Create scheme") expect(page).not_to have_button("Create scheme")
end end
it "allows you to edit the newly added location" do
click_link "Locations"
expect(page).to have_link(nil, href: /edit/)
end
context "when you click save" do
it "displays a updated banner" do
click_button "Save"
expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success")
expect(page).to have_content("has been updated")
end
it "does not let you edit the saved location" do
click_link "Locations"
expect(page).to have_link(nil, href: /edit(?!-name)/)
click_button "Save"
click_link "Locations"
expect(page).not_to have_link(nil, href: /edit(?!-name)/)
end
end
context "when you click to view the scheme details" do context "when you click to view the scheme details" do
before do before do
click_link("Scheme") click_link("Scheme")
end end
it "does not let you change details other than the name" do it "does not let you change details other than the name, confidential information and housing stock owner" do
assert_selector "a", text: "Change", count: 1 assert_selector "a", text: "Change", count: 3
end end
end end
end end
@ -451,6 +472,7 @@ RSpec.describe "Schemes scheme Features" do
it "lets me check my answers after adding a location" do it "lets me check my answers after adding a location" do
fill_in_and_save_location fill_in_and_save_location
expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers")
expect(page).to have_content "Check your changes before creating this scheme" expect(page).to have_content "Check your changes before creating this scheme"
end end
@ -561,11 +583,12 @@ RSpec.describe "Schemes scheme Features" do
end end
it "adds scheme to the list of schemes" do it "adds scheme to the list of schemes" do
expect(page).to have_content "#{scheme.service_name} has been created."
click_link "Schemes"
expect(page).to have_content "Supported housing schemes" expect(page).to have_content "Supported housing schemes"
expect(page).to have_content scheme.id_to_display expect(page).to have_content scheme.id_to_display
expect(page).to have_content scheme.service_name expect(page).to have_content scheme.service_name
expect(page).to have_content scheme.owning_organisation.name expect(page).to have_content scheme.owning_organisation.name
expect(page).to have_content "#{scheme.service_name} has been created."
end end
end end
@ -704,9 +727,17 @@ RSpec.describe "Schemes scheme Features" do
click_button "Save changes" click_button "Save changes"
end end
it "lets me see amended details on the show page" do it "lets me see amended details on the check answers page" do
expect(page).to have_content "FooBar" expect(page).to have_content "FooBar"
expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers")
assert_selector "a", text: "Change", count: 3
end
it "lets me save the scheme" do
click_button "Save"
expect(page).to have_current_path("/schemes/#{scheme.id}") expect(page).to have_current_path("/schemes/#{scheme.id}")
expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success")
expect(page).to have_content("has been updated")
end end
end end
end end
@ -748,10 +779,10 @@ RSpec.describe "Schemes scheme Features" do
click_button "Save and continue" click_button "Save and continue"
end end
it "returns to locations page and shows the new name" do it "returns to locations check your answers page and shows the new name" do
expect(page).to have_content location.id expect(page).to have_content location.id
expect(page).to have_content "NewName" expect(page).to have_content "NewName"
expect(page).to have_current_path("/schemes/#{scheme.id}/locations") expect(page.current_url.split("/").last).to eq("check-answers#locations")
end end
end end
end end
@ -834,8 +865,8 @@ RSpec.describe "Schemes scheme Features" do
click_link("Scheme") click_link("Scheme")
end end
it "does not let you change details other than the name" do it "does not let you change details other than the name, Confidential information and Housing stock owned by" do
assert_selector "a", text: "Change", count: 1 assert_selector "a", text: "Change", count: 3
end end
end end
end end
@ -846,6 +877,55 @@ RSpec.describe "Schemes scheme Features" do
end end
end end
context "when I am signed in as a data coordinator" do
let(:user) { FactoryBot.create(:user, :data_coordinator, last_sign_in_at: Time.zone.now) }
let!(:schemes) { FactoryBot.create_list(:scheme, 5, owning_organisation_id: user.organisation_id) }
before do
visit("/logs")
fill_in("user[email]", with: user.email)
fill_in("user[password]", with: user.password)
click_button("Sign in")
end
context "when editing a scheme" do
context "when I visit schemes page" do
before do
visit("schemes")
end
context "when I click to see individual scheme" do
let(:scheme) { schemes.first }
before do
FactoryBot.create(:location, scheme:)
click_link(scheme.service_name)
end
context "when I click to change scheme name" do
before do
click_link("Change", href: "/schemes/#{scheme.id}/edit-name", match: :first)
end
context "when I edit details" do
before do
fill_in "Scheme name", with: "FooBar"
check "This scheme contains confidential information"
click_button "Save changes"
end
it "lets me see amended details on the check answers page" do
expect(page).to have_content "FooBar"
expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers")
expect(page).to have_link("Change", href: /schemes\/#{scheme.id}\/edit-name/, count: 2)
end
end
end
end
end
end
end
context "when selecting a scheme" do context "when selecting a scheme" do
let!(:user) { FactoryBot.create(:user, :data_coordinator, last_sign_in_at: Time.zone.now) } let!(:user) { FactoryBot.create(:user, :data_coordinator, last_sign_in_at: Time.zone.now) }
let!(:schemes) { FactoryBot.create_list(:scheme, 5, owning_organisation: user.organisation, managing_organisation: user.organisation, arrangement_type: "The same organisation that owns the housing stock") } let!(:schemes) { FactoryBot.create_list(:scheme, 5, owning_organisation: user.organisation, managing_organisation: user.organisation, arrangement_type: "The same organisation that owns the housing stock") }

4
spec/fixtures/files/case_logs_download.csv vendored

@ -1,2 +1,2 @@
id,status,created_at,updated_at,tenancycode,age1,sex1,ethnic,national,prevten,ecstat1,hhmemb,age2,sex2,ecstat2,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,leftreg,reservist,illness,preg_occ,startertenancy,tenancylength,tenancy,ppostcode_full,rsnvac,unittype_gn,beds,offered,wchair,earnings,incfreq,benefits,period,layear,waityear,postcode_full,reasonpref,cbl,chr,cap,reasonother,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,illness_type_1,illness_type_2,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,net_income_value_check,property_owner_organisation,property_manager_organisation,sale_or_letting,irproduct_other,purchaser_code,reason,propcode,majorrepairs,la,prevloc,hb,hbrentshortfall,property_relet,mrcdate,incref,sale_completion_date,startdate,armedforces,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,owning_organisation_id,managing_organisation_id,renttype,needstype,lettype,postcode_known,is_la_inferred,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,brent,scharge,pscharge,supcharg,tcharge,tshortfall,chcharge,declaration,ppcodenk,previous_la_known,is_previous_la_inferred,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,ethnic_other,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,rent_type,has_benefits,renewal,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat2,relat3,relat4,relat5,relat6,relat7,relat8,rent_value_check,old_form_id,lar,irproduct,old_id,joint,created_by_id,illness_type_0,retirement_value_check,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,scheme_id,location_id,unittype_sh id,status,created_at,updated_at,tenancycode,age1,sex1,ethnic,national,prevten,ecstat1,hhmemb,age2,sex2,ecstat2,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,leftreg,reservist,illness,preg_occ,startertenancy,tenancylength,tenancy,ppostcode_full,rsnvac,unittype_gn,beds,offered,wchair,earnings,incfreq,benefits,period,layear,waityear,postcode_full,reasonpref,cbl,chr,cap,reasonother,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,illness_type_1,illness_type_2,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,net_income_value_check,property_owner_organisation,property_manager_organisation,sale_or_letting,irproduct_other,purchaser_code,reason,propcode,majorrepairs,la,prevloc,hb,hbrentshortfall,property_relet,mrcdate,incref,sale_completion_date,startdate,armedforces,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,needstype,lettype,postcode_known,is_la_inferred,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,brent,scharge,pscharge,supcharg,tcharge,tshortfall,chcharge,declaration,ppcodenk,previous_la_known,is_previous_la_inferred,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,ethnic_other,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,rent_type,has_benefits,renewal,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat2,relat3,relat4,relat5,relat6,relat7,relat8,rent_value_check,old_form_id,lar,irproduct,old_id,joint,illness_type_0,retirement_value_check,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,scheme_id,location_id,major_repairs_date_value_check,void_date_value_check,unittype_sh,owning_organisation_name,managing_organisation_name,created_by_name
{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Westminster,,,,,,,,,,,,,,DLUHC,{owning_org_id},,Supported housing,,,false,0,0,0,,0,,,,,,,,,,,,,,false,,,,,,,,,,,,,,,,,,,,0,,,,,,,,0,,,,,,,,,,,,,,,,,Danny Rojas,,,,,,9,,,{scheme_id},SE1 1TE,6 {id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,SE1 1TE,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Westminster,,,,,,,,,,,,,,,Supported housing,,,No,0,0,0,,0,,,,,,,,,,,,,,No,,,,,,,,,,,,,,,,,,,,0,,,,,,,,0,,,,,,,,,,,,,,,,,,,,,,9,,,{scheme_id},SE1 1TE,,,6,DLUHC,DLUHC,Danny Rojas

1 id status created_at updated_at tenancycode age1 sex1 ethnic national prevten ecstat1 hhmemb age2 sex2 ecstat2 age3 sex3 ecstat3 age4 sex4 ecstat4 age5 sex5 ecstat5 age6 sex6 ecstat6 age7 sex7 ecstat7 age8 sex8 ecstat8 homeless underoccupation_benefitcap leftreg reservist illness preg_occ startertenancy tenancylength tenancy ppostcode_full rsnvac unittype_gn beds offered wchair earnings incfreq benefits period layear waityear postcode_full reasonpref cbl chr cap reasonother housingneeds_a housingneeds_b housingneeds_c housingneeds_f housingneeds_g housingneeds_h illness_type_1 illness_type_2 illness_type_3 illness_type_4 illness_type_8 illness_type_5 illness_type_6 illness_type_7 illness_type_9 illness_type_10 rp_homeless rp_insan_unsat rp_medwel rp_hardship rp_dontknow tenancyother net_income_value_check property_owner_organisation property_manager_organisation sale_or_letting irproduct_other purchaser_code reason propcode majorrepairs la prevloc hb hbrentshortfall property_relet mrcdate incref sale_completion_date startdate armedforces first_time_property_let_as_social_housing unitletas builtype voiddate owning_organisation_id renttype managing_organisation_id needstype lettype postcode_known is_la_inferred totchild totelder totadult net_income_known nocharge is_carehome household_charge referral brent scharge pscharge supcharg tcharge tshortfall chcharge declaration ppcodenk previous_la_known is_previous_la_inferred age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known ethnic_group ethnic_other letting_allocation_unknown details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 rent_type has_benefits renewal wrent wscharge wpschrge wsupchrg wtcharge wtshortfall refused housingneeds wchchrg newprop relat2 relat3 relat4 relat5 relat6 relat7 relat8 rent_value_check old_form_id lar irproduct old_id joint illness_type_0 retirement_value_check created_by_id tshortfall_known sheltered pregnancy_value_check hhtype new_old vacdays scheme_id location_id major_repairs_date_value_check void_date_value_check unittype_sh owning_organisation_name managing_organisation_name created_by_name
2 {id} in_progress 2022-02-08 16:52:15 +0000 2022-02-08 16:52:15 +0000 2 SE1 1TE Westminster DLUHC {owning_org_id} Supported housing false No 0 0 0 0 false No 0 0 Danny Rojas 9 {scheme_id} SE1 1TE 6 DLUHC DLUHC Danny Rojas

2
spec/fixtures/files/case_logs_download_non_support.csv vendored

@ -0,0 +1,2 @@
id,status,created_at,updated_at,tenancycode,age1,sex1,ethnic,national,prevten,ecstat1,age2,sex2,ecstat2,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,leftreg,reservist,illness,preg_occ,startertenancy,tenancylength,tenancy,ppostcode_full,rsnvac,unittype_gn,beds,offered,wchair,earnings,incfreq,benefits,period,layear,waityear,postcode_full,reasonpref,cbl,chr,cap,reasonother,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,illness_type_1,illness_type_2,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,irproduct_other,purchaser_code,reason,propcode,majorrepairs,la,prevloc,hb,hbrentshortfall,property_relet,mrcdate,incref,sale_completion_date,startdate,armedforces,unitletas,builtype,voiddate,lettype,nocharge,household_charge,referral,brent,scharge,pscharge,supcharg,tcharge,tshortfall,chcharge,declaration,ppcodenk,ethnic_group,ethnic_other,has_benefits,renewal,refused,housingneeds,wchchrg,newprop,relat2,relat3,relat4,relat5,relat6,relat7,relat8,lar,irproduct,joint,illness_type_0,sheltered,scheme_id,location_id,major_repairs_date_value_check,void_date_value_check,unittype_sh,owning_organisation_name,managing_organisation_name,created_by_name
{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,SE1 1TE,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Westminster,,,,,,,,,,,,,,0,,,,,,,,,,,,,,0,,0,,,,,,,,,,,,,,,,{scheme_id},SE1 1TE,,,6,DLUHC,DLUHC,Danny Rojas
1 id status created_at updated_at tenancycode age1 sex1 ethnic national prevten ecstat1 age2 sex2 ecstat2 age3 sex3 ecstat3 age4 sex4 ecstat4 age5 sex5 ecstat5 age6 sex6 ecstat6 age7 sex7 ecstat7 age8 sex8 ecstat8 homeless underoccupation_benefitcap leftreg reservist illness preg_occ startertenancy tenancylength tenancy ppostcode_full rsnvac unittype_gn beds offered wchair earnings incfreq benefits period layear waityear postcode_full reasonpref cbl chr cap reasonother housingneeds_a housingneeds_b housingneeds_c housingneeds_f housingneeds_g housingneeds_h illness_type_1 illness_type_2 illness_type_3 illness_type_4 illness_type_8 illness_type_5 illness_type_6 illness_type_7 illness_type_9 illness_type_10 rp_homeless rp_insan_unsat rp_medwel rp_hardship rp_dontknow tenancyother property_owner_organisation property_manager_organisation irproduct_other purchaser_code reason propcode majorrepairs la prevloc hb hbrentshortfall property_relet mrcdate incref sale_completion_date startdate armedforces unitletas builtype voiddate lettype nocharge household_charge referral brent scharge pscharge supcharg tcharge tshortfall chcharge declaration ppcodenk ethnic_group ethnic_other has_benefits renewal refused housingneeds wchchrg newprop relat2 relat3 relat4 relat5 relat6 relat7 relat8 lar irproduct joint illness_type_0 sheltered scheme_id location_id major_repairs_date_value_check void_date_value_check unittype_sh owning_organisation_name managing_organisation_name created_by_name
2 {id} in_progress 2022-02-08 16:52:15 +0000 2022-02-08 16:52:15 +0000 2 SE1 1TE Westminster 0 0 0 {scheme_id} SE1 1TE 6 DLUHC DLUHC Danny Rojas

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

@ -13,6 +13,7 @@
"tenant_code_test": { "tenant_code_test": {
"questions": { "questions": {
"tenancycode": { "tenancycode": {
"check_answers_card_number": 0,
"check_answer_label": "Tenant code", "check_answer_label": "Tenant code",
"header": "What is the tenant code?", "header": "What is the tenant code?",
"hint_text": "This is how you usually refer to this tenancy on your own systems.", "hint_text": "This is how you usually refer to this tenancy on your own systems.",
@ -31,6 +32,7 @@
"person_1_age": { "person_1_age": {
"questions": { "questions": {
"age1": { "age1": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s age", "check_answer_label": "Lead tenant’s age",
"header": "What is the tenant’s age?", "header": "What is the tenant’s age?",
"type": "numeric", "type": "numeric",
@ -52,6 +54,7 @@
"person_1_gender": { "person_1_gender": {
"questions": { "questions": {
"sex1": { "sex1": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s gender identity", "check_answer_label": "Lead tenant’s gender identity",
"header": "Which of these best describes the tenant’s gender identity?", "header": "Which of these best describes the tenant’s gender identity?",
"type": "radio", "type": "radio",
@ -77,6 +80,7 @@
"description": "", "description": "",
"questions": { "questions": {
"ecstat1": { "ecstat1": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s working situation", "check_answer_label": "Lead tenant’s working situation",
"header": "Which of these best describes the lead tenant’s socks?", "header": "Which of these best describes the lead tenant’s socks?",
"hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.",
@ -125,6 +129,7 @@
"household_number_of_members": { "household_number_of_members": {
"questions": { "questions": {
"hhmemb": { "hhmemb": {
"check_answers_card_number": 0,
"check_answer_label": "Number of Household Members", "check_answer_label": "Number of Household Members",
"header": "How many people are there in the household?", "header": "How many people are there in the household?",
"hint_text": "The maximum number of members is 8", "hint_text": "The maximum number of members is 8",
@ -140,6 +145,7 @@
} }
}, },
"relat2": { "relat2": {
"check_answers_card_number": 2,
"check_answer_label": "Person 2’s relationship to lead tenant", "check_answer_label": "Person 2’s relationship to lead tenant",
"header": "What is person 2’s relationship to lead tenant", "header": "What is person 2’s relationship to lead tenant",
"type": "radio", "type": "radio",
@ -153,6 +159,7 @@
} }
}, },
"age2": { "age2": {
"check_answers_card_number": 2,
"check_answer_label": "Person 2’s age", "check_answer_label": "Person 2’s age",
"header": "Do you know person 2’s age?", "header": "Do you know person 2’s age?",
"type": "numeric", "type": "numeric",
@ -162,6 +169,7 @@
"width": 2 "width": 2
}, },
"sex2": { "sex2": {
"check_answers_card_number": 2,
"check_answer_label": "Person 2’s gender identity", "check_answer_label": "Person 2’s gender identity",
"header": "Which of these best describes person 2’s gender identity?", "header": "Which of these best describes person 2’s gender identity?",
"type": "radio", "type": "radio",
@ -182,11 +190,35 @@
} }
} }
}, },
"retirement_value_check": {
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement age soft validation",
"hidden_in_check_answers": true,
"header": "Are you sure this person is retired?",
"type": "radio",
"answer_options": {
"0": {
"value": "Yes"
},
"1": {
"value": "No"
}
}
}
},
"depends_on": [
{
"age2": { "operator": ">", "operand": 50 }
}
]
},
"person_2_working_situation": { "person_2_working_situation": {
"header": "", "header": "",
"description": "", "description": "",
"questions": { "questions": {
"ecstat2": { "ecstat2": {
"check_answers_card_number": 2,
"check_answer_label": "Person 2’s Work", "check_answer_label": "Person 2’s Work",
"header": "Which of these best describes person 2’s working situation?", "header": "Which of these best describes person 2’s working situation?",
"type": "radio", "type": "radio",
@ -216,6 +248,7 @@
"propcode": { "propcode": {
"questions": { "questions": {
"propcode": { "propcode": {
"check_answers_card_number": 0,
"check_answer_label": "", "check_answer_label": "",
"header": "property reference?", "header": "property reference?",
"type": "text" "type": "text"

0
spec/fixtures/imports/data_protection_confirmations/7c5bd5fb549c09a2c55d7cb90d7ba84927e64618.xml → spec/fixtures/imports/dataprotect/7c5bd5fb549c09a2c55d7cb90d7ba84927e64618.xml vendored

0
spec/fixtures/imports/organisations/7c5bd5fb549c09a2c55d7cb90d7ba84927e64618.xml → spec/fixtures/imports/institution/7c5bd5fb549c09a2c55d7cb90d7ba84927e64618.xml vendored

0
spec/fixtures/imports/case_logs/00d2343e-d5fa-4c89-8400-ec3854b0f2b4.xml → spec/fixtures/imports/logs/00d2343e-d5fa-4c89-8400-ec3854b0f2b4.xml vendored

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

0
spec/fixtures/imports/case_logs/0ead17cb-1668-442d-898c-0d52879ff592.xml → spec/fixtures/imports/logs/0ead17cb-1668-442d-898c-0d52879ff592.xml vendored

0
spec/fixtures/imports/case_logs/166fc004-392e-47a8-acb8-1c018734882b.xml → spec/fixtures/imports/logs/166fc004-392e-47a8-acb8-1c018734882b.xml vendored

0
spec/fixtures/imports/case_logs/5ybz29dj-l33t-k1l0-hj86-n4k4ma77xkcd.xml → spec/fixtures/imports/logs/5ybz29dj-l33t-k1l0-hj86-n4k4ma77xkcd.xml vendored

0
spec/fixtures/imports/case_logs/893ufj2s-lq77-42m4-rty6-ej09gh585uy1.xml → spec/fixtures/imports/logs/893ufj2s-lq77-42m4-rty6-ej09gh585uy1.xml vendored

0
spec/fixtures/imports/schemes/6d6d7618b58affe2a150a5ef2e9f4765fa6cd05d.xml → spec/fixtures/imports/mgmtgroups/6d6d7618b58affe2a150a5ef2e9f4765fa6cd05d.xml vendored

0
spec/fixtures/imports/organisation_rent_periods/ebd22326d33e389e9f1bfd546979d2c05f9e68d6.xml → spec/fixtures/imports/rent-period/ebd22326d33e389e9f1bfd546979d2c05f9e68d6.xml vendored

0
spec/fixtures/imports/scheme_locations/0ae7ad6dc0f1cf7ef33c18cc8c108bebc1b4923e.xml → spec/fixtures/imports/schemes/0ae7ad6dc0f1cf7ef33c18cc8c108bebc1b4923e.xml vendored

0
spec/fixtures/imports/scheme_locations/0bb3836b70b4dd9903263d5a764a5c45b964a89d.xml → spec/fixtures/imports/schemes/0bb3836b70b4dd9903263d5a764a5c45b964a89d.xml vendored

0
spec/fixtures/imports/users/10c887710550844e2551b3e0fb88dc9b4a8a642b.xml → spec/fixtures/imports/user/10c887710550844e2551b3e0fb88dc9b4a8a642b.xml vendored

0
spec/fixtures/imports/users/9ed81a262215a1634f0809effa683e38924d8bcb.xml → spec/fixtures/imports/user/9ed81a262215a1634f0809effa683e38924d8bcb.xml vendored

0
spec/fixtures/imports/users/b7829b1a5dfb68bb1e01c08445830c0add40907c.xml → spec/fixtures/imports/user/b7829b1a5dfb68bb1e01c08445830c0add40907c.xml vendored

0
spec/fixtures/imports/users/d4729b1a5dfb68bb1e01c08445830c0add40907c.xml → spec/fixtures/imports/user/d4729b1a5dfb68bb1e01c08445830c0add40907c.xml vendored

0
spec/fixtures/imports/users/d6717836154cd9a58f9e2f1d3077e3ab81e07613.xml → spec/fixtures/imports/user/d6717836154cd9a58f9e2f1d3077e3ab81e07613.xml vendored

0
spec/fixtures/imports/users/fc7625a02b24ae16162aa63ae7cb33feeec0c373.xml → spec/fixtures/imports/user/fc7625a02b24ae16162aa63ae7cb33feeec0c373.xml vendored

8
spec/lib/tasks/data_export_spec.rb

@ -5,7 +5,7 @@ describe "rake core:data_export", type: task do
subject(:task) { Rake::Task["core:data_export"] } subject(:task) { Rake::Task["core:data_export"] }
let(:paas_instance) { "paas_export_instance" } let(:paas_instance) { "paas_export_instance" }
let(:storage_service) { instance_double(StorageService) } let(:storage_service) { instance_double(S3StorageService) }
let(:paas_config_service) { instance_double(PaasConfigurationService) } let(:paas_config_service) { instance_double(PaasConfigurationService) }
let(:export_service) { instance_double(Exports::CaseLogExportService) } let(:export_service) { instance_double(Exports::CaseLogExportService) }
@ -14,7 +14,7 @@ describe "rake core:data_export", type: task do
Rake::Task.define_task(:environment) Rake::Task.define_task(:environment)
task.reenable task.reenable
allow(StorageService).to receive(:new).and_return(storage_service) allow(S3StorageService).to receive(:new).and_return(storage_service)
allow(PaasConfigurationService).to receive(:new).and_return(paas_config_service) allow(PaasConfigurationService).to receive(:new).and_return(paas_config_service)
allow(Exports::CaseLogExportService).to receive(:new).and_return(export_service) allow(Exports::CaseLogExportService).to receive(:new).and_return(export_service)
allow(ENV).to receive(:[]) allow(ENV).to receive(:[])
@ -23,7 +23,7 @@ describe "rake core:data_export", type: task do
context "when exporting case logs with no parameters" do context "when exporting case logs with no parameters" do
it "starts the XML export process" do it "starts the XML export process" do
expect(StorageService).to receive(:new).with(paas_config_service, paas_instance) expect(S3StorageService).to receive(:new).with(paas_config_service, paas_instance)
expect(Exports::CaseLogExportService).to receive(:new).with(storage_service) expect(Exports::CaseLogExportService).to receive(:new).with(storage_service)
expect(export_service).to receive(:export_xml_case_logs) expect(export_service).to receive(:export_xml_case_logs)
@ -33,7 +33,7 @@ describe "rake core:data_export", type: task do
context "when exporting case logs with CSV format" do context "when exporting case logs with CSV format" do
it "starts the CSV export process" do it "starts the CSV export process" do
expect(StorageService).to receive(:new).with(paas_config_service, paas_instance) expect(S3StorageService).to receive(:new).with(paas_config_service, paas_instance)
expect(Exports::CaseLogExportService).to receive(:new).with(storage_service) expect(Exports::CaseLogExportService).to receive(:new).with(storage_service)
expect(export_service).to receive(:export_csv_case_logs) expect(export_service).to receive(:export_csv_case_logs)

18
spec/lib/tasks/data_import_spec.rb

@ -5,7 +5,7 @@ describe "rake core:data_import", type: :task do
subject(:task) { Rake::Task["core:data_import"] } subject(:task) { Rake::Task["core:data_import"] }
let(:instance_name) { "paas_import_instance" } let(:instance_name) { "paas_import_instance" }
let(:storage_service) { instance_double(StorageService) } let(:storage_service) { instance_double(S3StorageService) }
let(:paas_config_service) { instance_double(PaasConfigurationService) } let(:paas_config_service) { instance_double(PaasConfigurationService) }
before do before do
@ -13,7 +13,7 @@ describe "rake core:data_import", type: :task do
Rake::Task.define_task(:environment) Rake::Task.define_task(:environment)
task.reenable task.reenable
allow(StorageService).to receive(:new).and_return(storage_service) allow(S3StorageService).to receive(:new).and_return(storage_service)
allow(PaasConfigurationService).to receive(:new).and_return(paas_config_service) allow(PaasConfigurationService).to receive(:new).and_return(paas_config_service)
allow(ENV).to receive(:[]) allow(ENV).to receive(:[])
allow(ENV).to receive(:[]).with("IMPORT_PAAS_INSTANCE").and_return(instance_name) allow(ENV).to receive(:[]).with("IMPORT_PAAS_INSTANCE").and_return(instance_name)
@ -29,7 +29,7 @@ describe "rake core:data_import", type: :task do
end end
it "creates an organisation from the given XML file" do it "creates an organisation from the given XML file" do
expect(StorageService).to receive(:new).with(paas_config_service, instance_name) expect(S3StorageService).to receive(:new).with(paas_config_service, instance_name)
expect(Imports::OrganisationImportService).to receive(:new).with(storage_service) expect(Imports::OrganisationImportService).to receive(:new).with(storage_service)
expect(import_service).to receive(:create_organisations).with(fixture_path) expect(import_service).to receive(:create_organisations).with(fixture_path)
@ -47,7 +47,7 @@ describe "rake core:data_import", type: :task do
end end
it "creates a user from the given XML file" do it "creates a user from the given XML file" do
expect(StorageService).to receive(:new).with(paas_config_service, instance_name) expect(S3StorageService).to receive(:new).with(paas_config_service, instance_name)
expect(Imports::UserImportService).to receive(:new).with(storage_service) expect(Imports::UserImportService).to receive(:new).with(storage_service)
expect(import_service).to receive(:create_users).with(fixture_path) expect(import_service).to receive(:create_users).with(fixture_path)
@ -65,7 +65,7 @@ describe "rake core:data_import", type: :task do
end end
it "creates an organisation from the given XML file" do it "creates an organisation from the given XML file" do
expect(StorageService).to receive(:new).with(paas_config_service, instance_name) expect(S3StorageService).to receive(:new).with(paas_config_service, instance_name)
expect(Imports::DataProtectionConfirmationImportService).to receive(:new).with(storage_service) expect(Imports::DataProtectionConfirmationImportService).to receive(:new).with(storage_service)
expect(import_service).to receive(:create_data_protection_confirmations).with(fixture_path) expect(import_service).to receive(:create_data_protection_confirmations).with(fixture_path)
@ -83,7 +83,7 @@ describe "rake core:data_import", type: :task do
end end
it "creates an organisation la from the given XML file" do it "creates an organisation la from the given XML file" do
expect(StorageService).to receive(:new).with(paas_config_service, instance_name) expect(S3StorageService).to receive(:new).with(paas_config_service, instance_name)
expect(Imports::OrganisationRentPeriodImportService).to receive(:new).with(storage_service) expect(Imports::OrganisationRentPeriodImportService).to receive(:new).with(storage_service)
expect(import_service).to receive(:create_organisation_rent_periods).with(fixture_path) expect(import_service).to receive(:create_organisation_rent_periods).with(fixture_path)
@ -101,7 +101,7 @@ describe "rake core:data_import", type: :task do
end end
it "creates case logs from the given XML file" do it "creates case logs from the given XML file" do
expect(StorageService).to receive(:new).with(paas_config_service, instance_name) expect(S3StorageService).to receive(:new).with(paas_config_service, instance_name)
expect(Imports::CaseLogsImportService).to receive(:new).with(storage_service) expect(Imports::CaseLogsImportService).to receive(:new).with(storage_service)
expect(import_service).to receive(:create_logs).with(fixture_path) expect(import_service).to receive(:create_logs).with(fixture_path)
@ -119,7 +119,7 @@ describe "rake core:data_import", type: :task do
end end
it "creates a scheme from the given XML file" do it "creates a scheme from the given XML file" do
expect(StorageService).to receive(:new).with(paas_config_service, instance_name) expect(S3StorageService).to receive(:new).with(paas_config_service, instance_name)
expect(Imports::SchemeImportService).to receive(:new).with(storage_service) expect(Imports::SchemeImportService).to receive(:new).with(storage_service)
expect(import_service).to receive(:create_schemes).with(fixture_path) expect(import_service).to receive(:create_schemes).with(fixture_path)
@ -137,7 +137,7 @@ describe "rake core:data_import", type: :task do
end end
it "creates a scheme location from the given XML file" do it "creates a scheme location from the given XML file" do
expect(StorageService).to receive(:new).with(paas_config_service, instance_name) expect(S3StorageService).to receive(:new).with(paas_config_service, instance_name)
expect(Imports::SchemeLocationImportService).to receive(:new).with(storage_service) expect(Imports::SchemeLocationImportService).to receive(:new).with(storage_service)
expect(import_service).to receive(:create_scheme_locations).with(fixture_path) expect(import_service).to receive(:create_scheme_locations).with(fixture_path)

10
spec/lib/tasks/date_import_field_spec.rb

@ -5,7 +5,7 @@ describe "rake core:data_import_field", type: :task do
subject(:task) { Rake::Task["core:data_import_field"] } subject(:task) { Rake::Task["core:data_import_field"] }
let(:instance_name) { "paas_import_instance" } let(:instance_name) { "paas_import_instance" }
let(:storage_service) { instance_double(StorageService) } let(:storage_service) { instance_double(S3StorageService) }
let(:paas_config_service) { instance_double(PaasConfigurationService) } let(:paas_config_service) { instance_double(PaasConfigurationService) }
before do before do
@ -13,7 +13,7 @@ describe "rake core:data_import_field", type: :task do
Rake::Task.define_task(:environment) Rake::Task.define_task(:environment)
task.reenable task.reenable
allow(StorageService).to receive(:new).and_return(storage_service) allow(S3StorageService).to receive(:new).and_return(storage_service)
allow(PaasConfigurationService).to receive(:new).and_return(paas_config_service) allow(PaasConfigurationService).to receive(:new).and_return(paas_config_service)
allow(ENV).to receive(:[]) allow(ENV).to receive(:[])
allow(ENV).to receive(:[]).with("IMPORT_PAAS_INSTANCE").and_return(instance_name) allow(ENV).to receive(:[]).with("IMPORT_PAAS_INSTANCE").and_return(instance_name)
@ -32,7 +32,7 @@ describe "rake core:data_import_field", type: :task do
let(:field) { "tenant_code" } let(:field) { "tenant_code" }
it "properly configures the storage service" do it "properly configures the storage service" do
expect(StorageService).to receive(:new).with(paas_config_service, instance_name) expect(S3StorageService).to receive(:new).with(paas_config_service, instance_name)
task.invoke(field, fixture_path) task.invoke(field, fixture_path)
end end
@ -46,7 +46,7 @@ describe "rake core:data_import_field", type: :task do
let(:field) { "lettings_allocation" } let(:field) { "lettings_allocation" }
it "properly configures the storage service" do it "properly configures the storage service" do
expect(StorageService).to receive(:new).with(paas_config_service, instance_name) expect(S3StorageService).to receive(:new).with(paas_config_service, instance_name)
task.invoke(field, fixture_path) task.invoke(field, fixture_path)
end end
@ -60,7 +60,7 @@ describe "rake core:data_import_field", type: :task do
let(:field) { "major_repairs" } let(:field) { "major_repairs" }
it "properly configures the storage service" do it "properly configures the storage service" do
expect(StorageService).to receive(:new).with(paas_config_service, instance_name) expect(S3StorageService).to receive(:new).with(paas_config_service, instance_name)
task.invoke(field, fixture_path) task.invoke(field, fixture_path)
end end

86
spec/lib/tasks/full_import_spec.rb

@ -0,0 +1,86 @@
require "rails_helper"
require "rake"
require "zip"
describe "rake core:full_import", type: :task do
subject(:task) { Rake::Task["core:full_import"] }
let(:s3_service) { instance_double(S3StorageService) }
let(:archive_service) { instance_double(ArchiveStorageService) }
let(:paas_config_service) { instance_double(PaasConfigurationService) }
before do
Rake.application.rake_require("tasks/full_import")
Rake::Task.define_task(:environment)
task.reenable
allow(PaasConfigurationService).to receive(:new).and_return(paas_config_service)
allow(S3StorageService).to receive(:new).and_return(s3_service)
allow(s3_service).to receive(:get_file_io)
allow(ArchiveStorageService).to receive(:new).and_return(archive_service)
end
context "when starting a full import" do
let(:case_logs_service) { instance_double(Imports::CaseLogsImportService) }
let(:rent_period_service) { instance_double(Imports::OrganisationRentPeriodImportService) }
let(:data_protection_service) { instance_double(Imports::DataProtectionConfirmationImportService) }
let(:user_service) { instance_double(Imports::UserImportService) }
let(:location_service) { instance_double(Imports::SchemeLocationImportService) }
let(:scheme_service) { instance_double(Imports::SchemeImportService) }
let(:organisation_service) { instance_double(Imports::OrganisationImportService) }
before do
allow(Imports::OrganisationImportService).to receive(:new).and_return(organisation_service)
allow(Imports::SchemeImportService).to receive(:new).and_return(scheme_service)
allow(Imports::SchemeLocationImportService).to receive(:new).and_return(location_service)
allow(Imports::UserImportService).to receive(:new).and_return(user_service)
allow(Imports::DataProtectionConfirmationImportService).to receive(:new).and_return(data_protection_service)
allow(Imports::OrganisationRentPeriodImportService).to receive(:new).and_return(rent_period_service)
allow(Imports::CaseLogsImportService).to receive(:new).and_return(case_logs_service)
end
it "raises an exception if no parameters are provided" do
expect { task.invoke }.to raise_error(/Usage/)
end
context "with all folders being present" do
before { allow(archive_service).to receive(:folder_present?).and_return(true) }
it "calls every import method with the correct folder" do
expect(organisation_service).to receive(:create_organisations).with("institution")
expect(scheme_service).to receive(:create_schemes).with("mgmtgroups")
expect(location_service).to receive(:create_scheme_locations).with("schemes")
expect(user_service).to receive(:create_users).with("user")
expect(data_protection_service).to receive(:create_data_protection_confirmations).with("dataprotect")
expect(rent_period_service).to receive(:create_organisation_rent_periods).with("rent-period")
expect(case_logs_service).to receive(:create_logs).with("logs")
task.invoke(fixture_path)
end
end
context "when a specific folders are missing" do
before do
allow(archive_service).to receive(:folder_present?).and_return(true)
allow(archive_service).to receive(:folder_present?).with("mgmtgroups").and_return(false)
allow(archive_service).to receive(:folder_present?).with("schemes").and_return(false)
allow(Rails.logger).to receive(:info)
end
it "only calls import methods for existing folders" do
expect(organisation_service).to receive(:create_organisations)
expect(user_service).to receive(:create_users)
expect(data_protection_service).to receive(:create_data_protection_confirmations)
expect(rent_period_service).to receive(:create_organisation_rent_periods)
expect(case_logs_service).to receive(:create_logs)
expect(scheme_service).not_to receive(:create_schemes)
expect(location_service).not_to receive(:create_scheme_locations)
expect(Rails.logger).to receive(:info).with("mgmtgroups does not exist, skipping Imports::SchemeImportService")
expect(Rails.logger).to receive(:info).with("schemes does not exist, skipping Imports::SchemeLocationImportService")
task.invoke(fixture_path)
end
end
end
end

82
spec/models/case_log_spec.rb

@ -1842,6 +1842,36 @@ RSpec.describe CaseLog do
end end
end end
context "when it changes from a supported housing to not a supported housing" do
let(:location) { FactoryBot.create(:location, mobility_type: "A", postcode: "SW1P 4DG") }
let(:case_log) { FactoryBot.create(:case_log, location:) }
it "resets inferred wchair value" do
case_log.update!({ needstype: 2 })
record_from_db = ActiveRecord::Base.connection.execute("select wchair from case_logs where id=#{case_log.id}").to_a[0]
expect(record_from_db["wchair"]).to eq(2)
expect(case_log["wchair"]).to eq(2)
case_log.update!({ needstype: 1 })
record_from_db = ActiveRecord::Base.connection.execute("select needstype from case_logs where id=#{case_log.id}").to_a[0]
expect(record_from_db["wchair"]).to eq(nil)
expect(case_log["wchair"]).to eq(nil)
end
it "resets location" do
case_log.update!({ needstype: 2 })
record_from_db = ActiveRecord::Base.connection.execute("select location_id from case_logs where id=#{case_log.id}").to_a[0]
expect(record_from_db["location_id"]).to eq(location.id)
expect(case_log["location_id"]).to eq(location.id)
case_log.update!({ needstype: 1 })
record_from_db = ActiveRecord::Base.connection.execute("select location_id from case_logs where id=#{case_log.id}").to_a[0]
expect(record_from_db["location_id"]).to eq(nil)
expect(case_log["location_id"]).to eq(nil)
end
end
context "when it is not a renewal" do context "when it is not a renewal" do
let(:case_log) { FactoryBot.create(:case_log) } let(:case_log) { FactoryBot.create(:case_log) }
@ -2043,6 +2073,20 @@ RSpec.describe CaseLog do
expect(result.count).to eq(1) expect(result.count).to eq(1)
expect(result.first.id).to eq case_log_to_search.id expect(result.first.id).to eq case_log_to_search.id
end end
context "when case log is supported housing" do
let(:location) { FactoryBot.create(:location, postcode: "W6 0ST") }
before do
case_log_to_search.update!(needstype: 2, location:)
end
it "allows searching by a Property Postcode" do
result = described_class.filter_by_location_postcode("W6 0ST")
expect(result.count).to eq(1)
expect(result.first.id).to eq case_log_to_search.id
end
end
end end
describe "#search_by" do describe "#search_by" do
@ -2070,6 +2114,20 @@ RSpec.describe CaseLog do
expect(result.first.id).to eq case_log_to_search.id expect(result.first.id).to eq case_log_to_search.id
end end
context "when case log is supported housing" do
let(:location) { FactoryBot.create(:location, postcode: "W6 0ST") }
before do
case_log_to_search.update!(needstype: 2, location:)
end
it "allows searching by a Property Postcode" do
result = described_class.search_by("W6 0ST")
expect(result.count).to eq(1)
expect(result.first.id).to eq case_log_to_search.id
end
end
context "when postcode has spaces and lower case letters" do context "when postcode has spaces and lower case letters" do
let(:matching_postcode_lower_case_with_spaces) { case_log_to_search.postcode_full.downcase.chars.insert(3, " ").join } let(:matching_postcode_lower_case_with_spaces) { case_log_to_search.postcode_full.downcase.chars.insert(3, " ").join }
@ -2235,22 +2293,32 @@ RSpec.describe CaseLog do
end end
describe "csv download" do describe "csv download" do
let(:csv_export_file) { File.open("spec/fixtures/files/case_logs_download.csv", "r:UTF-8") }
let(:scheme) { FactoryBot.create(:scheme) } let(:scheme) { FactoryBot.create(:scheme) }
let(:location) { FactoryBot.create(:location, :export, scheme:, type_of_unit: 6, postcode: "SE11TE") } let(:location) { FactoryBot.create(:location, :export, scheme:, type_of_unit: 6, postcode: "SE11TE") }
let(:user) { FactoryBot.create(:user, organisation: location.scheme.owning_organisation) } let(:user) { FactoryBot.create(:user, organisation: location.scheme.owning_organisation) }
let(:expected_content) { csv_export_file.read }
before do before do
Timecop.freeze(Time.utc(2022, 6, 5)) Timecop.freeze(Time.utc(2022, 6, 5))
end
it "generates a correct csv from a case log" do
case_log = FactoryBot.create(:case_log, needstype: 2, scheme:, location:, owning_organisation: scheme.owning_organisation, created_by: user) case_log = FactoryBot.create(:case_log, needstype: 2, scheme:, location:, owning_organisation: scheme.owning_organisation, created_by: user)
expected_content = csv_export_file.read
expected_content.sub!(/\{id\}/, case_log["id"].to_s) expected_content.sub!(/\{id\}/, case_log["id"].to_s)
expected_content.sub!(/\{owning_org_id\}/, case_log["owning_organisation_id"].to_s)
expected_content.sub!(/\{scheme_id\}/, scheme["service_name"].to_s) expected_content.sub!(/\{scheme_id\}/, scheme["service_name"].to_s)
expect(described_class.to_csv).to eq(expected_content) end
context "with a support user" do
let(:csv_export_file) { File.open("spec/fixtures/files/case_logs_download.csv", "r:UTF-8") }
it "generates a correct csv from a case log" do
expect(described_class.to_csv).to eq(expected_content)
end
end
context "with a non support user" do
let(:csv_export_file) { File.open("spec/fixtures/files/case_logs_download_non_support.csv", "r:UTF-8") }
it "generates a correct csv from a case log" do
expect(described_class.to_csv(user)).to eq(expected_content)
end
end end
end end
end end

4
spec/models/form/subsection_spec.rb

@ -25,12 +25,12 @@ RSpec.describe Form::Subsection, type: :model do
end end
it "has pages" do it "has pages" do
expected_pages = %w[tenant_code_test person_1_age person_1_gender person_1_working_situation household_number_of_members person_2_working_situation propcode] expected_pages = %w[tenant_code_test person_1_age person_1_gender person_1_working_situation household_number_of_members retirement_value_check person_2_working_situation propcode]
expect(subsection.pages.map(&:id)).to eq(expected_pages) expect(subsection.pages.map(&:id)).to eq(expected_pages)
end end
it "has questions" do it "has questions" do
expected_questions = %w[tenancycode age1 sex1 ecstat1 hhmemb relat2 age2 sex2 ecstat2 propcode] expected_questions = %w[tenancycode age1 sex1 ecstat1 hhmemb relat2 age2 sex2 retirement_value_check ecstat2 propcode]
expect(subsection.questions.map(&:id)).to eq(expected_questions) expect(subsection.questions.map(&:id)).to eq(expected_questions)
end end

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

Loading…
Cancel
Save