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. 35
      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"
# Possessives in strings
gem "possessive"
# Strip whitespace from active record attributes
gem "auto_strip_attributes"
group :development, :test do
# Check gems for known vulnerabilities

36
Gemfile.lock

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

7
app/controllers/form_controller.rb

@ -73,7 +73,7 @@ private
end
if session["fields"]
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
end
end
@ -89,7 +89,7 @@ private
year = params["case_log"]["#{question.id}(1i)"]
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)
else
Date.new(0, 1, 1)
@ -161,10 +161,11 @@ private
def question_missing_response?(responses_for_page, question)
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
params["case_log"][question.id].exclude?(option)
end
answered.all?
else
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?

2
app/controllers/locations_controller.rb

@ -56,7 +56,7 @@ class LocationsController < ApplicationController
when "edit"
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"
redirect_to(locations_path(@scheme))
redirect_to(scheme_check_answers_path(@scheme, anchor: "locations"))
end
else
render :edit, status: :unprocessable_entity

19
app/controllers/schemes_controller.rb

@ -5,9 +5,9 @@ class SchemesController < ApplicationController
before_action :authenticate_user!
before_action :find_resource, except: %i[index]
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
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?
all_schemes = Scheme.all.order("service_name ASC")
@ -52,10 +52,19 @@ class SchemesController < ApplicationController
check_answers = params[:scheme][:check_answers]
page = params[:scheme][:page]
scheme_previously_confirmed = @scheme.confirmed?
validation_errors 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
redirect_to scheme_secondary_client_group_path(@scheme, check_answers: "true")
else
@ -177,7 +186,7 @@ private
scheme_details_path(@scheme)
end
when "edit-name"
scheme_path(@scheme)
scheme_check_answers_path(@scheme)
when "check-answers"
schemes_path(scheme_id: @scheme.id)
end
@ -246,4 +255,8 @@ private
render_not_found and return
end
end
def redirect_if_scheme_confirmed
redirect_to @scheme if @scheme.confirmed?
end
end

8
app/controllers/users_controller.rb

@ -58,7 +58,7 @@ class UsersController < ApplicationController
end
elsif user_params.key?("password")
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
else
format_error_messages
@ -79,7 +79,6 @@ class UsersController < ApplicationController
redirect_to created_user_redirect_path
else
unless @resource.errors[:organisation].empty?
@resource.errors.add(:organisation_id, message: @resource.errors[:organisation])
@resource.errors.delete(:organisation)
end
render :new, status: :unprocessable_entity
@ -87,7 +86,7 @@ class UsersController < ApplicationController
end
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" }
end
@ -114,9 +113,6 @@ private
if user_params[:role].present? && !current_user.assignable_roles.key?(user_params[:role].to_sym)
@resource.errors.add :role, I18n.t("validations.role.invalid")
end
if user_params[:role].empty?
@resource.errors.add :role, I18n.t("activerecord.errors.models.user.attributes.role.blank")
end
end
def format_error_messages

17
app/helpers/check_answers_helper.rb

@ -11,6 +11,23 @@ module CheckAnswersHelper
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
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_propcode, ->(propcode) { where("propcode ILIKE ?", "%#{propcode}%") }
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|
filter_by_id(param)
.or(filter_by_tenant_code(param))
.or(filter_by_propcode(param))
.or(filter_by_postcode(param))
filter_by_location_postcode(param)
.or(filter_by_tenant_code(param))
.or(filter_by_propcode(param))
.or(filter_by_postcode(param))
.or(filter_by_id(param))
}
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
SUFFIX_FROM_PERIOD = { 2 => "every 2 weeks", 3 => "every 4 weeks", 4 => "every month" }.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
def form
@ -424,18 +427,43 @@ class CaseLog < ApplicationRecord
[30, 31].any?(prevten)
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 << attribute_names + %w[unittype_sh]
attributes = csv_attributes(user)
csv << attributes
all.find_each do |record|
csv << record.attributes.merge({ "unittype_sh" => record.unittype_sh, "la" => record.la }).map do |att, val|
record.form.get_question(att, record)&.label_from_value(val) || val
csv << attributes.map do |att|
record.form.get_question(att, record)&.label_from_value(record.send(att)) || label_from_value(record.send(att))
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
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'}"
@ -536,7 +564,9 @@ private
dependent_questions = { waityear: [{ key: :renewal, value: 0 }],
homeless: [{ 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|
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?
return false unless scheme
@scheme_locations_count ||= scheme.locations.size
@scheme_locations_count ||= scheme.locations.active.size
@scheme_locations_count > 1
end
@ -188,7 +188,7 @@ private
def reset_scheme_location!
self.location = nil
if scheme && scheme.locations.size == 1
if scheme && scheme.locations.active.size == 1
self.location = scheme.locations.first
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,
:conditional_for, :readonly, :answer_options, :page, :check_answer_label,
: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
TOP = 1
@ -37,6 +37,7 @@ class Form::Question
@suffix = hsh["suffix"]
@requires_js = hsh["requires_js"]
@fields_added = hsh["fields_added"]
@check_answers_card_number = hsh["check_answers_card_number"]
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"
@header = ""
@description = ""
@questions = questions
@subsection = subsection
end
def questions
[
@questions ||= [
Form::Setup::Questions::CreatedById.new(nil, nil, self),
]
end

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

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

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

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

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

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

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

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

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

@ -4,12 +4,11 @@ class Form::Setup::Pages::Renewal < ::Form::Page
@id = "renewal"
@header = ""
@description = ""
@questions = questions
@subsection = subsection
end
def questions
[
@questions ||= [
Form::Setup::Questions::Renewal.new(nil, nil, self),
]
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)
@header = ""
@description = ""
@questions = questions
@depends_on = [{ "supported_housing_schemes_enabled?" => true }]
@derived = true
end
def questions
[
@questions ||= [
Form::Setup::Questions::RentType.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)
@header = ""
@description = ""
@questions = questions
@depends_on = [{
"supported_housing_schemes_enabled?" => true,
"needstype" => 2,
@ -11,7 +10,7 @@ class Form::Setup::Pages::Scheme < ::Form::Page
end
def questions
[
@questions ||= [
Form::Setup::Questions::SchemeId.new(nil, nil, self),
]
end

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

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

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

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

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

@ -3,12 +3,11 @@ class Form::Subsections::Setup < ::Form::Subsection
super
@id = "setup"
@label = "Set up this lettings log"
@pages = [pages]
@section = section
end
def pages
[
@pages ||= [
Form::Setup::Pages::Organisation.new(nil, nil, self),
Form::Setup::Pages::CreatedBy.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
end
def enabled?(_case_log)
true
end
private
def support_only_questions

4
app/models/location.rb

@ -7,11 +7,15 @@ class Location < ApplicationRecord
before_save :lookup_postcode!, if: :postcode_changed?
auto_strip_attributes :name
attr_accessor :add_another_location
scope :search_by_postcode, ->(postcode) { where("REPLACE(postcode, ' ', '') ILIKE ?", "%#{postcode.delete(' ')}%") }
scope :search_by_name, ->(name) { where("name ILIKE ?", "%#{name}%") }
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 = {
"Wheelchair-user standard": "W",

29
app/models/organisation.rb

@ -1,17 +1,23 @@
class Organisation < ApplicationRecord
has_many :users
has_many :owned_case_logs, class_name: "CaseLog", foreign_key: "owning_organisation_id"
has_many :users, dependent: :delete_all
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 :data_protection_confirmations
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 :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, ->(param) { search_by_name(param) }
has_paper_trail
auto_strip_attributes :name
PROVIDER_TYPE = {
LA: 1,
PRP: 2,
@ -63,15 +69,16 @@ class Organisation < ApplicationRecord
def display_attributes
[
{ name: "name", value: name, editable: true },
{ name: "address", value: address_string, editable: true },
{ name: "telephone_number", value: phone, editable: true },
{ name: "type", value: display_provider_type, editable: false },
{ name: "rent_periods", value: rent_period_labels, editable: false, format: :bullet },
{ name: "Name", value: name, editable: true },
{ name: "Address", value: address_string, editable: true },
{ name: "Telephone_number", value: phone, editable: true },
{ name: "Type of provider", value: display_provider_type, editable: false },
{ 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: "other_stock_owners", value: other_stock_owners, editable: false },
{ name: "managing_agents", value: managing_agents, editable: false },
{ name: "data_protection_agreement", value: data_protection_agreement_string, editable: false },
{ name: "Other stock owners", value: other_stock_owners, editable: false },
{ name: "Managing agents", value: managing_agents, editable: false },
{ name: "Data protection agreement", value: data_protection_agreement_string, editable: false },
]
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
belongs_to :owning_organisation, class_name: "Organisation"
belongs_to :managing_organisation, optional: true, class_name: "Organisation"
has_many :locations
has_many :case_logs
has_many :locations, dependent: :delete_all
has_many :case_logs, dependent: :delete_all
has_paper_trail
@ -19,6 +19,8 @@ class Scheme < ApplicationRecord
validate :validate_confirmed
auto_strip_attributes :service_name
SENSITIVE = {
No: 0,
Yes: 1,

36
app/models/user.rb

@ -1,16 +1,23 @@
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :omniauthable
include Helpers::Email
devise :database_authenticatable, :recoverable, :rememberable, :validatable,
devise :database_authenticatable, :recoverable, :rememberable,
:trackable, :lockable, :two_factor_authenticatable, :confirmable, :timeoutable
belongs_to :organisation
has_many :owned_case_logs, through: :organisation
# Marked as optional because we validate organisation_id below instead so that
# 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
validate :validate_email
validates :name, :email, presence: true
validates :name, 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
current_sign_in_at
@ -27,6 +34,8 @@ class User < ApplicationRecord
has_one_time_password(encrypted: true)
auto_strip_attributes :name
ROLES = {
data_provider: 1,
data_coordinator: 2,
@ -154,15 +163,12 @@ class User < ApplicationRecord
super && active?
end
private
protected
def validate_email
unless email_valid?(email)
if User.exists?(["email LIKE ?", "%#{email}%"])
errors.add :email, I18n.t("activerecord.errors.models.user.attributes.email.taken")
else
errors.add :email, I18n.t("activerecord.errors.models.user.attributes.email.invalid")
end
end
# Checks whether a password is needed or not. For validations only.
# Passwords are always required if it's a new record, or if the password
# or confirmation are being set somewhere.
def password_required?
!persisted? || !password.nil? || !password_confirmation.nil?
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")
end
if record["mrcdate"].present? && record["startdate"].present? && record["startdate"].to_date - record["mrcdate"].to_date > 730
record.errors.add :mrcdate, I18n.t("validations.property.mrcdate.730_days_before_tenancy_start")
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.ten_years_before_tenancy_start")
end
end

11
app/models/validations/soft_validations.rb

@ -62,6 +62,17 @@ module Validations::SoftValidations
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
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
attributes["pregnancy_value_check"] = 0
attributes["major_repairs_date_value_check"] = 0
attributes["void_date_value_check"] = 0
attributes["retirement_value_check"] = 0
attributes["rent_value_check"] = 0
attributes["net_income_value_check"] = 0
@ -273,7 +275,7 @@ module Imports
end
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
def check_status_completed(case_log, previous_status)

22
app/services/imports/scheme_location_import_service.rb

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

2
app/services/imports/user_import_service.rb

@ -13,8 +13,8 @@ module Imports
def create_user(xml_document)
organisation = Organisation.find_by(old_org_id: user_field_value(xml_document, "institution"))
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
name = user_field_value(xml_document, "full-name") || email
deleted = user_field_value(xml_document, "deleted")
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
attr_reader :configuration
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) }
def list_files(_folder)
raise NotImplementedError
end
def get_file_io(file_name)
@client.get_object(bucket: @configuration.bucket_name, key: file_name)
.body
def folder_present?(_folder)
raise NotImplementedError
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]
def get_file_io(_file_name)
raise NotImplementedError
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
def write_file(_file_name, _data)
raise NotImplementedError
end
end

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

@ -29,7 +29,7 @@
<%= status_tag(@case_log.status) %>
</p>
<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>
<% end %>
<%= render "tasklist" %>

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

@ -17,10 +17,16 @@
<% end %>
<%= display_answered_questions_summary(subsection, @case_log, current_user) %>
<%= render partial: "form/check_answers_summary_list", locals: {
subsection:,
case_log: @case_log,
} %>
<% if any_questions_have_summary_card_number?(subsection, @case_log) %>
<% subsection.applicable_questions(@case_log).group_by(&:check_answers_card_number).values.each do |question_group| %>
<%= 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| %>
<%= f.govuk_submit "Save and return to log" do %>

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

@ -11,7 +11,7 @@
<%= content_for(:title) %>
</h1>
<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>
<% @case_log.form.sections.map do |section| %>
<h2 class="govuk-heading-m"><%= section.label %></h2>

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

@ -3,11 +3,11 @@
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: "/schemes/#{@scheme.id}/support",
href: scheme_check_answers_path(@scheme, anchor: "locations"),
) %>
<% 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-column-two-thirds">
<%= f.govuk_error_summary %>

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

@ -22,7 +22,7 @@
<% row.action(
visually_hidden_text: attr[:name].to_s.humanize.downcase,
href: edit_organisation_path,
html_attributes: { "data-qa": "change-#{attr[:name]}" },
html_attributes: { "data-qa": "change-#{attr[:name].downcase}" },
) %>
<% end %>
<% 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">
<%= attribute[:name].to_s %>
</dt>
@ -14,7 +14,7 @@
<%= details_html(attribute) %>
</dd>
<% end %>
<% if !scheme.confirmed? || attribute[:name] == "Name" %>
<% if can_change_scheme_answer?(attribute[:name], scheme) %>
<dd class="govuk-summary-list__actions">
<a class="govuk-link" href="<%= change_link %>">Change</a>
</dd>

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

@ -10,7 +10,7 @@
<dl class="govuk-summary-list">
<% @scheme.check_details_attributes.each do |attr| %>
<% 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 %>
<% if !@scheme.arrangement_type_same? %>
<% @scheme.check_support_services_provider_attributes.each do |attr| %>
@ -68,7 +68,7 @@
<%= table.body do |body| %>
<%= body.row do |row| %>
<% 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: simple_format("<span>#{location.type_of_unit}</span>")) %>
<% 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 } %>
<% 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,
secondary_client_group_selection,
:id,

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

@ -37,11 +37,12 @@
answer_options,
:id,
:name,
"data-controller": "accessible-autocomplete",
label: { text: "Organisation", size: "m" },
options: { disabled: [""], selected: @organisation_id ? answer_options.first : "" } %>
<% 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]) } %>
@ -52,7 +53,8 @@
lambda { |option|
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" %>
</div>

432
config/forms/2021_2022.json

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

436
config/forms/2022_2023.json

@ -34,7 +34,14 @@
"conditional_for": {
"postcode_full": [1]
},
"hidden_in_check_answers": true
"hidden_in_check_answers": {
"depends_on": [{
"postcode_known": 0
},
{
"postcode_known": 1
}]
}
},
"postcode_full": {
"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": {
"header": "",
"description": "",
@ -860,6 +899,38 @@
"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": "",
"guidance_partial": "privacy_notice",
"check_answer_label": "Tenant has seen the privacy notice",
"check_answers_card_number": 0,
"type": "checkbox",
"answer_options": {
"declaration": {
@ -1105,6 +1177,7 @@
"description": "",
"questions": {
"hhmemb": {
"check_answers_card_number": 0,
"check_answer_label": "Number of household members",
"header": "How many people live in the household for this letting?",
"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": {
"depends_on": [{ "female_in_pregnant_household_in_soft_validation_range?": true }],
"depends_on": [
{
"female_in_pregnant_household_in_soft_validation_range?": true
}
],
"title_text": {
"translation": "soft_validations.pregnancy.title",
"arguments": [
@ -1200,6 +1277,7 @@
"description": "",
"questions": {
"age1_known": {
"check_answers_card_number": 1,
"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.",
"type": "radio",
@ -1226,6 +1304,7 @@
}
},
"age1": {
"check_answers_card_number": 1,
"header": "Age",
"check_answer_label": "Lead tenant’s age",
"type": "numeric",
@ -1283,7 +1362,11 @@
}
},
"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": {
"translation": "soft_validations.pregnancy.title",
"arguments": [
@ -1327,6 +1410,7 @@
"questions": {
"sex1": {
"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?",
"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",
@ -1391,7 +1475,11 @@
}
},
"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": {
"translation": "soft_validations.pregnancy.title",
"arguments": [
@ -1435,6 +1523,7 @@
"questions": {
"ethnic_group": {
"check_answer_label": "Lead tenant’s ethnic group",
"check_answers_card_number": 1,
"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.",
"type": "radio",
@ -1470,6 +1559,7 @@
"description": "",
"questions": {
"ethnic": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s ethnic 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.",
@ -1491,6 +1581,7 @@
"description": "",
"questions": {
"ethnic": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s ethnic 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.",
@ -1521,6 +1612,7 @@
"description": "",
"questions": {
"ethnic": {
"check_answers_card_number": 1,
"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?",
"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": "",
"questions": {
"ethnic": {
"check_answers_card_number": 1,
"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?",
"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": "",
"questions": {
"ethnic": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s ethnic 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.",
@ -1599,6 +1693,7 @@
"description": "",
"questions": {
"national": {
"check_answers_card_number": 1,
"check_answer_label": "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.",
@ -1632,6 +1727,7 @@
"ecstat1": {
"check_answer_label": "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.",
"type": "radio",
"answer_options": {
@ -1702,8 +1798,17 @@
},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement age soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?",
"type": "interruption_screen",
"answer_options": {
@ -1718,7 +1823,9 @@
}
},
"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": {
"translation": "soft_validations.retirement.max.title",
"arguments": [
@ -1746,8 +1853,17 @@
},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement age soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?",
"type": "interruption_screen",
"answer_options": {
@ -1767,6 +1883,7 @@
"questions": {
"details_known_2": {
"check_answer_label": "Details known for person 2",
"check_answers_card_number": 2,
"header": "Do you know details for person 2?",
"hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio",
@ -1810,6 +1927,7 @@
"questions": {
"relat2": {
"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?",
"hint_text": "",
"type": "radio",
@ -1845,6 +1963,7 @@
"questions": {
"age2_known": {
"header": "Do you know person 2’s age?",
"check_answers_card_number": 2,
"hint_text": "",
"type": "radio",
"answer_options": {
@ -1872,6 +1991,7 @@
"age2": {
"header": "Age",
"check_answer_label": "Person 2’s age",
"check_answers_card_number": 2,
"type": "numeric",
"min": 0,
"max": 120,
@ -1983,6 +2103,7 @@
"sex2": {
"check_answer_label": "Person 2’s gender identity",
"header": "Which of these best describes person 2’s gender identity?",
"check_answers_card_number": 2,
"hint_text": "",
"type": "radio",
"answer_options": {
@ -2104,6 +2225,7 @@
"questions": {
"ecstat2": {
"check_answer_label": "Person 2’s working situation",
"check_answers_card_number": 2,
"header": "Which of these best describes person 2’s working situation?",
"hint_text": "",
"type": "radio",
@ -2194,8 +2316,17 @@
},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement age soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?",
"type": "interruption_screen",
"answer_options": {
@ -2240,8 +2371,17 @@
},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement age soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?",
"type": "interruption_screen",
"answer_options": {
@ -2261,6 +2401,7 @@
"questions": {
"details_known_3": {
"check_answer_label": "Details known for person 3",
"check_answers_card_number": 3,
"header": "Do you know details for person 3?",
"hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio",
@ -2301,6 +2442,7 @@
"questions": {
"relat3": {
"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?",
"hint_text": "",
"type": "radio",
@ -2336,6 +2478,7 @@
"questions": {
"age3_known": {
"header": "Do you know person 3’s age?",
"check_answers_card_number": 3,
"hint_text": "",
"type": "radio",
"answer_options": {
@ -2363,6 +2506,7 @@
"age3": {
"header": "Age",
"check_answer_label": "Person 3’s age",
"check_answers_card_number": 3,
"type": "numeric",
"min": 0,
"max": 120,
@ -2473,6 +2617,7 @@
"questions": {
"sex3": {
"check_answer_label": "Person 3’s gender identity",
"check_answers_card_number": 3,
"header": "Which of these best describes person 3’s gender identity?",
"hint_text": "",
"type": "radio",
@ -2595,6 +2740,7 @@
"questions": {
"ecstat3": {
"check_answer_label": "Person 3’s working situation",
"check_answers_card_number": 3,
"header": "Which of these best describes person 3’s working situation?",
"hint_text": "",
"type": "radio",
@ -2685,8 +2831,17 @@
},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement age soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?",
"type": "interruption_screen",
"answer_options": {
@ -2731,8 +2886,17 @@
},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement age soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?",
"type": "interruption_screen",
"answer_options": {
@ -2752,6 +2916,7 @@
"questions": {
"details_known_4": {
"check_answer_label": "Details known for person 4",
"check_answers_card_number": 4,
"header": "Do you know details for person 4?",
"hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio",
@ -2789,6 +2954,7 @@
"questions": {
"relat4": {
"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?",
"hint_text": "",
"type": "radio",
@ -2824,6 +2990,7 @@
"questions": {
"age4_known": {
"header": "Do you know person 4’s age?",
"check_answers_card_number": 4,
"hint_text": "",
"type": "radio",
"answer_options": {
@ -2851,6 +3018,7 @@
"age4": {
"header": "Age",
"check_answer_label": "Person 4’s age",
"check_answers_card_number": 4,
"type": "numeric",
"min": 0,
"max": 120,
@ -2961,6 +3129,7 @@
"questions": {
"sex4": {
"check_answer_label": "Person 4’s gender identity",
"check_answers_card_number": 4,
"header": "Which of these best describes person 4’s gender identity?",
"hint_text": "",
"type": "radio",
@ -3083,6 +3252,7 @@
"questions": {
"ecstat4": {
"check_answer_label": "Person 4’s working situation",
"check_answers_card_number": 4,
"header": "Which of these best describes person 4’s working situation?",
"hint_text": "",
"type": "radio",
@ -3173,8 +3343,17 @@
},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement age soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?",
"type": "interruption_screen",
"answer_options": {
@ -3219,8 +3398,17 @@
},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement age soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?",
"type": "interruption_screen",
"answer_options": {
@ -3240,6 +3428,7 @@
"questions": {
"details_known_5": {
"check_answer_label": "Details known for person 5",
"check_answers_card_number": 5,
"header": "Do you know details for person 5?",
"hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio",
@ -3274,6 +3463,7 @@
"questions": {
"relat5": {
"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?",
"hint_text": "",
"type": "radio",
@ -3309,6 +3499,7 @@
"questions": {
"age5_known": {
"header": "Do you know person 5’s age?",
"check_answers_card_number": 5,
"hint_text": "",
"type": "radio",
"answer_options": {
@ -3336,6 +3527,7 @@
"age5": {
"header": "Age",
"check_answer_label": "Person 5’s age",
"check_answers_card_number": 5,
"type": "numeric",
"min": 0,
"max": 120,
@ -3446,6 +3638,7 @@
"questions": {
"sex5": {
"check_answer_label": "Person 5’s gender identity",
"check_answers_card_number": 5,
"header": "Which of these best describes person 5’s gender identity?",
"hint_text": "",
"type": "radio",
@ -3568,6 +3761,7 @@
"questions": {
"ecstat5": {
"check_answer_label": "Person 5’s working situation",
"check_answers_card_number": 5,
"header": "Which of these best describes person 5’s working situation?",
"hint_text": "",
"type": "radio",
@ -3658,8 +3852,17 @@
},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement age soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?",
"type": "interruption_screen",
"answer_options": {
@ -3704,8 +3907,17 @@
},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement age soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?",
"type": "interruption_screen",
"answer_options": {
@ -3725,6 +3937,7 @@
"questions": {
"details_known_6": {
"check_answer_label": "Details known for person 6",
"check_answers_card_number": 6,
"header": "Do you know details for person 6?",
"hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio",
@ -3756,6 +3969,7 @@
"questions": {
"relat6": {
"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?",
"hint_text": "",
"type": "radio",
@ -3791,6 +4005,7 @@
"questions": {
"age6_known": {
"header": "Do you know person 6’s age?",
"check_answers_card_number": 6,
"hint_text": "",
"type": "radio",
"answer_options": {
@ -3818,6 +4033,7 @@
"age6": {
"header": "Age",
"check_answer_label": "Person 6’s age",
"check_answers_card_number": 6,
"type": "numeric",
"min": 0,
"max": 120,
@ -3928,6 +4144,7 @@
"questions": {
"sex6": {
"check_answer_label": "Person 6’s gender identity",
"check_answers_card_number": 6,
"header": "Which of these best describes person 6’s gender identity?",
"hint_text": "",
"type": "radio",
@ -4050,6 +4267,7 @@
"questions": {
"ecstat6": {
"check_answer_label": "Person 6’s working situation",
"check_answers_card_number": 6,
"header": "Which of these best describes person 6’s working situation?",
"hint_text": "",
"type": "radio",
@ -4140,8 +4358,17 @@
},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement age soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?",
"type": "interruption_screen",
"answer_options": {
@ -4186,8 +4413,17 @@
},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement age soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?",
"type": "interruption_screen",
"answer_options": {
@ -4207,6 +4443,7 @@
"questions": {
"details_known_7": {
"check_answer_label": "Details known for person 7",
"check_answers_card_number": 7,
"header": "Do you know details for person 7?",
"hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio",
@ -4235,6 +4472,7 @@
"questions": {
"relat7": {
"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?",
"hint_text": "",
"type": "radio",
@ -4270,6 +4508,7 @@
"questions": {
"age7_known": {
"header": "Do you know person 7’s age?",
"check_answers_card_number": 7,
"hint_text": "",
"type": "radio",
"answer_options": {
@ -4297,6 +4536,7 @@
"age7": {
"header": "Age",
"check_answer_label": "Person 7’s age",
"check_answers_card_number": 7,
"type": "numeric",
"min": 0,
"max": 120,
@ -4407,6 +4647,7 @@
"questions": {
"sex7": {
"check_answer_label": "Person 7’s gender identity",
"check_answers_card_number": 7,
"header": "Which of these best describes person 7’s gender identity?",
"hint_text": "",
"type": "radio",
@ -4529,6 +4770,7 @@
"questions": {
"ecstat7": {
"check_answer_label": "Person 7’s working situation",
"check_answers_card_number": 7,
"header": "Which of these best describes person 7’s working situation?",
"hint_text": "",
"type": "radio",
@ -4619,8 +4861,17 @@
},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement age soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?",
"type": "interruption_screen",
"answer_options": {
@ -4665,8 +4916,17 @@
},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement age soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?",
"type": "interruption_screen",
"answer_options": {
@ -4686,6 +4946,7 @@
"questions": {
"details_known_8": {
"check_answer_label": "Details known for person 8",
"check_answers_card_number": 8,
"header": "Do you know details for person 8?",
"hint_text": "You must provide details for everyone in the household if you know them.",
"type": "radio",
@ -4711,6 +4972,7 @@
"questions": {
"relat8": {
"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?",
"hint_text": "",
"type": "radio",
@ -4746,6 +5008,7 @@
"questions": {
"age8_known": {
"header": "Do you know person 8’s age?",
"check_answers_card_number": 8,
"hint_text": "",
"type": "radio",
"answer_options": {
@ -4773,6 +5036,7 @@
"age8": {
"header": "Age",
"check_answer_label": "Person 8’s age",
"check_answers_card_number": 8,
"type": "numeric",
"min": 0,
"max": 120,
@ -4883,6 +5147,7 @@
"questions": {
"sex8": {
"check_answer_label": "Person 8’s gender identity",
"check_answers_card_number": 8,
"header": "Which of these best describes person 8’s gender identity?",
"hint_text": "",
"type": "radio",
@ -5005,6 +5270,7 @@
"questions": {
"ecstat8": {
"check_answer_label": "Person 8’s working situation",
"check_answers_card_number": 8,
"header": "Which of these best describes person 8’s working situation?",
"hint_text": "",
"type": "radio",
@ -5095,8 +5361,17 @@
},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement age soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person is retired?",
"type": "interruption_screen",
"answer_options": {
@ -5141,8 +5416,17 @@
},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement age soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Retirement confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"retirement_value_check": 0
},
{
"retirement_value_check": 1
}
]
},
"header": "Are you sure this person isn’t retired?",
"type": "interruption_screen",
"answer_options": {
@ -5362,8 +5646,16 @@
},
"questions": {
"pregnancy_value_check": {
"check_answer_label": "Pregnancy soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Pregnancy confirmation",
"hidden_in_check_answers": {
"depends_on": [{
"pregnancy_value_check": 0
},
{
"pregnancy_value_check": 1
}
]
},
"header": "Are you sure this is correct?",
"type": "interruption_screen",
"answer_options": {
@ -6811,8 +7103,17 @@
},
"questions": {
"net_income_value_check": {
"check_answer_label": "Net income soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Net income confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"net_income_value_check": 0
},
{
"net_income_value_check": 1
}
]
},
"header": "Are you sure this is correct?",
"type": "interruption_screen",
"answer_options": {
@ -7712,8 +8013,17 @@
},
"questions": {
"rent_value_check": {
"check_answer_label": "Rent soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Total rent confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"rent_value_check": 0
},
{
"rent_value_check": 1
}
]
},
"header": "Are you sure this is correct?",
"type": "interruption_screen",
"answer_options": {
@ -7756,8 +8066,17 @@
},
"questions": {
"rent_value_check": {
"check_answer_label": "Rent soft validation",
"hidden_in_check_answers": true,
"check_answer_label": "Total rent confirmation",
"hidden_in_check_answers": {
"depends_on": [
{
"rent_value_check": 0
},
{
"rent_value_check": 1
}
]
},
"header": "Are you sure this is correct?",
"type": "interruption_screen",
"answer_options": {
@ -7776,7 +8095,7 @@
"description": "",
"questions": {
"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?",
"hint_text": "Also known as the ‘outstanding amount’.",
"type": "radio",
@ -7812,17 +8131,15 @@
}
]
},
"outstanding_amount_known": {
"outstanding_amount": {
"header": "",
"description": "",
"questions": {
"tshortfall_known": {
"check_answer_label": "",
"header": "",
"hint_text": "",
"hidden_in_check_answers": true,
"check_answer_label": "Do you know the outstanding amount?",
"header": "Can you estimate the outstanding amount?",
"hint_text": "You only need to give an approximate figure.",
"type": "radio",
"derived": true,
"answer_options": {
"0": {
"value": "Yes"
@ -7830,19 +8147,14 @@
"1": {
"value": "No"
}
},
"conditional_for": {
"tshortfall": [0]
}
}
},
"depends_on": [false]
},
"outstanding_amount": {
"header": "",
"description": "",
"questions": {
},
"tshortfall": {
"check_answer_label": "Estimated outstanding amount",
"header": "What do you expect the outstanding amount to be?",
"hint_text": "Give an estimated amount if you don’t know the exact figure.",
"header": "Estimated outstanding amount",
"type": "numeric",
"min": 0,
"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
# one (and only one) @ exists in the given string. This is mainly
# 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
# 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:
already_authenticated: "You are already signed in"
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."
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."
unauthenticated: "You need to sign in or sign up before continuing"
unconfirmed: "You must confirm your email address before continuing"

10
config/locales/en.yml

@ -78,8 +78,8 @@ en:
user:
attributes:
organisation_id:
blank: "Enter the existing organisation’s name"
invalid: "Enter the existing organisation’s name"
blank: "Select the user’s organisation"
invalid: "Select the user’s organisation"
name:
blank: "Enter a name"
email:
@ -122,7 +122,7 @@ en:
mrcdate:
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"
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:
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"
@ -319,6 +319,10 @@ en:
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."
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:
two_factor_authentication:

1
config/routes.rb

@ -48,6 +48,7 @@ Rails.application.routes.draw do
member do
resources :locations do
get "edit-name", to: "locations#edit_name"
get "edit", to: "locations#edit"
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.
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
enable_extension "plpgsql"
@ -200,6 +200,8 @@ ActiveRecord::Schema[7.0].define(version: 2022_08_02_125711) do
t.integer "vacdays"
t.bigint "scheme_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 ["location_id"], name: "index_case_logs_on_location_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.datetime "startdate", precision: nil
t.string "location_admin_district"
t.boolean "confirmed"
t.index ["old_id"], name: "index_locations_on_old_id", unique: true
t.index ["scheme_id"], name: "index_locations_on_scheme_id"
end
@ -262,6 +265,13 @@ ActiveRecord::Schema[7.0].define(version: 2022_08_02_125711) do
t.boolean "empty_export", default: false, null: false
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|
t.bigint "organisation_id"
t.integer "rent_period"
@ -379,8 +389,10 @@ ActiveRecord::Schema[7.0].define(version: 2022_08_02_125711) do
end
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 "locations", "schemes"
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

2
lib/tasks/data_export.rake

@ -4,7 +4,7 @@ namespace :core do
format = args[:format]
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)
if format.present? && format == "CSV"

4
lib/tasks/data_import.rake

@ -1,11 +1,11 @@
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|
type = args[:type]
path = args[:path]
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
when "organisation"

2
lib/tasks/data_import_field.rake

@ -5,7 +5,7 @@ namespace :core do
path = args[:path]
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
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 }
tenancyother { nil }
net_income_value_check { nil }
void_date_value_check { 1 }
major_repairs_date_value_check { 1 }
net_income_known { 0 }
previous_la_known { 1 }
property_owner_organisation { "Test" }

1
spec/factories/location.rb

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

5
spec/factories/organisation.rb

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

13
spec/features/form/check_answers_page_spec.rb

@ -133,6 +133,19 @@ RSpec.describe "Form Check Answers Page" do
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
before do
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)
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

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

35
spec/features/organisation_spec.rb

@ -61,7 +61,7 @@ RSpec.describe "User Features" do
expect(page).to have_link("Schemes", href: "/schemes")
end
end
context "when the user is support and the organisation does not hold housing stock" do
before do
organisation.update!(holds_own_stock: false)
@ -252,6 +252,39 @@ RSpec.describe "User Features" do
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

2
spec/features/schemes_helpers.rb

@ -44,7 +44,7 @@ module SchemesHelpers
end
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"
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")
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
before do
click_link("Scheme")
end
it "does not let you change details other than the name" do
assert_selector "a", text: "Change", count: 1
it "does not let you change details other than the name, confidential information and housing stock owner" do
assert_selector "a", text: "Change", count: 3
end
end
end
@ -451,6 +472,7 @@ RSpec.describe "Schemes scheme Features" do
it "lets me check my answers after adding a location" do
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"
end
@ -561,11 +583,12 @@ RSpec.describe "Schemes scheme Features" do
end
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 scheme.id_to_display
expect(page).to have_content scheme.service_name
expect(page).to have_content scheme.owning_organisation.name
expect(page).to have_content "#{scheme.service_name} has been created."
end
end
@ -704,9 +727,17 @@ RSpec.describe "Schemes scheme Features" do
click_button "Save changes"
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_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_css(".govuk-notification-banner.govuk-notification-banner--success")
expect(page).to have_content("has been updated")
end
end
end
@ -748,10 +779,10 @@ RSpec.describe "Schemes scheme Features" do
click_button "Save and continue"
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 "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
@ -834,8 +865,8 @@ RSpec.describe "Schemes scheme Features" do
click_link("Scheme")
end
it "does not let you change details other than the name" do
assert_selector "a", text: "Change", count: 1
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: 3
end
end
end
@ -846,6 +877,55 @@ RSpec.describe "Schemes scheme Features" do
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
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") }

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},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,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,,,,,,,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": {
"questions": {
"tenancycode": {
"check_answers_card_number": 0,
"check_answer_label": "Tenant code",
"header": "What is the tenant code?",
"hint_text": "This is how you usually refer to this tenancy on your own systems.",
@ -31,6 +32,7 @@
"person_1_age": {
"questions": {
"age1": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s age",
"header": "What is the tenant’s age?",
"type": "numeric",
@ -52,6 +54,7 @@
"person_1_gender": {
"questions": {
"sex1": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s gender identity",
"header": "Which of these best describes the tenant’s gender identity?",
"type": "radio",
@ -77,6 +80,7 @@
"description": "",
"questions": {
"ecstat1": {
"check_answers_card_number": 1,
"check_answer_label": "Lead tenant’s working situation",
"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.",
@ -125,6 +129,7 @@
"household_number_of_members": {
"questions": {
"hhmemb": {
"check_answers_card_number": 0,
"check_answer_label": "Number of Household Members",
"header": "How many people are there in the household?",
"hint_text": "The maximum number of members is 8",
@ -140,6 +145,7 @@
}
},
"relat2": {
"check_answers_card_number": 2,
"check_answer_label": "Person 2’s relationship to lead tenant",
"header": "What is person 2’s relationship to lead tenant",
"type": "radio",
@ -153,6 +159,7 @@
}
},
"age2": {
"check_answers_card_number": 2,
"check_answer_label": "Person 2’s age",
"header": "Do you know person 2’s age?",
"type": "numeric",
@ -162,6 +169,7 @@
"width": 2
},
"sex2": {
"check_answers_card_number": 2,
"check_answer_label": "Person 2’s gender identity",
"header": "Which of these best describes person 2’s gender identity?",
"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": {
"header": "",
"description": "",
"questions": {
"ecstat2": {
"check_answers_card_number": 2,
"check_answer_label": "Person 2’s Work",
"header": "Which of these best describes person 2’s working situation?",
"type": "radio",
@ -216,6 +248,7 @@
"propcode": {
"questions": {
"propcode": {
"check_answers_card_number": 0,
"check_answer_label": "",
"header": "property reference?",
"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"] }
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(:export_service) { instance_double(Exports::CaseLogExportService) }
@ -14,7 +14,7 @@ describe "rake core:data_export", type: task do
Rake::Task.define_task(:environment)
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(Exports::CaseLogExportService).to receive(:new).and_return(export_service)
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
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(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
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(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"] }
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) }
before do
@ -13,7 +13,7 @@ describe "rake core:data_import", type: :task do
Rake::Task.define_task(:environment)
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(ENV).to receive(:[])
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
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(import_service).to receive(:create_organisations).with(fixture_path)
@ -47,7 +47,7 @@ describe "rake core:data_import", type: :task do
end
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(import_service).to receive(:create_users).with(fixture_path)
@ -65,7 +65,7 @@ describe "rake core:data_import", type: :task do
end
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(import_service).to receive(:create_data_protection_confirmations).with(fixture_path)
@ -83,7 +83,7 @@ describe "rake core:data_import", type: :task do
end
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(import_service).to receive(:create_organisation_rent_periods).with(fixture_path)
@ -101,7 +101,7 @@ describe "rake core:data_import", type: :task do
end
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(import_service).to receive(:create_logs).with(fixture_path)
@ -119,7 +119,7 @@ describe "rake core:data_import", type: :task do
end
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(import_service).to receive(:create_schemes).with(fixture_path)
@ -137,7 +137,7 @@ describe "rake core:data_import", type: :task do
end
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(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"] }
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) }
before do
@ -13,7 +13,7 @@ describe "rake core:data_import_field", type: :task do
Rake::Task.define_task(:environment)
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(ENV).to receive(:[])
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" }
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)
end
@ -46,7 +46,7 @@ describe "rake core:data_import_field", type: :task do
let(:field) { "lettings_allocation" }
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)
end
@ -60,7 +60,7 @@ describe "rake core:data_import_field", type: :task do
let(:field) { "major_repairs" }
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)
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
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
let(:case_log) { FactoryBot.create(:case_log) }
@ -2043,6 +2073,20 @@ RSpec.describe CaseLog do
expect(result.count).to eq(1)
expect(result.first.id).to eq case_log_to_search.id
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
describe "#search_by" do
@ -2070,6 +2114,20 @@ RSpec.describe CaseLog do
expect(result.first.id).to eq case_log_to_search.id
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
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
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(:location) { FactoryBot.create(:location, :export, scheme:, type_of_unit: 6, postcode: "SE11TE") }
let(:user) { FactoryBot.create(:user, organisation: location.scheme.owning_organisation) }
let(:expected_content) { csv_export_file.read }
before do
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)
expected_content = csv_export_file.read
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)
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

4
spec/models/form/subsection_spec.rb

@ -25,12 +25,12 @@ RSpec.describe Form::Subsection, type: :model do
end
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)
end
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)
end

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

Loading…
Cancel
Save