Browse Source

Merge branch 'main' into CLDC-1812-proplen-updates

# Conflicts:
#	db/schema.rb
pull/1296/head
natdeanlewissoftwire 4 years ago
parent
commit
205ca9c873
  1. 3
      .github/workflows/review_pipeline.yml
  2. 5
      Gemfile
  3. 276
      Gemfile.lock
  4. 4
      app/controllers/lettings_logs_controller.rb
  5. 4
      app/controllers/organisations_controller.rb
  6. 4
      app/controllers/sales_logs_controller.rb
  7. 36
      app/helpers/collection_time_helper.rb
  8. 8
      app/helpers/filters_helper.rb
  9. 33
      app/helpers/interruption_screen_helper.rb
  10. 25
      app/helpers/tasklist_helper.rb
  11. 28
      app/mailers/bulk_upload_mailer.rb
  12. 12
      app/models/bulk_upload.rb
  13. 4
      app/models/derived_variables/lettings_log_variables.rb
  14. 6
      app/models/form.rb
  15. 10
      app/models/form/lettings/questions/location_id.rb
  16. 2
      app/models/form/lettings/questions/working_situation1.rb
  17. 6
      app/models/form/sales/pages/about_price_shared_ownership_value_check.rb
  18. 9
      app/models/form/sales/pages/about_staircase.rb
  19. 15
      app/models/form/sales/pages/buyer1_income_value_check.rb
  20. 15
      app/models/form/sales/pages/buyer2_ethnic_background_arab.rb
  21. 15
      app/models/form/sales/pages/buyer2_ethnic_background_asian.rb
  22. 15
      app/models/form/sales/pages/buyer2_ethnic_background_black.rb
  23. 15
      app/models/form/sales/pages/buyer2_ethnic_background_mixed.rb
  24. 15
      app/models/form/sales/pages/buyer2_ethnic_background_white.rb
  25. 22
      app/models/form/sales/pages/buyer2_ethnic_group.rb
  26. 35
      app/models/form/sales/pages/buyer2_income_value_check.rb
  27. 2
      app/models/form/sales/questions/buyer1_income_value_check.rb
  28. 16
      app/models/form/sales/questions/buyer2_ethnic_background_arab.rb
  29. 19
      app/models/form/sales/questions/buyer2_ethnic_background_asian.rb
  30. 17
      app/models/form/sales/questions/buyer2_ethnic_background_black.rb
  31. 18
      app/models/form/sales/questions/buyer2_ethnic_background_mixed.rb
  32. 18
      app/models/form/sales/questions/buyer2_ethnic_background_white.rb
  33. 27
      app/models/form/sales/questions/buyer2_ethnic_group.rb
  34. 1
      app/models/form/sales/questions/buyer2_income.rb
  35. 25
      app/models/form/sales/questions/buyer2_income_value_check.rb
  36. 16
      app/models/form/sales/questions/staircase_sale.rb
  37. 17
      app/models/form/sales/subsections/household_characteristics.rb
  38. 1
      app/models/form/sales/subsections/income_benefits_and_savings.rb
  39. 8
      app/models/form_handler.rb
  40. 12
      app/models/lettings_log.rb
  41. 6
      app/models/location.rb
  42. 8
      app/models/log.rb
  43. 27
      app/models/organisation.rb
  44. 30
      app/models/sales_log.rb
  45. 10
      app/models/scheme.rb
  46. 61
      app/models/validations/sales/financial_validations.rb
  47. 9
      app/models/validations/sales/property_validations.rb
  48. 38
      app/models/validations/sales/setup_validations.rb
  49. 12
      app/models/validations/sales/soft_validations.rb
  50. 2
      app/services/bulk_upload/lettings/log_creator.rb
  51. 194
      app/services/bulk_upload/lettings/row_parser.rb
  52. 1
      app/services/bulk_upload/lettings/validator.rb
  53. 5
      app/services/bulk_upload/processor.rb
  54. 9
      app/services/exports/lettings_log_export_constants.rb
  55. 11
      app/services/exports/lettings_log_export_service.rb
  56. 45
      app/views/content/privacy_notice.md
  57. 16
      app/views/form/review.html.erb
  58. 3
      app/views/logs/_log_filters.erb
  59. 2
      config/credentials.yml.enc
  60. 2
      config/forms/2022_2023.json
  61. 17
      config/initializers/feature_toggle.rb
  62. 31
      config/locales/en.yml
  63. 4
      config/routes.rb
  64. 1185
      config/sale_range_data/2023.csv
  65. 5
      db/migrate/20230105103134_add_income2_value_check.rb
  66. 5
      db/migrate/20230210122037_add_staircasesale_to_sales_logs.rb
  67. 8
      db/migrate/20230210143120_add_ethnic_fields_for_buyer2.rb
  68. 7
      db/schema.rb
  69. 63
      docs/Gemfile.lock
  70. 18
      lib/tasks/data_export.rake
  71. 5
      spec/factories/location.rb
  72. 8
      spec/factories/organisation.rb
  73. 4
      spec/factories/scheme.rb
  74. 2
      spec/fixtures/exports/general_needs_log.csv
  75. 2
      spec/fixtures/exports/general_needs_log.xml
  76. 2
      spec/fixtures/exports/supported_housing_logs.xml
  77. 46
      spec/helpers/collection_time_helper_spec.rb
  78. 26
      spec/helpers/filters_helper_spec.rb
  79. 53
      spec/helpers/interruption_screen_helper_spec.rb
  80. 113
      spec/helpers/tasklist_helper_spec.rb
  81. 54
      spec/mailers/bulk_upload_mailer_spec.rb
  82. 6
      spec/models/form/lettings/pages/location_spec.rb
  83. 18
      spec/models/form/lettings/questions/location_id_spec.rb
  84. 16
      spec/models/form/sales/pages/about_staircase_spec.rb
  85. 33
      spec/models/form/sales/pages/buyer2_ethnic_background_arab_spec.rb
  86. 33
      spec/models/form/sales/pages/buyer2_ethnic_background_asian_spec.rb
  87. 33
      spec/models/form/sales/pages/buyer2_ethnic_background_black_spec.rb
  88. 33
      spec/models/form/sales/pages/buyer2_ethnic_background_mixed_spec.rb
  89. 33
      spec/models/form/sales/pages/buyer2_ethnic_background_white_spec.rb
  90. 42
      spec/models/form/sales/pages/buyer2_ethnic_group_spec.rb
  91. 2
      spec/models/form/sales/questions/buyer1_income_value_check_spec.rb
  92. 48
      spec/models/form/sales/questions/buyer2_ethnic_background_arab_spec.rb
  93. 51
      spec/models/form/sales/questions/buyer2_ethnic_background_asian_spec.rb
  94. 49
      spec/models/form/sales/questions/buyer2_ethnic_background_black_spec.rb
  95. 50
      spec/models/form/sales/questions/buyer2_ethnic_background_mixed_spec.rb
  96. 50
      spec/models/form/sales/questions/buyer2_ethnic_background_white_spec.rb
  97. 62
      spec/models/form/sales/questions/buyer2_ethnic_group_spec.rb
  98. 49
      spec/models/form/sales/questions/staircase_sale_spec.rb
  99. 92
      spec/models/form/sales/subsections/household_characteristics_spec.rb
  100. 2
      spec/models/form/sales/subsections/income_benefits_and_savings_spec.rb
  101. Some files were not shown because too many files have changed in this diff Show More

3
.github/workflows/review_pipeline.yml

@ -72,6 +72,9 @@ jobs:
runs-on: ubuntu-latest
environment: staging
needs: [postgres, redis]
permissions:
issues: write
pull-requests: write
steps:
- name: Checkout code

5
Gemfile

@ -20,7 +20,7 @@ gem "bootsnap", ">= 1.4.4", require: false
# GOV UK frontend components
gem "govuk-components"
# GOV UK component form builder DSL
gem "govuk_design_system_formbuilder"
gem "govuk_design_system_formbuilder", "3.1.2"
# Convert Markdown into GOV.UK frontend-styled HTML
gem "govuk_markdown"
# GOV UK Notify
@ -68,6 +68,7 @@ group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem "byebug", platforms: %i[mri mingw x64_mingw]
gem "dotenv-rails"
gem "factory_bot_rails"
gem "pry-byebug"
gem "parallel_tests"
@ -90,7 +91,7 @@ end
group :test do
gem "capybara", require: false
gem "capybara-lockstep"
gem "factory_bot_rails"
gem "capybara-screenshot"
gem "faker"
gem "rspec-rails", require: false
gem "selenium-webdriver", require: false

276
Gemfile.lock

@ -13,67 +13,67 @@ GIT
GEM
remote: https://rubygems.org/
specs:
actioncable (7.0.4.1)
actionpack (= 7.0.4.1)
activesupport (= 7.0.4.1)
actioncable (7.0.4.2)
actionpack (= 7.0.4.2)
activesupport (= 7.0.4.2)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (7.0.4.1)
actionpack (= 7.0.4.1)
activejob (= 7.0.4.1)
activerecord (= 7.0.4.1)
activestorage (= 7.0.4.1)
activesupport (= 7.0.4.1)
actionmailbox (7.0.4.2)
actionpack (= 7.0.4.2)
activejob (= 7.0.4.2)
activerecord (= 7.0.4.2)
activestorage (= 7.0.4.2)
activesupport (= 7.0.4.2)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
actionmailer (7.0.4.1)
actionpack (= 7.0.4.1)
actionview (= 7.0.4.1)
activejob (= 7.0.4.1)
activesupport (= 7.0.4.1)
actionmailer (7.0.4.2)
actionpack (= 7.0.4.2)
actionview (= 7.0.4.2)
activejob (= 7.0.4.2)
activesupport (= 7.0.4.2)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0)
actionpack (7.0.4.1)
actionview (= 7.0.4.1)
activesupport (= 7.0.4.1)
actionpack (7.0.4.2)
actionview (= 7.0.4.2)
activesupport (= 7.0.4.2)
rack (~> 2.0, >= 2.2.0)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (7.0.4.1)
actionpack (= 7.0.4.1)
activerecord (= 7.0.4.1)
activestorage (= 7.0.4.1)
activesupport (= 7.0.4.1)
actiontext (7.0.4.2)
actionpack (= 7.0.4.2)
activerecord (= 7.0.4.2)
activestorage (= 7.0.4.2)
activesupport (= 7.0.4.2)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (7.0.4.1)
activesupport (= 7.0.4.1)
actionview (7.0.4.2)
activesupport (= 7.0.4.2)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activejob (7.0.4.1)
activesupport (= 7.0.4.1)
activejob (7.0.4.2)
activesupport (= 7.0.4.2)
globalid (>= 0.3.6)
activemodel (7.0.4.1)
activesupport (= 7.0.4.1)
activerecord (7.0.4.1)
activemodel (= 7.0.4.1)
activesupport (= 7.0.4.1)
activestorage (7.0.4.1)
actionpack (= 7.0.4.1)
activejob (= 7.0.4.1)
activerecord (= 7.0.4.1)
activesupport (= 7.0.4.1)
activemodel (7.0.4.2)
activesupport (= 7.0.4.2)
activerecord (7.0.4.2)
activemodel (= 7.0.4.2)
activesupport (= 7.0.4.2)
activestorage (7.0.4.2)
actionpack (= 7.0.4.2)
activejob (= 7.0.4.2)
activerecord (= 7.0.4.2)
activesupport (= 7.0.4.2)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (7.0.4.1)
activesupport (7.0.4.2)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
@ -84,20 +84,20 @@ GEM
auto_strip_attributes (2.6.0)
activerecord (>= 4.0)
aws-eventstream (1.2.0)
aws-partitions (1.635.0)
aws-sdk-core (3.153.0)
aws-partitions (1.714.0)
aws-sdk-core (3.170.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.58.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (1.62.0)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.114.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-s3 (1.119.1)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.5.1)
aws-sigv4 (1.5.2)
aws-eventstream (~> 1, >= 1.0.2)
bcrypt (3.1.18)
better_html (2.0.1)
@ -108,14 +108,14 @@ GEM
parser (>= 2.4)
smart_properties
bindex (0.8.1)
bootsnap (1.13.0)
bootsnap (1.16.0)
msgpack (~> 1.2)
builder (3.2.4)
bundler-audit (0.9.1)
bundler (>= 1.2.0, < 3)
thor (~> 1.0)
byebug (11.1.3)
capybara (3.37.1)
capybara (3.38.0)
addressable
matrix
mini_mime (>= 0.1.3)
@ -124,14 +124,17 @@ GEM
rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
capybara-lockstep (1.2.1)
capybara-lockstep (1.3.0)
activesupport (>= 3.2)
capybara (>= 2.0)
ruby2_keywords
selenium-webdriver (>= 3)
capybara-screenshot (1.0.26)
capybara (>= 1.0, < 4)
launchy
childprocess (4.1.0)
coderay (1.1.3)
concurrent-ruby (1.1.10)
concurrent-ruby (1.2.0)
connection_pool (2.3.0)
crack (0.4.5)
rexml
@ -150,7 +153,7 @@ GEM
dotenv (= 2.8.1)
railties (>= 3.2)
encryptor (3.0.0)
erb_lint (0.2.0)
erb_lint (0.3.1)
activesupport
better_html (>= 2.0.1)
parser (>= 2.7.1.4)
@ -160,19 +163,19 @@ GEM
erubi (1.12.0)
et-orbi (1.2.7)
tzinfo
excon (0.92.5)
excon (0.99.0)
factory_bot (6.2.1)
activesupport (>= 5.0.0)
factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0)
railties (>= 5.0.0)
faker (2.23.0)
faker (3.1.1)
i18n (>= 1.8.11, < 2)
ffi (1.15.5)
fugit (1.8.1)
et-orbi (~> 1, >= 1.2.7)
raabro (~> 1.4)
globalid (1.0.1)
globalid (1.1.0)
activesupport (>= 5.0)
govuk-components (3.2.1)
actionpack (>= 6.1)
@ -186,7 +189,7 @@ GEM
activemodel (>= 6.1)
activesupport (>= 6.1)
html-attributes-utils (~> 0.9, >= 0.9.2)
govuk_markdown (1.0.0)
govuk_markdown (2.0.0)
activesupport
redcarpet
hashdiff (1.0.1)
@ -195,19 +198,21 @@ GEM
i18n (1.12.0)
concurrent-ruby (~> 1.0)
iniparse (1.5.0)
jmespath (1.6.1)
jsbundling-rails (1.0.3)
jmespath (1.6.2)
jsbundling-rails (1.1.1)
railties (>= 6.0.0)
json-schema (3.0.0)
addressable (>= 2.8)
jwt (2.5.0)
listen (3.7.1)
jwt (2.7.0)
launchy (2.5.2)
addressable (~> 2.8)
listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
loofah (2.19.1)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.8.0.1)
mail (2.8.1)
mini_mime (>= 0.1.1)
net-imap
net-pop
@ -217,7 +222,7 @@ GEM
method_source (1.0.0)
mini_mime (1.1.2)
minitest (5.17.0)
msgpack (1.5.6)
msgpack (1.6.0)
net-imap (0.3.4)
date
net-protocol
@ -228,33 +233,33 @@ GEM
net-smtp (0.3.3)
net-protocol
nio4r (2.5.8)
nokogiri (1.14.0-arm64-darwin)
nokogiri (1.14.2-arm64-darwin)
racc (~> 1.4)
nokogiri (1.14.0-x86_64-darwin)
nokogiri (1.14.2-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.14.0-x86_64-linux)
nokogiri (1.14.2-x86_64-linux)
racc (~> 1.4)
notifications-ruby-client (5.3.0)
notifications-ruby-client (5.4.0)
jwt (>= 1.5, < 3)
orm_adapter (0.5.0)
overcommit (0.59.1)
overcommit (0.60.0)
childprocess (>= 0.6.3, < 5)
iniparse (~> 1.4)
rexml (~> 3.2)
pagy (5.10.1)
activesupport
paper_trail (13.0.0)
activerecord (>= 5.2)
request_store (~> 1.1)
paper_trail (14.0.0)
activerecord (>= 6.0)
request_store (~> 1.4)
paper_trail-globalid (0.2.0)
globalid
paper_trail (>= 3.0.0)
parallel (1.22.1)
parallel_tests (4.0.0)
parallel_tests (4.2.0)
parallel
parser (3.1.2.1)
parser (3.2.1.0)
ast (~> 2.4.1)
pg (1.4.3)
pg (1.4.5)
possessive (1.0.1)
postcodes_io (0.4.0)
excon (~> 0.39)
@ -263,13 +268,13 @@ GEM
activesupport (>= 7.0.0)
rack
railties (>= 7.0.0)
pry (0.14.1)
pry (0.14.2)
coderay (~> 1.1)
method_source (~> 1.0)
pry-byebug (3.10.1)
byebug (~> 11.0)
pry (>= 0.13, < 0.15)
public_suffix (5.0.0)
public_suffix (5.0.1)
puma (5.6.5)
nio4r (~> 2.0)
raabro (1.4.0)
@ -281,28 +286,28 @@ GEM
rack (>= 1.2.0)
rack-test (2.0.2)
rack (>= 1.3)
rails (7.0.4.1)
actioncable (= 7.0.4.1)
actionmailbox (= 7.0.4.1)
actionmailer (= 7.0.4.1)
actionpack (= 7.0.4.1)
actiontext (= 7.0.4.1)
actionview (= 7.0.4.1)
activejob (= 7.0.4.1)
activemodel (= 7.0.4.1)
activerecord (= 7.0.4.1)
activestorage (= 7.0.4.1)
activesupport (= 7.0.4.1)
rails (7.0.4.2)
actioncable (= 7.0.4.2)
actionmailbox (= 7.0.4.2)
actionmailer (= 7.0.4.2)
actionpack (= 7.0.4.2)
actiontext (= 7.0.4.2)
actionview (= 7.0.4.2)
activejob (= 7.0.4.2)
activemodel (= 7.0.4.2)
activerecord (= 7.0.4.2)
activestorage (= 7.0.4.2)
activesupport (= 7.0.4.2)
bundler (>= 1.15.0)
railties (= 7.0.4.1)
railties (= 7.0.4.2)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.4.4)
rails-html-sanitizer (1.5.0)
loofah (~> 2.19, >= 2.19.1)
railties (7.0.4.1)
actionpack (= 7.0.4.1)
activesupport (= 7.0.4.1)
railties (7.0.4.2)
actionpack (= 7.0.4.2)
activesupport (= 7.0.4.2)
method_source
rake (>= 12.2)
thor (~> 1.0)
@ -313,36 +318,38 @@ GEM
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
redcarpet (3.5.1)
redis (4.8.0)
regexp_parser (2.5.0)
redcarpet (3.6.0)
redis (4.8.1)
redis-client (0.12.2)
connection_pool
regexp_parser (2.7.0)
request_store (1.5.1)
rack (>= 1.4)
responders (3.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
responders (3.1.0)
actionpack (>= 5.2)
railties (>= 5.2)
rexml (3.2.5)
roo (2.9.0)
roo (2.10.0)
nokogiri (~> 1)
rubyzip (>= 1.3.0, < 3.0.0)
rotp (6.2.0)
rspec-core (3.11.0)
rspec-support (~> 3.11.0)
rspec-expectations (3.11.1)
rotp (6.2.2)
rspec-core (3.12.1)
rspec-support (~> 3.12.0)
rspec-expectations (3.12.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.11.0)
rspec-mocks (3.11.1)
rspec-support (~> 3.12.0)
rspec-mocks (3.12.3)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.11.0)
rspec-rails (5.1.2)
actionpack (>= 5.2)
activesupport (>= 5.2)
railties (>= 5.2)
rspec-core (~> 3.10)
rspec-expectations (~> 3.10)
rspec-mocks (~> 3.10)
rspec-support (~> 3.10)
rspec-support (3.11.1)
rspec-support (~> 3.12.0)
rspec-rails (6.0.1)
actionpack (>= 6.1)
activesupport (>= 6.1)
railties (>= 6.1)
rspec-core (~> 3.11)
rspec-expectations (~> 3.11)
rspec-mocks (~> 3.11)
rspec-support (~> 3.11)
rspec-support (3.12.0)
rubocop (1.25.0)
parallel (~> 1.10)
parser (>= 3.1.0.0)
@ -360,7 +367,7 @@ GEM
rubocop-rails (= 2.13.2)
rubocop-rake (= 0.6.0)
rubocop-rspec (= 2.7.0)
rubocop-performance (1.15.0)
rubocop-performance (1.16.0)
rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0)
rubocop-rails (2.13.2)
@ -374,39 +381,39 @@ GEM
ruby-progressbar (1.11.0)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
selenium-webdriver (4.4.0)
childprocess (>= 0.5, < 5.0)
selenium-webdriver (4.8.1)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
sentry-rails (5.4.2)
sentry-rails (5.8.0)
railties (>= 5.0)
sentry-ruby (~> 5.4.2)
sentry-ruby (5.4.2)
sentry-ruby (~> 5.8.0)
sentry-ruby (5.8.0)
concurrent-ruby (~> 1.0, >= 1.0.2)
sidekiq (6.5.5)
connection_pool (>= 2.2.2)
rack (~> 2.0)
redis (>= 4.5.0)
sidekiq-cron (1.8.0)
fugit (~> 1)
sidekiq (7.0.5)
concurrent-ruby (< 2)
connection_pool (>= 2.3.0)
rack (>= 2.2.4)
redis-client (>= 0.11.0)
sidekiq-cron (1.9.1)
fugit (~> 1.8)
sidekiq (>= 4.2.1)
simplecov (0.21.2)
simplecov (0.22.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.4)
smart_properties (1.17.0)
stimulus-rails (1.1.0)
stimulus-rails (1.2.1)
railties (>= 6.0.0)
thor (1.2.1)
timecop (0.9.5)
timeout (0.3.1)
tzinfo (2.0.5)
timecop (0.9.6)
timeout (0.3.2)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
uk_postcode (2.1.8)
unicode-display_width (2.3.0)
unicode-display_width (2.4.2)
view_component (2.69.0)
activesupport (>= 5.0.0, < 8.0)
concurrent-ruby (~> 1.0)
@ -428,7 +435,7 @@ GEM
websocket-extensions (0.1.5)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.6.6)
zeitwerk (2.6.7)
PLATFORMS
arm64-darwin-21
@ -447,6 +454,7 @@ DEPENDENCIES
byebug
capybara
capybara-lockstep
capybara-screenshot
devise!
devise_two_factor_authentication
dotenv-rails
@ -454,7 +462,7 @@ DEPENDENCIES
factory_bot_rails
faker
govuk-components
govuk_design_system_formbuilder
govuk_design_system_formbuilder (= 3.1.2)
govuk_markdown
jsbundling-rails
json-schema

4
app/controllers/lettings_logs_controller.rb

@ -1,7 +1,7 @@
class LettingsLogsController < LogsController
before_action :find_resource, except: %i[create index edit]
before_action :session_filters, if: :current_user
before_action :set_session_filters, if: :current_user
before_action :session_filters, if: :current_user, only: %i[index email_csv download_csv]
before_action :set_session_filters, if: :current_user, only: %i[index email_csv download_csv]
before_action :extract_bulk_upload_from_session_filters, only: [:index]
before_action :redirect_if_bulk_upload_resolved, only: [:index]

4
app/controllers/organisations_controller.rb

@ -6,8 +6,8 @@ class OrganisationsController < ApplicationController
before_action :authenticate_user!
before_action :find_resource, except: %i[index new create]
before_action :authenticate_scope!, except: [:index]
before_action -> { session_filters(specific_org: true) }, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }
before_action :set_session_filters, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }
before_action -> { session_filters(specific_org: true) }, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_csv download_csv]
before_action :set_session_filters, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_csv download_csv]
def index
redirect_to organisation_path(current_user.organisation) unless current_user.support?

4
app/controllers/sales_logs_controller.rb

@ -1,6 +1,6 @@
class SalesLogsController < LogsController
before_action :session_filters, if: :current_user
before_action :set_session_filters, if: :current_user
before_action :session_filters, if: :current_user, only: %i[index email_csv download_csv]
before_action :set_session_filters, if: :current_user, only: %i[index email_csv download_csv]
def create
super { SalesLog.new(log_params) }

36
app/helpers/collection_time_helper.rb

@ -1,16 +1,42 @@
module CollectionTimeHelper
def collection_start_year(date)
window_end_date = Time.zone.local(date.year, 4, 1)
date < window_end_date ? date.year - 1 : date.year
end
def current_collection_start_year
today = Time.zone.now
window_end_date = Time.zone.local(today.year, 4, 1)
today < window_end_date ? today.year - 1 : today.year
collection_start_year(Time.zone.now)
end
def collection_start_date(date)
window_end_date = Time.zone.local(date.year, 4, 1)
date < window_end_date ? Time.zone.local(date.year - 1, 4, 1) : Time.zone.local(date.year, 4, 1)
Time.zone.local(collection_start_year(date), 4, 1)
end
def current_collection_start_date
Time.zone.local(current_collection_start_year, 4, 1)
end
def collection_end_date(date)
Time.zone.local(collection_start_year(date) + 1, 3, 31)
end
def current_collection_end_date
Time.zone.local(current_collection_start_year + 1, 3, 31)
end
def previous_collection_end_date
current_collection_end_date - 1.year
end
def next_collection_start_year
current_collection_start_year + 1
end
def previous_collection_start_year
current_collection_start_year - 1
end
def previous_collection_start_date
current_collection_start_date - 1.year
end
end

8
app/helpers/filters_helper.rb

@ -27,4 +27,12 @@ module FiltersHelper
organisation_options = user.support? ? Organisation.all : [user.organisation] + user.organisation.managing_agents
[OpenStruct.new(id: "", name: "Select an option")] + organisation_options.map { |org| OpenStruct.new(id: org.id, name: org.name) }
end
def collection_year_options
if FeatureToggle.collection_2023_2024_year_enabled?
{ "2023": "2023/24", "2022": "2022/23", "2021": "2021/22" }
else
{ "2022": "2022/23", "2021": "2021/22" }
end
end
end

33
app/helpers/interruption_screen_helper.rb

@ -1,17 +1,10 @@
module InterruptionScreenHelper
def display_informative_text(informative_text, lettings_log)
def display_informative_text(informative_text, log)
return "" unless informative_text["arguments"]
translation_params = {}
informative_text["arguments"].each do |argument|
value = if argument["label"]
pre_casing_value = lettings_log.form.get_question(argument["key"], lettings_log).answer_label(lettings_log)
pre_casing_value.downcase
elsif argument["currency"]
number_to_currency(lettings_log.public_send(argument["key"]), delimiter: ",", format: "%n", unit: "£")
else
lettings_log.public_send(argument["key"])
end
value = get_value_from_argument(log, argument)
translation_params[argument["i18n_template"].to_sym] = value
end
@ -24,21 +17,27 @@ module InterruptionScreenHelper
end
end
def display_title_text(title_text, lettings_log)
def display_title_text(title_text, log)
return "" if title_text.nil?
translation_params = {}
arguments = title_text["arguments"] || {}
arguments.each do |argument|
value = if argument["label"]
lettings_log.form.get_question(argument["key"], lettings_log).answer_label(lettings_log).downcase
elsif argument["currency"]
number_to_currency(lettings_log.public_send(argument["key"]), delimiter: ",", format: "%n", unit: "£")
else
lettings_log.public_send(argument["key"])
end
value = get_value_from_argument(log, argument)
translation_params[argument["i18n_template"].to_sym] = value
end
I18n.t(title_text["translation"], **translation_params).to_s
end
private
def get_value_from_argument(log, argument)
if argument["label"]
log.form.get_question(argument["key"], log).answer_label(log).downcase
elsif argument["arguments_for_key"]
log.public_send(argument["key"], argument["arguments_for_key"])
else
log.public_send(argument["key"])
end
end
end

25
app/helpers/tasklist_helper.rb

@ -11,15 +11,6 @@ module TasklistHelper
log.form.subsections.count { |subsection| subsection.status(log) == status && subsection.applicable_questions(log).count.positive? }
end
def next_page_or_check_answers(subsection, log, current_user)
path = if subsection.is_started?(log)
"#{log.class.name.underscore}_#{subsection.id}_check_answers_path"
else
"#{log.class.name.underscore}_#{next_question_page(subsection, log, current_user)}_path"
end
send(path, log)
end
def next_question_page(subsection, log, current_user)
if subsection.pages.first.routed_to?(log, current_user)
subsection.pages.first.id
@ -39,9 +30,23 @@ module TasklistHelper
def review_log_text(log)
if log.collection_period_open?
"You can #{govuk_link_to 'review and make changes to this log', review_lettings_log_path(log)} until #{log.form.end_date.to_formatted_s(:govuk_date)}.".html_safe
link = log.sales? ? review_sales_log_path(id: log, sales_log: true) : review_lettings_log_path(log)
"You can #{govuk_link_to 'review and make changes to this log', link} until #{log.form.end_date.to_formatted_s(:govuk_date)}.".html_safe
else
"This log is from the #{log.form.start_date.year}/#{log.form.start_date.year + 1} collection window, which is now closed."
end
end
private
def next_page_or_check_answers(subsection, log, current_user)
path = if subsection.is_started?(log)
"#{log.class.name.underscore}_#{subsection.id}_check_answers_path"
else
"#{log.class.name.underscore}_#{next_question_page(subsection, log, current_user)}_path"
end
send(path, log)
end
end

28
app/mailers/bulk_upload_mailer.rb

@ -33,17 +33,29 @@ class BulkUploadMailer < NotifyMailer
)
end
def send_bulk_upload_failed_csv_errors_mail(user, bulk_upload)
def columns_with_errors(bulk_upload:)
array = bulk_upload.columns_with_errors
if array.size > 3
"#{array.take(3).join(', ')} and more"
else
array.join(", ")
end
end
def send_correct_and_upload_again_mail(bulk_upload:)
error_description = "We noticed that you have a lot of similar errors in column #{columns_with_errors(bulk_upload:)}. Please correct your data export and upload again."
send_email(
user.email,
bulk_upload.user.email,
BULK_UPLOAD_FAILED_CSV_ERRORS_TEMPLATE_ID,
{
filename: "[#{bulk_upload} filename]",
upload_timestamp: "[#{bulk_upload} upload_timestamp]",
year_combo: "[#{bulk_upload} year_combo]",
lettings_or_sales: "[#{bulk_upload} lettings_or_sales]",
error_description: "[#{bulk_upload} error_description]",
summary_report_link: "[#{bulk_upload} summary_report_link]",
filename: bulk_upload.filename,
upload_timestamp: bulk_upload.created_at.to_fs(:govuk_date_and_time),
year_combo: bulk_upload.year_combo,
lettings_or_sales: bulk_upload.log_type,
error_description:,
summary_report_link: summary_bulk_upload_lettings_result_url(bulk_upload),
},
)
end

12
app/models/bulk_upload.rb

@ -30,10 +30,22 @@ class BulkUpload < ApplicationRecord
end
end
def columns_with_errors
bulk_upload_errors
.select(:col)
.distinct(:col)
.pluck(:col)
.sort_by { |col| col.rjust(2, "0") }
end
def general_needs?
needstype == 1
end
def supported_housing?
needstype == 2
end
private
def generate_identifier

4
app/models/derived_variables/lettings_log_variables.rb

@ -45,6 +45,9 @@ module DerivedVariables::LettingsLogVariables
self.underoccupation_benefitcap = 2 if collection_start_year == 2021
self.referral = 1
self.waityear = 2
self.offered = 0
self.voiddate = startdate
self.first_time_property_let_as_social_housing = 0
if is_general_needs?
# fixed term
self.prevten = 32 if managing_organisation&.provider_type == "PRP"
@ -60,7 +63,6 @@ module DerivedVariables::LettingsLogVariables
if is_supported_housing? && location
self.wchair = location.mobility_type_before_type_cast == "W" ? 1 : 2
end
self.voiddate = startdate if is_renewal?
self.vacdays = property_vacant_days
set_housingneeds_fields if housingneeds?

6
app/models/form.rb

@ -6,7 +6,11 @@ class Form
def initialize(form_path, start_year = "", sections_in_form = [], type = "lettings")
if sales_or_start_year_after_2022?(type, start_year)
@start_date = Time.zone.local(start_year, 4, 1)
@end_date = Time.zone.local(start_year + 1, 7, 1)
@end_date = if start_year && start_year.to_i > 2022
Time.zone.local(start_year + 1, 7, 9)
else
Time.zone.local(start_year + 1, 7, 7)
end
@setup_sections = type == "sales" ? [Form::Sales::Sections::Setup.new(nil, nil, self)] : [Form::Lettings::Sections::Setup.new(nil, nil, self)]
@form_sections = sections_in_form.map { |sec| sec.new(nil, nil, self) }
@type = type

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

@ -2,7 +2,7 @@ class Form::Lettings::Questions::LocationId < ::Form::Question
def initialize(_id, hsh, page)
super("location_id", hsh, page)
@check_answer_label = "Location"
@header = "Which location is this log for?"
@header = header_text
@type = "radio"
@answer_options = answer_options
@inferred_answers = {
@ -47,4 +47,12 @@ private
def selected_answer_option_is_derived?(_lettings_log)
false
end
def header_text
if form.start_date && form.start_date.year >= 2023
"Which location is this letting for?"
else
"Which location is this log for?"
end
end
end

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

@ -11,8 +11,8 @@ class Form::Lettings::Questions::WorkingSituation1 < ::Form::Question
end
ANSWER_OPTIONS = {
"2" => { "value" => "Part-time – Less than 30 hours" },
"1" => { "value" => "Full-time – 30 hours or more" },
"2" => { "value" => "Part-time – Less than 30 hours" },
"7" => { "value" => "Full-time student" },
"3" => { "value" => "In government training into work, such as New Deal" },
"4" => { "value" => "Jobseeker" },

6
app/models/form/sales/pages/about_price_shared_ownership_value_check.rb

@ -20,14 +20,12 @@ class Form::Sales::Pages::AboutPriceSharedOwnershipValueCheck < ::Form::Page
"translation" => "soft_validations.purchase_price.hint_text",
"arguments" => [
{
"key" => "purchase_price_soft_min_or_soft_max",
"label" => false,
"key" => "field_formatted_as_currency",
"arguments_for_key" => "purchase_price_soft_min_or_soft_max",
"i18n_template" => "soft_min_or_soft_max",
"currency" => true,
},
{
"key" => "purchase_price_min_or_max_text",
"label" => false,
"i18n_template" => "min_or_max",
},
],

9
app/models/form/sales/pages/about_staircase.rb

@ -12,6 +12,13 @@ class Form::Sales::Pages::AboutStaircase < ::Form::Page
@questions ||= [
Form::Sales::Questions::StaircaseBought.new(nil, nil, self),
Form::Sales::Questions::StaircaseOwned.new(nil, nil, self),
]
staircase_sale_question,
].compact
end
def staircase_sale_question
if form.start_date.year >= 2023
Form::Sales::Questions::StaircaseSale.new(nil, nil, self)
end
end
end

15
app/models/form/sales/pages/buyer1_income_value_check.rb

@ -6,6 +6,21 @@ class Form::Sales::Pages::Buyer1IncomeValueCheck < ::Form::Page
"income1_under_soft_min?" => true,
},
]
@title_text = {
"translation" => "soft_validations.income.under_soft_min_for_economic_status",
"arguments" => [
{
"key" => "field_formatted_as_currency",
"arguments_for_key" => "income1",
"i18n_template" => "income",
},
{
"key" => "income_soft_min_for_ecstat",
"arguments_for_key" => "ecstat1",
"i18n_template" => "minimum",
},
],
}
@informative_text = {}
end

15
app/models/form/sales/pages/buyer2_ethnic_background_arab.rb

@ -0,0 +1,15 @@
class Form::Sales::Pages::Buyer2EthnicBackgroundArab < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "buyer_2_ethnic_background_arab"
@depends_on = [{
"ethnic_group2" => 4,
}]
end
def questions
@questions ||= [
Form::Sales::Questions::Buyer2EthnicBackgroundArab.new(nil, nil, self),
]
end
end

15
app/models/form/sales/pages/buyer2_ethnic_background_asian.rb

@ -0,0 +1,15 @@
class Form::Sales::Pages::Buyer2EthnicBackgroundAsian < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "buyer_2_ethnic_background_asian"
@depends_on = [{
"ethnic_group2" => 2,
}]
end
def questions
@questions ||= [
Form::Sales::Questions::Buyer2EthnicBackgroundAsian.new(nil, nil, self),
]
end
end

15
app/models/form/sales/pages/buyer2_ethnic_background_black.rb

@ -0,0 +1,15 @@
class Form::Sales::Pages::Buyer2EthnicBackgroundBlack < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "buyer_2_ethnic_background_black"
@depends_on = [{
"ethnic_group2" => 3,
}]
end
def questions
@questions ||= [
Form::Sales::Questions::Buyer2EthnicBackgroundBlack.new(nil, nil, self),
]
end
end

15
app/models/form/sales/pages/buyer2_ethnic_background_mixed.rb

@ -0,0 +1,15 @@
class Form::Sales::Pages::Buyer2EthnicBackgroundMixed < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "buyer_2_ethnic_background_mixed"
@depends_on = [{
"ethnic_group2" => 1,
}]
end
def questions
@questions ||= [
Form::Sales::Questions::Buyer2EthnicBackgroundMixed.new(nil, nil, self),
]
end
end

15
app/models/form/sales/pages/buyer2_ethnic_background_white.rb

@ -0,0 +1,15 @@
class Form::Sales::Pages::Buyer2EthnicBackgroundWhite < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "buyer_2_ethnic_background_white"
@depends_on = [{
"ethnic_group2" => 0,
}]
end
def questions
@questions ||= [
Form::Sales::Questions::Buyer2EthnicBackgroundWhite.new(nil, nil, self),
]
end
end

22
app/models/form/sales/pages/buyer2_ethnic_group.rb

@ -0,0 +1,22 @@
class Form::Sales::Pages::Buyer2EthnicGroup < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "buyer_2_ethnic_group"
@depends_on = [
{
"jointpur" => 1,
"privacynotice" => 1,
},
{
"jointpur" => 1,
"noint" => 1,
},
]
end
def questions
@questions ||= [
Form::Sales::Questions::Buyer2EthnicGroup.new(nil, nil, self),
]
end
end

35
app/models/form/sales/pages/buyer2_income_value_check.rb

@ -0,0 +1,35 @@
class Form::Sales::Pages::Buyer2IncomeValueCheck < ::Form::Page
def initialize(id, hsh, subsection)
super
@header = ""
@description = ""
@subsection = subsection
@depends_on = [
{
"income2_under_soft_min?" => true,
},
]
@title_text = {
"translation" => "soft_validations.income.under_soft_min_for_economic_status",
"arguments" => [
{
"key" => "field_formatted_as_currency",
"arguments_for_key" => "income2",
"i18n_template" => "income",
},
{
"key" => "income_soft_min_for_ecstat",
"arguments_for_key" => "ecstat2",
"i18n_template" => "minimum",
},
],
}
@informative_text = {}
end
def questions
@questions ||= [
Form::Sales::Questions::Buyer2IncomeValueCheck.new(nil, nil, self),
]
end
end

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

@ -3,7 +3,7 @@ class Form::Sales::Questions::Buyer1IncomeValueCheck < ::Form::Question
super
@id = "income1_value_check"
@check_answer_label = "Income confirmation"
@header = "Are you sure this income is correct?"
@header = "Are you sure this is correct?"
@type = "interruption_screen"
@answer_options = {
"0" => { "value" => "Yes" },

16
app/models/form/sales/questions/buyer2_ethnic_background_arab.rb

@ -0,0 +1,16 @@
class Form::Sales::Questions::Buyer2EthnicBackgroundArab < ::Form::Question
def initialize(id, hsh, page)
super
@id = "ethnicbuy2"
@check_answer_label = "Buyer 2’s ethnic background"
@header = "Which of the following best describes the buyer 2’s Arab background?"
@type = "radio"
@answer_options = ANSWER_OPTIONS
@check_answers_card_number = 2
end
ANSWER_OPTIONS = {
"19" => { "value" => "Arab" },
"16" => { "value" => "Other ethnic group" },
}.freeze
end

19
app/models/form/sales/questions/buyer2_ethnic_background_asian.rb

@ -0,0 +1,19 @@
class Form::Sales::Questions::Buyer2EthnicBackgroundAsian < ::Form::Question
def initialize(id, hsh, page)
super
@id = "ethnicbuy2"
@check_answer_label = "Buyer 2’s ethnic background"
@header = "Which of the following best describes the buyer 2’s Asian or Asian British background?"
@type = "radio"
@answer_options = ANSWER_OPTIONS
@check_answers_card_number = 2
end
ANSWER_OPTIONS = {
"10" => { "value" => "Bangladeshi" },
"15" => { "value" => "Chinese" },
"8" => { "value" => "Indian" },
"9" => { "value" => "Pakistani" },
"11" => { "value" => "Any other Asian or Asian British background" },
}.freeze
end

17
app/models/form/sales/questions/buyer2_ethnic_background_black.rb

@ -0,0 +1,17 @@
class Form::Sales::Questions::Buyer2EthnicBackgroundBlack < ::Form::Question
def initialize(id, hsh, page)
super
@id = "ethnicbuy2"
@check_answer_label = "Buyer 2’s ethnic background"
@header = "Which of the following best describes the buyer 2’s Black, African, Caribbean or Black British background?"
@type = "radio"
@answer_options = ANSWER_OPTIONS
@check_answers_card_number = 2
end
ANSWER_OPTIONS = {
"13" => { "value" => "African" },
"12" => { "value" => "Caribbean" },
"14" => { "value" => "Any other Black, African or Caribbean background" },
}.freeze
end

18
app/models/form/sales/questions/buyer2_ethnic_background_mixed.rb

@ -0,0 +1,18 @@
class Form::Sales::Questions::Buyer2EthnicBackgroundMixed < ::Form::Question
def initialize(id, hsh, page)
super
@id = "ethnicbuy2"
@check_answer_label = "Buyer 2’s ethnic background"
@header = "Which of the following best describes the buyer 2’s Mixed or Multiple ethnic groups background?"
@type = "radio"
@answer_options = ANSWER_OPTIONS
@check_answers_card_number = 2
end
ANSWER_OPTIONS = {
"4" => { "value" => "White and Black Caribbean" },
"5" => { "value" => "White and Black African" },
"6" => { "value" => "White and Asian" },
"7" => { "value" => "Any other Mixed or Multiple ethnic background" },
}.freeze
end

18
app/models/form/sales/questions/buyer2_ethnic_background_white.rb

@ -0,0 +1,18 @@
class Form::Sales::Questions::Buyer2EthnicBackgroundWhite < ::Form::Question
def initialize(id, hsh, page)
super
@id = "ethnicbuy2"
@check_answer_label = "Buyer 2’s ethnic background"
@header = "Which of the following best describes the buyer 2’s White background?"
@type = "radio"
@answer_options = ANSWER_OPTIONS
@check_answers_card_number = 2
end
ANSWER_OPTIONS = {
"1" => { "value" => "English, Welsh, Northern Irish, Scottish or British" },
"2" => { "value" => "Irish" },
"18" => { "value" => "Gypsy or Irish Traveller" },
"3" => { "value" => "Any other White background" },
}.freeze
end

27
app/models/form/sales/questions/buyer2_ethnic_group.rb

@ -0,0 +1,27 @@
class Form::Sales::Questions::Buyer2EthnicGroup < ::Form::Question
def initialize(id, hsh, page)
super
@id = "ethnic_group2"
@check_answer_label = "Buyer 2’s ethnic group"
@header = "What is buyer 2’s ethnic group?"
@type = "radio"
@answer_options = ANSWER_OPTIONS
@inferred_check_answers_value = [{
"condition" => {
"ethnic_group2" => 17,
},
"value" => "Prefers not to say",
}]
@check_answers_card_number = 2
end
ANSWER_OPTIONS = {
"0" => { "value" => "White" },
"1" => { "value" => "Mixed or Multiple ethnic groups" },
"2" => { "value" => "Asian or Asian British" },
"3" => { "value" => "Black, African, Caribbean or Black British" },
"4" => { "value" => "Arab or other ethnic group" },
"divider" => { "value" => true },
"17" => { "value" => "Buyer 1 prefers not to say" },
}.freeze
end

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

@ -7,6 +7,7 @@ class Form::Sales::Questions::Buyer2Income < ::Form::Question
@type = "numeric"
@hint_text = "Provide the gross annual income (i.e. salary before tax) plus the annual amount of benefits, Universal Credit or pensions, and income from investments."
@min = 0
@max = 999_999
@step = 1
@width = 5
@prefix = "£"

25
app/models/form/sales/questions/buyer2_income_value_check.rb

@ -0,0 +1,25 @@
class Form::Sales::Questions::Buyer2IncomeValueCheck < ::Form::Question
def initialize(id, hsh, page)
super
@id = "income2_value_check"
@check_answer_label = "Income confirmation"
@header = "Are you sure this is correct?"
@type = "interruption_screen"
@answer_options = {
"0" => { "value" => "Yes" },
"1" => { "value" => "No" },
}
@hidden_in_check_answers = {
"depends_on" => [
{
"income2_value_check" => 0,
},
{
"income2_value_check" => 1,
},
],
}
@check_answers_card_number = 2
@page = page
end
end

16
app/models/form/sales/questions/staircase_sale.rb

@ -0,0 +1,16 @@
class Form::Sales::Questions::StaircaseSale < ::Form::Question
def initialize(id, hsh, page)
super
@id = "staircasesale"
@check_answer_label = "Part of a back-to-back staircasing transaction"
@header = "Is this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?"
@type = "radio"
@answer_options = ANSWER_OPTIONS
end
ANSWER_OPTIONS = {
"1" => { "value" => "Yes" },
"2" => { "value" => "No" },
"3" => { "value" => "Don't know" },
}.freeze
end

17
app/models/form/sales/subsections/household_characteristics.rb

@ -32,8 +32,10 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Sales::Pages::RetirementValueCheck.new("age_2_buyer_retirement_value_check", nil, self, person_index: 2),
Form::Sales::Pages::GenderIdentity2.new(nil, nil, self),
Form::Sales::Pages::RetirementValueCheck.new("gender_2_buyer_retirement_value_check", nil, self, person_index: 2),
ethnic_pages_for_buyer_2,
Form::Sales::Pages::Buyer2WorkingSituation.new(nil, nil, self),
Form::Sales::Pages::RetirementValueCheck.new("working_situation_2_buyer_retirement_value_check", nil, self, person_index: 2),
Form::Sales::Pages::RetirementValueCheck.new("working_situation_2_retirement_value_check_joint_purchase", nil, self, person_index: 2),
Form::Sales::Pages::Buyer2IncomeValueCheck.new("working_situation_buyer_2_income_value_check", nil, self),
Form::Sales::Pages::Buyer2LiveInProperty.new(nil, nil, self),
Form::Sales::Pages::NumberOfOthersInProperty.new(nil, nil, self),
Form::Sales::Pages::PersonKnown.new("person_2_known", nil, self, person_index: 2),
@ -68,6 +70,17 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Sales::Pages::RetirementValueCheck.new("gender_5_retirement_value_check", nil, self, person_index: 5),
Form::Sales::Pages::PersonWorkingSituation.new("person_5_working_situation", nil, self, person_index: 5),
Form::Sales::Pages::RetirementValueCheck.new("working_situation_5_retirement_value_check", nil, self, person_index: 5),
]
].flatten.compact
end
def ethnic_pages_for_buyer_2
if form.start_date.year >= 2023
[Form::Sales::Pages::Buyer2EthnicGroup.new(nil, nil, self),
Form::Sales::Pages::Buyer2EthnicBackgroundBlack.new(nil, nil, self),
Form::Sales::Pages::Buyer2EthnicBackgroundAsian.new(nil, nil, self),
Form::Sales::Pages::Buyer2EthnicBackgroundArab.new(nil, nil, self),
Form::Sales::Pages::Buyer2EthnicBackgroundMixed.new(nil, nil, self),
Form::Sales::Pages::Buyer2EthnicBackgroundWhite.new(nil, nil, self)]
end
end
end

1
app/models/form/sales/subsections/income_benefits_and_savings.rb

@ -15,6 +15,7 @@ class Form::Sales::Subsections::IncomeBenefitsAndSavings < ::Form::Subsection
Form::Sales::Pages::MortgageValueCheck.new("buyer_1_mortgage_value_check", nil, self, 1),
Form::Sales::Pages::Buyer2Income.new(nil, nil, self),
Form::Sales::Pages::MortgageValueCheck.new("buyer_2_income_mortgage_value_check", nil, self, 2),
Form::Sales::Pages::Buyer2IncomeValueCheck.new("buyer_2_income_value_check", nil, self),
Form::Sales::Pages::Buyer2Mortgage.new(nil, nil, self),
Form::Sales::Pages::MortgageValueCheck.new("buyer_2_mortgage_value_check", nil, self, 2),
Form::Sales::Pages::HousingBenefits.new(nil, nil, self),

8
app/models/form_handler.rb

@ -35,8 +35,8 @@ class FormHandler
def sales_forms
{
"current_sales" => Form.new(nil, current_collection_start_year, SALES_SECTIONS, "sales"),
"previous_sales" => Form.new(nil, current_collection_start_year - 1, SALES_SECTIONS, "sales"),
"next_sales" => Form.new(nil, current_collection_start_year + 1, SALES_SECTIONS, "sales"),
"previous_sales" => Form.new(nil, previous_collection_start_year, SALES_SECTIONS, "sales"),
"next_sales" => Form.new(nil, next_collection_start_year, SALES_SECTIONS, "sales"),
}
end
@ -52,10 +52,10 @@ class FormHandler
end
if forms["previous_lettings"].blank? && current_collection_start_year >= 2022
forms["previous_lettings"] = Form.new(nil, current_collection_start_year - 1, LETTINGS_SECTIONS, "lettings")
forms["previous_lettings"] = Form.new(nil, previous_collection_start_year, LETTINGS_SECTIONS, "lettings")
end
forms["current_lettings"] = Form.new(nil, current_collection_start_year, LETTINGS_SECTIONS, "lettings") if forms["current_lettings"].blank?
forms["next_lettings"] = Form.new(nil, current_collection_start_year + 1, LETTINGS_SECTIONS, "lettings") if forms["next_lettings"].blank?
forms["next_lettings"] = Form.new(nil, next_collection_start_year, LETTINGS_SECTIONS, "lettings") if forms["next_lettings"].blank?
forms
end

12
app/models/lettings_log.rb

@ -70,6 +70,14 @@ class LettingsLog < Log
collection_start_year
end
def lettings?
true
end
def sales?
false
end
def form_name
return unless startdate
@ -481,10 +489,6 @@ class LettingsLog < Log
location.type_of_unit_before_type_cast if location
end
def lettings?
true
end
def rent_type_detail
form.get_question("rent_type", self)&.label_from_value(rent_type)
end

6
app/models/location.rb

@ -366,6 +366,12 @@ class Location < ApplicationRecord
enum type_of_unit: TYPE_OF_UNIT
def self.find_by_id_on_mulitple_fields(id)
return if id.nil?
where(id:).or(where(old_visible_id: id)).first
end
def postcode=(postcode)
if postcode
super UKPostcode.parse(postcode).to_s

8
app/models/log.rb

@ -43,6 +43,10 @@ class Log < ApplicationRecord
false
end
def sales?
false
end
def ethnic_refused?
ethnic_group == 17
end
@ -143,4 +147,8 @@ private
self[is_inferred_key] = false
self[postcode_key] = nil
end
def format_as_currency(num_string)
ActionController::Base.helpers.number_to_currency(num_string, unit: "£")
end
end

27
app/models/organisation.rb

@ -17,8 +17,21 @@ class Organisation < ApplicationRecord
has_many :managing_agent_relationships, foreign_key: :parent_organisation_id, class_name: "OrganisationRelationship"
has_many :managing_agents, through: :managing_agent_relationships, source: :child_organisation
def affiliated_stock_owners
ids = []
if holds_own_stock? && persisted?
ids << id
end
ids.concat(stock_owners.pluck(:id))
Organisation.where(id: ids)
end
scope :search_by_name, ->(name) { where("name ILIKE ?", "%#{name}%") }
scope :search_by, ->(param) { search_by_name(param) }
has_paper_trail
auto_strip_attributes :name
@ -35,6 +48,20 @@ class Organisation < ApplicationRecord
validates :name, presence: { message: I18n.t("validations.organisation.name_missing") }
validates :provider_type, presence: { message: I18n.t("validations.organisation.provider_type_missing") }
def self.find_by_id_on_mulitple_fields(id)
return if id.nil?
if id.start_with?("ORG")
where(id: id[3..]).first
else
where(old_visible_id: id).first
end
end
def can_be_managed_by?(organisation:)
organisation == self || managing_agents.include?(organisation)
end
def lettings_logs
LettingsLog.filter_by_organisation(self)
end

30
app/models/sales_log.rb

@ -38,6 +38,14 @@ class SalesLog < Log
OPTIONAL_FIELDS = %w[saledate_check purchid monthly_charges_value_check old_persons_shared_ownership_value_check].freeze
RETIREMENT_AGES = { "M" => 65, "F" => 60, "X" => 65 }.freeze
def lettings?
false
end
def sales?
true
end
def startdate
saledate
end
@ -116,6 +124,10 @@ class SalesLog < Log
la && LONDON_BOROUGHS.include?(la)
end
def property_not_in_london?
!london_property?
end
def income1_used_for_mortgage?
inc1mort == 1
end
@ -223,6 +235,10 @@ class SalesLog < Log
type == 24
end
def is_bedsit?
proptype == 2
end
def shared_ownership_scheme?
ownershipsch == 1
end
@ -244,4 +260,18 @@ class SalesLog < Log
def purchase_price_soft_max
LaSaleRange.find_by(start_year: collection_start_year, la:, bedrooms: beds).soft_max
end
def income_soft_min_for_ecstat(ecstat_field)
economic_status_code = public_send(ecstat_field)
return unless ALLOWED_INCOME_RANGES_SALES
soft_min = ALLOWED_INCOME_RANGES_SALES[economic_status_code]&.soft_min
format_as_currency(soft_min)
end
def field_formatted_as_currency(field_name)
field_value = public_send(field_name)
format_as_currency(field_value)
end
end

10
app/models/scheme.rb

@ -110,6 +110,16 @@ class Scheme < ApplicationRecord
enum arrangement_type: ARRANGEMENT_TYPE, _suffix: true
def self.find_by_id_on_mulitple_fields(id)
return if id.nil?
if id.start_with?("S")
where(id: id[1..]).first
else
where(old_visible_id: id).first
end
end
def id_to_display
"S#{id}"
end

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

@ -3,21 +3,37 @@ module Validations::Sales::FinancialValidations
# or 'validate_' to run on submit as well
def validate_income1(record)
if record.ecstat1 && record.income1 && record.la && record.ownershipsch == 1
if record.london_property?
record.errors.add :income1, I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000) if record.income1 > 90_000
record.errors.add :ecstat1, I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000) if record.income1 > 90_000
record.errors.add :ownershipsch, I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000) if record.income1 > 90_000
record.errors.add :la, I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000) if record.income1 > 90_000
record.errors.add :postcode_full, I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000) if record.income1 > 90_000
elsif record.income1 > 80_000
record.errors.add :income1, I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000)
record.errors.add :ecstat1, I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000)
record.errors.add :ownershipsch, I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000)
record.errors.add :la, I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000) if record.income1 > 80_000
record.errors.add :postcode_full, I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000) if record.income1 > 80_000
return unless record.income1 && record.la && record.shared_ownership_scheme?
relevant_fields = %i[income1 ownershipsch la postcode_full]
if record.london_property? && record.income1 > 90_000
relevant_fields.each { |field| record.errors.add field, I18n.t("validations.financial.income.over_hard_max_for_london") }
elsif record.property_not_in_london? && record.income1 > 80_000
relevant_fields.each { |field| record.errors.add field, I18n.t("validations.financial.income.over_hard_max_for_outside_london") }
end
end
def validate_income2(record)
return unless record.income2 && record.la && record.shared_ownership_scheme?
relevant_fields = %i[income2 ownershipsch la postcode_full]
if record.london_property? && record.income2 > 90_000
relevant_fields.each { |field| record.errors.add field, I18n.t("validations.financial.income.over_hard_max_for_london") }
elsif record.property_not_in_london? && record.income2 > 80_000
relevant_fields.each { |field| record.errors.add field, I18n.t("validations.financial.income.over_hard_max_for_outside_london") }
end
end
def validate_combined_income(record)
return unless record.income1 && record.income2 && record.la && record.shared_ownership_scheme?
combined_income = record.income1 + record.income2
relevant_fields = %i[income1 income2 ownershipsch la postcode_full]
if record.london_property? && combined_income > 90_000
relevant_fields.each { |field| record.errors.add field, I18n.t("validations.financial.income.combined_over_hard_max_for_london") }
elsif record.property_not_in_london? && combined_income > 80_000
relevant_fields.each { |field| record.errors.add field, I18n.t("validations.financial.income.combined_over_hard_max_for_outside_london") }
end
end
def validate_cash_discount(record)
@ -36,6 +52,15 @@ module Validations::Sales::FinancialValidations
end
end
def validate_child_income(record)
return unless record.income2 && record.ecstat2
if record.income2.positive? && is_economic_status_child?(record.ecstat2)
record.errors.add :ecstat2, I18n.t("validations.financial.income.child_has_income")
record.errors.add :income2, I18n.t("validations.financial.income.child_has_income")
end
end
def validate_percentage_owned_not_too_much_if_older_person(record)
return unless record.old_persons_shared_ownership? && record.stairowned
@ -44,4 +69,14 @@ module Validations::Sales::FinancialValidations
record.errors.add :type, I18n.t("validations.financial.staircasing.older_person_percentage_owned_maximum_75")
end
end
private
def is_relationship_child?(relationship)
relationship == "C"
end
def is_economic_status_child?(economic_status)
economic_status == 9
end
end

9
app/models/validations/sales/property_validations.rb

@ -7,4 +7,13 @@ module Validations::Sales::PropertyValidations
record.errors.add :ppostcode_full, I18n.t("validations.property.postcode.must_match_previous")
end
end
def validate_bedsit_number_of_beds(record)
return unless record.proptype.present? && record.beds.present?
if record.is_bedsit? && record.beds > 1
record.errors.add :proptype, I18n.t("validations.property.proptype.bedsits_have_max_one_bedroom")
record.errors.add :beds, I18n.t("validations.property.beds.bedsits_have_max_one_bedroom")
end
end
end

38
app/models/validations/sales/setup_validations.rb

@ -1,11 +1,45 @@
module Validations::Sales::SetupValidations
include Validations::SharedValidations
include CollectionTimeHelper
def validate_saledate(record)
return unless record.saledate && date_valid?("saledate", record)
unless record.saledate.between?(Time.zone.local(2022, 4, 1), Time.zone.local(2023, 3, 31)) || !FeatureToggle.saledate_collection_window_validation_enabled?
record.errors.add :saledate, I18n.t("validations.setup.saledate.financial_year")
unless record.saledate.between?(active_collection_start_date, current_collection_end_date) || !FeatureToggle.saledate_collection_window_validation_enabled?
record.errors.add :saledate, validation_error_message
end
end
private
def active_collection_start_date
if FormHandler.instance.sales_in_crossover_period?
previous_collection_start_date
else
current_collection_start_date
end
end
def validation_error_message
current_end_year_long = current_collection_end_date.strftime("#{current_collection_end_date.day.ordinalize} %B %Y")
if FormHandler.instance.sales_in_crossover_period?
I18n.t(
"validations.setup.saledate.previous_and_current_financial_year",
previous_start_year_short: previous_collection_start_date.strftime("%y"),
previous_end_year_short: previous_collection_end_date.strftime("%y"),
previous_start_year_long: previous_collection_start_date.strftime("#{previous_collection_start_date.day.ordinalize} %B %Y"),
current_end_year_short: current_collection_end_date.strftime("%y"),
current_end_year_long:,
)
else
I18n.t(
"validations.setup.saledate.current_financial_year",
current_start_year_short: current_collection_start_date.strftime("%y"),
current_end_year_short: current_collection_end_date.strftime("%y"),
current_start_year_long: current_collection_start_date.strftime("#{current_collection_start_date.day.ordinalize} %B %Y"),
current_end_year_long:,
)
end
end
end

12
app/models/validations/sales/soft_validations.rb

@ -1,5 +1,5 @@
module Validations::Sales::SoftValidations
ALLOWED_INCOME_RANGES = {
ALLOWED_INCOME_RANGES_SALES = {
1 => OpenStruct.new(soft_min: 5000),
2 => OpenStruct.new(soft_min: 1500),
3 => OpenStruct.new(soft_min: 1000),
@ -8,9 +8,15 @@ module Validations::Sales::SoftValidations
}.freeze
def income1_under_soft_min?
return false unless ecstat1 && income1 && ALLOWED_INCOME_RANGES[ecstat1]
return false unless ecstat1 && income1 && ALLOWED_INCOME_RANGES_SALES[ecstat1]
income1 < ALLOWED_INCOME_RANGES[ecstat1][:soft_min]
income1 < ALLOWED_INCOME_RANGES_SALES[ecstat1][:soft_min]
end
def income2_under_soft_min?
return false unless ecstat2 && income2 && ALLOWED_INCOME_RANGES_SALES[ecstat2]
income2 < ALLOWED_INCOME_RANGES_SALES[ecstat2][:soft_min]
end
def staircase_bought_above_fifty?

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

@ -10,6 +10,8 @@ class BulkUpload::Lettings::LogCreator
row_parsers.each do |row_parser|
row_parser.valid?
next if row_parser.blank_row?
row_parser.log.blank_invalid_non_setup_fields!
row_parser.log.bulk_upload = bulk_upload

194
app/services/bulk_upload/lettings/row_parser.rb

@ -3,11 +3,12 @@ class BulkUpload::Lettings::RowParser
include ActiveModel::Attributes
attribute :bulk_upload
attribute :block_log_creation, :boolean, default: -> { false }
attribute :field_1, :integer
attribute :field_2
attribute :field_3
attribute :field_4, :integer
attribute :field_4, :string
attribute :field_5, :integer
attribute :field_6
attribute :field_7, :string
@ -114,9 +115,9 @@ class BulkUpload::Lettings::RowParser
attribute :field_108, :string
attribute :field_109, :string
attribute :field_110
attribute :field_111, :integer
attribute :field_111, :string
attribute :field_112, :string
attribute :field_113, :integer
attribute :field_113, :string
attribute :field_114, :integer
attribute :field_115
attribute :field_116, :integer
@ -142,17 +143,38 @@ class BulkUpload::Lettings::RowParser
validates :field_1, presence: { message: I18n.t("validations.not_answered", question: "letting type") },
inclusion: { in: (1..12).to_a, message: I18n.t("validations.invalid_option", question: "letting type") }
validates :field_4, presence: { if: proc { [2, 4, 6, 8, 10, 12].include?(field_1) } }
validates :field_98, format: { with: /\A\d{2}\z/, message: I18n.t("validations.setup.startdate.year_not_two_digits") }
validate :validate_data_types
validate :validate_nulls
validate :validate_relevant_collection_window
validate :validate_la_with_local_housing_referral
validate :validate_cannot_be_la_referral_if_general_needs
validate :leaving_reason_for_renewal
validate :validate_cannot_be_la_referral_if_general_needs_and_la
validate :validate_leaving_reason_for_renewal
validate :validate_lettings_type_matches_bulk_upload
validate :validate_only_one_housing_needs_type
validate :validate_no_disabled_needs_conjunction
validate :validate_dont_know_disabled_needs_conjunction
validate :validate_no_and_dont_know_disabled_needs_conjunction
validate :validate_owning_org_permitted
validate :validate_owning_org_owns_stock
validate :validate_owning_org_exists
validate :validate_managing_org_related
validate :validate_managing_org_exists
validate :validate_scheme_related
validate :validate_scheme_exists
validate :validate_location_related
validate :validate_location_exists
def valid?
errors.clear
return true if blank_row?
super
log.valid?
@ -165,14 +187,139 @@ class BulkUpload::Lettings::RowParser
errors.blank?
end
def blank_row?
attribute_set.to_hash.reject { |k, _| %w[bulk_upload block_log_creation].include?(k) }.values.compact.empty?
end
def log
@log ||= LettingsLog.new(attributes_for_log)
end
def block_log_creation!
self.block_log_creation = true
end
def block_log_creation?
block_log_creation
end
private
def validate_cannot_be_la_referral_if_general_needs
if field_78 == 4 && bulk_upload.general_needs?
def validate_location_related
return if scheme.blank? || location.blank?
unless location.scheme == scheme
block_log_creation!
errors.add(:field_5, "Scheme code must relate to a location that is owned by owning organisation or managing organisation")
end
end
def location
return if scheme.nil?
@location ||= scheme.locations.find_by_id_on_mulitple_fields(field_5)
end
def validate_location_exists
if scheme && field_5.present? && location.nil?
errors.add(:field_5, "Location could be found with provided scheme code")
end
end
def validate_scheme_related
return unless field_4.present? && scheme.present?
owned_by_owning_org = owning_organisation && scheme.owning_organisation == owning_organisation
owned_by_managing_org = managing_organisation && scheme.owning_organisation == managing_organisation
unless owned_by_owning_org || owned_by_managing_org
block_log_creation!
errors.add(:field_4, "This management group code does not belong to your organisation, or any of your stock owners / managing agents")
end
end
def validate_scheme_exists
if field_4.present? && scheme.nil?
errors.add(:field_4, "The management group code is not correct")
end
end
def validate_managing_org_related
if owning_organisation && managing_organisation && !owning_organisation.can_be_managed_by?(organisation: managing_organisation)
block_log_creation!
errors.add(:field_113, "This managing organisation does not have a relationship with the owning organisation")
end
end
def validate_managing_org_exists
if managing_organisation.nil?
errors.delete(:field_113)
errors.add(:field_113, "The managing organisation code is incorrect")
end
end
def validate_owning_org_owns_stock
if owning_organisation && !owning_organisation.holds_own_stock?
block_log_creation!
errors.delete(:field_111)
errors.add(:field_111, "The owning organisation code provided is for an organisation that does not own stock")
end
end
def validate_owning_org_exists
if owning_organisation.nil?
errors.delete(:field_111)
errors.add(:field_111, "The owning organisation code is incorrect")
end
end
def validate_owning_org_permitted
if owning_organisation && !bulk_upload.user.organisation.affiliated_stock_owners.include?(owning_organisation)
block_log_creation!
errors.delete(:field_111)
errors.add(:field_111, "You do not have permission to add logs for this owning organisation")
end
end
def validate_no_and_dont_know_disabled_needs_conjunction
if field_59 == 1 && field_60 == 1
errors.add(:field_59, I18n.t("validations.household.housingneeds.no_and_dont_know_disabled_needs_conjunction"))
errors.add(:field_60, I18n.t("validations.household.housingneeds.no_and_dont_know_disabled_needs_conjunction"))
end
end
def validate_dont_know_disabled_needs_conjunction
if field_60 == 1 && [field_55, field_56, field_57, field_58].compact.count.positive?
errors.add(:field_60, I18n.t("validations.household.housingneeds.dont_know_disabled_needs_conjunction"))
end
end
def validate_no_disabled_needs_conjunction
if field_59 == 1 && [field_55, field_56, field_57, field_58].compact.count.positive?
errors.add(:field_59, I18n.t("validations.household.housingneeds.no_disabled_needs_conjunction"))
end
end
def validate_only_one_housing_needs_type
if [field_55, field_56, field_57].compact.count.positive?
errors.add(:field_55, I18n.t("validations.household.housingneeds_type.only_one_option_permitted"))
errors.add(:field_56, I18n.t("validations.household.housingneeds_type.only_one_option_permitted"))
errors.add(:field_57, I18n.t("validations.household.housingneeds_type.only_one_option_permitted"))
end
end
def validate_lettings_type_matches_bulk_upload
if [1, 3, 5, 7, 9, 11].include?(field_1) && !bulk_upload.general_needs?
errors.add(:field_1, I18n.t("validations.setup.lettype.supported_housing_mismatch"))
end
if [2, 4, 6, 8, 10, 12].include?(field_1) && !bulk_upload.supported_housing?
errors.add(:field_1, I18n.t("validations.setup.lettype.general_needs_mismatch"))
end
end
def validate_cannot_be_la_referral_if_general_needs_and_la
if field_78 == 4 && bulk_upload.general_needs? && owning_organisation && owning_organisation.la?
errors.add :field_78, I18n.t("validations.household.referral.la_general_needs.prp_referred_by_la")
end
end
@ -183,7 +330,7 @@ private
end
end
def leaving_reason_for_renewal
def validate_leaving_reason_for_renewal
if field_134 == 1 && ![40, 42].include?(field_52)
errors.add(:field_52, I18n.t("validations.household.reason.renewal_reason_needed"))
end
@ -399,6 +546,8 @@ private
def startdate
Date.new(field_98 + 2000, field_97, field_96) if field_98.present? && field_97.present? && field_96.present?
rescue Date::Error
Date.new
end
def renttype
@ -435,15 +584,19 @@ private
end
def owning_organisation
Organisation.find_by(old_visible_id: field_111)
Organisation.find_by_id_on_mulitple_fields(field_111)
end
def owning_organisation_id
owning_organisation&.id
end
def managing_organisation
Organisation.find_by_id_on_mulitple_fields(field_113)
end
def managing_organisation_id
Organisation.find_by(old_visible_id: field_113)&.id
managing_organisation&.id
end
def attributes_for_log
@ -458,6 +611,7 @@ private
attributes["managing_organisation_id"] = managing_organisation_id
attributes["renewal"] = renewal
attributes["scheme"] = scheme
attributes["location"] = location
attributes["created_by"] = bulk_upload.user
attributes["needstype"] = bulk_upload.needstype
attributes["rent_type"] = rent_type
@ -535,6 +689,8 @@ private
attributes["preg_occ"] = field_47
attributes["housingneeds"] = housingneeds
attributes["housingneeds_type"] = housingneeds_type
attributes["housingneeds_other"] = housingneeds_other
attributes["illness"] = field_118
@ -791,7 +947,7 @@ private
def housingneeds
if field_59 == 1
1
2
elsif field_60 == 1
3
else
@ -799,6 +955,20 @@ private
end
end
def housingneeds_type
if field_55 == 1
0
elsif field_56 == 1
1
elsif field_57 == 1
2
end
end
def housingneeds_other
return 1 if field_58 == 1
end
def ethnic_group_from_ethnic
return nil if field_43.blank?
@ -819,6 +989,6 @@ private
end
def scheme
@scheme ||= Scheme.find_by(old_visible_id: field_4)
@scheme ||= Scheme.find_by_id_on_mulitple_fields(field_4)
end
end

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

@ -176,6 +176,7 @@ class BulkUpload::Lettings::Validator
def create_logs?
return false if any_setup_sections_incomplete?
return false if over_column_error_threshold?
return false if row_parsers.any?(&:block_log_creation?)
row_parsers.all? { |row_parser| row_parser.log.valid? }
end

5
app/services/bulk_upload/processor.rb

@ -13,6 +13,7 @@ class BulkUpload::Processor
validator.call
create_logs if validator.create_logs?
send_correct_and_upload_again_mail unless validator.create_logs?
send_fix_errors_mail if created_logs_but_incompleted?
send_success_mail if created_logs_and_all_completed?
@ -25,6 +26,10 @@ class BulkUpload::Processor
private
def send_correct_and_upload_again_mail
BulkUploadMailer.send_correct_and_upload_again_mail(bulk_upload:).deliver_later
end
def send_fix_errors_mail
BulkUploadMailer.send_bulk_upload_with_errors_mail(bulk_upload:).deliver_later
end

9
app/services/exports/lettings_log_export_constants.rb

@ -7,13 +7,6 @@ module Exports::LettingsLogExportConstants
csv: 2,
}.freeze
QUARTERS = {
0 => "jan_mar",
1 => "apr_jun",
2 => "jul_sep",
3 => "oct_dec",
}.freeze
EXPORT_FIELDS = Set[
"armedforces",
"beds",
@ -30,7 +23,7 @@ module Exports::LettingsLogExportConstants
"confidential",
"earnings",
"ethnic",
"form",
"formid",
"has_benefits",
"hb",
"hbrentshortfall",

11
app/services/exports/lettings_log_export_service.rb

@ -1,6 +1,7 @@
module Exports
class LettingsLogExportService
include Exports::LettingsLogExportConstants
include CollectionTimeHelper
def initialize(storage_service, logger = Rails.logger)
@storage_service = storage_service
@ -66,11 +67,11 @@ module Exports
return unless lettings_log.startdate
collection_start = lettings_log.collection_start_year
month = lettings_log.startdate.month
quarter = QUARTERS[(month - 1) / 3]
start_month = collection_start_date(lettings_log.startdate).strftime("%b")
end_month = collection_end_date(lettings_log.startdate).strftime("%b")
base_number_str = "f#{base_number.to_s.rjust(4, '0')}"
increment_str = "inc#{increment.to_s.rjust(4, '0')}"
"core_#{collection_start}_#{collection_start + 1}_#{quarter}_#{base_number_str}_#{increment_str}"
"core_#{collection_start}_#{collection_start + 1}_#{start_month}_#{end_month}_#{base_number_str}_#{increment_str}".downcase
end
def write_export_archive(export, lettings_logs)
@ -152,7 +153,7 @@ module Exports
def apply_cds_transformation(lettings_log, export_mode)
attribute_hash = lettings_log.attributes_before_type_cast
attribute_hash["form"] = attribute_hash["old_form_id"] || (attribute_hash["id"] + LOG_ID_OFFSET)
attribute_hash["formid"] = attribute_hash["old_form_id"] || (attribute_hash["id"] + LOG_ID_OFFSET)
# We can't have a variable number of columns in CSV
unless export_mode == EXPORT_MODE[:csv]
@ -214,7 +215,7 @@ module Exports
attribute_hash["reghome"] = scheme.registered_under_care_act_before_type_cast
attribute_hash["schtype"] = scheme.scheme_type_before_type_cast
attribute_hash["support"] = scheme.support_type_before_type_cast
attribute_hash["units_scheme"] = scheme.locations.map(&:units).sum
attribute_hash["units_scheme"] = scheme.locations.map(&:units).compact.sum
end
def add_location_fields!(location, attribute_hash)

45
app/views/content/privacy_notice.md

@ -1,46 +1,47 @@
## How are we using your information?
## How do we use your information?
If your household has entered a new social housing tenancy, social housing providers will share your personal information with the Department for Levelling Up, Housing & Communities (DLUHC) for research and statistical purposes.
If your household enters a new social housing tenancy or purchases a social housing property, social housing providers will share your personal information with the Department for Levelling Up, Housing & Communities (DLUHC) for research and statistical purposes only.
## How is this information provided?
## How do we get this information?
The information is provided via ‘<%= t('service_name') %>’, a service funded and managed by DLUHC. It collects information on the tenants or buyers, tenancy or sale, and the dwelling itself. Some of this information is personal and sensitive, so DLUHC is responsible for ensuring that all data is processed in line with data protection legislation.
The information is provided via ‘<%= t('service_name') %>’, a service funded and managed by DLUHC. It collects information on the tenants or residents, tenancy or sale, and the dwelling itself. Some of this data is personal and sensitive, so DLUHC is responsible for ensuring it’s processed in line with data protection legislation.
## Why are we sharing this information?
Information collected using this service is shared with other government departments and agencies. Data is shared with the Greater London Authority and the Regulator of Social Housing. Data providers can also access data for their organisations via the online service. Data is only shared for research and statistical purposes.
## Why do we share this information?
Information collected via CORE is shared with other government departments and agencies. It’s shared with the Greater London Authority and the Regulator of Social Housing. Data providers can also access data for their organisations via CORE. Data is only shared for research and statistical purposes.
## How does this affect you?
It will not affect your benefits, services or any treatments you receive. The information shared is anonymous and handled in accordance with the law. We are collecting and sharing your information to help us better understand the social housing market and inform social housing policy.
Information sharing will not affect your benefits, services or any treatments you receive. It’s anonymous and handled in accordance with the law. We collect and share your information to help us better understand the social housing market and inform social housing policy.
## If you want to know more
## To find out more…
Social housing lettings and sales data is collected on behalf of DLUHC for research and statistical purposes only. Data providers do not require the consent of tenants to provide the information, but tenants have the right to know how and for what purpose data is being collected, held and used.
Social housing lettings and sales data is collected on DLUHC’s behalf. Data providers do not require the tenant or buyer’s consent to provide this information, but tenants and buyers have the right to know how and for what purpose data is being collected, held and used.
The processing must have a lawful basis. In this case the processing is necessary for the performance of a task carried out in the public interest to meet a function of the Crown, a Minister of the Crown, or a government department.
Data processing must have a lawful basis. In this case it’s necessary for a task carried out in the public interest meeting a function of the Crown, a Minister of the Crown, or government department.
You have the right to object and you have the right to obtain confirmation that your data is being processed, and to access your personal data. You also have the right to have any incorrect personal data corrected.
You have the right to object, and obtain confirmation that your data is being processed, as well as access your personal data, and have any incorrect personal data corrected.
The information collected via this service relates to your tenancy, the dwelling you are living in or buying, and your household. Some of the information may have been provided by you as a tenant when signing the new tenancy or buying your property. Other information has been gathered from the housing management systems of social housing providers.
Information collected via CORE relates to your tenancy, the dwelling you are living in or buying, and your household. Some information may have been provided by you (as a tenant or buyer) when signing the new tenancy or buying your property. Other information has been gathered from the housing management systems of social housing providers.
Data collected will be held for as long as necessary for research and statistical purposes. When no longer needed, data will be deleted in a safe manner. We are aware that some of the data collected is particularly sensitive. For example:
Collected data will be held for as long as necessary for research and statistical purposes. When no longer needed, data will be deleted in a safe manner. We’re aware some collected data is particularly sensitive. For example:
* ethnic group
* if previous tenure is a hospital or prison or approved probation hostel support
* if previous tenure is a hospital, prison or approved probation hostel support
* if household left last settled home because discharged from prison, a long stay hospital or other institution
* if source of referral is probation or prison, youth offending team, community mental health team or health service
* if referral source is probation or prison, youth offending or community mental health team, or health service
All the information collected via this service is treated in accordance with data protection requirements and guidelines.
DLUHC publishes data annually, in aggregate form, as part of a report and complementary tables.
Data is published by DLUHC in aggregate form on an annual basis as part of a report and complementary tables.
* For annual lettings data, visit: [www.gov.uk/government/collections/rents-lettings-and-tenancies](www.gov.uk/government/collections/rents-lettings-and-tenancies)
You can visit <www.gov.uk/government/collections/rents-lettings-and-tenancies> to access the annual publications on lettings. Or visit <gov.uk/government/collections/social-housing-sales-including-right-to-buy-and-transfers> to view the publications on sales.
* For annual sales data, visit: [www.gov.uk/government/collections/social-housing-sales-including-right-to-buy-and-transfers](www.gov.uk/government/collections/social-housing-sales-including-right-to-buy-and-transfers)
The detail level data is anonymised and protected to minimise the risk of identification and held with the UK Data Archive for research purposes.
Detail-level data is anonymised and protected, minimising identification risk. It's held with the UK Data Archive.
## Making a complaint
## Complaints
If you are unhappy with any aspect of this privacy notice, or how your personal information is being processed, contact the Department Data Protection Officer at: <dataprotection@communities.gsi.gov.uk>
If you’re unhappy with any privacy notice aspect, or how we process your information, contact the Department Data Protection Officer: <dataprotection@communities.gsi.gov.uk>
If you are still not happy, you have the right to lodge a complaint with the Information Commissioner’s Office (ICO) at [ico.org.uk/concern](https://ico.org.uk/concern).
You also have the right to complain to the Information Commissioner’s Office (ICO): [www.ico.org.uk/concern](www.ico.org.uk/concern)

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

@ -1,9 +1,18 @@
<% if @log.sales? %>
<% content_for :title, "Review sales log" %>
<% content_for :breadcrumbs, govuk_breadcrumbs(breadcrumbs: {
"Logs" => "/logs",
"Log #{@log.id}" => "/sales-logs/#{@log.id}",
"Review sales log" => "",
}) %>
<% else %>
<% content_for :title, "Review lettings log" %>
<% content_for :breadcrumbs, govuk_breadcrumbs(breadcrumbs: {
"Logs" => "/logs",
"Log #{@log.id}" => "/lettings-logs/#{@log.id}",
"Review lettings log" => "",
}) %>
<% end %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
@ -16,18 +25,17 @@
<% @log.form.sections.map do |section| %>
<h2 class="govuk-heading-m"><%= section.label %></h2>
<% section.subsections.map do |subsection| %>
<% if total_applicable_questions(subsection, @log, current_user).any? %>
<div class="x-govuk-summary-card govuk-!-margin-bottom-6">
<div class="x-govuk-summary-card__header">
<h3 class="x-govuk-summary-card__title"><%= subsection.label %></h3>
</div>
<div class="x-govuk-summary-card__body">
<%= render partial: "form/check_answers_summary_list", locals: {
subsection:,
lettings_log: @log,
} %>
<%= render partial: "form/check_answers_summary_list", locals: { subsection: } %>
</div>
</div>
<% end %>
<% end %>
<% end %>
</div>
</div>

3
app/views/logs/_log_filters.erb

@ -6,7 +6,6 @@
<div class="app-filter__content">
<%= form_with html: { method: :get } do |f| %>
<% years = { "2021": "2021/22", "2022": "2022/23" } %>
<% all_or_yours = { "all": { label: "All" }, "yours": { label: "Yours" } } %>
<% if bulk_upload_options(@bulk_upload).present? %>
@ -23,7 +22,7 @@
<%= render partial: "filters/checkbox_filter",
locals: {
f: f,
options: years,
options: collection_year_options,
label: "Collection year",
category: "years",
} %>

2
config/credentials.yml.enc

@ -1 +1 @@
PWC9A1AmalyFv4A63MG9jCm2YtwE1Eyu1BJKodtbmq2nW7FD9svZZ3V4yC0JO9J8sLfCS6UrxQC4TOKKYWP5iYpOr5TKPi9MqNCVueLsoErN+nMAPhCJVpl8+eJ4BOvNYGlmle4bcwbAvHLq6IoANTBjktFh5/tgJS+IHBWK1FwPzK1eAhgQ1fE1jluWslee4iYesmh+ufHIMZAkoWuGJVky4i4uN5nKnwQfaPN5MUxBlvGiDH+s+yex4pcIaJ6hxYWAQRZXJRQVpZcf/agwU0Tk/S/fuDMm8zVeHCQkkmsCYzH5czB8b8IWYwyslqFBZNCix7YnbgwgYk8MUh7wPBuF8CFoPKVyteqic9HgUp9KY8kkt/RcWJ4zpv+Vwz3cre+iZ3S1bxFcSxXqO0MGRug2H9iwhAnBQDLl3vXLNNRYEL5LhNv0Z9Cy7at1fnYe1FcvF+3DR9kG/RAYzR8S2eEDdzBl797+DG81yhfkjP3/gfWxD+J+Mx0F4SDEOaGK5c/MqNTdiRRbnhzuRaQFMg6itoJbZybe+EOQScNft1QLqC4QwPd4Qevhj/A=--QffpsNB1u+1Lk/tD--spkCqBIGHl8g6HGwd89yCg==
EZNV2LiNWzf52erbQ41Dz3Bh+2f3Uih8liEyhXp5XzHCLzAbmN6/IJqr7b9cTZiCiroFo4n/dFoG3yYrospp3frKsDXxF1K2/MTCJWjpgnn7wc+HiPQWG0W3HRtQCNkyyrHes0YKcYyDWIP6kztYv1I/Me3p0pGEx6t3CpSTg1v46eRnOlDWiUz3rVxPauwq9IYZ75gmnThqvg/Z8wcYsWLx0arago0SXtRPASCNj4uO/lbqTcAfyIXOTSiOlcAIjoPFRSQY7UqY0o2p8jRR/1L16SmGDsk8ijm+UygNmMexa3Khy5WcKctpQICakHs4NRjHNflqgXpXKL9dVBmNc9d7h+gbhbGJQ53Y0d+a35UbhPRMiv4SRH98FwB+WEsLCDdGSHvdmM6ArfOLljTrqrsmSRf0JfUrvzyVYmMCxjv4xgJwUS/TD5lQD1yPwkp2ss00kQJqzNmB7qwFhA8a3e2iNzV8qtAV/Nj+tMlr99Hb7vZZs98/38G2p5RAsE/5Xl9taKhc/ACnVc/bwJND4JWaBB7duCa08xVB8nkjlt5cCwMurzAcy1ZT+e8JepR+g6s8fpScMEWVJXE0hd8=--rZ41rY9TMXmiBUJw--QiLRVNVXZzTW446s7cec1g==

2
config/forms/2022_2023.json

@ -1,7 +1,7 @@
{
"form_type": "lettings",
"start_date": "2022-04-01T00:00:00.000+01:00",
"end_date": "2023-07-01T00:00:00.000+01:00",
"end_date": "2023-07-09T00:00:00.000+01:00",
"unresolved_log_redirect_page_id": "tenancy_start_date",
"sections": {
"tenancy_and_property": {

17
config/initializers/feature_toggle.rb

@ -1,14 +1,15 @@
class FeatureToggle
def self.startdate_two_week_validation_enabled?
Rails.env.production? || Rails.env.test?
# Disable check on preview apps to allow for testing of future forms
def self.saledate_collection_window_validation_enabled?
Rails.env.production? || Rails.env.test? || Rails.env.staging?
end
def self.startdate_collection_window_validation_enabled?
Rails.env.production? || Rails.env.test?
def self.startdate_two_week_validation_enabled?
Rails.env.production? || Rails.env.test? || Rails.env.staging?
end
def self.saledate_collection_window_validation_enabled?
Rails.env.production? || Rails.env.test?
def self.startdate_collection_window_validation_enabled?
Rails.env.production? || Rails.env.test? || Rails.env.staging?
end
def self.sales_log_enabled?
@ -42,4 +43,8 @@ class FeatureToggle
def self.validate_valid_radio_options?
!(Rails.env.production? || Rails.env.staging?)
end
def self.collection_2023_2024_year_enabled?
!Rails.env.production?
end
end

31
config/locales/en.yml

@ -150,12 +150,17 @@ en:
intermediate_rent_product_name:
blank: "Enter name of other intermediate rent product"
saledate:
financial_year: "Date must be from 22/23 financial year, which is between 1st April 2022 and 31st March 2023"
current_financial_year:
Enter a date within the %{current_start_year_short}/%{current_end_year_short} financial year, which is between %{current_start_year_long} and %{current_end_year_long}
previous_and_current_financial_year:
"Enter a date within the %{previous_start_year_short}/%{previous_end_year_short} or %{previous_end_year_short}/%{current_end_year_short} financial years, which is between %{previous_start_year_long} and %{current_end_year_long}"
startdate:
later_than_14_days_after: "The tenancy start date must not be later than 14 days from today’s date"
before_scheme_end_date: "The tenancy start date must be before the end date for this supported housing scheme"
after_void_date: "Enter a tenancy start date that is after the void date"
after_major_repair_date: "Enter a tenancy start date that is after the major repair date"
year_not_two_digits: Tenancy start year must be 2 digits
location:
deactivated: "The location %{postcode} was deactivated on %{date} and was not available on the day you entered."
reactivating_soon: "The location %{postcode} is not available until %{date}. Select another location or edit the tenancy start date"
@ -170,6 +175,9 @@ en:
invalid: "Please select owning organisation or managing organisation that you belong to"
created_by:
invalid: "Please select owning organisation or managing organisation that you belong to"
lettype:
general_needs_mismatch: Lettings type must be a general needs type because you selected general needs when uploading the file
supported_housing_mismatch: Lettings type must be a supported housing type because you selected supported housing when uploading the file
property:
mrcdate:
@ -199,6 +207,9 @@ en:
beds:
non_positive: "Number of bedrooms has to be greater than 0"
over_max: "Number of bedrooms cannot be more than 12"
bedsits_have_max_one_bedroom: "Number of bedrooms must be 1 if the property is a bedsit"
proptype:
bedsits_have_max_one_bedroom: "Answer cannot be 'Bedsit' if the property has 2 or more bedrooms"
postcode:
must_match_previous: "Buyer's last accommodation and discounted ownership postcodes must match"
@ -216,8 +227,12 @@ en:
under_hard_min: "Net income cannot be less than £%{hard_min} per week given the tenant’s working situation"
freq_missing: "Select how often the household receives income"
earnings_missing: "Enter how much income the household has in total"
income1:
over_hard_max: "Income must be lower than £%{hard_max}"
income:
over_hard_max_for_london: "Income must not exceed £90,000 for properties within London local authorities"
over_hard_max_for_outside_london: "Income must not exceed £80,000 for properties outside London local authorities"
combined_over_hard_max_for_london: "Combined income must not exceed £90,000 for properties within London local authorities"
combined_over_hard_max_for_outside_london: "Combined income must not exceed £80,000 for properties outside London local authorities"
child_has_income: "Child's income must be £0"
negative_currency: "Enter an amount above 0"
rent:
less_than_shortfall: "Enter an amount that is more than the shortfall in basic rent"
@ -341,6 +356,12 @@ en:
must_be_child: "Answer must be ‘child’ if the person is aged 16-19 and a student"
housingneeds_a:
one_or_two_choices: "You can only select one option or ‘other disabled access needs’ plus ‘wheelchair-accessible housing’, ‘wheelchair access to essential rooms’ or ‘level access housing’"
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"
housingneeds:
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"
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_and_dont_know_disabled_needs_conjunction: "No disabled access needs and don’t know disabled access needs cannot be selected together"
prevten:
non_temp_accommodation: "Answer cannot be non-temporary accommodation as this is a re-let to a tenant who occupied the same property as temporary accommodation"
over_20_foster_care: "Answer cannot be a children’s home or foster care as the lead tenant is 20 or older"
@ -454,6 +475,8 @@ en:
message: "Net income is lower than expected based on the lead tenant’s working situation. Are you sure this is correct?"
in_soft_max_range:
message: "Net income is higher than expected based on the lead tenant’s working situation. Are you sure this is correct?"
income:
under_soft_min_for_economic_status: "You said income was %{income}, which is below this working situation's minimum (%{minimum})"
rent:
outside_range_title: "You told us the rent is %{brent}"
min_hint_text: "The minimum rent expected for this type of property in this local authority is £%{soft_min_for_period}."
@ -546,4 +569,4 @@ en:
one_argument: "This is based on the tenant’s work situation: %{ecstat1}"
title_text:
no_argument: "Some test text"
one_argument: "You said this: %{ecstat1}"
one_argument: "You said this: %{argument}"

4
config/routes.rb

@ -171,6 +171,10 @@ Rails.application.routes.draw do
resources :bulk_upload_sales_results, path: "bulk-upload-results", only: [:show]
end
member do
get "review", to: "form#review"
end
FormHandler.instance.sales_forms.each do |_key, form|
form.pages.map do |page|
get page.id.to_s.dasherize, to: "form#show_page"

1185
config/sale_range_data/2023.csv

File diff suppressed because it is too large Load Diff

5
db/migrate/20230105103134_add_income2_value_check.rb

@ -0,0 +1,5 @@
class AddIncome2ValueCheck < ActiveRecord::Migration[7.0]
def change
add_column :sales_logs, :income2_value_check, :integer
end
end

5
db/migrate/20230210122037_add_staircasesale_to_sales_logs.rb

@ -0,0 +1,5 @@
class AddStaircasesaleToSalesLogs < ActiveRecord::Migration[7.0]
def change
add_column :sales_logs, :staircasesale, :integer
end
end

8
db/migrate/20230210143120_add_ethnic_fields_for_buyer2.rb

@ -0,0 +1,8 @@
class AddEthnicFieldsForBuyer2 < ActiveRecord::Migration[7.0]
def change
change_table :sales_logs, bulk: true do |t|
t.column :ethnic_group2, :integer
t.column :ethnicbuy2, :integer
end
end
end

7
db/schema.rb

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_02_13_140932) do
ActiveRecord::Schema[7.0].define(version: 2023_02_10_143120) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -520,12 +520,15 @@ ActiveRecord::Schema[7.0].define(version: 2023_02_13_140932) do
t.integer "value_value_check"
t.integer "old_persons_shared_ownership_value_check"
t.integer "staircase_bought_value_check"
t.integer "income2_value_check"
t.integer "monthly_charges_value_check"
t.integer "details_known_5"
t.integer "details_known_6"
t.integer "saledate_check"
t.integer "prevshared"
t.integer "proplen_asked"
t.integer "staircasesale"
t.integer "ethnic_group2"
t.integer "ethnicbuy2"
t.index ["bulk_upload_id"], name: "index_sales_logs_on_bulk_upload_id"
t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id"
t.index ["owning_organisation_id"], name: "index_sales_logs_on_owning_organisation_id"

63
docs/Gemfile.lock

@ -1,12 +1,11 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (6.0.6)
activesupport (7.0.4.2)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
zeitwerk (~> 2.2, >= 2.2.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0)
coffee-script (2.4.1)
@ -14,30 +13,30 @@ GEM
execjs
coffee-script-source (1.11.1)
colorator (1.1.0)
commonmarker (0.23.6)
concurrent-ruby (1.1.10)
commonmarker (0.23.8)
concurrent-ruby (1.2.0)
dnsruby (1.61.9)
simpleidn (~> 0.1)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
ethon (0.15.0)
ethon (0.16.0)
ffi (>= 1.15.0)
eventmachine (1.2.7)
execjs (2.8.1)
faraday (2.5.2)
faraday (2.7.4)
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.0)
faraday-net_http (3.0.2)
ffi (1.15.5)
forwardable-extended (2.6.0)
gemoji (3.0.1)
github-pages (227)
github-pages (228)
github-pages-health-check (= 1.17.9)
jekyll (= 3.9.2)
jekyll (= 3.9.3)
jekyll-avatar (= 0.7.0)
jekyll-coffeescript (= 1.1.1)
jekyll-commonmark-ghpages (= 0.2.0)
jekyll-commonmark-ghpages (= 0.4.0)
jekyll-default-layout (= 0.1.4)
jekyll-feed (= 0.15.1)
jekyll-gist (= 1.5.0)
@ -71,7 +70,7 @@ GEM
jemoji (= 0.12.0)
kramdown (= 2.3.2)
kramdown-parser-gfm (= 1.1.0)
liquid (= 4.0.3)
liquid (= 4.0.4)
mercenary (~> 0.3)
minima (= 2.5.1)
nokogiri (>= 1.13.6, < 2.0)
@ -83,17 +82,17 @@ GEM
octokit (~> 4.0)
public_suffix (>= 3.0, < 5.0)
typhoeus (~> 1.3)
html-pipeline (2.14.2)
html-pipeline (2.14.3)
activesupport (>= 2)
nokogiri (>= 1.4)
http_parser.rb (0.8.0)
i18n (0.9.5)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
jekyll (3.9.2)
jekyll (3.9.3)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (~> 0.7)
i18n (>= 0.7, < 2)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 2.0)
kramdown (>= 1.17, < 3)
@ -109,11 +108,11 @@ GEM
coffee-script-source (~> 1.11.1)
jekyll-commonmark (1.4.0)
commonmarker (~> 0.22)
jekyll-commonmark-ghpages (0.2.0)
commonmarker (~> 0.23.4)
jekyll-commonmark-ghpages (0.4.0)
commonmarker (~> 0.23.7)
jekyll (~> 3.9.0)
jekyll-commonmark (~> 1.4.0)
rouge (>= 2.0, < 4.0)
rouge (>= 2.0, < 5.0)
jekyll-default-layout (0.1.4)
jekyll (~> 3.0)
jekyll-feed (0.15.1)
@ -201,8 +200,8 @@ GEM
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
liquid (4.0.3)
listen (3.7.1)
liquid (4.0.4)
listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.3.6)
@ -210,12 +209,12 @@ GEM
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.16.3)
nokogiri (1.13.10-arm64-darwin)
minitest (5.17.0)
nokogiri (1.14.2-arm64-darwin)
racc (~> 1.4)
nokogiri (1.13.10-x86_64-darwin)
nokogiri (1.14.2-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.13.10-x86_64-linux)
nokogiri (1.14.2-x86_64-linux)
racc (~> 1.4)
octokit (4.25.1)
faraday (>= 1, < 3)
@ -223,7 +222,7 @@ GEM
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (4.0.7)
racc (1.6.1)
racc (1.6.2)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
@ -244,17 +243,15 @@ GEM
unf (~> 0.1.4)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thread_safe (0.3.6)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (1.2.10)
thread_safe (~> 0.1)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2)
unicode-display_width (1.8.0)
webrick (1.7.0)
zeitwerk (2.6.0)
webrick (1.8.1)
PLATFORMS
arm64-darwin-21

18
lib/tasks/data_export.rake

@ -11,21 +11,3 @@ namespace :core do
DataExportXmlJob.perform_later(full_update:)
end
end
namespace :illness_type_0 do
desc "Export log data where illness_type_0 == 1"
task export: :environment do |_task|
logs = LettingsLog.where(illness_type_0: 1, status: "completed").includes(created_by: :organisation)
puts "log_id,created_by_id,organisation_id,organisation_name,startdate"
logs.each do |log|
puts [
log.id,
log.created_by_id,
log.created_by.organisation.id,
log.created_by.organisation.name,
log.startdate&.strftime("%d/%m/%Y"),
].join(",")
end
end
end

5
spec/factories/location.rb

@ -10,6 +10,7 @@ FactoryBot.define do
startdate { Time.zone.local(2022, 4, 1) }
confirmed { true }
scheme
trait :export do
postcode { "SW1A 2AA" }
name { "Downing Street" }
@ -19,5 +20,9 @@ FactoryBot.define do
scheme { FactoryBot.create(:scheme, :export) }
old_visible_id { "111" }
end
trait :with_old_visible_id do
old_visible_id { rand(9_999_999).to_s }
end
end
end

8
spec/factories/organisation.rb

@ -13,6 +13,14 @@ FactoryBot.define do
trait :with_old_visible_id do
old_visible_id { rand(9_999_999).to_s }
end
trait :prp do
provider_type { "PRP" }
end
trait :does_not_own_stock do
holds_own_stock { false }
end
end
factory :organisation_rent_period do

4
spec/factories/scheme.rb

@ -21,5 +21,9 @@ FactoryBot.define do
primary_client_group { "G" }
secondary_client_group { "M" }
end
trait :with_old_visible_id do
old_visible_id { rand(9_999_999) }
end
end
end

2
spec/fixtures/exports/general_needs_log.csv vendored

@ -1,2 +1,2 @@
status,tenancycode,age1,sex1,ethnic,national,prevten,ecstat1,hhmemb,age2,sex2,ecstat2,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,leftreg,reservist,illness,preg_occ,startertenancy,tenancylength,tenancy,ppostcode_full,rsnvac,unittype_gn,beds,offered,wchair,earnings,incfreq,benefits,period,layear,waityear,postcode_full,reasonpref,cbl,chr,cap,reasonother,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,illness_type_1,illness_type_2,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,irproduct_other,reason,propcode,la,prevloc,hb,hbrentshortfall,mrcdate,incref,startdate,armedforces,unitletas,builtype,voiddate,renttype,needstype,lettype,totchild,totelder,totadult,nocharge,referral,brent,scharge,pscharge,supcharg,tcharge,tshortfall,chcharge,ppcodenk,has_benefits,renewal,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat2,relat3,relat4,relat5,relat6,relat7,relat8,lar,irproduct,joint,sheltered,hhtype,new_old,vacdays,form,owningorgid,owningorgname,hcnum,maningorgid,maningorgname,manhcnum,createddate,uploaddate
status,tenancycode,age1,sex1,ethnic,national,prevten,ecstat1,hhmemb,age2,sex2,ecstat2,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,leftreg,reservist,illness,preg_occ,startertenancy,tenancylength,tenancy,ppostcode_full,rsnvac,unittype_gn,beds,offered,wchair,earnings,incfreq,benefits,period,layear,waityear,postcode_full,reasonpref,cbl,chr,cap,reasonother,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,illness_type_1,illness_type_2,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,irproduct_other,reason,propcode,la,prevloc,hb,hbrentshortfall,mrcdate,incref,startdate,armedforces,unitletas,builtype,voiddate,renttype,needstype,lettype,totchild,totelder,totadult,nocharge,referral,brent,scharge,pscharge,supcharg,tcharge,tshortfall,chcharge,ppcodenk,has_benefits,renewal,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat2,relat3,relat4,relat5,relat6,relat7,relat8,lar,irproduct,joint,sheltered,hhtype,new_old,vacdays,formid,owningorgid,owningorgname,hcnum,maningorgid,maningorgname,manhcnum,createddate,uploaddate
2,BZ737,35,F,2,4,6,0,2,32,M,6,,,,,,,,,,,,,,,,,,,1,4,1,1,1,2,1,5,1,SE2 6RT,6,7,3,2,1,68,1,1,2,2,1,NW1 5TY,1,2,1,2,,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,,,4,123,E09000003,E07000105,6,1,2020-05-05T10:36:49+01:00,0,2022-02-02T10:36:49+00:00,1,2,1,2019-11-03T00:00:00+00:00,2,1,7,0,0,2,0,2,200.0,50.0,40.0,35.0,325.0,12.0,,1,1,0,100.0,25.0,20.0,17.5,162.5,6.0,0,1,,2,P,,,,,,,,,,,4,2,638,{id},{owning_org_id},DLUHC,1234,{managing_org_id},DLUHC,1234,2022-02-08T16:52:15+00:00,2022-02-08T16:52:15+00:00

1 status tenancycode age1 sex1 ethnic national prevten ecstat1 hhmemb age2 sex2 ecstat2 age3 sex3 ecstat3 age4 sex4 ecstat4 age5 sex5 ecstat5 age6 sex6 ecstat6 age7 sex7 ecstat7 age8 sex8 ecstat8 homeless underoccupation_benefitcap leftreg reservist illness preg_occ startertenancy tenancylength tenancy ppostcode_full rsnvac unittype_gn beds offered wchair earnings incfreq benefits period layear waityear postcode_full reasonpref cbl chr cap reasonother housingneeds_a housingneeds_b housingneeds_c housingneeds_f housingneeds_g housingneeds_h illness_type_1 illness_type_2 illness_type_3 illness_type_4 illness_type_8 illness_type_5 illness_type_6 illness_type_7 illness_type_9 illness_type_10 rp_homeless rp_insan_unsat rp_medwel rp_hardship rp_dontknow tenancyother irproduct_other reason propcode la prevloc hb hbrentshortfall mrcdate incref startdate armedforces unitletas builtype voiddate renttype needstype lettype totchild totelder totadult nocharge referral brent scharge pscharge supcharg tcharge tshortfall chcharge ppcodenk has_benefits renewal wrent wscharge wpschrge wsupchrg wtcharge wtshortfall refused housingneeds wchchrg newprop relat2 relat3 relat4 relat5 relat6 relat7 relat8 lar irproduct joint sheltered hhtype new_old vacdays form formid owningorgid owningorgname hcnum maningorgid maningorgname manhcnum createddate uploaddate
2 2 BZ737 35 F 2 4 6 0 2 32 M 6 1 4 1 1 1 2 1 5 1 SE2 6RT 6 7 3 2 1 68 1 1 2 2 1 NW1 5TY 1 2 1 2 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 4 123 E09000003 E07000105 6 1 2020-05-05T10:36:49+01:00 0 2022-02-02T10:36:49+00:00 1 2 1 2019-11-03T00:00:00+00:00 2 1 7 0 0 2 0 2 200.0 50.0 40.0 35.0 325.0 12.0 1 1 0 100.0 25.0 20.0 17.5 162.5 6.0 0 1 2 P 4 2 638 {id} {id} {owning_org_id} DLUHC 1234 {managing_org_id} DLUHC 1234 2022-02-08T16:52:15+00:00 2022-02-08T16:52:15+00:00

2
spec/fixtures/exports/general_needs_log.xml vendored

@ -135,7 +135,7 @@
<hhtype>4</hhtype>
<new_old>2</new_old>
<vacdays>638</vacdays>
<form>{id}</form>
<formid>{id}</formid>
<owningorgid>{owning_org_id}</owningorgid>
<owningorgname>DLUHC</owningorgname>
<hcnum>1234</hcnum>

2
spec/fixtures/exports/supported_housing_logs.xml vendored

@ -134,7 +134,7 @@
<hhtype>4</hhtype>
<new_old>2</new_old>
<vacdays>638</vacdays>
<form>{id}</form>
<formid>{id}</formid>
<owningorgid>{owning_org_id}</owningorgid>
<owningorgname>DLUHC</owningorgname>
<hcnum>1234</hcnum>

46
spec/helpers/collection_time_helper_spec.rb

@ -21,6 +21,10 @@ RSpec.describe CollectionTimeHelper do
it "returns the correct current start date" do
expect(current_collection_start_date).to eq(Time.zone.local(2022, 4, 1))
end
it "returns the correct current end date" do
expect(current_collection_end_date).to eq(Time.zone.local(2023, 3, 31))
end
end
context "with the date before 1st of April" do
@ -29,6 +33,48 @@ RSpec.describe CollectionTimeHelper do
it "returns the previous year as the current start year" do
expect(current_collection_start_year).to eq(2021)
end
it "returns the correct current start date" do
expect(current_collection_start_date).to eq(Time.zone.local(2021, 4, 1))
end
it "returns the correct current end date" do
expect(current_collection_end_date).to eq(Time.zone.local(2022, 3, 31))
end
end
end
describe "Any collection year" do
context "when the date is after 1st of April" do
let(:now) { Time.utc(2022, 8, 3) }
it "returns the same year as the current start year" do
expect(collection_start_year(now)).to eq(2022)
end
it "returns the correct current start date" do
expect(collection_start_date(now)).to eq(Time.zone.local(2022, 4, 1))
end
it "returns the correct current end date" do
expect(collection_end_date(now)).to eq(Time.zone.local(2023, 3, 31))
end
end
context "with the date before 1st of April" do
let(:now) { Time.utc(2022, 2, 3) }
it "returns the previous year as the current start year" do
expect(collection_start_year(now)).to eq(2021)
end
it "returns the correct current start date" do
expect(collection_start_date(now)).to eq(Time.zone.local(2021, 4, 1))
end
it "returns the correct current end date" do
expect(collection_end_date(now)).to eq(Time.zone.local(2022, 3, 31))
end
end
end
end

26
spec/helpers/filters_helper_spec.rb

@ -116,4 +116,30 @@ RSpec.describe FiltersHelper do
end
end
end
describe "#collection_year_options" do
context "when not production" do
it "includes 2023/2024 option" do
expect(collection_year_options).to eq(
{
"2021": "2021/22", "2022": "2022/23", "2023": "2023/24"
},
)
end
end
context "when production" do
before do
allow(Rails.env).to receive(:production?).and_return(true)
end
it "includes 2023/2024 option" do
expect(collection_year_options).to eq(
{
"2021": "2021/22", "2022": "2022/23"
},
)
end
end
end
end

53
spec/helpers/interruption_screen_helper_spec.rb

@ -13,6 +13,7 @@ RSpec.describe InterruptionScreenHelper do
earnings: 750,
incfreq: 1,
created_by: user,
sex1: "F",
)
end
@ -94,6 +95,54 @@ RSpec.describe InterruptionScreenHelper do
.to eq("")
end
end
context "when an argument is given not for a label" do
translation = "test.title_text.one_argument"
it "returns the correct text" do
informative_text_hash = {
"translation" => translation,
"arguments" => [
{
"key" => "earnings",
"i18n_template" => "argument",
},
],
}
expect(display_informative_text(informative_text_hash, lettings_log)).to eq(I18n.t(translation, argument: lettings_log.earnings))
end
end
context "when and argument is given with a key and arguments for the key" do
it "makes the correct method call" do
informative_text_hash = {
"arguments" => [
{
"key" => "retirement_age_for_person",
"arguments_for_key" => 1,
"i18n_template" => "argument",
},
],
}
allow(lettings_log).to receive(:retirement_age_for_person)
display_informative_text(informative_text_hash, lettings_log)
expect(lettings_log).to have_received(:retirement_age_for_person).with(1)
end
it "returns the correct text" do
translation = "test.title_text.one_argument"
informative_text_hash = {
"translation" => translation,
"arguments" => [
{
"key" => "retirement_age_for_person",
"arguments_for_key" => 1,
"i18n_template" => "argument",
},
],
}
expect(display_informative_text(informative_text_hash, lettings_log)).to eq(I18n.t(translation, argument: lettings_log.retirement_age_for_person(1)))
end
end
end
describe "display_title_text" do
@ -113,12 +162,12 @@ RSpec.describe InterruptionScreenHelper do
{
"key" => "ecstat1",
"label" => true,
"i18n_template" => "ecstat1",
"i18n_template" => "argument",
},
],
}
expect(display_title_text(title_text, lettings_log))
.to eq(I18n.t("test.title_text.one_argument", ecstat1: lettings_log.form.get_question("ecstat1", lettings_log).answer_label(lettings_log).downcase))
.to eq(I18n.t("test.title_text.one_argument", argument: lettings_log.form.get_question("ecstat1", lettings_log).answer_label(lettings_log).downcase))
end
end

113
spec/helpers/tasklist_helper_spec.rb

@ -1,15 +1,20 @@
require "rails_helper"
RSpec.describe TasklistHelper do
describe "with lettings" do
let(:empty_lettings_log) { FactoryBot.create(:lettings_log) }
let(:lettings_log) { FactoryBot.create(:lettings_log, :in_progress, needstype: 1) }
let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") }
let(:now) { Time.utc(2022, 6, 1) }
context "with 2021 2022 form" do
before do
allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form)
around do |example|
Timecop.freeze(now) do
Singleton.__init__(FormHandler)
example.run
end
Timecop.return
Singleton.__init__(FormHandler)
end
describe "with lettings" do
let(:empty_lettings_log) { create(:lettings_log) }
let(:lettings_log) { build(:lettings_log, :in_progress, needstype: 1) }
describe "get next incomplete section" do
it "returns the first subsection name if it is not completed" do
@ -24,7 +29,7 @@ RSpec.describe TasklistHelper do
describe "get sections count" do
it "returns the total of sections if no status is given" do
expect(get_subsections_count(empty_lettings_log)).to eq(8)
expect(get_subsections_count(empty_lettings_log)).to eq(1)
end
it "returns 0 sections for completed sections if no sections are completed" do
@ -32,11 +37,11 @@ RSpec.describe TasklistHelper do
end
it "returns the number of not started sections" do
expect(get_subsections_count(empty_lettings_log, :not_started)).to eq(8)
expect(get_subsections_count(empty_lettings_log, :not_started)).to eq(1)
end
it "returns the number of sections in progress" do
expect(get_subsections_count(lettings_log, :in_progress)).to eq(3)
expect(get_subsections_count(lettings_log, :in_progress)).to eq(2)
end
it "returns 0 for invalid state" do
@ -44,57 +49,7 @@ RSpec.describe TasklistHelper do
end
end
describe "get_next_page_or_check_answers" do
let(:subsection) { lettings_log.form.get_subsection("household_characteristics") }
let(:user) { FactoryBot.build(:user) }
it "returns the check answers page path if the section has been started already" do
expect(next_page_or_check_answers(subsection, lettings_log, user)).to match(/check-answers/)
end
it "returns the first question page path for the section if it has not been started yet" do
expect(next_page_or_check_answers(subsection, empty_lettings_log, user)).to match(/tenant-code-test/)
end
it "when first question being not routed to returns the next routed question link" do
empty_lettings_log.housingneeds_a = "No"
expect(next_page_or_check_answers(subsection, empty_lettings_log, user)).to match(/person-1-gender/)
end
end
describe "subsection link" do
let(:subsection) { lettings_log.form.get_subsection("household_characteristics") }
let(:user) { FactoryBot.build(:user) }
context "with a subsection that's enabled" do
it "returns the subsection link url" do
expect(subsection_link(subsection, lettings_log, user)).to match(/household-characteristics/)
end
end
context "with a subsection that cannot be started yet" do
before do
allow(subsection).to receive(:status).with(lettings_log).and_return(:cannot_start_yet)
end
it "returns the label instead of a link" do
expect(subsection_link(subsection, lettings_log, user)).to match(subsection.label)
end
end
end
end
end
describe "#review_log_text" do
around do |example|
Timecop.freeze(now) do
Singleton.__init__(FormHandler)
example.run
end
Singleton.__init__(FormHandler)
end
context "with lettings log" do
describe "review_log_text" do
context "when collection_period_open? == true" do
context "with 2023 deadline" do
let(:now) { Time.utc(2022, 6, 1) }
@ -113,7 +68,7 @@ RSpec.describe TasklistHelper do
it "returns relevant text" do
expect(review_log_text(lettings_log)).to eq(
"You can #{govuk_link_to 'review and make changes to this log', review_lettings_log_path(lettings_log)} until 1 July 2024.".html_safe,
"You can #{govuk_link_to 'review and make changes to this log', review_lettings_log_path(lettings_log)} until 9 July 2024.".html_safe,
)
end
end
@ -129,6 +84,30 @@ RSpec.describe TasklistHelper do
end
end
describe "subsection link" do
let(:lettings_log) { create(:lettings_log, :completed) }
let(:subsection) { lettings_log.form.get_subsection("household_characteristics") }
let(:user) { build(:user) }
context "with a subsection that's enabled" do
it "returns the subsection link url" do
expect(subsection_link(subsection, lettings_log, user)).to match(/household-characteristics/)
end
end
context "with a subsection that cannot be started yet" do
before do
allow(subsection).to receive(:status).with(lettings_log).and_return(:cannot_start_yet)
end
it "returns the label instead of a link" do
expect(subsection_link(subsection, lettings_log, user)).to match(subsection.label)
end
end
end
end
describe "#review_log_text" do
context "with sales log" do
context "when collection_period_open? == true" do
let(:now) { Time.utc(2022, 6, 1) }
@ -136,17 +115,19 @@ RSpec.describe TasklistHelper do
it "returns relevant text" do
expect(review_log_text(sales_log)).to eq(
"You can #{govuk_link_to 'review and make changes to this log', review_lettings_log_path(sales_log)} until 1 July 2023.".html_safe,
"You can #{govuk_link_to 'review and make changes to this log', review_sales_log_path(id: sales_log, sales_log: true)} until 7 July 2023.".html_safe,
)
end
end
context "when collection_period_open? == false" do
let(:now) { Time.utc(2023, 7, 8) }
let(:sales_log) { create(:sales_log, :completed, saledate: Time.utc(2023, 2, 8)) }
let(:now) { Time.utc(2022, 6, 1) }
let!(:sales_log) { create(:sales_log, :completed) }
it "returns relevant text" do
expect(review_log_text(sales_log)).to eq("This log is from the 2022/2023 collection window, which is now closed.")
Timecop.freeze(now + 1.year) do
expect(review_log_text(sales_log)).to eq("This log is from the 2021/2022 collection window, which is now closed.")
end
end
end
end

54
spec/mailers/bulk_upload_mailer_spec.rb

@ -5,7 +5,7 @@ RSpec.describe BulkUploadMailer do
let(:notify_client) { instance_double(Notifications::Client) }
let(:user) { create(:user, email: "user@example.com") }
let(:bulk_upload) { build(:bulk_upload, :lettings, user:) }
let(:bulk_upload) { create(:bulk_upload, :lettings, user:) }
before do
allow(Notifications::Client).to receive(:new).and_return(notify_client)
@ -75,4 +75,56 @@ RSpec.describe BulkUploadMailer do
end
end
end
describe "#send_correct_and_upload_again_mail" do
context "when 2 columns with errors" do
before do
create(:bulk_upload_error, bulk_upload:, col: "A")
create(:bulk_upload_error, bulk_upload:, col: "B")
end
it "sends correctly formed email with A, B" do
expect(notify_client).to receive(:send_email).with(
email_address: user.email,
template_id: described_class::BULK_UPLOAD_FAILED_CSV_ERRORS_TEMPLATE_ID,
personalisation: {
filename: bulk_upload.filename,
upload_timestamp: bulk_upload.created_at.to_fs(:govuk_date_and_time),
year_combo: bulk_upload.year_combo,
lettings_or_sales: bulk_upload.log_type,
error_description: "We noticed that you have a lot of similar errors in column A, B. Please correct your data export and upload again.",
summary_report_link: "http://localhost:3000/lettings-logs/bulk-upload-results/#{bulk_upload.id}/summary",
},
)
mailer.send_correct_and_upload_again_mail(bulk_upload:)
end
end
context "when 4 columns with errors" do
before do
create(:bulk_upload_error, bulk_upload:, col: "A")
create(:bulk_upload_error, bulk_upload:, col: "B")
create(:bulk_upload_error, bulk_upload:, col: "C")
create(:bulk_upload_error, bulk_upload:, col: "D")
end
it "sends correctly formed email with A, B, C and more" do
expect(notify_client).to receive(:send_email).with(
email_address: user.email,
template_id: described_class::BULK_UPLOAD_FAILED_CSV_ERRORS_TEMPLATE_ID,
personalisation: {
filename: bulk_upload.filename,
upload_timestamp: bulk_upload.created_at.to_fs(:govuk_date_and_time),
year_combo: bulk_upload.year_combo,
lettings_or_sales: bulk_upload.log_type,
error_description: "We noticed that you have a lot of similar errors in column A, B, C and more. Please correct your data export and upload again.",
summary_report_link: "http://localhost:3000/lettings-logs/bulk-upload-results/#{bulk_upload.id}/summary",
},
)
mailer.send_correct_and_upload_again_mail(bulk_upload:)
end
end
end
end

6
spec/models/form/lettings/pages/location_spec.rb

@ -6,6 +6,12 @@ RSpec.describe Form::Lettings::Pages::Location, type: :model do
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
let(:form) { instance_double(Form) }
before do
allow(form).to receive(:start_date).and_return(Time.zone.local(2022, 4, 1))
allow(subsection).to receive(:form).and_return(form)
end
it "has correct subsection" do
expect(page.subsection).to eq(subsection)

18
spec/models/form/lettings/questions/location_id_spec.rb

@ -6,6 +6,14 @@ RSpec.describe Form::Lettings::Questions::LocationId, type: :model do
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
let(:subsection) { instance_double(Form::Subsection) }
let(:form) { instance_double(Form) }
before do
allow(form).to receive(:start_date).and_return(Time.zone.local(2022, 4, 1))
allow(page).to receive(:subsection).and_return(subsection)
allow(subsection).to receive(:form).and_return(form)
end
it "has correct page" do
expect(question.page).to eq(page)
@ -103,4 +111,14 @@ RSpec.describe Form::Lettings::Questions::LocationId, type: :model do
end
end
end
context "with collection year on or after 2023" do
before do
allow(form).to receive(:start_date).and_return(Time.zone.local(2023, 4, 1))
end
it "has the correct header" do
expect(question.header).to eq("Which location is this letting for?")
end
end
end

16
spec/models/form/sales/pages/about_staircase_spec.rb

@ -11,9 +11,25 @@ RSpec.describe Form::Sales::Pages::AboutStaircase, type: :model do
expect(page.subsection).to eq(subsection)
end
describe "questions" do
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date:)) }
context "when 2022" do
let(:start_date) { Time.utc(2022, 2, 8) }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[stairbought stairowned])
end
end
context "when 2023" do
let(:start_date) { Time.utc(2023, 2, 8) }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[stairbought stairowned staircasesale])
end
end
end
it "has the correct id" do
expect(page.id).to eq("about_staircasing")

33
spec/models/form/sales/pages/buyer2_ethnic_background_arab_spec.rb

@ -0,0 +1,33 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::Buyer2EthnicBackgroundArab, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[ethnicbuy2])
end
it "has the correct id" do
expect(page.id).to eq("buyer_2_ethnic_background_arab")
end
it "has the correct header" do
expect(page.header).to be_nil
end
it "has the correct description" do
expect(page.description).to be_nil
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "ethnic_group2" => 4 }])
end
end

33
spec/models/form/sales/pages/buyer2_ethnic_background_asian_spec.rb

@ -0,0 +1,33 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::Buyer2EthnicBackgroundAsian, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[ethnicbuy2])
end
it "has the correct id" do
expect(page.id).to eq("buyer_2_ethnic_background_asian")
end
it "has the correct header" do
expect(page.header).to be_nil
end
it "has the correct description" do
expect(page.description).to be_nil
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "ethnic_group2" => 2 }])
end
end

33
spec/models/form/sales/pages/buyer2_ethnic_background_black_spec.rb

@ -0,0 +1,33 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::Buyer2EthnicBackgroundBlack, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[ethnicbuy2])
end
it "has the correct id" do
expect(page.id).to eq("buyer_2_ethnic_background_black")
end
it "has the correct header" do
expect(page.header).to be_nil
end
it "has the correct description" do
expect(page.description).to be_nil
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "ethnic_group2" => 3 }])
end
end

33
spec/models/form/sales/pages/buyer2_ethnic_background_mixed_spec.rb

@ -0,0 +1,33 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::Buyer2EthnicBackgroundMixed, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[ethnicbuy2])
end
it "has the correct id" do
expect(page.id).to eq("buyer_2_ethnic_background_mixed")
end
it "has the correct header" do
expect(page.header).to be_nil
end
it "has the correct description" do
expect(page.description).to be_nil
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "ethnic_group2" => 1 }])
end
end

33
spec/models/form/sales/pages/buyer2_ethnic_background_white_spec.rb

@ -0,0 +1,33 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::Buyer2EthnicBackgroundWhite, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[ethnicbuy2])
end
it "has the correct id" do
expect(page.id).to eq("buyer_2_ethnic_background_white")
end
it "has the correct header" do
expect(page.header).to be_nil
end
it "has the correct description" do
expect(page.description).to be_nil
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "ethnic_group2" => 0 }])
end
end

42
spec/models/form/sales/pages/buyer2_ethnic_group_spec.rb

@ -0,0 +1,42 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::Buyer2EthnicGroup, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[ethnic_group2])
end
it "has the correct id" do
expect(page.id).to eq("buyer_2_ethnic_group")
end
it "has the correct header" do
expect(page.header).to be_nil
end
it "has the correct description" do
expect(page.description).to be_nil
end
it "has correct depends_on" do
expect(page.depends_on).to eq([
{
"jointpur" => 1,
"privacynotice" => 1,
},
{
"jointpur" => 1,
"noint" => 1,
},
])
end
end

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

@ -16,7 +16,7 @@ RSpec.describe Form::Sales::Questions::Buyer1IncomeValueCheck, type: :model do
end
it "has the correct header" do
expect(question.header).to eq("Are you sure this income is correct?")
expect(question.header).to eq("Are you sure this is correct?")
end
it "has the correct check_answer_label" do

48
spec/models/form/sales/questions/buyer2_ethnic_background_arab_spec.rb

@ -0,0 +1,48 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::Buyer2EthnicBackgroundArab, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("ethnicbuy2")
end
it "has the correct header" do
expect(question.header).to eq("Which of the following best describes the buyer 2’s Arab background?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Buyer 2’s ethnic background")
end
it "has the correct type" do
expect(question.type).to eq("radio")
end
it "is not marked as derived" do
expect(question.derived?).to be false
end
it "has the correct hint_text" do
expect(question.hint_text).to be nil
end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"16" => { "value" => "Other ethnic group" },
"19" => { "value" => "Arab" },
})
end
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(2)
end
end

51
spec/models/form/sales/questions/buyer2_ethnic_background_asian_spec.rb

@ -0,0 +1,51 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::Buyer2EthnicBackgroundAsian, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("ethnicbuy2")
end
it "has the correct header" do
expect(question.header).to eq("Which of the following best describes the buyer 2’s Asian or Asian British background?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Buyer 2’s ethnic background")
end
it "has the correct type" do
expect(question.type).to eq("radio")
end
it "is not marked as derived" do
expect(question.derived?).to be false
end
it "has the correct hint_text" do
expect(question.hint_text).to be nil
end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"10" => { "value" => "Bangladeshi" },
"11" => { "value" => "Any other Asian or Asian British background" },
"15" => { "value" => "Chinese" },
"8" => { "value" => "Indian" },
"9" => { "value" => "Pakistani" },
})
end
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(2)
end
end

49
spec/models/form/sales/questions/buyer2_ethnic_background_black_spec.rb

@ -0,0 +1,49 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::Buyer2EthnicBackgroundBlack, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("ethnicbuy2")
end
it "has the correct header" do
expect(question.header).to eq("Which of the following best describes the buyer 2’s Black, African, Caribbean or Black British background?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Buyer 2’s ethnic background")
end
it "has the correct type" do
expect(question.type).to eq("radio")
end
it "is not marked as derived" do
expect(question.derived?).to be false
end
it "has the correct hint_text" do
expect(question.hint_text).to be nil
end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"12" => { "value" => "Caribbean" },
"13" => { "value" => "African" },
"14" => { "value" => "Any other Black, African or Caribbean background" },
})
end
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(2)
end
end

50
spec/models/form/sales/questions/buyer2_ethnic_background_mixed_spec.rb

@ -0,0 +1,50 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::Buyer2EthnicBackgroundMixed, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("ethnicbuy2")
end
it "has the correct header" do
expect(question.header).to eq("Which of the following best describes the buyer 2’s Mixed or Multiple ethnic groups background?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Buyer 2’s ethnic background")
end
it "has the correct type" do
expect(question.type).to eq("radio")
end
it "is not marked as derived" do
expect(question.derived?).to be false
end
it "has the correct hint_text" do
expect(question.hint_text).to be nil
end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"4" => { "value" => "White and Black Caribbean" },
"5" => { "value" => "White and Black African" },
"6" => { "value" => "White and Asian" },
"7" => { "value" => "Any other Mixed or Multiple ethnic background" },
})
end
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(2)
end
end

50
spec/models/form/sales/questions/buyer2_ethnic_background_white_spec.rb

@ -0,0 +1,50 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::Buyer2EthnicBackgroundWhite, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("ethnicbuy2")
end
it "has the correct header" do
expect(question.header).to eq("Which of the following best describes the buyer 2’s White background?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Buyer 2’s ethnic background")
end
it "has the correct type" do
expect(question.type).to eq("radio")
end
it "is not marked as derived" do
expect(question.derived?).to be false
end
it "has the correct hint_text" do
expect(question.hint_text).to be nil
end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"1" => { "value" => "English, Welsh, Northern Irish, Scottish or British" },
"18" => { "value" => "Gypsy or Irish Traveller" },
"2" => { "value" => "Irish" },
"3" => { "value" => "Any other White background" },
})
end
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(2)
end
end

62
spec/models/form/sales/questions/buyer2_ethnic_group_spec.rb

@ -0,0 +1,62 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::Buyer2EthnicGroup, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("ethnic_group2")
end
it "has the correct header" do
expect(question.header).to eq("What is buyer 2’s ethnic group?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Buyer 2’s ethnic group")
end
it "has the correct type" do
expect(question.type).to eq("radio")
end
it "is not marked as derived" do
expect(question.derived?).to be false
end
it "has the correct hint_text" do
expect(question.hint_text).to be nil
end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"0" => { "value" => "White" },
"1" => { "value" => "Mixed or Multiple ethnic groups" },
"17" => { "value" => "Buyer 1 prefers not to say" },
"2" => { "value" => "Asian or Asian British" },
"3" => { "value" => "Black, African, Caribbean or Black British" },
"4" => { "value" => "Arab or other ethnic group" },
"divider" => { "value" => true },
})
end
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(2)
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to eq([{
"condition" => {
"ethnic_group2" => 17,
},
"value" => "Prefers not to say",
}])
end
end

49
spec/models/form/sales/questions/staircase_sale_spec.rb

@ -0,0 +1,49 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::StaircaseSale, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("staircasesale")
end
it "has the correct header" do
expect(question.header).to eq("Is this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Part of a back-to-back staircasing transaction")
end
it "has the correct type" do
expect(question.type).to eq("radio")
end
it "is not marked as derived" do
expect(question.derived?).to be false
end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"1" => { "value" => "Yes" },
"2" => { "value" => "No" },
"3" => { "value" => "Don't know" },
})
end
it "has correct conditional for" do
expect(question.conditional_for).to eq(nil)
end
it "has the correct hint" do
expect(question.hint_text).to be_nil
end
end

92
spec/models/form/sales/subsections/household_characteristics_spec.rb

@ -6,11 +6,93 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
let(:subsection_id) { nil }
let(:subsection_definition) { nil }
let(:section) { instance_double(Form::Sales::Sections::Household) }
let(:form) { instance_double(Form) }
it "has correct section" do
expect(household_characteristics.section).to eq(section)
end
context "with 2022/23 form" do
before do
allow(form).to receive(:start_date).and_return(Time.zone.local(2022, 4, 1))
allow(section).to receive(:form).and_return(form)
end
it "has correct pages" do
expect(household_characteristics.pages.map(&:id)).to eq(
%w[
buyer_interview
privacy_notice
buyer_1_age
age_1_retirement_value_check
age_1_old_persons_shared_ownership_value_check
buyer_1_gender_identity
gender_1_retirement_value_check
buyer_1_ethnic_group
buyer_1_ethnic_background_black
buyer_1_ethnic_background_asian
buyer_1_ethnic_background_arab
buyer_1_ethnic_background_mixed
buyer_1_ethnic_background_white
buyer_1_nationality
buyer_1_working_situation
working_situation_1_retirement_value_check
working_situation_buyer_1_income_value_check
buyer_1_live_in_property
buyer_2_relationship_to_buyer_1
buyer_2_age
age_2_old_persons_shared_ownership_value_check
age_2_buyer_retirement_value_check
buyer_2_gender_identity
gender_2_buyer_retirement_value_check
buyer_2_working_situation
working_situation_2_retirement_value_check_joint_purchase
working_situation_buyer_2_income_value_check
buyer_2_live_in_property
number_of_others_in_property
person_2_known
person_2_relationship_to_buyer_1
person_2_age
age_2_retirement_value_check
person_2_gender_identity
gender_2_retirement_value_check
person_2_working_situation
working_situation_2_retirement_value_check
person_3_known
person_3_relationship_to_buyer_1
person_3_age
age_3_retirement_value_check
person_3_gender_identity
gender_3_retirement_value_check
person_3_working_situation
working_situation_3_retirement_value_check
person_4_known
person_4_relationship_to_buyer_1
person_4_age
age_4_retirement_value_check
person_4_gender_identity
gender_4_retirement_value_check
person_4_working_situation
working_situation_4_retirement_value_check
person_5_known
person_5_relationship_to_buyer_1
person_5_age
age_5_retirement_value_check
person_5_gender_identity
gender_5_retirement_value_check
person_5_working_situation
working_situation_5_retirement_value_check
],
)
end
end
context "with 2023/24 form" do
before do
allow(form).to receive(:start_date).and_return(Time.zone.local(2023, 4, 1))
allow(section).to receive(:form).and_return(form)
end
it "has correct pages" do
expect(household_characteristics.pages.map(&:id)).to eq(
%w[
@ -38,8 +120,15 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
age_2_buyer_retirement_value_check
buyer_2_gender_identity
gender_2_buyer_retirement_value_check
buyer_2_ethnic_group
buyer_2_ethnic_background_black
buyer_2_ethnic_background_asian
buyer_2_ethnic_background_arab
buyer_2_ethnic_background_mixed
buyer_2_ethnic_background_white
buyer_2_working_situation
working_situation_2_buyer_retirement_value_check
working_situation_2_retirement_value_check_joint_purchase
working_situation_buyer_2_income_value_check
buyer_2_live_in_property
number_of_others_in_property
person_2_known
@ -77,6 +166,7 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
],
)
end
end
it "has the correct id" do
expect(household_characteristics.id).to eq("household_characteristics")

2
spec/models/form/sales/subsections/income_benefits_and_savings_spec.rb

@ -27,6 +27,7 @@ RSpec.describe Form::Sales::Subsections::IncomeBenefitsAndSavings, type: :model
buyer_1_mortgage_value_check
buyer_2_income
buyer_2_income_mortgage_value_check
buyer_2_income_value_check
buyer_2_mortgage
buyer_2_mortgage_value_check
housing_benefits
@ -52,6 +53,7 @@ RSpec.describe Form::Sales::Subsections::IncomeBenefitsAndSavings, type: :model
buyer_1_mortgage_value_check
buyer_2_income
buyer_2_income_mortgage_value_check
buyer_2_income_value_check
buyer_2_mortgage
buyer_2_mortgage_value_check
housing_benefits

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

Loading…
Cancel
Save