Browse Source

Merge branch 'main' into LaPrevlocLayearValidation

pull/2884/head
Rachael Booth 2 years ago committed by GitHub
parent
commit
829e37ad0c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      Dockerfile
  2. 18
      app/components/check_answers_summary_list_card_component.rb
  3. 4
      app/controllers/check_errors_controller.rb
  4. 54
      app/controllers/form_controller.rb
  5. 12
      app/controllers/locations_controller.rb
  6. 4
      app/controllers/merge_requests_controller.rb
  7. 4
      app/controllers/organisations_controller.rb
  8. 6
      app/controllers/schemes_controller.rb
  9. 5
      app/frontend/application.js
  10. 34
      app/frontend/styles/_custom-rails-admin.scss
  11. 14
      app/frontend/styles/_red-link.scss
  12. 3
      app/frontend/styles/application.scss
  13. 19
      app/helpers/check_answers_helper.rb
  14. 2
      app/helpers/check_errors_helper.rb
  15. 36
      app/helpers/details_table_helper.rb
  16. 8
      app/helpers/duplicate_logs_helper.rb
  17. 6
      app/helpers/form_page_helper.rb
  18. 5
      app/helpers/formatting_helper.rb
  19. 2
      app/helpers/guidance_helper.rb
  20. 35
      app/helpers/locations_helper.rb
  21. 41
      app/helpers/merge_requests_helper.rb
  22. 13
      app/helpers/organisations_helper.rb
  23. 18
      app/helpers/schemes_helper.rb
  24. 4
      app/helpers/tasklist_helper.rb
  25. 20
      app/helpers/user_helper.rb
  26. 2
      app/models/derived_variables/lettings_log_variables.rb
  27. 2
      app/models/derived_variables/sales_log_variables.rb
  28. 2
      app/models/form.rb
  29. 2
      app/models/form/lettings/pages/address_matcher.rb
  30. 12
      app/models/form/lettings/pages/person_lead_partner.rb
  31. 19
      app/models/form/lettings/pages/rent_4_weekly.rb
  32. 19
      app/models/form/lettings/pages/rent_bi_weekly.rb
  33. 19
      app/models/form/lettings/pages/rent_monthly.rb
  34. 19
      app/models/form/lettings/pages/rent_weekly.rb
  35. 30
      app/models/form/lettings/questions/person_partner.rb
  36. 32
      app/models/form/lettings/questions/previous_tenure.rb
  37. 12
      app/models/form/lettings/questions/sheltered.rb
  38. 25
      app/models/form/lettings/questions/waityear.rb
  39. 22
      app/models/form/lettings/subsections/household_characteristics.rb
  40. 22
      app/models/form/lettings/subsections/income_and_benefits.rb
  41. 1
      app/models/form/lettings/subsections/property_information.rb
  42. 2
      app/models/form/lettings/subsections/tenancy_information.rb
  43. 17
      app/models/form/question.rb
  44. 2
      app/models/form/sales/pages/address_matcher.rb
  45. 4
      app/models/form/sales/questions/ownership_scheme.rb
  46. 2
      app/models/forms/bulk_upload_form/prepare_your_file.rb
  47. 10
      app/models/lettings_log.rb
  48. 36
      app/models/log.rb
  49. 33
      app/models/sales_log.rb
  50. 55
      app/models/validations/date_validations.rb
  51. 2
      app/models/validations/financial_validations.rb
  52. 2
      app/models/validations/household_validations.rb
  53. 2
      app/models/validations/sales/financial_validations.rb
  54. 2
      app/models/validations/sales/household_validations.rb
  55. 4
      app/models/validations/sales/sale_information_validations.rb
  56. 17
      app/models/validations/shared_validations.rb
  57. 4
      app/models/validations/soft_validations.rb
  58. 58
      app/services/documentation_generator.rb
  59. 3
      app/services/exports/organisation_export_constants.rb
  60. 2
      app/services/exports/organisation_export_service.rb
  61. 46
      app/views/bulk_upload_lettings_logs/forms/prepare_your_file_2023.html.erb
  62. 40
      app/views/bulk_upload_sales_logs/forms/prepare_your_file_2023.html.erb
  63. 21
      app/views/bulk_upload_shared/guidance.html.erb
  64. 2
      app/views/check_errors/confirm_clear_all_answers.html.erb
  65. 2
      app/views/check_errors/confirm_clear_answer.html.erb
  66. 25
      app/views/components/_date_picker.html.erb
  67. 2
      app/views/duplicate_logs/_duplicate_log.html.erb
  68. 2
      app/views/form/_check_answers_summary_list.html.erb
  69. 18
      app/views/form/_date_question.html.erb
  70. 4
      app/views/form/check_errors.html.erb
  71. 9
      app/views/layouts/rails_admin/_navigation.html.erb
  72. 70
      app/views/layouts/rails_admin/application.html.erb
  73. 12
      app/views/locations/availability.erb
  74. 10
      app/views/locations/check_answers.html.erb
  75. 10
      app/views/locations/show.html.erb
  76. 15
      app/views/locations/toggle_active.html.erb
  77. 4
      app/views/logs/delete_duplicates.html.erb
  78. 14
      app/views/merge_requests/merge_date.html.erb
  79. 19
      app/views/organisations/show.html.erb
  80. 25
      app/views/rails_admin/main/_submit_buttons.html.erb
  81. 68
      app/views/rails_admin/main/dashboard.html.erb
  82. 21
      app/views/rails_admin/main/delete.html.erb
  83. 187
      app/views/rails_admin/main/index.html.erb
  84. 8
      app/views/schemes/_scheme_summary_list_row.html.erb
  85. 2
      app/views/schemes/check_answers.html.erb
  86. 2
      app/views/schemes/show.html.erb
  87. 12
      app/views/schemes/toggle_active.html.erb
  88. 16
      app/views/users/show.html.erb
  89. 3
      config/initializers/assets.rb
  90. 5
      config/local_authorities_data/2025_local_authorities.csv
  91. 5
      config/local_authorities_data/local_authority_links_2025.csv
  92. 2
      config/locales/en.yml
  93. 71
      config/locales/forms/2023/lettings/household_characteristics.en.yml
  94. 12
      config/locales/forms/2023/lettings/household_needs.en.yml
  95. 34
      config/locales/forms/2023/lettings/household_situation.en.yml
  96. 32
      config/locales/forms/2023/lettings/income_and_benefits.en.yml
  97. 22
      config/locales/forms/2023/lettings/property_information.en.yml
  98. 13
      config/locales/forms/2023/lettings/setup.en.yml
  99. 184
      config/locales/forms/2023/lettings/soft_validations.en.yml
  100. 17
      config/locales/forms/2023/lettings/tenancy_information.en.yml
  101. Some files were not shown because too many files have changed in this diff Show More

2
Dockerfile

@ -10,7 +10,7 @@ RUN apk add --update --no-cache tzdata && \
# build-base: compilation tools for bundle
# yarn: node package manager
# postgresql-dev: postgres driver and libraries
RUN apk add --no-cache build-base=0.5-r3 busybox=1.36.1-r7 nodejs-current=20.8.1-r0 yarn=1.22.19-r0 postgresql13-dev=13.18-r0 git=2.40.3-r0 bash=5.2.15-r5
RUN apk add --no-cache build-base=0.5-r3 busybox=1.36.1-r7 nodejs-current=20.8.1-r0 yarn=1.22.19-r0 postgresql13-dev=13.18-r0 bash=5.2.15-r5
# Bundler version should be the same version as what the Gemfile.lock was bundled with
RUN gem install bundler:2.3.14 --no-document

18
app/components/check_answers_summary_list_card_component.rb

@ -31,27 +31,29 @@ class CheckAnswersSummaryListCardComponent < ViewComponent::Base
def action_href(question, log)
referrer = question.displayed_as_answered?(log) ? "check_answers" : "check_answers_new_answer"
send("#{log.model_name.param_key}_#{question.page.id}_path", log, referrer:)
send("#{log.log_type}_#{question.page.id}_path", log, referrer:)
end
def correct_validation_action_href(question, log, _related_question_ids, correcting_hard_validation)
return action_href(question, log) unless correcting_hard_validation
if question.displayed_as_answered?(log)
send("#{log.model_name.param_key}_confirm_clear_answer_path", log, question_id: question.id)
send("#{log.log_type}_confirm_clear_answer_path", log, question_id: question.id)
else
send("#{log.model_name.param_key}_#{question.page.id}_path", log, referrer: "check_errors", related_question_ids: request.query_parameters["related_question_ids"], original_page_id: request.query_parameters["original_page_id"])
send("#{log.log_type}_#{question.page.id}_path", log, referrer: "check_errors", related_question_ids: request.query_parameters["related_question_ids"], original_page_id: request.query_parameters["original_page_id"])
end
end
private
def unanswered_value(question)
if log.creation_method_bulk_upload? && log.bulk_upload.present? && !log.optional_fields.include?(question.id)
"<span class=\"app-!-colour-red\">You still need to answer this question</span>".html_safe
else
"<span class=\"app-!-colour-muted\">You didn’t answer this question</span>".html_safe
end
link_class = if log.creation_method_bulk_upload? && log.bulk_upload.present? && !log.optional_fields.include?(question.id)
"app-red-link app-red-link---no-visited-state"
else
"govuk-link govuk-link--no-visited-state"
end
govuk_link_to question.check_answer_prompt, correct_validation_action_href(question, log, nil, @correcting_hard_validation), class: link_class
end
def number_of_buyers

4
app/controllers/check_errors_controller.rb

@ -7,8 +7,8 @@ class CheckErrorsController < ApplicationController
def confirm_clear_answer
return render_not_found unless @log
@related_question_ids = params[@log.model_name.param_key].keys.reject { |id| id == "page_id" }
@page = @log.form.get_page(params[@log.model_name.param_key]["page_id"])
@related_question_ids = params[@log.log_type].keys.reject { |id| id == "page_id" }
@page = @log.form.get_page(params[@log.log_type]["page_id"])
if params["clear_all"]
@questions_to_clear = @related_question_ids.map { |id|

54
app/controllers/form_controller.rb

@ -9,7 +9,7 @@ class FormController < ApplicationController
def submit_form
if @log
@page = form.get_page(params[@log.model_name.param_key][:page])
@page = form.get_page(params[@log.log_type][:page])
return render_check_errors_page if params["check_errors"]
shown_page_ids_with_unanswered_questions_before_update = @page.subsection.pages
@ -47,7 +47,7 @@ class FormController < ApplicationController
flash[:log_data] = responses_for_page
question_ids = (@log.errors.map(&:attribute) - [:base]).uniq
flash[:pages_with_errors_count] = question_ids.map { |id| @log.form.get_question(id, @log)&.page&.id }.compact.uniq.count
redirect_to send("#{@log.class.name.underscore}_#{@page.id}_path", @log, { referrer: request.params["referrer"], original_page_id: request.params["original_page_id"], related_question_ids: request.params["related_question_ids"] })
redirect_to send("#{@log.log_type}_#{@page.id}_path", @log, { referrer: request.params["referrer"], original_page_id: request.params["original_page_id"], related_question_ids: request.params["related_question_ids"] })
end
else
render_not_found
@ -136,11 +136,9 @@ private
def responses_for_page(page)
page.questions.each_with_object({}) do |question, result|
question_params = params[@log.model_name.param_key][question.id]
question_params = params[@log.log_type][question.id]
if question.type == "date"
day = params[@log.model_name.param_key]["#{question.id}(3i)"]
month = params[@log.model_name.param_key]["#{question.id}(2i)"]
year = params[@log.model_name.param_key]["#{question.id}(1i)"]
day, month, year = params[@log.log_type][question.id].split("/")
next unless [day, month, year].any?(&:present?)
result[question.id] = if Date.valid_date?(year.to_i, month.to_i, day.to_i) && year.to_i.positive?
@ -160,7 +158,7 @@ private
question.answer_keys_without_dividers.each do |option|
result[option] = question_params.include?(option) ? 1 : 0
end
else
elsif question.type != "date"
result[question.id] = question_params
end
@ -215,11 +213,11 @@ private
end
def previous_interruption_screen_page_id
params[@log.model_name.param_key]["interruption_page_id"]
params[@log.log_type]["interruption_page_id"]
end
def previous_interruption_screen_referrer
params[@log.model_name.param_key]["interruption_page_referrer_type"].presence
params[@log.log_type]["interruption_page_referrer_type"].presence
end
def page_has_duplicate_check_question
@ -229,7 +227,7 @@ private
def update_duplication_tracking
return unless page_has_duplicate_check_question
class_name = @log.class.name.underscore
class_name = @log.log_type
dynamic_duplicates = current_user.send(class_name.pluralize).duplicate_logs(@log)
if dynamic_duplicates.any?
@ -245,7 +243,7 @@ private
end
def successful_redirect_path(pages_to_check)
class_name = @log.class.name.underscore
class_name = @log.log_type
if is_referrer_type?("duplicate_logs") || is_referrer_type?("duplicate_logs_banner")
original_log = current_user.send(class_name.pluralize).find_by(id: from_referrer_query("original_log_id"))
@ -262,7 +260,7 @@ private
end
unless @log.duplicate_set_id.nil?
return send("#{@log.class.name.underscore}_duplicate_logs_path", @log, original_log_id: @log.id)
return send("#{@log.log_type}_duplicate_logs_path", @log, original_log_id: @log.id)
end
if is_referrer_type?("check_answers")
@ -275,25 +273,25 @@ private
elsif pages_to_check.any?
return redirect_path_to_question(pages_to_check[0], pages_to_check)
else
return send("#{@log.model_name.param_key}_#{form.subsection_for_page(@page).id}_check_answers_path", @log)
return send("#{@log.log_type}_#{form.subsection_for_page(@page).id}_check_answers_path", @log)
end
end
if previous_interruption_screen_page_id.present?
return send("#{@log.class.name.underscore}_#{previous_interruption_screen_page_id}_path", @log, { referrer: previous_interruption_screen_referrer, original_log_id: original_duplicate_log_id_from_query }.compact)
return send("#{@log.log_type}_#{previous_interruption_screen_page_id}_path", @log, { referrer: previous_interruption_screen_referrer, original_log_id: original_duplicate_log_id_from_query }.compact)
end
if params[@log.model_name.param_key]["check_errors"]
@page = form.get_page(params[@log.model_name.param_key]["page"])
if params[@log.log_type]["check_errors"]
@page = form.get_page(params[@log.log_type]["page"])
flash[:notice] = "You have successfully updated #{@page.questions.map(&:check_answer_label).to_sentence}"
original_page_id = params[@log.model_name.param_key]["original_page_id"]
related_question_ids = params[@log.model_name.param_key]["related_question_ids"].split(" ")
return send("#{@log.class.name.underscore}_#{original_page_id}_path", @log, { check_errors: true, related_question_ids: }.compact)
original_page_id = params[@log.log_type]["original_page_id"]
related_question_ids = params[@log.log_type]["related_question_ids"].split(" ")
return send("#{@log.log_type}_#{original_page_id}_path", @log, { check_errors: true, related_question_ids: }.compact)
end
if params["referrer"] == "check_errors"
@page = form.get_page(params[@log.model_name.param_key]["page"])
@page = form.get_page(params[@log.log_type]["page"])
flash[:notice] = "You have successfully updated #{@page.questions.map(&:check_answer_label).to_sentence}"
return send("#{@log.class.name.underscore}_#{params['original_page_id']}_path", @log, { check_errors: true, related_question_ids: params["related_question_ids"] }.compact)
return send("#{@log.log_type}_#{params['original_page_id']}_path", @log, { check_errors: true, related_question_ids: params["related_question_ids"] }.compact)
end
is_new_answer_from_check_answers = is_referrer_type?("check_answers_new_answer")
@ -306,7 +304,7 @@ private
def redirect_path_to_question(page_to_show, unanswered_pages)
remaining_pages = unanswered_pages.excluding(page_to_show)
remaining_page_ids = remaining_pages.any? ? remaining_pages.map(&:id).join(",") : nil
send("#{@log.class.name.underscore}_#{page_to_show.id}_path", @log, { referrer: "check_answers", unanswered_pages: remaining_page_ids })
send("#{@log.log_type}_#{page_to_show.id}_path", @log, { referrer: "check_answers", unanswered_pages: remaining_page_ids })
end
def pages_requiring_update(previously_visible_empty_page_ids)
@ -350,8 +348,8 @@ private
def question_missing_response?(responses_for_page, question)
if %w[checkbox validation_override].include?(question.type)
answered = question.answer_keys_without_dividers.map do |option|
session["fields"][option] = @log[option] = params[@log.model_name.param_key][question.id].include?(option) ? 1 : 0
params[@log.model_name.param_key][question.id].exclude?(option)
session["fields"][option] = @log[option] = params[@log.log_type][question.id].include?(option) ? 1 : 0
params[@log.log_type][question.id].exclude?(option)
end
answered.all?
else
@ -371,7 +369,7 @@ private
CONFIRMATION_PAGE_IDS = %w[uprn_confirmation uprn_selection].freeze
def deduplication_success_banner
deduplicated_log_link = "<a class=\"govuk-notification-banner__link govuk-!-font-weight-bold\" href=\"#{send("#{@log.class.name.underscore}_path", @log)}\">Log #{@log.id}</a>"
deduplicated_log_link = "<a class=\"govuk-notification-banner__link govuk-!-font-weight-bold\" href=\"#{send("#{@log.log_type}_path", @log)}\">Log #{@log.id}</a>"
changed_labels = {
property_postcode: "postcode",
lead_tenant_age: "lead tenant’s age",
@ -430,8 +428,8 @@ private
end
def render_check_errors_page
if params[@log.model_name.param_key]["clear_question_ids"].present?
question_ids = params[@log.model_name.param_key]["clear_question_ids"].split(" ")
if params[@log.log_type]["clear_question_ids"].present?
question_ids = params[@log.log_type]["clear_question_ids"].split(" ")
question_ids.each do |question_id|
question = @log.form.get_question(question_id, @log)
next if question.subsection.id == "setup"
@ -440,7 +438,7 @@ private
@log.previous_la_known = nil if question.id == "ppostcode_full"
end
@log.save!
@questions = params[@log.model_name.param_key].keys.reject { |id| %w[clear_question_ids page].include?(id) }.map { |id| @log.form.get_question(id, @log) }
@questions = params[@log.log_type].keys.reject { |id| %w[clear_question_ids page].include?(id) }.map { |id| @log.form.get_question(id, @log) }
else
responses_for_page = responses_for_page(@page)
@log.assign_attributes(responses_for_page)

12
app/controllers/locations_controller.rb

@ -137,9 +137,7 @@ class LocationsController < ApplicationController
def availability; end
def update_availability
day = location_params["startdate(3i)"]
month = location_params["startdate(2i)"]
year = location_params["startdate(1i)"]
day, month, year = location_params["startdate"].split("/")
@location.startdate = if [day, month, year].none?(&:blank?) && Date.valid_date?(year.to_i, month.to_i, day.to_i)
Time.zone.local(year.to_i, month.to_i, day.to_i)
end
@ -258,7 +256,7 @@ private
end
def location_params
required_params = params.require(:location).permit(:postcode, :location_admin_district, :location_code, :name, :units, :type_of_unit, :mobility_type, "startdate(1i)", "startdate(2i)", "startdate(3i)").merge(scheme_id: @scheme.id)
required_params = params.require(:location).permit(:postcode, :location_admin_district, :location_code, :name, :units, :type_of_unit, :mobility_type, :startdate).merge(scheme_id: @scheme.id)
required_params[:postcode] = PostcodeService.clean(required_params[:postcode]) if required_params[:postcode]
required_params[:location_admin_district] = nil if required_params[:location_admin_district] == "Select an option"
required_params
@ -297,13 +295,9 @@ private
return
elsif params[:location_deactivation_period]["#{key}_type".to_sym] == "default"
return FormHandler.instance.start_date_of_earliest_open_for_editing_collection_period
elsif params[:location_deactivation_period][key.to_sym].present?
return params[:location_deactivation_period][key.to_sym]
end
day = params[:location_deactivation_period]["#{key}(3i)"]
month = params[:location_deactivation_period]["#{key}(2i)"]
year = params[:location_deactivation_period]["#{key}(1i)"]
day, month, year = params[:location_deactivation_period][key.to_s].split("/")
return nil if [day, month, year].any?(&:blank?)
Time.zone.local(year.to_i, month.to_i, day.to_i) if Date.valid_date?(year.to_i, month.to_i, day.to_i)

4
app/controllers/merge_requests_controller.rb

@ -137,9 +137,7 @@ private
@merge_request.errors.add(:absorbing_organisation_id, :blank)
end
when "merge_date"
day = merge_request_params["merge_date(3i)"]
month = merge_request_params["merge_date(2i)"]
year = merge_request_params["merge_date(1i)"]
day, month, year = merge_request_params["merge_date"].split("/")
return @merge_request.errors.add(:merge_date, :blank) if [day, month, year].all?(&:blank?)

4
app/controllers/organisations_controller.rb

@ -176,7 +176,7 @@ class OrganisationsController < ApplicationController
end
def lettings_logs
organisation_logs = LettingsLog.visible.filter_by_organisation(@organisation).filter_by_years_or_nil(FormHandler.instance.years_of_available_lettings_forms)
organisation_logs = LettingsLog.visible.filter_by_organisation(@organisation.absorbed_organisations + [@organisation]).filter_by_years_or_nil(FormHandler.instance.years_of_available_lettings_forms)
unpaginated_filtered_logs = filter_manager.filtered_logs(organisation_logs, search_term, session_filters)
@search_term = search_term
@ -206,7 +206,7 @@ class OrganisationsController < ApplicationController
end
def sales_logs
organisation_logs = SalesLog.visible.filter_by_organisation(@organisation).filter_by_years_or_nil(FormHandler.instance.years_of_available_sales_forms)
organisation_logs = SalesLog.visible.filter_by_organisation(@organisation.absorbed_organisations + [@organisation]).filter_by_years_or_nil(FormHandler.instance.years_of_available_sales_forms)
unpaginated_filtered_logs = filter_manager.filtered_logs(organisation_logs, search_term, session_filters)
respond_to do |format|

6
app/controllers/schemes_controller.rb

@ -356,13 +356,9 @@ private
return
elsif params[:scheme_deactivation_period]["#{key}_type".to_sym] == "default"
return FormHandler.instance.start_date_of_earliest_open_for_editing_collection_period
elsif params[:scheme_deactivation_period][key.to_sym].present?
return params[:scheme_deactivation_period][key.to_sym]
end
day = params[:scheme_deactivation_period]["#{key}(3i)"]
month = params[:scheme_deactivation_period]["#{key}(2i)"]
year = params[:scheme_deactivation_period]["#{key}(1i)"]
day, month, year = params[:scheme_deactivation_period][key.to_s].split("/")
return nil if [day, month, year].any?(&:blank?)
Time.zone.local(year.to_i, month.to_i, day.to_i) if Date.valid_date?(year.to_i, month.to_i, day.to_i)

5
app/frontend/application.js

@ -14,11 +14,16 @@ import 'regenerator-runtime/runtime'
//
import { initAll as GOVUKFrontend } from 'govuk-frontend'
import { initAll as GOVUKPrototypeComponents } from '@x-govuk/govuk-prototype-components'
import { initAll as MOJFrontend } from '@ministryofjustice/frontend'
import './controllers'
import './cookie-banner'
import './styles/application.scss'
import 'moj-frontend'
import $ from 'jquery'
window.$ = $
require.context('govuk-frontend/dist/govuk/assets')
GOVUKFrontend()
GOVUKPrototypeComponents()
MOJFrontend()

34
app/frontend/styles/_custom-rails-admin.scss

@ -0,0 +1,34 @@
.rails-admin-sidescroll {
overflow-x: scroll;
}
.rails-admin-description_field {
min-width: 500px;
}
.rails-admin-case_field {
min-width: 500px;
}
.rails-admin-error_message_field {
min-width: 500px;
}
.rails-admin-actions {
min-width: 160px;
ul {
float: right;
}
}
.rails-admin-filters-box {
.filter {
// stylelint-disable-next-line declaration-no-important
display: flex !important;
}
button {
min-width: 20%;
}
}

14
app/frontend/styles/_red-link.scss

@ -0,0 +1,14 @@
.app-red-link {
@include govuk-typography-common;
@include govuk-link-decoration;
@include govuk-link-style-text;
}
.app-red-link:link,
.app-red-link---no-visited-state:visited {
color: govuk-colour("red");
}
.app-red-link:hover {
color: govuk-shade(govuk-colour("red"), 20);
}

3
app/frontend/styles/application.scss

@ -48,6 +48,9 @@ $govuk-breakpoints: (
@import "search";
@import "sub-navigation";
@import "unread-notification";
@import "red-link";
@import "custom-rails-admin";
@import "node_modules/@ministryofjustice/frontend/moj/components/date-picker/date-picker";
// App utilities
.app-\!-colour-muted {

19
app/helpers/check_answers_helper.rb

@ -26,7 +26,7 @@ module CheckAnswersHelper
end
def next_incomplete_section_path(log, redirect_path)
"#{log.class.name.underscore}_#{redirect_path.underscore.tr('/', '_')}_path"
"#{log.log_type}_#{redirect_path.underscore.tr('/', '_')}_path"
end
private
@ -55,11 +55,18 @@ private
[question.question_number_string, question.check_answer_label.to_s.presence || question.header.to_s].compact.join(" - ")
end
def unanswered_action_href(question, log)
referrer = question.displayed_as_answered?(log) ? "check_answers" : "check_answers_new_answer"
send("#{log.model_name.param_key}_#{question.page.id}_path", log, referrer:)
end
def unanswered_value(log:, question:)
if log.creation_method_bulk_upload? && log.bulk_upload.present? && !log.optional_fields.include?(question.id)
"<span class=\"app-!-colour-red\">You still need to answer this question</span>".html_safe
else
"<span class=\"app-!-colour-muted\">You didn’t answer this question</span>".html_safe
end
link_class = if log.creation_method_bulk_upload? && log.bulk_upload.present? && !log.optional_fields.include?(question.id)
"app-red-link app-red-link---no-visited-state"
else
"govuk-link govuk-link--no-visited-state"
end
govuk_link_to question.check_answer_prompt, unanswered_action_href(question, log), class: link_class
end
end

2
app/helpers/check_errors_helper.rb

@ -6,6 +6,6 @@ module CheckErrorsHelper
end
def check_errors_answer_link(log, question, page, applicable_questions)
send("#{log.model_name.param_key}_#{question.page.id}_path", log, referrer: "check_errors", original_page_id: page.id, related_question_ids: applicable_questions.map(&:id))
send("#{log.log_type}_#{question.page.id}_path", log, referrer: "check_errors", original_page_id: page.id, related_question_ids: applicable_questions.map(&:id))
end
end

36
app/helpers/details_table_helper.rb

@ -1,14 +1,42 @@
module DetailsTableHelper
def details_html(attribute)
def details_html(attribute, resource = nil)
if attribute[:format] == :bullet && attribute[:value].length > 1
list = attribute[:value].map { |value| "<li>#{value}</li>" }.join
simple_format(list, { class: "govuk-list govuk-list--bullet" }, wrapper_tag: "ul")
else
return simple_format(attribute[:value].first.to_s, { class: "govuk-body" }, wrapper_tag: "p") if attribute[:value].is_a?(Array)
value = attribute[:value].presence || "<span class=\"app-!-colour-muted\">You didn’t answer this question</span>".html_safe
return simple_format(attribute[:value].first.to_s, { class: "govuk-body" }, wrapper_tag: "p") if attribute[:value].is_a?(Array) && attribute[:value].any?
value = determine_value(attribute, resource)
simple_format(value.to_s, { class: "govuk-body" }, wrapper_tag: "p")
end
end
private
def determine_value(attribute, resource)
return attribute[:value] if attribute[:value].present?
method_name = "#{resource.class.name.downcase}_value"
return send(method_name, attribute, resource) if respond_to?(method_name, true)
"<span class=\"app-!-colour-muted\">No answer provided</span>".html_safe
end
def location_value(attribute, resource)
return nil unless LocationPolicy.new(current_user, resource).update?
govuk_link_to(location_details_link_message(attribute), location_edit_path(resource, attribute[:attribute]), class: "govuk-link govuk-link--no-visited-state")
end
def organisation_value(attribute, resource)
return nil unless can_edit_org?(current_user) && attribute[:editable]
govuk_link_to(organisation_details_link_message(attribute), edit_organisation_path(resource), class: "govuk-link govuk-link--no-visited-state")
end
def scheme_value(attribute, resource)
return nil unless can_change_scheme_answer?(attribute[:name], resource)
govuk_link_to(scheme_details_link_message(attribute), scheme_edit_path(resource, attribute[:id]), class: "govuk-link govuk-link--no-visited-state")
end
end

8
app/helpers/duplicate_logs_helper.rb

@ -7,7 +7,7 @@ module DuplicateLogsHelper
return govuk_button_link_to "Keep this log and delete duplicates", url_for(
controller: "duplicate_logs",
action: "delete_duplicates",
"#{duplicate_log.class.name.underscore}_id": duplicate_log.id,
"#{duplicate_log.log_type}_id": duplicate_log.id,
original_log_id: original_log.id,
referrer: params[:referrer],
organisation_id: params[:organisation_id],
@ -16,7 +16,7 @@ module DuplicateLogsHelper
if params[:referrer] == "duplicate_logs_banner"
current_user.support? ? govuk_button_link_to("Review other duplicates", organisation_duplicates_path(organisation_id: params[:organisation_id], referrer: params[:referrer])) : govuk_button_link_to("Review other duplicates", duplicate_logs_path(referrer: params[:referrer]))
elsif !original_log.deleted?
govuk_button_link_to "Back to Log #{original_log.id}", send("#{original_log.class.name.underscore}_path", original_log)
govuk_button_link_to "Back to Log #{original_log.id}", send("#{original_log.log_type}_path", original_log)
else
type = duplicate_log.lettings? ? "lettings" : "sales"
govuk_button_link_to "Back to #{type} logs", url_for(duplicate_log.class)
@ -24,12 +24,12 @@ module DuplicateLogsHelper
end
def duplicate_logs_action_href(log, page_id, original_log_id)
send("#{log.model_name.param_key}_#{page_id}_path", log, referrer: "interruption_screen", original_log_id:)
send("#{log.log_type}_#{page_id}_path", log, referrer: "interruption_screen", original_log_id:)
end
def change_duplicate_logs_action_href(log, page_id, all_duplicates, original_log_id)
first_remaining_duplicate_id = all_duplicates.map(&:id).reject { |id| id == log.id }.first
send("#{log.model_name.param_key}_#{page_id}_path", log, referrer: params[:referrer] == "duplicate_logs_banner" ? "duplicate_logs_banner" : "duplicate_logs", first_remaining_duplicate_id:, original_log_id:, organisation_id: params[:organisation_id])
send("#{log.log_type}_#{page_id}_path", log, referrer: params[:referrer] == "duplicate_logs_banner" ? "duplicate_logs_banner" : "duplicate_logs", first_remaining_duplicate_id:, original_log_id:, organisation_id: params[:organisation_id])
end
def duplicates_for_user(user)

6
app/helpers/form_page_helper.rb

@ -1,6 +1,6 @@
module FormPageHelper
def action_href(log, page_id, referrer = "check_answers")
send("#{log.model_name.param_key}_#{page_id}_path", log, referrer:)
send("#{log.log_type}_#{page_id}_path", log, referrer:)
end
def returning_to_question_page?(page, referrer)
@ -12,11 +12,11 @@ module FormPageHelper
end
def duplicate_log_set_path(log, original_log_id)
send("#{log.class.name.underscore}_duplicate_logs_path", log, original_log_id:)
send("#{log.log_type}_duplicate_logs_path", log, original_log_id:)
end
def relevant_check_answers_path(log, subsection)
send("#{log.class.name.underscore}_#{subsection.id}_check_answers_path", log)
send("#{log.log_type}_#{subsection.id}_check_answers_path", log)
end
def submit_button_text(page, referrer)

5
app/helpers/formatting_helper.rb

@ -2,8 +2,7 @@ module FormattingHelper
def format_ending(text)
return text if text.blank?
first_word = text.split.first
modified_text = first_word == first_word.upcase ? text : lowercase_first_letter(text)
modified_text = lowercase_first_letter(text)
ensure_sentence_ending(modified_text)
end
@ -17,7 +16,7 @@ module FormattingHelper
end
def lowercase_first_letter(text)
return text if text.blank?
return text if text.blank? || text.split.first == text.split.first.upcase
text[0].downcase + text[1..]
end

2
app/helpers/guidance_helper.rb

@ -6,6 +6,6 @@ module GuidanceHelper
question = log.form.get_question(question_id, log)
return "" unless question.page.routed_to?(log, user)
"(#{govuk_link_to "Q#{question.question_number}", send("#{log.class.name.underscore}_#{question.page.id}_path", log)})".html_safe
"(#{govuk_link_to "Q#{question.question_number}", send("#{log.log_type}_#{question.page.id}_path", log)})".html_safe
end
end

35
app/helpers/locations_helper.rb

@ -8,6 +8,10 @@ module LocationsHelper
selection_options(%w[Yes No])
end
def location_editable_attributes
%w[postcode name units type_of_unit mobility_standards]
end
def type_of_units_selection
selection_options(Location.type_of_units)
end
@ -64,8 +68,25 @@ module LocationsHelper
send("scheme_location_#{attribute}_path", location.scheme, location, referrer: "check_answers", route: params[:route])
end
def action_text_helper(attr, location)
attr[:value].blank? || (attr[:attribute] == "availability" && location.startdate.blank?) ? "Answer" : "Change"
def location_action_text(attr, location)
return "" if attr[:value].blank? || (attr[:attribute] == "availability" && location.startdate.blank?)
"Change"
end
def location_action_link(attr, scheme, location, current_user)
return unless LocationPolicy.new(current_user, location).update?
return unless current_user.support? && attr[:value].present?
paths = {
"postcode" => scheme_location_postcode_path(scheme, location, referrer: "details"),
"name" => scheme_location_name_path(scheme, location, referrer: "details"),
"units" => scheme_location_units_path(scheme, location, referrer: "details"),
"type_of_unit" => scheme_location_type_of_unit_path(scheme, location, referrer: "details"),
"mobility_standards" => scheme_location_mobility_standards_path(scheme, location, referrer: "details"),
}
paths[attr[:attribute]]
end
def toggle_location_link(location)
@ -95,6 +116,14 @@ module LocationsHelper
end
end
def location_details_link_message(attribute)
text = lowercase_first_letter(attribute[:name])
return "Select #{text}" if %w[local_authority type_of_unit mobility_standards].include?(attribute[:attribute])
return "Set #{text}" if attribute[:attribute] == "availability"
"Enter #{text}"
end
private
ActivePeriod = Struct.new(:from, :to)
@ -150,7 +179,7 @@ private
def formatted_local_authority_timeline(location)
sorted_linked_authorities = location.linked_local_authorities.sort_by(&:start_date)
return sorted_linked_authorities.first["name"] if sorted_linked_authorities.count == 1
return sorted_linked_authorities.first["name"] if sorted_linked_authorities.count == 1 || sorted_linked_authorities.map(&:name).uniq.count == 1
sorted_linked_authorities.map { |linked_local_authority|
formatted_start_date = linked_local_authority.start_date.year == 2021 ? "until" : "#{linked_local_authority.start_date&.to_formatted_s(:govuk_date)} -"

41
app/helpers/merge_requests_helper.rb

@ -2,10 +2,32 @@ module MergeRequestsHelper
include GovukLinkHelper
include GovukVisuallyHiddenHelper
def display_value_or_placeholder(value, placeholder = "You didn't answer this question")
def display_value_or_placeholder(value, placeholder = "No answer provided")
value.presence || content_tag(:span, placeholder, class: "app-!-colour-muted")
end
def value_exists?(merge_request, attribute)
merge_request.send(attribute).present? || (attribute == "helpdesk_ticket" && merge_request.has_helpdesk_ticket == false)
end
def details_prompt_link(page, merge_request)
govuk_link_to(merge_request_details_prompt(page), send("#{page}_merge_request_path", merge_request, referrer: "check_answers"), class: "govuk-link govuk-link--no-visited-state")
end
def merge_request_details_prompt(page)
messages = {
"existing_absorbing_organisation" => "Tell us if absorbing organisation is already active",
"helpdesk_ticket" => "Enter helpdesk ticket number",
"merging_organisations" => "Add merging organisations",
"merge_date" => "Set merge date",
}
messages[page] || "Enter #{lowercase_first_letter(page.humanize)}"
end
def merge_request_action_text(merge_request, attribute)
value_exists?(merge_request, attribute) ? "Change" : ""
end
def request_details(merge_request)
[
{ label: "Requester", value: display_value_or_placeholder(merge_request.requester&.name) },
@ -16,10 +38,10 @@ module MergeRequestsHelper
def merge_details(merge_request)
[
{ label: "Absorbing organisation", value: display_value_or_placeholder(merge_request.absorbing_organisation_name), action: merge_request_action(merge_request, "absorbing_organisation") },
{ label: "Merging organisations", value: merge_request.merge_request_organisations.any? ? merge_request.merge_request_organisations.map(&:merging_organisation_name).join("<br>").html_safe : display_value_or_placeholder(nil), action: merge_request_action(merge_request, "merging_organisations") },
{ label: "Merge date", value: display_value_or_placeholder(merge_request.merge_date), action: merge_request_action(merge_request, "merge_date") },
{ label: "Absorbing organisation already active?", value: display_value_or_placeholder(merge_request.existing_absorbing_organisation_label), action: merge_request_action(merge_request, "existing_absorbing_organisation") },
{ label: "Absorbing organisation", value: display_value_or_placeholder(merge_request.absorbing_organisation_name, details_prompt_link("absorbing_organisation", merge_request)), action: merge_request_action(merge_request, "absorbing_organisation") },
{ label: "Merging organisations", value: merge_request.merge_request_organisations.any? ? merge_request.merge_request_organisations.map(&:merging_organisation_name).join("<br>").html_safe : display_value_or_placeholder(nil, details_prompt_link("merging_organisations", merge_request)), action: merge_request_action(merge_request, "merging_organisations") },
{ label: "Merge date", value: display_value_or_placeholder(merge_request.merge_date, details_prompt_link("merge_date", merge_request)), action: merge_request_action(merge_request, "merge_date") },
{ label: "Absorbing organisation already active?", value: display_value_or_placeholder(merge_request.existing_absorbing_organisation_label, details_prompt_link("existing_absorbing_organisation", merge_request)), action: merge_request_action(merge_request, "existing_absorbing_organisation") },
]
end
@ -75,9 +97,12 @@ module MergeRequestsHelper
end
end
def merge_request_action(merge_request, page)
def merge_request_action(merge_request, page, attribute = nil)
attribute = page if attribute.nil?
return nil unless value_exists?(merge_request, attribute)
unless merge_request.status == "request_merged" || merge_request.status == "processing"
{ text: "Change", href: send("#{page}_merge_request_path", merge_request, referrer: "check_answers"), visually_hidden_text: page.humanize }
{ text: merge_request_action_text(merge_request, attribute), href: send("#{page}_merge_request_path", merge_request, referrer: "check_answers"), visually_hidden_text: page.humanize }
end
end
@ -287,7 +312,7 @@ module MergeRequestsHelper
elsif merge_request.has_helpdesk_ticket == false
"Not reported by a helpdesk ticket"
else
display_value_or_placeholder(nil)
display_value_or_placeholder(nil, details_prompt_link("helpdesk_ticket", merge_request))
end
end
end

13
app/helpers/organisations_helper.rb

@ -54,4 +54,17 @@ module OrganisationsHelper
def delete_organisation_link(organisation)
govuk_button_link_to "Delete this organisation", delete_confirmation_organisation_path(organisation), warning: true
end
def organisation_action_text(attr, organisation)
return "" if attr[:value].blank? || (attr[:attribute] == "phone" && organisation.phone.blank?)
"Change"
end
def organisation_details_link_message(attribute)
text = lowercase_first_letter(attribute[:name])
return "Add #{text}" if attribute[:name] == "Rent periods"
"Enter #{text}"
end
end

18
app/helpers/schemes_helper.rb

@ -64,10 +64,10 @@ module SchemesHelper
schemes_csv_download_organisation_path(organisation, search:, download_type:)
end
def change_answer_link(scheme, question_id, user)
def scheme_edit_path(scheme, question_id, user = nil)
case question_id
when "service_name", "sensitive", "scheme_type", "registered_under_care_act", "owning_organisation_id", "arrangement_type"
user.support? || !scheme.confirmed? ? scheme_details_path(scheme, referrer: "check-answers") : scheme_edit_name_path(scheme)
user&.support? || !scheme.confirmed? ? scheme_details_path(scheme, referrer: "check-answers") : scheme_edit_name_path(scheme)
when "primary_client_group"
scheme_primary_client_group_path(scheme, referrer: "check-answers")
when "has_other_client_group"
@ -79,6 +79,12 @@ module SchemesHelper
end
end
def change_link_text(question_id, scheme)
return "" if scheme.public_send(question_id).nil?
"Change"
end
def scheme_status_hint(scheme)
case scheme.status
when :deactivating_soon
@ -101,6 +107,14 @@ module SchemesHelper
organisation.owned_schemes.duplicate_sets.any? || organisation.owned_schemes.any? { |scheme| scheme.locations.duplicate_sets.any? }
end
def scheme_details_link_message(attribute)
text = lowercase_first_letter(attribute[:name])
return "Select #{text}" if %w[primary_client_group secondary_client_group support_type intended_stay].include?(attribute[:id])
return "Tell us if it #{text}" if attribute[:id] == "has_other_client_group"
"Enter #{text}"
end
def scheme_back_button_path(scheme, current_page)
return scheme_check_answers_path(scheme) if request.params[:referrer] == "check-answers"
return scheme_confirm_secondary_client_group_path(scheme, referrer: "check-answers") if request.params[:referrer] == "has-other-client-group"

4
app/helpers/tasklist_helper.rb

@ -78,9 +78,9 @@ private
def next_page_or_check_answers(subsection, log, current_user)
path = if subsection.is_started?(log)
"#{log.class.name.underscore}_#{subsection.id}_check_answers_path"
"#{log.log_type}_#{subsection.id}_check_answers_path"
else
"#{log.class.name.underscore}_#{next_question_page(subsection, log, current_user)}_path"
"#{log.log_type}_#{next_question_page(subsection, log, current_user)}_path"
end
send(path, log)

20
app/helpers/user_helper.rb

@ -75,4 +75,24 @@ module UserHelper
text
end
def user_details_html(user, current_user, attribute)
value = user.send(attribute)
return value.humanize if value.present?
case attribute
when "role"
current_user.data_coordinator? || current_user.support? ? govuk_link_to("Select role", aliased_user_edit(user, current_user), class: "govuk-link govuk-link--no-visited-state") : "No role assigned"
when "phone"
govuk_link_to("Enter telephone number", aliased_user_edit(user, current_user), class: "govuk-link govuk-link--no-visited-state")
else
"No answer provided"
end
end
def user_action_text(user, attribute)
return "Change" if %w[role phone].include?(attribute) && user.send(attribute).present?
""
end
end

2
app/models/derived_variables/lettings_log_variables.rb

@ -346,7 +346,7 @@ private
end
def address_answered_without_uprn?
[address_line1, town_or_city].all?(&:present?) && uprn.nil? && form.start_date.year >= 2023
[address_line1, town_or_city].all?(&:present?) && uprn.nil?
end
def get_lar

2
app/models/derived_variables/sales_log_variables.rb

@ -226,7 +226,7 @@ private
end
def address_answered_without_uprn?
[address_line1, town_or_city].all?(&:present?) && uprn.nil? && form.start_date.year >= 2023
[address_line1, town_or_city].all?(&:present?) && uprn.nil?
end
def soctenant_from_prevten_values

2
app/models/form.rb

@ -135,7 +135,7 @@ class Form
end
def cancel_path(page, log)
"#{log.class.name.underscore}_#{page.subsection.id}_check_answers_path"
"#{log.log_type}_#{page.subsection.id}_check_answers_path"
end
def unresolved_log_path

2
app/models/form/lettings/pages/address_matcher.rb

@ -24,6 +24,6 @@ class Form::Lettings::Pages::AddressMatcher < ::Form::Page
def skip_href(log = nil)
return unless log
"/#{log.model_name.param_key.dasherize}s/#{log.id}/property-unit-type"
"/#{log.log_type.dasherize}s/#{log.id}/property-unit-type"
end
end

12
app/models/form/lettings/pages/person_lead_partner.rb

@ -0,0 +1,12 @@
class Form::Lettings::Pages::PersonLeadPartner < ::Form::Page
def initialize(id, hsh, subsection, person_index:)
super(id, hsh, subsection)
@id = "person_#{person_index}_lead_partner"
@depends_on = [{ "details_known_#{person_index}" => 0 }]
@person_index = person_index
end
def questions
@questions ||= [Form::Lettings::Questions::PersonPartner.new(nil, nil, self, person_index: @person_index)]
end
end

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

@ -3,10 +3,7 @@ class Form::Lettings::Pages::Rent4Weekly < ::Form::Page
super
@id = "rent_4_weekly"
@copy_key = "lettings.income_and_benefits.rent_and_charges"
@depends_on = [
{ "household_charge" => 0, "rent_and_charges_paid_every_4_weeks?" => true, "is_carehome?" => false },
{ "household_charge" => nil, "rent_and_charges_paid_every_4_weeks?" => true, "is_carehome?" => false },
]
@depends_on = depends_on
end
def questions
@ -18,4 +15,18 @@ class Form::Lettings::Pages::Rent4Weekly < ::Form::Page
Form::Lettings::Questions::Tcharge4Weekly.new(nil, nil, self),
]
end
def depends_on
if form.start_year_2025_or_later?
[
{ "household_charge" => 0, "rent_and_charges_paid_every_4_weeks?" => true },
{ "household_charge" => nil, "rent_and_charges_paid_every_4_weeks?" => true },
]
else
[
{ "household_charge" => 0, "rent_and_charges_paid_every_4_weeks?" => true, "is_carehome?" => false },
{ "household_charge" => nil, "rent_and_charges_paid_every_4_weeks?" => true, "is_carehome?" => false },
]
end
end
end

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

@ -3,10 +3,7 @@ class Form::Lettings::Pages::RentBiWeekly < ::Form::Page
super
@id = "rent_bi_weekly"
@copy_key = "lettings.income_and_benefits.rent_and_charges"
@depends_on = [
{ "household_charge" => nil, "rent_and_charges_paid_every_2_weeks?" => true, "is_carehome?" => false },
{ "household_charge" => 0, "rent_and_charges_paid_every_2_weeks?" => true, "is_carehome?" => false },
]
@depends_on = depends_on
end
def questions
@ -18,4 +15,18 @@ class Form::Lettings::Pages::RentBiWeekly < ::Form::Page
Form::Lettings::Questions::TchargeBiWeekly.new(nil, nil, self),
]
end
def depends_on
if form.start_year_2025_or_later?
[
{ "household_charge" => nil, "rent_and_charges_paid_every_2_weeks?" => true },
{ "household_charge" => 0, "rent_and_charges_paid_every_2_weeks?" => true },
]
else
[
{ "household_charge" => nil, "rent_and_charges_paid_every_2_weeks?" => true, "is_carehome?" => false },
{ "household_charge" => 0, "rent_and_charges_paid_every_2_weeks?" => true, "is_carehome?" => false },
]
end
end
end

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

@ -3,10 +3,7 @@ class Form::Lettings::Pages::RentMonthly < ::Form::Page
super
@id = "rent_monthly"
@copy_key = "lettings.income_and_benefits.rent_and_charges"
@depends_on = [
{ "household_charge" => nil, "rent_and_charges_paid_monthly?" => true, "is_carehome?" => false },
{ "household_charge" => 0, "rent_and_charges_paid_monthly?" => true, "is_carehome?" => false },
]
@depends_on = depends_on
end
def questions
@ -18,4 +15,18 @@ class Form::Lettings::Pages::RentMonthly < ::Form::Page
Form::Lettings::Questions::TchargeMonthly.new(nil, nil, self),
]
end
def depends_on
if form.start_year_2025_or_later?
[
{ "household_charge" => nil, "rent_and_charges_paid_monthly?" => true },
{ "household_charge" => 0, "rent_and_charges_paid_monthly?" => true },
]
else
[
{ "household_charge" => nil, "rent_and_charges_paid_monthly?" => true, "is_carehome?" => false },
{ "household_charge" => 0, "rent_and_charges_paid_monthly?" => true, "is_carehome?" => false },
]
end
end
end

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

@ -3,10 +3,7 @@ class Form::Lettings::Pages::RentWeekly < ::Form::Page
super
@id = "rent_weekly"
@copy_key = "lettings.income_and_benefits.rent_and_charges"
@depends_on = [
{ "rent_and_charges_paid_weekly?" => true, "household_charge" => 0, "is_carehome?" => false },
{ "rent_and_charges_paid_weekly?" => true, "household_charge" => nil, "is_carehome?" => false },
]
@depends_on = depends_on
end
def questions
@ -18,4 +15,18 @@ class Form::Lettings::Pages::RentWeekly < ::Form::Page
Form::Lettings::Questions::TchargeWeekly.new(nil, nil, self),
]
end
def depends_on
if form.start_year_2025_or_later?
[
{ "rent_and_charges_paid_weekly?" => true, "household_charge" => 0 },
{ "rent_and_charges_paid_weekly?" => true, "household_charge" => nil },
]
else
[
{ "rent_and_charges_paid_weekly?" => true, "household_charge" => 0, "is_carehome?" => false },
{ "rent_and_charges_paid_weekly?" => true, "household_charge" => nil, "is_carehome?" => false },
]
end
end
end

30
app/models/form/lettings/questions/person_partner.rb

@ -0,0 +1,30 @@
class Form::Lettings::Questions::PersonPartner < ::Form::Question
def initialize(id, hsh, page, person_index:)
super(id, hsh, page)
@id = "relat#{person_index}"
@type = "radio"
@check_answers_card_number = person_index
@answer_options = answer_options
@person_index = person_index
@question_number = question_number
end
def answer_options
{
"P" => { "value" => "Yes" },
"X" => { "value" => "No" },
"R" => { "value" => "Tenant prefers not to say" },
}
end
def question_number
base_question_number = case form.start_date.year
when 2023
30
else
29
end
base_question_number + (4 * @person_index)
end
end

32
app/models/form/lettings/questions/previous_tenure.rb

@ -5,7 +5,7 @@ class Form::Lettings::Questions::PreviousTenure < ::Form::Question
@copy_key = "lettings.household_situation.prevten.not_renewal"
@type = "radio"
@check_answers_card_number = 0
@answer_options = ANSWER_OPTIONS
@answer_options = form.start_year_2025_or_later? ? ANSWER_OPTIONS_2025 : ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end
@ -38,5 +38,35 @@ class Form::Lettings::Questions::PreviousTenure < ::Form::Question
"25" => { "value" => "Any other accommodation" },
}.freeze
ANSWER_OPTIONS_2025 = {
"30" => { "value" => "Fixed-term local authority general needs tenancy" },
"32" => { "value" => "Fixed-term private registered provider (PRP) general needs tenancy" },
"31" => { "value" => "Lifetime local authority general needs tenancy" },
"33" => { "value" => "Lifetime private registered provider (PRP) general needs tenancy" },
"34" => { "value" => "Specialist retirement housing" },
"36" => { "value" => "Sheltered housing for adults aged under 55 years" },
"35" => { "value" => "Extra care housing" },
"6" => { "value" => "Other supported housing" },
"3" => { "value" => "Private sector tenancy" },
"27" => { "value" => "Owner occupation (low-cost home ownership)" },
"26" => { "value" => "Owner occupation (private)" },
"28" => { "value" => "Living with friends and family (long-term)" },
"38" => { "value" => "Sofa surfing (moving regularly between family and friends, no permanent bed)" },
"14" => { "value" => "Bed and breakfast" },
"7" => { "value" => "Direct access hostel" },
"10" => { "value" => "Hospital" },
"29" => { "value" => "Prison or approved probation hostel" },
"19" => { "value" => "Rough sleeping" },
"18" => { "value" => "Any other temporary accommodation" },
"13" => { "value" => "Children’s home or foster care" },
"24" => { "value" => "Home Office Asylum Support" },
"37" => { "value" => "Host family or similar refugee accommodation" },
"23" => { "value" => "Mobile home or caravan" },
"21" => { "value" => "Refuge" },
"9" => { "value" => "Residential care home" },
"4" => { "value" => "Tied housing or rented with job" },
"25" => { "value" => "Any other accommodation" },
}.freeze
QUESTION_NUMBER_FROM_YEAR = { 2023 => 78, 2024 => 77 }.freeze
end

12
app/models/form/lettings/questions/sheltered.rb

@ -8,18 +8,18 @@ class Form::Lettings::Questions::Sheltered < ::Form::Question
end
def answer_options
if form.start_year_2024_or_later?
{ "1" => { "value" => "Yes – specialist retirement housing" },
if form.start_year_2025_or_later?
{ "1" => { "value" => "Yes – sheltered housing for tenants with low support needs" },
"2" => { "value" => "Yes – extra care housing" },
"5" => { "value" => "Yes – sheltered housing for adults aged under 55 years" },
"6" => { "value" => "Yes – sheltered housing for adults aged 55 years and over who are not retired" },
"7" => { "value" => "Yes - other" },
"3" => { "value" => "No" },
"divider" => { "value" => true },
"4" => { "value" => "Don’t know" } }
else
{ "2" => { "value" => "Yes – extra care housing" },
"1" => { "value" => "Yes – specialist retirement housing" },
{ "1" => { "value" => "Yes – specialist retirement housing" },
"2" => { "value" => "Yes – extra care housing" },
"5" => { "value" => "Yes – sheltered housing for adults aged under 55 years" },
"6" => { "value" => "Yes – sheltered housing for adults aged 55 years and over who are not retired" },
"3" => { "value" => "No" },
"divider" => { "value" => true },
"4" => { "value" => "Don’t know" } }

25
app/models/form/lettings/questions/waityear.rb

@ -8,8 +8,9 @@ class Form::Lettings::Questions::Waityear < ::Form::Question
end
def answer_options
if form.start_year_2024_or_later?
{
if form.start_year_2025_or_later?
return {
"13" => { "value" => "Household not on the housing register (or waiting list) in this area" },
"2" => { "value" => "Less than 1 year" },
"7" => { "value" => "1 year but under 2 years" },
"8" => { "value" => "2 years but under 3 years" },
@ -20,18 +21,32 @@ class Form::Lettings::Questions::Waityear < ::Form::Question
"divider" => { "value" => true },
"6" => { "value" => "Don’t know" },
}.freeze
else
{
end
if form.start_year_2024_or_later?
return {
"2" => { "value" => "Less than 1 year" },
"7" => { "value" => "1 year but under 2 years" },
"8" => { "value" => "2 years but under 3 years" },
"9" => { "value" => "3 years but under 4 years" },
"10" => { "value" => "4 years but under 5 years" },
"5" => { "value" => "5 years or more" },
"11" => { "value" => "5 years but under 10 years" },
"12" => { "value" => "10 years or more" },
"divider" => { "value" => true },
"6" => { "value" => "Don’t know" },
}.freeze
end
{
"2" => { "value" => "Less than 1 year" },
"7" => { "value" => "1 year but under 2 years" },
"8" => { "value" => "2 years but under 3 years" },
"9" => { "value" => "3 years but under 4 years" },
"10" => { "value" => "4 years but under 5 years" },
"5" => { "value" => "5 years or more" },
"divider" => { "value" => true },
"6" => { "value" => "Don’t know" },
}.freeze
end
QUESTION_NUMBER_FROM_YEAR = { 2023 => 76, 2024 => 75 }.freeze

22
app/models/form/lettings/subsections/household_characteristics.rb

@ -32,7 +32,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::LeadTenantUnderRetirementValueCheck.new("working_situation_lead_tenant_under_retirement_value_check", nil, self),
Form::Lettings::Pages::LeadTenantOverRetirementValueCheck.new("working_situation_lead_tenant_over_retirement_value_check", nil, self),
Form::Lettings::Pages::PersonKnown.new(nil, nil, self, person_index: 2),
Form::Lettings::Pages::PersonRelationshipToLead.new(nil, nil, self, person_index: 2),
relationship_question(person_index: 2),
(Form::Lettings::Pages::PartnerUnder16ValueCheck.new("relationship_2_partner_under_16_value_check", nil, self, person_index: 2) if form.start_year_2024_or_later?),
(Form::Lettings::Pages::MultiplePartnersValueCheck.new("relationship_2_multiple_partners_value_check", nil, self, person_index: 2) if form.start_year_2024_or_later?),
Form::Lettings::Pages::PersonAge.new(nil, nil, self, person_index: 2),
@ -52,7 +52,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("working_situation_2_under_retirement_value_check", nil, self, person_index: 2),
Form::Lettings::Pages::PersonOverRetirementValueCheck.new("working_situation_2_over_retirement_value_check", nil, self, person_index: 2),
Form::Lettings::Pages::PersonKnown.new(nil, nil, self, person_index: 3),
Form::Lettings::Pages::PersonRelationshipToLead.new(nil, nil, self, person_index: 3),
relationship_question(person_index: 3),
(Form::Lettings::Pages::PartnerUnder16ValueCheck.new("relationship_3_partner_under_16_value_check", nil, self, person_index: 3) if form.start_year_2024_or_later?),
(Form::Lettings::Pages::MultiplePartnersValueCheck.new("relationship_3_multiple_partners_value_check", nil, self, person_index: 3) if form.start_year_2024_or_later?),
Form::Lettings::Pages::PersonAge.new(nil, nil, self, person_index: 3),
@ -72,7 +72,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("working_situation_3_under_retirement_value_check", nil, self, person_index: 3),
Form::Lettings::Pages::PersonOverRetirementValueCheck.new("working_situation_3_over_retirement_value_check", nil, self, person_index: 3),
Form::Lettings::Pages::PersonKnown.new(nil, nil, self, person_index: 4),
Form::Lettings::Pages::PersonRelationshipToLead.new(nil, nil, self, person_index: 4),
relationship_question(person_index: 4),
(Form::Lettings::Pages::PartnerUnder16ValueCheck.new("relationship_4_partner_under_16_value_check", nil, self, person_index: 4) if form.start_year_2024_or_later?),
(Form::Lettings::Pages::MultiplePartnersValueCheck.new("relationship_4_multiple_partners_value_check", nil, self, person_index: 4) if form.start_year_2024_or_later?),
Form::Lettings::Pages::PersonAge.new(nil, nil, self, person_index: 4),
@ -92,7 +92,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("working_situation_4_under_retirement_value_check", nil, self, person_index: 4),
Form::Lettings::Pages::PersonOverRetirementValueCheck.new("working_situation_4_over_retirement_value_check", nil, self, person_index: 4),
Form::Lettings::Pages::PersonKnown.new(nil, nil, self, person_index: 5),
Form::Lettings::Pages::PersonRelationshipToLead.new(nil, nil, self, person_index: 5),
relationship_question(person_index: 5),
(Form::Lettings::Pages::PartnerUnder16ValueCheck.new("relationship_5_partner_under_16_value_check", nil, self, person_index: 5) if form.start_year_2024_or_later?),
(Form::Lettings::Pages::MultiplePartnersValueCheck.new("relationship_5_multiple_partners_value_check", nil, self, person_index: 5) if form.start_year_2024_or_later?),
Form::Lettings::Pages::PersonAge.new(nil, nil, self, person_index: 5),
@ -112,7 +112,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("working_situation_5_under_retirement_value_check", nil, self, person_index: 5),
Form::Lettings::Pages::PersonOverRetirementValueCheck.new("working_situation_5_over_retirement_value_check", nil, self, person_index: 5),
Form::Lettings::Pages::PersonKnown.new(nil, nil, self, person_index: 6),
Form::Lettings::Pages::PersonRelationshipToLead.new(nil, nil, self, person_index: 6),
relationship_question(person_index: 6),
(Form::Lettings::Pages::PartnerUnder16ValueCheck.new("relationship_6_partner_under_16_value_check", nil, self, person_index: 6) if form.start_year_2024_or_later?),
(Form::Lettings::Pages::MultiplePartnersValueCheck.new("relationship_6_multiple_partners_value_check", nil, self, person_index: 6) if form.start_year_2024_or_later?),
Form::Lettings::Pages::PersonAge.new(nil, nil, self, person_index: 6),
@ -132,7 +132,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("working_situation_6_under_retirement_value_check", nil, self, person_index: 6),
Form::Lettings::Pages::PersonOverRetirementValueCheck.new("working_situation_6_over_retirement_value_check", nil, self, person_index: 6),
Form::Lettings::Pages::PersonKnown.new(nil, nil, self, person_index: 7),
Form::Lettings::Pages::PersonRelationshipToLead.new(nil, nil, self, person_index: 7),
relationship_question(person_index: 7),
(Form::Lettings::Pages::PartnerUnder16ValueCheck.new("relationship_7_partner_under_16_value_check", nil, self, person_index: 7) if form.start_year_2024_or_later?),
(Form::Lettings::Pages::MultiplePartnersValueCheck.new("relationship_7_multiple_partners_value_check", nil, self, person_index: 7) if form.start_year_2024_or_later?),
Form::Lettings::Pages::PersonAge.new(nil, nil, self, person_index: 7),
@ -152,7 +152,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("working_situation_7_under_retirement_value_check", nil, self, person_index: 7),
Form::Lettings::Pages::PersonOverRetirementValueCheck.new("working_situation_7_over_retirement_value_check", nil, self, person_index: 7),
Form::Lettings::Pages::PersonKnown.new(nil, nil, self, person_index: 8),
Form::Lettings::Pages::PersonRelationshipToLead.new(nil, nil, self, person_index: 8),
relationship_question(person_index: 8),
(Form::Lettings::Pages::PartnerUnder16ValueCheck.new("relationship_8_partner_under_16_value_check", nil, self, person_index: 8) if form.start_year_2024_or_later?),
(Form::Lettings::Pages::MultiplePartnersValueCheck.new("relationship_8_multiple_partners_value_check", nil, self, person_index: 8) if form.start_year_2024_or_later?),
Form::Lettings::Pages::PersonAge.new(nil, nil, self, person_index: 8),
@ -173,4 +173,12 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::PersonOverRetirementValueCheck.new("working_situation_8_over_retirement_value_check", nil, self, person_index: 8),
].compact
end
def relationship_question(person_index:)
if form.start_year_2025_or_later?
Form::Lettings::Pages::PersonLeadPartner.new(nil, nil, self, person_index:)
else
Form::Lettings::Pages::PersonRelationshipToLead.new(nil, nil, self, person_index:)
end
end
end

22
app/models/form/lettings/subsections/income_and_benefits.rb

@ -15,11 +15,7 @@ class Form::Lettings::Subsections::IncomeAndBenefits < ::Form::Subsection
Form::Lettings::Pages::BenefitsProportion.new("benefits_proportion", nil, self),
Form::Lettings::Pages::RentOrOtherCharges.new(nil, nil, self),
Form::Lettings::Pages::RentPeriod.new(nil, nil, self),
Form::Lettings::Pages::CareHomeWeekly.new(nil, nil, self),
Form::Lettings::Pages::CareHomeBiWeekly.new(nil, nil, self),
Form::Lettings::Pages::CareHome4Weekly.new(nil, nil, self),
Form::Lettings::Pages::CareHomeMonthly.new(nil, nil, self),
Form::Lettings::Pages::CareHomeChargesValueCheck.new(nil, nil, self),
carehome_questions,
Form::Lettings::Pages::RentWeekly.new(nil, nil, self),
Form::Lettings::Pages::RentBiWeekly.new(nil, nil, self),
Form::Lettings::Pages::Rent4Weekly.new(nil, nil, self),
@ -30,6 +26,20 @@ class Form::Lettings::Subsections::IncomeAndBenefits < ::Form::Subsection
Form::Lettings::Pages::SupchargValueCheck.new(nil, nil, self),
Form::Lettings::Pages::Outstanding.new(nil, nil, self),
Form::Lettings::Pages::OutstandingAmount.new(nil, nil, self),
].compact
].flatten.compact
end
private
def carehome_questions
return [] if form.start_year_2025_or_later?
[
Form::Lettings::Pages::CareHomeWeekly.new(nil, nil, self),
Form::Lettings::Pages::CareHomeBiWeekly.new(nil, nil, self),
Form::Lettings::Pages::CareHome4Weekly.new(nil, nil, self),
Form::Lettings::Pages::CareHomeMonthly.new(nil, nil, self),
Form::Lettings::Pages::CareHomeChargesValueCheck.new(nil, nil, self),
]
end
end

1
app/models/form/lettings/subsections/property_information.rb

@ -25,6 +25,7 @@ class Form::Lettings::Subsections::PropertyInformation < ::Form::Subsection
Form::Lettings::Pages::VoidDateValueCheck.new(nil, nil, self),
Form::Lettings::Pages::PropertyMajorRepairs.new(nil, nil, self),
Form::Lettings::Pages::PropertyMajorRepairsValueCheck.new(nil, nil, self),
(Form::Lettings::Pages::ShelteredAccommodation.new(nil, nil, self) if form.start_year_2025_or_later?),
].flatten.compact
end

2
app/models/form/lettings/subsections/tenancy_information.rb

@ -16,7 +16,7 @@ class Form::Lettings::Subsections::TenancyInformation < ::Form::Subsection
Form::Lettings::Pages::TenancyLengthAffordableRent.new(nil, nil, self),
Form::Lettings::Pages::TenancyLengthIntermediateRent.new(nil, nil, self),
(Form::Lettings::Pages::TenancyLengthPeriodic.new(nil, nil, self) if form.start_year_2024_or_later?),
Form::Lettings::Pages::ShelteredAccommodation.new(nil, nil, self),
(Form::Lettings::Pages::ShelteredAccommodation.new(nil, nil, self) unless form.start_year_2025_or_later?),
].flatten.compact
end
end

17
app/models/form/question.rb

@ -15,6 +15,7 @@ class Form::Question
@page = page
if hsh
@check_answer_label = hsh["check_answer_label"]
@check_answer_prompt = hsh["check_answer_prompt"]
@header = hsh["header"]
@top_guidance_partial = hsh["top_guidance_partial"]
@bottom_guidance_partial = hsh["bottom_guidance_partial"]
@ -58,6 +59,10 @@ class Form::Question
@check_answer_label ||= I18n.t("forms.#{form.start_date.year}.#{copy_key}.check_answer_label", default: "")
end
def check_answer_prompt
@check_answer_prompt || I18n.t("forms.#{form.start_date.year}.#{copy_key}.check_answer_prompt", default: nil).presence || generate_check_answer_prompt
end
def header
@header ||= I18n.t("forms.#{form.start_date.year}.#{copy_key}.question_text", default: "")
end
@ -130,7 +135,7 @@ class Form::Question
end
def action_text(log, correcting_hard_validation: false)
return "Answer" unless displayed_as_answered?(log)
return "" unless displayed_as_answered?(log)
correcting_hard_validation ? "Clear" : "Change"
end
@ -239,6 +244,14 @@ class Form::Question
I18n.t("validations.not_answered", question: question_text.downcase)
end
def generate_check_answer_prompt
prompt_ending = lowercase_first_letter(error_label.presence || check_answer_label.presence || header.presence || id.humanize) || "this question."
return "Select #{prompt_ending}" if %w[checkbox radio select].include?(type)
return "Set #{prompt_ending}" if type == "date"
"Enter #{prompt_ending}"
end
def suffix_label(log)
return "" unless suffix
return suffix if suffix.is_a?(String)
@ -276,7 +289,7 @@ class Form::Question
end
def question_number_string(hidden: false)
if @question_number && !hidden && form.start_date.year >= 2023
if @question_number && !hidden
"Q#{@question_number}"
end
end

2
app/models/form/sales/pages/address_matcher.rb

@ -24,6 +24,6 @@ class Form::Sales::Pages::AddressMatcher < ::Form::Page
def skip_href(log = nil)
return unless log
"/#{log.model_name.param_key.dasherize}s/#{log.id}/property-number-of-bedrooms"
"/#{log.log_type.dasherize}s/#{log.id}/property-number-of-bedrooms"
end
end

4
app/models/form/sales/questions/ownership_scheme.rb

@ -9,8 +9,8 @@ class Form::Sales::Questions::OwnershipScheme < ::Form::Question
def answer_options
if form.start_year_2025_or_later?
{
"1" => { "value" => "Yes - a shared ownership scheme", "hint" => "When the purchaser buys an initial share of up to 75% of the property value and pays rent to the Private Registered Provider (PRP) on the remaining portion, or a subsequent staircasing transaction" },
"2" => { "value" => "Yes - a discounted ownership scheme" },
"1" => { "value" => "Shared Ownership", "hint" => "When the purchaser buys an initial share of up to 75% of the property value and pays rent to the Private Registered Provider (PRP) on the remaining portion, or a subsequent staircasing transaction" },
"2" => { "value" => "Discounted Ownership" },
}.freeze
elsif form.start_year_2024_or_later?
{

2
app/models/forms/bulk_upload_form/prepare_your_file.rb

@ -11,8 +11,6 @@ module Forms
def view_path
case year
when 2023
"bulk_upload_#{log_type}_logs/forms/prepare_your_file_2023"
when 2024
"bulk_upload_#{log_type}_logs/forms/prepare_your_file_2024"
end

10
app/models/lettings_log.rb

@ -240,7 +240,7 @@ class LettingsLog < Log
end
def applicable_income_range
return unless ecstat1 && hhmemb
return unless ecstat1 && hhmemb && ALLOWED_INCOME_RANGES[ecstat1]
range = ALLOWED_INCOME_RANGES[ecstat1].clone
@ -670,8 +670,7 @@ class LettingsLog < Log
["owning_organisation_id",
"startdate",
"tenancycode",
form.start_date.year < 2023 || uprn.blank? ? "postcode_full" : nil,
form.start_date.year >= 2023 && uprn.present? ? "uprn" : nil,
uprn.blank? ? "postcode_full" : "uprn",
"scheme_id",
"location_id",
"age1",
@ -736,6 +735,10 @@ class LettingsLog < Log
scheme_locations_count > 19
end
def log_type
"lettings_log"
end
private
def reset_invalid_unresolved_log_fields!
@ -884,7 +887,6 @@ private
def should_process_uprn_change?
return unless uprn
return unless startdate
return unless collection_start_year_for_date(startdate) >= 2023
uprn_changed? || startdate_changed?
end

36
app/models/log.rb

@ -343,31 +343,27 @@ private
PIO = PostcodeService.new
LA_CHANGES = {
"E07000027" => "E06000064", # Barrow-in-Furness => Westmorland and Furness
"E07000030" => "E06000064", # Eden => Westmorland and Furness
"E07000031" => "E06000064", # South Lakeland => Westmorland and Furness
"E07000026" => "E06000063", # Allerdale => Cumberland
"E07000028" => "E06000063", # Carlisle => Cumberland
"E07000029" => "E06000063", # Copeland => Cumberland
"E07000163" => "E06000065", # Craven => North Yorkshire
"E07000164" => "E06000065", # Hambleton => North Yorkshire
"E07000165" => "E06000065", # Harrogate => North Yorkshire
"E07000166" => "E06000065", # Richmondshire => North Yorkshire
"E07000167" => "E06000065", # Ryedale => North Yorkshire
"E07000168" => "E06000065", # Scarborough => North Yorkshire
"E07000169" => "E06000065", # Selby => North Yorkshire
"E07000187" => "E06000066", # Mendip => Somerset
"E07000188" => "E06000066", # Sedgemoor => Somerset
"E07000246" => "E06000066", # Somerset West and Taunton => Somerset
"E07000189" => "E06000066", # South Somerset => Somerset
2025 => {
"E08000016" => "E08000038", # Barnsley
"E08000019" => "E08000039", # Sheffield
},
}.freeze
BACKWARDS_LA_CHANGES = {
2024 => {
"E08000038" => "E08000016", # Barnsley
"E08000039" => "E08000019", # Sheffield
},
}.freeze
def get_inferred_la(postcode)
result = PIO.lookup(postcode)
location_code = result[:location_code] if result
if LA_CHANGES.key?(location_code) && form.start_date.year >= 2023
LA_CHANGES[location_code]
elsif !(LA_CHANGES.value?(location_code) && form.start_date.year < 2023)
if LA_CHANGES[form.start_date.year]&.key?(location_code)
LA_CHANGES[form.start_date.year][location_code]
elsif BACKWARDS_LA_CHANGES[form.start_date.year]&.key?(location_code)
BACKWARDS_LA_CHANGES[form.start_date.year][location_code]
elsif !LA_CHANGES.value?(location_code)
location_code
end
end

33
app/models/sales_log.rb

@ -129,33 +129,12 @@ class SalesLog < Log
def dynamically_not_required
not_required = []
not_required << "proplen" if proplen_optional?
not_required << "mortlen" if mortlen_optional?
not_required << "frombeds" if frombeds_optional?
not_required << "deposit" if form.start_year_2024_or_later? && stairowned_100?
not_required += %w[address_line2 county]
not_required
end
def proplen_optional?
return false unless collection_start_year
collection_start_year < 2023
end
def mortlen_optional?
return false unless collection_start_year
collection_start_year < 2023
end
def frombeds_optional?
return false unless collection_start_year
collection_start_year < 2023
end
def unresolved
false
end
@ -440,16 +419,15 @@ class SalesLog < Log
def income_soft_min_for_ecstat(ecstat_field)
economic_status_code = public_send(ecstat_field)
return unless ALLOWED_INCOME_RANGES_SALES
return unless ALLOWED_INCOME_RANGES_SALES && ALLOWED_INCOME_RANGES_SALES[economic_status_code]
soft_min = ALLOWED_INCOME_RANGES_SALES[economic_status_code]&.soft_min
soft_min = ALLOWED_INCOME_RANGES_SALES[economic_status_code].soft_min
format_as_currency(soft_min)
end
def should_process_uprn_change?
return unless uprn
return unless saledate
return unless collection_start_year_for_date(saledate) >= 2023
uprn_changed? || saledate_changed?
end
@ -514,8 +492,7 @@ class SalesLog < Log
"age1",
"sex1",
"ecstat1",
form.start_date.year < 2023 || uprn.blank? ? "postcode_full" : nil,
form.start_date.year >= 2023 && uprn.present? ? "uprn" : nil].compact
uprn.blank? ? "postcode_full" : "uprn"].compact
end
def soctenant_is_inferred?
@ -561,4 +538,8 @@ class SalesLog < Log
def is_firststair?
firststair == 1
end
def log_type
"sales_log"
end
end

55
app/models/validations/date_validations.rb

@ -2,53 +2,56 @@ module Validations::DateValidations
include Validations::SharedValidations
def validate_property_major_repairs(record)
date_valid?("mrcdate", record)
if record["startdate"].present? && record["mrcdate"].present? && record["startdate"] < record["mrcdate"]
record.errors.add :mrcdate, I18n.t("validations.lettings.date.mrcdate.before_tenancy_start")
end
return unless record["mrcdate"].present? && date_valid?("mrcdate", record)
if is_rsnvac_first_let?(record) && record["mrcdate"].present?
record.errors.add :mrcdate, I18n.t("validations.lettings.date.mrcdate.not_first_let")
end
return unless record["startdate"].present? && date_valid?("startdate", record)
if record["mrcdate"].present? && record["startdate"].present? && record["startdate"].to_date - record["mrcdate"].to_date > 3650
if record["startdate"].present? && record["mrcdate"].present? && record["startdate"] < record["mrcdate"]
record.errors.add :mrcdate, I18n.t("validations.lettings.date.mrcdate.before_tenancy_start")
record.errors.add :startdate, I18n.t("validations.lettings.date.startdate.after_major_repair_date")
end
if record.form.start_year_2025_or_later?
if record["startdate"].to_date - 20.years > record["mrcdate"].to_date
record.errors.add :mrcdate, I18n.t("validations.lettings.date.mrcdate.twenty_years_before_tenancy_start")
record.errors.add :startdate, I18n.t("validations.lettings.date.startdate.twenty_years_after_mrc_date")
end
elsif record["startdate"].to_date - 10.years > record["mrcdate"].to_date
record.errors.add :mrcdate, I18n.t("validations.lettings.date.mrcdate.ten_years_before_tenancy_start")
record.errors.add :startdate, I18n.t("validations.lettings.date.startdate.ten_years_after_mrc_date")
end
end
def validate_property_void_date(record)
if record["voiddate"].present? && record["startdate"].present? && record["startdate"].to_date - record["voiddate"].to_date > 3650
record.errors.add :voiddate, I18n.t("validations.lettings.date.void_date.ten_years_before_tenancy_start")
end
if record["voiddate"].present? && record["startdate"].present? && record["startdate"].to_date < record["voiddate"].to_date
record.errors.add :voiddate, I18n.t("validations.lettings.date.void_date.before_tenancy_start")
end
return unless record["voiddate"].present? && date_valid?("voiddate", record)
if record["voiddate"].present? && record["mrcdate"].present? && record["mrcdate"].to_date < record["voiddate"].to_date
if record["mrcdate"].present? && record["mrcdate"].to_date < record["voiddate"].to_date
record.errors.add :voiddate, :after_mrcdate, message: I18n.t("validations.lettings.date.void_date.after_mrcdate")
record.errors.add :mrcdate, I18n.t("validations.lettings.date.mrcdate.before_void_date")
end
end
def validate_startdate(record)
return unless record.startdate && date_valid?("startdate", record)
return unless record["startdate"].present? && date_valid?("startdate", record)
if record["voiddate"].present? && record.startdate < record["voiddate"]
if record["startdate"].to_date < record["voiddate"].to_date
record.errors.add :voiddate, I18n.t("validations.lettings.date.void_date.before_tenancy_start")
record.errors.add :startdate, I18n.t("validations.lettings.date.startdate.after_void_date")
end
if record["mrcdate"].present? && record.startdate < record["mrcdate"]
record.errors.add :startdate, I18n.t("validations.lettings.date.startdate.after_major_repair_date")
end
if record["voiddate"].present? && record["startdate"].to_date - record["voiddate"].to_date > 3650
if record.form.start_year_2025_or_later?
if record["startdate"].to_date - 20.years > record["voiddate"].to_date
record.errors.add :voiddate, I18n.t("validations.lettings.date.void_date.twenty_years_before_tenancy_start")
record.errors.add :startdate, I18n.t("validations.lettings.date.startdate.twenty_years_after_void_date")
end
elsif record["startdate"].to_date - 10.years > record["voiddate"].to_date
record.errors.add :voiddate, I18n.t("validations.lettings.date.void_date.ten_years_before_tenancy_start")
record.errors.add :startdate, I18n.t("validations.lettings.date.startdate.ten_years_after_void_date")
end
end
if record["mrcdate"].present? && record["startdate"].to_date - record["mrcdate"].to_date > 3650
record.errors.add :startdate, I18n.t("validations.lettings.date.startdate.ten_years_after_mrc_date")
end
def validate_startdate(record)
date_valid?("startdate", record)
end
private

2
app/models/validations/financial_validations.rb

@ -25,7 +25,7 @@ module Validations::FinancialValidations
end
def validate_net_income(record)
if record.ecstat1 && record.hhmemb && record.weekly_net_income && record.startdate && record.form.start_date.year >= 2023
if record.ecstat1 && record.hhmemb && record.weekly_net_income && record.startdate
if record.weekly_net_income > record.applicable_income_range.hard_max
frequency = record.form.get_question("incfreq", record).label_from_value(record.incfreq).downcase
hard_max = format_as_currency(record.applicable_income_range.hard_max)

2
app/models/validations/household_validations.rb

@ -201,7 +201,7 @@ module Validations::HouseholdValidations
def validate_layear_and_prevloc(record)
return unless record.layear && record.la && record.prevloc && record.collection_start_year
if record.la == record.prevloc && record.layear == 1 && record.collection_start_year >= 2023
if record.la == record.prevloc && record.layear == 1
record.errors.add :layear, I18n.t("validations.lettings.household.layear.same_la_just_moved_to_area")
record.errors.add :la, I18n.t("validations.lettings.household.la.same_la_just_moved_to_area")
record.errors.add :postcode_full, I18n.t("validations.lettings.household.postcode_full.same_la_just_moved_to_area")

2
app/models/validations/sales/financial_validations.rb

@ -82,7 +82,7 @@ module Validations::Sales::FinancialValidations
def validate_child_income(record)
return unless record.income2 && record.ecstat2
if record.income2.positive? && is_economic_status_child?(record.ecstat2) && record.form.start_date.year >= 2023
if record.income2.positive? && is_economic_status_child?(record.ecstat2)
record.errors.add :ecstat2, I18n.t("validations.sales.financial.ecstat2.child_has_income")
record.errors.add :income2, I18n.t("validations.sales.financial.income2.child_has_income")
end

2
app/models/validations/sales/household_validations.rb

@ -13,8 +13,6 @@ module Validations::Sales::HouseholdValidations
end
def validate_buyers_living_in_property(record)
return unless record.form.start_date.year >= 2023
if record.buyers_will_live_in? && record.buyer_one_will_not_live_in_property? && record.buyer_two_will_not_live_in_property?
record.errors.add :buylivein, I18n.t("validations.sales.household.buylivein.buyers_will_live_in_property_values_inconsistent")
record.errors.add :buy1livein, I18n.t("validations.sales.household.buy1livein.buyers_will_live_in_property_values_inconsistent")

4
app/models/validations/sales/sale_information_validations.rb

@ -144,11 +144,11 @@ module Validations::Sales::SaleInformationValidations
return unless record.saledate && record.form.start_year_2024_or_later?
return unless record.discount && record.value && record.la
if record.london_property? && record.discount_value > 136_400
if record.london_property? && record.discount_value > 137_400
%i[discount value la postcode_full uprn].each do |field|
record.errors.add field, I18n.t("validations.sales.sale_information.#{field}.value_over_discounted_london_max", discount_value: record.field_formatted_as_currency("discount_value"))
end
elsif record.property_not_in_london? && record.discount_value > 102_400
elsif record.property_not_in_london? && record.discount_value > 103_400
%i[discount value la postcode_full uprn].each do |field|
record.errors.add field, I18n.t("validations.sales.sale_information.#{field}.value_over_discounted_max", discount_value: record.field_formatted_as_currency("discount_value"))
end

17
app/models/validations/shared_validations.rb

@ -54,14 +54,15 @@ module Validations::SharedValidations
field = question.check_answer_label || question.id
incorrect_accuracy = (value.to_d * 100) % (question.step * 100) != 0
if question.step < 1 && incorrect_accuracy
record.errors.add question.id.to_sym, I18n.t("validations.shared.numeric.nearest_hundredth", field:)
elsif incorrect_accuracy || value.to_d != value.to_i # if the user enters a value in exponent notation (eg '4e1') the to_i method does not convert this to the correct value
field = question.check_answer_label || question.id
case question.step
when 1 then record.errors.add question.id.to_sym, :not_integer, message: I18n.t("validations.shared.numeric.whole_number", field:)
when 10 then record.errors.add question.id.to_sym, I18n.t("validations.shared.numeric.nearest_ten", field:)
end
next unless incorrect_accuracy
case question.step
when 0.01 then record.errors.add question.id.to_sym, I18n.t("validations.shared.numeric.nearest_hundredth", field:)
when 0.1 then record.errors.add question.id.to_sym, I18n.t("validations.shared.numeric.nearest_tenth", field:)
when 1 then record.errors.add question.id.to_sym, :not_integer, message: I18n.t("validations.shared.numeric.whole_number", field:)
when 10 then record.errors.add question.id.to_sym, I18n.t("validations.shared.numeric.nearest_ten", field:)
else
record.errors.add question.id.to_sym, I18n.t("validations.shared.numeric.nearest_step", field:, step: question.step)
end
end
end

4
app/models/validations/soft_validations.rb

@ -16,13 +16,13 @@ module Validations::SoftValidations
}.freeze
def net_income_in_soft_max_range?
return unless weekly_net_income && ecstat1 && hhmemb
return unless weekly_net_income && ecstat1 && hhmemb && applicable_income_range
weekly_net_income.between?(applicable_income_range.soft_max, applicable_income_range.hard_max)
end
def net_income_in_soft_min_range?
return unless weekly_net_income && ecstat1 && hhmemb
return unless weekly_net_income && ecstat1 && hhmemb && applicable_income_range
weekly_net_income.between?(applicable_income_range.hard_min, applicable_income_range.soft_min)
end

58
app/services/documentation_generator.rb

@ -18,14 +18,16 @@ class DocumentationGenerator
next
end
validation_source = method(meth).source
validation = method(meth)
validation_source = validation.source
file_path = validation.source_location[0]
helper_methods_source = all_helper_methods.map { |helper_method|
if validation_source.include?(helper_method.to_s)
method(helper_method).source
end
}.compact.join("\n")
response = describe_hard_validation(client, meth, validation_source, helper_methods_source, form)
response = describe_hard_validation(client, meth, validation_source, helper_methods_source, form, file_path)
next unless response
begin
@ -41,19 +43,19 @@ class DocumentationGenerator
def describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, log_type)
all_validation_methods.each do |meth|
if LogValidation.where(validation_name: meth.to_s, bulk_upload_specific: true, from: form.start_date, log_type:).exists?
if LogValidation.where(validation_name: meth.to_s, bulk_upload_specific: true, collection_year: "#{form.start_date.year}/#{form.start_date.year + 1}", log_type:).exists?
Rails.logger.info("Validation #{meth} already exists for #{form.start_date.year}")
next
end
validation_source = row_parser_class.instance_method(meth).source
validation = row_parser_class.instance_method(meth)
validation_source = validation.source
helper_methods_source = all_helper_methods.map { |helper_method|
if validation_source.include?(helper_method.to_s)
row_parser_class.instance_method(helper_method).source
end
}.compact.join("\n")
response = describe_hard_validation(client, meth, validation_source, helper_methods_source, form)
response = describe_hard_validation(client, meth, validation_source, helper_methods_source, form, validation.source_location[0])
next unless response
begin
@ -69,7 +71,7 @@ class DocumentationGenerator
def describe_soft_validations(client, all_validation_methods, all_helper_methods, log_type)
validation_descriptions = {}
all_validation_methods.each do |meth|
all_validation_methods[0..5].each do |meth|
validation_source = method(meth).source
helper_methods_source = all_helper_methods.map { |helper_method|
if validation_source.include?(helper_method.to_s)
@ -101,8 +103,8 @@ class DocumentationGenerator
private
def describe_hard_validation(client, meth, validation_source, helper_methods_source, form)
en_yml = File.read("./config/locales/en.yml")
def describe_hard_validation(client, meth, validation_source, helper_methods_source, form, file_path)
en_yml = File.read(translation_file_path(form, file_path))
begin
client.chat(
@ -166,14 +168,6 @@ private
required: %w[error_message field],
},
},
from: {
type: :number,
description: "the year from which the validation starts. If this validation runs for logs with a startdate after a certain year, specify that year here, only if it is not specified in the validation method, leave this field blank",
},
to: {
type: :number,
description: "the year in which the validation ends. If this validation runs for logs with a startdate before a certain year, specify that year here, only if it is not specified in the validation method, leave this field blank",
},
validation_type: {
type: :string,
enum: %w[presence format minimum maximum range inclusion length other],
@ -271,8 +265,7 @@ Look at these helper methods where needed to understand what is being checked in
error_message: error["error_message"],
case: case_info["case_description"],
section: form.get_question(error["field"], nil)&.subsection&.id,
from: case_info["from"] || "",
to: case_info["to"] || "",
collection_year: "#{form.start_date.year}/#{form.start_date.year + 1}",
validation_type: case_info["validation_type"],
hard_soft: "hard",
other_validated_models: case_info["other_validated_models"])
@ -295,8 +288,7 @@ Look at these helper methods where needed to understand what is being checked in
error_message: error["error_message"],
case: case_info["case_description"],
section: form.get_question(error_field, nil)&.subsection&.id,
from: form.start_date,
to: form.start_date + 1.year,
collection_year: "#{form.start_date.year}/#{form.start_date.year + 1}",
validation_type: case_info["validation_type"],
hard_soft: "hard",
other_validated_models: case_info["other_validated_models"],
@ -333,7 +325,7 @@ Look at these helper methods where needed to understand what is being checked in
return
end
if LogValidation.where(validation_name: validation_depends_on_hash.keys.first, field: page_the_validation_applied_to.questions.first.id, from: form.start_date, log_type:).exists?
if LogValidation.where(validation_name: validation_depends_on_hash.keys.first, field: page_the_validation_applied_to.questions.first.id, collection_year: "#{form.start_date.year}/#{form.start_date.year + 1}", log_type:).exists?
Rails.logger.info("Validation #{validation_depends_on_hash.keys.first} already exists for #{page_the_validation_applied_to.questions.first.id} for start year #{form.start_date.year}")
return
end
@ -360,12 +352,30 @@ Look at these helper methods where needed to understand what is being checked in
error_message:,
case: case_info,
section: form.get_question(page_the_validation_applied_to.questions.first.id, nil)&.subsection&.id,
from: form.start_date,
to: form.start_date + 1.year,
collection_year: "#{form.start_date.year}/#{form.start_date.year + 1}",
validation_type: result["validation_type"],
hard_soft: "soft",
other_validated_models: result["other_validated_models"])
Rails.logger.info("******** described #{validation_depends_on_hash.keys.first} for #{page_the_validation_applied_to.questions.first.id} ********")
end
TRANSLATION_FILE_MAPPINGS = {
"property" => "property_information",
}.freeze
def translation_file_path(form, file_path)
return "./config/locales/validations/#{form.type}/#{form.start_date.year}/bulk_upload.en.yml" if file_path.include?("bulk_upload")
file_name = file_path.split("/").last.gsub("_validations.rb", "")
translation_file_name = TRANSLATION_FILE_MAPPINGS[file_name] || file_name
file_path = "./config/locales/validations/#{form.type}/#{translation_file_name}.en.yml"
return file_path if File.exist?(file_path)
shared_file_path = "./config/locales/validations/#{translation_file_name}.en.yml"
return shared_file_path if File.exist?(shared_file_path)
"./config/locales/en.yml"
end
end

3
app/services/exports/organisation_export_constants.rb

@ -22,6 +22,7 @@ module Exports::OrganisationExportConstants
"dsa_signed_at",
"dpo_email",
"profit_status",
"group"
"group",
"status"
]
end

2
app/services/exports/organisation_export_service.rb

@ -65,6 +65,8 @@ module Exports
attribute_hash["available_from"] = organisation.available_from&.iso8601
attribute_hash["profit_status"] = nil # will need update when we add the field to the org
attribute_hash["group"] = nil # will need update when we add the field to the org
attribute_hash["status"] = organisation.status
attribute_hash["active"] = attribute_hash["status"] == :active
attribute_hash
end

46
app/views/bulk_upload_lettings_logs/forms/prepare_your_file_2023.html.erb

@ -1,46 +0,0 @@
<% content_for :before_content do %>
<%= govuk_back_link href: @form.back_path %>
<% end %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= form_with model: @form, scope: :form, url: bulk_upload_lettings_log_path(id: "prepare-your-file"), method: :patch do |f| %>
<%= f.hidden_field :year %>
<span class="govuk-caption-l">Upload lettings logs in bulk (<%= @form.year_combo %>)</span>
<h1 class="govuk-heading-l">Prepare your file</h1>
<p class="govuk-body govuk-!-margin-bottom-2"><%= govuk_link_to "Read the full guidance", bulk_upload_lettings_log_path(id: "guidance", form: { year: @form.year }, referrer: "prepare-your-file") %> before you start if you have not used bulk upload before.</p>
<h2 class="govuk-heading-s">Download template</h2>
<p class="govuk-body govuk-!-margin-bottom-2">Use one of these templates to upload logs for 2023/24:</p>
<ul class="govuk-list govuk-list--bullet">
<li>
<%= govuk_link_to "Download the new template", @form.template_path %>: In this template, the questions are in the same order as the 2023/24 paper form and web form.
</li>
</ul>
<p class="govuk-body govuk-!-margin-bottom-2">There are 7 or 8 rows of content in the templates. These rows are called the ‘headers’. They contain the CORE form questions and guidance about which questions are required and how to format your answers.</p>
<h2 class="govuk-heading-s">Create your file</h2>
<ul class="govuk-list govuk-list--bullet">
<li>Fill in the template with data from your housing management system. Your data should go below the headers, with one row per log. Leave column A blank - the bulk upload fields start in column B.</li>
<li>Make sure each column of your data aligns with the matching headers above. You may need to reorder your data.</li>
<li>Use the <%= govuk_link_to "Lettings #{@form.year_combo} Bulk Upload Specification", @form.specification_path %> to check your data is in the correct format.</li>
<li><strong>Username field:</strong> To assign a log to someone else, enter the email address they use to log into CORE.</li>
<li>If you are using the new template, keep the headers. If you are using the legacy template, you can either keep or remove the headers. If you remove the headers, you should also remove the blank column A.</li>
</ul>
<%= govuk_inset_text(text: "You can upload both general needs and supported housing logs in the same file for 2023 to 2024 data.") %>
<h2 class="govuk-heading-s">Save your file</h2>
<ul class="govuk-list govuk-list--bullet">
<li>Save your file as a CSV.</li>
<li>Your file should now be ready to upload.</li>
</ul>
<%= f.govuk_submit class: "govuk-!-margin-top-7" %>
<% end %>
</div>
</div>

40
app/views/bulk_upload_sales_logs/forms/prepare_your_file_2023.html.erb

@ -1,40 +0,0 @@
<% content_for :before_content do %>
<%= govuk_back_link href: @form.back_path %>
<% end %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= form_with model: @form, scope: :form, url: bulk_upload_sales_log_path(id: "prepare-your-file"), method: :patch do |f| %>
<%= f.hidden_field :year %>
<span class="govuk-caption-l">Upload sales logs in bulk (<%= @form.year_combo %>)</span>
<h1 class="govuk-heading-l">Prepare your file</h1>
<p class="govuk-body govuk-!-margin-bottom-2"><%= govuk_link_to "Read the full guidance", bulk_upload_sales_log_path(id: "guidance", form: { year: @form.year }, referrer: "prepare-your-file") %> before you start if you have not used bulk upload before.</p>
<h2 class="govuk-heading-s">Download template</h2>
<p class="govuk-body govuk-!-margin-bottom-2">Use one of these templates to upload logs for 2023/24:</p>
<ul class="govuk-list govuk-list--bullet">
<li><%= govuk_link_to "Download the new template", @form.template_path %>: In this template, the questions are in the same order as the 2023/24 paper form and web form.</li>
</ul>
<p class="govuk-body govuk-!-margin-bottom-2">There are 7 or 8 rows of content in the templates. These rows are called the ‘headers’. They contain the CORE form questions and guidance about which questions are required and how to format your answers.</p>
<h2 class="govuk-heading-s">Create your file</h2>
<ul class="govuk-list govuk-list--bullet">
<li>Fill in the template with data from your housing management system. Your data should go below the headers, with one row per log. The bulk upload fields start at column B. Leave column A blank.</li>
<li>Make sure each column of your data aligns with the matching headers above. You may need to reorder your data.</li>
<li>Use the <%= govuk_link_to "Sales #{@form.year_combo} Bulk Upload Specification", @form.specification_path %> to check your data is in the correct format.</li>
<li><strong>Username field:</strong> To assign a log to someone else, enter the email address they use to log into CORE.</li>
<li>If you are using the new template, keep the headers. If you are using the legacy template, you can either keep or remove the headers. If you remove the headers, you should also remove the blank column A.</li>
</ul>
<h2 class="govuk-heading-s">Save your file</h2>
<ul class="govuk-list govuk-list--bullet">
<li>Save your file as a CSV.</li>
<li>Your file should now be ready to upload.</li>
</ul>
<%= f.govuk_submit %>
<% end %>
</div>
</div>

21
app/views/bulk_upload_shared/guidance.html.erb

@ -28,29 +28,14 @@
<%= accordion.with_section(heading_text: "Using the bulk upload template") do %>
<p class="govuk-body">For each collection year, we publish a bulk upload template and specification.</p>
<% if @form.year == 2023 %>
<p class="govuk-body">The bulk upload templates contain 7 or 8 rows of ‘headers’ with information about how to fill in the template, including:</p>
<% else %>
<p class="govuk-body">The bulk upload templates contain 8 rows of ‘headers’ with information about how to fill in the template, including:</p>
<% end %>
<p class="govuk-body">The bulk upload templates contain 8 rows of ‘headers’ with information about how to fill in the template, including:</p>
<%= govuk_list ["the CORE form questions and their field numbers", "each field’s valid responses", "if/when certain fields can be left blank", "which fields are used to check for duplicate logs"], type: :bullet %>
<p class="govuk-body">You can paste your data below the headers or copy the headers and insert them above the data in your file. The bulk upload fields start at column B. Leave column A blank.</p>
<p class="govuk-body">Make sure that each column of data aligns with the corresponding question in the headers. We recommend ordering your data to match the headers, but you can also reorder the headers to match your data. When processing the file, we check what each column of data represents based on the headers above.</p>
<% if @form.year == 2023 %>
<p class="govuk-body">For 2023/24 uploads, there are 2 templates to choose from, a new template and a legacy template. They outline suggested ways of ordering your data.</p>
<p class="govuk-body">You must include the headers in your file when you upload, unless your data matches the exact order of the legacy template. If you choose to remove the headers, you must also remove the blank column A.</p>
<p class="govuk-body"><strong>New template</strong>: In this template, the questions are in the same order as the 2023/24 paper form and web form. Use this template if your organisation is new to bulk upload or if your housing management system matches the new column ordering.</p>
<p class="govuk-body"><%= govuk_link_to "Download the lettings bulk upload template (2023 to 2024) – New question ordering", @form.lettings_template_path %></p>
<p class="govuk-body"><%= govuk_link_to "Download the sales bulk upload template (2023 to 2024) – New question ordering", @form.sales_template_path %></p>
<p class="govuk-body"><strong>Legacy template</strong>: In this template, the questions are in the same order as the 2022/23 template, with new questions added on to the end. Use this template if you have not updated your system to match the new template yet.</p>
<% else %>
<p class="govuk-body"><%= govuk_link_to "Download the lettings bulk upload template (2024 to 2025)", @form.lettings_template_path %></p>
<p class="govuk-body"><%= govuk_link_to "Download the sales bulk upload template (2024 to 2025)", @form.sales_template_path %></p>
<% end %>
<p class="govuk-body"><%= govuk_link_to "Download the lettings bulk upload template (2024 to 2025)", @form.lettings_template_path %></p>
<p class="govuk-body"><%= govuk_link_to "Download the sales bulk upload template (2024 to 2025)", @form.sales_template_path %></p>
<% end %>
<%= accordion.with_section(heading_text: "Using the bulk upload specification") do %>

2
app/views/check_errors/confirm_clear_all_answers.html.erb

@ -10,7 +10,7 @@
<p class="govuk-body">You've selected <%= @questions_to_clear.count %> answers to clear</p>
<%= govuk_warning_text(text: "Dependent answers related to this question may also get cleared. You will not be able to undo this action") %>
<%= form_with model: @log, url: send("#{@log.model_name.param_key}_#{@page.id}_path", @log), method: "post", local: true do |f| %>
<%= form_with model: @log, url: send("#{@log.log_type}_#{@page.id}_path", @log), method: "post", local: true do |f| %>
<% @related_question_ids.each do |id| %>
<%= f.hidden_field id, value: @log[id] %>

2
app/views/check_errors/confirm_clear_answer.html.erb

@ -9,7 +9,7 @@
</h1>
<%= govuk_warning_text(text: "Dependent answers related to this question may also get cleared. You will not be able to undo this action.") %>
<%= form_with model: @log, url: send("#{@log.model_name.param_key}_#{@page.id}_path", @log), method: "post", local: true do |f| %>
<%= form_with model: @log, url: send("#{@log.log_type}_#{@page.id}_path", @log), method: "post", local: true do |f| %>
<% @related_question_ids.each do |id| %>
<%= f.hidden_field id, value: @log[id] %>

25
app/views/components/_date_picker.html.erb

@ -0,0 +1,25 @@
<div class="moj-datepicker" data-module="moj-date-picker">
<% question_has_errors = resource.errors[question_id].any? %>
<div class="govuk-form-group<%= " govuk-form-group--error" if question_has_errors %>">
<% if legend.present? %>
<legend class="govuk-fieldset__legend govuk-fieldset__legend--<%= legend[:size] %>">
<h1 class="govuk-fieldset__heading">
<span class="govuk-caption-l">
<%= legend[:caption][:text] if legend[:caption].present? %>
</span>
<%= legend[:text] %>
</h1>
</legend>
<% end %>
<div class="govuk-hint" id="<%= [resource_type.dasherize, question_id.to_s.dasherize, "hint"].join("-") %>">
<%= hint %>
</div>
<% if question_has_errors %>
<p class="govuk-error-message" id="<%= [resource_type.dasherize, question_id.to_s.dasherize, "error"].join("-") %>">
<span class="govuk-visually-hidden">Error:</span> <%= resource.errors[question_id].first %>
</p>
<% end %>
<%= f.text_field question_id.to_sym, class: "govuk-input moj-js-datepicker-input#{' govuk-input--error' if question_has_errors}", id: [resource_type, question_id].join("_"), aria: { describedby: "#{[resource_type, question_id].join('_')}-hint#{"#{[resource_type, question_id].join('_')} -error" if question_has_errors}" }, autocomplete: "off", value: resource[question_id]&.strftime("%d/%m/%Y") %>
</div>
</div>

2
app/views/duplicate_logs/_duplicate_log.html.erb

@ -4,7 +4,7 @@
<div class="govuk-grid-column-one-third">
<header class="app-log-summary__header">
<h2 class="app-log-summary__title">
<%= govuk_link_to "Log #{log.id}", send("#{log.class.name.underscore}_path", log) %>
<%= govuk_link_to "Log #{log.id}", send("#{log.log_type}_path", log) %>
</h2>
</header>
</div>

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

@ -25,7 +25,7 @@
<% end %>
<% end %>
<% if @log.collection_period_open_for_editing? %>
<% if @log.collection_period_open_for_editing? && question.answer_label(@log, current_user).present? %>
<% row.with_action(
text: question.action_text(@log),
href: action_href(

18
app/views/form/_date_question.html.erb

@ -1,12 +1,14 @@
<%= render partial: "form/guidance/#{question.top_guidance_partial}" if question.top_guidance? %>
<%= render partial: "components/date_picker", locals:
{
resource: @log,
question_id: question.id,
legend: { text: legend(question, page_header, conditional)[:text], size: "l", caption: caption(caption_text, page_header, conditional) },
resource_type: @log.log_type,
hint: (question.hint_text.blank? ? "" : (question.hint_text.html_safe + "</br></br>".html_safe)) + "For example, #{date_mid_collection_year_formatted(@log.startdate).tr(' ', '/')}",
f:,
} %>
<%= f.govuk_date_field question.id.to_sym,
caption: caption(caption_text, page_header, conditional),
legend: legend(question, page_header, conditional),
hint: { text: (question.hint_text.blank? ? "" : (question.hint_text.html_safe + "</br></br>".html_safe)) + "For example, #{date_mid_collection_year_formatted(@log.startdate)}" },
width: 20,
**stimulus_html_attributes(question) do %>
<%= govuk_inset_text(text: question.unresolved_hint_text) if question.unresolved_hint_text.present? && @log.unresolved %>
<% end %>
<%= govuk_inset_text(text: question.unresolved_hint_text) if question.unresolved_hint_text.present? && @log.unresolved %>
<%= render partial: "form/guidance/#{question.bottom_guidance_partial}" if question.bottom_guidance? %>

4
app/views/form/check_errors.html.erb

@ -1,7 +1,7 @@
<div class="govuk-grid-row">
<div class="govuk-grid-column-three-quarters-from-desktop">
<%= form_with model: @log, url: send("#{@log.model_name.param_key}_confirm_clear_answer_path", @log), method: "post", local: true do |f| %>
<%= form_with model: @log, url: send("#{@log.log_type}_confirm_clear_answer_path", @log), method: "post", local: true do |f| %>
<% remove_duplicate_page_errors(@log) %>
<%= f.govuk_error_summary %>
<%= f.hidden_field :page_id, value: @page.id %>
@ -62,6 +62,6 @@
</div>
<% end %>
<%= govuk_button_link_to "Confirm and continue", @original_page_id ? send("#{@log.model_name.param_key}_#{@original_page_id}_path", @log) : send("#{@log.model_name.param_key}_#{@page.id}_path", @log) %>
<%= govuk_button_link_to "Confirm and continue", @original_page_id ? send("#{@log.log_type}_#{@original_page_id}_path", @log) : send("#{@log.log_type}_#{@page.id}_path", @log) %>
</div>
</div>

9
app/views/layouts/rails_admin/_navigation.html.erb

@ -0,0 +1,9 @@
<%= govuk_header(
classes: "app-header app-header--orange",
homepage_url: Rails.application.routes.url_helpers.root_path,
navigation_classes: "govuk-header__navigation--end",
) do |component|
component.with_product_name(name: t("service_name"))
component.with_navigation_item(text: "Your account", href: Rails.application.routes.url_helpers.account_path)
component.with_navigation_item(text: "Sign out", href: Rails.application.routes.url_helpers.destroy_user_session_path)
end %>

70
app/views/layouts/rails_admin/application.html.erb

@ -0,0 +1,70 @@
</html><!DOCTYPE html>
<html lang="en" class="govuk-template">
<head>
<title>Admin</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= tag.meta name: "viewport", content: "width=device-width, initial-scale=1" %>
<%= tag.meta property: "og:image", content: asset_path("images/govuk-opengraph-image.png") %>
<%= tag.meta name: "theme-color", content: "#0b0c0c" %>
<%= favicon_link_tag asset_path("images/favicon.ico"), type: nil, sizes: "48x48" %>
<%= favicon_link_tag asset_path("images/favicon.svg"), type: "image/svg+xml", sizes: "any" %>
<%= favicon_link_tag asset_path("images/govuk-icon-mask.svg"), rel: "mask-icon", color: "#0b0c0c", type: nil %>
<%= favicon_link_tag asset_path("images/govuk-icon-180.png"), rel: "apple-touch-icon", type: nil %>
<%= stylesheet_link_tag "application" %>
<%= javascript_include_tag "vendor/html5shiv.min.js" %>
<script>
window.html5.elements = "output";
html5.shivDocument(document);
</script>
<%= javascript_include_tag "vendor/polyfill-output-value.js" %>
<%= javascript_include_tag "vendor/outerHTML.js" %>
<%= javascript_include_tag "application", defer: true %>
<% if content_for?(:head) %>
<%= yield(:head) %>
<% end %>
<%= capybara_lockstep if defined?(Capybara::Lockstep) %>
<% if Rails.env.development? %>
<script>
console.log(<%= session.to_json.html_safe %>)
</script>
<% end %>
<%= render "layouts/rails_admin/head" %>
</head>
<body class="govuk-template__body app-template--wide">
<script>
document.body.className += " js-enabled" + ("noModule" in HTMLScriptElement.prototype ? " govuk-frontend-supported" : "");
</script>
<div data-i18n-options="<%= I18n.t("admin.js").to_json %>" id="admin-js"></div>
<div class="badge bg-warning" id="loading" style="display:none; position:fixed; right:20px; bottom:20px; z-index:100000">
<%= t("admin.loading") %>
</div>
<%= govuk_skip_link %>
<%= render "layouts/rails_admin/navigation" %>
<% feedback_link = govuk_link_to "giving us your feedback (opens in a new tab)", t("feedback_form"), rel: "noreferrer noopener", target: "_blank" %>
<%= govuk_phase_banner(
classes: "#{current_user.present? ? 'no-bottom-border ' : ''}govuk-width-container",
tag: { colour: "orange", text: "Support beta" },
text: "This is a new service – help us improve it by #{feedback_link}".html_safe,
) %>
<div class="govuk-width-container">
<main class="govuk-main-wrapper govuk-main-wrapper--auto-spacing" id="main-content" role="main">
<%= render template: "layouts/rails_admin/content" %>
</main>
</div>
<%= render partial: "layouts/feedback" %>
<%= render partial: "layouts/footer", locals: {
accessibility_statement_path: Rails.application.routes.url_helpers.accessibility_statement_path,
privacy_notice_path: Rails.application.routes.url_helpers.privacy_notice_path,
cookies_path: Rails.application.routes.url_helpers.cookies_path,
} %>
</body>
</html>

12
app/views/locations/availability.erb

@ -12,11 +12,13 @@
<%= f.govuk_error_summary %>
<%= render partial: "organisations/headings", locals: { main: I18n.t("questions.location.startdate"), sub: "Add a location to #{@scheme.service_name}" } %>
<%= f.govuk_date_field :startdate,
hint: { text: I18n.t("hints.location.startdate") },
legend: nil,
width: 20 %>
<%= render partial: "components/date_picker", locals: {
resource: @location,
question_id: "startdate",
legend: nil,
resource_type: "location",
hint: I18n.t("hints.location.startdate"),
f: } %>
<div class="govuk-button-group">
<% if params[:referrer] == "check_answers" %>

10
app/views/locations/check_answers.html.erb

@ -23,14 +23,16 @@
<% row.with_key { attr[:name] } %>
<% if attr[:attribute].eql?("postcode") && @location.is_la_inferred %>
<% row.with_value do %>
<%= details_html(attr) %>
<%= details_html(attr, @location) %>
<span class="govuk-!-font-weight-regular app-!-colour-muted"><%= formatted_local_authority_timeline(@location) %></span>
<% end %>
<% else %>
<% row.with_value { details_html(attr) } %>
<% row.with_value { details_html(attr, @location) if user_can_edit_scheme?(current_user, @scheme) } %>
<% end %>
<% if LocationPolicy.new(current_user, @location).update? %>
<% row.with_action(text: action_text_helper(attr, @location), href: location_edit_path(@location, attr[:attribute])) %>
<% if LocationPolicy.new(current_user, @location).update? && attr[:value] %>
<% row.with_action(text: location_action_text(attr, @location), href: location_edit_path(@location, attr[:attribute])) %>
<% else %>
<% row.with_action %>
<% end %>
<% end %>
<% end %>

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

@ -27,18 +27,14 @@
<% end %>
<% elsif attr[:attribute].eql?("postcode") && @location.is_la_inferred %>
<% row.with_value do %>
<%= details_html(attr) %>
<%= details_html(attr, @location) %>
<span class="govuk-!-font-weight-regular app-!-colour-muted"><%= formatted_local_authority_timeline(@location) %></span>
<% end %>
<% else %>
<%= row.with_value { details_html(attr) } %>
<%= row.with_value { details_html(attr, @location) } %>
<% end %>
<% if LocationPolicy.new(current_user, @location).update? %>
<% row.with_action(text: "Change", href: scheme_location_postcode_path(@scheme, @location, referrer: "details")) if attr[:attribute] == "postcode" && current_user.support? %>
<% row.with_action(text: "Change", href: scheme_location_name_path(@scheme, @location, referrer: "details")) if attr[:attribute] == "name" %>
<% row.with_action(text: "Change", href: scheme_location_units_path(@scheme, @location, referrer: "details")) if attr[:attribute] == "units" && current_user.support? %>
<% row.with_action(text: "Change", href: scheme_location_type_of_unit_path(@scheme, @location, referrer: "details")) if attr[:attribute] == "type_of_unit" && current_user.support? %>
<% row.with_action(text: "Change", href: scheme_location_mobility_standards_path(@scheme, @location, referrer: "details")) if attr[:attribute] == "mobility_standards" && current_user.support? %>
<%= row.with_action(text: "Change", href: location_action_link(attr, @scheme, @location, current_user)) if location_editable_attributes.include?(attr[:attribute]) %>
<% end %>
<% end %>
<% end %>

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

@ -25,11 +25,16 @@
"other",
label: { text: "For tenancies starting after a certain date" },
**basic_conditional_html_attributes({ "deactivation_date" => %w[other] }, "location") do %>
<%= f.govuk_date_field date_question(action),
legend: { text: "Date", size: "m" },
hint: { text: "For example, 27 3 2022" },
width: 20 %>
<% end %>
<%= render partial: "components/date_picker", locals: {
resource: @location,
question_id: date_question(action),
legend: { text: "Date", size: "m" },
resource_type: "location",
hint: "For example, 27/3/2024",
f:,
} %>
<% end %>
<% end %>
<%= f.govuk_submit "Continue" %>

4
app/views/logs/delete_duplicates.html.erb

@ -19,12 +19,12 @@
<div class="govuk-button-group">
<%= govuk_button_to @duplicate_logs.count == 1 ? "Delete this log" : "Delete these logs",
send("delete_logs_#{@log.class.name.underscore}s_path"),
send("delete_logs_#{@log.log_type}s_path"),
method: "delete",
params: { ids: @duplicate_logs.map(&:id), original_log_id: @original_log.id, remaining_log_id: @log.id, referrer: params[:referrer], organisation_id: params[:organisation_id] } %>
<%= govuk_button_link_to(
"Cancel",
send("#{@log.class.name.underscore}_duplicate_logs_path", @original_log, original_log_id: @original_log.id, referrer: params[:referrer], organisation_id: params[:organisation_id]),
send("#{@log.log_type}_duplicate_logs_path", @original_log, original_log_id: @original_log.id, referrer: params[:referrer], organisation_id: params[:organisation_id]),
secondary: true,
) %>
</div>

14
app/views/merge_requests/merge_date.html.erb

@ -11,11 +11,15 @@
<h2 class="govuk-heading-l">What is the merge date?</h2>
<p class="govuk-hint">
Enter the official merge date. Log and organisation page data will show the new organisation name from this date. <br><br>
For example, <%= date_mid_collection_year_formatted(Time.zone.now) %></p>
<%= f.govuk_date_field :merge_date,
legend: { hidden: true },
width: 20 do %>
<% end %>
For example, <%= date_mid_collection_year_formatted(Time.zone.now).tr(" ", "/") %></p>
<%= render partial: "components/date_picker", locals: {
resource: @merge_request,
question_id: "merge_date",
legend: nil,
resource_type: "merge_request",
hint: "",
f:,
} %>
<%= f.hidden_field :page, value: "merge_date" %>
<div class="govuk-button-group">
<%= f.govuk_submit submit_merge_request_button_text(request.query_parameters["referrer"]) %>

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

@ -22,17 +22,22 @@
<% if can_edit_org?(current_user) && attr[:editable] %>
<%= summary_list.with_row do |row| %>
<% row.with_key { attr[:name] } %>
<% row.with_value { details_html(attr) } %>
<% row.with_action(
visually_hidden_text: attr[:name].to_s.humanize.downcase,
href: edit_organisation_path(@organisation),
html_attributes: { "data-qa": "change-#{attr[:name].downcase}" },
) %>
<% row.with_value { details_html(attr, @organisation) } %>
<% if attr[:value] %>
<% row.with_action(
text: organisation_action_text(attr, @organisation),
visually_hidden_text: attr[:name].to_s.humanize.downcase,
href: edit_organisation_path(@organisation),
html_attributes: { "data-qa": "change-#{attr[:name].downcase}" },
) %>
<% else %>
<% row.with_action %>
<% end %>
<% end %>
<% else %>
<%= summary_list.with_row do |row| %>
<% row.with_key { attr[:name] } %>
<% row.with_value { details_html(attr) } %>
<% row.with_value { details_html(attr, @organisation) } %>
<% row.with_action %>
<% end %>
<% end %>

25
app/views/rails_admin/main/_submit_buttons.html.erb

@ -0,0 +1,25 @@
<div class="form-actions row justify-content-end my-3">
<div class="col-sm-10">
<input name="return_to" type="<%= :hidden %>" value="<%= (params[:return_to].presence || request.referer) %>">
<button class="govuk-button" data-disable-with="<%= t("admin.form.save") %>" name="_save" type="submit"<%= " disabled" unless @action.enabled? %>>
<i class="fas fa-check"></i>
<%= t("admin.form.save") %>
</button>
<span class="extra_buttons">
<% if @action.enabled? && authorized?(:new, @abstract_model) %>
<button class="govuk-button govuk-button--secondary" data-disable-with="<%= t("admin.form.save_and_add_another") %>" name="_add_another" type="submit">
<%= t("admin.form.save_and_add_another") %>
</button>
<% end %>
<% if @action.enabled? && authorized?(:edit, @abstract_model) %>
<button class="govuk-button govuk-button--secondary" data-disable-with="<%= t("admin.form.save_and_edit") %>" name="_add_edit" type="submit"<%= " disabled" unless @action.enabled? %>>
<%= t("admin.form.save_and_edit") %>
</button>
<% end %>
<button class="govuk-button govuk-button--secondary" data-disable-with="<%= t("admin.form.cancel") %>" formnovalidate="<%= true %>" name="_continue" type="submit">
<i class="fas fa-times"></i>
<%= t("admin.form.cancel") %>
</button>
</span>
</div>
</div>

68
app/views/rails_admin/main/dashboard.html.erb

@ -0,0 +1,68 @@
<% if @abstract_models %>
<table class="table table-condensed table-striped table-hover">
<thead>
<tr>
<th class="shrink model-name">
<%= t "admin.table_headers.model_name" %>
</th>
<th class="shrink last-created">
<%= t "admin.table_headers.last_created" %>
</th>
<th class="records">
<%= t "admin.table_headers.records" %>
</th>
<th class="shrink controls"></th>
</tr>
</thead>
<tbody class="table-group-divider">
<% @abstract_models.each do |abstract_model| %>
<% if authorized? :index, abstract_model %>
<% index_path = index_path(model_name: abstract_model.to_param) %>
<% row_class = "#{cycle('odd', 'even')}#{' link' if index_path} #{abstract_model.param_key}_links" %>
<tr class="<%= row_class %>" data-link="<%= index_path %>">
<% last_created = @most_recent_created[abstract_model.model.name] %>
<% active = last_created.try(:today?) %>
<td>
<span class="show">
<%= link_to abstract_model.config.label_plural, index_path %>
</span>
</td>
<td>
<% if last_created %>
<%= t "admin.misc.time_ago", time: time_ago_in_words(last_created), default: "#{time_ago_in_words(last_created)} #{t('admin.misc.ago')}" %>
<% end %>
</td>
<td>
<% count = @count[abstract_model.model.name] %>
<% percent = if count.positive?
@max <= 1 ? count : ((Math.log(count + 1) * 100.0) / Math.log(@max + 1)).to_i
else
-1
end %>
<div class="<%= active ? "active progress-bar-striped " : "" %>progress" style="margin-bottom:0px">
<div class="bg-<%= get_indicator(percent) %> progress-bar animate-width-to" data-animate-length="<%= ([1.0, percent].max.to_i * 20) %>" data-animate-width-to="<%= [2.0, percent].max.to_i %>%" style="width:2%">
<%= @count[abstract_model.model.name] %>
</div>
</div>
</td>
<td class="last links rails-admin-actions">
<ul class="nav list-inline">
<%= menu_for :collection, abstract_model, nil, true %>
</ul>
</td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
<% end %>
<% if @history && authorized?(:history_index) %>
<div class="block" id="block-tables">
<div class="content">
<h2>
<%= t("admin.actions.history_index.menu") %>
</h2>
<%= render partial: "rails_admin/main/dashboard_history" %>
</div>
</div>
<% end %>

21
app/views/rails_admin/main/delete.html.erb

@ -0,0 +1,21 @@
<h4>
<%= t("admin.form.are_you_sure_you_want_to_delete_the_object", model_name: @abstract_model.pretty_name.downcase) %>
<q><strong><%= @model_config.with(object: @object).object_label %></strong></q>
<%= t("admin.form.all_of_the_following_related_items_will_be_deleted") %>
</h4>
<ul>
<%= render partial: "delete_notice", object: @object %>
</ul>
<%= form_for(@object, url: delete_path(model_name: @abstract_model.to_param, id: @object.id), html: { method: "delete" }) do %>
<input name="return_to" type="<%= :hidden %>" value="<%= (params[:return_to].presence || request.referer) %>">
<div class="form-actions">
<button class="govuk-button govuk-button--warning" data-disable-with="<%= t("admin.form.confirmation") %>" type="submit">
<i class="fas fa-check"></i>
<%= t("admin.form.confirmation") %>
</button>
<button class="govuk-button" data-disable-with="<%= t("admin.form.cancel") %>" name="_continue" type="submit">
<i class="fas fa-times"></i>
<%= t("admin.form.cancel") %>
</button>
</div>
<% end %>

187
app/views/rails_admin/main/index.html.erb

@ -0,0 +1,187 @@
<% query = params[:query] %>
<% params = request.params.except(:authenticity_token, :action, :controller, :utf8, :bulk_export) %>
<% params.delete(:query) if params[:query].blank? %>
<% params.delete(:sort_reverse) unless params[:sort_reverse] == "true" %>
<% sort_reverse = params[:sort_reverse] %>
<% sort = params[:sort] %>
<% params.delete(:sort) if params[:sort] == @model_config.list.sort_by.to_s %>
<% export_action = RailsAdmin::Config::Actions.find(:export, { controller:, abstract_model: @abstract_model }) %>
<% export_action = nil unless export_action && authorized?(export_action.authorization_key, @abstract_model) %>
<% description = RailsAdmin.config(@abstract_model.model_name).description %>
<% properties = @model_config.list.with(controller:, view: self, object: @abstract_model.model.new).fields_for_table %>
<% checkboxes = @model_config.list.checkboxes? %>
<% table_table_header_count = begin
count = checkboxes ? 1 : 0
count += properties.count
end %>
<% content_for :contextual_tabs do %>
<% if filterable_fields.present? %>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#">
<%= t("admin.misc.add_filter") %>
<b class="caret"></b>
</a>
<ul class="dropdown-menu dropdown-menu-end" id="filters">
<% filterable_fields.each do |field| %>
<li>
<a
href="#"
class="dropdown-item"
data-options="<%= field.with(view: self).filter_options.to_json %>">
<%= field.label %>
</a>
</li>
<% end %>
</ul>
</li>
<% end %>
<% if checkboxes %>
<%= bulk_menu %>
<% end %>
<% end %>
<style>
<% properties.select { |p| p.column_width.present? }.each do |property| %>
<%= "#list th.#{property.css_class} { width: #{property.column_width}px; min-width: #{property.column_width}px; }" %>
<%= "#list td.#{property.css_class} { max-width: #{property.column_width}px;}" %>
<% end %>
</style>
<div id="list">
<%= form_tag(index_path(params.except(*%w[page f query])), method: :get) do %>
<div class="card mb-3 p-3 bg-light">
<div class="row rails-admin-filters-box" data-options="<%= ordered_filter_options.to_json %>" id="filters_box"></div>
<hr class="filters_box" style="display:<%= ordered_filters.empty? ? "none" : "block" %>">
<div class="row">
<div class="col-sm-8">
<div class="input-group">
<input class="govuk-input govuk-input--width-20" name="query" placeholder="<%= t("admin.misc.filter") %>" type="search" value="<%= query %>" autocomplete="off">
<div class="govuk-!-margin-left-2">
<button class="govuk-button govuk-!-margin-bottom-0" data-disable-with="<%= "<i class=\"fas fa-sync\"></i>#{t('admin.misc.refresh')}" %>" type="submit">
<i class="fas fa-sync"></i>
<%= t("admin.misc.refresh") %>
</button>
</div>
<div id="remove_filter" title="<%= t("admin.misc.reset_filters") %>">
<button class="govuk-button govuk-button--secondary govuk-!-margin-bottom-0">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<% if @model_config.list.search_help.present? %>
<div class="form-text"><%= @model_config.list.search_help %></div>
<% end %>
</div>
<div class="col-sm-4 text-end">
<% if export_action %>
<%= govuk_button_link_to wording_for(:link, export_action), export_path(params.except("page")), class: "govuk-!-margin-bottom-0" %>
<% end %>
</div>
</div>
</div>
<% end %>
<% unless @model_config.list.scopes.empty? %>
<ul class="nav nav-tabs" id="scope_selector">
<% @model_config.list.scopes.each_with_index do |scope, index| %>
<% scope = "_all" if scope.nil? %>
<li class="nav-item">
<a href="<%= index_path(params.merge(scope:, page: nil)) %>" class="nav-link <%= "active" if scope.to_s == params[:scope] || (params[:scope].blank? && index.zero?) %>">
<%= I18n.t("admin.scopes.#{@abstract_model.to_param}.#{scope}", default: I18n.t("admin.scopes.#{scope}", default: scope.to_s.titleize)) %>
</a>
</li>
<% end %>
</ul>
<% end %>
<%= form_tag bulk_action_path(model_name: @abstract_model.to_param), method: :post, id: "bulk_form", class: %w[form mb-3] do %>
<%= hidden_field_tag :bulk_action %>
<% if description.present? %>
<p>
<strong>
<%= description %>
</strong>
</p>
<% end %>
<div id="sidescroll" class="rails-admin-sidescroll">
<table class="table table-condensed table-striped table-hover">
<thead>
<tr>
<% if checkboxes %>
<th class="shrink sticky">
<input class="toggle" type="checkbox">
</th>
<% end %>
<% properties.each do |property| %>
<% selected = (sort == property.name.to_s) %>
<% if property.sortable %>
<% sort_location = index_path params.except("sort_reverse").except("page").merge(sort: property.name).merge(selected && sort_reverse != "true" ? { sort_reverse: "true" } : {}) %>
<% sort_direction = (if selected
sort_reverse == "true" ? "headerSortUp" : "headerSortDown"
end) %>
<% end %>
<th class="<%= [property.sortable && "header", property.sortable && sort_direction, property.sticky? && "sticky", property.css_class, property.type_css_class].select(&:present?).join(" ") %>" data-href="<%= property.sortable && sort_location %>" rel="tooltip" title="<%= property.hint %>">
<%= property.label %>
</th>
<% end %>
<th class="last shrink"></th>
</tr>
</thead>
<tbody class="table-group-divider">
<% @objects.each do |object| %>
<tr class="<%= @abstract_model.param_key %>_row <%= @model_config.list.with(object:).row_css_class %>">
<% if checkboxes %>
<td class="sticky">
<%= check_box_tag "bulk_ids[]", object.id.to_s, false %>
</td>
<% end %>
<% properties.map { |property| property.bind(:object, object) }.each do |property| %>
<% value = property.pretty_value %>
<%= content_tag(:td, class: [property.sticky? && "sticky", property.css_class, property.type_css_class].select(&:present?).map { |x| "rails-admin-#{x}" }, title: strip_tags(value.to_s)) do %>
<%= value %>
<% end %>
<% end %>
<td class="last links ra-sidescroll-frozen rails-admin-actions">
<ul class="nav list-inline">
<%= menu_for :member, @abstract_model, object, true %>
</ul>
</td>
</tr>
<% end %>
<% if @objects.empty? %>
<tr class="empty_row">
<td colspan="<%= table_table_header_count %>">
<%= I18n.t("admin.actions.index.no_records") %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% if @model_config.list.limited_pagination %>
<div class="row">
<div class="col-md-6">
<%= paginate(@objects, theme: "ra-twitter-bootstrap/without_count", total_pages: Float::INFINITY) %>
</div>
</div>
<% elsif @objects.respond_to?(:total_count) %>
<% total_count = @objects.total_count.to_i %>
<div class="row">
<div class="col-md-6">
<%= paginate(@objects, theme: "ra-twitter-bootstrap") %>
</div>
</div>
<div class="row">
<div class="col-md-6">
<%= link_to(t("admin.misc.show_all"), index_path(params.merge(all: true)), class: "govuk-button govuk-button--secondary") unless total_count > 100 || total_count <= @objects.to_a.size %>
</div>
</div>
<div class="clearfix total-count">
<%= "#{total_count} #{@model_config.pluralize(total_count).downcase}" %>
</div>
<% else %>
<div class="clearfix total-count">
<%= "#{@objects.size} #{@model_config.pluralize(@objects.size).downcase}" %>
</div>
<% end %>
<% end %>
</div>

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

@ -7,17 +7,17 @@
<p class="govuk-error-message">
<span class="govuk-error-message govuk-visually-hidden">Error:</span> <%= scheme.errors[attribute[:id].to_sym][0] %>
</p>
<%= details_html(attribute) %>
<%= details_html(attribute, @scheme) %>
</dd>
<% else %>
<dd class="govuk-summary-list__value">
<%= details_html(attribute) %>
<%= details_html(attribute, @scheme) %>
</dd>
<% end %>
<% if can_change_scheme_answer?(attribute[:name], scheme) && attribute[:edit] %>
<% if can_change_scheme_answer?(attribute[:name], scheme) && attribute[:edit] && attribute[:value] %>
<dd class="govuk-summary-list__actions">
<a class="govuk-link" href="<%= change_link %>">Change</a>
<a class="govuk-link" href="<%= change_link %>"><%= change_link_text(attribute[:id], @scheme) %></a>
</dd>
<% end %>
</div>

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

@ -10,7 +10,7 @@
<dl class="govuk-summary-list">
<% @scheme.check_details_attributes.each do |attr| %>
<% if attr[:name] != "Status" && (attr[:id] != "secondary_client_group" || @scheme.has_other_client_group == "Yes") %>
<%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: change_answer_link(@scheme, attr[:id], current_user) } %>
<%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_edit_path(@scheme, attr[:id], current_user) } %>
<% end %>
<% end %>
</dl>

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

@ -38,7 +38,7 @@
</dd>
</div>
<% elsif attr[:id] != "secondary_client_group" || @scheme.has_other_client_group == "Yes" %>
<%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: change_answer_link(@scheme, attr[:id], current_user) } %>
<%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_edit_path(@scheme, attr[:id], current_user) } %>
<% end %>
<% end %>

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

@ -25,10 +25,14 @@
"other",
label: { text: "For tenancies starting after a certain date" },
**basic_conditional_html_attributes({ "deactivation_date" => %w[other] }, "scheme") do %>
<%= f.govuk_date_field date_question(action),
legend: { text: "Date", size: "m" },
hint: { text: "For example, 27 3 2025" },
width: 20 %>
<%= render partial: "components/date_picker", locals: {
resource: @scheme,
question_id: date_question(action),
legend: { text: "Date", size: "m" },
resource_type: "scheme",
hint: "For example, 27/3/2025",
f:,
} %>
<% end %>
<% end %>
<%= f.govuk_submit "Continue" %>

16
app/views/users/show.html.erb

@ -52,9 +52,14 @@
<%= summary_list.with_row do |row|
row.with_key { "Telephone number" }
row.with_value { @user.phone_with_extension }
if UserPolicy.new(current_user, @user).edit_telephone_numbers?
row.with_action(visually_hidden_text: "telephone number", href: aliased_user_edit(@user, current_user), html_attributes: { "data-qa": "change-telephone-number" })
row.with_value { user_details_html(@user, current_user, "phone") }
if UserPolicy.new(current_user, @user).edit_telephone_numbers? && @user.phone.present?
row.with_action(
text: user_action_text(@user, "phone"),
visually_hidden_text: "telephone number",
href: aliased_user_edit(@user, current_user),
html_attributes: { "data-qa": "change-telephone-number" },
)
else
row.with_action
end
@ -90,9 +95,10 @@
<%= summary_list.with_row do |row|
row.with_key { "Role" }
row.with_value { @user.role&.humanize }
if UserPolicy.new(current_user, @user).edit_roles?
row.with_value { user_details_html(@user, current_user, "role") }
if UserPolicy.new(current_user, @user).edit_roles? && @user.role
row.with_action(
text: user_action_text(@user, "role"),
visually_hidden_text: "role",
href: aliased_user_edit(@user, current_user),
html_attributes: { "data-qa": "change-role" },

3
config/initializers/assets.rb

@ -5,3 +5,6 @@ Rails.application.config.assets.version = "1.0"
# Add additional assets to the asset load path.
Rails.application.config.assets.paths << Rails.root.join("node_modules/@fortawesome/fontawesome-free/webfonts")
Rails.application.config.assets.paths << Rails.root.join("/node_modules/@ministryofjustice/frontend/moj/assets")
Rails.application.config.assets.precompile += %w[rails_admin/rails_admin.css rails_admin/rails_admin.js]

5
config/local_authorities_data/2025_local_authorities.csv

@ -0,0 +1,5 @@
code,name,start_year,end_year
E08000016,Barnsley,2021,2025
E08000019,Sheffield,2021,2025
E08000038,Barnsley,2025,
E08000039,Sheffield,2025,
1 code name start_year end_year
2 E08000016 Barnsley 2021 2025
3 E08000019 Sheffield 2021 2025
4 E08000038 Barnsley 2025
5 E08000039 Sheffield 2025

5
config/local_authorities_data/local_authority_links_2025.csv

@ -0,0 +1,5 @@
local_authority_code,linked_local_authority_code
E08000038,E08000016
E08000039,E08000019
E08000016,E08000038
E08000019,E08000039
1 local_authority_code linked_local_authority_code
2 E08000038 E08000016
3 E08000039 E08000019
4 E08000016 E08000038
5 E08000019 E08000039

2
config/locales/en.yml

@ -413,7 +413,7 @@ en:
name: "This is how you refer to this location within your organisation."
units: "A unit is the space being let. For example, the property might be a block of flats and the unit would be the specific flat being let. A unit can also be a bedroom in a shared house or flat. Do not include spaces used for staff."
toggle_active: "If the date is before %{date}, select ‘From the start of the open collection period’ because the previous period has now closed."
startdate: "For example, 27 3 2021."
startdate: "For example, 27/3/2024."
scheme:
toggle_active: "If the date is before %{date}, select ‘From the start of the open collection period’ because the previous period has now closed."
bulk_upload:

71
config/locales/forms/2023/lettings/household_characteristics.en.yml

@ -6,35 +6,41 @@ en:
declaration:
page_header: "Ministry of Housing, Communities and Local Government privacy notice"
check_answer_label: "Tenant has seen the privacy notice"
check_answer_prompt: "Tell us if tenant has seen the privacy notice"
hint_text: ""
question_text: "Declaration"
hhmemb:
page_header: ""
check_answer_label: "Number of household members"
check_answer_prompt: "Enter total number of household members"
hint_text: "You can provide details for a maximum of 8 people."
question_text: "How many people live in the household for this letting?"
age1:
page_header: ""
age1_known:
check_answer_label: ""
check_answer_label: "Lead tenant's age"
check_answer_prompt: "Enter lead tenant's age if known"
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."
question_text: "Do you know the lead tenant’s age?"
age1:
check_answer_label: "Lead tenant’s age"
check_answer_prompt: ""
hint_text: ""
question_text: "Age"
sex1:
page_header: ""
check_answer_label: "Lead tenant’s gender identity"
check_answer_prompt: ""
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."
question_text: "Which of these best describes the lead tenant’s gender identity?"
ethnic_group:
page_header: ""
check_answer_label: "Lead tenant’s ethnic group"
check_answer_prompt: ""
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."
question_text: "What is the lead tenant’s ethnic group?"
@ -42,282 +48,331 @@ en:
ethnic_background_black:
page_header: ""
check_answer_label: "Lead tenant’s ethnic background"
check_answer_prompt: ""
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."
question_text: "Which of the following best describes lead tenant’s Black, African, Caribbean or Black British background?"
ethnic_background_asian:
page_header: ""
check_answer_label: "Lead tenant’s ethnic background"
check_answer_prompt: ""
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."
question_text: "Which of the following best describes lead tenant’s Asian or Asian British background?"
ethnic_background_arab:
page_header: ""
check_answer_label: "Lead tenant’s ethnic background"
check_answer_prompt: ""
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."
question_text: "Which of the following best describes the lead tenant’s Arab background?"
ethnic_background_mixed:
page_header: ""
check_answer_label: "Lead tenant’s ethnic background"
check_answer_prompt: ""
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."
question_text: "Which of the following best describes lead tenant’s Mixed or Multiple ethnic groups background?"
ethnic_background_white:
page_header: ""
check_answer_label: "Lead tenant’s ethnic background"
check_answer_prompt: ""
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."
question_text: "Which of the following best describes lead tenant’s White background?"
national:
page_header: ""
check_answer_label: "Lead tenant’s nationality"
check_answer_prompt: ""
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."
question_text: "What is the nationality of the lead tenant?"
ecstat1:
page_header: ""
check_answer_label: "Lead tenant’s working situation"
check_answer_prompt: ""
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."
question_text: "Which of these best describes the lead tenant’s working situation?"
details_known_2:
page_header: "You’ve given us the details for 1 person in the household"
check_answer_label: "Details known for person 2"
check_answer_prompt: "Tell us if you know person 2's details"
hint_text: "You must provide details for everyone in the household if you know them."
question_text: "Do you know details for person 2?"
relat2:
page_header: ""
check_answer_label: "Person 2’s relationship to the lead tenant"
check_answer_prompt: ""
hint_text: ""
question_text: "What is person 2’s relationship to the lead tenant?"
age2:
page_header: ""
age2_known:
check_answer_label: ""
check_answer_label: "Person 2’s age"
check_answer_prompt: "Enter person 2's age if known"
hint_text: ""
question_text: "Do you know person 2’s age?"
age2:
check_answer_label: "Person 2’s age"
check_answer_prompt: ""
hint_text: ""
question_text: "Age"
sex2:
page_header: ""
check_answer_label: "Person 2’s gender identity"
check_answer_prompt: ""
hint_text: ""
question_text: "Which of these best describes person 2’s gender identity?"
ecstat2:
page_header: ""
check_answer_label: "Person 2’s working situation"
check_answer_prompt: ""
hint_text: ""
question_text: "Which of these best describes person 2’s working situation?"
details_known_3:
page_header: "You’ve given us the details for 2 people in the household"
check_answer_label: "Details known for person 3"
check_answer_prompt: "Tell us if you know person 3's details"
hint_text: "You must provide details for everyone in the household if you know them."
question_text: "Do you know details for person 3?"
relat3:
page_header: ""
check_answer_label: "Person 3’s relationship to the lead tenant"
check_answer_prompt: ""
hint_text: ""
question_text: "What is person 3’s relationship to the lead tenant?"
age3:
page_header: ""
age3_known:
check_answer_label: ""
check_answer_label: "Person 3’s age"
check_answer_prompt: "Enter person 3's age if known"
hint_text: ""
question_text: "Do you know person 3’s age?"
age3:
check_answer_label: "Person 3’s age"
check_answer_prompt: ""
hint_text: ""
question_text: "Age"
sex3:
page_header: ""
check_answer_label: "Person 3’s gender identity"
check_answer_prompt: ""
hint_text: ""
question_text: "Which of these best describes person 3’s gender identity?"
ecstat3:
page_header: ""
check_answer_label: "Person 3’s working situation"
check_answer_prompt: ""
hint_text: ""
question_text: "Which of these best describes person 3’s working situation?"
details_known_4:
page_header: "You’ve given us the details for 3 people in the household"
check_answer_label: "Details known for person 4"
check_answer_prompt: "Tell us if you know person 4's details"
hint_text: "You must provide details for everyone in the household if you know them."
question_text: "Do you know details for person 4?"
relat4:
page_header: ""
check_answer_label: "Person 4’s relationship to the lead tenant"
check_answer_prompt: ""
hint_text: ""
question_text: "What is person 4’s relationship to the lead tenant?"
age4:
page_header: ""
age4_known:
check_answer_label: ""
check_answer_label: "Person 4’s age"
check_answer_prompt: "Enter person 4's age if known"
hint_text: ""
question_text: "Do you know person 4’s age?"
age4:
check_answer_label: "Person 4’s age"
check_answer_prompt: ""
hint_text: ""
question_text: "Age"
sex4:
page_header: ""
check_answer_label: "Person 4’s gender identity"
check_answer_prompt: ""
hint_text: ""
question_text: "Which of these best describes person 4’s gender identity?"
ecstat4:
page_header: ""
check_answer_label: "Person 4’s working situation"
check_answer_prompt: ""
hint_text: ""
question_text: "Which of these best describes person 4’s working situation?"
details_known_5:
page_header: "You’ve given us the details for 4 people in the household"
check_answer_label: "Details known for person 5"
check_answer_prompt: "Tell us if you know person 5's details"
hint_text: "You must provide details for everyone in the household if you know them."
question_text: "Do you know details for person 5?"
relat5:
page_header: ""
check_answer_label: "Person 5’s relationship to the lead tenant"
check_answer_prompt: ""
hint_text: ""
question_text: "What is person 5’s relationship to the lead tenant?"
age5:
page_header: ""
age5_known:
check_answer_label: ""
check_answer_label: "Person 5’s age"
check_answer_prompt: "Enter person 5's age if known"
hint_text: ""
question_text: "Do you know person 5’s age?"
age5:
check_answer_label: "Person 5’s age"
check_answer_prompt: ""
hint_text: ""
question_text: "Age"
sex5:
page_header: ""
check_answer_label: "Person 5’s gender identity"
check_answer_prompt: ""
hint_text: ""
question_text: "Which of these best describes person 5’s gender identity?"
ecstat5:
page_header: ""
check_answer_label: "Person 5’s working situation"
check_answer_prompt: ""
hint_text: ""
question_text: "Which of these best describes person 5’s working situation?"
details_known_6:
page_header: "You’ve given us the details for 5 people in the household"
check_answer_label: "Details known for person 6"
check_answer_prompt: "Tell us if you know person 6's details"
hint_text: "You must provide details for everyone in the household if you know them."
question_text: "Do you know details for person 6?"
relat6:
page_header: ""
check_answer_label: "Person 6’s relationship to the lead tenant"
check_answer_prompt: ""
hint_text: ""
question_text: "What is person 6’s relationship to the lead tenant?"
age6:
page_header: ""
age6_known:
check_answer_label: ""
check_answer_label: "Person 6’s age"
check_answer_prompt: "Enter person 6's age if known"
hint_text: ""
question_text: "Do you know person 6’s age?"
age6:
check_answer_label: "Person 6’s age"
check_answer_prompt: ""
hint_text: ""
question_text: "Age"
sex6:
page_header: ""
check_answer_label: "Person 6’s gender identity"
check_answer_prompt: ""
hint_text: ""
question_text: "Which of these best describes person 6’s gender identity?"
ecstat6:
page_header: ""
check_answer_label: "Person 6’s working situation"
check_answer_prompt: ""
hint_text: ""
question_text: "Which of these best describes person 6’s working situation?"
details_known_7:
page_header: "You’ve given us the details for 6 people in the household"
check_answer_label: "Details known for person 7"
check_answer_prompt: "Tell us if you know person 7's details"
hint_text: "You must provide details for everyone in the household if you know them."
question_text: "Do you know details for person 7?"
relat7:
page_header: ""
check_answer_label: "Person 7’s relationship to the lead tenant"
check_answer_prompt: ""
hint_text: ""
question_text: "What is person 7’s relationship to the lead tenant?"
age7:
page_header: ""
age7_known:
check_answer_label: ""
check_answer_label: "Person 7’s age"
check_answer_prompt: "Enter person 7's age if known"
hint_text: ""
question_text: "Do you know person 7’s age?"
age7:
check_answer_label: "Person 7’s age"
check_answer_prompt: ""
hint_text: ""
question_text: "Age"
sex7:
page_header: ""
check_answer_label: "Person 7’s gender identity"
check_answer_prompt: ""
hint_text: ""
question_text: "Which of these best describes person 7’s gender identity?"
ecstat7:
page_header: ""
check_answer_label: "Person 7’s working situation"
check_answer_prompt: ""
hint_text: ""
question_text: "Which of these best describes person 7’s working situation?"
details_known_8:
page_header: "You’ve given us the details for 7 people in the household"
check_answer_label: "Details known for person 8"
check_answer_prompt: "Tell us if you know person 8's details"
hint_text: "You must provide details for everyone in the household if you know them."
question_text: "Do you know details for person 8?"
relat8:
page_header: ""
check_answer_label: "Person 8’s relationship to the lead tenant"
check_answer_prompt: ""
hint_text: ""
question_text: "What is person 8’s relationship to the lead tenant?"
age8:
page_header: ""
age8_known:
check_answer_label: ""
check_answer_label: "Person 8’s age"
check_answer_prompt: "Enter person 8's age if known"
hint_text: ""
question_text: "Do you know person 8’s age?"
age8:
check_answer_label: "Person 8’s age"
check_answer_prompt: ""
hint_text: ""
question_text: "Age"
sex8:
page_header: ""
check_answer_label: "Person 8’s gender identity"
check_answer_prompt: ""
hint_text: ""
question_text: "Which of these best describes person 8’s gender identity?"
ecstat8:
page_header: ""
check_answer_label: "Person 8’s working situation"
check_answer_prompt: ""
hint_text: ""
question_text: "Which of these best describes person 8’s working situation?"

12
config/locales/forms/2023/lettings/household_needs.en.yml

@ -6,30 +6,35 @@ en:
armedforces:
page_header: ""
check_answer_label: "Household links to UK armed forces"
check_answer_prompt: "Tell us if there are any household links to UK armed forces"
hint_text: "This excludes national service.<br><br>If there are several people in the household with links to the UK armed forces, you should answer for the regular. If there’s no regular, answer for the reserve. If there’s no reserve, answer for the spouse or civil partner."
question_text: "Does anybody in the household have any links to the UK armed forces?"
leftreg:
page_header: ""
check_answer_label: "Person still serving in UK armed forces"
check_answer_prompt: "Tell us if the person is still serving in UK armed forces"
hint_text: ""
question_text: "Is the person still serving in the UK armed forces?"
reservist:
page_header: ""
check_answer_label: "Person seriously injured or ill as result of serving in UK armed forces"
check_answer_prompt: "Tell us if the person is seriously injured or ill as result of serving in UK armed forces"
hint_text: ""
question_text: "Was the person seriously injured or ill as a result of serving in the UK armed forces?"
preg_occ:
page_header: ""
check_answer_label: "Anybody in household pregnant"
check_answer_prompt: "Tell us if anybody in the household is pregnant"
hint_text: ""
question_text: "Is anybody in the household pregnant?"
housingneeds:
page_header: ""
check_answer_label: "Anybody with disabled access needs"
check_answer_prompt: "Tell us if there is anybody with disabled access needs"
hint_text: ""
question_text: "Does anybody in the household have any disabled access needs?"
@ -37,22 +42,25 @@ en:
page_header: "Disabled access needs"
housingneeds_type:
check_answer_label: "Disabled access needs"
check_answer_prompt: ""
hint_text: ""
question_text: "What type of access needs do they have?"
housingneeds_other:
check_answer_label: "Other disabled access needs"
check_answer_prompt: "Tell us if they have any other disabled access needs"
hint_text: ""
question_text: "Do they have any other disabled access needs?"
illness:
page_header: ""
check_answer_label: "Anybody in household with physical or mental health condition"
check_answer_prompt: "Tell us if anybody in the household has a physical or mental health condition"
hint_text: ""
question_text: "Does anybody in the household have a physical or mental health condition (or other illness) expected to last 12 months or more?"
condition_effects:
page_header: ""
check_answer_label: "How is person affected by condition or illness"
check_answer_prompt: "Select how the person is affected by their condition or illness"
hint_text: "Select all that apply."
question_text: "How is the person affected by their condition or illness?"

34
config/locales/forms/2023/lettings/household_situation.en.yml

@ -6,12 +6,14 @@ en:
layear:
page_header: ""
check_answer_label: "Length of time in local authority area"
check_answer_prompt: ""
hint_text: ""
question_text: "How long has the household continuously lived in the local authority area of the new letting?"
waityear:
page_header: ""
check_answer_label: "Length of time on local authority waiting list"
check_answer_prompt: ""
hint_text: ""
question_text: "How long has the household been on the local authority waiting list for the new letting?"
@ -20,78 +22,92 @@ en:
page_header: ""
reason:
check_answer_label: "Reason for leaving last settled home"
check_answer_prompt: ""
hint_text: "You told us this letting is a renewal. We have removed some options because of this."
question_text: "What is the tenant’s main reason for the household leaving their last settled home?"
reasonother:
check_answer_label: "Length of time on local authority waiting list"
check_answer_label: ""
check_answer_prompt: ""
hint_text: ""
question_text: "How long has the household been on the local authority waiting list for the new letting?"
question_text: "What is the reason?"
reason_for_leaving_last_settled_home:
page_header: ""
reason:
check_answer_label: "Reason for leaving last settled home"
check_answer_prompt: ""
hint_text: "The tenant’s ‘last settled home’ is their last long-standing home. For tenants who were in temporary accommodation or sleeping rough, their last settled home is where they were living previously."
question_text: "What is the tenant’s main reason for the household leaving their last settled home?"
reasonother:
check_answer_label: ""
check_answer_prompt: ""
hint_text: ""
question_text: "What is the reason?"
prevten:
renewal:
page_header: ""
check_answer_label: "Where was the household immediately before this letting?"
check_answer_label: "Where household was immediately before this letting"
check_answer_prompt: ""
hint_text: "You told us this letting is a renewal. We have removed some options because of this.<br><br>This is where the household was the night before they moved into this new let."
question_text: "Where was the household immediately before this letting?"
not_renewal:
page_header: ""
check_answer_label: "Where was the household immediately before this letting?"
check_answer_label: "Where household was immediately before this letting"
check_answer_prompt: ""
hint_text: "This is where the household was the night before they moved into this new let."
question_text: "Where was the household immediately before this letting?"
homeless:
page_header: ""
check_answer_label: "Household homeless immediately before letting"
check_answer_prompt: "Tell us if household homeless immediately before letting"
hint_text: ""
question_text: "Did the household experience homelessness immediately before this letting?"
previous_postcode:
page_header: ""
ppcodenk:
check_answer_label: ""
check_answer_label: "Postcode of the household’s last settled accommodation"
check_answer_prompt: "Enter the postcode of the household’s last settled accommodation if known"
hint_text: "This is also known as the household’s ‘last settled home’."
question_text: "Do you know the postcode of the household’s last settled accommodation?"
ppostcode_full:
check_answer_label: "Postcode of household’s last settled accommodation"
check_answer_prompt: ""
hint_text: ""
question_text: "Postcode for the previous accommodation"
previous_local_authority:
page_header: ""
previous_la_known:
check_answer_label: ""
check_answer_label: "Local authority of household's last settled accommodation"
check_answer_prompt: "Enter the local authority of the buyer's last settled accommodation if known"
hint_text: "This is also known as the household’s ‘last settled home’."
question_text: "Do you know the local authority of the household’s last settled accommodation?"
prevloc:
check_answer_label: "Location of household’s last settled accommodation"
check_answer_prompt: ""
hint_text: "Select ‘Northern Ireland’, ‘Scotland’, ‘Wales’ or ‘Outside the UK’ if the household’s last settled home was outside England."
question_text: "Select a local authority"
reasonpref:
page_header: ""
check_answer_label: "Household given reasonable preference"
check_answer_prompt: "Tell us if the household was given reasonable preference"
hint_text: "Households may be given ‘reasonable preference’ for social housing, also known as ‘priority need’, by the local authority."
question_text: "Was the household given ‘reasonable preference’ by the local authority?"
reasonable_preference_reason:
page_header: ""
check_answer_label: "Reason for reasonable preference"
check_answer_prompt: ""
hint_text: "Select all that apply."
question_text: "Why was the household given ‘reasonable preference’?"
letting_allocation:
page_header: ""
check_answer_label: "Allocation system"
check_answer_prompt: ""
hint_text: "Select all that apply."
question_text: "How was this letting allocated?"
@ -100,21 +116,25 @@ en:
prp:
page_header: ""
check_answer_label: "Source of referral for letting"
check_answer_prompt: ""
hint_text: ""
question_text: "What was the source of referral for this letting?"
la:
page_header: ""
check_answer_label: "Source of referral for letting"
check_answer_prompt: ""
hint_text: "You told us that you are a local authority. We have removed some options because of this."
question_text: "What was the source of referral for this letting?"
general_needs:
prp:
page_header: ""
check_answer_label: "Source of referral for letting"
check_answer_prompt: ""
hint_text: "You told us that the needs type is general needs. We have removed some options because of this."
question_text: "What was the source of referral for this letting?"
la:
page_header: ""
check_answer_label: "Source of referral for letting"
check_answer_prompt: ""
hint_text: "You told us that you are a local authority and that the needs type is general needs. We have removed some options because of this."
question_text: "What was the source of referral for this letting?"
question_text: "What was the source of referral for this letting?"

32
config/locales/forms/2023/lettings/income_and_benefits.en.yml

@ -5,42 +5,49 @@ en:
income_and_benefits:
net_income_known:
page_header: "Household’s combined income after tax"
check_answer_label: "Do you know the household’s combined total income after tax?"
check_answer_label: "Household’s combined total income after tax"
check_answer_prompt: "Tell us if you know the household’s combined total income after tax"
hint_text: ""
question_text: "Do you know the household’s combined income after tax?"
income_amount:
page_header: "Total household income"
incfreq:
check_answer_label: "How often does the household receive this amount?"
check_answer_label: "How often the household receives this amount"
check_answer_prompt: "Tell us how often does the household receives this amount"
hint_text: ""
question_text: "How often does the household receive this amount?"
earnings:
check_answer_label: "Total household income"
check_answer_prompt: ""
hint_text: ""
question_text: "How much income does the household have in total?"
hb:
page_header: ""
check_answer_label: "Housing-related benefits received"
check_answer_label: "Housing related benefits received"
check_answer_prompt: "Tell us if household receives housing related benefits"
hint_text: ""
question_text: "Is the household likely to be receiving any of these housing-related benefits?"
question_text: "Is the household likely to be receiving any of these housing related benefits?"
benefits:
page_header: ""
check_answer_label: "Household income from Universal Credit, state pension or benefit"
check_answer_prompt: "Tell us if household income is from Universal Credit, state pension or benefit"
hint_text: "This excludes child and housing benefit, council tax support and tax credits."
question_text: "How much of the household’s income is from Universal Credit, state pensions or benefits?"
household_charge:
page_header: ""
check_answer_label: "Does the household pay rent or charges"
check_answer_prompt: "Tell us if the household pay rent or charges"
hint_text: "If rent is charged on the property then answer Yes to this question, even if the tenants do not pay it themselves."
question_text: "Does the household pay rent or other charges for the accommodation?"
period:
page_header: ""
check_answer_label: "Frequency of household rent and charges"
check_answer_prompt: ""
hint_text: "Select how often the household is charged. This may be different to how often they pay."
question_text: "How often does the household pay rent and other charges?"
@ -48,22 +55,27 @@ en:
page_header: ""
is_carehome:
check_answer_label: "Care home accommodation"
check_answer_prompt: "Tell us if care home accommodation"
hint_text: ""
question_text: "Is this accommodation a care home?"
chcharge_weekly:
check_answer_label: "Care home charges"
check_answer_prompt: ""
hint_text: ""
question_text: "How much does the household pay every week?"
chcharge_bi_weekly:
check_answer_label: "Care home charges"
check_answer_prompt: ""
hint_text: ""
question_text: "How much does the household pay every 2 weeks?"
chcharge_4_weekly:
check_answer_label: "Care home charges"
check_answer_prompt: ""
hint_text: ""
question_text: "How much does the household pay every 4 weeks?"
chcharge_monthly:
check_answer_label: "Care home charges"
check_answer_prompt: ""
hint_text: ""
question_text: "How much does the household pay every month?"
@ -71,38 +83,46 @@ en:
page_header: "Household rent and charges"
brent:
check_answer_label: "Basic rent"
check_answer_prompt: ""
hint_text: "This is the amount paid before any charges are added for services (for example, hot water or cleaning). Households may receive housing benefit or Universal Credit towards basic rent."
question_text: "What is the basic rent?"
scharge:
check_answer_label: "Service charge"
check_answer_prompt: ""
hint_text: "For example, for cleaning. Households may receive housing benefit or Universal Credit towards the service charge."
question_text: "What is the service charge?"
pscharge:
check_answer_label: "Personal service charge"
check_answer_prompt: ""
hint_text: "For example, for heating or hot water. This doesn’t include housing benefit or Universal Credit."
question_text: "What is the personal service charge?"
supcharg:
check_answer_label: "Support charge"
check_answer_prompt: ""
hint_text: "Any charges made to fund support services included in tenancy agreement."
question_text: "What is the support charge?"
tcharge:
check_answer_label: "Household rent and charges"
check_answer_prompt: ""
hint_text: "This is the total for rent and all charges."
question_text: "Total charge"
hbrentshortfall:
page_header: ""
check_answer_label: "Any outstanding amount for basic rent and charges"
check_answer_prompt: "Tell us if any outstanding amount for basic rent and charges"
hint_text: "Also known as the ‘outstanding amount’."
question_text: "After the household has received any housing-related benefits, will they still need to pay for rent and charges?"
question_text: "After the household has received any housing related benefits, will they still need to pay for rent and charges?"
outstanding_amount:
page_header: ""
tshortfall_known:
check_answer_label: "Do you know the outstanding amount?"
check_answer_label: "Outstanding amount known"
check_answer_prompt: "Tell us if you know the outstanding amount"
hint_text: "You only need to give an approximate figure."
question_text: "Can you estimate the outstanding amount?"
tshortfall:
check_answer_label: "Estimated outstanding amount"
check_answer_prompt: ""
hint_text: "Also known as the ‘outstanding amount’."
question_text: "Estimated outstanding amount"

22
config/locales/forms/2023/lettings/property_information.en.yml

@ -6,17 +6,20 @@ en:
uprn:
page_header: ""
uprn_known:
check_answer_label: "UPRN known?"
check_answer_label: "UPRN known"
check_answer_prompt: "Enter UPRN if known"
hint_text: "The Unique Property Reference Number (UPRN) is a unique number system created by Ordnance Survey and used by housing providers and various industries across the UK. An example UPRN is 10010457355.<br><br>The UPRN may not be the same as the property reference assigned by your organisation.<br><br>If you don’t know the UPRN you can enter the address of the property instead on the next screen."
question_text: "Do you know the property's UPRN?"
uprn:
check_answer_label: "UPRN"
check_answer_prompt: ""
hint_text: ""
question_text: "What is the property's UPRN?"
uprn_confirmed:
page_header: "We found an address that might be this property"
check_answer_label: "Is this the right address?"
check_answer_prompt: "Tell us if this is the right address"
hint_text: ""
question_text: "Is this the property address?"
@ -24,81 +27,96 @@ en:
page_header: "Q12 - What is the property's address?"
address_line1:
check_answer_label: "Address lines 1 and 2"
check_answer_prompt: "Enter address lines 1 and 2"
hint_text: ""
question_text: "Address line 1"
address_line2:
check_answer_label: ""
check_answer_prompt: ""
hint_text: ""
question_text: "Address line 2 (optional)"
town_or_city:
check_answer_label: "Town or city"
check_answer_prompt: ""
hint_text: ""
question_text: "Town or city"
county:
check_answer_label: "County"
check_answer_prompt: ""
hint_text: ""
question_text: "County (optional)"
postcode_full:
check_answer_label: "Postcode"
check_answer_prompt: ""
hint_text: ""
question_text: "Postcode"
la:
page_header: ""
check_answer_label: "Local authority"
check_answer_prompt: ""
hint_text: ""
question_text: "What is the property’s local authority?"
first_time_property_let_as_social_housing:
page_header: ""
check_answer_label: "First time being let as social-housing?"
check_answer_label: "First time being let as social housing"
check_answer_prompt: "Tell us if it's the first time being let as social housing"
hint_text: ""
question_text: "Is this the first time the property has been let as social housing?"
unitletas:
page_header: ""
check_answer_label: "Most recent let type"
check_answer_prompt: ""
hint_text: ""
question_text: "What type was the property most recently let as?"
rsnvac:
page_header: ""
check_answer_label: "Vacancy reason"
check_answer_prompt: ""
hint_text: ""
question_text: "What is the reason for the property being vacant?"
offered:
page_header: ""
check_answer_label: "Times previously offered since becoming available."
check_answer_prompt: ""
hint_text: "Do not include the offer that led to this letting. This is after the last tenancy ended. If the property is being offered for let for the first time, enter 0."
question_text: "How many times was the property offered between becoming vacant and this letting?"
unittype_gn:
page_header: ""
check_answer_label: "Type of unit"
check_answer_prompt: ""
hint_text: ""
question_text: "What type of unit is the property?"
builtype:
page_header: ""
check_answer_label: "Type of building"
check_answer_prompt: ""
hint_text: ""
question_text: "What type of building is the property?"
wchair:
page_header: ""
check_answer_label: "Property built or adapted to wheelchair-user standards"
check_answer_prompt: "Tell us if property is built or adapted to wheelchair-user standards"
hint_text: ""
question_text: "Is the property built or adapted to wheelchair-user standards?"
beds:
page_header: ""
check_answer_label: "Number of bedrooms"
check_answer_prompt: ""
hint_text: "If shared accommodation, enter the number of bedrooms occupied by this household. A bedsit has 1 bedroom."
question_text: "How many bedrooms does the property have?"
voiddate:
page_header: "Void date"
check_answer_label: "Void date"
check_answer_prompt: ""
hint_text: ""
question_text: "What is the void date?"

13
config/locales/forms/2023/lettings/setup.en.yml

@ -6,30 +6,35 @@ en:
owning_organisation_id:
page_header: ""
check_answer_label: "Stock owner"
check_answer_prompt: ""
hint_text: ""
question_text: "Which organisation owns this property?"
managing_organisation_id:
page_header: ""
check_answer_label: "Managing agent"
check_answer_prompt: ""
hint_text: ""
question_text: "Which organisation manages this letting?"
assigned_to_id:
page_header: ""
check_answer_label: "Log owner"
check_answer_prompt: "Select user"
hint_text: ""
question_text: "Which user are you creating this log for?"
needstype:
page_header: ""
check_answer_label: "Needs type"
check_answer_prompt: ""
hint_text: "General needs housing includes both self-contained and shared housing without support or specific adaptations. Supported housing can include direct access hostels, group homes, residential care and nursing homes."
question_text: "What is the needs type?"
scheme_id:
page_header: "Scheme"
check_answer_label: "Scheme name"
check_answer_prompt: ""
hint_text: "Enter postcode or scheme name"
question_text: "What scheme is this log for?"
@ -37,23 +42,27 @@ en:
less_than_twenty:
page_header: "Location"
check_answer_label: "Location"
check_answer_prompt: ""
hint_text: ""
question_text: "Which location is this letting for?"
twenty_or_more:
page_header: "Location"
check_answer_label: "Location"
check_answer_prompt: ""
hint_text: '<div class="govuk-inset-text">This scheme has 20 or more locations.</div>Enter postcode or address.'
question_text: "Which location is this letting for?"
renewal:
page_header: ""
check_answer_label: "Property renewal"
check_answer_prompt: "Tell us if it's a property renewal"
hint_text: "A renewal is a letting to the same tenant in the same property. If the property was previously being used as temporary accommodation, then answer 'no'."
question_text: "Is this letting a renewal?"
startdate:
page_header: ""
check_answer_label: "Tenancy start date"
check_answer_prompt: ""
hint_text: ""
question_text: "What is the tenancy start date?"
@ -61,21 +70,25 @@ en:
page_header: "Rent Type"
rent_type:
check_answer_label: "Rent type"
check_answer_prompt: ""
hint_text: ""
question_text: "What is the rent type?"
irproduct_other:
check_answer_label: "Product name"
check_answer_prompt: ""
hint_text: ""
question_text: "Name of rent product"
tenancycode:
page_header: ""
check_answer_label: "Tenant code"
check_answer_prompt: ""
hint_text: "This is how you usually refer to this tenancy on your own systems."
question_text: "What is the tenant code?"
propcode:
page_header: ""
check_answer_label: "Property reference"
check_answer_prompt: ""
hint_text: "This is how you usually refer to this property on your own systems."
question_text: "What is the property reference?"

184
config/locales/forms/2023/lettings/soft_validations.en.yml

@ -7,6 +7,7 @@ en:
no_females_pregnant_household_value_check:
page_header: ""
check_answer_label: "Pregnancy confirmation"
check_answer_prompt: "Confirm pregnancy answer"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us somebody in the household is pregnant."
@ -14,119 +15,134 @@ en:
females_in_soft_age_range_in_pregnant_household_value_check:
page_header: ""
check_answer_label: "Pregnancy confirmation"
check_answer_prompt: "Confirm pregnancy status"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us somebody in the household is pregnant."
informative_text: "You also told us that any female tenants living at the property are in the following age ranges:<ul class=\"govuk-body-l app-panel--interruption\"><li>under 16 years old</li><li>over 50 years old</li></ul>"
no_retirement_value_check:
page_header: ""
check_answer_label: "Retirement confirmation"
hint_text: ""
question_text: "Are you sure this person is retired?"
title_text: "You told us this person is aged %{age} years and retired."
informative_text: "The minimum expected retirement age in England is 66."
page_header: ""
check_answer_label: "Retirement confirmation"
check_answer_prompt: "Confirm retirement status"
hint_text: ""
question_text: "Are you sure this person is retired?"
title_text: "You told us this person is aged %{age} years and retired."
informative_text: "The minimum expected retirement age in England is 66."
retirement_value_check:
page_header: ""
check_answer_label: "Retirement confirmation"
hint_text: ""
question_text: "Are you sure this person isn’t retired?"
title_text: "You told us this person is over 66 and not retired."
informative_text: ""
page_header: ""
check_answer_label: "Retirement confirmation"
check_answer_prompt: "Confirm retirement status"
hint_text: ""
question_text: "Are you sure this person isn’t retired?"
title_text: "You told us this person is over 66 and not retired."
informative_text: ""
partner_under_16_value_check:
page_header: ""
check_answer_label: "Partner under 16 confirmation"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us this person is aged %{age} years and has 'Partner' relationship to the lead tenant."
informative_text: "Are you sure this is correct?"
page_header: ""
check_answer_label: "Partner under 16 confirmation"
check_answer_prompt: "Confirm partner's age"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us this person is aged %{age} years and has 'Partner' relationship to the lead tenant."
informative_text: "Are you sure this is correct?"
multiple_partners_value_check:
page_header: ""
check_answer_label: "Multiple partners confirmation"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us there are more than 1 persons with 'Partner' relationship to the lead tenant."
informative_text: "Are you sure this is correct?"
page_header: ""
check_answer_label: "Multiple partners confirmation"
check_answer_prompt: "Confirm multiple partners"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us there are more than 1 persons with 'Partner' relationship to the lead tenant."
informative_text: "Are you sure this is correct?"
reasonother_value_check:
page_header: ""
check_answer_label: "Reason other confirmation"
hint_text: ""
question_text: "Are you sure this doesn’t fit an existing category?"
title_text: "You told us that the tenant’s main reason for leaving their last settled home was %{reasonother}."
informative_text: "The reason you have entered looks very similar to one of the existing response categories. Please check the categories and select the appropriate one. If the existing categories are not suitable, please confirm here to move onto the next question."
page_header: ""
check_answer_label: "Reason other confirmation"
check_answer_prompt: "Confirm reason for leaving"
hint_text: ""
question_text: "Are you sure this doesn’t fit an existing category?"
title_text: "You told us that the tenant’s main reason for leaving their last settled home was %{reasonother}."
informative_text: "The reason you have entered looks very similar to one of the existing response categories. Please check the categories and select the appropriate one. If the existing categories are not suitable, please confirm here to move onto the next question."
referral_value_check:
page_header: ""
check_answer_label: "Referral confirmation"
hint_text: ""
question_text: "Are you sure?"
title_text: "Are you sure?"
informative_text: "This is a general needs log, and this referral type is for supported housing."
page_header: ""
check_answer_label: "Referral confirmation"
check_answer_prompt: "Confirm referral type"
hint_text: ""
question_text: "Are you sure?"
title_text: "Are you sure?"
informative_text: "This is a general needs log, and this referral type is for supported housing."
net_income_value_check:
page_header: ""
check_answer_label: "Net income confirmation"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that the household’s income is %{earnings} %{incfreq}."
informative_text: "This is %{net_income_higher_or_lower_text} than we would expect for the household’s working situation."
page_header: ""
check_answer_label: "Net income confirmation"
check_answer_prompt: "Confirm net income"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that the household’s income is %{earnings} %{incfreq}."
informative_text: "This is %{net_income_higher_or_lower_text} than we would expect for the household’s working situation."
care_home_charges_value_check:
page_header: ""
check_answer_label: "Care home charges confirmation"
hint_text: ""
question_text: "Are you sure there are no care home charges?"
title_text: "Care home charges should be provided if this is a care home accommodation."
informative_text: "Are you sure there are no care home charges?"
page_header: ""
check_answer_label: "Care home charges confirmation"
check_answer_prompt: "Confirm care home charges"
hint_text: ""
question_text: "Are you sure there are no care home charges?"
title_text: "Care home charges should be provided if this is a care home accommodation."
informative_text: "Are you sure there are no care home charges?"
rent_value_check:
page_header: ""
check_answer_label: "Total rent confirmation"
hint_text: "Check the following:<ul class=\"govuk-body-l app-panel--interruption\"><li>the decimal point</li><li>the frequency, for example every week or every calendar month</li><li>the rent type is correct, for example affordable or social rent</li></ul>"
question_text: "Are you sure this is correct?"
title_text: "You told us the rent is %{brent}."
informative_text: "This is %{higher_or_lower} than we would expect."
page_header: ""
check_answer_label: "Total rent confirmation"
check_answer_prompt: "Confirm total rent"
hint_text: "Check the following:<ul class=\"govuk-body-l app-panel--interruption\"><li>the decimal point</li><li>the frequency, for example every week or every calendar month</li><li>the rent type is correct, for example affordable or social rent</li></ul>"
question_text: "Are you sure this is correct?"
title_text: "You told us the rent is %{brent}."
informative_text: "This is %{higher_or_lower} than we would expect."
scharge_value_check:
page_header: ""
check_answer_label: "Service charge confirmation"
hint_text: "Check the following:<ul class=\"govuk-body-l app-panel--interruption\"><li>the decimal point</li><li>the frequency, for example every week or every calendar month</li><li>the needs type</li></ul>"
question_text: "Are you sure?"
title_text: "You told us the service charge is %{scharge}."
informative_text: "This is higher than we would expect."
page_header: ""
check_answer_label: "Service charge confirmation"
check_answer_prompt: "Confirm service charge"
hint_text: "Check the following:<ul class=\"govuk-body-l app-panel--interruption\"><li>the decimal point</li><li>the frequency, for example every week or every calendar month</li><li>the needs type</li></ul>"
question_text: "Are you sure?"
title_text: "You told us the service charge is %{scharge}."
informative_text: "This is higher than we would expect."
pscharge_value_check:
page_header: ""
check_answer_label: "Personal service charge confirmation"
hint_text: "Check the following:<ul class=\"govuk-body-l app-panel--interruption\"><li>the decimal point</li><li>the frequency, for example every week or every calendar month</li><li>the needs type</li></ul>"
question_text: "Are you sure?"
title_text: "You told us the personal service charge is %{pscharge}."
informative_text: "This is higher than we would expect."
page_header: ""
check_answer_label: "Personal service charge confirmation"
check_answer_prompt: "Confirm personal service charge"
hint_text: "Check the following:<ul class=\"govuk-body-l app-panel--interruption\"><li>the decimal point</li><li>the frequency, for example every week or every calendar month</li><li>the needs type</li></ul>"
question_text: "Are you sure?"
title_text: "You told us the personal service charge is %{pscharge}."
informative_text: "This is higher than we would expect."
supcharg_value_check:
page_header: ""
check_answer_label: "Support charge confirmation"
hint_text: "Check the following:<ul class=\"govuk-body-l app-panel--interruption\"><li>the decimal point</li><li>the frequency, for example every week or every calendar month</li><li>the needs type</li></ul>"
question_text: "Are you sure?"
title_text: "You told us the support charge is %{supcharg}."
informative_text: "This is higher than we would expect."
page_header: ""
check_answer_label: "Support charge confirmation"
check_answer_prompt: "Confirm support charge"
hint_text: "Check the following:<ul class=\"govuk-body-l app-panel--interruption\"><li>the decimal point</li><li>the frequency, for example every week or every calendar month</li><li>the needs type</li></ul>"
question_text: "Are you sure?"
title_text: "You told us the support charge is %{supcharg}."
informative_text: "This is higher than we would expect."
void_date_value_check:
page_header: ""
check_answer_label: "Void date confirmation"
hint_text: ""
question_text: "Are you sure the property has been vacant for this long?"
title_text: "You told us that the property has been vacant for more than 2 years."
informative_text: "This is higher than we would expect."
page_header: ""
check_answer_label: "Void date confirmation"
check_answer_prompt: "Confirm void date"
hint_text: ""
question_text: "Are you sure the property has been vacant for this long?"
title_text: "You told us that the property has been vacant for more than 2 years."
informative_text: "This is higher than we would expect."
major_repairs_date_value_check:
page_header: ""
check_answer_label: "Major repairs date confirmation"
hint_text: ""
question_text: "Are you sure the property has been vacant for this long?"
title_text: "You told us the property has been vacant for 2 years."
informative_text: "This is higher than we would expect."
page_header: ""
check_answer_label: "Major repairs date confirmation"
check_answer_prompt: "Confirm major repairs date"
hint_text: ""
question_text: "Are you sure the property has been vacant for this long?"
title_text: "You told us the property has been vacant for 2 years."
informative_text: "This is higher than we would expect."

17
config/locales/forms/2023/lettings/tenancy_information.en.yml

@ -5,13 +5,15 @@ en:
tenancy_information:
joint:
page_header: ""
check_answer_label: "Is this a joint tenancy?"
check_answer_label: "Joint tenancy"
check_answer_prompt: "Tell us if it's a joint tenancy"
hint_text: ""
question_text: "Is this a joint tenancy?"
startertenancy:
page_header: ""
check_answer_label: "Is this a starter or introductory tenancy?"
check_answer_label: "Starter or introductory tenancy"
check_answer_prompt: "Tell us if it's a starter or introductory tenancy"
hint_text: "This is also known as an ‘introductory period’."
question_text: "Is this a starter tenancy?"
@ -20,20 +22,24 @@ en:
page_header: ""
tenancy:
check_answer_label: "Type of main tenancy"
check_answer_prompt: ""
hint_text: ""
question_text: "What is the type of tenancy?"
tenancyother:
check_answer_label: ""
check_answer_prompt: ""
hint_text: ""
question_text: "Please state the tenancy type"
starter_tenancy_type:
page_header: ""
tenancy:
check_answer_label: "Type of main tenancy after the starter period has ended"
check_answer_prompt: ""
hint_text: "This is also known as an ‘introductory period’."
question_text: "What is the type of tenancy after the starter period has ended?"
tenancyother:
check_answer_label: ""
check_answer_prompt: ""
hint_text: ""
question_text: "Please state the tenancy type"
@ -41,26 +47,31 @@ en:
tenancy_length:
page_header: ""
check_answer_label: "Length of fixed-term tenancy"
check_answer_prompt: ""
hint_text: "Do not include the starter or introductory period."
question_text: "What is the length of the fixed-term tenancy to the nearest year?"
tenancy_length_affordable_rent:
page_header: ""
check_answer_label: "Length of fixed-term tenancy"
check_answer_prompt: ""
hint_text: "Do not include the starter or introductory period.</br>The minimum period is 2 years for social or affordable rent general needs logs and you do not need a log for shorter tenancies."
question_text: "What is the length of the fixed-term tenancy to the nearest year?"
tenancy_length_intermediate_rent:
page_header: ""
check_answer_label: "Length of fixed-term tenancy"
check_answer_prompt: ""
hint_text: "Do not include the starter or introductory period.</br>The minimum period is 1 year for intermediate rent general needs logs and you do not need a log for shorter tenancies."
question_text: "What is the length of the fixed-term tenancy to the nearest year?"
tenancy_length_periodic:
page_header: ""
check_answer_label: "Length of periodic tenancy"
check_answer_prompt: ""
hint_text: "As this is a periodic tenancy, this question is optional. If you do not have the information available click save and continue"
question_text: "What is the length of the periodic tenancy to the nearest year?"
sheltered:
page_header: ""
check_answer_label: "Is this letting in sheltered accommodation?"
check_answer_label: "Letting in sheltered accommodation"
check_answer_prompt: "Tell us if letting is in sheltered accommodation"
hint_text: "Sheltered housing and special retirement housing are for tenants with low-level care and support needs. This typically provides some limited support to enable independent living, such as alarm-based assistance or a scheme manager.</br></br>Extra care housing is for tenants with medium to high care and support needs, often with 24 hour access to support staff provided by an agency registered with the Care Quality Commission."
question_text: "Is this letting in sheltered accommodation?"

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

Loading…
Cancel
Save