Browse Source

Merge branch 'main' into CLDC-2272-uprn-income-validations-bug

pull/1553/head
natdeanlewissoftwire 3 years ago
parent
commit
8c79d7c505
  1. 8
      .github/workflows/review_pipeline.yml
  2. 6
      .github/workflows/review_teardown_pipeline.yml
  3. 4
      app/controllers/form_controller.rb
  4. 86
      app/controllers/merge_requests_controller.rb
  5. 4
      app/controllers/organisations_controller.rb
  6. 40
      app/controllers/start_controller.rb
  7. 5
      app/helpers/tasklist_helper.rb
  8. 97
      app/models/form.rb
  9. 8
      app/models/form/lettings/pages/max_rent_value_check.rb
  10. 20
      app/models/form/lettings/pages/min_rent_value_check.rb
  11. 1
      app/models/form/lettings/questions/address_line1.rb
  12. 1
      app/models/form/lettings/questions/address_line2.rb
  13. 1
      app/models/form/lettings/questions/county.rb
  14. 2
      app/models/form/lettings/questions/earnings.rb
  15. 3
      app/models/form/lettings/questions/la.rb
  16. 6
      app/models/form/lettings/questions/location_id.rb
  17. 1
      app/models/form/lettings/questions/postcode_for_full_address.rb
  18. 1
      app/models/form/lettings/questions/postcode_full.rb
  19. 1
      app/models/form/lettings/questions/postcode_known.rb
  20. 1
      app/models/form/lettings/questions/ppcodenk.rb
  21. 1
      app/models/form/lettings/questions/ppostcode_full.rb
  22. 1
      app/models/form/lettings/questions/previous_la_known.rb
  23. 1
      app/models/form/lettings/questions/prevloc.rb
  24. 6
      app/models/form/lettings/questions/rent_value_check.rb
  25. 1
      app/models/form/lettings/questions/town_or_city.rb
  26. 13
      app/models/form/lettings/questions/uprn_known.rb
  27. 4
      app/models/form/lettings/subsections/income_and_benefits.rb
  28. 4
      app/models/form/lettings/subsections/property_information.rb
  29. 8
      app/models/form/lettings/subsections/setup.rb
  30. 3
      app/models/form/question.rb
  31. 3
      app/models/form/sales/pages/extra_borrowing_value_check.rb
  32. 4
      app/models/form/sales/pages/uprn.rb
  33. 1
      app/models/form/sales/questions/address_line1.rb
  34. 1
      app/models/form/sales/questions/address_line2.rb
  35. 1
      app/models/form/sales/questions/county.rb
  36. 1
      app/models/form/sales/questions/postcode.rb
  37. 1
      app/models/form/sales/questions/postcode_for_full_address.rb
  38. 1
      app/models/form/sales/questions/previous_la_known.rb
  39. 1
      app/models/form/sales/questions/previous_postcode.rb
  40. 1
      app/models/form/sales/questions/previous_postcode_known.rb
  41. 1
      app/models/form/sales/questions/prevloc.rb
  42. 1
      app/models/form/sales/questions/property_local_authority.rb
  43. 12
      app/models/form/sales/questions/purchase_price.rb
  44. 1
      app/models/form/sales/questions/town_or_city.rb
  45. 7
      app/models/form/sales/questions/uprn_known.rb
  46. 2
      app/models/form/sales/subsections/property_information.rb
  47. 7
      app/models/forms/bulk_upload_lettings/prepare_your_file.rb
  48. 16
      app/models/lettings_log.rb
  49. 4
      app/models/log.rb
  50. 11
      app/models/merge_request.rb
  51. 34
      app/models/merge_request_organisation.rb
  52. 2
      app/models/validations/setup_validations.rb
  53. 1
      app/models/validations/shared_validations.rb
  54. 7
      app/models/validations/soft_validations.rb
  55. 114
      app/services/bulk_upload/lettings/year2022/row_parser.rb
  56. 116
      app/services/bulk_upload/lettings/year2023/row_parser.rb
  57. 10
      app/services/feature_toggle.rb
  58. 37
      app/services/imports/lettings_logs_import_service.rb
  59. 41
      app/services/imports/sales_logs_import_service.rb
  60. 9
      app/services/imports/scheme_location_import_service.rb
  61. 2
      app/views/bulk_upload_lettings_results/summary.html.erb
  62. 10
      app/views/form/_checkbox_question.html.erb
  63. 25
      app/views/layouts/_collection_resources.html.erb
  64. 8
      app/views/logs/index.html.erb
  65. 49
      app/views/merge_requests/organisations.html.erb
  66. 9
      app/views/organisations/merge_request.html.erb
  67. 2
      app/views/organisations/show.html.erb
  68. 7
      config/forms/2021_2022.json
  69. 7
      config/forms/2022_2023.json
  70. 2
      config/initializers/filter_parameter_logging.rb
  71. 5
      config/locales/en.yml
  72. 21
      config/routes.rb
  73. 9
      db/migrate/20230412111338_add_merge_requests_table.rb
  74. 9
      db/migrate/20230413135407_add_merge_organisations.rb
  75. 5
      db/migrate/20230418095819_add_status_to_merge_request.rb
  76. 17
      db/schema.rb
  77. BIN
      public/files/bulk-upload-lettings-legacy-template-2023-24.xlsx
  78. 6
      spec/factories/lettings_log.rb
  79. 24
      spec/factories/sales_log.rb
  80. 4
      spec/fixtures/files/lettings_logs_download.csv
  81. 4
      spec/fixtures/files/lettings_logs_download_codes_only.csv
  82. 6
      spec/fixtures/forms/2021_2022.json
  83. 5
      spec/fixtures/imports/logs/00d2343e-d5fa-4c89-8400-ec3854b0f2b4.xml
  84. 6
      spec/fixtures/imports/sales_logs/outright_sale_sales_log.xml
  85. 9
      spec/fixtures/imports/sales_logs/shared_ownership_sales_log.xml
  86. 22
      spec/helpers/tasklist_helper_spec.rb
  87. 2
      spec/models/form/lettings/pages/max_rent_value_check_spec.rb
  88. 11
      spec/models/form/lettings/pages/min_rent_value_check_spec.rb
  89. 16
      spec/models/form/lettings/questions/uprn_known_spec.rb
  90. 4
      spec/models/form/lettings/subsections/income_and_benefits_spec.rb
  91. 8
      spec/models/form/lettings/subsections/property_information_spec.rb
  92. 16
      spec/models/form/lettings/subsections/setup_spec.rb
  93. 19
      spec/models/form/sales/pages/uprn_spec.rb
  94. 4
      spec/models/form/sales/questions/age2_spec.rb
  95. 4
      spec/models/form/sales/questions/person_age_spec.rb
  96. 30
      spec/models/form/sales/questions/purchase_price_spec.rb
  97. 2
      spec/models/form/sales/questions/uprn_confirmation_spec.rb
  98. 16
      spec/models/form/sales/questions/uprn_known_spec.rb
  99. 1
      spec/models/form/sales/questions/uprn_spec.rb
  100. 8
      spec/models/form/sales/subsections/property_information_spec.rb
  101. Some files were not shown because too many files have changed in this diff Show More

8
.github/workflows/review_pipeline.yml

@ -18,7 +18,7 @@ jobs:
postgres:
name: Provision postgres
runs-on: ubuntu-latest
environment: staging
environment: review
steps:
- name: Install Cloud Foundry CLI
@ -44,7 +44,7 @@ jobs:
redis:
name: Provision redis
runs-on: ubuntu-latest
environment: staging
environment: review
steps:
- name: Install Cloud Foundry CLI
@ -70,7 +70,7 @@ jobs:
deploy:
name: Deploy review app
runs-on: ubuntu-latest
environment: staging
environment: review
needs: [postgres, redis]
permissions:
issues: write
@ -125,7 +125,7 @@ jobs:
cf set-env $APP_NAME IMPORT_PAAS_INSTANCE $IMPORT_PAAS_INSTANCE
cf set-env $APP_NAME EXPORT_PAAS_INSTANCE "dluhc-core-review-export-bucket"
cf set-env $APP_NAME S3_CONFIG $S3_CONFIG
cf set-env $APP_NAME CSV_DOWNLOAD_PAAS_INSTANCE "dluhc-core-staging-csv-bucket"
cf set-env $APP_NAME CSV_DOWNLOAD_PAAS_INSTANCE "dluhc-core-review-csv-bucket"
cf set-env $APP_NAME SENTRY_DSN $SENTRY_DSN
cf set-env $APP_NAME APP_HOST "https://dluhc-core-review-${{ github.event.pull_request.number }}.london.cloudapps.digital"

6
.github/workflows/review_teardown_pipeline.yml

@ -14,7 +14,7 @@ jobs:
app:
name: Teardown app
runs-on: ubuntu-latest
environment: staging
environment: review
steps:
- name: Install Cloud Foundry CLI
@ -40,7 +40,7 @@ jobs:
postgres:
name: Teardown postgres
runs-on: ubuntu-latest
environment: staging
environment: review
needs: [app]
steps:
@ -67,7 +67,7 @@ jobs:
redis:
name: Teardown redis
runs-on: ubuntu-latest
environment: staging
environment: review
needs: [app]
steps:

4
app/controllers/form_controller.rb

@ -126,7 +126,7 @@ private
next_page = form.get_page(next_page_id)
previous_page = form.previous_page_id(@page, @log, current_user)
if next_page&.interruption_screen? || next_page_id == previous_page
if next_page&.interruption_screen? || next_page_id == previous_page || CONFIRMATION_PAGE_IDS.include?(next_page_id)
return send("#{@log.class.name.underscore}_#{next_page_id}_path", @log, { referrer: "check_answers" })
else
return send("#{@log.model_name.param_key}_#{form.subsection_for_page(@page).id}_check_answers_path", @log)
@ -180,4 +180,6 @@ private
redirect_to lettings_log_path(@log) unless @log.collection_period_open?
end
CONFIRMATION_PAGE_IDS = %w[uprn_confirmation].freeze
end

86
app/controllers/merge_requests_controller.rb

@ -0,0 +1,86 @@
class MergeRequestsController < ApplicationController
before_action :find_resource, only: %i[update organisations update_organisations remove_merging_organisation]
before_action :authenticate_user!
before_action :authenticate_scope!, except: [:create]
def create
ActiveRecord::Base.transaction do
@merge_request = MergeRequest.create!(merge_request_params.merge(status: :unsubmitted))
MergeRequestOrganisation.create!({ merge_request: @merge_request, merging_organisation: @merge_request.requesting_organisation })
end
redirect_to organisations_merge_request_path(@merge_request)
rescue ActiveRecord::RecordInvalid
render_not_found
end
def organisations
@answer_options = organisations_answer_options
end
def update
if @merge_request.update(merge_request_params)
redirect_to next_page_path
else
render previous_template, status: :unprocessable_entity
end
end
def update_organisations
merge_request_organisation = MergeRequestOrganisation.new(merge_request_organisation_params)
@answer_options = organisations_answer_options
if merge_request_organisation.save
render :organisations
else
render :organisations, status: :unprocessable_entity
end
end
def remove_merging_organisation
MergeRequestOrganisation.find_by(merge_request_organisation_params)&.destroy!
@answer_options = organisations_answer_options
render :organisations
end
private
def organisations_answer_options
answer_options = { "" => "Select an option" }
Organisation.all.pluck(:id, :name).each do |organisation|
answer_options[organisation[0]] = organisation[1]
end
answer_options
end
def merge_request_params
merge_params = params.fetch(:merge_request, {}).permit(:requesting_organisation_id, :other_merging_organisations, :status)
if merge_params[:requesting_organisation_id].present? && (current_user.data_coordinator? || current_user.data_provider?)
merge_params[:requesting_organisation_id] = current_user.organisation.id
end
merge_params
end
def merge_request_organisation_params
{ merge_request: @merge_request, merging_organisation_id: params[:merge_request][:merging_organisation] }
end
def find_resource
@merge_request = MergeRequest.find(params[:id])
end
def next_page_path
absorbing_organisation_merge_request_path(@merge_request)
end
def previous_template
:organisations
end
def authenticate_scope!
if current_user.organisation != @merge_request.requesting_organisation && !current_user.support?
render_not_found
end
end
end

4
app/controllers/organisations_controller.rb

@ -137,6 +137,10 @@ class OrganisationsController < ApplicationController
end
end
def merge_request
@merge_request = MergeRequest.new
end
private
def org_params

40
app/controllers/start_controller.rb

@ -28,4 +28,44 @@ class StartController < ApplicationController
type: "application/pdf",
)
end
def download_23_24_lettings_bulk_upload_template
send_file(
Rails.root.join("public/files/bulk-upload-lettings-template-2023-24.xlsx"),
filename: "2023-24-lettings-bulk-upload-template.xlsx",
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
)
end
def download_23_24_lettings_bulk_upload_legacy_template
send_file(
Rails.root.join("public/files/bulk-upload-lettings-legacy-template-2023-24.xlsx"),
filename: "2023-24-lettings-bulk-upload-legacy-template.xlsx",
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
)
end
def download_23_24_lettings_bulk_upload_specification
send_file(
Rails.root.join("public/files/bulk-upload-lettings-specification-2023-24.xlsx"),
filename: "2023-24-lettings-bulk-upload-specification.xlsx",
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
)
end
def download_22_23_lettings_bulk_upload_template
send_file(
Rails.root.join("public/files/bulk-upload-lettings-template-2022-23.xlsx"),
filename: "2022-23-lettings-bulk-upload-template.xlsx",
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
)
end
def download_22_23_lettings_bulk_upload_specification
send_file(
Rails.root.join("public/files/bulk-upload-lettings-specification-2022-23.xlsx"),
filename: "2022-23-lettings-bulk-upload-specification.xlsx",
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
)
end
end

5
app/helpers/tasklist_helper.rb

@ -1,5 +1,6 @@
module TasklistHelper
include GovukLinkHelper
include CollectionTimeHelper
def get_next_incomplete_section(log)
log.form.subsections.find { |subsection| subsection.is_incomplete?(log) }
@ -34,7 +35,9 @@ module TasklistHelper
"You can #{govuk_link_to 'review and make changes to this log', link} until #{log.form.end_date.to_formatted_s(:govuk_date)}.".html_safe
else
"This log is from the #{log.form.start_date.year}/#{log.form.start_date.year + 1} collection window, which is now closed."
start_year = log.startdate ? collection_start_year_for_date(log.startdate) : log.form.start_date.year
"This log is from the #{start_year}/#{start_year + 1} collection window, which is now closed."
end
end

97
app/models/form.rb

@ -178,48 +178,78 @@ class Form
pages.reject { |p| p.routed_to?(log, current_user) }
end
def invalidated_questions(log)
invalidated_page_questions(log) + invalidated_conditional_questions(log)
end
def invalidated_page_questions(log, current_user = nil)
# we're already treating these fields as a special case and reset their values upon saving a log
callback_questions = %w[postcode_known la ppcodenk previous_la_known prevloc postcode_full ppostcode_full location_id address_line1 address_line2 town_or_city county]
questions.reject { |q| q.page.routed_to?(log, current_user) || q.derived? || callback_questions.include?(q.id) } || []
end
def reset_not_routed_questions_and_invalid_answers(log)
reset_checkbox_questions_if_not_routed(log)
def reset_not_routed_questions(log)
enabled_questions = enabled_page_questions(log)
enabled_question_ids = enabled_questions.map(&:id)
reset_radio_questions_if_not_routed_or_invalid_answers(log)
invalidated_page_questions(log).each do |question|
if %w[radio checkbox].include?(question.type)
enabled_answer_options = enabled_question_ids.include?(question.id) ? enabled_questions.find { |q| q.id == question.id }.answer_options : {}
current_answer_option_valid = enabled_answer_options.present? ? enabled_answer_options.key?(log.public_send(question.id).to_s) : false
reset_free_user_input_questions_if_not_routed(log)
end
if !current_answer_option_valid && log.respond_to?(question.id.to_s)
Rails.logger.debug("Cleared #{question.id} value")
log.public_send("#{question.id}=", nil)
def reset_checkbox_questions_if_not_routed(log)
checkbox_questions = routed_and_not_routed_questions_by_type(log, type: "checkbox")
checkbox_questions[:not_routed].each do |not_routed_question|
valid_options = checkbox_questions[:routed]
.select { |q| q.id == not_routed_question.id }
.flat_map { |q| q.answer_options.keys }
not_routed_question.answer_options.each_key do |invalid_option|
if !log.respond_to?(invalid_option) || valid_options.include?(invalid_option) || log.public_send(invalid_option).nil?
next
else
(question.answer_options.keys - enabled_answer_options.keys).map do |invalid_answer_option|
Rails.logger.debug("Cleared #{invalid_answer_option} value")
log.public_send("#{invalid_answer_option}=", nil) if log.respond_to?(invalid_answer_option)
end
clear_attribute(log, invalid_option)
end
end
end
end
def reset_radio_questions_if_not_routed_or_invalid_answers(log)
radio_questions = routed_and_not_routed_questions_by_type(log, type: "radio")
valid_radio_options = radio_questions[:routed]
.group_by(&:id)
.transform_values! { |q_array| q_array.flat_map { |q| q.answer_options.keys } }
radio_questions[:not_routed].each do |not_routed_question|
question_id = not_routed_question.id
if !log.respond_to?(question_id) || log.public_send(question_id).nil? || valid_radio_options.key?(question_id)
next
else
clear_attribute(log, question_id)
end
end
valid_radio_options.each do |question_id, valid_options|
if !log.respond_to?(question_id) || valid_options.include?(log.public_send(question_id).to_s)
next
else
clear_attribute(log, question_id)
end
end
end
def reset_free_user_input_questions_if_not_routed(log)
non_radio_checkbox_questions = routed_and_not_routed_questions_by_type(log)
enabled_question_ids = non_radio_checkbox_questions[:routed].map(&:id)
non_radio_checkbox_questions[:not_routed].each do |not_routed_question|
question_id = not_routed_question.id
if log.public_send(question_id).nil? || enabled_question_ids.include?(question_id)
next
else
Rails.logger.debug("Cleared #{question.id} value")
log.public_send("#{question.id}=", nil) unless enabled_question_ids.include?(question.id)
clear_attribute(log, question_id)
end
end
end
def enabled_page_questions(log)
questions - invalidated_page_questions(log)
def routed_and_not_routed_questions_by_type(log, type: nil, current_user: nil)
questions_by_type = if type
questions.reject { |q| q.type != type || q.disable_clearing_if_not_routed_or_dynamic_answer_options }
else
questions.reject { |q| %w[radio checkbox].include?(q.type) || q.disable_clearing_if_not_routed_or_dynamic_answer_options }
end
routed, not_routed = questions_by_type.partition { |q| q.page.routed_to?(log, current_user) || q.derived? }
{ routed:, not_routed: }
end
def invalidated_conditional_questions(log)
questions.reject { |q| q.enabled?(log) } || []
def clear_attribute(log, attribute)
Rails.logger.debug("Cleared #{attribute} value")
log.public_send("#{attribute}=", nil)
end
def readonly_questions
@ -230,6 +260,13 @@ class Form
questions.select { |q| q.type == "numeric" }
end
def previous_page(page_ids, page_index, log, current_user)
prev_page = get_page(page_ids[page_index - 1])
return prev_page.id if prev_page.routed_to?(log, current_user)
previous_page(page_ids, page_index - 1, log, current_user)
end
def send_chain(arr, log)
Array(arr).inject(log) { |o, a| o.public_send(*a) }
end

8
app/models/form/lettings/pages/max_rent_value_check.rb

@ -1,7 +1,6 @@
class Form::Lettings::Pages::MaxRentValueCheck < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "max_rent_value_check"
def initialize(id, hsh, subsection, check_answers_card_number: nil)
super(id, hsh, subsection)
@depends_on = [{ "rent_in_soft_max_range?" => true }]
@title_text = {
"translation" => "soft_validations.rent.outside_range_title",
@ -23,9 +22,10 @@ class Form::Lettings::Pages::MaxRentValueCheck < ::Form::Page
},
],
}
@check_answers_card_number = check_answers_card_number
end
def questions
@questions ||= [Form::Lettings::Questions::RentValueCheck.new(nil, nil, self)]
@questions ||= [Form::Lettings::Questions::RentValueCheck.new(nil, nil, self, check_answers_card_number: @check_answers_card_number)]
end
end

20
app/models/form/lettings/pages/min_rent_value_check.rb

@ -1,7 +1,6 @@
class Form::Lettings::Pages::MinRentValueCheck < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "min_rent_value_check"
def initialize(id, hsh, subsection, check_answers_card_number: nil)
super(id, hsh, subsection)
@depends_on = [{ "rent_in_soft_min_range?" => true }]
@title_text = {
"translation" => "soft_validations.rent.outside_range_title",
@ -13,17 +12,16 @@ class Form::Lettings::Pages::MinRentValueCheck < ::Form::Page
}
@informative_text = {
"translation" => "soft_validations.rent.min_hint_text",
"arguments" => [
{
"key" => "field_formatted_as_currency",
"arguments_for_key" => "soft_min_for_period",
"i18n_template" => "soft_min_for_period",
},
],
"arguments" => [{
"key" => "field_formatted_as_currency",
"arguments_for_key" => "soft_min_for_period",
"i18n_template" => "soft_min_for_period",
}],
}
@check_answers_card_number = check_answers_card_number
end
def questions
@questions ||= [Form::Lettings::Questions::RentValueCheck.new(nil, nil, self)]
@questions ||= [Form::Lettings::Questions::RentValueCheck.new(nil, nil, self, check_answers_card_number: @check_answers_card_number)]
end
end

1
app/models/form/lettings/questions/address_line1.rb

@ -7,6 +7,7 @@ class Form::Lettings::Questions::AddressLine1 < ::Form::Question
@type = "text"
@plain_label = true
@check_answer_label = "Q12 - Address"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def answer_label(log, _current_user = nil)

1
app/models/form/lettings/questions/address_line2.rb

@ -5,6 +5,7 @@ class Form::Lettings::Questions::AddressLine2 < ::Form::Question
@header = "Address line 2 (optional)"
@type = "text"
@plain_label = true
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def hidden_in_check_answers?(_log = nil, _current_user = nil)

1
app/models/form/lettings/questions/county.rb

@ -5,6 +5,7 @@ class Form::Lettings::Questions::County < ::Form::Question
@header = "County (optional)"
@type = "text"
@plain_label = true
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def hidden_in_check_answers?(_log = nil, _current_user = nil)

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

@ -10,7 +10,7 @@ class Form::Lettings::Questions::Earnings < ::Form::Question
@min = 0
@guidance_partial = "what_counts_as_income"
@hint_text = ""
@step = 1
@step = 0.01
@prefix = "£"
@suffix = [
{ "label" => " every week", "depends_on" => { "incfreq" => 1 } },

3
app/models/form/lettings/questions/la.rb

@ -5,9 +5,10 @@ class Form::Lettings::Questions::La < ::Form::Question
@check_answer_label = "Local Authority"
@header = "What is the property’s local authority?"
@type = "select"
@check_answers_card_number = 0
@check_answers_card_number = nil
@hint_text = ""
@question_number = 13
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def answer_options

6
app/models/form/lettings/questions/location_id.rb

@ -1,6 +1,7 @@
class Form::Lettings::Questions::LocationId < ::Form::Question
def initialize(_id, hsh, page)
super("location_id", hsh, page)
def initialize(id, hsh, page)
super
@id = "location_id"
@check_answer_label = "Location"
@header = header_text
@type = "radio"
@ -11,6 +12,7 @@ class Form::Lettings::Questions::LocationId < ::Form::Question
},
}
@question_number = 10
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def answer_options

1
app/models/form/lettings/questions/postcode_for_full_address.rb

@ -17,6 +17,7 @@ class Form::Lettings::Questions::PostcodeForFullAddress < ::Form::Question
},
}
@plain_label = true
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def hidden_in_check_answers?(_log = nil, _current_user = nil)

1
app/models/form/lettings/questions/postcode_full.rb

@ -10,5 +10,6 @@ class Form::Lettings::Questions::PostcodeFull < ::Form::Question
@check_answers_card_number = 0
@hint_text = ""
@inferred_answers = { "la" => { "is_la_inferred" => true } }
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
end

1
app/models/form/lettings/questions/postcode_known.rb

@ -8,6 +8,7 @@ class Form::Lettings::Questions::PostcodeKnown < ::Form::Question
@check_answers_card_number = 0
@hint_text = ""
@answer_options = ANSWER_OPTIONS
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
@conditional_for = { "postcode_full" => [1] }
@hidden_in_check_answers = { "depends_on" => [{ "postcode_known" => 0 }, { "postcode_known" => 1 }] }
end

1
app/models/form/lettings/questions/ppcodenk.rb

@ -11,6 +11,7 @@ class Form::Lettings::Questions::Ppcodenk < ::Form::Question
@conditional_for = { "ppostcode_full" => [1] }
@hidden_in_check_answers = { "depends_on" => [{ "ppcodenk" => 0 }, { "ppcodenk" => 1 }] }
@question_number = 80
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
ANSWER_OPTIONS = { "1" => { "value" => "Yes" }, "0" => { "value" => "No" } }.freeze

1
app/models/form/lettings/questions/ppostcode_full.rb

@ -11,5 +11,6 @@ class Form::Lettings::Questions::PpostcodeFull < ::Form::Question
@hint_text = ""
@inferred_answers = { "prevloc" => { "is_previous_la_inferred" => true } }
@question_number = 80
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
end

1
app/models/form/lettings/questions/previous_la_known.rb

@ -11,6 +11,7 @@ class Form::Lettings::Questions::PreviousLaKnown < ::Form::Question
@conditional_for = { "prevloc" => [1] }
@hidden_in_check_answers = { "depends_on" => [{ "previous_la_known" => 0 }, { "previous_la_known" => 1 }] }
@question_number = 81
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
ANSWER_OPTIONS = { "1" => { "value" => "Yes" }, "0" => { "value" => "No" } }.freeze

1
app/models/form/lettings/questions/prevloc.rb

@ -9,6 +9,7 @@ class Form::Lettings::Questions::Prevloc < ::Form::Question
@check_answers_card_number = 0
@hint_text = "Select ‘Northern Ireland’, ‘Scotland’, ‘Wales’ or ‘Outside the UK’ if the household’s last settled home was outside England."
@question_number = 81
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def answer_options

6
app/models/form/lettings/questions/rent_value_check.rb

@ -1,11 +1,11 @@
class Form::Lettings::Questions::RentValueCheck < ::Form::Question
def initialize(id, hsh, page)
super
def initialize(id, hsh, page, check_answers_card_number:)
super(id, hsh, page)
@id = "rent_value_check"
@check_answer_label = "Total rent confirmation"
@header = "Are you sure this is correct?"
@type = "interruption_screen"
@check_answers_card_number = 0
@check_answers_card_number = check_answers_card_number
@answer_options = ANSWER_OPTIONS
@hidden_in_check_answers = { "depends_on" => [{ "rent_value_check" => 0 }, { "rent_value_check" => 1 }] }
end

1
app/models/form/lettings/questions/town_or_city.rb

@ -5,6 +5,7 @@ class Form::Lettings::Questions::TownOrCity < ::Form::Question
@header = "Town or city"
@type = "text"
@plain_label = true
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def hidden_in_check_answers?(_log = nil, _current_user = nil)

13
app/models/form/lettings/questions/uprn_known.rb

@ -9,7 +9,18 @@ class Form::Lettings::Questions::UprnKnown < ::Form::Question
@hint_text = "The Unique Property Reference Number (UPRN) is a unique number system created by Ordnance Survey and used by housing providers and sectors UK-wide. For example 10010457355.<br><br>
You can continue without the UPRN, but it means we will need you to enter the address of the property."
@conditional_for = { "uprn" => [1] }
@hidden_in_check_answers = true
@inferred_check_answers_value = [
{
"condition" => { "uprn_known" => 0 },
"value" => "Not known",
},
]
@hidden_in_check_answers = {
"depends_on" => [
{ "uprn_known" => 0 },
{ "uprn_known" => 1 },
],
}
end
ANSWER_OPTIONS = {

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

@ -24,8 +24,8 @@ class Form::Lettings::Subsections::IncomeAndBenefits < ::Form::Subsection
Form::Lettings::Pages::RentBiWeekly.new(nil, nil, self),
Form::Lettings::Pages::Rent4Weekly.new(nil, nil, self),
Form::Lettings::Pages::RentMonthly.new(nil, nil, self),
Form::Lettings::Pages::MinRentValueCheck.new(nil, nil, self),
Form::Lettings::Pages::MaxRentValueCheck.new(nil, nil, self),
Form::Lettings::Pages::MinRentValueCheck.new("brent_min_rent_value_check", nil, self, check_answers_card_number: 0),
Form::Lettings::Pages::MaxRentValueCheck.new("brent_max_rent_value_check", nil, self, check_answers_card_number: 0),
Form::Lettings::Pages::Outstanding.new(nil, nil, self),
Form::Lettings::Pages::OutstandingAmount.new(nil, nil, self),
].compact

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

@ -10,6 +10,8 @@ class Form::Lettings::Subsections::PropertyInformation < ::Form::Subsection
@pages ||= [
uprn_questions,
Form::Lettings::Pages::PropertyLocalAuthority.new(nil, nil, self),
Form::Lettings::Pages::MinRentValueCheck.new("local_authority_min_rent_value_check", nil, self, check_answers_card_number: nil),
Form::Lettings::Pages::MaxRentValueCheck.new("local_authority_max_rent_value_check", nil, self, check_answers_card_number: nil),
Form::Lettings::Pages::FirstTimePropertyLetAsSocialHousing.new(nil, nil, self),
Form::Lettings::Pages::PropertyLetType.new(nil, nil, self),
Form::Lettings::Pages::PropertyVacancyReasonNotFirstLet.new(nil, nil, self),
@ -20,6 +22,8 @@ class Form::Lettings::Subsections::PropertyInformation < ::Form::Subsection
Form::Lettings::Pages::PropertyBuildingType.new(nil, nil, self),
Form::Lettings::Pages::PropertyWheelchairAccessible.new(nil, nil, self),
Form::Lettings::Pages::PropertyNumberOfBedrooms.new(nil, nil, self),
Form::Lettings::Pages::MinRentValueCheck.new("beds_min_rent_value_check", nil, self, check_answers_card_number: 0),
Form::Lettings::Pages::MaxRentValueCheck.new("beds_max_rent_value_check", nil, self, check_answers_card_number: 0),
Form::Lettings::Pages::VoidDate.new(nil, nil, self),
Form::Lettings::Pages::VoidDateValueCheck.new(nil, nil, self),
Form::Lettings::Pages::PropertyMajorRepairs.new(nil, nil, self),

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

@ -10,14 +10,22 @@ class Form::Lettings::Subsections::Setup < ::Form::Subsection
@pages ||= [
organisation_page,
stock_owner_page,
Form::Lettings::Pages::MinRentValueCheck.new("stock_owner_min_rent_value_check", nil, self),
Form::Lettings::Pages::MaxRentValueCheck.new("stock_owner_max_rent_value_check", nil, self),
managing_organisation_page,
created_by_page,
Form::Lettings::Pages::NeedsType.new(nil, nil, self),
Form::Lettings::Pages::Scheme.new(nil, nil, self),
Form::Lettings::Pages::Location.new(nil, nil, self),
Form::Lettings::Pages::MinRentValueCheck.new("needs_type_min_rent_value_check", nil, self),
Form::Lettings::Pages::MaxRentValueCheck.new("needs_type_max_rent_value_check", nil, self),
Form::Lettings::Pages::Renewal.new(nil, nil, self),
Form::Lettings::Pages::TenancyStartDate.new(nil, nil, self),
Form::Lettings::Pages::MinRentValueCheck.new("start_date_min_rent_value_check", nil, self),
Form::Lettings::Pages::MaxRentValueCheck.new("start_date_max_rent_value_check", nil, self),
Form::Lettings::Pages::RentType.new(nil, nil, self),
Form::Lettings::Pages::MinRentValueCheck.new("rent_type_min_rent_value_check", nil, self),
Form::Lettings::Pages::MaxRentValueCheck.new("rent_type_max_rent_value_check", nil, self),
Form::Lettings::Pages::TenantCode.new(nil, nil, self),
Form::Lettings::Pages::PropertyReference.new(nil, nil, self),
].compact

3
app/models/form/question.rb

@ -1,5 +1,5 @@
class Form::Question
attr_accessor :id, :header, :hint_text, :description, :questions,
attr_accessor :id, :header, :hint_text, :description, :questions, :disable_clearing_if_not_routed_or_dynamic_answer_options,
:type, :min, :max, :step, :width, :fields_to_add, :result_field,
:conditional_for, :readonly, :answer_options, :page, :check_answer_label,
:inferred_answers, :hidden_in_check_answers, :inferred_check_answers_value,
@ -42,6 +42,7 @@ class Form::Question
@unresolved_hint_text = hsh["unresolved_hint_text"]
@question_number = hsh["question_number"]
@plain_label = hsh["plain_label"]
@disable_clearing_if_not_routed_or_dynamic_answer_options = hsh["disable_clearing_if_not_routed_or_dynamic_answer_options"]
end
end

3
app/models/form/sales/pages/extra_borrowing_value_check.rb

@ -9,8 +9,7 @@ class Form::Sales::Pages::ExtraBorrowingValueCheck < Form::Page
@title_text = {
"translation" => "soft_validations.extra_borrowing.title",
}
@informative_text = {
}
@informative_text = {}
end
def questions

4
app/models/form/sales/pages/uprn.rb

@ -11,10 +11,6 @@ class Form::Sales::Pages::Uprn < ::Form::Page
]
end
def routed_to?(log, _current_user = nil)
log.uprn_known == 1
end
def skip_text
"Enter address instead"
end

1
app/models/form/sales/questions/address_line1.rb

@ -7,6 +7,7 @@ class Form::Sales::Questions::AddressLine1 < ::Form::Question
@type = "text"
@plain_label = true
@check_answer_label = "Q15 - Address"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def answer_label(log, _current_user = nil)

1
app/models/form/sales/questions/address_line2.rb

@ -5,6 +5,7 @@ class Form::Sales::Questions::AddressLine2 < ::Form::Question
@header = "Address line 2 (optional)"
@type = "text"
@plain_label = true
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def hidden_in_check_answers?(_log = nil, _current_user = nil)

1
app/models/form/sales/questions/county.rb

@ -5,6 +5,7 @@ class Form::Sales::Questions::County < ::Form::Question
@header = "County (optional)"
@type = "text"
@plain_label = true
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def hidden_in_check_answers?(_log = nil, _current_user = nil)

1
app/models/form/sales/questions/postcode.rb

@ -17,5 +17,6 @@ class Form::Sales::Questions::Postcode < ::Form::Question
"is_la_inferred" => true,
},
}
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
end

1
app/models/form/sales/questions/postcode_for_full_address.rb

@ -17,6 +17,7 @@ class Form::Sales::Questions::PostcodeForFullAddress < ::Form::Question
},
}
@plain_label = true
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def hidden_in_check_answers?(_log = nil, _current_user = nil)

1
app/models/form/sales/questions/previous_la_known.rb

@ -21,6 +21,7 @@ class Form::Sales::Questions::PreviousLaKnown < ::Form::Question
"prevloc" => [1],
}
@question_number = 58
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
ANSWER_OPTIONS = {

1
app/models/form/sales/questions/previous_postcode.rb

@ -18,5 +18,6 @@ class Form::Sales::Questions::PreviousPostcode < ::Form::Question
},
}
@question_number = 57
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
end

1
app/models/form/sales/questions/previous_postcode_known.rb

@ -21,6 +21,7 @@ class Form::Sales::Questions::PreviousPostcodeKnown < ::Form::Question
],
}
@question_number = 57
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
ANSWER_OPTIONS = {

1
app/models/form/sales/questions/prevloc.rb

@ -12,6 +12,7 @@ class Form::Sales::Questions::Prevloc < ::Form::Question
"value" => "Not known",
}]
@question_number = 58
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def answer_options

1
app/models/form/sales/questions/property_local_authority.rb

@ -6,6 +6,7 @@ class Form::Sales::Questions::PropertyLocalAuthority < ::Form::Question
@header = "What is the property’s local authority?"
@type = "select"
@question_number = 16
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def answer_options

12
app/models/form/sales/questions/purchase_price.rb

@ -9,17 +9,23 @@ class Form::Sales::Questions::PurchasePrice < ::Form::Question
@step = 0.01
@width = 5
@prefix = "£"
@hint_text = "For all schemes, including Right to Acquire (RTA), Right to Buy (RTB), Voluntary Right to Buy (VRTB) or Preserved Right to Buy (PRTB) sales, enter the full price of the property without any discount"
@hint_text = hint_text
@ownership_sch = ownershipsch
@question_number = question_number
end
def question_number
case @ownership_sch
when 2
when 2 # discounted ownership scheme
100
when 3
when 3 # outright sale
110
end
end
def hint_text
return if @ownership_sch == 3 # outright sale
"For all schemes, including Right to Acquire (RTA), Right to Buy (RTB), Voluntary Right to Buy (VRTB) or Preserved Right to Buy (PRTB) sales, enter the full price of the property without any discount"
end
end

1
app/models/form/sales/questions/town_or_city.rb

@ -5,6 +5,7 @@ class Form::Sales::Questions::TownOrCity < ::Form::Question
@header = "Town or city"
@type = "text"
@plain_label = true
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
end
def hidden_in_check_answers?(_log = nil, _current_user = nil)

7
app/models/form/sales/questions/uprn_known.rb

@ -15,7 +15,12 @@ class Form::Sales::Questions::UprnKnown < ::Form::Question
"value" => "Not known",
},
]
@hidden_in_check_answers = true
@hidden_in_check_answers = {
"depends_on" => [
{ "uprn_known" => 0 },
{ "uprn_known" => 1 },
],
}
end
ANSWER_OPTIONS = {

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

@ -8,13 +8,13 @@ class Form::Sales::Subsections::PropertyInformation < ::Form::Subsection
def pages
@pages ||= [
uprn_questions,
Form::Sales::Pages::PropertyNumberOfBedrooms.new(nil, nil, self),
Form::Sales::Pages::AboutPriceValueCheck.new("about_price_bedrooms_value_check", nil, self),
Form::Sales::Pages::PropertyUnitType.new(nil, nil, self),
Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_property_type_value_check", nil, self),
Form::Sales::Pages::PercentageDiscountValueCheck.new("percentage_discount_proptype_value_check", nil, self),
Form::Sales::Pages::PropertyBuildingType.new(nil, nil, self),
uprn_questions,
postcode_and_la_questions,
Form::Sales::Pages::AboutPriceValueCheck.new("about_price_la_value_check", nil, self),
Form::Sales::Pages::PropertyWheelchairAccessible.new(nil, nil, self),

7
app/models/forms/bulk_upload_lettings/prepare_your_file.rb

@ -26,7 +26,12 @@ module Forms
end
def old_template_path
"/files/bulk-upload-lettings-template-2022-23.xlsx"
case year
when 2022
"/files/bulk-upload-lettings-template-2022-23.xlsx"
when 2023
"/files/bulk-upload-lettings-legacy-template-2023-24.xlsx"
end
end
def template_path

16
app/models/lettings_log.rb

@ -55,7 +55,7 @@ class LettingsLog < Log
scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org).or(where(managing_organisation: org)) }
AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze
OPTIONAL_FIELDS = %w[first_time_property_let_as_social_housing tenancycode propcode chcharge].freeze
OPTIONAL_FIELDS = %w[rent_value_check first_time_property_let_as_social_housing tenancycode propcode chcharge].freeze
RENT_TYPE_MAPPING_LABELS = { 1 => "Social Rent", 2 => "Affordable Rent", 3 => "Intermediate Rent" }.freeze
HAS_BENEFITS_OPTIONS = [1, 6, 8, 7].freeze
NUM_OF_WEEKS_FROM_PERIOD = { 2 => 26, 3 => 13, 4 => 12, 5 => 50, 6 => 49, 7 => 48, 8 => 47, 9 => 46, 1 => 52, 10 => 53 }.freeze
@ -435,12 +435,22 @@ class LettingsLog < Log
end
def soft_min_for_period
soft_min = LaRentRange.find_by(start_year: collection_start_year, la:, beds: beds_for_la_rent_range, lettype:).soft_min
soft_min = LaRentRange.find_by(
start_year: collection_start_year,
la:,
beds: beds_for_la_rent_range,
lettype:,
).soft_min
"#{soft_value_for_period(soft_min)} #{SUFFIX_FROM_PERIOD[period].presence || 'every week'}"
end
def soft_max_for_period
soft_max = LaRentRange.find_by(start_year: collection_start_year, la:, beds: beds_for_la_rent_range, lettype:).soft_max
soft_max = LaRentRange.find_by(
start_year: collection_start_year,
la:,
beds: beds_for_la_rent_range,
lettype:,
).soft_max
"#{soft_value_for_period(soft_max)} #{SUFFIX_FROM_PERIOD[period].presence || 'every week'}"
end

4
app/models/log.rb

@ -48,7 +48,7 @@ class Log < ApplicationRecord
service = UprnClient.new(uprn)
service.call
return errors.add(:uprn, service.error) if service.error.present?
return errors.add(:uprn, :uprn_error, message: service.error) if service.error.present?
presenter = UprnDataPresenter.new(service.result)
@ -174,7 +174,7 @@ private
def reset_invalidated_dependent_fields!
return unless form
form.reset_not_routed_questions(self)
form.reset_not_routed_questions_and_invalid_answers(self)
reset_created_by!
end

11
app/models/merge_request.rb

@ -0,0 +1,11 @@
class MergeRequest < ApplicationRecord
belongs_to :requesting_organisation, class_name: "Organisation"
has_many :merge_request_organisations
has_many :merging_organisations, through: :merge_request_organisations, source: :merging_organisation
STATUS = {
"unsubmitted" => 0,
"submitted" => 1,
}.freeze
enum status: STATUS
end

34
app/models/merge_request_organisation.rb

@ -0,0 +1,34 @@
class MergeRequestOrganisation < ApplicationRecord
belongs_to :merge_request, class_name: "MergeRequest"
belongs_to :merging_organisation, class_name: "Organisation"
validates :merge_request, presence: { message: I18n.t("validations.merge_request.merge_request_id.blank") }
validates :merging_organisation, presence: { message: I18n.t("validations.merge_request.merging_organisation_id.blank") }
validate :validate_merging_organisations
scope :not_unsubmitted, -> { joins(:merge_request).where.not(merge_requests: { status: "unsubmitted" }) }
scope :with_merging_organisation, ->(merging_organisation) { where(merging_organisation:) }
has_paper_trail
private
def validate_merging_organisations
if MergeRequestOrganisation.where(merge_request:, merging_organisation:).count.positive?
errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge"))
end
if MergeRequestOrganisation.not_unsubmitted.with_merging_organisation(merging_organisation).count.positive?
errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge"))
merge_request.errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge"))
end
if MergeRequest.not_unsubmitted.where.not(id: merge_request_id).where(requesting_organisation: merging_organisation).count.positive?
errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge"))
merge_request.errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge"))
end
if merging_organisation_id.blank? || !Organisation.where(id: merging_organisation_id).exists?
merge_request.errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_not_selected"))
end
end
end

2
app/models/validations/setup_validations.rb

@ -3,7 +3,7 @@ module Validations::SetupValidations
include CollectionTimeHelper
def validate_startdate_setup(record)
return unless record.startdate && date_valid?("startdate", record) && FeatureToggle.startdate_collection_window_validation_enabled?
return unless record.startdate && date_valid?("startdate", record)
unless record.startdate.between?(active_collection_start_date, current_collection_end_date)
record.errors.add :startdate, startdate_validation_error_message

1
app/models/validations/shared_validations.rb

@ -7,6 +7,7 @@ module Validations::SharedValidations
main_field_label = main_label || main_field.to_s.humanize(capitalize: false)
other_field_label = other_label || other_field.to_s.humanize(capitalize: false)
if record[main_field] == value_other && record[other_field].blank?
record.errors.add main_field.to_sym, I18n.t("validations.other_field_missing", main_field_label:, other_field_label:)
record.errors.add other_field.to_sym, I18n.t("validations.other_field_missing", main_field_label:, other_field_label:)
end

7
app/models/validations/soft_validations.rb

@ -40,7 +40,12 @@ module Validations::SoftValidations
def rent_in_soft_max_range?
return unless brent && weekly_value(brent) && startdate
rent_range = LaRentRange.find_by(start_year: collection_start_year, la:, beds: beds_for_la_rent_range, lettype: get_lettype)
rent_range = LaRentRange.find_by(
start_year: collection_start_year,
la:,
beds: beds_for_la_rent_range,
lettype: get_lettype,
)
if beds.present? && rent_range.present? && beds > LaRentRange::MAX_BEDS
weekly_value(brent) > rent_range.soft_max
elsif rent_range.present?

114
app/services/bulk_upload/lettings/year2022/row_parser.rb

@ -278,58 +278,58 @@ class BulkUpload::Lettings::Year2022::RowParser
attribute :field_134, :integer
validates :field_1, presence: { message: I18n.t("validations.not_answered", question: "letting type") },
inclusion: { in: (1..12).to_a, message: I18n.t("validations.invalid_option", question: "letting type") }
validates :field_4, presence: { if: proc { [2, 4, 6, 8, 10, 12].include?(field_1) } }
validates :field_12, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 1 must be a number or the letter R" }
validates :field_13, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 2 must be a number or the letter R" }, allow_blank: true
validates :field_14, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 3 must be a number or the letter R" }, allow_blank: true
validates :field_15, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 4 must be a number or the letter R" }, allow_blank: true
validates :field_16, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 5 must be a number or the letter R" }, allow_blank: true
validates :field_17, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 6 must be a number or the letter R" }, allow_blank: true
validates :field_18, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 7 must be a number or the letter R" }, allow_blank: true
validates :field_19, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 8 must be a number or the letter R" }, allow_blank: true
validates :field_96, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (day)") }
validates :field_97, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (month)") }
validates :field_98, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (year)") }
validates :field_98, format: { with: /\A\d{2}\z/, message: I18n.t("validations.setup.startdate.year_not_two_digits") }
validate :validate_data_types
validate :validate_nulls
validate :validate_relevant_collection_window
validate :validate_la_with_local_housing_referral
validate :validate_cannot_be_la_referral_if_general_needs_and_la
validate :validate_leaving_reason_for_renewal
validate :validate_lettings_type_matches_bulk_upload
validate :validate_only_one_housing_needs_type
validate :validate_no_disabled_needs_conjunction
validate :validate_dont_know_disabled_needs_conjunction
validate :validate_no_and_dont_know_disabled_needs_conjunction
validate :validate_owning_org_data_given
validate :validate_owning_org_exists
validate :validate_owning_org_owns_stock
validate :validate_owning_org_permitted
validate :validate_managing_org_data_given
validate :validate_managing_org_exists
validate :validate_managing_org_related
validate :validate_scheme_related
validate :validate_scheme_exists
validate :validate_scheme_data_given
validate :validate_location_related
validate :validate_location_exists
validate :validate_location_data_given
validate :validate_created_by_exists
validate :validate_created_by_related
validate :validate_rent_type
validate :validate_valid_radio_option
inclusion: { in: (1..12).to_a, message: I18n.t("validations.invalid_option", question: "letting type") }, on: :after_log
validates :field_4, presence: { if: proc { [2, 4, 6, 8, 10, 12].include?(field_1) } }, on: :after_log
validates :field_12, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 1 must be a number or the letter R" }, on: :after_log
validates :field_13, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 2 must be a number or the letter R" }, allow_blank: true, on: :after_log
validates :field_14, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 3 must be a number or the letter R" }, allow_blank: true, on: :after_log
validates :field_15, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 4 must be a number or the letter R" }, allow_blank: true, on: :after_log
validates :field_16, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 5 must be a number or the letter R" }, allow_blank: true, on: :after_log
validates :field_17, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 6 must be a number or the letter R" }, allow_blank: true, on: :after_log
validates :field_18, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 7 must be a number or the letter R" }, allow_blank: true, on: :after_log
validates :field_19, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 8 must be a number or the letter R" }, allow_blank: true, on: :after_log
validates :field_96, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (day)") }, on: :after_log
validates :field_97, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (month)") }, on: :after_log
validates :field_98, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (year)") }, on: :after_log
validates :field_98, format: { with: /\A\d{2}\z/, message: I18n.t("validations.setup.startdate.year_not_two_digits") }, on: :after_log
validate :validate_data_types, on: :after_log
validate :validate_nulls, on: :after_log
validate :validate_relevant_collection_window, on: :after_log
validate :validate_la_with_local_housing_referral, on: :after_log
validate :validate_cannot_be_la_referral_if_general_needs_and_la, on: :after_log
validate :validate_leaving_reason_for_renewal, on: :after_log
validate :validate_lettings_type_matches_bulk_upload, on: :after_log
validate :validate_only_one_housing_needs_type, on: :after_log
validate :validate_no_disabled_needs_conjunction, on: :after_log
validate :validate_dont_know_disabled_needs_conjunction, on: :after_log
validate :validate_no_and_dont_know_disabled_needs_conjunction, on: :after_log
validate :validate_owning_org_data_given, on: :after_log
validate :validate_owning_org_exists, on: :after_log
validate :validate_owning_org_owns_stock, on: :after_log
validate :validate_owning_org_permitted, on: :after_log
validate :validate_managing_org_data_given, on: :after_log
validate :validate_managing_org_exists, on: :after_log
validate :validate_managing_org_related, on: :after_log
validate :validate_scheme_related, on: :after_log
validate :validate_scheme_exists, on: :after_log
validate :validate_scheme_data_given, on: :after_log
validate :validate_location_related, on: :after_log
validate :validate_location_exists, on: :after_log
validate :validate_location_data_given, on: :after_log
validate :validate_created_by_exists, on: :after_log
validate :validate_created_by_related, on: :after_log
validate :validate_rent_type, on: :after_log
validate :validate_valid_radio_option, on: :before_log
def self.question_for_field(field)
QUESTIONS[field]
@ -340,9 +340,13 @@ class BulkUpload::Lettings::Year2022::RowParser
return true if blank_row?
super(:before_log)
before_errors = errors.dup
log.valid?
super
super(:after_log)
errors.merge!(before_errors)
log.errors.each do |error|
fields = field_mapping_for_errors[error.attribute] || []
@ -1089,6 +1093,8 @@ private
def voiddate
Date.new(field_91 + 2000, field_90, field_89) if field_91.present? && field_90.present? && field_89.present?
rescue Date::Error
Date.new
end
def majorrepairs
@ -1097,6 +1103,8 @@ private
def mrcdate
Date.new(field_94 + 2000, field_93, field_92) if field_94.present? && field_93.present? && field_92.present?
rescue Date::Error
Date.new
end
def prevloc

116
app/services/bulk_upload/lettings/year2023/row_parser.rb

@ -280,59 +280,59 @@ class BulkUpload::Lettings::Year2023::RowParser
attribute :field_134, :decimal
validates :field_5, presence: { message: I18n.t("validations.not_answered", question: "letting type") },
inclusion: { in: (1..12).to_a, message: I18n.t("validations.invalid_option", question: "letting type") }
validates :field_16, presence: { if: proc { [2, 4, 6, 8, 10, 12].include?(field_5) } }
validates :field_46, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 1 must be a number or the letter R" }
validates :field_52, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 2 must be a number or the letter R" }, allow_blank: true
validates :field_56, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 3 must be a number or the letter R" }, allow_blank: true
validates :field_60, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 4 must be a number or the letter R" }, allow_blank: true
validates :field_64, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 5 must be a number or the letter R" }, allow_blank: true
validates :field_68, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 6 must be a number or the letter R" }, allow_blank: true
validates :field_72, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 7 must be a number or the letter R" }, allow_blank: true
validates :field_76, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 8 must be a number or the letter R" }, allow_blank: true
validates :field_6, presence: { message: I18n.t("validations.not_answered", question: "property renewal") }
validates :field_7, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (day)") }
validates :field_8, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (month)") }
validates :field_9, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (year)") }
validates :field_9, format: { with: /\A\d{2}\z/, message: I18n.t("validations.setup.startdate.year_not_two_digits") }
validate :validate_needs_type_present
validate :validate_data_types
validate :validate_nulls
validate :validate_relevant_collection_window
validate :validate_la_with_local_housing_referral
validate :validate_cannot_be_la_referral_if_general_needs_and_la
validate :validate_leaving_reason_for_renewal
validate :validate_lettings_type_matches_bulk_upload
validate :validate_only_one_housing_needs_type
validate :validate_no_disabled_needs_conjunction
validate :validate_dont_know_disabled_needs_conjunction
validate :validate_no_and_dont_know_disabled_needs_conjunction
validate :validate_owning_org_data_given
validate :validate_owning_org_exists
validate :validate_owning_org_owns_stock
validate :validate_owning_org_permitted
validate :validate_managing_org_data_given
validate :validate_managing_org_exists
validate :validate_managing_org_related
validate :validate_scheme_related
validate :validate_scheme_exists
validate :validate_scheme_data_given
validate :validate_location_related
validate :validate_location_exists
validate :validate_location_data_given
validate :validate_created_by_exists
validate :validate_created_by_related
validate :validate_valid_radio_option
inclusion: { in: (1..12).to_a, message: I18n.t("validations.invalid_option", question: "letting type") }, on: :after_log
validates :field_16, presence: { if: proc { [2, 4, 6, 8, 10, 12].include?(field_5) } }, on: :after_log
validates :field_46, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 1 must be a number or the letter R" }, on: :after_log
validates :field_52, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 2 must be a number or the letter R" }, allow_blank: true, on: :after_log
validates :field_56, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 3 must be a number or the letter R" }, allow_blank: true, on: :after_log
validates :field_60, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 4 must be a number or the letter R" }, allow_blank: true, on: :after_log
validates :field_64, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 5 must be a number or the letter R" }, allow_blank: true, on: :after_log
validates :field_68, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 6 must be a number or the letter R" }, allow_blank: true, on: :after_log
validates :field_72, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 7 must be a number or the letter R" }, allow_blank: true, on: :after_log
validates :field_76, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 8 must be a number or the letter R" }, allow_blank: true, on: :after_log
validates :field_6, presence: { message: I18n.t("validations.not_answered", question: "property renewal") }, on: :after_log
validates :field_7, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (day)") }, on: :after_log
validates :field_8, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (month)") }, on: :after_log
validates :field_9, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (year)") }, on: :after_log
validates :field_9, format: { with: /\A\d{2}\z/, message: I18n.t("validations.setup.startdate.year_not_two_digits") }, on: :after_log
validate :validate_needs_type_present, on: :after_log
validate :validate_data_types, on: :after_log
validate :validate_nulls, on: :after_log
validate :validate_relevant_collection_window, on: :after_log
validate :validate_la_with_local_housing_referral, on: :after_log
validate :validate_cannot_be_la_referral_if_general_needs_and_la, on: :after_log
validate :validate_leaving_reason_for_renewal, on: :after_log
validate :validate_lettings_type_matches_bulk_upload, on: :after_log
validate :validate_only_one_housing_needs_type, on: :after_log
validate :validate_no_disabled_needs_conjunction, on: :after_log
validate :validate_dont_know_disabled_needs_conjunction, on: :after_log
validate :validate_no_and_dont_know_disabled_needs_conjunction, on: :after_log
validate :validate_owning_org_data_given, on: :after_log
validate :validate_owning_org_exists, on: :after_log
validate :validate_owning_org_owns_stock, on: :after_log
validate :validate_owning_org_permitted, on: :after_log
validate :validate_managing_org_data_given, on: :after_log
validate :validate_managing_org_exists, on: :after_log
validate :validate_managing_org_related, on: :after_log
validate :validate_scheme_related, on: :after_log
validate :validate_scheme_exists, on: :after_log
validate :validate_scheme_data_given, on: :after_log
validate :validate_location_related, on: :after_log
validate :validate_location_exists, on: :after_log
validate :validate_location_data_given, on: :after_log
validate :validate_created_by_exists, on: :after_log
validate :validate_created_by_related, on: :after_log
validate :validate_valid_radio_option, on: :before_log
def self.question_for_field(field)
QUESTIONS[field]
@ -343,9 +343,13 @@ class BulkUpload::Lettings::Year2023::RowParser
return true if blank_row?
super(:before_log)
before_errors = errors.dup
log.valid?
super
super(:after_log)
errors.merge!(before_errors)
log.errors.each do |error|
fields = field_mapping_for_errors[error.attribute] || []
@ -1313,10 +1317,14 @@ private
def mrcdate
Date.new(field_38 + 2000, field_37, field_36) if field_38.present? && field_37.present? && field_36.present?
rescue Date::Error
Date.new
end
def voiddate
Date.new(field_35 + 2000, field_34, field_33) if field_35.present? && field_34.present? && field_33.present?
rescue Date::Error
Date.new
end
def first_time_property_let_as_social_housing

10
config/initializers/feature_toggle.rb → app/services/feature_toggle.rb

@ -4,10 +4,6 @@ class FeatureToggle
Rails.env.production? || Rails.env.test? || Rails.env.staging?
end
def self.startdate_collection_window_validation_enabled?
Rails.env.production? || Rails.env.test?
end
def self.startdate_two_week_validation_enabled?
Rails.env.production? || Rails.env.test? || Rails.env.staging?
end
@ -36,7 +32,11 @@ class FeatureToggle
true
end
def self.bulk_upload_logs?
def self.bulk_upload_lettings_logs?
true
end
def self.bulk_upload_sales_logs?
!Rails.env.production?
end

37
app/services/imports/lettings_logs_import_service.rb

@ -202,6 +202,14 @@ module Imports
attributes["first_time_property_let_as_social_housing"] = first_time_let(attributes["rsnvac"])
attributes["declaration"] = declaration(xml_doc)
attributes["uprn"] = string_or_nil(xml_doc, "UPRN")
attributes["uprn_known"] = attributes["uprn"].present? ? 1 : 0
attributes["uprn_confirmed"] = attributes["uprn"].present? ? 1 : 0
attributes["address_line1"] = string_or_nil(xml_doc, "AddressLine1")
attributes["address_line2"] = string_or_nil(xml_doc, "AddressLine2")
attributes["town_or_city"] = string_or_nil(xml_doc, "TownCity")
attributes["county"] = string_or_nil(xml_doc, "County")
set_partial_charges_to_zero(attributes)
# Supported Housing fields
@ -211,15 +219,19 @@ module Imports
schemes = Scheme.where(old_visible_id: scheme_old_visible_id, owning_organisation_id: attributes["owning_organisation_id"])
location = Location.find_by(old_visible_id: location_old_visible_id, scheme: schemes)
raise "No matching location for scheme #{scheme_old_visible_id} and location #{location_old_visible_id} (visible IDs)" if location.nil?
if location.nil? && [location_old_visible_id, scheme_old_visible_id].all?(&:present?) && previous_status != "saved"
raise "No matching location for scheme #{scheme_old_visible_id} and location #{location_old_visible_id} (visible IDs)"
end
# Set the scheme via location, because the scheme old visible ID can be duplicated at import
attributes["location_id"] = location.id
attributes["scheme_id"] = location.scheme.id
attributes["sheltered"] = unsafe_string_as_integer(xml_doc, "Q1e")
attributes["chcharge"] = safe_string_as_decimal(xml_doc, "Q18b")
attributes["household_charge"] = household_charge(xml_doc)
attributes["is_carehome"] = is_carehome(location.scheme)
if location.present?
# Set the scheme via location, because the scheme old visible ID can be duplicated at import
attributes["location_id"] = location.id
attributes["scheme_id"] = location.scheme.id
attributes["sheltered"] = unsafe_string_as_integer(xml_doc, "Q1e")
attributes["chcharge"] = safe_string_as_decimal(xml_doc, "Q18b")
attributes["household_charge"] = household_charge(xml_doc)
attributes["is_carehome"] = is_carehome(location.scheme)
end
end
# Handles confidential schemes
@ -333,6 +345,13 @@ module Imports
return save_lettings_log(attributes, previous_status)
end
if lettings_log.errors.of_kind?(:uprn, :uprn_error)
@logger.warn("Log #{lettings_log.old_id}: Setting uprn_known to no with error: #{lettings_log.errors[:uprn].join(', ')}")
@logs_overridden << lettings_log.old_id
attributes["uprn_known"] = 0
return save_lettings_log(attributes, previous_status)
end
@logger.error("Log #{lettings_log.old_id}: Failed to import")
lettings_log.errors.each do |error|
@logger.error("Validation error: Field #{error.attribute}:")
@ -360,7 +379,7 @@ module Imports
end
def fields_not_present_in_softwire_data
%w[majorrepairs illness_type_0 tshortfall_known pregnancy_value_check retirement_value_check rent_value_check net_income_value_check major_repairs_date_value_check void_date_value_check carehome_charges_value_check housingneeds_type housingneeds_other created_by]
%w[majorrepairs illness_type_0 tshortfall_known pregnancy_value_check retirement_value_check rent_value_check net_income_value_check major_repairs_date_value_check void_date_value_check carehome_charges_value_check housingneeds_type housingneeds_other created_by uprn_known uprn_confirmed]
end
def check_status_completed(lettings_log, previous_status)

41
app/services/imports/sales_logs_import_service.rb

@ -126,10 +126,6 @@ module Imports
attributes["postcode_full"] = parse_postcode(string_or_nil(xml_doc, "Q14Postcode"))
attributes["pcodenk"] = 0 if attributes["postcode_full"].present? # known if given
attributes["soctenant"] = 0 if attributes["ownershipsch"] == 1
attributes["ethnic_group2"] = nil # 23/24 variable
attributes["ethnicbuy2"] = nil # 23/24 variable
attributes["prevshared"] = nil # 23/24 variable
attributes["staircasesale"] = nil # 23/24 variable
attributes["previous_la_known"] = 1 if attributes["prevloc"].present?
if attributes["la"].present?
@ -158,6 +154,27 @@ module Imports
attributes["buyer_livein_value_check"] = 0
attributes["percentage_discount_value_check"] = 0
# 2023/34 attributes
attributes["uprn"] = string_or_nil(xml_doc, "UPRN")
attributes["uprn_known"] = attributes["uprn"].present? ? 1 : 0
attributes["uprn_confirmed"] = attributes["uprn"].present? ? 1 : 0
attributes["address_line1"] = string_or_nil(xml_doc, "AddressLine1")
attributes["address_line2"] = string_or_nil(xml_doc, "AddressLine2")
attributes["town_or_city"] = string_or_nil(xml_doc, "TownCity")
attributes["county"] = string_or_nil(xml_doc, "County")
attributes["proplen_asked"] = 0 if attributes["proplen"]&.positive?
attributes["proplen_asked"] = 1 if attributes["proplen"]&.zero?
attributes["prevshared"] = unsafe_string_as_integer(xml_doc, "PREVSHARED")
attributes["ethnicbuy2"] = unsafe_string_as_integer(xml_doc, "P2Eth")
attributes["ethnic_group2"] = ethnic_group(attributes["ethnicbuy2"])
attributes["nationalbuy2"] = unsafe_string_as_integer(xml_doc, "P2Nat")
attributes["buy2living"] = unsafe_string_as_integer(xml_doc, "BUY2LIVEIN")
attributes["staircasesale"] = unsafe_string_as_integer(xml_doc, "STAIRCASESALE")
attributes["prevtenbuy2"] = unsafe_string_as_integer(xml_doc, "PREVTENBUY2")
# Sets the log creator
owner_id = meta_field_value(xml_doc, "owner-user-id").strip
if owner_id.present?
@ -233,6 +250,11 @@ module Imports
@logs_overridden << sales_log.old_id
attributes.delete("mortgage")
save_sales_log(attributes, previous_status)
elsif sales_log.errors.of_kind?(:uprn, :uprn_error)
@logger.warn("Log #{sales_log.old_id}: Setting uprn_known to no with error: #{sales_log.errors[:uprn].join(', ')}")
@logs_overridden << sales_log.old_id
attributes["uprn_known"] = 0
save_sales_log(attributes, previous_status)
else
@logger.error("Log #{sales_log.old_id}: Failed to import")
sales_log.errors.each do |error|
@ -282,7 +304,9 @@ module Imports
student_not_child_value_check
discounted_sale_value_check
buyer_livein_value_check
percentage_discount_value_check]
percentage_discount_value_check
uprn_known
uprn_confirmed]
end
def check_status_completed(sales_log, previous_status)
@ -461,12 +485,14 @@ module Imports
safe_string_as_decimal(xml_doc, "Q29MonthlyCharges")
when 2
safe_string_as_decimal(xml_doc, "Q37MonthlyCharges")
when 3
safe_string_as_decimal(xml_doc, "Q44MonthlyCharges")
end
end
def ownership_from_type(attributes)
case attributes["type"]
when 2, 24, 18, 16, 28, 31, 30
when 2, 24, 18, 16, 28, 31, 30, 32
1 # shared ownership
when 8, 14, 27, 9, 29, 21, 22
2 # discounted ownership
@ -562,6 +588,9 @@ module Imports
attributes["relat2"] ||= "R"
attributes["inc2mort"] ||= 3
attributes["buy2livein"] ||= 1 unless attributes["ownershipsch"] == 3
attributes["ethnic_group2"] ||= 17
attributes["ethnicbuy2"] ||= 17
attributes["nationalbuy2"] ||= 13
end
# other household members characteristics

9
app/services/imports/scheme_location_import_service.rb

@ -77,7 +77,7 @@ module Imports
attributes["scheme_type"] = safe_string_as_integer(xml_doc, "scheme-type")
registered_under_care_act = safe_string_as_integer(xml_doc, "reg-home-type")
attributes["registered_under_care_act"] = registered_under_care_act&.zero? ? nil : registered_under_care_act
attributes["support_type"] = safe_string_as_integer(xml_doc, "support-type")
attributes["support_type"] = support_type(xml_doc)
attributes["intended_stay"] = string_or_nil(xml_doc, "intended-stay")
attributes["mobility_type"] = string_or_nil(xml_doc, "mobility-type")
attributes["primary_client_group"] = string_or_nil(xml_doc, "client-group-1")
@ -205,5 +205,12 @@ module Imports
date = string_or_nil(xml_doc, attribute)
Time.zone.parse(date) if date
end
def support_type(xml_doc)
type = safe_string_as_integer(xml_doc, "support-type")
return unless type
Scheme::SUPPORT_TYPE.value?(type) ? type : 0
end
end
end

2
app/views/bulk_upload_lettings_results/summary.html.erb

@ -20,7 +20,7 @@
<% end %>
<% c.with_tab(label: "Full error report") do %>
<% @bulk_upload.bulk_upload_errors.group_by(&:row).each do |_row, errors_for_row| %>
<% @bulk_upload.bulk_upload_errors.order_by_cell.group_by(&:row).each do |_row, errors_for_row| %>
<%= render BulkUploadErrorRowComponent.new(bulk_upload_errors: errors_for_row) %>
<% end %>
<% end %>

10
app/views/form/_checkbox_question.html.erb

@ -6,14 +6,14 @@
hint: { text: question.hint_text&.html_safe } do %>
<% after_divider = false %>
<% question.displayed_answer_options(@log).map do |key, options| %>
<% question.displayed_answer_options(@log).map do |key, option| %>
<% if key.starts_with?("divider") %>
<% after_divider = true %>
<%= f.govuk_check_box_divider %>
<% after_divider = true %>
<%= f.govuk_check_box_divider %>
<% else %>
<%= f.govuk_check_box question.id, key,
label: { text: options["value"] },
hint: { text: options["hint"] },
label: { text: option["value"] },
hint: { text: option["hint"] },
checked: @log[key] == 1,
exclusive: after_divider,
**stimulus_html_attributes(question) %>

25
app/views/layouts/_collection_resources.html.erb

@ -9,6 +9,21 @@
href: download_23_24_lettings_form_path,
metadata: "PDF, 278 KB, 8 pages",
},
{
name: "Lettings bulk upload template (2023/24) *new question ordering*",
href: download_23_24_lettings_bulk_upload_template_path,
metadata: "Microsoft Excel, 15 KB",
},
{
name: "Lettings bulk upload template (2023/24)",
href: download_23_24_lettings_bulk_upload_legacy_template_path,
metadata: "Microsoft Excel, 15 KB",
},
{
name: "Lettings bulk upload specification (2023/24)",
href: download_23_24_lettings_bulk_upload_specification_path,
metadata: "Microsoft Excel, 90 KB",
},
]) %>
<h2 class="govuk-body-l govuk-!-margin-bottom-3">Lettings 22/23</h2>
@ -18,6 +33,16 @@
href: "https://core.communities.gov.uk/public/download/guides-and-manuals/2022-23%20Lettings%20paper%20form.pdf?download-format=pdf",
metadata: "PDF, 654 KB, 4 pages",
},
{
name: "Lettings bulk upload template (2022/23)",
href: download_22_23_lettings_bulk_upload_template_path,
metadata: "Microsoft Excel, 36 KB",
},
{
name: "Lettings bulk upload specification (2022/23)",
href: download_22_23_lettings_bulk_upload_specification_path,
metadata: "Microsoft Excel, 53 KB",
},
]) %>
<h2 class="govuk-body-l govuk-!-margin-bottom-3">Sales 23/24</h2>

8
app/views/logs/index.html.erb

@ -55,8 +55,12 @@
<%= govuk_button_to "Create a new sales log", sales_logs_path, class: "govuk-!-margin-right-6" %>
<% end %>
<% if FeatureToggle.bulk_upload_logs? %>
<%= govuk_button_link_to "Upload #{log_type_for_controller(controller)} logs in bulk", bulk_upload_path_for_controller(controller, id: "start"), secondary: true %>
<% if FeatureToggle.bulk_upload_lettings_logs? && log_type_for_controller(controller) == "lettings" %>
<%= govuk_button_link_to "Upload lettings logs in bulk", bulk_upload_path_for_controller(controller, id: "start"), secondary: true %>
<% end %>
<% if FeatureToggle.bulk_upload_sales_logs? && log_type_for_controller(controller) == "sales" %>
<%= govuk_button_link_to "Upload sales logs in bulk", bulk_upload_path_for_controller(controller, id: "start"), secondary: true %>
<% end %>
</div>
<% end %>

49
app/views/merge_requests/organisations.html.erb

@ -0,0 +1,49 @@
<% content_for :before_content do %>
<% title = "Tell us if your organisation is merging" %>
<% content_for :title, title %>
<%= govuk_back_link href: merge_request_organisation_path(id: @merge_request.requesting_organisation_id) %>
<% end %>
<h2 class="govuk-heading-l">Which organisations are merging?</h2>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<p class="govuk-body">Add all organisations to be merged - we have already added your own.</p>
<%= form_with model: @merge_request, url: organisations_merge_request_path, method: :patch do |f| %>
<%= f.govuk_error_summary %>
<p class="govuk-body">Start typing to search</p>
<%= render partial: "organisation_relationships/related_organisation_select_question", locals: {
field: :merging_organisation,
question: Form::Question.new("", { "answer_options" => @answer_options }, nil),
f:,
} %>
<%= f.govuk_submit "Add organisation", classes: "govuk-button--secondary" %>
<%= govuk_table do |table| %>
<% @merge_request.merging_organisations.each do |merging_organisation| %>
<%= table.body do |body| %>
<%= body.row do |row| %>
<% row.cell(text: merging_organisation.name) %>
<% row.cell(html_attributes: {
scope: "row",
class: "govuk-!-text-align-right",
}) do %>
<% if @merge_request.requesting_organisation != merging_organisation %>
<%= govuk_link_to("Remove", organisations_remove_merge_request_path(merge_request: { merging_organisation: merging_organisation.id })) %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<%= form_with model: @merge_request, url: merge_request_path(id: @merge_request.id), method: :patch do |f| %>
<%= govuk_details(summary_text: "I cannot find an organisation on the list") do %>
<%= f.govuk_text_area :other_merging_organisations, label: { text: "Other organisations" }, hint: { text: "List other organisations that are part of the merge but not registered on CORE." }, rows: 9 %>
<% end %>
<% if @merge_request.merging_organisations.count > 1 %>
<%= f.govuk_submit "Continue" %>
<% end %>
<% end %>
</div>

9
app/views/organisations/merge.html.erb → app/views/organisations/merge_request.html.erb

@ -40,9 +40,10 @@
</ul>
<%= govuk_warning_text text: "You will not be able to submit your request without the above information. Do not start the form until you have obtained all of the information. " %>
<%= govuk_start_button(
text: "Start now",
href: "#",
) %>
<%= form_for @merge_request, url: merge_requests_path do |f| %>
<%= f.hidden_field :requesting_organisation_id, value: @organisation.id %>
<%= f.submit "Start now", class: "govuk-button govuk-button--start" %>
<% end %>
</div>
</div>

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

@ -36,7 +36,7 @@
<% end %>
<% end %>
<% if FeatureToggle.merge_organisations_enabled? %>
<p>Is your organisation merging with another? <%= govuk_link_to "Let us know using this form", merge_organisation_path %></p>
<p>Is your organisation merging with another? <%= govuk_link_to "Let us know using this form", merge_request_organisation_path %></p>
<% end %>
</div>

7
config/forms/2021_2022.json

@ -24,6 +24,7 @@
"header": "Do you know the property’s postcode?",
"hint_text": "",
"type": "radio",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": {
"1": {
"value": "Yes"
@ -54,6 +55,7 @@
"hint_text": "",
"type": "text",
"width": 5,
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"inferred_answers": {
"la": {
"is_la_inferred": true
@ -82,6 +84,7 @@
"header": "What is the local authority of the property?",
"hint_text": "",
"type": "select",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": {
"": "Select an option",
"E07000223": "Adur",
@ -6482,6 +6485,7 @@
"header": "Do you know the postcode of the household’s last settled accommodation?",
"hint_text": "This is also known as the household’s ‘last settled home’.",
"type": "radio",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": {
"1": {
"value": "Yes"
@ -6512,6 +6516,7 @@
"hint_text": "",
"type": "text",
"width": 5,
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"inferred_answers": {
"prevloc": {
"is_previous_la_inferred": true
@ -6535,6 +6540,7 @@
"header": "Do you know the local authority of the household’s last settled accommodation?",
"hint_text": "This is also known as the household’s ‘last settled home’.",
"type": "radio",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"hidden_in_check_answers": {
"depends_on": [
{
@ -6564,6 +6570,7 @@
"header": "Select a local authority",
"hint_text": "Select ‘Northern Ireland’, ‘Scotland’, ‘Wales’ or ‘Outside the UK’ if the household’s last settled home was outside England.",
"type": "select",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": {
"": "Select an option",
"S12000033": "Aberdeen City",

7
config/forms/2022_2023.json

@ -24,6 +24,7 @@
"header": "Do you know the property’s postcode?",
"hint_text": "",
"type": "radio",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": {
"1": {
"value": "Yes"
@ -54,6 +55,7 @@
"hint_text": "",
"type": "text",
"width": 5,
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"inferred_answers": {
"la": {
"is_la_inferred": true
@ -82,6 +84,7 @@
"header": "What is the local authority of the property?",
"hint_text": "",
"type": "select",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": {
"": "Select an option",
"E07000223": "Adur",
@ -6435,6 +6438,7 @@
"header": "Do you know the postcode of the household’s last settled accommodation?",
"hint_text": "This is also known as the household’s ‘last settled home’.",
"type": "radio",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": {
"1": {
"value": "Yes"
@ -6465,6 +6469,7 @@
"hint_text": "",
"type": "text",
"width": 5,
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"inferred_answers": {
"prevloc": {
"is_previous_la_inferred": true
@ -6488,6 +6493,7 @@
"header": "Do you know the local authority of the household’s last settled accommodation?",
"hint_text": "This is also known as the household’s ‘last settled home’.",
"type": "radio",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"hidden_in_check_answers": {
"depends_on": [
{
@ -6517,6 +6523,7 @@
"header": "Select a local authority",
"hint_text": "Select ‘Northern Ireland’, ‘Scotland’, ‘Wales’ or ‘Outside the UK’ if the household’s last settled home was outside England.",
"type": "select",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": {
"": "Select an option",
"S12000033": "Aberdeen City",

2
config/initializers/filter_parameter_logging.rb

@ -2,5 +2,5 @@
# Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += %i[
passw secret token crypt salt certificate otp ssn
passw secret token crypt salt certificate otp ssn reset_password_token
]

5
config/locales/en.yml

@ -369,7 +369,7 @@ en:
over_20: "The lead tenant must be under 20 as you told us their housing situation immediately before this letting was a children’s home or foster care"
ecstat:
retired_over_70: "Person %{person_num} must be retired if over 70"
child_under_16: "Person’s %{person_num} working situation must be ’child under 16‘ as you told us they’re under 16"
child_under_16: "Person %{person_num}’s working situation must be ‘child under 16’ as you told us they’re under 16"
child_over_16: "Answer cannot be ‘child under 16’ as you told us the person %{person_num} is older than 16"
not_student_16_19: "Person’s %{person_num} working situation must be full-time student or prefers not to say as you told us they’re between 16 and 19."
student_16_19:
@ -508,6 +508,9 @@ en:
discounted_ownership_value: "Mortgage, deposit, and grant total must equal %{value_with_discount}"
monthly_rent:
higher_than_expected: "Basic monthly rent must be between £0.00 and £9,999.00"
merge_request:
organisation_part_of_another_merge: "This organisation is part of another merge - select a different one"
organisation_not_selected: "Select an organisation from the search list"
soft_validations:
net_income:

21
config/routes.rb

@ -35,9 +35,17 @@ Rails.application.routes.draw do
get "/accessibility-statement", to: "content#accessibility_statement"
get "/privacy-notice", to: "content#privacy_notice"
get "/data-sharing-agreement", to: "content#data_sharing_agreement"
get "/download-23-24-lettings-form", to: "start#download_23_24_lettings_form"
get "/download-23-24-lettings-bulk-upload-template", to: "start#download_23_24_lettings_bulk_upload_template"
get "/download-23-24-lettings-bulk-upload-legacy-template", to: "start#download_23_24_lettings_bulk_upload_legacy_template"
get "/download-23-24-lettings-bulk-upload-specification", to: "start#download_23_24_lettings_bulk_upload_specification"
get "/download-22-23-lettings-bulk-upload-template", to: "start#download_22_23_lettings_bulk_upload_template"
get "/download-22-23-lettings-bulk-upload-specification", to: "start#download_22_23_lettings_bulk_upload_specification"
get "/download-23-24-sales-form", to: "start#download_23_24_sales_form"
get "/download-22-23-sales-form", to: "start#download_22_23_sales_form"
get "/download-23-24-lettings-form", to: "start#download_23_24_lettings_form"
resource :account, only: %i[show edit], controller: "users" do
get "edit/password", to: "users#edit_password"
@ -119,7 +127,16 @@ Rails.application.routes.draw do
get "managing-agents/remove", to: "organisation_relationships#remove_managing_agent"
post "managing-agents", to: "organisation_relationships#create_managing_agent"
delete "managing-agents", to: "organisation_relationships#delete_managing_agent"
get "merge", to: "organisations#merge"
get "merge-request", to: "organisations#merge_request"
end
end
resources :merge_requests, path: "/merge-request" do
member do
get "organisations"
patch "organisations", to: "merge_requests#update_organisations"
get "organisations/remove", to: "merge_requests#remove_merging_organisation"
get "absorbing-organisation"
end
end

9
db/migrate/20230412111338_add_merge_requests_table.rb

@ -0,0 +1,9 @@
class AddMergeRequestsTable < ActiveRecord::Migration[7.0]
def change
create_table :merge_requests do |t|
t.integer :requesting_organisation_id
t.text :other_merging_organisations
t.timestamps
end
end
end

9
db/migrate/20230413135407_add_merge_organisations.rb

@ -0,0 +1,9 @@
class AddMergeOrganisations < ActiveRecord::Migration[7.0]
def change
create_table :merge_request_organisations do |t|
t.integer :merge_request_id, foreign_key: true
t.integer :merging_organisation_id, foreign_key: true
t.timestamps
end
end
end

5
db/migrate/20230418095819_add_status_to_merge_request.rb

@ -0,0 +1,5 @@
class AddStatusToMergeRequest < ActiveRecord::Migration[7.0]
def change
add_column :merge_requests, :status, :integer
end
end

17
db/schema.rb

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_04_12_143245) do
ActiveRecord::Schema[7.0].define(version: 2023_04_18_095819) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -354,6 +354,21 @@ ActiveRecord::Schema[7.0].define(version: 2023_04_12_143245) do
t.string "collection"
end
create_table "merge_request_organisations", force: :cascade do |t|
t.integer "merge_request_id"
t.integer "merging_organisation_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "merge_requests", force: :cascade do |t|
t.integer "requesting_organisation_id"
t.text "other_merging_organisations"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "status"
end
create_table "organisation_relationships", force: :cascade do |t|
t.integer "child_organisation_id"
t.integer "parent_organisation_id"

BIN
public/files/bulk-upload-lettings-legacy-template-2023-24.xlsx

Binary file not shown.

6
spec/factories/lettings_log.rb

@ -158,6 +158,12 @@ FactoryBot.define do
sheltered { 0 }
household_charge { 0 }
end
trait :sheltered_housing do
needstype { 2 }
end
trait :startdate_today do
startdate { Time.zone.today }
end
created_at { Time.zone.today }
updated_at { Time.zone.today }
end

24
spec/factories/sales_log.rb

@ -10,6 +10,30 @@ FactoryBot.define do
type { 8 }
saledate { Time.utc(2023, 2, 2, 10, 36, 49) }
end
trait :shared_ownership do
ownershipsch { 1 }
type { 30 }
end
trait :privacy_notice_seen do
privacynotice { 1 }
end
trait :saledate_today do
saledate { Time.zone.today }
end
trait :shared_ownership_setup_complete do
saledate_today
ownershipsch { 1 }
type { 30 }
jointpur { 2 }
end
trait :outright_sale_setup_complete do
saledate_today
ownershipsch { 3 }
type { 10 }
companybuy { 2 }
buylivein { 1 }
jointpur { 2 }
end
trait :completed do
ownershipsch { 2 }
type { 8 }

4
spec/fixtures/files/lettings_logs_download.csv vendored

@ -1,2 +1,2 @@
id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,rent_value_check,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate
{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,No,DLUHC,DLUHC,2021,Supported housing,,2 October 2021,London Affordable Rent,,,,,,,,,,,,,,,,,,,,No,,,,,No,Westminster,E09000033,,SE1 1TE,,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,6,{scheme_code},{scheme_service_name},{scheme_sensitive},Missing,No,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,Bungalow,Fitted with equipment and adaptations,Westminster,{location_startdate}
id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,rent_value_check,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate
{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,No,DLUHC,DLUHC,2021,,Supported housing,,2 October 2021,London Affordable Rent,,,,,,,,,,,,,,,,,,,,No,,,,,No,Westminster,E09000033,,SE1 1TE,,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,6,{scheme_code},{scheme_service_name},{scheme_sensitive},Missing,No,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,Bungalow,Fitted with equipment and adaptations,Westminster,{location_startdate}

1 id status created_at updated_at created_by_name is_dpo owning_organisation_name managing_organisation_name collection_start_year rent_value_check needstype renewal startdate rent_type_detail irproduct_other tenancycode propcode age1 sex1 ecstat1 hhmemb relat2 age2 sex2 retirement_value_check ecstat2 armedforces leftreg illness housingneeds_a housingneeds_b housingneeds_c housingneeds_h is_previous_la_inferred prevloc_label prevloc illness_type_1 illness_type_2 is_la_inferred la_label la postcode_known postcode_full previous_la_known wchair preg_occ cbl earnings incfreq net_income_value_check benefits hb period brent scharge pscharge supcharg tcharge offered layear ppostcode_full mrcdate declaration ethnic national prevten age3 sex3 ecstat3 age4 sex4 ecstat4 age5 sex5 ecstat5 age6 sex6 ecstat6 age7 sex7 ecstat7 age8 sex8 ecstat8 homeless underoccupation_benefitcap reservist startertenancy tenancylength tenancy rsnvac unittype_gn beds waityear reasonpref chr cap reasonother housingneeds_f housingneeds_g illness_type_3 illness_type_4 illness_type_8 illness_type_5 illness_type_6 illness_type_7 illness_type_9 illness_type_10 rp_homeless rp_insan_unsat rp_medwel rp_hardship rp_dontknow tenancyother property_owner_organisation property_manager_organisation purchaser_code reason majorrepairs hbrentshortfall property_relet incref first_time_property_let_as_social_housing unitletas builtype voiddate renttype lettype totchild totelder totadult net_income_known nocharge is_carehome household_charge referral tshortfall chcharge ppcodenk age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known ethnic_group letting_allocation_unknown details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 has_benefits wrent wscharge wpschrge wsupchrg wtcharge wtshortfall refused housingneeds wchchrg newprop relat3 relat4 relat5 relat6 relat7 relat8 old_form_id lar irproduct old_id joint tshortfall_known sheltered pregnancy_value_check hhtype new_old vacdays major_repairs_date_value_check void_date_value_check housingneeds_type housingneeds_other unresolved updated_by_id uprn uprn_known uprn_confirmed address_line1 address_line2 town_or_city county carehome_charges_value_check status_cache unittype_sh scheme_code scheme_service_name scheme_sensitive scheme_type scheme_registered_under_care_act scheme_owning_organisation_name scheme_primary_client_group scheme_has_other_client_group scheme_secondary_client_group scheme_support_type scheme_intended_stay scheme_created_at location_code location_postcode location_name location_units location_type_of_unit location_mobility_type location_admin_district location_startdate
2 {id} in_progress 2022-02-08 16:52:15 +0000 2022-02-08 16:52:15 +0000 Danny Rojas No DLUHC DLUHC 2021 Supported housing 2 October 2021 London Affordable Rent No No Westminster E09000033 SE1 1TE No 2 8 0 0 0 0 0 0 9 1 not_started 6 {scheme_code} {scheme_service_name} {scheme_sensitive} Missing No DLUHC {scheme_primary_client_group} {scheme_secondary_client_group} {scheme_support_type} {scheme_intended_stay} 2021-04-01 00:00:00 +0100 {location_code} SE1 1TE Downing Street 20 Bungalow Fitted with equipment and adaptations Westminster {location_startdate}

4
spec/fixtures/files/lettings_logs_download_codes_only.csv vendored

@ -1,2 +1,2 @@
id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,rent_value_check,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate
{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,false,DLUHC,DLUHC,2021,2,,2 October 2021,2,,,,,,,,,,,,,,,,,,,,false,,,,,false,Westminster,E09000033,,SE1 1TE,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,6,{scheme_code},{scheme_service_name},{scheme_sensitive},0,1,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,6,A,Westminster,{location_startdate}
id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,rent_value_check,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate
{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,false,DLUHC,DLUHC,2021,,2,,2 October 2021,2,,,,,,,,,,,,,,,,,,,,false,,,,,false,Westminster,E09000033,,SE1 1TE,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,6,{scheme_code},{scheme_service_name},{scheme_sensitive},0,1,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,6,A,Westminster,{location_startdate}

1 id status created_at updated_at created_by_name is_dpo owning_organisation_name managing_organisation_name collection_start_year rent_value_check needstype renewal startdate rent_type_detail irproduct_other tenancycode propcode age1 sex1 ecstat1 hhmemb relat2 age2 sex2 retirement_value_check ecstat2 armedforces leftreg illness housingneeds_a housingneeds_b housingneeds_c housingneeds_h is_previous_la_inferred prevloc_label prevloc illness_type_1 illness_type_2 is_la_inferred la_label la postcode_known postcode_full previous_la_known wchair preg_occ cbl earnings incfreq net_income_value_check benefits hb period brent scharge pscharge supcharg tcharge offered layear ppostcode_full mrcdate declaration ethnic national prevten age3 sex3 ecstat3 age4 sex4 ecstat4 age5 sex5 ecstat5 age6 sex6 ecstat6 age7 sex7 ecstat7 age8 sex8 ecstat8 homeless underoccupation_benefitcap reservist startertenancy tenancylength tenancy rsnvac unittype_gn beds waityear reasonpref chr cap reasonother housingneeds_f housingneeds_g illness_type_3 illness_type_4 illness_type_8 illness_type_5 illness_type_6 illness_type_7 illness_type_9 illness_type_10 rp_homeless rp_insan_unsat rp_medwel rp_hardship rp_dontknow tenancyother property_owner_organisation property_manager_organisation purchaser_code reason majorrepairs hbrentshortfall property_relet incref first_time_property_let_as_social_housing unitletas builtype voiddate renttype lettype totchild totelder totadult net_income_known nocharge is_carehome household_charge referral tshortfall chcharge ppcodenk age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known ethnic_group letting_allocation_unknown details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 has_benefits wrent wscharge wpschrge wsupchrg wtcharge wtshortfall refused housingneeds wchchrg newprop relat3 relat4 relat5 relat6 relat7 relat8 old_form_id lar irproduct old_id joint tshortfall_known sheltered pregnancy_value_check hhtype new_old vacdays major_repairs_date_value_check void_date_value_check housingneeds_type housingneeds_other unresolved updated_by_id uprn uprn_known uprn_confirmed address_line1 address_line2 town_or_city county carehome_charges_value_check status_cache unittype_sh scheme_code scheme_service_name scheme_sensitive scheme_type scheme_registered_under_care_act scheme_owning_organisation_name scheme_primary_client_group scheme_has_other_client_group scheme_secondary_client_group scheme_support_type scheme_intended_stay scheme_created_at location_code location_postcode location_name location_units location_type_of_unit location_mobility_type location_admin_district location_startdate
2 {id} in_progress 2022-02-08 16:52:15 +0000 2022-02-08 16:52:15 +0000 Danny Rojas false DLUHC DLUHC 2021 2 2 October 2021 2 false false Westminster E09000033 SE1 1TE 2 2 8 0 0 0 0 0 0 9 1 not_started 6 {scheme_code} {scheme_service_name} {scheme_sensitive} 0 1 DLUHC {scheme_primary_client_group} {scheme_secondary_client_group} {scheme_support_type} {scheme_intended_stay} 2021-04-01 00:00:00 +0100 {location_code} SE1 1TE Downing Street 20 6 A Westminster {location_startdate}

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

@ -411,6 +411,7 @@
"hint_text": "Type ahead to filter the options",
"type": "select",
"check_answer_label": "Accessible Select",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": {
"": "Select an option",
"E07000223": "Adur",
@ -473,6 +474,7 @@
"hint_text": "Type ahead to filter the options",
"type": "select",
"check_answer_label": "Accessible Select",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": {
"": "Select an option",
"E07000223": "Adur",
@ -500,6 +502,7 @@
"header": "Do you know the property’s postcode?",
"hint_text": "",
"type": "radio",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": {
"1": {
"value": "Yes"
@ -521,6 +524,7 @@
"hint_text": "",
"type": "text",
"width": 5,
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"inferred_answers": {
"la": {
"is_la_inferred": true
@ -544,6 +548,7 @@
"header": "Do you know what local authority the property is located in?",
"hint_text": "",
"type": "radio",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"answer_options": {
"0": {
"value": "No"
@ -1153,6 +1158,7 @@
"header": "Postcode for the previous accommodation",
"hint_text": "If the household has moved from settled accommodation immediately prior to being re-housed",
"type": "text",
"disable_clearing_if_not_routed_or_dynamic_answer_options": true,
"width": 5
}
}

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

@ -131,6 +131,11 @@
<_2cYears/>
</Group>
<Group>
<UPRN>12345678</UPRN>
<AddressLine1/>
<AddressLine2/>
<TownCity/>
<County/>
<Q28pc override-field="">SR8 3HF</Q28pc>
<Q28Auth>Durham</Q28Auth>
<Q28ONS>E06000047</Q28ONS>

6
spec/fixtures/imports/sales_logs/outright_sale_sales_log.xml vendored

@ -32,6 +32,11 @@
<Q11Bedrooms override-field="">1</Q11Bedrooms>
<Q12PropertyType>1 Flat or maisonette</Q12PropertyType>
<Q13BuildingType>1 Purpose built</Q13BuildingType>
<UPRN/>
<AddressLine1/>
<AddressLine2/>
<TownCity/>
<County/>
<Q14Postcode override-field="">SW1A 1AA</Q14Postcode>
<Q14PropertyLocation>Westminster</Q14PropertyLocation>
<Q14ONSLACode>E09000033</Q14ONSLACode>
@ -275,6 +280,7 @@
<Q41b/>
<Q42Borrowing/>
<Q43CashDeposit override-field="">300000</Q43CashDeposit>
<Q44MonthlyCharges/>
</Group>
<Group>
<HHMEMB>1</HHMEMB>

9
spec/fixtures/imports/sales_logs/shared_ownership_sales_log.xml vendored

@ -24,6 +24,7 @@
<Q38OtherSale/>
<company>2 No</company>
<LiveInBuyer>1 Yes</LiveInBuyer>
<BUY2LIVEIN/>
<joint>2 No</joint>
<JointMore/>
<PartAPurchaser>2 Yes</PartAPurchaser>
@ -32,6 +33,12 @@
<Q11Bedrooms override-field="">2</Q11Bedrooms>
<Q12PropertyType>1 Flat or maisonette</Q12PropertyType>
<Q13BuildingType>1 Purpose built</Q13BuildingType>
<UPRN/>
<AddressLine1/>
<AddressLine2/>
<TownCity/>
<County/>
<PREVSHARED/>
<Q14Postcode override-field="">SW1A 1AA</Q14Postcode>
<Q14PropertyLocation>Westminster</Q14PropertyLocation>
<Q14ONSLACode>E09000033</Q14ONSLACode>
@ -47,6 +54,8 @@
<P2Sex override-field=""/>
<P2Rel/>
<P2Eco/>
<P2Eth/>
<P2Nat/>
<P3Age/>
<P3Sex override-field=""/>
<P3Rel/>

22
spec/helpers/tasklist_helper_spec.rb

@ -131,10 +131,19 @@ RSpec.describe TasklistHelper do
it "returns relevant text" do
Timecop.freeze(now + 1.year) do
expect(review_log_text(sales_log)).to eq("This log is from the 2021/2022 collection window, which is now closed.")
expect(review_log_text(sales_log)).to eq("This log is from the 2022/2023 collection window, which is now closed.")
end
end
end
context "when older_than_previous_collection_year" do
let(:now) { Time.utc(2023, 6, 1) }
let(:sales_log) { build(:sales_log, :completed, saledate: Time.utc(2022, 2, 1)) }
it "returns relevant text" do
expect(review_log_text(sales_log)).to eq("This log is from the 2021/2022 collection window, which is now closed.")
end
end
end
context "with lettings log" do
@ -168,10 +177,19 @@ RSpec.describe TasklistHelper do
it "returns relevant text" do
Timecop.freeze(now + 1.year) do
expect(review_log_text(sales_log)).to eq("This log is from the 2021/2022 collection window, which is now closed.")
expect(review_log_text(sales_log)).to eq("This log is from the 2022/2023 collection window, which is now closed.")
end
end
end
context "when older_than_previous_collection_year" do
let(:now) { Time.utc(2023, 6, 1) }
let(:lettings_log) { build(:lettings_log, :completed, startdate: Time.utc(2022, 2, 1)) }
it "returns relevant text" do
expect(review_log_text(lettings_log)).to eq("This log is from the 2021/2022 collection window, which is now closed.")
end
end
end
end
end

2
spec/models/form/lettings/pages/max_rent_value_check_spec.rb

@ -3,7 +3,7 @@ require "rails_helper"
RSpec.describe Form::Lettings::Pages::MaxRentValueCheck, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { "shared_ownership_deposit_value_check" }
let(:page_id) { "max_rent_value_check" }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }

11
spec/models/form/lettings/pages/min_rent_value_check_spec.rb

@ -1,8 +1,9 @@
require "rails_helper"
RSpec.describe Form::Lettings::Pages::MinRentValueCheck, type: :model do
subject(:page) { described_class.new(nil, page_definition, subsection) }
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { "min_rent_value_check" }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
@ -41,8 +42,14 @@ RSpec.describe Form::Lettings::Pages::MinRentValueCheck, type: :model do
it "has the correct informative_text" do
expect(page.informative_text).to eq({
"arguments" => [{ "arguments_for_key" => "soft_min_for_period", "i18n_template" => "soft_min_for_period", "key" => "field_formatted_as_currency" }],
"translation" => "soft_validations.rent.min_hint_text",
"arguments" => [
{
"key" => "field_formatted_as_currency",
"arguments_for_key" => "soft_min_for_period",
"i18n_template" => "soft_min_for_period",
},
],
})
end
end

16
spec/models/form/lettings/questions/uprn_known_spec.rb

@ -54,6 +54,20 @@ RSpec.describe Form::Lettings::Questions::UprnKnown, type: :model do
end
it "has the correct hidden_in_check_answers" do
expect(question.hidden_in_check_answers).to eq(true)
expect(question.hidden_in_check_answers).to eq({
"depends_on" => [
{ "uprn_known" => 0 },
{ "uprn_known" => 1 },
],
})
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to eq([
{
"condition" => { "uprn_known" => 0 },
"value" => "Not known",
},
])
end
end

4
spec/models/form/lettings/subsections/income_and_benefits_spec.rb

@ -30,8 +30,8 @@ RSpec.describe Form::Lettings::Subsections::IncomeAndBenefits, type: :model do
rent_bi_weekly
rent_4_weekly
rent_monthly
min_rent_value_check
max_rent_value_check
brent_min_rent_value_check
brent_max_rent_value_check
outstanding
outstanding_amount
],

8
spec/models/form/lettings/subsections/property_information_spec.rb

@ -20,6 +20,8 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do
%w[
property_postcode
property_local_authority
local_authority_min_rent_value_check
local_authority_max_rent_value_check
first_time_property_let_as_social_housing
property_let_type
property_vacancy_reason_not_first_let
@ -30,6 +32,8 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do
property_building_type
property_wheelchair_accessible
property_number_of_bedrooms
beds_min_rent_value_check
beds_max_rent_value_check
void_date
void_date_value_check
property_major_repairs
@ -49,6 +53,8 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do
uprn_confirmation
address
property_local_authority
local_authority_min_rent_value_check
local_authority_max_rent_value_check
first_time_property_let_as_social_housing
property_let_type
property_vacancy_reason_not_first_let
@ -59,6 +65,8 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do
property_building_type
property_wheelchair_accessible
property_number_of_bedrooms
beds_min_rent_value_check
beds_max_rent_value_check
void_date
void_date_value_check
property_major_repairs

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

@ -15,14 +15,22 @@ RSpec.describe Form::Lettings::Subsections::Setup, type: :model do
expect(setup.pages.map(&:id)).to eq(
%w[
stock_owner
stock_owner_min_rent_value_check
stock_owner_max_rent_value_check
managing_organisation
created_by
needs_type
scheme
location
needs_type_min_rent_value_check
needs_type_max_rent_value_check
renewal
tenancy_start_date
start_date_min_rent_value_check
start_date_max_rent_value_check
rent_type
rent_type_min_rent_value_check
rent_type_max_rent_value_check
tenant_code
property_reference
],
@ -42,14 +50,22 @@ RSpec.describe Form::Lettings::Subsections::Setup, type: :model do
expect(setup.pages.map(&:id)).to eq(
%w[
stock_owner
stock_owner_min_rent_value_check
stock_owner_max_rent_value_check
managing_organisation
created_by
needs_type
scheme
location
needs_type_min_rent_value_check
needs_type_max_rent_value_check
renewal
tenancy_start_date
start_date_min_rent_value_check
start_date_max_rent_value_check
rent_type
rent_type_min_rent_value_check
rent_type_max_rent_value_check
tenant_code
property_reference
],

19
spec/models/form/sales/pages/uprn_spec.rb

@ -35,25 +35,6 @@ RSpec.describe Form::Sales::Pages::Uprn, type: :model do
expect(page.skip_text).to eq("Enter address instead")
end
describe "has correct routed_to?" do
context "when uprn_known != 1" do
let(:log) { create(:sales_log, uprn_known: 0) }
it "returns false" do
expect(page.routed_to?(log)).to eq(false)
end
end
context "when uprn_known == 1" do
let(:log) { create(:sales_log) }
it "returns true" do
log.uprn_known = 1
expect(page.routed_to?(log)).to eq(true)
end
end
end
describe "has correct skip_href" do
context "when log is nil" do
it "is nil" do

4
spec/models/form/sales/questions/age2_spec.rb

@ -59,4 +59,8 @@ RSpec.describe Form::Sales::Questions::Age2, type: :model do
it "has the correct max" do
expect(question.max).to eq(110)
end
it "has the correct step" do
expect(question.step).to be 1
end
end

4
spec/models/form/sales/questions/person_age_spec.rb

@ -40,6 +40,10 @@ RSpec.describe Form::Sales::Questions::PersonAge, type: :model do
expect(question.width).to eq(3)
end
it "has the correct step" do
expect(question.step).to be 1
end
context "with person 2" do
let(:person_index) { 2 }
let(:question_id) { "age2" }

30
spec/models/form/sales/questions/purchase_price_spec.rb

@ -37,6 +37,36 @@ RSpec.describe Form::Sales::Questions::PurchasePrice, type: :model do
)
end
it "has the correct question_number" do
expect(question.question_number).to be_nil
end
context "when discounted ownership scheme" do
subject(:question) { described_class.new(question_id, question_definition, page, ownershipsch: 2) }
it "has the correct hint" do
expect(question.hint_text).to eq(
"For all schemes, including Right to Acquire (RTA), Right to Buy (RTB), Voluntary Right to Buy (VRTB) or Preserved Right to Buy (PRTB) sales, enter the full price of the property without any discount",
)
end
it "has the correct question_number" do
expect(question.question_number).to eq(100)
end
end
context "when outright sale" do
subject(:question) { described_class.new(question_id, question_definition, page, ownershipsch: 3) }
it "has the correct hint" do
expect(question.hint_text).to be_nil
end
it "has the correct question_number" do
expect(question.question_number).to eq(110)
end
end
it "has correct width" do
expect(question.width).to eq(5)
end

2
spec/models/form/sales/questions/uprn_confirmation_spec.rb

@ -50,7 +50,7 @@ RSpec.describe Form::Sales::Questions::UprnConfirmation, type: :model do
context "when address is present" do
it "returns formatted value" do
log = create(:sales_log, address_line1: "1, Test Street", town_or_city: "Test Town", county: "Test County", postcode_full: "AA1 1AA", uprn: "1234", uprn_known: 1)
log = build(:sales_log, :outright_sale_setup_complete, address_line1: "1, Test Street", town_or_city: "Test Town", county: "Test County", postcode_full: "AA1 1AA", uprn: "1234", uprn_known: 1)
expect(question.notification_banner(log)).to eq(
{

16
spec/models/form/sales/questions/uprn_known_spec.rb

@ -54,6 +54,20 @@ RSpec.describe Form::Sales::Questions::UprnKnown, type: :model do
end
it "has the correct hidden_in_check_answers" do
expect(question.hidden_in_check_answers).to eq(true)
expect(question.hidden_in_check_answers).to eq({
"depends_on" => [
{ "uprn_known" => 0 },
{ "uprn_known" => 1 },
],
})
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to eq([
{
"condition" => { "uprn_known" => 0 },
"value" => "Not known",
},
])
end
end

1
spec/models/form/sales/questions/uprn_spec.rb

@ -56,6 +56,7 @@ RSpec.describe Form::Sales::Questions::Uprn, type: :model do
let(:log) do
create(
:sales_log,
:completed,
address_line1: "1, Test Street",
town_or_city: "Test Town",
county: "Test County",

8
spec/models/form/sales/subsections/property_information_spec.rb

@ -41,16 +41,16 @@ RSpec.describe Form::Sales::Subsections::PropertyInformation, type: :model do
it "has correct pages" do
expect(property_information.pages.map(&:id)).to eq(
%w[
uprn
uprn_confirmation
address
property_local_authority
property_number_of_bedrooms
about_price_bedrooms_value_check
property_unit_type
monthly_charges_property_type_value_check
percentage_discount_proptype_value_check
property_building_type
uprn
uprn_confirmation
address
property_local_authority
about_price_la_value_check
property_wheelchair_accessible
],

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

Loading…
Cancel
Save