Browse Source

Merge branch 'main' into CLDC-3857-Add-new-questions-to-organisation-setup

pull/2971/head
Manny Dinssa 4 months ago committed by GitHub
parent
commit
bfcb72c608
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 0
      .rake_tasks~
  2. 54
      Gemfile.lock
  3. 11
      app/components/create_log_actions_component.html.erb
  4. 4
      app/components/create_log_actions_component.rb
  5. 26
      app/controllers/test_data_controller.rb
  6. 4
      app/frontend/styles/_task-list.scss
  7. 253
      app/helpers/bulk_upload/lettings_log_to_csv.rb
  8. 139
      app/helpers/bulk_upload/sales_log_to_csv.rb
  9. 5
      app/helpers/collection_deadline_helper.rb
  10. 17
      app/helpers/tasklist_helper.rb
  11. 2
      app/models/bulk_upload.rb
  12. 12
      app/models/derived_variables/lettings_log_variables.rb
  13. 10
      app/models/derived_variables/sales_log_variables.rb
  14. 11
      app/models/form/lettings/pages/referral_direct.rb
  15. 4
      app/models/form/lettings/pages/referral_general_needs.rb
  16. 12
      app/models/form/lettings/pages/referral_general_needs_prp.rb
  17. 11
      app/models/form/lettings/pages/referral_hsc.rb
  18. 11
      app/models/form/lettings/pages/referral_justice.rb
  19. 11
      app/models/form/lettings/pages/referral_la.rb
  20. 3
      app/models/form/lettings/pages/referral_prp.rb
  21. 10
      app/models/form/lettings/pages/referral_type.rb
  22. 5
      app/models/form/lettings/pages/rent_value_check.rb
  23. 1
      app/models/form/lettings/questions/beds.rb
  24. 1
      app/models/form/lettings/questions/builtype.rb
  25. 1
      app/models/form/lettings/questions/first_time_property_let_as_social_housing.rb
  26. 1
      app/models/form/lettings/questions/major_repairs_date_value_check.rb
  27. 1
      app/models/form/lettings/questions/majorrepairs.rb
  28. 1
      app/models/form/lettings/questions/mrcdate.rb
  29. 1
      app/models/form/lettings/questions/previous_let_type.rb
  30. 26
      app/models/form/lettings/questions/referral_direct.rb
  31. 2
      app/models/form/lettings/questions/referral_general_needs.rb
  32. 105
      app/models/form/lettings/questions/referral_general_needs_prp.rb
  33. 32
      app/models/form/lettings/questions/referral_hsc.rb
  34. 23
      app/models/form/lettings/questions/referral_justice.rb
  35. 29
      app/models/form/lettings/questions/referral_la.rb
  36. 89
      app/models/form/lettings/questions/referral_prp.rb
  37. 38
      app/models/form/lettings/questions/referral_type.rb
  38. 5
      app/models/form/lettings/questions/rent_value_check.rb
  39. 1
      app/models/form/lettings/questions/rsnvac.rb
  40. 1
      app/models/form/lettings/questions/rsnvac_first_let.rb
  41. 1
      app/models/form/lettings/questions/sheltered.rb
  42. 1
      app/models/form/lettings/questions/unittype_gn.rb
  43. 1
      app/models/form/lettings/questions/void_date_value_check.rb
  44. 1
      app/models/form/lettings/questions/voiddate.rb
  45. 1
      app/models/form/lettings/questions/wheelchair.rb
  46. 23
      app/models/form/lettings/subsections/household_situation.rb
  47. 2
      app/models/form/lettings/subsections/income_and_benefits.rb
  48. 19
      app/models/form/lettings/subsections/property_information.rb
  49. 2
      app/models/form/sales/pages/living_before_purchase.rb
  50. 2
      app/models/form/sales/questions/living_before_purchase_years.rb
  51. 9
      app/models/forms/bulk_upload_form/prepare_your_file.rb
  52. 3
      app/models/validations/household_validations.rb
  53. 1
      app/models/validations/property_validations.rb
  54. 2
      app/services/bulk_upload/lettings/log_creator.rb
  55. 2
      app/services/bulk_upload/lettings/validator.rb
  56. 43
      app/services/bulk_upload/lettings/year2024/row_parser.rb
  57. 122
      app/services/bulk_upload/lettings/year2025/csv_parser.rb
  58. 1707
      app/services/bulk_upload/lettings/year2025/row_parser.rb
  59. 2
      app/services/bulk_upload/sales/log_creator.rb
  60. 2
      app/services/bulk_upload/sales/validator.rb
  61. 1
      app/services/bulk_upload/sales/year2024/row_parser.rb
  62. 124
      app/services/bulk_upload/sales/year2025/csv_parser.rb
  63. 1502
      app/services/bulk_upload/sales/year2025/row_parser.rb
  64. 4
      app/views/bulk_upload_lettings_logs/forms/prepare_your_file.html.erb
  65. 6
      app/views/bulk_upload_sales_logs/forms/prepare_your_file.html.erb
  66. 2
      app/views/logs/edit.html.erb
  67. 2
      config/locales/en.yml
  68. 32
      config/locales/forms/2025/lettings/household_situation.en.yml
  69. 2
      config/locales/forms/2025/sales/sale_information.en.yml
  70. 4
      config/locales/validations/lettings/2024/bulk_upload.en.yml
  71. 64
      config/locales/validations/lettings/2025/bulk_upload.en.yml
  72. 46
      config/locales/validations/sales/2025/bulk_upload.en.yml
  73. 2
      config/routes.rb
  74. 5
      db/migrate/20250225180643_add_referral_type_to_lettings_logs.rb
  75. 2
      db/schema.rb
  76. 15
      lib/tasks/correct_reasonpref_values.rake
  77. 12
      lib/tasks/recalculate_invalid_reasonpref_dontknow.rake
  78. BIN
      session-manager-plugin.deb
  79. 1
      spec/factories/lettings_log.rb
  80. 45
      spec/helpers/tasklist_helper_spec.rb
  81. 111
      spec/lib/tasks/correct_reasonpref_values_spec.rb
  82. 70
      spec/lib/tasks/recalculate_invalid_reasonpref_dontknow_spec.rb
  83. 1
      spec/models/bulk_upload_spec.rb
  84. 2
      spec/models/form/lettings/questions/referral_general_needs_prp_spec.rb
  85. 2
      spec/models/form/lettings/questions/referral_general_needs_spec.rb
  86. 4
      spec/models/form/lettings/questions/rsnvac_spec.rb
  87. 4
      spec/models/form/lettings/questions/voiddate_spec.rb
  88. 38
      spec/models/form/lettings/subsections/household_situation_spec.rb
  89. 8
      spec/models/form/lettings/subsections/property_information_spec.rb
  90. 2
      spec/models/form/sales/pages/living_before_purchase_spec.rb
  91. 20
      spec/models/lettings_log_derived_fields_spec.rb
  92. 20
      spec/models/sales_log_derived_fields_spec.rb
  93. 25
      spec/models/validations/household_validations_spec.rb
  94. 1
      spec/models/validations/property_validations_spec.rb
  95. 15
      spec/requests/lettings_logs_controller_spec.rb
  96. 11
      spec/requests/sales_logs_controller_spec.rb
  97. 4
      spec/services/bulk_upload/lettings/validator_spec.rb
  98. 32
      spec/services/bulk_upload/lettings/year2023/csv_parser_spec.rb
  99. 36
      spec/services/bulk_upload/lettings/year2024/csv_parser_spec.rb
  100. 31
      spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb
  101. Some files were not shown because too many files have changed in this diff Show More

0
.rake_tasks~

54
Gemfile.lock

@ -118,7 +118,7 @@ GEM
erubi (~> 1.4) erubi (~> 1.4)
parser (>= 2.4) parser (>= 2.4)
smart_properties smart_properties
bigdecimal (3.1.8) bigdecimal (3.1.9)
bindex (0.8.1) bindex (0.8.1)
bootsnap (1.18.3) bootsnap (1.18.3)
msgpack (~> 1.2) msgpack (~> 1.2)
@ -148,15 +148,15 @@ GEM
coderay (1.1.3) coderay (1.1.3)
coercible (1.0.0) coercible (1.0.0)
descendants_tracker (~> 0.0.1) descendants_tracker (~> 0.0.1)
concurrent-ruby (1.3.4) concurrent-ruby (1.3.5)
connection_pool (2.4.1) connection_pool (2.5.0)
crack (1.0.0) crack (1.0.0)
bigdecimal bigdecimal
rexml rexml
crass (1.0.6) crass (1.0.6)
cssbundling-rails (1.4.0) cssbundling-rails (1.4.0)
railties (>= 6.0.0) railties (>= 6.0.0)
csv (3.3.0) csv (3.3.2)
date (3.4.1) date (3.4.1)
descendants_tracker (0.0.4) descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
@ -188,7 +188,7 @@ GEM
rainbow rainbow
rubocop rubocop
smart_properties smart_properties
erubi (1.13.0) erubi (1.13.1)
et-orbi (1.2.11) et-orbi (1.2.11)
tzinfo tzinfo
event_stream_parser (1.0.0) event_stream_parser (1.0.0)
@ -227,12 +227,13 @@ GEM
hashdiff (1.1.0) hashdiff (1.1.0)
html-attributes-utils (1.0.2) html-attributes-utils (1.0.2)
activesupport (>= 6.1.4.4) activesupport (>= 6.1.4.4)
i18n (1.14.6) i18n (1.14.7)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
ice_nine (0.11.2) ice_nine (0.11.2)
iniparse (1.5.0) iniparse (1.5.0)
io-console (0.8.0) io-console (0.8.0)
irb (1.14.1) irb (1.15.1)
pp (>= 0.6.0)
rdoc (>= 4.0.0) rdoc (>= 4.0.0)
reline (>= 0.4.2) reline (>= 0.4.2)
jmespath (1.6.2) jmespath (1.6.2)
@ -259,8 +260,8 @@ GEM
listen (3.9.0) listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3) rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10) rb-inotify (~> 0.9, >= 0.9.10)
logger (1.6.2) logger (1.6.6)
loofah (2.23.1) loofah (2.24.0)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.12.0) nokogiri (>= 1.12.0)
mail (2.8.1) mail (2.8.1)
@ -285,7 +286,7 @@ GEM
net-protocol net-protocol
net-protocol (0.2.2) net-protocol (0.2.2)
timeout timeout
net-smtp (0.5.0) net-smtp (0.5.1)
net-protocol net-protocol
nio4r (2.7.4) nio4r (2.7.4)
nokogiri (1.18.3-arm64-darwin) nokogiri (1.18.3-arm64-darwin)
@ -318,6 +319,9 @@ GEM
racc racc
pg (1.5.5) pg (1.5.5)
possessive (1.0.1) possessive (1.0.1)
pp (0.6.2)
prettyprint
prettyprint (0.2.0)
propshaft (0.8.0) propshaft (0.8.0)
actionpack (>= 7.0.0) actionpack (>= 7.0.0)
activesupport (>= 7.0.0) activesupport (>= 7.0.0)
@ -329,7 +333,7 @@ GEM
pry-byebug (3.10.1) pry-byebug (3.10.1)
byebug (~> 11.0) byebug (~> 11.0)
pry (>= 0.13, < 0.15) pry (>= 0.13, < 0.15)
psych (5.2.1) psych (5.2.3)
date date
stringio stringio
public_suffix (5.0.4) public_suffix (5.0.4)
@ -339,14 +343,15 @@ GEM
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
raabro (1.4.0) raabro (1.4.0)
racc (1.8.1) racc (1.8.1)
rack (3.1.10) rack (3.1.11)
rack-attack (6.7.0) rack-attack (6.7.0)
rack (>= 1.0, < 4) rack (>= 1.0, < 4)
rack-mini-profiler (3.3.1) rack-mini-profiler (3.3.1)
rack (>= 1.2.0) rack (>= 1.2.0)
rack-session (2.0.0) rack-session (2.1.0)
base64 (>= 0.1.0)
rack (>= 3.0.0) rack (>= 3.0.0)
rack-test (2.1.0) rack-test (2.2.0)
rack (>= 1.3) rack (>= 1.3)
rackup (2.2.1) rackup (2.2.1)
rack (>= 3) rack (>= 3)
@ -368,7 +373,7 @@ GEM
activesupport (>= 5.0.0) activesupport (>= 5.0.0)
minitest minitest
nokogiri (>= 1.6) nokogiri (>= 1.6)
rails-html-sanitizer (1.6.1) rails-html-sanitizer (1.6.2)
loofah (~> 2.21) loofah (~> 2.21)
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
rails_admin (3.3.0) rails_admin (3.3.0)
@ -392,14 +397,14 @@ GEM
rb-fsevent (0.11.2) rb-fsevent (0.11.2)
rb-inotify (0.10.1) rb-inotify (0.10.1)
ffi (~> 1.0) ffi (~> 1.0)
rdoc (6.8.1) rdoc (6.12.0)
psych (>= 4.0.0) psych (>= 4.0.0)
redcarpet (3.6.0) redcarpet (3.6.0)
redis (4.8.1) redis (4.8.1)
redis-client (0.22.1) redis-client (0.22.1)
connection_pool connection_pool
regexp_parser (2.9.0) regexp_parser (2.9.0)
reline (0.5.12) reline (0.6.0)
io-console (~> 0.5) io-console (~> 0.5)
request_store (1.7.0) request_store (1.7.0)
rack (>= 1.4) rack (>= 1.4)
@ -463,7 +468,7 @@ GEM
ruby-progressbar (1.13.0) ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5) ruby2_keywords (0.0.5)
rubyzip (2.3.2) rubyzip (2.3.2)
securerandom (0.4.0) securerandom (0.4.1)
selenium-webdriver (4.18.1) selenium-webdriver (4.18.1)
base64 (~> 0.2) base64 (~> 0.2)
rexml (~> 3.2, >= 3.2.5) rexml (~> 3.2, >= 3.2.5)
@ -492,21 +497,21 @@ GEM
smart_properties (1.17.0) smart_properties (1.17.0)
stimulus-rails (1.3.3) stimulus-rails (1.3.3)
railties (>= 6.0.0) railties (>= 6.0.0)
stringio (3.1.2) stringio (3.1.5)
thor (1.3.2) thor (1.3.2)
thread_safe (0.3.6) thread_safe (0.3.6)
timecop (0.9.8) timecop (0.9.8)
timeout (0.4.3) timeout (0.4.3)
turbo-rails (2.0.11) turbo-rails (2.0.13)
actionpack (>= 6.0.0) actionpack (>= 7.1.0)
railties (>= 6.0.0) railties (>= 7.1.0)
tzinfo (2.0.6) tzinfo (2.0.6)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
uk_postcode (2.1.8) uk_postcode (2.1.8)
unicode-display_width (2.5.0) unicode-display_width (2.5.0)
unread (0.14.0) unread (0.14.0)
activerecord (>= 6.1) activerecord (>= 6.1)
uri (0.13.0) uri (1.0.3)
useragent (0.16.11) useragent (0.16.11)
view_component (3.10.0) view_component (3.10.0)
activesupport (>= 5.2.0, < 8.0) activesupport (>= 5.2.0, < 8.0)
@ -528,7 +533,8 @@ GEM
crack (>= 0.3.2) crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0) hashdiff (>= 0.4.0, < 2.0.0)
websocket (1.2.10) websocket (1.2.10)
websocket-driver (0.7.6) websocket-driver (0.7.7)
base64
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5) websocket-extensions (0.1.5)
xpath (3.2.0) xpath (3.2.0)

11
app/components/create_log_actions_component.html.erb

@ -1,5 +1,5 @@
<div class="govuk-button-group app-filter-toggle <%= "govuk-!-margin-bottom-6" if display_actions? %>"> <div class="govuk-button-group app-filter-toggle <%= "govuk-!-margin-bottom-6" if display_actions? %>">
<% if display_actions? %> <% if display_actions? %>
<%= govuk_button_to create_button_copy, create_button_href, class: "govuk-!-margin-right-3" %> <%= govuk_button_to create_button_copy, create_button_href, class: "govuk-!-margin-right-3" %>
<% unless user.support? %> <% unless user.support? %>
<%= govuk_button_link_to upload_button_copy, upload_button_href, secondary: true %> <%= govuk_button_link_to upload_button_copy, upload_button_href, secondary: true %>
@ -9,9 +9,10 @@
<% end %> <% end %>
<% if FeatureToggle.create_test_logs_enabled? %> <% if FeatureToggle.create_test_logs_enabled? %>
<%= govuk_link_to "Create test log", create_test_log_href %> <%= govuk_link_to "New test log", create_test_log_href %>
<%= govuk_link_to "Create test log (setup only)", create_setup_test_log_href %> <%= govuk_link_to "New test log (setup only)", create_setup_test_log_href %>
<%= govuk_link_to "Get test BU file (2024)", create_2024_test_bulk_upload_href %> <%= govuk_link_to "24 BU test file", create_test_bulk_upload_href(2024) %>
<%= govuk_link_to "25 BU test file", create_test_bulk_upload_href(2025) %>
<% end %>
<% end %> <% end %>
<% end %>
</div> </div>

4
app/components/create_log_actions_component.rb

@ -42,8 +42,8 @@ class CreateLogActionsComponent < ViewComponent::Base
send("create_setup_test_#{log_type}_log_path") send("create_setup_test_#{log_type}_log_path")
end end
def create_2024_test_bulk_upload_href def create_test_bulk_upload_href(year)
send("create_2024_test_#{log_type}_bulk_upload_path") send("create_#{year}_test_#{log_type}_bulk_upload_path")
end end
def view_uploads_button_copy def view_uploads_button_copy

26
app/controllers/test_data_controller.rb

@ -15,23 +15,27 @@ class TestDataController < ApplicationController
redirect_to lettings_log_path(log) redirect_to lettings_log_path(log)
end end
def create_2024_test_lettings_bulk_upload %w[2024 2025].each do |year|
define_method("create_#{year}_test_lettings_bulk_upload") do
return render_not_found unless FeatureToggle.create_test_logs_enabled? return render_not_found unless FeatureToggle.create_test_logs_enabled?
file = Tempfile.new("test_lettings_log.csv") file = Tempfile.new("#{year}_test_lettings_log.csv")
log = FactoryBot.create(:lettings_log, :completed, assigned_to: current_user, ppostcode_full: "SW1A 1AA") log = FactoryBot.create(:lettings_log, :completed, assigned_to: current_user, ppostcode_full: "SW1A 1AA", startdate: Time.zone.local(year.to_i, rand(4..12), rand(1..28)))
log_to_csv = BulkUpload::LettingsLogToCsv.new(log:, line_ending: "\n", overrides: { organisation_id: "ORG#{log.owning_organisation_id}", managing_organisation_id: "ORG#{log.owning_organisation_id}" }) log_to_csv = BulkUpload::LettingsLogToCsv.new(log:, line_ending: "\n", overrides: { organisation_id: "ORG#{log.owning_organisation_id}", managing_organisation_id: "ORG#{log.owning_organisation_id}" })
file.write(log_to_csv.default_field_numbers_row) file.write(log_to_csv.default_field_numbers_row)
file.write(log_to_csv.to_csv_row) file.write(log_to_csv.to_csv_row)
file.rewind file.rewind
send_file file.path, type: "text/csv", send_file file.path, type: "text/csv",
filename: "test_lettings_log.csv", filename: "#{year}_test_lettings_log.csv",
disposition: "attachment", disposition: "attachment",
after_send: lambda { after_send: lambda {
file.close file.close
file.unlink file.unlink
} }
end end
end
def create_2025_test_sales_bulk_upload; end
def create_test_sales_log def create_test_sales_log
return render_not_found unless FeatureToggle.create_test_logs_enabled? return render_not_found unless FeatureToggle.create_test_logs_enabled?
@ -47,22 +51,24 @@ class TestDataController < ApplicationController
redirect_to sales_log_path(log) redirect_to sales_log_path(log)
end end
def create_2024_test_sales_bulk_upload [2024, 2025].each do |year|
define_method("create_#{year}_test_sales_bulk_upload") do
return render_not_found unless FeatureToggle.create_test_logs_enabled? return render_not_found unless FeatureToggle.create_test_logs_enabled?
file = Tempfile.new("test_sales_log.csv") file = Tempfile.new("#{year}_test_sales_log.csv")
log = FactoryBot.create(:sales_log, :completed, assigned_to: current_user, value: 180_000, deposit: 150_000, county: "Somerset", saledate: Time.zone.local(year.to_i, rand(4..12), rand(1..28)))
log = FactoryBot.create(:sales_log, :completed, assigned_to: current_user, value: 180_000, deposit: 150_000)
log_to_csv = BulkUpload::SalesLogToCsv.new(log:, line_ending: "\n", overrides: { organisation_id: "ORG#{log.owning_organisation_id}", managing_organisation_id: "ORG#{log.owning_organisation_id}" }) log_to_csv = BulkUpload::SalesLogToCsv.new(log:, line_ending: "\n", overrides: { organisation_id: "ORG#{log.owning_organisation_id}", managing_organisation_id: "ORG#{log.owning_organisation_id}" })
file.write(log_to_csv.default_field_numbers_row) file.write(log_to_csv.default_field_numbers_row)
file.write(log_to_csv.to_csv_row) file.write(log_to_csv.to_csv_row)
file.rewind file.rewind
send_file file.path, type: "text/csv", send_file file.path,
filename: "test_sales_log.csv", type: "text/csv",
filename: "#{year}_test_sales_log.csv",
disposition: "attachment", disposition: "attachment",
after_send: lambda { after_send: lambda {
file.close file.close
file.unlink file.unlink
} }
end end
end
end end

4
app/frontend/styles/_task-list.scss

@ -52,3 +52,7 @@
margin-bottom: 0; margin-bottom: 0;
} }
} }
.app-red-text {
color: govuk-colour("red");
}

253
app/helpers/bulk_upload/lettings_log_to_csv.rb

@ -17,12 +17,8 @@ class BulkUpload::LettingsLogToCsv
def to_csv_row(seed: nil) def to_csv_row(seed: nil)
year = log.collection_start_year year = log.collection_start_year
case year case year
when 2022 when 2022, 2023, 2024, 2025
to_2022_csv_row(seed:) to_year_csv_row(year, seed:)
when 2023
to_2023_csv_row(seed:)
when 2024
to_2024_csv_row(seed:)
else else
raise NotImplementedError "No mapping function implemented for year #{year}" raise NotImplementedError "No mapping function implemented for year #{year}"
end end
@ -30,82 +26,32 @@ class BulkUpload::LettingsLogToCsv
def to_row def to_row
year = log.collection_start_year year = log.collection_start_year
case year send("to_#{year}_row")
when 2022 rescue NoMethodError
to_2022_row
when 2023
to_2023_row
when 2024
to_2024_row
else
raise NotImplementedError "No mapping function implemented for year #{year}" raise NotImplementedError "No mapping function implemented for year #{year}"
end end
end
def default_field_numbers_row(seed: nil) def default_field_numbers_row(seed: nil)
year = log.collection_start_year year = log.collection_start_year
case year default_field_numbers_row_for_year(year, seed:)
when 2022 rescue NoMethodError
default_2022_field_numbers_row(seed:)
when 2023
default_2023_field_numbers_row(seed:)
when 2024
default_2024_field_numbers_row(seed:)
else
raise NotImplementedError "No mapping function implemented for year #{year}" raise NotImplementedError "No mapping function implemented for year #{year}"
end end
end
def default_field_numbers def default_field_numbers
year = log.collection_start_year year = log.collection_start_year
case year send("default_#{year}_field_numbers")
when 2022 rescue NoMethodError
default_2022_field_numbers
when 2023
default_2023_field_numbers
when 2024
default_2024_field_numbers
else
raise NotImplementedError "No mapping function implemented for year #{year}" raise NotImplementedError "No mapping function implemented for year #{year}"
end end
end
def to_2022_csv_row(seed: nil) def to_year_csv_row(year, seed: nil)
unshuffled_row = send("to_#{year}_row")
if seed if seed
row = to_2022_row.shuffle(random: Random.new(seed)) row = unshuffled_row.shuffle(random: Random.new(seed))
(row_prefix + row).flatten.join(",") + line_ending (row_prefix + row).flatten.join(",") + line_ending
else else
(row_prefix + to_2022_row).flatten.join(",") + line_ending (row_prefix + unshuffled_row).flatten.join(",") + line_ending
end
end
def default_2022_field_numbers
(1..134).to_a
end
def default_2022_field_numbers_row(seed: nil)
if seed
["Field number"] + default_2022_field_numbers.shuffle(random: Random.new(seed))
else
["Field number"] + default_2022_field_numbers
end.flatten.join(",") + line_ending
end
def to_2023_csv_row(seed: nil)
if seed
row = to_2023_row.shuffle(random: Random.new(seed))
(row_prefix + row).flatten.join(",") + line_ending
else
(row_prefix + to_2023_row).flatten.join(",") + line_ending
end
end
def to_2024_csv_row(seed: nil)
if seed
row = to_2024_row.shuffle(random: Random.new(seed))
(row_prefix + row).flatten.join(",") + line_ending
else
(row_prefix + to_2024_row).flatten.join(",") + line_ending
end end
end end
@ -121,20 +67,16 @@ class BulkUpload::LettingsLogToCsv
] ]
end end
def default_2023_field_numbers_row(seed: nil) def default_field_numbers_row_for_year(year, seed: nil)
if seed if seed
["Field number"] + default_2023_field_numbers.shuffle(random: Random.new(seed)) ["Field number"] + send("default_#{year}_field_numbers").shuffle(random: Random.new(seed))
else else
["Field number"] + default_2023_field_numbers ["Field number"] + send("default_#{year}_field_numbers")
end.flatten.join(",") + line_ending end.flatten.join(",") + line_ending
end end
def default_2024_field_numbers_row(seed: nil) def default_2022_field_numbers
if seed (1..134).to_a
["Field number"] + default_2024_field_numbers.shuffle(random: Random.new(seed))
else
["Field number"] + default_2024_field_numbers
end.flatten.join(",") + line_ending
end end
def default_2023_field_numbers def default_2023_field_numbers
@ -145,6 +87,156 @@ class BulkUpload::LettingsLogToCsv
(1..130).to_a (1..130).to_a
end end
def default_2025_field_numbers
(1..129).to_a
end
def to_2025_row
[
overrides[:organisation_id] || log.owning_organisation&.old_visible_id, # 1
overrides[:managing_organisation_id] || log.managing_organisation&.old_visible_id,
log.assigned_to&.email,
log.needstype,
log.scheme&.id ? "S#{log.scheme&.id}" : "",
log.location&.id,
renewal,
log.startdate&.day,
log.startdate&.month,
log.startdate&.strftime("%y"), # 10
rent_type,
log.irproduct_other,
log.tenancycode,
log.propcode,
log.declaration,
log.rsnvac,
log.unitletas,
log.uprn,
log.address_line1&.tr(",", " "),
log.address_line2&.tr(",", " "), # 20
log.town_or_city&.tr(",", " "),
log.county&.tr(",", " "),
((log.postcode_full || "").split(" ") || [""]).first,
((log.postcode_full || "").split(" ") || [""]).last,
log.la,
log.unittype_gn,
log.builtype,
log.wchair,
log.beds,
log.voiddate&.day, # 30
log.voiddate&.month,
log.voiddate&.strftime("%y"),
log.mrcdate&.day,
log.mrcdate&.month,
log.mrcdate&.strftime("%y"),
log.sheltered,
log.joint,
log.startertenancy,
log.tenancy,
log.tenancyother, # 40
log.tenancylength,
log.age1 || overrides[:age1],
log.sex1,
log.ethnic,
log.nationality_all_group,
log.ecstat1,
relat_number(log.relat2),
log.age2 || overrides[:age2],
log.sex2,
log.ecstat2, # 50
relat_number(log.relat3),
log.age3 || overrides[:age3],
log.sex3,
log.ecstat3,
relat_number(log.relat4),
log.age4 || overrides[:age4],
log.sex4,
log.ecstat4,
relat_number(log.relat5),
log.age5 || overrides[:age5], # 60
log.sex5,
log.ecstat5,
relat_number(log.relat6),
log.age6 || overrides[:age6],
log.sex6,
log.ecstat6,
relat_number(log.relat7),
log.age7 || overrides[:age7],
log.sex7,
log.ecstat7, # 70
relat_number(log.relat8),
log.age8 || overrides[:age8],
log.sex8,
log.ecstat8,
log.armedforces,
log.leftreg,
log.reservist,
log.preg_occ,
log.housingneeds_a,
log.housingneeds_b, # 80
log.housingneeds_c,
log.housingneeds_f,
log.housingneeds_g,
log.housingneeds_h,
overrides[:illness] || log.illness,
log.illness_type_1,
log.illness_type_2,
log.illness_type_3,
log.illness_type_4,
log.illness_type_5, # 90
log.illness_type_6,
log.illness_type_7,
log.illness_type_8,
log.illness_type_9,
log.illness_type_10,
log.layear,
log.waityear,
log.reason,
log.reasonother,
log.prevten, # 100
homeless,
previous_postcode_known,
((log.ppostcode_full || "").split(" ") || [""]).first,
((log.ppostcode_full || "").split(" ") || [""]).last,
log.prevloc,
log.reasonpref,
log.rp_homeless,
log.rp_insan_unsat,
log.rp_medwel,
log.rp_hardship, # 110
log.rp_dontknow,
cbl,
chr,
cap,
accessible_register,
log.referral,
net_income_known,
log.incfreq,
log.earnings,
log.hb, # 120
log.benefits,
log.household_charge,
log.period,
log.brent,
log.scharge,
log.pscharge,
log.supcharg,
log.hbrentshortfall,
log.tshortfall, # 129
]
end
def to_2024_row def to_2024_row
[ [
overrides[:organisation_id] || log.owning_organisation&.old_visible_id, # 1 overrides[:organisation_id] || log.owning_organisation&.old_visible_id, # 1
@ -551,4 +643,15 @@ private
log.hhregres log.hhregres
end end
end end
def relat_number(value)
case value
when "P"
1
when "R"
3
when "C", "X"
2
end
end
end end

139
app/helpers/bulk_upload/sales_log_to_csv.rb

@ -19,7 +19,7 @@ class BulkUpload::SalesLogToCsv
case year case year
when 2022 when 2022
to_2022_csv_row to_2022_csv_row
when 2023, 2024 when 2023, 2024, 2025
to_year_csv_row(year, seed:) to_year_csv_row(year, seed:)
else else
raise NotImplementedError "No mapping function implemented for year #{year}" raise NotImplementedError "No mapping function implemented for year #{year}"
@ -67,6 +67,8 @@ class BulkUpload::SalesLogToCsv
[6, 3, 4, 5, nil, 28, 30, 38, 47, 51, 55, 59, 31, 39, 48, 52, 56, 60, 37, 46, 50, 54, 58, 35, 43, 49, 53, 57, 61, 32, 33, 78, 80, 79, 81, 83, 84, nil, 62, 66, 64, 65, 63, 67, 69, 70, 68, 76, 77, 16, 17, 18, 26, 24, 25, 27, 8, 91, 95, 96, 97, 92, 93, 94, 98, 100, 101, 103, 104, 106, 110, 111, 112, 113, 114, 9, 116, 117, 118, 120, 124, 125, 126, 10, 11, nil, 127, 129, 133, 134, 135, 1, 2, nil, 73, nil, 75, 107, 108, 121, 122, 130, 131, 82, 109, 123, 132, 115, 15, 86, 87, 29, 7, 12, 13, 14, 36, 44, 45, 88, 89, 102, 105, 119, 128, 19, 20, 21, 22, 23, 34, 40, 41, 42, 71, 72, 74, 85, 90, 99] [6, 3, 4, 5, nil, 28, 30, 38, 47, 51, 55, 59, 31, 39, 48, 52, 56, 60, 37, 46, 50, 54, 58, 35, 43, 49, 53, 57, 61, 32, 33, 78, 80, 79, 81, 83, 84, nil, 62, 66, 64, 65, 63, 67, 69, 70, 68, 76, 77, 16, 17, 18, 26, 24, 25, 27, 8, 91, 95, 96, 97, 92, 93, 94, 98, 100, 101, 103, 104, 106, 110, 111, 112, 113, 114, 9, 116, 117, 118, 120, 124, 125, 126, 10, 11, nil, 127, 129, 133, 134, 135, 1, 2, nil, 73, nil, 75, 107, 108, 121, 122, 130, 131, 82, 109, 123, 132, 115, 15, 86, 87, 29, 7, 12, 13, 14, 36, 44, 45, 88, 89, 102, 105, 119, 128, 19, 20, 21, 22, 23, 34, 40, 41, 42, 71, 72, 74, 85, 90, 99]
when 2024 when 2024
(1..131).to_a (1..131).to_a
when 2025
(1..121).to_a
else else
raise NotImplementedError "No mapping function implemented for year #{year}" raise NotImplementedError "No mapping function implemented for year #{year}"
end end
@ -395,6 +397,141 @@ class BulkUpload::SalesLogToCsv
] ]
end end
def to_2025_row
[
log.saledate&.day,
log.saledate&.month,
log.saledate&.strftime("%y"),
overrides[:organisation_id] || log.owning_organisation&.old_visible_id,
overrides[:managing_organisation_id] || log.managing_organisation&.old_visible_id,
log.assigned_to&.email,
log.purchid,
log.ownershipsch,
log.ownershipsch == 1 ? log.type : "", # field_9: "What is the type of shared ownership sale?",
log.staircase, # 10
log.ownershipsch == 2 ? log.type : "", # field_11: "What is the type of discounted ownership sale?",
log.jointpur,
log.jointmore,
log.noint,
log.privacynotice,
log.uprn,
log.address_line1&.tr(",", " "), # 20
log.address_line2&.tr(",", " "),
log.town_or_city&.tr(",", " "),
log.county&.tr(",", " "),
((log.postcode_full || "").split(" ") || [""]).first,
((log.postcode_full || "").split(" ") || [""]).last,
log.la,
log.proptype,
log.beds,
log.builtype,
log.wchair,
log.age1,
log.sex1,
log.ethnic, # 30
log.nationality_all_group,
log.ecstat1,
log.buy1livein,
log.relat2,
log.age2,
log.sex2,
log.ethnic_group2,
log.nationality_all_buyer2_group,
log.ecstat2,
log.buy2livein, # 40
log.hholdcount,
log.relat3,
log.age3,
log.sex3,
log.ecstat3,
log.relat4,
log.age4,
log.sex4,
log.ecstat4,
log.relat5, # 50
log.age5,
log.sex5,
log.ecstat5,
log.relat6,
log.age6,
log.sex6,
log.ecstat6,
log.prevten,
log.ppcodenk,
((log.ppostcode_full || "").split(" ") || [""]).first, # 60
((log.ppostcode_full || "").split(" ") || [""]).last,
log.prevloc,
log.buy2living,
log.prevtenbuy2,
log.hhregres,
log.hhregresstill,
log.armedforcesspouse,
log.disabled,
log.wheel,
log.income1, # 70
log.inc1mort,
log.income2,
log.inc2mort,
log.hb,
log.savings.present? || "R",
log.prevown,
log.prevshared,
log.resale,
log.proplen,
log.hodate&.day, # 80
log.hodate&.month,
log.hodate&.strftime("%y"),
log.frombeds,
log.fromprop,
log.socprevten,
log.value,
log.equity,
log.mortgageused,
log.mortgage,
log.mortlen, # 90
log.deposit,
log.cashdis,
log.mrent,
log.mscharge,
log.management_fee,
log.stairbought,
log.stairowned,
log.staircasesale,
log.firststair,
log.initialpurchase&.day, # 100
log.initialpurchase&.month,
log.initialpurchase&.strftime("%y"),
log.numstair,
log.lasttransaction&.day,
log.lasttransaction&.month,
log.lasttransaction&.strftime("%y"),
log.value,
log.equity,
log.mortgageused,
log.mrentprestaircasing, # 110
log.mrent,
log.proplen,
log.value,
log.grant,
log.discount,
log.mortgageused,
log.mortgage,
log.mortlen,
log.extrabor,
log.deposit, # 120
log.mscharge,
]
end
def custom_field_numbers_row(seed: nil, field_numbers: nil) def custom_field_numbers_row(seed: nil, field_numbers: nil)
if seed if seed
["Field number"] + field_numbers.shuffle(random: Random.new(seed)) ["Field number"] + field_numbers.shuffle(random: Random.new(seed))

5
app/helpers/collection_deadline_helper.rb

@ -61,12 +61,15 @@ module CollectionDeadlineHelper
first_quarter(year).merge(quarter: "Q1"), first_quarter(year).merge(quarter: "Q1"),
second_quarter(year).merge(quarter: "Q2"), second_quarter(year).merge(quarter: "Q2"),
third_quarter(year).merge(quarter: "Q3"), third_quarter(year).merge(quarter: "Q3"),
fourth_quarter(year).merge(quarter: "Q4"),
] ]
end end
def quarter_for_date(date: Time.zone.now) def quarter_for_date(date: Time.zone.now)
quarters = quarter_dates(current_collection_start_year) collection_start_year = collection_start_year_for_date(date)
return unless QUARTERLY_DEADLINES.key?(collection_start_year)
quarters = quarter_dates(collection_start_year)
quarter = quarters.find { |q| date.between?(q[:start_date], q[:cutoff_date] + 1.day) } quarter = quarters.find { |q| date.between?(q[:start_date], q[:cutoff_date] + 1.day) }
return unless quarter return unless quarter

17
app/helpers/tasklist_helper.rb

@ -2,6 +2,7 @@ module TasklistHelper
include GovukLinkHelper include GovukLinkHelper
include GovukVisuallyHiddenHelper include GovukVisuallyHiddenHelper
include CollectionTimeHelper include CollectionTimeHelper
include CollectionDeadlineHelper
def breadcrumb_logs_title(log, current_user) def breadcrumb_logs_title(log, current_user)
log_type = log.lettings? ? "Lettings" : "Sales" log_type = log.lettings? ? "Lettings" : "Sales"
@ -70,6 +71,22 @@ module TasklistHelper
status == :cannot_start_yet ? "" : "govuk-task-list__item--with-link" status == :cannot_start_yet ? "" : "govuk-task-list__item--with-link"
end end
def deadline_text(log)
return if log.completed?
return if log.startdate.nil?
log_quarter = quarter_for_date(date: log.startdate)
return if log_quarter.nil?
deadline_for_log = log_quarter.cutoff_date
if deadline_for_log.beginning_of_day >= Time.zone.today.beginning_of_day
"<p class=\"govuk-body\">Upcoming #{log_quarter.quarter} deadline: #{log_quarter.cutoff_date.strftime('%-d %B %Y')}.<p>".html_safe
else
"<p class=\"govuk-body app-red-text\"><strong>Overdue: #{log_quarter.quarter} deadline #{log_quarter.cutoff_date.strftime('%-d %B %Y')}.</strong></p>".html_safe
end
end
private private
def breadcrumb_organisation(log) def breadcrumb_organisation(log)

2
app/models/bulk_upload.rb

@ -104,6 +104,8 @@ class BulkUpload < ApplicationRecord
end end
year_class = case year year_class = case year
when 2025
"Year2025"
when 2024 when 2024
"Year2024" "Year2024"
when 2023 when 2023

12
app/models/derived_variables/lettings_log_variables.rb

@ -72,6 +72,7 @@ module DerivedVariables::LettingsLogVariables
self.beds = 1 self.beds = 1
end end
clear_child_ecstat_for_age_changes!
child_under_16_constraints! child_under_16_constraints!
self.hhtype = household_type self.hhtype = household_type
@ -141,6 +142,9 @@ module DerivedVariables::LettingsLogVariables
self.is_la_inferred = false self.is_la_inferred = false
end end
self.referral = 7 if referral_type == 6
self.referral = 16 if referral_type == 7
reset_address_fields! if is_supported_housing? reset_address_fields! if is_supported_housing?
set_checkbox_values! set_checkbox_values!
@ -243,6 +247,14 @@ private
end end
end end
def clear_child_ecstat_for_age_changes!
(2..8).each do |idx|
if public_send("age#{idx}_changed?") && self["ecstat#{idx}"] == 9
self["ecstat#{idx}"] = nil
end
end
end
def household_type def household_type
return unless totelder && totadult && totchild return unless totelder && totadult && totchild

10
app/models/derived_variables/sales_log_variables.rb

@ -46,6 +46,7 @@ module DerivedVariables::SalesLogVariables
if saledate && form.start_year_2024_or_later? if saledate && form.start_year_2024_or_later?
self.soctenant = soctenant_from_prevten_values self.soctenant = soctenant_from_prevten_values
clear_child_ecstat_for_age_changes!
child_under_16_constraints! child_under_16_constraints!
end end
@ -181,6 +182,15 @@ private
end end
end end
def clear_child_ecstat_for_age_changes!
start_index = joint_purchase? ? 3 : 2
(start_index..6).each do |idx|
if public_send("age#{idx}_changed?") && self["ecstat#{idx}"] == 9
self["ecstat#{idx}"] = nil
end
end
end
def household_type def household_type
return unless total_elder && total_adult && totchild return unless total_elder && total_adult && totchild

11
app/models/form/lettings/pages/referral_direct.rb

@ -0,0 +1,11 @@
class Form::Lettings::Pages::ReferralDirect < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "referral_direct"
@depends_on = [{ "referral_type" => 1 }]
end
def questions
@questions ||= [Form::Lettings::Questions::ReferralDirect.new(nil, nil, self)]
end
end

4
app/models/form/lettings/pages/referral.rb → app/models/form/lettings/pages/referral_general_needs.rb

@ -1,4 +1,4 @@
class Form::Lettings::Pages::Referral < ::Form::Page class Form::Lettings::Pages::ReferralGeneralNeeds < ::Form::Page
def initialize(id, hsh, subsection) def initialize(id, hsh, subsection)
super super
@id = "referral" @id = "referral"
@ -7,6 +7,6 @@ class Form::Lettings::Pages::Referral < ::Form::Page
end end
def questions def questions
@questions ||= [Form::Lettings::Questions::Referral.new(nil, nil, self)] @questions ||= [Form::Lettings::Questions::ReferralGeneralNeeds.new(nil, nil, self)]
end end
end end

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

@ -0,0 +1,12 @@
class Form::Lettings::Pages::ReferralGeneralNeedsPrp < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "referral_prp"
@copy_key = "lettings.household_situation.referral.general_needs.prp"
@depends_on = [{ "owning_organisation_provider_type" => "PRP", "needstype" => 1, "renewal" => 0 }]
end
def questions
@questions ||= [Form::Lettings::Questions::ReferralGeneralNeedsPrp.new(nil, nil, self)]
end
end

11
app/models/form/lettings/pages/referral_hsc.rb

@ -0,0 +1,11 @@
class Form::Lettings::Pages::ReferralHsc < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "referral_hsc"
@depends_on = [{ "referral_type" => 4 }]
end
def questions
@questions ||= [Form::Lettings::Questions::ReferralHsc.new(nil, nil, self)]
end
end

11
app/models/form/lettings/pages/referral_justice.rb

@ -0,0 +1,11 @@
class Form::Lettings::Pages::ReferralJustice < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "referral_justice"
@depends_on = [{ "referral_type" => 5 }]
end
def questions
@questions ||= [Form::Lettings::Questions::ReferralJustice.new(nil, nil, self)]
end
end

11
app/models/form/lettings/pages/referral_la.rb

@ -0,0 +1,11 @@
class Form::Lettings::Pages::ReferralLa < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "referral_la"
@depends_on = [{ "referral_type" => 2 }]
end
def questions
@questions ||= [Form::Lettings::Questions::ReferralLa.new(nil, nil, self)]
end
end

3
app/models/form/lettings/pages/referral_prp.rb

@ -2,8 +2,7 @@ class Form::Lettings::Pages::ReferralPrp < ::Form::Page
def initialize(id, hsh, subsection) def initialize(id, hsh, subsection)
super super
@id = "referral_prp" @id = "referral_prp"
@copy_key = "lettings.household_situation.referral.general_needs.prp" @depends_on = [{ "referral_type" => 3 }]
@depends_on = [{ "owning_organisation_provider_type" => "PRP", "needstype" => 1, "renewal" => 0 }]
end end
def questions def questions

10
app/models/form/lettings/pages/referral_type.rb

@ -0,0 +1,10 @@
class Form::Lettings::Pages::ReferralType < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "referral_type"
end
def questions
@questions ||= [Form::Lettings::Questions::ReferralType.new(nil, nil, self)]
end
end

5
app/models/form/lettings/pages/rent_value_check.rb

@ -1,5 +1,5 @@
class Form::Lettings::Pages::RentValueCheck < ::Form::Page class Form::Lettings::Pages::RentValueCheck < ::Form::Page
def initialize(id, hsh, subsection, check_answers_card_number: nil) def initialize(id, hsh, subsection)
super(id, hsh, subsection) super(id, hsh, subsection)
@depends_on = [{ "rent_soft_validation_triggered?" => true }] @depends_on = [{ "rent_soft_validation_triggered?" => true }]
@copy_key = "lettings.soft_validations.rent_value_check" @copy_key = "lettings.soft_validations.rent_value_check"
@ -23,11 +23,10 @@ class Form::Lettings::Pages::RentValueCheck < ::Form::Page
}, },
], ],
} }
@check_answers_card_number = check_answers_card_number
end end
def questions def questions
@questions ||= [Form::Lettings::Questions::RentValueCheck.new(nil, nil, self, check_answers_card_number: @check_answers_card_number)] @questions ||= [Form::Lettings::Questions::RentValueCheck.new(nil, nil, self)]
end end
def interruption_screen_question_ids def interruption_screen_question_ids

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

@ -4,7 +4,6 @@ class Form::Lettings::Questions::Beds < ::Form::Question
@id = "beds" @id = "beds"
@type = "numeric" @type = "numeric"
@width = 2 @width = 2
@check_answers_card_number = 0
@max = 12 @max = 12
@min = 1 @min = 1
@step = 1 @step = 1

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

@ -3,7 +3,6 @@ class Form::Lettings::Questions::Builtype < ::Form::Question
super super
@id = "builtype" @id = "builtype"
@type = "radio" @type = "radio"
@check_answers_card_number = 0
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

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

@ -3,7 +3,6 @@ class Form::Lettings::Questions::FirstTimePropertyLetAsSocialHousing < ::Form::Q
super super
@id = "first_time_property_let_as_social_housing" @id = "first_time_property_let_as_social_housing"
@type = "radio" @type = "radio"
@check_answers_card_number = 0
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

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

@ -4,7 +4,6 @@ class Form::Lettings::Questions::MajorRepairsDateValueCheck < ::Form::Question
@id = "major_repairs_date_value_check" @id = "major_repairs_date_value_check"
@copy_key = "lettings.soft_validations.major_repairs_date_value_check" @copy_key = "lettings.soft_validations.major_repairs_date_value_check"
@type = "interruption_screen" @type = "interruption_screen"
@check_answers_card_number = 0
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@hidden_in_check_answers = { @hidden_in_check_answers = {
"depends_on" => [ "depends_on" => [

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

@ -4,7 +4,6 @@ class Form::Lettings::Questions::Majorrepairs < ::Form::Question
@id = "majorrepairs" @id = "majorrepairs"
@copy_key = "lettings.property_information.property_major_repairs.majorrepairs" @copy_key = "lettings.property_information.property_major_repairs.majorrepairs"
@type = "radio" @type = "radio"
@check_answers_card_number = 0
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@conditional_for = { "mrcdate" => [1] } @conditional_for = { "mrcdate" => [1] }
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]

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

@ -4,7 +4,6 @@ class Form::Lettings::Questions::Mrcdate < ::Form::Question
@id = "mrcdate" @id = "mrcdate"
@copy_key = "lettings.property_information.property_major_repairs.mrcdate" @copy_key = "lettings.property_information.property_major_repairs.mrcdate"
@type = "date" @type = "date"
@check_answers_card_number = 0
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

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

@ -3,7 +3,6 @@ class Form::Lettings::Questions::PreviousLetType < ::Form::Question
super super
@id = "unitletas" @id = "unitletas"
@type = "radio" @type = "radio"
@check_answers_card_number = 0
@answer_options = answer_options @answer_options = answer_options
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

26
app/models/form/lettings/questions/referral_direct.rb

@ -0,0 +1,26 @@
class Form::Lettings::Questions::ReferralDirect < ::Form::Question
def initialize(id, hsh, page)
super
@id = "referral"
@copy_key = "lettings.household_situation.referral.direct"
@type = "radio"
@check_answers_card_number = 0
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end
def answer_options
{
"20" => {
"value" => "Homeless households owed a duty and not on a housing register or waiting list",
},
"2" => {
"value" => "Tenant applied directly for an available property",
},
"8" => {
"value" => "Relocated through official housing mobility scheme",
},
}.freeze
end
QUESTION_NUMBER_FROM_YEAR = { 2025 => 84 }.freeze
end

2
app/models/form/lettings/questions/referral.rb → app/models/form/lettings/questions/referral_general_needs.rb

@ -1,4 +1,4 @@
class Form::Lettings::Questions::Referral < ::Form::Question class Form::Lettings::Questions::ReferralGeneralNeeds < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "referral" @id = "referral"

105
app/models/form/lettings/questions/referral_general_needs_prp.rb

@ -0,0 +1,105 @@
class Form::Lettings::Questions::ReferralGeneralNeedsPrp < ::Form::Question
def initialize(id, hsh, page)
super
@id = "referral"
@copy_key = "lettings.household_situation.referral.general_needs.prp"
@type = "radio"
@check_answers_card_number = 0
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end
def answer_options
if form.start_year_2024_or_later?
{
"1" => {
"value" => "Internal transfer",
"hint" => "Where the tenant has moved to another social property owned by the same landlord.",
},
"2" => {
"value" => "Tenant applied directly (no referral or nomination)",
},
"3" => {
"value" => "Nominated by a local housing authority",
},
"8" => {
"value" => "Re-located through official housing mobility scheme",
},
"10" => {
"value" => "Other social landlord",
},
"9" => {
"value" => "Community learning disability team",
},
"14" => {
"value" => "Community mental health team",
},
"15" => {
"value" => "Health service",
},
"18" => {
"value" => "Police, probation, prison or youth offending team – tenant had custodial sentence",
},
"19" => {
"value" => "Police, probation, prison or youth offending team – no custodial sentence",
},
"7" => {
"value" => "Voluntary agency",
},
"17" => {
"value" => "Children’s Social Care",
},
"16" => {
"value" => "Other",
},
}.freeze
else
{
"1" => {
"value" => "Internal transfer",
"hint" => "Where the tenant has moved to another social property owned by the same landlord.",
},
"2" => {
"value" => "Tenant applied directly (no referral or nomination)",
},
"3" => {
"value" => "Nominated by a local housing authority",
},
"4" => {
"value" => "Referred by local authority housing department",
},
"8" => {
"value" => "Re-located through official housing mobility scheme",
},
"10" => {
"value" => "Other social landlord",
},
"9" => {
"value" => "Community learning disability team",
},
"14" => {
"value" => "Community mental health team",
},
"15" => {
"value" => "Health service",
},
"12" => {
"value" => "Police, probation or prison",
},
"7" => {
"value" => "Voluntary agency",
},
"13" => {
"value" => "Youth offending team",
},
"17" => {
"value" => "Children’s Social Care",
},
"16" => {
"value" => "Other",
},
}.freeze
end
end
QUESTION_NUMBER_FROM_YEAR = { 2023 => 85, 2024 => 84 }.freeze
end

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

@ -0,0 +1,32 @@
class Form::Lettings::Questions::ReferralHsc < ::Form::Question
def initialize(id, hsh, page)
super
@id = "referral"
@copy_key = "lettings.household_situation.referral.hsc"
@type = "radio"
@check_answers_card_number = 0
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end
def answer_options
{
"15" => {
"value" => "Health service",
},
"9" => {
"value" => "Community learning disability team",
},
"14" => {
"value" => "Community mental health team",
},
"24" => {
"value" => "Adult social services",
},
"17" => {
"value" => "Children's social care",
},
}.freeze
end
QUESTION_NUMBER_FROM_YEAR = { 2025 => 84 }.freeze
end

23
app/models/form/lettings/questions/referral_justice.rb

@ -0,0 +1,23 @@
class Form::Lettings::Questions::ReferralJustice < ::Form::Question
def initialize(id, hsh, page)
super
@id = "referral"
@copy_key = "lettings.household_situation.referral.justice"
@type = "radio"
@check_answers_card_number = 0
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end
def answer_options
{
"18" => {
"value" => "With a custodial sentence",
},
"19" => {
"value" => "No custodial sentence",
},
}.freeze
end
QUESTION_NUMBER_FROM_YEAR = { 2025 => 84 }.freeze
end

29
app/models/form/lettings/questions/referral_la.rb

@ -0,0 +1,29 @@
class Form::Lettings::Questions::ReferralLa < ::Form::Question
def initialize(id, hsh, page)
super
@id = "referral"
@copy_key = "lettings.household_situation.referral.la"
@type = "radio"
@check_answers_card_number = 0
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end
def answer_options
{
"21" => {
"value" => "Local authority lettings",
},
"3" => {
"value" => "PRP lettings nominated by a local authority",
},
"4" => {
"value" => "PRP support lettings referred by a local authority",
},
"22" => {
"value" => "Other",
},
}.freeze
end
QUESTION_NUMBER_FROM_YEAR = { 2025 => 84 }.freeze
end

89
app/models/form/lettings/questions/referral_prp.rb

@ -2,104 +2,25 @@ class Form::Lettings::Questions::ReferralPrp < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "referral" @id = "referral"
@copy_key = "lettings.household_situation.referral.general_needs.prp" @copy_key = "lettings.household_situation.referral.prp"
@type = "radio" @type = "radio"
@check_answers_card_number = 0 @check_answers_card_number = 0
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end
def answer_options def answer_options
if form.start_year_2024_or_later?
{ {
"1" => { "1" => {
"value" => "Internal transfer", "value" => "Internal transfer from another property with the same landlord",
"hint" => "Where the tenant has moved to another social property owned by the same landlord.",
},
"2" => {
"value" => "Tenant applied directly (no referral or nomination)",
},
"3" => {
"value" => "Nominated by a local housing authority",
},
"8" => {
"value" => "Re-located through official housing mobility scheme",
},
"10" => {
"value" => "Other social landlord",
},
"9" => {
"value" => "Community learning disability team",
},
"14" => {
"value" => "Community mental health team",
},
"15" => {
"value" => "Health service",
},
"18" => {
"value" => "Police, probation, prison or youth offending team – tenant had custodial sentence",
},
"19" => {
"value" => "Police, probation, prison or youth offending team – no custodial sentence",
},
"7" => {
"value" => "Voluntary agency",
},
"17" => {
"value" => "Children’s Social Care",
},
"16" => {
"value" => "Other",
},
}.freeze
else
{
"1" => {
"value" => "Internal transfer",
"hint" => "Where the tenant has moved to another social property owned by the same landlord.",
},
"2" => {
"value" => "Tenant applied directly (no referral or nomination)",
},
"3" => {
"value" => "Nominated by a local housing authority",
},
"4" => {
"value" => "Referred by local authority housing department",
},
"8" => {
"value" => "Re-located through official housing mobility scheme",
}, },
"10" => { "10" => {
"value" => "Other social landlord", "value" => "A different PRP landlord",
},
"9" => {
"value" => "Community learning disability team",
}, },
"14" => { "23" => {
"value" => "Community mental health team",
},
"15" => {
"value" => "Health service",
},
"12" => {
"value" => "Police, probation or prison",
},
"7" => {
"value" => "Voluntary agency",
},
"13" => {
"value" => "Youth offending team",
},
"17" => {
"value" => "Children’s Social Care",
},
"16" => {
"value" => "Other", "value" => "Other",
}, },
}.freeze }.freeze
end end
end
QUESTION_NUMBER_FROM_YEAR = { 2023 => 85, 2024 => 84 }.freeze QUESTION_NUMBER_FROM_YEAR = { 2025 => 84 }.freeze
end end

38
app/models/form/lettings/questions/referral_type.rb

@ -0,0 +1,38 @@
class Form::Lettings::Questions::ReferralType < ::Form::Question
def initialize(id, hsh, page)
super
@id = "referral_type"
@copy_key = "lettings.household_situation.referral.type"
@type = "radio"
@check_answers_card_number = 0
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end
def answer_options
{
"1" => {
"value" => "Direct",
},
"2" => {
"value" => "From a local authority housing register or waiting list",
},
"3" => {
"value" => "From a PRP-only housing register or waiting list (no local authority involvement)",
},
"4" => {
"value" => "Health and social care services",
},
"5" => {
"value" => "Police, probation, prison or youth offending team",
},
"6" => {
"value" => "Voluntary agency",
},
"7" => {
"value" => "Other",
},
}.freeze
end
QUESTION_NUMBER_FROM_YEAR = { 2025 => 84 }.freeze
end

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

@ -1,10 +1,9 @@
class Form::Lettings::Questions::RentValueCheck < ::Form::Question class Form::Lettings::Questions::RentValueCheck < ::Form::Question
def initialize(id, hsh, page, check_answers_card_number:) def initialize(id, hsh, page)
super(id, hsh, page) super
@id = "rent_value_check" @id = "rent_value_check"
@copy_key = "lettings.soft_validations.rent_value_check" @copy_key = "lettings.soft_validations.rent_value_check"
@type = "interruption_screen" @type = "interruption_screen"
@check_answers_card_number = check_answers_card_number
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@hidden_in_check_answers = { "depends_on" => [{ "rent_value_check" => 0 }, { "rent_value_check" => 1 }] } @hidden_in_check_answers = { "depends_on" => [{ "rent_value_check" => 0 }, { "rent_value_check" => 1 }] }
end end

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

@ -3,7 +3,6 @@ class Form::Lettings::Questions::Rsnvac < ::Form::Question
super super
@id = "rsnvac" @id = "rsnvac"
@type = "radio" @type = "radio"
@check_answers_card_number = 0
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

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

@ -3,7 +3,6 @@ class Form::Lettings::Questions::RsnvacFirstLet < ::Form::Question
super super
@id = "rsnvac" @id = "rsnvac"
@type = "radio" @type = "radio"
@check_answers_card_number = 0
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

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

@ -3,7 +3,6 @@ class Form::Lettings::Questions::Sheltered < ::Form::Question
super super
@id = "sheltered" @id = "sheltered"
@type = "radio" @type = "radio"
@check_answers_card_number = 0
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

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

@ -3,7 +3,6 @@ class Form::Lettings::Questions::UnittypeGn < ::Form::Question
super super
@id = "unittype_gn" @id = "unittype_gn"
@type = "radio" @type = "radio"
@check_answers_card_number = 0
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

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

@ -4,7 +4,6 @@ class Form::Lettings::Questions::VoidDateValueCheck < ::Form::Question
@id = "void_date_value_check" @id = "void_date_value_check"
@copy_key = "lettings.soft_validations.void_date_value_check" @copy_key = "lettings.soft_validations.void_date_value_check"
@type = "interruption_screen" @type = "interruption_screen"
@check_answers_card_number = 0
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@hidden_in_check_answers = { "depends_on" => [{ "void_date_value_check" => 0 }, { "void_date_value_check" => 1 }] } @hidden_in_check_answers = { "depends_on" => [{ "void_date_value_check" => 0 }, { "void_date_value_check" => 1 }] }
end end

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

@ -3,7 +3,6 @@ class Form::Lettings::Questions::Voiddate < ::Form::Question
super super
@id = "voiddate" @id = "voiddate"
@type = "date" @type = "date"
@check_answers_card_number = 0
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
@top_guidance_partial = "void_date" @top_guidance_partial = "void_date"
end end

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

@ -3,7 +3,6 @@ class Form::Lettings::Questions::Wheelchair < ::Form::Question
super super
@id = "wchair" @id = "wchair"
@type = "radio" @type = "radio"
@check_answers_card_number = 0
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

23
app/models/form/lettings/subsections/household_situation.rb

@ -21,11 +21,28 @@ class Form::Lettings::Subsections::HouseholdSituation < ::Form::Subsection
Form::Lettings::Pages::ReasonablePreference.new("reasonable_preference", nil, self), Form::Lettings::Pages::ReasonablePreference.new("reasonable_preference", nil, self),
Form::Lettings::Pages::ReasonablePreferenceReason.new(nil, nil, self), Form::Lettings::Pages::ReasonablePreferenceReason.new(nil, nil, self),
Form::Lettings::Pages::AllocationSystem.new("allocation_system", nil, self), Form::Lettings::Pages::AllocationSystem.new("allocation_system", nil, self),
Form::Lettings::Pages::Referral.new(nil, nil, self), referral_questions,
Form::Lettings::Pages::ReferralValueCheck.new(nil, nil, self),
].flatten.compact
end
def referral_questions
if form.start_year_2025_or_later?
[
Form::Lettings::Pages::ReferralType.new(nil, nil, self),
Form::Lettings::Pages::ReferralDirect.new(nil, nil, self),
Form::Lettings::Pages::ReferralLa.new(nil, nil, self),
Form::Lettings::Pages::ReferralPrp.new(nil, nil, self), Form::Lettings::Pages::ReferralPrp.new(nil, nil, self),
Form::Lettings::Pages::ReferralHsc.new(nil, nil, self),
Form::Lettings::Pages::ReferralJustice.new(nil, nil, self),
]
else
[
Form::Lettings::Pages::ReferralGeneralNeeds.new(nil, nil, self),
Form::Lettings::Pages::ReferralGeneralNeedsPrp.new(nil, nil, self),
Form::Lettings::Pages::ReferralSupportedHousing.new(nil, nil, self), Form::Lettings::Pages::ReferralSupportedHousing.new(nil, nil, self),
Form::Lettings::Pages::ReferralSupportedHousingPrp.new(nil, nil, self), Form::Lettings::Pages::ReferralSupportedHousingPrp.new(nil, nil, self),
Form::Lettings::Pages::ReferralValueCheck.new(nil, nil, self), ]
].compact end
end end
end end

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

@ -20,7 +20,7 @@ class Form::Lettings::Subsections::IncomeAndBenefits < ::Form::Subsection
Form::Lettings::Pages::RentBiWeekly.new(nil, nil, self), Form::Lettings::Pages::RentBiWeekly.new(nil, nil, self),
Form::Lettings::Pages::Rent4Weekly.new(nil, nil, self), Form::Lettings::Pages::Rent4Weekly.new(nil, nil, self),
Form::Lettings::Pages::RentMonthly.new(nil, nil, self), Form::Lettings::Pages::RentMonthly.new(nil, nil, self),
Form::Lettings::Pages::RentValueCheck.new("brent_rent_value_check", nil, self, check_answers_card_number: 0), Form::Lettings::Pages::RentValueCheck.new("brent_rent_value_check", nil, self),
Form::Lettings::Pages::SchargeValueCheck.new(nil, nil, self), Form::Lettings::Pages::SchargeValueCheck.new(nil, nil, self),
Form::Lettings::Pages::PschargeValueCheck.new(nil, nil, self), Form::Lettings::Pages::PschargeValueCheck.new(nil, nil, self),
Form::Lettings::Pages::SupchargValueCheck.new(nil, nil, self), Form::Lettings::Pages::SupchargValueCheck.new(nil, nil, self),

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

@ -8,19 +8,17 @@ class Form::Lettings::Subsections::PropertyInformation < ::Form::Subsection
def pages def pages
@pages ||= [ @pages ||= [
(first_let_questions if form.start_year_2025_or_later?),
uprn_questions, uprn_questions,
Form::Lettings::Pages::PropertyLocalAuthority.new(nil, nil, self), Form::Lettings::Pages::PropertyLocalAuthority.new(nil, nil, self),
Form::Lettings::Pages::RentValueCheck.new("local_authority_rent_value_check", nil, self, check_answers_card_number: nil), Form::Lettings::Pages::RentValueCheck.new("local_authority_rent_value_check", nil, self),
Form::Lettings::Pages::FirstTimePropertyLetAsSocialHousing.new(nil, nil, self), (first_let_questions unless form.start_year_2025_or_later?),
Form::Lettings::Pages::PropertyLetType.new(nil, nil, self),
Form::Lettings::Pages::PropertyVacancyReasonNotFirstLet.new(nil, nil, self),
Form::Lettings::Pages::PropertyVacancyReasonFirstLet.new(nil, nil, self),
number_of_times_relet, number_of_times_relet,
Form::Lettings::Pages::PropertyUnitType.new(nil, nil, self), Form::Lettings::Pages::PropertyUnitType.new(nil, nil, self),
Form::Lettings::Pages::PropertyBuildingType.new(nil, nil, self), Form::Lettings::Pages::PropertyBuildingType.new(nil, nil, self),
Form::Lettings::Pages::PropertyWheelchairAccessible.new(nil, nil, self), Form::Lettings::Pages::PropertyWheelchairAccessible.new(nil, nil, self),
Form::Lettings::Pages::PropertyNumberOfBedrooms.new(nil, nil, self), Form::Lettings::Pages::PropertyNumberOfBedrooms.new(nil, nil, self),
Form::Lettings::Pages::RentValueCheck.new("beds_rent_value_check", nil, self, check_answers_card_number: 0), Form::Lettings::Pages::RentValueCheck.new("beds_rent_value_check", nil, self),
Form::Lettings::Pages::VoidDate.new(nil, nil, self), Form::Lettings::Pages::VoidDate.new(nil, nil, self),
Form::Lettings::Pages::VoidDateValueCheck.new(nil, nil, self), Form::Lettings::Pages::VoidDateValueCheck.new(nil, nil, self),
Form::Lettings::Pages::PropertyMajorRepairs.new(nil, nil, self), Form::Lettings::Pages::PropertyMajorRepairs.new(nil, nil, self),
@ -52,6 +50,15 @@ class Form::Lettings::Subsections::PropertyInformation < ::Form::Subsection
Form::Lettings::Pages::PropertyNumberOfTimesRelet.new(nil, nil, self) unless form.start_year_2024_or_later? Form::Lettings::Pages::PropertyNumberOfTimesRelet.new(nil, nil, self) unless form.start_year_2024_or_later?
end end
def first_let_questions
[
Form::Lettings::Pages::FirstTimePropertyLetAsSocialHousing.new(nil, nil, self),
Form::Lettings::Pages::PropertyLetType.new(nil, nil, self),
Form::Lettings::Pages::PropertyVacancyReasonNotFirstLet.new(nil, nil, self),
Form::Lettings::Pages::PropertyVacancyReasonFirstLet.new(nil, nil, self),
]
end
def displayed_in_tasklist?(log) def displayed_in_tasklist?(log)
!(log.is_supported_housing? && log.is_renewal?) !(log.is_supported_housing? && log.is_renewal?)
end end

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

@ -24,7 +24,7 @@ class Form::Sales::Pages::LivingBeforePurchase < ::Form::Page
end end
def page_routed_to?(log) def page_routed_to?(log)
return false if form.start_year_2025_or_later? && log.resale != 2 return false if form.start_year_2025_or_later? && log.resale != 2 && log.ownershipsch == 1
if @joint_purchase if @joint_purchase
log.joint_purchase? log.joint_purchase?

2
app/models/form/sales/questions/living_before_purchase_years.rb

@ -9,7 +9,7 @@ class Form::Sales::Questions::LivingBeforePurchaseYears < ::Form::Question
@step = 1 @step = 1
@width = 5 @width = 5
@ownershipsch = ownershipsch @ownershipsch = ownershipsch
@question_number = question_number @question_number = QUESTION_NUMBER_FROM_YEAR_AND_OWNERSHIP.fetch(form.start_date.year, QUESTION_NUMBER_FROM_YEAR_AND_OWNERSHIP.max_by { |k, _v| k }.last)[ownershipsch]
end end
def suffix_label(log) def suffix_label(log)

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

@ -10,10 +10,7 @@ module Forms
attribute :organisation_id, :integer attribute :organisation_id, :integer
def view_path def view_path
case year "bulk_upload_#{log_type}_logs/forms/prepare_your_file"
when 2024
"bulk_upload_#{log_type}_logs/forms/prepare_your_file_2024"
end
end end
def back_path def back_path
@ -42,6 +39,10 @@ module Forms
"#{year} to #{year + 1}" "#{year} to #{year + 1}"
end end
def slash_year_combo
"#{year}/#{(year + 1) % 100}"
end
def save! def save!
true true
end end

3
app/models/validations/household_validations.rb

@ -32,6 +32,7 @@ module Validations::HouseholdValidations
if record.is_reason_permanently_decanted? && record.referral.present? && !record.is_internal_transfer? if record.is_reason_permanently_decanted? && record.referral.present? && !record.is_internal_transfer?
record.errors.add :referral, I18n.t("validations.lettings.household.referral.leaving_last_settled_home.reason_permanently_decanted") record.errors.add :referral, I18n.t("validations.lettings.household.referral.leaving_last_settled_home.reason_permanently_decanted")
record.errors.add :referral_type, I18n.t("validations.lettings.household.referral.leaving_last_settled_home.reason_permanently_decanted")
record.errors.add :reason, I18n.t("validations.lettings.household.reason.leaving_last_settled_home.not_internal_transfer") record.errors.add :reason, I18n.t("validations.lettings.household.reason.leaving_last_settled_home.not_internal_transfer")
end end
@ -171,6 +172,7 @@ module Validations::HouseholdValidations
label = record.form.get_question("prevten", record).present? ? record.form.get_question("prevten", record).label_from_value(record.prevten) : "" label = record.form.get_question("prevten", record).present? ? record.form.get_question("prevten", record).label_from_value(record.prevten) : ""
record.errors.add :prevten, :internal_transfer_non_social_housing, message: I18n.t("validations.lettings.household.prevten.internal_transfer", prevten: label) record.errors.add :prevten, :internal_transfer_non_social_housing, message: I18n.t("validations.lettings.household.prevten.internal_transfer", prevten: label)
record.errors.add :referral, :internal_transfer_non_social_housing, message: I18n.t("validations.lettings.household.referral.prevten_invalid", prevten: label) record.errors.add :referral, :internal_transfer_non_social_housing, message: I18n.t("validations.lettings.household.referral.prevten_invalid", prevten: label)
record.errors.add :referral_type, :internal_transfer_non_social_housing, message: I18n.t("validations.lettings.household.referral.prevten_invalid", prevten: label)
end end
end end
@ -180,6 +182,7 @@ module Validations::HouseholdValidations
if record.is_internal_transfer? && record.owning_organisation.provider_type == "PRP" && record.is_prevten_la_general_needs? if record.is_internal_transfer? && record.owning_organisation.provider_type == "PRP" && record.is_prevten_la_general_needs?
record.errors.add :prevten, :internal_transfer_fixed_or_lifetime, message: I18n.t("validations.lettings.household.prevten.la_general_needs.internal_transfer") record.errors.add :prevten, :internal_transfer_fixed_or_lifetime, message: I18n.t("validations.lettings.household.prevten.la_general_needs.internal_transfer")
record.errors.add :referral, :internal_transfer_fixed_or_lifetime, message: I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer") record.errors.add :referral, :internal_transfer_fixed_or_lifetime, message: I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer")
record.errors.add :referral_type, :internal_transfer_fixed_or_lifetime, message: I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer")
end end
end end

1
app/models/validations/property_validations.rb

@ -11,6 +11,7 @@ module Validations::PropertyValidations
if record.is_relet_to_temp_tenant? && REFERRAL_INVALID_TMP.include?(record.referral) if record.is_relet_to_temp_tenant? && REFERRAL_INVALID_TMP.include?(record.referral)
record.errors.add :rsnvac, I18n.t("validations.lettings.property.rsnvac.referral_invalid") record.errors.add :rsnvac, I18n.t("validations.lettings.property.rsnvac.referral_invalid")
record.errors.add :referral, :referral_invalid, message: I18n.t("validations.lettings.property.referral.rsnvac_non_temp") record.errors.add :referral, :referral_invalid, message: I18n.t("validations.lettings.property.referral.rsnvac_non_temp")
record.errors.add :referral_type, :referral_invalid, message: I18n.t("validations.lettings.property.referral.rsnvac_non_temp")
end end
if record.renewal.present? && record.renewal.zero? && record.rsnvac == 14 if record.renewal.present? && record.renewal.zero? && record.rsnvac == 14

2
app/services/bulk_upload/lettings/log_creator.rb

@ -34,6 +34,8 @@ private
BulkUpload::Lettings::Year2023::CsvParser.new(path:) BulkUpload::Lettings::Year2023::CsvParser.new(path:)
when 2024 when 2024
BulkUpload::Lettings::Year2024::CsvParser.new(path:) BulkUpload::Lettings::Year2024::CsvParser.new(path:)
when 2025
BulkUpload::Lettings::Year2025::CsvParser.new(path:)
else else
raise "csv parser not found" raise "csv parser not found"
end end

2
app/services/bulk_upload/lettings/validator.rb

@ -111,6 +111,8 @@ private
BulkUpload::Lettings::Year2023::CsvParser.new(path:) BulkUpload::Lettings::Year2023::CsvParser.new(path:)
when 2024 when 2024
BulkUpload::Lettings::Year2024::CsvParser.new(path:) BulkUpload::Lettings::Year2024::CsvParser.new(path:)
when 2025
BulkUpload::Lettings::Year2025::CsvParser.new(path:)
else else
raise "csv parser not found" raise "csv parser not found"
end end

43
app/services/bulk_upload/lettings/year2024/row_parser.rb

@ -418,6 +418,7 @@ class BulkUpload::Lettings::Year2024::RowParser
validate :validate_no_and_dont_know_disabled_needs_conjunction, on: :after_log validate :validate_no_and_dont_know_disabled_needs_conjunction, on: :after_log
validate :validate_no_housing_needs_questions_answered, on: :after_log validate :validate_no_housing_needs_questions_answered, on: :after_log
validate :validate_reasonable_preference_homeless, on: :after_log validate :validate_reasonable_preference_homeless, on: :after_log
validate :validate_reasonable_preference_dont_know, on: :after_log
validate :validate_condition_effects, on: :after_log validate :validate_condition_effects, on: :after_log
validate :validate_if_log_already_exists, on: :after_log, if: -> { FeatureToggle.bulk_upload_duplicate_log_check_enabled? } validate :validate_if_log_already_exists, on: :after_log, if: -> { FeatureToggle.bulk_upload_duplicate_log_check_enabled? }
@ -444,6 +445,7 @@ class BulkUpload::Lettings::Year2024::RowParser
validate :validate_incomplete_soft_validations, on: :after_log validate :validate_incomplete_soft_validations, on: :after_log
validate :validate_nationality, on: :after_log validate :validate_nationality, on: :after_log
validate :validate_reasonpref_reason_values, on: :after_log
validate :validate_nulls, on: :after_log validate :validate_nulls, on: :after_log
@ -677,6 +679,17 @@ private
end end
end end
def validate_reasonpref_reason_values
valid_reasonpref_reason_options = %w[0 1]
%w[field_107 field_108 field_109 field_110 field_111].each do |field|
next unless send(field).present? && !valid_reasonpref_reason_options.include?(send(field).to_s)
question_text = QUESTIONS[field.to_sym]
question_text[0] = question_text[0].downcase
errors.add(field.to_sym, I18n.t("#{ERROR_BASE_KEY}.invalid_option", question: question_text))
end
end
def duplicate_check_fields def duplicate_check_fields
[ [
"startdate", "startdate",
@ -739,6 +752,15 @@ private
end end
end end
def validate_reasonable_preference_dont_know
if rp_dontknow_conflict?
errors.add(:field_111, I18n.t("#{ERROR_BASE_KEY}.reasonpref.conflict.dont_know"))
%i[field_107 field_108 field_109 field_110].each do |field|
errors.add(field, I18n.t("#{ERROR_BASE_KEY}.reasonpref.conflict.other")) if send(field) == 1
end
end
end
def validate_reasonable_preference_homeless def validate_reasonable_preference_homeless
reason_fields = %i[field_107 field_108 field_109 field_110 field_111] reason_fields = %i[field_107 field_108 field_109 field_110 field_111]
if field_106 == 1 && reason_fields.all? { |field| attributes[field.to_s].blank? } if field_106 == 1 && reason_fields.all? { |field| attributes[field.to_s].blank? }
@ -1258,11 +1280,11 @@ private
attributes["ppostcode_full"] = ppostcode_full attributes["ppostcode_full"] = ppostcode_full
attributes["reasonpref"] = field_106 attributes["reasonpref"] = field_106
attributes["rp_homeless"] = field_107 attributes["rp_homeless"] = field_107 unless rp_dontknow_conflict?
attributes["rp_insan_unsat"] = field_108 attributes["rp_insan_unsat"] = field_108 unless rp_dontknow_conflict?
attributes["rp_medwel"] = field_109 attributes["rp_medwel"] = field_109 unless rp_dontknow_conflict?
attributes["rp_hardship"] = field_110 attributes["rp_hardship"] = field_110 unless rp_dontknow_conflict?
attributes["rp_dontknow"] = field_111 attributes["rp_dontknow"] = field_111 unless rp_dontknow_conflict?
attributes["cbl"] = cbl attributes["cbl"] = cbl
attributes["chr"] = chr attributes["chr"] = chr
@ -1649,4 +1671,15 @@ private
def bulk_upload_organisation def bulk_upload_organisation
Organisation.find(bulk_upload.organisation_id) Organisation.find(bulk_upload.organisation_id)
end end
def rp_dontknow_conflict?
other_reason_fields = %i[field_107 field_108 field_109 field_110]
if field_106 == 1
selected_reasons = other_reason_fields.select { |field| send(field) == 1 }
dont_know_selected = field_111 == 1
return true if selected_reasons.any? && dont_know_selected
end
false
end
end end

122
app/services/bulk_upload/lettings/year2025/csv_parser.rb

@ -0,0 +1,122 @@
require "csv"
class BulkUpload::Lettings::Year2025::CsvParser
include CollectionTimeHelper
FIELDS = 129
MAX_COLUMNS = 130
FORM_YEAR = 2025
attr_reader :path
def initialize(path:)
@path = path
end
def row_offset
if with_headers?
rows.find_index { |row| row[0].present? && row[0].match(/field number/i) } + 1
else
0
end
end
def col_offset
with_headers? ? 1 : 0
end
def cols
@cols ||= ("A".."DZ").to_a
end
def row_parsers
@row_parsers ||= body_rows.map { |row|
next if row.empty?
stripped_row = row[col_offset..]
hash = Hash[field_numbers.zip(stripped_row)]
BulkUpload::Lettings::Year2025::RowParser.new(hash)
}.compact
end
def body_rows
rows[row_offset..]
end
def rows
@rows ||= CSV.parse(normalised_string, row_sep:)
end
def column_for_field(field)
cols[field_numbers.find_index(field) + col_offset]
end
def correct_field_count?
valid_field_numbers_count = field_numbers.count { |f| f != "field_blank" }
valid_field_numbers_count == FIELDS
end
def too_many_columns?
return if with_headers?
max_columns_count = body_rows.map(&:size).max - col_offset
max_columns_count > MAX_COLUMNS
end
def wrong_template_for_year?
collection_start_year_for_date(first_record_start_date) != FORM_YEAR
rescue Date::Error
false
end
def missing_required_headers?
!with_headers?
end
private
def default_field_numbers
(1..FIELDS).map { |h| h.present? && h.to_s.match?(/^[0-9]+$/) ? "field_#{h}" : "field_blank" }
end
def field_numbers
@field_numbers ||= if with_headers?
rows[row_offset - 1][col_offset..].map { |h| h.present? && h.match?(/^[0-9]+$/) ? "field_#{h}" : "field_blank" }
else
default_field_numbers
end
end
def with_headers?
rows.map { |r| r[0] }.any? { |cell| cell&.match?(/field number/i) }
end
def row_sep
"\n"
end
def normalised_string
return @normalised_string if @normalised_string
@normalised_string = File.read(path, encoding: "bom|utf-8")
@normalised_string.gsub!("\r\n", "\n")
@normalised_string.scrub!("")
@normalised_string.tr!("\r", "\n")
@normalised_string
end
def first_record_start_date
if with_headers?
year = row_parsers.first.field_10.to_s.strip.length.between?(1, 2) ? row_parsers.first.field_10.to_i + 2000 : row_parsers.first.field_10.to_i
Date.new(year, row_parsers.first.field_9.to_i, row_parsers.first.field_8.to_i)
else
year = rows.first[9].to_s.strip.length.between?(1, 2) ? rows.first[9].to_i + 2000 : rows.first[9].to_i
Date.new(year, rows.first[8].to_i, rows.first[7].to_i)
end
end
end

1707
app/services/bulk_upload/lettings/year2025/row_parser.rb

File diff suppressed because it is too large Load Diff

2
app/services/bulk_upload/sales/log_creator.rb

@ -33,6 +33,8 @@ private
BulkUpload::Sales::Year2023::CsvParser.new(path:) BulkUpload::Sales::Year2023::CsvParser.new(path:)
when 2024 when 2024
BulkUpload::Sales::Year2024::CsvParser.new(path:) BulkUpload::Sales::Year2024::CsvParser.new(path:)
when 2025
BulkUpload::Sales::Year2025::CsvParser.new(path:)
else else
raise "csv parser not found" raise "csv parser not found"
end end

2
app/services/bulk_upload/sales/validator.rb

@ -108,6 +108,8 @@ private
BulkUpload::Sales::Year2023::CsvParser.new(path:) BulkUpload::Sales::Year2023::CsvParser.new(path:)
when 2024 when 2024
BulkUpload::Sales::Year2024::CsvParser.new(path:) BulkUpload::Sales::Year2024::CsvParser.new(path:)
when 2025
BulkUpload::Sales::Year2025::CsvParser.new(path:)
else else
raise "csv parser not found" raise "csv parser not found"
end end

1
app/services/bulk_upload/sales/year2024/row_parser.rb

@ -375,6 +375,7 @@ class BulkUpload::Sales::Year2024::RowParser
greater_than_or_equal_to: 0, greater_than_or_equal_to: 0,
less_than_or_equal_to: 70, less_than_or_equal_to: 70,
if: :discounted_ownership?, if: :discounted_ownership?,
allow_blank: true,
}, },
on: :before_log on: :before_log

124
app/services/bulk_upload/sales/year2025/csv_parser.rb

@ -0,0 +1,124 @@
require "csv"
class BulkUpload::Sales::Year2025::CsvParser
include CollectionTimeHelper
FIELDS = 121
MAX_COLUMNS = 142
FORM_YEAR = 2025
attr_reader :path
def initialize(path:)
@path = path
end
def row_offset
if with_headers?
rows.find_index { |row| row[0].present? && row[0].match(/field number/i) } + 1
else
0
end
end
def col_offset
with_headers? ? 1 : 0
end
def cols
@cols ||= ("A".."DR").to_a
end
def row_parsers
@row_parsers ||= body_rows.map { |row|
next if row.empty?
stripped_row = row[col_offset..]
hash = Hash[field_numbers.zip(stripped_row)]
BulkUpload::Sales::Year2025::RowParser.new(hash)
}.compact
end
def body_rows
rows[row_offset..]
end
def rows
@rows ||= CSV.parse(normalised_string, row_sep:)
end
def column_for_field(field)
cols[field_numbers.find_index(field) + col_offset]
end
def wrong_template_for_year?
collection_start_year_for_date(first_record_start_date) != FORM_YEAR
rescue Date::Error
false
end
def missing_required_headers?
!with_headers?
end
def correct_field_count?
valid_field_numbers_count = field_numbers.count { |f| f != "field_blank" }
valid_field_numbers_count == FIELDS
end
private
def default_field_numbers
(1..FIELDS).map do |number|
if number.to_s.match?(/^[0-9]+$/)
"field_#{number}"
else
"field_blank"
end
end
end
def field_numbers
@field_numbers ||= if with_headers?
rows[row_offset - 1][col_offset..].map { |number| number.to_s.match?(/^[0-9]+$/) ? "field_#{number}" : "field_blank" }
else
default_field_numbers
end
end
def headers
@headers ||= ("field_1".."field_#{FIELDS}").to_a
end
def with_headers?
# we will eventually want to validate that headers exist for this year
rows.map { |r| r[0] }.any? { |cell| cell&.match?(/field number/i) }
end
def row_sep
"\n"
end
def normalised_string
return @normalised_string if @normalised_string
@normalised_string = File.read(path, encoding: "bom|utf-8")
@normalised_string.gsub!("\r\n", "\n")
@normalised_string.scrub!("")
@normalised_string.tr!("\r", "\n")
@normalised_string
end
def first_record_start_date
if with_headers?
year = row_parsers.first.field_3.to_s.strip.length.between?(1, 2) ? row_parsers.first.field_3.to_i + 2000 : row_parsers.first.field_3.to_i
Date.new(year, row_parsers.first.field_2.to_i, row_parsers.first.field_1.to_i)
else
year = rows.first[2].to_s.strip.length.between?(1, 2) ? rows.first[2].to_i + 2000 : rows.first[2].to_i
Date.new(year, rows.first[1].to_i, rows.first[0].to_i)
end
end
end

1502
app/services/bulk_upload/sales/year2025/row_parser.rb

File diff suppressed because it is too large Load Diff

4
app/views/bulk_upload_lettings_logs/forms/prepare_your_file_2024.html.erb → app/views/bulk_upload_lettings_logs/forms/prepare_your_file.html.erb

@ -14,7 +14,7 @@
<h2 class="govuk-heading-s">Download template</h2> <h2 class="govuk-heading-s">Download template</h2>
<p class="govuk-body govuk-!-margin-bottom-2"><%= govuk_link_to "Download the lettings bulk upload template (2024 to 2025)", @form.template_path %></p> <p class="govuk-body govuk-!-margin-bottom-2"><%= govuk_link_to "Download the lettings bulk upload template (#{@form.year_combo})", @form.template_path %></p>
<p class="govuk-body govuk-!-margin-bottom-2">There are 8 rows of content in the templates. These rows are called the ‘headers’. They contain the CORE form questions and guidance about which questions are required and how to format your answers.</p> <p class="govuk-body govuk-!-margin-bottom-2">There are 8 rows of content in the templates. These rows are called the ‘headers’. They contain the CORE form questions and guidance about which questions are required and how to format your answers.</p>
<h2 class="govuk-heading-s">Create your file</h2> <h2 class="govuk-heading-s">Create your file</h2>
@ -22,7 +22,7 @@
<%= govuk_list [ <%= govuk_list [
"Fill in the template with data from your housing management system. Your data should go below the headers, with one row per log. Leave column A blank - the bulk upload fields start in column B.", "Fill in the template with data from your housing management system. Your data should go below the headers, with one row per log. Leave column A blank - the bulk upload fields start in column B.",
"Make sure each column of your data aligns with the matching headers above. You may need to reorder your data.", "Make sure each column of your data aligns with the matching headers above. You may need to reorder your data.",
"Use the #{govuk_link_to 'Lettings bulk upload Specification (2024 to 2025)', @form.specification_path} to check your data is in the correct format.".html_safe, "Use the #{govuk_link_to "Lettings bulk upload Specification (#{@form.year_combo})", @form.specification_path} to check your data is in the correct format.".html_safe,
"<strong>Username field:</strong> To assign a log to someone else, enter the email address they use to log into CORE.".html_safe, "<strong>Username field:</strong> To assign a log to someone else, enter the email address they use to log into CORE.".html_safe,
"If you have reordered the headers, keep the headers in the file.", "If you have reordered the headers, keep the headers in the file.",
], type: :bullet %> ], type: :bullet %>

6
app/views/bulk_upload_sales_logs/forms/prepare_your_file_2024.html.erb → app/views/bulk_upload_sales_logs/forms/prepare_your_file.html.erb

@ -14,15 +14,15 @@
<h2 class="govuk-heading-s">Download template</h2> <h2 class="govuk-heading-s">Download template</h2>
<p class="govuk-body govuk-!-margin-bottom-2">Use one of these templates to upload logs for 2024/25:</p> <p class="govuk-body govuk-!-margin-bottom-2">Use one of these templates to upload logs for <%= @form.slash_year_combo %>:</p>
<p class="govuk-body govuk-!-margin-bottom-2"><%= govuk_link_to "Download the sales bulk upload template (2024 to 2025)", @form.template_path %>: In this template, the questions are in the same order as the 2024/25 paper form and web form.</p> <p class="govuk-body govuk-!-margin-bottom-2"><%= govuk_link_to "Download the sales bulk upload template (#{@form.year_combo})", @form.template_path %>: In this template, the questions are in the same order as the <%= @form.slash_year_combo %> paper form and web form.</p>
<p class="govuk-body govuk-!-margin-bottom-2">There are 8 rows of content in the templates. These rows are called the ‘headers’. They contain the CORE form questions and guidance about which questions are required and how to format your answers.</p> <p class="govuk-body govuk-!-margin-bottom-2">There are 8 rows of content in the templates. These rows are called the ‘headers’. They contain the CORE form questions and guidance about which questions are required and how to format your answers.</p>
<h2 class="govuk-heading-s">Create your file</h2> <h2 class="govuk-heading-s">Create your file</h2>
<%= govuk_list [ <%= govuk_list [
"Fill in the template with data from your housing management system. Your data should go below the headers, with one row per log. The bulk upload fields start at column B. Leave column A blank.", "Fill in the template with data from your housing management system. Your data should go below the headers, with one row per log. The bulk upload fields start at column B. Leave column A blank.",
"Make sure each column of your data aligns with the matching headers above. You may need to reorder your data.", "Make sure each column of your data aligns with the matching headers above. You may need to reorder your data.",
"Use the #{govuk_link_to 'Sales bulk upload Specification (2024 to 2025)', @form.specification_path} to check your data is in the correct format.".html_safe, "Use the #{govuk_link_to "Sales bulk upload Specification (#{@form.year_combo})", @form.specification_path} to check your data is in the correct format.".html_safe,
"<strong>Username field:</strong> To assign a log to someone else, enter the email address they use to log into CORE.".html_safe, "<strong>Username field:</strong> To assign a log to someone else, enter the email address they use to log into CORE.".html_safe,
"If you have reordered the headers, keep the headers in the file.", "If you have reordered the headers, keep the headers in the file.",
], type: :bullet %> ], type: :bullet %>

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

@ -25,6 +25,7 @@
<% end %> <% end %>
</p> </p>
<% elsif @log.status == "not_started" %> <% elsif @log.status == "not_started" %>
<p class="govuk-body"><%= govuk_link_to "Guidance for submitting social housing lettings and sales data (opens in a new tab)", guidance_path, target: "#" %></p>
<p class="govuk-body">This log has not been started.</p> <p class="govuk-body">This log has not been started.</p>
<% elsif @log.status == "completed" %> <% elsif @log.status == "completed" %>
<p class="govuk-body"> <p class="govuk-body">
@ -36,6 +37,7 @@
</p> </p>
<% end %> <% end %>
<%= deadline_text(@log) %>
<%= render "tasklist" %> <%= render "tasklist" %>
<%= edit_actions_for_log(@log, bulk_upload_filter_applied) %> <%= edit_actions_for_log(@log, bulk_upload_filter_applied) %>

2
config/locales/en.yml

@ -57,6 +57,8 @@ en:
<<: *bulk_upload__row_parser__base <<: *bulk_upload__row_parser__base
bulk_upload/lettings/year2023/row_parser: bulk_upload/lettings/year2023/row_parser:
<<: *bulk_upload__row_parser__base <<: *bulk_upload__row_parser__base
bulk_upload/sales/year2025/row_parser:
<<: *bulk_upload__row_parser__base
bulk_upload/sales/year2024/row_parser: bulk_upload/sales/year2024/row_parser:
<<: *bulk_upload__row_parser__base <<: *bulk_upload__row_parser__base
bulk_upload/sales/year2023/row_parser: bulk_upload/sales/year2023/row_parser:

32
config/locales/forms/2025/lettings/household_situation.en.yml

@ -112,29 +112,39 @@ en:
question_text: "How was this letting allocated?" question_text: "How was this letting allocated?"
referral: referral:
supported_housing: type:
prp:
page_header: "" page_header: ""
check_answer_label: "Source of referral for letting" check_answer_label: "Source of referral for letting"
check_answer_prompt: "" check_answer_prompt: "Select source of referral"
hint_text: ""
question_text: "What was the source of referral for this letting?"
direct:
page_header: ""
check_answer_label: "Source of referral for letting"
check_answer_prompt: "Select source of referral"
hint_text: "" hint_text: ""
question_text: "What was the source of referral for this letting?" question_text: "What was the source of referral for this letting?"
la: la:
page_header: "" page_header: ""
check_answer_label: "Source of referral for letting" check_answer_label: "Source of referral for letting"
check_answer_prompt: "" check_answer_prompt: "Select source of referral"
hint_text: "You told us that you are a local authority. We have removed some options because of this." hint_text: ""
question_text: "What was the source of referral for this letting?" question_text: "What was the source of referral for this letting?"
general_needs:
prp: prp:
page_header: "" page_header: ""
check_answer_label: "Source of referral for letting" check_answer_label: "Source of referral for letting"
check_answer_prompt: "" check_answer_prompt: "Select source of referral"
hint_text: "You told us that the needs type is general needs. We have removed some options because of this." hint_text: ""
question_text: "What was the source of referral for this letting?" question_text: "What was the source of referral for this letting?"
la: hsc:
page_header: "" page_header: ""
check_answer_label: "Source of referral for letting" check_answer_label: "Source of referral for letting"
check_answer_prompt: "" check_answer_prompt: "Select source of referral"
hint_text: "You told us that you are a local authority and that the needs type is general needs. We have removed some options because of this." hint_text: ""
question_text: "What was the source of referral for this letting?"
justice:
page_header: ""
check_answer_label: "Source of referral for letting"
check_answer_prompt: "Select source of referral"
hint_text: ""
question_text: "What was the source of referral for this letting?" question_text: "What was the source of referral for this letting?"

2
config/locales/forms/2025/sales/sale_information.en.yml

@ -148,7 +148,7 @@ en:
value_shared_ownership: value_shared_ownership:
check_answer_label: "Full purchase price" check_answer_label: "Full purchase price"
check_answer_prompt: "" check_answer_prompt: ""
hint_text: "Enter the full purchase price of the property before any discounts are applied. For shared ownership, enter the full purchase price paid for 100% equity (this is equal to the value of the share owned by the PRP plus the value bought by the purchaser)." hint_text: "Enter the full purchase price of the property before any discounts are applied. This is the full purchase price paid for 100% equity (this is equal to the value of the share owned by the PRP plus the value bought by the purchaser)."
question_text: "What was the full purchase price?" question_text: "What was the full purchase price?"
value_shared_ownership_staircase: value_shared_ownership_staircase:
check_answer_label: "Full purchase price" check_answer_label: "Full purchase price"

4
config/locales/validations/lettings/2024/bulk_upload.en.yml

@ -58,3 +58,7 @@ en:
invalid: "Select a valid nationality." invalid: "Select a valid nationality."
charges: charges:
missing_charges: "Please enter the %{sentence_fragment}. If there is no %{sentence_fragment}, please enter '0'." missing_charges: "Please enter the %{sentence_fragment}. If there is no %{sentence_fragment}, please enter '0'."
reasonpref:
conflict:
dont_know: "You cannot select 'Don't know' if any of the other reasonable preference reasons are also selected."
other: "You cannot select this reasonable preference reason as you've also selected 'Don't know' as a reason."

64
config/locales/validations/lettings/2025/bulk_upload.en.yml

@ -0,0 +1,64 @@
en:
validations:
lettings:
2025:
bulk_upload:
not_answered: "You must answer %{question}"
invalid_option: "Enter a valid value for %{question}"
invalid_number: "Enter a number for %{question}"
spreadsheet_dupe: "This is a duplicate of a log in your file."
duplicate: "This is a duplicate log."
blank_file: "Template is blank - The template must be filled in for us to create the logs and check if data is correct."
wrong_template:
wrong_template: "Incorrect start dates, please ensure you have used the correct template."
no_headers: "Your file does not contain the required header rows. Add or check the header rows and upload your file again. [Read more about using the template headers](%{guidance_link})."
wrong_field_numbers_count: "Incorrect number of fields, please ensure you have used the correct template."
over_max_column_count: "Too many columns, please ensure you have used the correct template."
owning_organisation:
not_found: "The owning organisation code is incorrect."
not_stock_owner: "The owning organisation code provided is for an organisation that does not own stock."
not_permitted:
not_support: "You do not have permission to add logs for this owning organisation."
support: "This owning organisation is not affiliated with %{org_name}."
managing_organisation:
no_relationship: "This managing organisation does not have a relationship with the owning organisation."
not_found: "The managing organisation code is incorrect."
assigned_to:
not_found: "User with the specified email could not be found."
organisation_not_related: "User must be related to owning organisation or managing organisation."
startdate:
outside_collection_window: "Enter a date within the %{year_combo} collection year, which is between 1st April %{start_year} and 31st March %{end_year}."
year_not_two_or_four_digits: "Tenancy start year must be 2 or 4 digits."
housingneeds:
no_and_dont_know_disabled_needs_conjunction: "No disabled access needs and don’t know disabled access needs cannot be selected together."
dont_know_disabled_needs_conjunction: "Don’t know disabled access needs can’t be selected if you have selected fully wheelchair-accessible housing, wheelchair access to essential rooms, level access housing or other disabled access needs."
no_disabled_needs_conjunction: "No disabled access needs can’t be selected if you have selected fully wheelchair-accessible housing, wheelchair access to essential rooms, level access housing or other disabled access needs."
housingneeds_type:
only_one_option_permitted: "Only one disabled access need: fully wheelchair-accessible housing, wheelchair access to essential rooms or level access housing, can be selected."
condition_effects:
no_choices: "You cannot answer this question as you told us nobody in the household has a physical or mental health condition (or other illness) expected to last 12 months or more."
reason:
renewal_reason_needed: "The reason for leaving must be \"End of social or private sector tenancy - no fault\", \"End of social or private sector tenancy - evicted due to anti-social behaviour (ASB)\", \"End of social or private sector tenancy - evicted due to rent arrears\" or \"End of social or private sector tenancy - evicted for any other reason\"."
referral:
general_needs_prp_referred_by_la: "The source of the referral cannot be referred by local authority housing department for a general needs log."
nominated_by_local_ha_but_la: "The source of the referral cannot be Nominated by local housing authority as your organisation is a local authority."
scheme:
must_relate_to_org: "This scheme code does not belong to the owning organisation or managing organisation."
location:
must_relate_to_org: "Location code must relate to a location that is owned by the owning organisation or managing organisation."
age:
invalid: "Age of person %{person_num} must be a number or the letter R"
address:
not_found: "We could not find this address. Check the address data in your CSV file is correct and complete, or find the correct address in the service."
not_determined:
one: "There is a possible match for this address which doesn't look right. Check the address data in your CSV file is correct and complete, or confirm the address in the service."
multiple: "There are multiple matches for this address. Check the address data in your CSV file is correct and complete, or select the correct address in the service."
not_answered: "Enter either the UPRN or the full address."
nationality:
invalid: "Select a valid nationality."
charges:
missing_charges: "Please enter the %{sentence_fragment}. If there is no %{sentence_fragment}, please enter '0'."
reasonpref:
conflict:
dont_know: "You cannot select 'Don't know' if any of the other reasonable preference reasons are also selected."
other: "You cannot select this reasonable preference reason as you've also selected 'Don't know' as a reason."

46
config/locales/validations/sales/2025/bulk_upload.en.yml

@ -0,0 +1,46 @@
en:
validations:
sales:
2025:
bulk_upload:
not_answered: "You must answer %{question}"
invalid_option: "Enter a valid value for %{question}"
spreadsheet_dupe: "This is a duplicate of a log in your file."
duplicate: "This is a duplicate log."
blank_file: "Template is blank - The template must be filled in for us to create the logs and check if data is correct."
wrong_template:
over_max_column_count: "Too many columns, please ensure you have used the correct template."
no_headers: "Your file does not contain the required header rows. Add or check the header rows and upload your file again. [Read more about using the template headers](%{guidance_link})."
wrong_field_numbers_count: "Incorrect number of fields, please ensure you have used the correct template."
wrong_template: "Incorrect sale dates, please ensure you have used the correct template."
numeric:
within_range: "%{field} must be between %{min} and %{max}."
owning_organisation:
not_found: "The owning organisation code is incorrect."
not_stock_owner: "The owning organisation code provided is for an organisation that does not own stock."
not_permitted:
support: "This owning organisation is not affiliated with %{name}."
not_support: "You do not have permission to add logs for this owning organisation."
assigned_to:
not_found: "User with the specified email could not be found."
organisation_not_related: "User must be related to owning organisation or managing organisation."
managing_organisation_not_related: "This organisation does not have a relationship with the owning organisation."
saledate:
outside_collection_window: "Enter a date within the %{year_combo} collection year, which is between 1st April %{start_year} and 31st March %{end_year}."
year_not_two_or_four_digits: "Sale completion year must be 2 or 4 digits."
ecstat1:
buyer_cannot_be_over_16_and_child: "Buyer 1's age cannot be 16 or over if their working situation is child under 16."
buyer_cannot_be_child: "Buyer 1 cannot have a working situation of child under 16."
age1:
buyer_cannot_be_over_16_and_child: "Buyer 1's age cannot be 16 or over if their working situation is child under 16."
ecstat2:
buyer_cannot_be_over_16_and_child: "Buyer 2's age cannot be 16 or over if their working situation is child under 16."
buyer_cannot_be_child: "Buyer 2 cannot have a working situation of child under 16."
age2:
buyer_cannot_be_over_16_and_child: "Buyer 2's age cannot be 16 or over if their working situation is child under 16."
address:
not_found: "We could not find this address. Check the address data in your CSV file is correct and complete, or select the correct address using the CORE site."
not_determined: "There are multiple matches for this address. Either select the correct address manually or correct the UPRN in the CSV file."
not_answered: "Enter either the UPRN or the full address."
nationality:
invalid: "Select a valid nationality."

2
config/routes.rb

@ -400,8 +400,10 @@ Rails.application.routes.draw do
get "create-test-lettings-log", to: "test_data#create_test_lettings_log" get "create-test-lettings-log", to: "test_data#create_test_lettings_log"
get "create-setup-test-lettings-log", to: "test_data#create_setup_test_lettings_log" get "create-setup-test-lettings-log", to: "test_data#create_setup_test_lettings_log"
get "create-2024-test-lettings-bulk-upload", to: "test_data#create_2024_test_lettings_bulk_upload" get "create-2024-test-lettings-bulk-upload", to: "test_data#create_2024_test_lettings_bulk_upload"
get "create-2025-test-lettings-bulk-upload", to: "test_data#create_2025_test_lettings_bulk_upload"
get "create-test-sales-log", to: "test_data#create_test_sales_log" get "create-test-sales-log", to: "test_data#create_test_sales_log"
get "create-setup-test-sales-log", to: "test_data#create_setup_test_sales_log" get "create-setup-test-sales-log", to: "test_data#create_setup_test_sales_log"
get "create-2024-test-sales-bulk-upload", to: "test_data#create_2024_test_sales_bulk_upload" get "create-2024-test-sales-bulk-upload", to: "test_data#create_2024_test_sales_bulk_upload"
get "create-2025-test-sales-bulk-upload", to: "test_data#create_2025_test_sales_bulk_upload"
end end
end end

5
db/migrate/20250225180643_add_referral_type_to_lettings_logs.rb

@ -0,0 +1,5 @@
class AddReferralTypeToLettingsLogs < ActiveRecord::Migration[7.2]
def change
add_column :lettings_logs, :referral_type, :integer
end
end

2
db/schema.rb

@ -373,8 +373,8 @@ ActiveRecord::Schema[7.2].define(version: 2025_02_27_085622) do
t.integer "partner_under_16_value_check" t.integer "partner_under_16_value_check"
t.integer "multiple_partners_value_check" t.integer "multiple_partners_value_check"
t.bigint "created_by_id" t.bigint "created_by_id"
t.boolean "manual_address_entry_selected", default: false
t.integer "referral_type" t.integer "referral_type"
t.boolean "manual_address_entry_selected", default: false
t.index ["assigned_to_id"], name: "index_lettings_logs_on_assigned_to_id" t.index ["assigned_to_id"], name: "index_lettings_logs_on_assigned_to_id"
t.index ["bulk_upload_id"], name: "index_lettings_logs_on_bulk_upload_id" t.index ["bulk_upload_id"], name: "index_lettings_logs_on_bulk_upload_id"
t.index ["created_by_id"], name: "index_lettings_logs_on_created_by_id" t.index ["created_by_id"], name: "index_lettings_logs_on_created_by_id"

15
lib/tasks/correct_reasonpref_values.rake

@ -0,0 +1,15 @@
desc "Correct invalid BU reasonable preference values"
task correct_reasonpref_values: :environment do
%w[rp_homeless rp_hardship rp_medwel rp_insan_unsat rp_dontknow].each do |field|
field_invalid = "#{field} != 1 AND #{field} != 0 AND #{field} is NOT NULL"
LettingsLog.filter_by_year(2024).where(field_invalid).find_each do |lettings_log|
lettings_log[field] = 0
unless lettings_log.save
Rails.logger.info("Failed to save reasonpref for LettingsLog with id #{lettings_log.id}: #{lettings_log.errors.full_messages}")
end
end
LettingsLog.filter_by_year(2023).where(field_invalid).update_all("#{field}": 0)
end
end

12
lib/tasks/recalculate_invalid_reasonpref_dontknow.rake

@ -0,0 +1,12 @@
desc "Bulk update logs with invalid rp_dontknow values"
task recalculate_invalid_rpdontknow: :environment do
validation_trigger_condition = "rp_dontknow = 1 AND (rp_homeless = 1 OR rp_insan_unsat = 1 OR rp_medwel = 1 OR rp_hardship = 1)"
LettingsLog.filter_by_year(2024).where(validation_trigger_condition).find_each do |log|
log.rp_dontknow = 0
unless log.save
Rails.logger.info "Could not save changes to lettings log #{log.id}"
end
end
end

BIN
session-manager-plugin.deb

Binary file not shown.

1
spec/factories/lettings_log.rb

@ -159,6 +159,7 @@ FactoryBot.define do
is_carehome { 0 } is_carehome { 0 }
declaration { 1 } declaration { 1 }
first_time_property_let_as_social_housing { 0 } first_time_property_let_as_social_housing { 0 }
referral_type { 1 }
referral { 2 } referral { 2 }
uprn_known { 0 } uprn_known { 0 }
joint { 3 } joint { 3 }

45
spec/helpers/tasklist_helper_spec.rb

@ -204,4 +204,49 @@ RSpec.describe TasklistHelper do
end end
end end
end end
describe "deadline text" do
context "when log does not have a sale/start date" do
let(:log) { build(:sales_log, saledate: nil) }
it "returns nil" do
expect(deadline_text(log)).to be_nil
end
end
context "when log is completed" do
let(:log) { build(:sales_log, :completed, status: "completed") }
it "returns nil" do
expect(deadline_text(log)).to be_nil
end
end
context "when today is before the deadline for log with sale/start date" do
let(:log) { build(:sales_log, saledate: Time.zone.local(2025, 6, 1)) }
it "returns the deadline text" do
allow(Time.zone).to receive(:today).and_return(Time.zone.local(2025, 5, 7))
expect(deadline_text(log)).to include("Upcoming Q1 deadline: 11 July 2025.")
end
end
context "when today is the deadline for log with sale/start date" do
let(:log) { build(:sales_log, saledate: Time.zone.local(2025, 2, 1)) }
it "returns the overdue text" do
allow(Time.zone).to receive(:today).and_return(Time.zone.local(2025, 6, 6))
expect(deadline_text(log)).to include("Upcoming Q4 deadline: 6 June 2025.")
end
end
context "when today is after the deadline for log with sale/start date" do
let(:log) { build(:sales_log, saledate: Time.zone.local(2025, 2, 1)) }
it "returns the overdue text" do
allow(Time.zone).to receive(:today).and_return(Time.zone.local(2025, 6, 7))
expect(deadline_text(log)).to include("Overdue: Q4 deadline 6 June 2025.")
end
end
end
end end

111
spec/lib/tasks/correct_reasonpref_values_spec.rb

@ -0,0 +1,111 @@
require "rails_helper"
require "rake"
RSpec.describe "correct_reasonpref_values" do
describe ":correct_reasonpref_values", type: :task do
subject(:task) { Rake::Task["correct_reasonpref_values"] }
let(:organisation) { create(:organisation, rent_periods: [2]) }
let(:user) { create(:user, organisation:) }
before do
Rake.application.rake_require("tasks/correct_reasonpref_values")
Rake::Task.define_task(:environment)
task.reenable
end
context "when the rake task is run" do
context "and any of the reasonable_preference_reason options are not 1, 0 or nil" do
let(:bulk_upload) { create(:bulk_upload, :lettings, year: 2024, rent_type_fix_status: BulkUpload.rent_type_fix_statuses[:not_applied]) }
it "sets the options to 0" do
log = build(:lettings_log, :completed, reasonpref: 1, rp_homeless: -2, rp_hardship: 2, rp_medwel: 3, rp_insan_unsat: 4, rp_dontknow: 1,
bulk_upload:, assigned_to: user)
log.save!(validate: false)
initial_updated_at = log.updated_at
task.invoke
log.reload
expect(log.updated_at).not_to eq(initial_updated_at)
expect(log.status).to eq("completed")
expect(log.rp_homeless).to be(0)
expect(log.rp_hardship).to be(0)
expect(log.rp_medwel).to be(0)
expect(log.rp_insan_unsat).to be(0)
expect(log.rp_dontknow).to be(1)
end
it "updates the reasonable preference reason values on a pending log" do
log = build(:lettings_log, :completed, status: "pending", reasonpref: 1, rp_homeless: -2, rp_hardship: 1, rp_medwel: 3, rp_insan_unsat: 4, rp_dontknow: 2, bulk_upload:, assigned_to: user)
log.save!(validate: false)
initial_updated_at = log.updated_at
expect(log.status).to eq("pending")
task.invoke
log.reload
expect(log.rp_homeless).to be(0)
expect(log.rp_hardship).to be(1)
expect(log.rp_medwel).to be(0)
expect(log.rp_insan_unsat).to be(0)
expect(log.rp_dontknow).to be(0)
expect(log.status).to eq("pending")
expect(log.updated_at).not_to eq(initial_updated_at)
end
it "does not update logs with valid values" do
log = build(:lettings_log, :completed, reasonpref: 1, rp_homeless: 0, rp_hardship: 1, rp_medwel: 0, rp_insan_unsat: 0, rp_dontknow: 0, bulk_upload:, assigned_to: user)
log.save!(validate: false)
initial_updated_at = log.updated_at
expect(log.status).to eq("completed")
task.invoke
log.reload
expect(log.status).to eq("completed")
expect(log.updated_at).to eq(initial_updated_at)
end
it "updates the reasonable preference reason values if some of the checkbox values are valid" do
log = build(:lettings_log, :completed, status: "pending", reasonpref: 1, rp_homeless: 0, rp_hardship: 2, rp_medwel: 1, rp_insan_unsat: 0, rp_dontknow: 0, bulk_upload:, assigned_to: user)
log.save!(validate: false)
initial_updated_at = log.updated_at
expect(log.status).to eq("pending")
task.invoke
log.reload
expect(log.rp_homeless).to be(0)
expect(log.rp_hardship).to be(0)
expect(log.rp_medwel).to be(1)
expect(log.rp_insan_unsat).to be(0)
expect(log.rp_dontknow).to be(0)
expect(log.status).to eq("pending")
expect(log.updated_at).not_to eq(initial_updated_at)
end
it "updates the reasonable preference reason values on a 2023 log" do
log = build(:lettings_log, :completed, startdate: Time.zone.local(2023, 6, 6), reasonpref: 1, rp_homeless: 0, rp_hardship: 2, rp_medwel: 1, rp_insan_unsat: 0, rp_dontknow: 0, bulk_upload:, assigned_to: user)
log.save!(validate: false)
initial_updated_at = log.updated_at
task.invoke
log.reload
expect(log.updated_at).to eq(initial_updated_at)
expect(log.rp_hardship).to eq(0)
end
it "does not update and logs error if a validation triggers" do
log = build(:lettings_log, :completed, postcode_full: "0", reasonpref: 1, rp_homeless: 0, rp_hardship: 2, rp_medwel: 1, rp_insan_unsat: 0, rp_dontknow: 0, bulk_upload:, assigned_to: user)
log.save!(validate: false)
initial_updated_at = log.updated_at
task.invoke
log.reload
expect(log.updated_at).to eq(initial_updated_at)
end
end
end
end
end

70
spec/lib/tasks/recalculate_invalid_reasonpref_dontknow_spec.rb

@ -0,0 +1,70 @@
require "rails_helper"
require "rake"
RSpec.describe "recalculate_invalid_reasonpref_dontknow" do
subject(:task) { Rake::Task["recalculate_invalid_rpdontknow"] }
before do
Rake.application.rake_require("tasks/recalculate_invalid_reasonpref_dontknow")
Rake::Task.define_task(:environment)
task.reenable
end
let(:invalid_logs) { create_list(:lettings_log, 5, :completed, reasonpref: 1, rp_dontknow: 1, rp_homeless: 1, rp_insan_unsat: rand(2), rp_medwel: rand(2), rp_hardship: rand(2), updated_at: Time.zone.local(2024, 4, 2, 12, 0, 0)) }
let(:pre_2024_invalid_logs) do
create_list(:lettings_log, 5, :completed, reasonpref: 1, rp_dontknow: 1, rp_homeless: 1, rp_insan_unsat: rand(2), rp_medwel: rand(2), rp_hardship: rand(2)).each do |log|
log.startdate = Time.zone.local(rand(2021..2023), 4, 1)
log.save!(validate: false)
end
end
let(:valid_logs) { create_list(:lettings_log, 3, :completed, reasonpref: 1, rp_dontknow: 0, rp_homeless: 1, rp_insan_unsat: 1, rp_medwel: rand(2), rp_hardship: rand(2), updated_at: Time.zone.local(2024, 4, 2, 12, 0, 0)) }
it "updates the logs from 2024/25 with invalid rp_dontknow values" do
invalid_logs.each do |log|
expect(log.reasonpref).to eq(1)
expect(log.rp_dontknow).to eq(1)
expect(log.rp_homeless).to eq(1)
end
task.invoke
invalid_logs.each do |log|
log.reload
expect(log.reasonpref).to eq(1)
expect(log.rp_dontknow).to eq(0)
expect(log.rp_homeless).to eq(1)
expect(log.updated_at).not_to eq(Time.zone.local(2024, 4, 2, 12, 0, 0))
end
end
it "does not update the logs pre 2024 with invalid rp_dontknow values" do
pre_2024_invalid_logs.each do |log|
expect(log.reasonpref).to eq(1)
expect(log.rp_dontknow).to eq(1)
expect(log.rp_homeless).to eq(1)
end
task.invoke
pre_2024_invalid_logs.each do |log|
log.reload
expect(log.reasonpref).to eq(1)
expect(log.rp_dontknow).to eq(1)
expect(log.rp_homeless).to eq(1)
end
end
it "does not update the logs with valid rp_dontknow values" do
valid_logs.each do |log|
expect(log.reasonpref).to eq(1)
expect(log.rp_dontknow).to eq(0)
expect(log.rp_homeless).to eq(1)
expect(log.rp_insan_unsat).to eq(1)
end
task.invoke
valid_logs.each do |log|
log.reload
expect(log.reasonpref).to eq(1)
expect(log.rp_dontknow).to eq(0)
expect(log.rp_homeless).to eq(1)
expect(log.rp_insan_unsat).to eq(1)
expect(log.updated_at).to eq(Time.zone.local(2024, 4, 2, 12, 0, 0))
end
end
end

1
spec/models/bulk_upload_spec.rb

@ -41,6 +41,7 @@ RSpec.describe BulkUpload, type: :model do
[ [
{ year: 2023, expected_value: "2023 to 2024" }, { year: 2023, expected_value: "2023 to 2024" },
{ year: 2024, expected_value: "2024 to 2025" }, { year: 2024, expected_value: "2024 to 2025" },
{ year: 2025, expected_value: "2025 to 2026" },
].each do |test_case| ].each do |test_case|
context "when the bulk upload year is #{test_case[:year]}" do context "when the bulk upload year is #{test_case[:year]}" do
let(:bulk_upload) { build(:bulk_upload, year: test_case[:year]) } let(:bulk_upload) { build(:bulk_upload, year: test_case[:year]) }

2
spec/models/form/lettings/questions/referral_prp_spec.rb → spec/models/form/lettings/questions/referral_general_needs_prp_spec.rb

@ -1,6 +1,6 @@
require "rails_helper" require "rails_helper"
RSpec.describe Form::Lettings::Questions::ReferralPrp, type: :model do RSpec.describe Form::Lettings::Questions::ReferralGeneralNeedsPrp, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) } subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil } let(:question_id) { nil }

2
spec/models/form/lettings/questions/referral_spec.rb → spec/models/form/lettings/questions/referral_general_needs_spec.rb

@ -1,6 +1,6 @@
require "rails_helper" require "rails_helper"
RSpec.describe Form::Lettings::Questions::Referral, type: :model do RSpec.describe Form::Lettings::Questions::ReferralGeneralNeeds, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) } subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil } let(:question_id) { nil }

4
spec/models/form/lettings/questions/rsnvac_spec.rb

@ -76,10 +76,6 @@ RSpec.describe Form::Lettings::Questions::Rsnvac, type: :model do
end end
end end
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(0)
end
context "with 2024/25 form" do context "with 2024/25 form" do
let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1)) } let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1)) }

4
spec/models/form/lettings/questions/voiddate_spec.rb

@ -15,10 +15,6 @@ RSpec.describe Form::Lettings::Questions::Voiddate, type: :model do
expect(question.id).to eq("voiddate") expect(question.id).to eq("voiddate")
end end
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(0)
end
it "has the correct question_number" do it "has the correct question_number" do
expect(question.question_number).to eq(23) expect(question.question_number).to eq(23)
end end

38
spec/models/form/lettings/subsections/household_situation_spec.rb

@ -19,6 +19,7 @@ RSpec.describe Form::Lettings::Subsections::HouseholdSituation, type: :model do
context "with form year before 2024" do context "with form year before 2024" do
before do before do
allow(form).to receive(:start_year_2024_or_later?).and_return(false) allow(form).to receive(:start_year_2024_or_later?).and_return(false)
allow(form).to receive(:start_year_2025_or_later?).and_return(false)
end end
it "has correct pages" do it "has correct pages" do
@ -46,9 +47,10 @@ RSpec.describe Form::Lettings::Subsections::HouseholdSituation, type: :model do
end end
end end
context "with form year >= 2024" do context "with form year is 2024" do
before do before do
allow(form).to receive(:start_year_2024_or_later?).and_return(true) allow(form).to receive(:start_year_2024_or_later?).and_return(true)
allow(form).to receive(:start_year_2025_or_later?).and_return(false)
end end
it "has correct pages" do it "has correct pages" do
@ -77,6 +79,40 @@ RSpec.describe Form::Lettings::Subsections::HouseholdSituation, type: :model do
end end
end end
context "with form year is 2025" do
before do
allow(form).to receive(:start_year_2024_or_later?).and_return(true)
allow(form).to receive(:start_year_2025_or_later?).and_return(true)
end
it "has correct pages" do
expect(household_situation.pages.map(&:id)).to eq(
%w[
time_lived_in_local_authority
time_on_waiting_list
reason_for_leaving_last_settled_home
reason_for_leaving_last_settled_home_renewal
reasonother_value_check
previous_housing_situation
previous_housing_situation_renewal
homelessness
previous_postcode
previous_local_authority
reasonable_preference
reasonable_preference_reason
allocation_system
referral_type
referral_direct
referral_la
referral_prp
referral_hsc
referral_justice
referral_value_check
],
)
end
end
it "has the correct id" do it "has the correct id" do
expect(household_situation.id).to eq("household_situation") expect(household_situation.id).to eq("household_situation")
end end

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

@ -101,6 +101,10 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do
it "has correct pages" do it "has correct pages" do
expect(property_information.pages.map(&:id)).to eq( expect(property_information.pages.map(&:id)).to eq(
%w[ %w[
first_time_property_let_as_social_housing
property_let_type
property_vacancy_reason_not_first_let
property_vacancy_reason_first_let
uprn uprn
uprn_confirmation uprn_confirmation
address_matcher address_matcher
@ -109,10 +113,6 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do
address address
property_local_authority property_local_authority
local_authority_rent_value_check local_authority_rent_value_check
first_time_property_let_as_social_housing
property_let_type
property_vacancy_reason_not_first_let
property_vacancy_reason_first_let
property_unit_type property_unit_type
property_building_type property_building_type
property_wheelchair_accessible property_wheelchair_accessible

2
spec/models/form/sales/pages/living_before_purchase_spec.rb

@ -95,7 +95,7 @@ RSpec.describe Form::Sales::Pages::LivingBeforePurchase, type: :model do
end end
it "does not route to the page when resale is not 2" do it "does not route to the page when resale is not 2" do
log = build(:sales_log, jointpur: 1, resale: nil) log = build(:sales_log, jointpur: 1, resale: nil, ownershipsch: 1)
expect(page.routed_to?(log, nil)).to eq(false) expect(page.routed_to?(log, nil)).to eq(false)
end end
end end

20
spec/models/lettings_log_derived_fields_spec.rb

@ -1206,4 +1206,24 @@ RSpec.describe LettingsLog, type: :model do
end end
end end
end end
describe "#clear_child_ecstat_for_age_changes!" do
it "clears the working situation of a person that was previously a child under 16" do
log = create(:lettings_log, :completed, age2: 13)
log.age2 = 17
expect { log.set_derived_fields! }.to change(log, :ecstat2).from(9).to(nil)
end
it "does not clear the working situation of a person that had an age change but is still a child under 16" do
log = create(:lettings_log, :completed, age2: 13)
log.age2 = 15
expect { log.set_derived_fields! }.to not_change(log, :ecstat2)
end
it "does not clear the working situation of a person that had an age change but is still an adult" do
log = create(:lettings_log, :completed, age2: 45)
log.age2 = 46
expect { log.set_derived_fields! }.to not_change(log, :ecstat2)
end
end
end end

20
spec/models/sales_log_derived_fields_spec.rb

@ -78,6 +78,26 @@ RSpec.describe SalesLog, type: :model do
expect { log.set_derived_fields! }.to change(log, :mortgage).from(50_000).to(nil) expect { log.set_derived_fields! }.to change(log, :mortgage).from(50_000).to(nil)
end end
describe "#clear_child_ecstat_for_age_changes!" do
it "clears the working situation of a person that was previously a child under 16" do
log = create(:sales_log, :completed, age3: 13, age4: 16, age5: 45)
log.age3 = 17
expect { log.set_derived_fields! }.to change(log, :ecstat3).from(9).to(nil)
end
it "does not clear the working situation of a person that had an age change but is still a child under 16" do
log = create(:sales_log, :completed, age3: 13, age4: 16, age5: 45)
log.age3 = 15
expect { log.set_derived_fields! }.to not_change(log, :ecstat3)
end
it "does not clear the working situation of a person that had an age change but is still an adult" do
log = create(:sales_log, :completed, age3: 13, age4: 16, age5: 45)
log.age5 = 46
expect { log.set_derived_fields! }.to not_change(log, :ecstat5)
end
end
context "with a log that is not outright sales" do context "with a log that is not outright sales" do
it "does not derive deposit when mortgage used is no" do it "does not derive deposit when mortgage used is no" do
log = build(:sales_log, :shared_ownership_setup_complete, value: 123_400, deposit: nil, mortgageused: 2) log = build(:sales_log, :shared_ownership_setup_complete, value: 123_400, deposit: nil, mortgageused: 2)

25
spec/models/validations/household_validations_spec.rb

@ -129,33 +129,42 @@ RSpec.describe Validations::HouseholdValidations do
context "when referral is not internal transfer" do context "when referral is not internal transfer" do
it "cannot be permanently decanted from another property owned by this landlord" do it "cannot be permanently decanted from another property owned by this landlord" do
record.reason = 1 record.reason = 1
record.referral_type = 1
record.referral = 2 record.referral = 2
household_validator.validate_reason_for_leaving_last_settled_home(record) household_validator.validate_reason_for_leaving_last_settled_home(record)
expect(record.errors["reason"]) expect(record.errors["reason"])
.to include(match(I18n.t("validations.lettings.household.reason.leaving_last_settled_home.not_internal_transfer"))) .to include(match(I18n.t("validations.lettings.household.reason.leaving_last_settled_home.not_internal_transfer")))
expect(record.errors["referral"]) expect(record.errors["referral"])
.to include(match(I18n.t("validations.lettings.household.referral.leaving_last_settled_home.reason_permanently_decanted"))) .to include(match(I18n.t("validations.lettings.household.referral.leaving_last_settled_home.reason_permanently_decanted")))
expect(record.errors["referral_type"])
.to include(match(I18n.t("validations.lettings.household.referral.leaving_last_settled_home.reason_permanently_decanted")))
end end
end end
context "when referral is internal transfer" do context "when referral is internal transfer" do
it "can be permanently decanted from another property owned by this landlord" do it "can be permanently decanted from another property owned by this landlord" do
record.reason = 1 record.reason = 1
record.referral_type = 3
record.referral = 1 record.referral = 1
household_validator.validate_reason_for_leaving_last_settled_home(record) household_validator.validate_reason_for_leaving_last_settled_home(record)
expect(record.errors["reason"]) expect(record.errors["reason"])
.to be_empty .to be_empty
expect(record.errors["referral"]) expect(record.errors["referral"])
.to be_empty .to be_empty
expect(record.errors["referral_type"])
.to be_empty
end end
it "cannot have a PRP as landlord and Housing situation before this letting cannot be LA general needs" do it "cannot have a PRP as landlord and Housing situation before this letting cannot be LA general needs" do
record.owning_organisation.provider_type = "PRP" record.owning_organisation.provider_type = "PRP"
record.prevten = 30 record.prevten = 30
record.referral_type = 3
record.referral = 1 record.referral = 1
household_validator.validate_referral(record) household_validator.validate_referral(record)
expect(record.errors["referral"]) expect(record.errors["referral"])
.to include(match(I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer"))) .to include(match(I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer")))
expect(record.errors["referral_type"])
.to include(match(I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer")))
expect(record.errors["prevten"]) expect(record.errors["prevten"])
.to include(match(I18n.t("validations.lettings.household.prevten.la_general_needs.internal_transfer"))) .to include(match(I18n.t("validations.lettings.household.prevten.la_general_needs.internal_transfer")))
@ -163,6 +172,8 @@ RSpec.describe Validations::HouseholdValidations do
household_validator.validate_referral(record) household_validator.validate_referral(record)
expect(record.errors["referral"]) expect(record.errors["referral"])
.to include(match(I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer"))) .to include(match(I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer")))
expect(record.errors["referral_type"])
.to include(match(I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer")))
expect(record.errors["prevten"]) expect(record.errors["prevten"])
.to include(match(I18n.t("validations.lettings.household.prevten.la_general_needs.internal_transfer"))) .to include(match(I18n.t("validations.lettings.household.prevten.la_general_needs.internal_transfer")))
end end
@ -603,37 +614,45 @@ RSpec.describe Validations::HouseholdValidations do
context "when homelessness is assessed" do context "when homelessness is assessed" do
it "can be internal transfer" do it "can be internal transfer" do
record.homeless = 11 record.homeless = 11
record.referral_type = 3
record.referral = 1 record.referral = 1
household_validator.validate_referral(record) household_validator.validate_referral(record)
expect(record.errors["referral"]).to be_empty expect(record.errors["referral"]).to be_empty
expect(record.errors["referral_type"]).to be_empty
expect(record.errors["homeless"]).to be_empty expect(record.errors["homeless"]).to be_empty
end end
it "can be non internal transfer" do it "can be non internal transfer" do
record.owning_organisation.provider_type = "PRP" record.owning_organisation.provider_type = "PRP"
record.homeless = 0 record.homeless = 0
record.referral_type = 2
record.referral = 3 record.referral = 3
household_validator.validate_referral(record) household_validator.validate_referral(record)
expect(record.errors["referral"]).to be_empty expect(record.errors["referral"]).to be_empty
expect(record.errors["referral_type"]).to be_empty
expect(record.errors["homeless"]).to be_empty expect(record.errors["homeless"]).to be_empty
end end
end end
context "when homelessness is other" do context "when homelessness is other" do
it "cannot be internal transfer" do it "cannot be internal transfer" do
record.referral_type = 3
record.referral = 1 record.referral = 1
record.homeless = 7 record.homeless = 7
household_validator.validate_referral(record) household_validator.validate_referral(record)
expect(record.errors["referral"]).to be_empty expect(record.errors["referral"]).to be_empty
expect(record.errors["referral_type"]).to be_empty
expect(record.errors["homeless"]).to be_empty expect(record.errors["homeless"]).to be_empty
end end
it "can be non internal transfer" do it "can be non internal transfer" do
record.owning_organisation.provider_type = "PRP" record.owning_organisation.provider_type = "PRP"
record.referral_type = 2
record.referral = 3 record.referral = 3
record.homeless = 1 record.homeless = 1
household_validator.validate_referral(record) household_validator.validate_referral(record)
expect(record.errors["referral"]).to be_empty expect(record.errors["referral"]).to be_empty
expect(record.errors["referral_type"]).to be_empty
expect(record.errors["homeless"]).to be_empty expect(record.errors["homeless"]).to be_empty
end end
end end
@ -715,6 +734,7 @@ RSpec.describe Validations::HouseholdValidations do
context "when the referral is internal transfer" do context "when the referral is internal transfer" do
it "prevten can be 9" do it "prevten can be 9" do
record.referral_type = 3
record.referral = 1 record.referral = 1
record.prevten = 9 record.prevten = 9
household_validator.validate_previous_housing_situation(record) household_validator.validate_previous_housing_situation(record)
@ -722,6 +742,8 @@ RSpec.describe Validations::HouseholdValidations do
.to be_empty .to be_empty
expect(record.errors["referral"]) expect(record.errors["referral"])
.to be_empty .to be_empty
expect(record.errors["referral_type"])
.to be_empty
end end
[ [
@ -740,6 +762,7 @@ RSpec.describe Validations::HouseholdValidations do
{ code: 29, label: "Prison or approved probation hostel" }, { code: 29, label: "Prison or approved probation hostel" },
].each do |prevten| ].each do |prevten|
it "prevten cannot be #{prevten[:code]}" do it "prevten cannot be #{prevten[:code]}" do
record.referral_type = 3
record.referral = 1 record.referral = 1
record.prevten = prevten[:code] record.prevten = prevten[:code]
household_validator.validate_previous_housing_situation(record) household_validator.validate_previous_housing_situation(record)
@ -748,6 +771,8 @@ RSpec.describe Validations::HouseholdValidations do
.to include(match I18n.t("validations.lettings.household.prevten.internal_transfer", prevten: label)) .to include(match I18n.t("validations.lettings.household.prevten.internal_transfer", prevten: label))
expect(record.errors["referral"]) expect(record.errors["referral"])
.to include(match I18n.t("validations.lettings.household.referral.prevten_invalid", prevten: "")) .to include(match I18n.t("validations.lettings.household.referral.prevten_invalid", prevten: ""))
expect(record.errors["referral_type"])
.to include(match I18n.t("validations.lettings.household.referral.prevten_invalid", prevten: ""))
end end
end end
end end

1
spec/models/validations/property_validations_spec.rb

@ -168,6 +168,7 @@ RSpec.describe Validations::PropertyValidations do
it "expects that the letting source can be a referral" do it "expects that the letting source can be a referral" do
log.prevten = 0 log.prevten = 0
log.referral_type = 1
log.referral = 2 log.referral = 2
property_validator.validate_rsnvac(log) property_validator.validate_rsnvac(log)
expect(log.errors["rsnvac"]).to be_empty expect(log.errors["rsnvac"]).to be_empty

15
spec/requests/lettings_logs_controller_spec.rb

@ -1155,6 +1155,21 @@ RSpec.describe LettingsLogsController, type: :request do
expect(lettings_log.status).to eq("completed") expect(lettings_log.status).to eq("completed")
expect(page).to have_link("review and make changes to this log", href: "/lettings-logs/#{lettings_log.id}/review") expect(page).to have_link("review and make changes to this log", href: "/lettings-logs/#{lettings_log.id}/review")
end end
it "does not show guidance link" do
expect(page).not_to have_content("Guidance for submitting social housing lettings and sales data (opens in a new tab)")
end
end
context "and the log is not started" do
let(:lettings_log) { create(:lettings_log, status: "not_started", assigned_to: user) }
it "shows guidance link" do
allow(Time.zone).to receive(:now).and_return(lettings_log.form.edit_end_date - 1.day)
get lettings_log_path(lettings_log)
expect(lettings_log.status).to eq("not_started")
expect(page).to have_content("Guidance for submitting social housing lettings and sales data (opens in a new tab)")
end
end end
context "with bulk_upload_id filter" do context "with bulk_upload_id filter" do

11
spec/requests/sales_logs_controller_spec.rb

@ -866,6 +866,7 @@ RSpec.describe SalesLogsController, type: :request do
context "when viewing a sales log" do context "when viewing a sales log" do
let(:headers) { { "Accept" => "text/html" } } let(:headers) { { "Accept" => "text/html" } }
let(:completed_sales_log) { FactoryBot.create(:sales_log, :completed, owning_organisation: user.organisation, assigned_to: user) } let(:completed_sales_log) { FactoryBot.create(:sales_log, :completed, owning_organisation: user.organisation, assigned_to: user) }
let(:not_started_sales_log) { FactoryBot.create(:sales_log, owning_organisation: user.organisation, assigned_to: user) }
before do before do
sign_in user sign_in user
@ -956,6 +957,16 @@ RSpec.describe SalesLogsController, type: :request do
expect(page).to have_content("This log is from the 2021 to 2022 collection window, which is now closed.") expect(page).to have_content("This log is from the 2021 to 2022 collection window, which is now closed.")
end end
end end
it "does not show guidance link" do
get "/sales-logs/#{completed_sales_log.id}", headers:, params: {}
expect(page).not_to have_content("Guidance for submitting social housing lettings and sales data (opens in a new tab)")
end
it "shows guidance link for not_started log" do
get "/sales-logs/#{not_started_sales_log.id}", headers:, params: {}
expect(page).to have_content("Guidance for submitting social housing lettings and sales data (opens in a new tab)")
end
end end
context "when requesting CSV download" do context "when requesting CSV download" do

4
spec/services/bulk_upload/lettings/validator_spec.rb

@ -103,7 +103,7 @@ RSpec.describe BulkUpload::Lettings::Validator do
before do before do
values = log_to_csv.to_2024_row values = log_to_csv.to_2024_row
values[7] = nil values[7] = nil
file.write(log_to_csv.default_2024_field_numbers_row) file.write(log_to_csv.default_field_numbers_row_for_year(2024))
file.write(log_to_csv.to_custom_csv_row(seed: nil, field_values: values)) file.write(log_to_csv.to_custom_csv_row(seed: nil, field_values: values))
file.rewind file.rewind
end end
@ -146,7 +146,7 @@ RSpec.describe BulkUpload::Lettings::Validator do
before do before do
log.needstype = nil log.needstype = nil
values = log_to_csv.to_2024_row values = log_to_csv.to_2024_row
file.write(log_to_csv.default_2024_field_numbers_row(seed:)) file.write(log_to_csv.default_field_numbers_row_for_year(2024, seed:))
file.write(log_to_csv.to_custom_csv_row(seed:, field_values: values)) file.write(log_to_csv.to_custom_csv_row(seed:, field_values: values))
file.close file.close
end end

32
spec/services/bulk_upload/lettings/year2023/csv_parser_spec.rb

@ -15,8 +15,8 @@ RSpec.describe BulkUpload::Lettings::Year2023::CsvParser do
file.write("Can be empty?\n") file.write("Can be empty?\n")
file.write("Type of letting the question applies to\n") file.write("Type of letting the question applies to\n")
file.write("Duplicate check field?\n") file.write("Duplicate check field?\n")
file.write(BulkUpload::LettingsLogToCsv.new(log:).default_2023_field_numbers_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).default_field_numbers_row_for_year(2023))
file.write(BulkUpload::LettingsLogToCsv.new(log:).to_2023_csv_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).to_year_csv_row(2023))
file.rewind file.rewind
end end
@ -39,8 +39,8 @@ RSpec.describe BulkUpload::Lettings::Year2023::CsvParser do
file.write("Can be empty?\n") file.write("Can be empty?\n")
file.write("Type of letting the question applies to\n") file.write("Type of letting the question applies to\n")
file.write("Duplicate check field?\n") file.write("Duplicate check field?\n")
file.write(BulkUpload::LettingsLogToCsv.new(log:).default_2023_field_numbers_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).default_field_numbers_row_for_year(2023))
file.write(BulkUpload::LettingsLogToCsv.new(log:).to_2023_csv_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).to_year_csv_row(2023))
file.rewind file.rewind
end end
@ -64,8 +64,8 @@ RSpec.describe BulkUpload::Lettings::Year2023::CsvParser do
file.write("Can be empty?\n") file.write("Can be empty?\n")
file.write("Type of letting the question applies to\n") file.write("Type of letting the question applies to\n")
file.write("Duplicate check field?\n") file.write("Duplicate check field?\n")
file.write(BulkUpload::LettingsLogToCsv.new(log:).default_2023_field_numbers_row(seed:)) file.write(BulkUpload::LettingsLogToCsv.new(log:).default_field_numbers_row_for_year(2023, seed:))
file.write(BulkUpload::LettingsLogToCsv.new(log:).to_2023_csv_row(seed:)) file.write(BulkUpload::LettingsLogToCsv.new(log:).to_year_csv_row(2023, seed:))
file.rewind file.rewind
end end
@ -108,7 +108,7 @@ RSpec.describe BulkUpload::Lettings::Year2023::CsvParser do
context "when parsing csv without headers" do context "when parsing csv without headers" do
before do before do
file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_2023_csv_row) file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_year_csv_row(2023))
file.rewind file.rewind
end end
@ -127,7 +127,7 @@ RSpec.describe BulkUpload::Lettings::Year2023::CsvParser do
before do before do
file.write(bom) file.write(bom)
file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_2023_csv_row) file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_year_csv_row(2023))
file.rewind file.rewind
end end
@ -141,7 +141,7 @@ RSpec.describe BulkUpload::Lettings::Year2023::CsvParser do
before do before do
file.write(invalid_sequence) file.write(invalid_sequence)
file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_2023_csv_row) file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_year_csv_row(2023))
file.rewind file.rewind
end end
@ -158,8 +158,8 @@ RSpec.describe BulkUpload::Lettings::Year2023::CsvParser do
file.write("Can be empty?\r") file.write("Can be empty?\r")
file.write("Type of letting the question applies to\r\n") file.write("Type of letting the question applies to\r\n")
file.write("Duplicate check field?\r") file.write("Duplicate check field?\r")
file.write(BulkUpload::LettingsLogToCsv.new(log:).default_2023_field_numbers_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).default_field_numbers_row_for_year(2023))
file.write(BulkUpload::LettingsLogToCsv.new(log:).to_2023_csv_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).to_year_csv_row(2023))
file.rewind file.rewind
end end
@ -177,8 +177,8 @@ RSpec.describe BulkUpload::Lettings::Year2023::CsvParser do
file.write("Can be empty?\n") file.write("Can be empty?\n")
file.write("Type of letting the question applies to\n") file.write("Type of letting the question applies to\n")
file.write("Duplicate check field?\n") file.write("Duplicate check field?\n")
file.write(BulkUpload::LettingsLogToCsv.new(log:).default_2023_field_numbers_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).default_field_numbers_row_for_year(2023))
file.write(BulkUpload::LettingsLogToCsv.new(log:).to_2023_csv_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).to_year_csv_row(2023))
file.rewind file.rewind
end end
@ -190,7 +190,7 @@ RSpec.describe BulkUpload::Lettings::Year2023::CsvParser do
context "when without headers using default ordering" do context "when without headers using default ordering" do
before do before do
file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_2023_csv_row) file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_year_csv_row(2023))
file.rewind file.rewind
end end
@ -210,8 +210,8 @@ RSpec.describe BulkUpload::Lettings::Year2023::CsvParser do
file.write("Can be empty?\n") file.write("Can be empty?\n")
file.write("Type of letting the question applies to\n") file.write("Type of letting the question applies to\n")
file.write("Duplicate check field?\n") file.write("Duplicate check field?\n")
file.write(BulkUpload::LettingsLogToCsv.new(log:).default_2023_field_numbers_row(seed:)) file.write(BulkUpload::LettingsLogToCsv.new(log:).default_field_numbers_row_for_year(2023, seed:))
file.write(BulkUpload::LettingsLogToCsv.new(log:).to_2023_csv_row(seed:)) file.write(BulkUpload::LettingsLogToCsv.new(log:).to_year_csv_row(2023, seed:))
file.rewind file.rewind
end end

36
spec/services/bulk_upload/lettings/year2024/csv_parser_spec.rb

@ -15,8 +15,8 @@ RSpec.describe BulkUpload::Lettings::Year2024::CsvParser do
file.write("Can be empty?\n") file.write("Can be empty?\n")
file.write("Type of letting the question applies to\n") file.write("Type of letting the question applies to\n")
file.write("Duplicate check field?\n") file.write("Duplicate check field?\n")
file.write(BulkUpload::LettingsLogToCsv.new(log:).default_2024_field_numbers_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).default_field_numbers_row_for_year(2024))
file.write(BulkUpload::LettingsLogToCsv.new(log:).to_2024_csv_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).to_year_csv_row(2024))
file.rewind file.rewind
end end
@ -38,8 +38,8 @@ RSpec.describe BulkUpload::Lettings::Year2024::CsvParser do
file.write("\n") file.write("\n")
file.write("Type of letting the question applies to\n") file.write("Type of letting the question applies to\n")
file.write("Duplicate check field?\n") file.write("Duplicate check field?\n")
file.write(BulkUpload::LettingsLogToCsv.new(log:).default_2024_field_numbers_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).default_field_numbers_row_for_year(2024))
file.write(BulkUpload::LettingsLogToCsv.new(log:).to_2024_csv_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).to_year_csv_row(2024))
file.rewind file.rewind
end end
@ -62,8 +62,8 @@ RSpec.describe BulkUpload::Lettings::Year2024::CsvParser do
file.write("Can be empty?\n") file.write("Can be empty?\n")
file.write("Type of letting the question applies to\n") file.write("Type of letting the question applies to\n")
file.write("Duplicate check field?\n") file.write("Duplicate check field?\n")
file.write(BulkUpload::LettingsLogToCsv.new(log:).default_2024_field_numbers_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).default_field_numbers_row_for_year(2024))
file.write(BulkUpload::LettingsLogToCsv.new(log:).to_2024_csv_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).to_year_csv_row(2024))
file.write("\n") file.write("\n")
file.rewind file.rewind
end end
@ -92,8 +92,8 @@ RSpec.describe BulkUpload::Lettings::Year2024::CsvParser do
file.write("Can be empty?\n") file.write("Can be empty?\n")
file.write("Type of letting the question applies to\n") file.write("Type of letting the question applies to\n")
file.write("Duplicate check field?\n") file.write("Duplicate check field?\n")
file.write(BulkUpload::LettingsLogToCsv.new(log:).default_2024_field_numbers_row(seed:)) file.write(BulkUpload::LettingsLogToCsv.new(log:).default_field_numbers_row_for_year(2024, seed:))
file.write(BulkUpload::LettingsLogToCsv.new(log:).to_2024_csv_row(seed:)) file.write(BulkUpload::LettingsLogToCsv.new(log:).to_year_csv_row(2024, seed:))
file.rewind file.rewind
end end
@ -136,7 +136,7 @@ RSpec.describe BulkUpload::Lettings::Year2024::CsvParser do
context "when parsing csv without headers" do context "when parsing csv without headers" do
before do before do
file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_2024_csv_row) file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_year_csv_row(2024))
file.rewind file.rewind
end end
@ -155,7 +155,7 @@ RSpec.describe BulkUpload::Lettings::Year2024::CsvParser do
before do before do
file.write(bom) file.write(bom)
file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_2024_csv_row) file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_year_csv_row(2024))
file.rewind file.rewind
end end
@ -169,7 +169,7 @@ RSpec.describe BulkUpload::Lettings::Year2024::CsvParser do
before do before do
file.write(invalid_sequence) file.write(invalid_sequence)
file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_2024_csv_row) file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_year_csv_row(2024))
file.rewind file.rewind
end end
@ -186,8 +186,8 @@ RSpec.describe BulkUpload::Lettings::Year2024::CsvParser do
file.write("Can be empty?\r") file.write("Can be empty?\r")
file.write("Type of letting the question applies to\r\n") file.write("Type of letting the question applies to\r\n")
file.write("Duplicate check field?\r") file.write("Duplicate check field?\r")
file.write(BulkUpload::LettingsLogToCsv.new(log:).default_2024_field_numbers_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).default_field_numbers_row_for_year(2024))
file.write(BulkUpload::LettingsLogToCsv.new(log:).to_2024_csv_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).to_year_csv_row(2024))
file.rewind file.rewind
end end
@ -205,8 +205,8 @@ RSpec.describe BulkUpload::Lettings::Year2024::CsvParser do
file.write("Can be empty?\n") file.write("Can be empty?\n")
file.write("Type of letting the question applies to\n") file.write("Type of letting the question applies to\n")
file.write("Duplicate check field?\n") file.write("Duplicate check field?\n")
file.write(BulkUpload::LettingsLogToCsv.new(log:).default_2024_field_numbers_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).default_field_numbers_row_for_year(2024))
file.write(BulkUpload::LettingsLogToCsv.new(log:).to_2024_csv_row) file.write(BulkUpload::LettingsLogToCsv.new(log:).to_year_csv_row(2024))
file.rewind file.rewind
end end
@ -218,7 +218,7 @@ RSpec.describe BulkUpload::Lettings::Year2024::CsvParser do
context "when without headers using default ordering" do context "when without headers using default ordering" do
before do before do
file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_2024_csv_row) file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_year_csv_row(2024))
file.rewind file.rewind
end end
@ -238,8 +238,8 @@ RSpec.describe BulkUpload::Lettings::Year2024::CsvParser do
file.write("Can be empty?\n") file.write("Can be empty?\n")
file.write("Type of letting the question applies to\n") file.write("Type of letting the question applies to\n")
file.write("Duplicate check field?\n") file.write("Duplicate check field?\n")
file.write(BulkUpload::LettingsLogToCsv.new(log:).default_2024_field_numbers_row(seed:)) file.write(BulkUpload::LettingsLogToCsv.new(log:).default_field_numbers_row_for_year(2024, seed:))
file.write(BulkUpload::LettingsLogToCsv.new(log:).to_2024_csv_row(seed:)) file.write(BulkUpload::LettingsLogToCsv.new(log:).to_year_csv_row(2024, seed:))
file.rewind file.rewind
end end

31
spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb

@ -1247,7 +1247,7 @@ RSpec.describe BulkUpload::Lettings::Year2024::RowParser do
end end
end end
context "when some reasonable preference options are seleceted" do context "when some reasonable preference options are selected" do
let(:attributes) { setup_section_params.merge({ bulk_upload:, field_106: "1", field_107: "1", field_108: nil, field_109: "1", field_110: nil, field_111: nil }) } let(:attributes) { setup_section_params.merge({ bulk_upload:, field_106: "1", field_107: "1", field_108: nil, field_109: "1", field_110: nil, field_111: nil }) }
it "sets the rest of the options to 0" do it "sets the rest of the options to 0" do
@ -1260,7 +1260,7 @@ RSpec.describe BulkUpload::Lettings::Year2024::RowParser do
end end
end end
context "when some reasonable preference options are seleceted but reasonpref is No" do context "when some reasonable preference options are selected but reasonpref is No" do
let(:attributes) { setup_section_params.merge({ bulk_upload:, field_106: "2", field_107: "1", field_108: nil, field_109: "1", field_110: nil, field_111: nil }) } let(:attributes) { setup_section_params.merge({ bulk_upload:, field_106: "2", field_107: "1", field_108: nil, field_109: "1", field_110: nil, field_111: nil }) }
it "sets the options to nil" do it "sets the options to nil" do
@ -1272,6 +1272,33 @@ RSpec.describe BulkUpload::Lettings::Year2024::RowParser do
expect(parser.log.rp_dontknow).to be_nil expect(parser.log.rp_dontknow).to be_nil
end end
end end
context "when some reasonable preference options are set as invalid values" do
let(:attributes) { setup_section_params.merge({ bulk_upload:, field_106: "2", field_107: "2", field_108: "3", field_109: "2", field_110: "3", field_111: "-4" }) }
it "adds errors" do
parser.valid?
expect(parser.errors[:field_107]).to be_present
expect(parser.errors[:field_108]).to be_present
expect(parser.errors[:field_109]).to be_present
expect(parser.errors[:field_110]).to be_present
expect(parser.errors[:field_111]).to be_present
end
end
context "when reasonpref is Yes, some reasonable preferences are selected but also so is 'Don't know'" do
let(:attributes) { setup_section_params.merge({ bulk_upload:, field_106: "1", field_107: "1", field_108: "1", field_109: nil, field_110: nil, field_111: "1" }) }
it "is not permitted" do
parser.valid?
expect(parser.errors[:field_107]).to be_present
expect(parser.errors[:field_108]).to be_present
expect(parser.errors[:field_111]).to be_present
expect(parser.errors[:field_107]).to include(I18n.t("validations.lettings.2024.bulk_upload.reasonpref.conflict.other"))
expect(parser.errors[:field_108]).to include(I18n.t("validations.lettings.2024.bulk_upload.reasonpref.conflict.other"))
expect(parser.errors[:field_111]).to include(I18n.t("validations.lettings.2024.bulk_upload.reasonpref.conflict.dont_know"))
end
end
end end
describe "#field_116" do # referral describe "#field_116" do # referral

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

Loading…
Cancel
Save