Browse Source

Merge branch 'main' into CLDC-3740-Replace-you-didnt-answer-with-link

pull/2836/head
Manny Dinssa 2 months ago
parent
commit
f268ae28ab
  1. 10
      Gemfile
  2. 231
      Gemfile.lock
  3. 2
      app/components/bulk_upload_error_row_component.html.erb
  4. 22
      app/components/primary_navigation_component.html.erb
  5. 2
      app/components/search_component.html.erb
  6. 15
      app/controllers/form_controller.rb
  7. 2
      app/controllers/lettings_logs_controller.rb
  8. 2
      app/controllers/logs_controller.rb
  9. 4
      app/controllers/organisations_controller.rb
  10. 2
      app/controllers/sales_logs_controller.rb
  11. 12
      app/controllers/sessions_controller.rb
  12. 69
      app/frontend/styles/_primary-navigation.scss
  13. 42
      app/frontend/styles/application.scss
  14. 4
      app/helpers/filters_helper.rb
  15. 4
      app/helpers/form_page_error_helper.rb
  16. 2
      app/helpers/logs_helper.rb
  17. 6
      app/models/bulk_upload.rb
  18. 2
      app/models/csv_download.rb
  19. 1
      app/models/form/lettings/questions/offered.rb
  20. 2
      app/models/form/sales/pages/owning_organisation.rb
  21. 2
      app/models/forms/bulk_upload_lettings_resume/fix_choice.rb
  22. 2
      app/models/forms/bulk_upload_sales_resume/fix_choice.rb
  23. 7
      app/models/location.rb
  24. 18
      app/models/log.rb
  25. 4
      app/models/merge_request.rb
  26. 2
      app/models/organisation.rb
  27. 18
      app/models/scheme.rb
  28. 2
      app/models/user.rb
  29. 2
      app/models/validations/property_validations.rb
  30. 2
      app/models/validations/sales/property_validations.rb
  31. 4
      app/models/validations/soft_validations.rb
  32. 2
      app/services/bulk_upload/lettings/log_creator.rb
  33. 2
      app/services/bulk_upload/sales/log_creator.rb
  34. 16
      app/services/feature_toggle.rb
  35. 21
      app/views/bulk_upload_lettings_logs/forms/prepare_your_file_2024.html.erb
  36. 2
      app/views/bulk_upload_lettings_resume/deletion_report.html.erb
  37. 12
      app/views/bulk_upload_lettings_resume/fix_choice.html.erb
  38. 2
      app/views/bulk_upload_lettings_soft_validations_check/confirm.html.erb
  39. 19
      app/views/bulk_upload_sales_logs/forms/prepare_your_file_2024.html.erb
  40. 2
      app/views/bulk_upload_sales_resume/deletion_report.html.erb
  41. 14
      app/views/bulk_upload_sales_resume/fix_choice.html.erb
  42. 2
      app/views/bulk_upload_sales_soft_validations_check/confirm.html.erb
  43. 4
      app/views/bulk_upload_shared/_moved_user_banner.html.erb
  44. 9
      app/views/bulk_upload_shared/guidance.html.erb
  45. 6
      app/views/cookies/show.html.erb
  46. 2
      app/views/devise/sessions/new.html.erb
  47. 3
      app/views/form/page.html.erb
  48. 2
      app/views/layouts/application.html.erb
  49. 2
      app/views/locations/check_answers.html.erb
  50. 2
      app/views/locations/show.html.erb
  51. 10
      app/views/logs/delete_duplicates.html.erb
  52. 30
      app/views/merge_requests/merge_request.html.erb
  53. 12
      app/views/organisation_relationships/add_managing_agent.html.erb
  54. 12
      app/views/organisation_relationships/add_stock_owner.html.erb
  55. 65
      app/views/organisations/data_sharing_agreement.html.erb
  56. 53
      app/views/organisations/duplicate_schemes.html.erb
  57. 10
      app/views/schemes/changes.html.erb
  58. 2
      app/views/schemes/check_answers.html.erb
  59. 2
      app/views/schemes/show.html.erb
  60. 68
      app/views/start/guidance.html.erb
  61. 2
      app/views/users/show.html.erb
  62. 8
      bin/rubocop
  63. 3
      bin/setup
  64. 13
      config/application.rb
  65. 19
      config/environments/development.rb
  66. 78
      config/environments/production.rb
  67. 34
      config/environments/test.rb
  68. 6
      config/initializers/assets.rb
  69. 13
      config/initializers/content_security_policy.rb
  70. 6
      config/initializers/filter_parameter_logging.rb
  71. 20
      config/initializers/permissions_policy.rb
  72. 4
      config/locales/en.yml
  73. 17
      config/locales/test.en.yml
  74. 4
      config/locales/validations/sales/2024/bulk_upload.en.yml
  75. 48
      config/puma.rb
  76. 22
      db/migrate/20241206142942_add_service_name_to_active_storage_blobs.active_storage.rb
  77. 28
      db/migrate/20241206142943_create_active_storage_variant_records.active_storage.rb
  78. 8
      db/migrate/20241206142944_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb
  79. 2
      db/schema.rb
  80. 12
      db/seeds.rb
  81. 1
      lib/tasks/correct_rent_type_value.rake
  82. 1
      lib/tasks/recalculate_status_after_sales_over_retirement_age_validation.rake
  83. 4
      package.json
  84. 2
      spec/components/primary_navigation_component_spec.rb
  85. 2
      spec/controllers/errors_controller_spec.rb
  86. 2
      spec/factories/organisation.rb
  87. 16
      spec/features/form/page_routing_spec.rb
  88. 6
      spec/features/organisation_spec.rb
  89. 4
      spec/features/schemes_spec.rb
  90. 6
      spec/features/user_spec.rb
  91. 2
      spec/lib/tasks/correct_rent_type_value_spec.rb
  92. 2
      spec/lib/tasks/recalculate_status_after_sales_over_retirement_age_validation_spec.rb
  93. 29
      spec/models/lettings_log_spec.rb
  94. 11
      spec/models/log_spec.rb
  95. 11
      spec/models/user_spec.rb
  96. 28
      spec/models/validations/soft_validations_spec.rb
  97. 2
      spec/rails_helper.rb
  98. 4
      spec/requests/auth/passwords_controller_spec.rb
  99. 8
      spec/requests/bulk_upload_lettings_results_controller_spec.rb
  100. 2
      spec/requests/bulk_upload_lettings_soft_validations_check_controller_spec.rb
  101. Some files were not shown because too many files have changed in this diff Show More

10
Gemfile

@ -6,11 +6,11 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby "3.1.4"
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails', branch: 'main'
gem "rails", "~> 7.0.8.7"
gem "rails", "~> 7.2.2"
# Use postgresql as the database for Active Record
gem "pg", "~> 1.1"
# Use Puma as the app server
gem "puma", "~> 5.6"
gem "puma", "~> 6.4"
# The modern asset pipeline for Rails [https://github.com/rails/propshaft]
gem "propshaft"
# Bundle and transpile JavaScript [https://github.com/rails/jsbundling-rails]
@ -18,9 +18,9 @@ gem "jsbundling-rails"
# Reduces boot times through caching; required in config/boot.rb
gem "bootsnap", ">= 1.4.4", require: false
# GOV UK frontend components
gem "govuk-components", "~> 5.1"
gem "govuk-components", "~> 5.7"
# GOV UK component form builder DSL
gem "govuk_design_system_formbuilder", "~> 5.0"
gem "govuk_design_system_formbuilder", "~> 5.7"
# Convert Markdown into GOV.UK frontend-styled HTML
gem "govuk_markdown"
gem "redcarpet", "~> 3.6"
@ -44,7 +44,7 @@ gem "view_component", "~> 3.9"
# Use the AWS S3 SDK as storage mechanism
gem "aws-sdk-s3"
# Track changes to models for auditing or versioning.
gem "paper_trail"
gem "paper_trail", "~> 15.2"
# Store active record objects in version whodunnits
gem "paper_trail-globalid"

231
Gemfile.lock

@ -1,75 +1,81 @@
GEM
remote: https://rubygems.org/
specs:
actioncable (7.0.8.7)
actionpack (= 7.0.8.7)
activesupport (= 7.0.8.7)
actioncable (7.2.2.1)
actionpack (= 7.2.2.1)
activesupport (= 7.2.2.1)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (7.0.8.7)
actionpack (= 7.0.8.7)
activejob (= 7.0.8.7)
activerecord (= 7.0.8.7)
activestorage (= 7.0.8.7)
activesupport (= 7.0.8.7)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
actionmailer (7.0.8.7)
actionpack (= 7.0.8.7)
actionview (= 7.0.8.7)
activejob (= 7.0.8.7)
activesupport (= 7.0.8.7)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0)
actionpack (7.0.8.7)
actionview (= 7.0.8.7)
activesupport (= 7.0.8.7)
rack (~> 2.0, >= 2.2.4)
zeitwerk (~> 2.6)
actionmailbox (7.2.2.1)
actionpack (= 7.2.2.1)
activejob (= 7.2.2.1)
activerecord (= 7.2.2.1)
activestorage (= 7.2.2.1)
activesupport (= 7.2.2.1)
mail (>= 2.8.0)
actionmailer (7.2.2.1)
actionpack (= 7.2.2.1)
actionview (= 7.2.2.1)
activejob (= 7.2.2.1)
activesupport (= 7.2.2.1)
mail (>= 2.8.0)
rails-dom-testing (~> 2.2)
actionpack (7.2.2.1)
actionview (= 7.2.2.1)
activesupport (= 7.2.2.1)
nokogiri (>= 1.8.5)
racc
rack (>= 2.2.4, < 3.2)
rack-session (>= 1.0.1)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (7.0.8.7)
actionpack (= 7.0.8.7)
activerecord (= 7.0.8.7)
activestorage (= 7.0.8.7)
activesupport (= 7.0.8.7)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
useragent (~> 0.16)
actiontext (7.2.2.1)
actionpack (= 7.2.2.1)
activerecord (= 7.2.2.1)
activestorage (= 7.2.2.1)
activesupport (= 7.2.2.1)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (7.0.8.7)
activesupport (= 7.0.8.7)
actionview (7.2.2.1)
activesupport (= 7.2.2.1)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activejob (7.0.8.7)
activesupport (= 7.0.8.7)
erubi (~> 1.11)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
activejob (7.2.2.1)
activesupport (= 7.2.2.1)
globalid (>= 0.3.6)
activemodel (7.0.8.7)
activesupport (= 7.0.8.7)
activemodel-serializers-xml (1.0.2)
activemodel (> 5.x)
activesupport (> 5.x)
activemodel (7.2.2.1)
activesupport (= 7.2.2.1)
activemodel-serializers-xml (1.0.3)
activemodel (>= 5.0.0.a)
activesupport (>= 5.0.0.a)
builder (~> 3.1)
activerecord (7.0.8.7)
activemodel (= 7.0.8.7)
activesupport (= 7.0.8.7)
activestorage (7.0.8.7)
actionpack (= 7.0.8.7)
activejob (= 7.0.8.7)
activerecord (= 7.0.8.7)
activesupport (= 7.0.8.7)
activerecord (7.2.2.1)
activemodel (= 7.2.2.1)
activesupport (= 7.2.2.1)
timeout (>= 0.4.0)
activestorage (7.2.2.1)
actionpack (= 7.2.2.1)
activejob (= 7.2.2.1)
activerecord (= 7.2.2.1)
activesupport (= 7.2.2.1)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (7.0.8.7)
concurrent-ruby (~> 1.0, >= 1.0.2)
activesupport (7.2.2.1)
base64
benchmark (>= 0.3)
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
logger (>= 1.4.2)
minitest (>= 5.1)
tzinfo (~> 2.0)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
ast (2.4.2)
@ -104,6 +110,7 @@ GEM
thread_safe (~> 0.3, >= 0.3.1)
base64 (0.2.0)
bcrypt (3.1.20)
benchmark (0.4.0)
better_html (2.0.2)
actionview (>= 6.0)
activesupport (>= 6.0)
@ -111,7 +118,7 @@ GEM
erubi (~> 1.4)
parser (>= 2.4)
smart_properties
bigdecimal (3.1.6)
bigdecimal (3.1.8)
bindex (0.8.1)
bootsnap (1.18.3)
msgpack (~> 1.2)
@ -149,6 +156,7 @@ GEM
crass (1.0.6)
cssbundling-rails (1.4.0)
railties (>= 6.0.0)
csv (3.3.0)
date (3.4.1)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
@ -170,6 +178,7 @@ GEM
dotenv-rails (3.0.2)
dotenv (= 3.0.2)
railties (>= 6.1)
drb (2.2.1)
dumb_delegator (1.0.0)
encryptor (3.0.0)
erb_lint (0.5.0)
@ -184,10 +193,10 @@ GEM
tzinfo
event_stream_parser (1.0.0)
excon (0.111.0)
factory_bot (6.4.6)
factory_bot (6.5.0)
activesupport (>= 5.0.0)
factory_bot_rails (6.4.3)
factory_bot (~> 6.4)
factory_bot_rails (6.4.4)
factory_bot (~> 6.5)
railties (>= 5.0.0)
faker (3.2.3)
i18n (>= 1.8.11, < 2)
@ -203,11 +212,11 @@ GEM
raabro (~> 1.4)
globalid (1.2.1)
activesupport (>= 6.1)
govuk-components (5.2.1)
govuk-components (5.7.0)
html-attributes-utils (~> 1.0.0, >= 1.0.0)
pagy (~> 6.0)
view_component (>= 3.9, < 3.11)
govuk_design_system_formbuilder (5.2.0)
pagy (>= 6, < 10)
view_component (>= 3.9, < 3.17)
govuk_design_system_formbuilder (5.7.1)
actionview (>= 6.1)
activemodel (>= 6.1)
activesupport (>= 6.1)
@ -222,6 +231,10 @@ GEM
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
iniparse (1.5.0)
io-console (0.8.0)
irb (1.14.1)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
jmespath (1.6.2)
jsbundling-rails (1.3.0)
railties (>= 6.0.0)
@ -246,6 +259,7 @@ GEM
listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
logger (1.6.2)
loofah (2.23.1)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
@ -287,8 +301,8 @@ GEM
childprocess (>= 0.6.3, < 6)
iniparse (~> 1.4)
rexml (~> 3.2)
pagy (6.5.0)
paper_trail (15.1.0)
pagy (9.3.2)
paper_trail (15.2.0)
activerecord (>= 6.1)
request_store (~> 1.4)
paper_trail-globalid (0.2.0)
@ -313,34 +327,41 @@ GEM
pry-byebug (3.10.1)
byebug (~> 11.0)
pry (>= 0.13, < 0.15)
psych (5.2.1)
date
stringio
public_suffix (5.0.4)
puma (5.6.9)
puma (6.5.0)
nio4r (~> 2.0)
pundit (2.3.1)
activesupport (>= 3.0.0)
raabro (1.4.0)
racc (1.8.1)
rack (2.2.10)
rack (3.1.8)
rack-attack (6.7.0)
rack (>= 1.0, < 4)
rack-mini-profiler (2.3.4)
rack (>= 1.2.0)
rack-session (2.0.0)
rack (>= 3.0.0)
rack-test (2.1.0)
rack (>= 1.3)
rails (7.0.8.7)
actioncable (= 7.0.8.7)
actionmailbox (= 7.0.8.7)
actionmailer (= 7.0.8.7)
actionpack (= 7.0.8.7)
actiontext (= 7.0.8.7)
actionview (= 7.0.8.7)
activejob (= 7.0.8.7)
activemodel (= 7.0.8.7)
activerecord (= 7.0.8.7)
activestorage (= 7.0.8.7)
activesupport (= 7.0.8.7)
rackup (2.2.1)
rack (>= 3)
rails (7.2.2.1)
actioncable (= 7.2.2.1)
actionmailbox (= 7.2.2.1)
actionmailer (= 7.2.2.1)
actionpack (= 7.2.2.1)
actiontext (= 7.2.2.1)
actionview (= 7.2.2.1)
activejob (= 7.2.2.1)
activemodel (= 7.2.2.1)
activerecord (= 7.2.2.1)
activestorage (= 7.2.2.1)
activesupport (= 7.2.2.1)
bundler (>= 1.15.0)
railties (= 7.0.8.7)
railties (= 7.2.2.1)
rails-dom-testing (2.2.0)
activesupport (>= 5.0.0)
minitest
@ -348,31 +369,37 @@ GEM
rails-html-sanitizer (1.6.1)
loofah (~> 2.21)
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
rails_admin (3.1.3)
rails_admin (3.3.0)
activemodel-serializers-xml (>= 1.0)
csv
kaminari (>= 0.14, < 2.0)
nested_form (~> 0.3)
rails (>= 6.0, < 8)
turbo-rails (~> 1.0)
railties (7.0.8.7)
actionpack (= 7.0.8.7)
activesupport (= 7.0.8.7)
method_source
rails (>= 6.0, < 9)
turbo-rails (>= 1.0, < 3)
railties (7.2.2.1)
actionpack (= 7.2.2.1)
activesupport (= 7.2.2.1)
irb (~> 1.13)
rackup (>= 1.0.0)
rake (>= 12.2)
thor (~> 1.0)
zeitwerk (~> 2.5)
thor (~> 1.0, >= 1.2.2)
zeitwerk (~> 2.6)
rainbow (3.1.1)
rake (13.2.1)
randexp (0.1.7)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
rdoc (6.8.1)
psych (>= 4.0.0)
redcarpet (3.6.0)
redis (4.8.1)
redis-client (0.22.1)
connection_pool
regexp_parser (2.9.0)
request_store (1.6.0)
reline (0.5.12)
io-console (~> 0.5)
request_store (1.7.0)
rack (>= 1.4)
responders (3.1.1)
actionpack (>= 5.2)
@ -434,6 +461,7 @@ GEM
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
securerandom (0.4.0)
selenium-webdriver (4.18.1)
base64 (~> 0.2)
rexml (~> 3.2, >= 3.2.5)
@ -462,21 +490,22 @@ GEM
smart_properties (1.17.0)
stimulus-rails (1.3.3)
railties (>= 6.0.0)
stringio (3.1.2)
thor (1.3.2)
thread_safe (0.3.6)
timecop (0.9.8)
timeout (0.4.2)
turbo-rails (1.5.0)
turbo-rails (2.0.11)
actionpack (>= 6.0.0)
activejob (>= 6.0.0)
railties (>= 6.0.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
uk_postcode (2.1.8)
unicode-display_width (2.5.0)
unread (0.13.1)
unread (0.14.0)
activerecord (>= 6.1)
uri (0.13.0)
useragent (0.16.11)
view_component (3.10.0)
activesupport (>= 5.2.0, < 8.0)
concurrent-ruby (~> 1.0)
@ -532,8 +561,8 @@ DEPENDENCIES
excon (~> 0.111.0)
factory_bot_rails
faker
govuk-components (~> 5.1)
govuk_design_system_formbuilder (~> 5.0)
govuk-components (~> 5.7)
govuk_design_system_formbuilder (~> 5.7)
govuk_markdown
jsbundling-rails
json-schema
@ -541,19 +570,19 @@ DEPENDENCIES
method_source (~> 1.1)
notifications-ruby-client
overcommit (>= 0.37.0)
paper_trail
paper_trail (~> 15.2)
paper_trail-globalid
parallel_tests
pg (~> 1.1)
possessive
propshaft
pry-byebug
puma (~> 5.6)
puma (~> 6.4)
pundit
rack (>= 2.2.6.3)
rack-attack
rack-mini-profiler (~> 2.0)
rails (~> 7.0.8.7)
rails (~> 7.2.2)
rails_admin (~> 3.1)
redcarpet (~> 3.6)
redis (~> 4.8)

2
app/components/bulk_upload_error_row_component.html.erb

@ -38,7 +38,7 @@
<% if potential_errors.any? %>
<h2 class="govuk-heading-m">Potential errors</h2>
<p class="govuk-body">The following groups of cells might have conflicting data. Check the answers and fix any incorrect data.<br><br>If the answers are correct, fix the critical errors and reupload the file. You'll need to confirm that the following data is correct when the file only contains potential errors.</p>
<p class="govuk-body">The following groups of cells might have conflicting data. Check the answers and fix any incorrect data.<br><br>If the answers are correct, fix the critical errors and upload the file again. You'll need to confirm that the following data is correct when the file only contains potential errors.</p>
<%= govuk_table(html_attributes: { class: "no-bottom-border" }) do |table| %>
<%= table.with_head do |head| %>
<% head.with_row do |row| %>

22
app/components/primary_navigation_component.html.erb

@ -1,17 +1,5 @@
<nav class="app-primary-navigation" aria-label="primary">
<div class="govuk-width-container">
<ul class="app-primary-navigation__list">
<% items.each do |item| %>
<% if item.current %>
<li class="app-primary-navigation__item app-primary-navigation__item--current">
<%= govuk_link_to item[:text], item[:href], class: "app-primary-navigation__link", aria: { current: "page" } %>
</li>
<% else %>
<li class="app-primary-navigation__item">
<%= govuk_link_to item[:text], item[:href], class: "app-primary-navigation__link" %>
</li>
<% end %>
<% end %>
</ul>
</div>
</nav>
<%= govuk_service_navigation(navigation_id: "primary-navigation", classes: "app-service-navigation") do |sn|
items.each do |item|
sn.with_navigation_item(text: item[:text], href: item[:href], classes: "", current: item[:current])
end
end %>

2
app/components/search_component.html.erb

@ -1,4 +1,4 @@
<%= form_with model: @user, url: path(current_user), method: "get", local: true do |f| %>
<%= form_with url: path(current_user), method: "get", local: true do |f| %>
<div class="app-search govuk-!-margin-bottom-4">
<%= f.govuk_text_field :search,
form_group: {

15
app/controllers/form_controller.rb

@ -44,6 +44,8 @@ class FormController < ApplicationController
end
end
flash[:log_data] = responses_for_page
question_ids = (@log.errors.map(&:attribute) - [:base]).uniq
flash[:pages_with_errors_count] = question_ids.map { |id| @log.form.get_question(id, @log)&.page&.id }.compact.uniq.count
redirect_to send("#{@log.class.name.underscore}_#{@page.id}_path", @log, { referrer: request.params["referrer"], original_page_id: request.params["original_page_id"], related_question_ids: request.params["related_question_ids"] })
end
else
@ -85,6 +87,7 @@ class FormController < ApplicationController
page_id = request.path.split("/")[-1].underscore
@page = form.get_page(page_id)
@subsection = form.subsection_for_page(@page)
@pages_with_errors_count = 0
if @page.routed_to?(@log, current_user) || is_referrer_type?("interruption_screen") || adding_answer_from_check_errors_page?
if updated_answer_from_check_errors_page?
@questions = request.params["related_question_ids"].map { |id| @log.form.get_question(id, @log) }
@ -93,6 +96,7 @@ class FormController < ApplicationController
if flash[:errors].present?
restore_previous_errors(flash[:errors])
restore_error_field_values(flash[:log_data])
@pages_with_errors_count = flash[:pages_with_errors_count]
end
render "form/page"
end
@ -109,8 +113,13 @@ private
def restore_error_field_values(previous_responses)
return unless previous_responses
previous_responses_to_reset = previous_responses.reject do |key, _|
@log.form.get_question(key, @log)&.type == "date"
previous_responses_to_reset = previous_responses.reject do |key, value|
if @log.form.get_question(key, @log)&.type == "date" && value.present?
year = value.split("-").first.to_i
year&.zero?
else
false
end
end
@log.assign_attributes(previous_responses_to_reset)
@ -437,7 +446,7 @@ private
@log.valid?
@log.reload
error_attributes = @log.errors.map(&:attribute)
@questions = @log.form.questions.select { |q| error_attributes.include?(q.id.to_sym) }
@questions = @log.form.questions.select { |q| error_attributes.include?(q.id.to_sym) && q.page.routed_to?(@log, current_user) }
end
render "form/check_errors"
end

2
app/controllers/lettings_logs_controller.rb

@ -22,7 +22,7 @@ class LettingsLogsController < LogsController
@total_count = all_logs.size
@unresolved_count = all_logs.unresolved.assigned_to(current_user).count
@filter_type = "lettings_logs"
@duplicate_sets_count = FeatureToggle.duplicate_summary_enabled? && !current_user.support? ? duplicate_sets_count(current_user, current_user.organisation) : 0
@duplicate_sets_count = !current_user.support? ? duplicate_sets_count(current_user, current_user.organisation) : 0
render "logs/index"
end

2
app/controllers/logs_controller.rb

@ -38,7 +38,7 @@ private
API_ACTIONS = %w[create show update].freeze
def json_api_request?
API_ACTIONS.include?(request["action"]) && request.format.json?
API_ACTIONS.include?(params["action"]) && request.format.json?
end
def authenticate

4
app/controllers/organisations_controller.rb

@ -186,7 +186,7 @@ class OrganisationsController < ApplicationController
@total_count = organisation_logs.size
@log_type = :lettings
@filter_type = "lettings_logs"
@duplicate_sets_count = FeatureToggle.duplicate_summary_enabled? ? duplicate_sets_count(current_user, @organisation) : 0
@duplicate_sets_count = duplicate_sets_count(current_user, @organisation)
render "logs", layout: "application"
end
@ -218,7 +218,7 @@ class OrganisationsController < ApplicationController
@total_count = organisation_logs.size
@log_type = :sales
@filter_type = "sales_logs"
@duplicate_sets_count = FeatureToggle.duplicate_summary_enabled? ? duplicate_sets_count(current_user, @organisation) : 0
@duplicate_sets_count = duplicate_sets_count(current_user, @organisation)
render "logs", layout: "application"
end

2
app/controllers/sales_logs_controller.rb

@ -24,7 +24,7 @@ class SalesLogsController < LogsController
@searched = search_term.presence
@total_count = all_logs.size
@filter_type = "sales_logs"
@duplicate_sets_count = FeatureToggle.duplicate_summary_enabled? && !current_user.support? ? duplicate_sets_count(current_user, current_user.organisation) : 0
@duplicate_sets_count = !current_user.support? ? duplicate_sets_count(current_user, current_user.organisation) : 0
render "logs/index"
end

12
app/controllers/sessions_controller.rb

@ -1,20 +1,20 @@
class SessionsController < ApplicationController
def clear_filters
session[session_name_for(params[:filter_type])] = "{}"
path_params = params[:path_params].presence || {}
filter_path_params = params[:filter_path_params].presence || {}
if path_params[:organisation_id].present?
redirect_to send("#{params[:filter_type]}_organisation_path", id: path_params[:organisation_id], scheme_id: path_params[:scheme_id], search: path_params[:search])
if filter_path_params[:organisation_id].present?
redirect_to send("#{params[:filter_type]}_organisation_path", id: filter_path_params[:organisation_id], scheme_id: filter_path_params[:scheme_id], search: filter_path_params[:search])
elsif params[:filter_type].include?("bulk_uploads")
bulk_upload_type = params[:filter_type].split("_").first
uploading_organisation = params[:organisation_id].presence
if uploading_organisation.present?
redirect_to send("bulk_uploads_#{bulk_upload_type}_logs_path", search: path_params[:search], uploading_organisation:)
redirect_to send("bulk_uploads_#{bulk_upload_type}_logs_path", search: filter_path_params[:search], uploading_organisation:)
else
redirect_to send("bulk_uploads_#{bulk_upload_type}_logs_path", search: path_params[:search])
redirect_to send("bulk_uploads_#{bulk_upload_type}_logs_path", search: filter_path_params[:search])
end
else
redirect_to send("#{params[:filter_type]}_path", scheme_id: path_params[:scheme_id], search: path_params[:search])
redirect_to send("#{params[:filter_type]}_path", scheme_id: filter_path_params[:scheme_id], search: filter_path_params[:search])
end
end

69
app/frontend/styles/_primary-navigation.scss

@ -1,69 +0,0 @@
.app-primary-navigation {
@include govuk-font(19, $weight: bold);
background-color: govuk-colour("light-grey");
border-bottom: 1px solid $govuk-border-colour;
}
.govuk-phase-banner + .app-primary-navigation {
margin-top: -1px;
}
.app-primary-navigation__list {
@include govuk-clearfix;
left: govuk-spacing(-3);
list-style: none;
margin: 0;
padding: 0;
position: relative;
right: govuk-spacing(-3);
width: calc(100% + #{govuk-spacing(6)});
}
.app-primary-navigation__item {
box-sizing: border-box;
display: block;
float: left;
line-height: 50px;
height: 50px;
padding: 0 govuk-spacing(3);
position: relative;
}
.app-primary-navigation__item--current {
border-bottom: $govuk-border-width-narrow solid $govuk-link-colour;
&:hover {
border-bottom-color: $govuk-link-hover-colour;
}
&:active {
border-bottom-color: $govuk-link-active-colour;
}
}
.app-primary-navigation__item--align-right {
@include govuk-media-query($from: tablet) {
float: right;
}
}
.app-primary-navigation__link {
@include govuk-link-common;
@include govuk-link-style-no-visited-state;
@include govuk-link-style-no-underline;
@include govuk-typography-weight-bold;
// Extend the touch area of the link to the list
&::after {
bottom: 0;
content: "";
left: 0;
position: absolute;
right: 0;
top: 0;
}
}
.app-primary-navigation__item--current .app-primary-navigation__link:hover {
text-decoration: none;
}

42
app/frontend/styles/application.scss

@ -45,7 +45,6 @@ $govuk-breakpoints: (
@import "task-list";
@import "template";
@import "panel";
@import "primary-navigation";
@import "search";
@import "sub-navigation";
@import "unread-notification";
@ -86,3 +85,44 @@ $govuk-breakpoints: (
.govuk-notification-banner__content > * {
max-width: fit-content;
}
.govuk-service-navigation__active-fallback,
.govuk-service-navigation__list {
font-weight: bold;
}
.govuk-service-navigation__link {
@include govuk-link-common;
@include govuk-link-style-no-visited-state;
@include govuk-link-style-no-underline;
@include govuk-typography-weight-bold;
// Extend the touch area of the link to the list
&::after {
bottom: 0;
content: "";
left: 0;
position: absolute;
right: 0;
top: 0;
}
}
.govuk-service-navigation__item--active {
border-bottom-width: 4px;
}
.govuk-service-navigation__item {
padding-right: 15px;
padding-left: 15px;
margin: 0;
}
.govuk-service-navigation__item:not(:last-child) {
margin-right: 0;
}
.govuk-service-navigation__container {
left: -15px;
position: relative;
}

4
app/helpers/filters_helper.rb

@ -165,9 +165,9 @@ module FiltersHelper
applied_filters_count(filter_type).zero? ? "No filters applied" : "#{pluralize(applied_filters_count(filter_type), 'filter')} applied"
end
def reset_filters_link(filter_type, path_params = {})
def reset_filters_link(filter_type, filter_path_params = {})
if applied_filters_count(filter_type).positive?
govuk_link_to "Clear", clear_filters_path(filter_type:, path_params:)
govuk_link_to "Clear", clear_filters_path(filter_type:, filter_path_params:)
end
end

4
app/helpers/form_page_error_helper.rb

@ -12,8 +12,4 @@ module FormPageErrorHelper
errors.each { |error| lettings_log.errors.delete(error.attribute) }
end
end
def all_questions_affected_by_errors(log)
(log.errors.map(&:attribute) - [:base]).uniq
end
end

2
app/helpers/logs_helper.rb

@ -10,7 +10,7 @@ module LogsHelper
def bulk_upload_options(bulk_upload)
array = bulk_upload ? [bulk_upload.id] : []
array.index_with { |_bulk_upload_id| "With logs from bulk upload" }
array.index_with { |_bulk_upload_id| "Only logs from this bulk upload" }
end
def search_label_for_controller(controller)

6
app/models/bulk_upload.rb

@ -1,7 +1,7 @@
class BulkUpload < ApplicationRecord
enum log_type: { lettings: "lettings", sales: "sales" }
enum rent_type_fix_status: { not_applied: "not_applied", applied: "applied", not_needed: "not_needed" }
enum failure_reason: { blank_template: "blank_template", wrong_template: "wrong_template" }
enum :log_type, { lettings: "lettings", sales: "sales" }
enum :rent_type_fix_status, { not_applied: "not_applied", applied: "applied", not_needed: "not_needed" }
enum :failure_reason, { blank_template: "blank_template", wrong_template: "wrong_template" }
belongs_to :user

2
app/models/csv_download.rb

@ -1,5 +1,5 @@
class CsvDownload < ApplicationRecord
enum download_type: { lettings: "lettings", sales: "sales", schemes: "schemes", locations: "locations", combined: "combined" }
enum :download_type, { lettings: "lettings", sales: "sales", schemes: "schemes", locations: "locations", combined: "combined" }
belongs_to :user
belongs_to :organisation

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

@ -7,7 +7,6 @@ class Form::Lettings::Questions::Offered < ::Form::Question
@check_answers_card_number = 0
@max = 150
@min = 0
@hint_text = I18n.t("hints.offered")
@step = 1
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end

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

@ -20,11 +20,13 @@ class Form::Sales::Pages::OwningOrganisation < ::Form::Page
if current_user.organisation.holds_own_stock?
return true if current_user.organisation.absorbed_organisations.any?(&:holds_own_stock?)
return true if stock_owners.count >= 1
return false if log.owning_organisation == current_user.organisation
log.update!(owning_organisation: current_user.organisation)
else
return false if stock_owners.count.zero?
return true if stock_owners.count > 1
return false if log.owning_organisation == stock_owners.first
log.update!(owning_organisation: stock_owners.first)
end

2
app/models/forms/bulk_upload_lettings_resume/fix_choice.rb

@ -14,7 +14,7 @@ module Forms
def options
[
OpenStruct.new(id: "create-fix-inline", name: "Upload these logs and fix errors on CORE site"),
OpenStruct.new(id: "upload-again", name: "Fix errors in the CSV and re-upload"),
OpenStruct.new(id: "upload-again", name: "Fix errors in the CSV and upload the file again"),
]
end

2
app/models/forms/bulk_upload_sales_resume/fix_choice.rb

@ -14,7 +14,7 @@ module Forms
def options
[
OpenStruct.new(id: "create-fix-inline", name: "Upload these logs and fix errors on CORE site"),
OpenStruct.new(id: "upload-again", name: "Fix errors in the CSV and re-upload"),
OpenStruct.new(id: "upload-again", name: "Fix errors in the CSV and upload the file again"),
]
end

7
app/models/location.rb

@ -171,7 +171,8 @@ class Location < ApplicationRecord
DUPLICATE_LOCATION_ATTRIBUTES = %w[scheme_id postcode mobility_type].freeze
LOCAL_AUTHORITIES = LocalAuthority.all.map { |la| [la.name, la.code] }.to_h
enum local_authorities: LOCAL_AUTHORITIES
attribute :local_authorities, :string
enum :local_authorities, LOCAL_AUTHORITIES
def self.local_authorities_for_current_year
LocalAuthority.all.active(Time.zone.today).england.map { |la| [la.code, la.name] }.to_h
end
@ -184,7 +185,7 @@ class Location < ApplicationRecord
"Missing": "X",
}.freeze
enum mobility_type: MOBILITY_TYPE
enum :mobility_type, MOBILITY_TYPE
TYPE_OF_UNIT = {
"Bungalow": 6,
@ -195,7 +196,7 @@ class Location < ApplicationRecord
"Shared house or hostel": 4,
}.freeze
enum type_of_unit: TYPE_OF_UNIT
enum :type_of_unit, TYPE_OF_UNIT
def self.find_by_id_on_multiple_fields(id)
return if id.nil?

18
app/models/log.rb

@ -18,14 +18,14 @@ class Log < ApplicationRecord
"pending" => 3,
"deleted" => 4,
}.freeze
enum status: STATUS
enum status_cache: STATUS, _prefix: true
enum :status, STATUS
enum :status_cache, STATUS, prefix: true
CREATION_METHOD = {
"single log" => 1,
"bulk upload" => 2,
}.freeze
enum creation_method: CREATION_METHOD, _prefix: true
enum :creation_method, CREATION_METHOD, prefix: true
scope :visible, -> { where(status: %w[not_started in_progress completed]) }
scope :exportable, -> { where(status: %w[not_started in_progress completed deleted]) }
@ -317,7 +317,11 @@ private
def update_status!
return if skip_update_status
self.status = calculate_status
if status == "pending"
self.status_cache = calculate_status
else
self.status = calculate_status
end
end
def all_subsections_completed?
@ -373,14 +377,14 @@ private
end
def reset_location_fields!
reset_location(is_la_inferred, "la", "is_la_inferred", "postcode_full", 1)
reset_log_location(is_la_inferred, "la", "is_la_inferred", "postcode_full", 1)
end
def reset_previous_location_fields!
reset_location(is_previous_la_inferred, "prevloc", "is_previous_la_inferred", "ppostcode_full", previous_la_known)
reset_log_location(is_previous_la_inferred, "prevloc", "is_previous_la_inferred", "ppostcode_full", previous_la_known)
end
def reset_location(is_inferred, la_key, is_inferred_key, postcode_key, is_la_known)
def reset_log_location(is_inferred, la_key, is_inferred_key, postcode_key, is_la_known)
if is_inferred || is_la_known != 1
self[la_key] = nil
end

4
app/models/merge_request.rb

@ -13,7 +13,9 @@ class MergeRequest < ApplicationRecord
request_merged: "request_merged",
deleted: "deleted",
}.freeze
enum status: STATUS
attribute :status, :string
enum :status, STATUS
scope :not_merged, -> { where(request_merged: [false, nil]) }
scope :merged, -> { where(request_merged: true) }

2
app/models/organisation.rb

@ -53,7 +53,7 @@ class Organisation < ApplicationRecord
PRP: 2,
}.freeze
enum provider_type: PROVIDER_TYPE
enum :provider_type, PROVIDER_TYPE
alias_method :la?, :LA?

18
app/models/scheme.rb

@ -145,7 +145,7 @@ class Scheme < ApplicationRecord
Yes: 1,
}.freeze
enum sensitive: SENSITIVE, _suffix: true
enum :sensitive, SENSITIVE, suffix: true
REGISTERED_UNDER_CARE_ACT = {
"Yes – registered care home providing nursing care": 4,
@ -154,7 +154,7 @@ class Scheme < ApplicationRecord
"No": 1,
}.freeze
enum registered_under_care_act: REGISTERED_UNDER_CARE_ACT
enum :registered_under_care_act, REGISTERED_UNDER_CARE_ACT
SCHEME_TYPE = {
"Direct Access Hostel": 5,
@ -164,7 +164,7 @@ class Scheme < ApplicationRecord
"Missing": 0,
}.freeze
enum scheme_type: SCHEME_TYPE, _suffix: true
enum :scheme_type, SCHEME_TYPE, suffix: true
SUPPORT_TYPE = {
"Missing": 0,
@ -175,7 +175,7 @@ class Scheme < ApplicationRecord
"Floating support": 6,
}.freeze
enum support_type: SUPPORT_TYPE, _suffix: true
enum :support_type, SUPPORT_TYPE, suffix: true
PRIMARY_CLIENT_GROUP = {
"Homeless families with support needs": "O",
@ -197,8 +197,8 @@ class Scheme < ApplicationRecord
"Missing": "X",
}.freeze
enum primary_client_group: PRIMARY_CLIENT_GROUP, _suffix: true
enum secondary_client_group: PRIMARY_CLIENT_GROUP, _suffix: true
enum :primary_client_group, PRIMARY_CLIENT_GROUP, suffix: true
enum :secondary_client_group, PRIMARY_CLIENT_GROUP, suffix: true
INTENDED_STAY = {
"Very short stay": "V",
@ -213,8 +213,8 @@ class Scheme < ApplicationRecord
Yes: 1,
}.freeze
enum intended_stay: INTENDED_STAY, _suffix: true
enum has_other_client_group: HAS_OTHER_CLIENT_GROUP, _suffix: true
enum :intended_stay, INTENDED_STAY, suffix: true
enum :has_other_client_group, HAS_OTHER_CLIENT_GROUP, suffix: true
ARRANGEMENT_TYPE = {
"The same organisation that owns the housing stock": "D",
@ -226,7 +226,7 @@ class Scheme < ApplicationRecord
DUPLICATE_SCHEME_ATTRIBUTES = %w[scheme_type registered_under_care_act primary_client_group secondary_client_group has_other_client_group support_type intended_stay].freeze
enum arrangement_type: ARRANGEMENT_TYPE, _suffix: true
enum :arrangement_type, ARRANGEMENT_TYPE, suffix: true
def self.find_by_id_on_multiple_fields(scheme_id, location_id)
return if scheme_id.nil?

2
app/models/user.rb

@ -56,7 +56,7 @@ class User < ApplicationRecord
unassign: "No, unassign the logs",
}.freeze
enum role: ROLES
enum :role, ROLES
scope :search_by_name, ->(name) { where("users.name ILIKE ?", "%#{name}%") }
scope :search_by_email, ->(email) { where("email ILIKE ?", "%#{email}%") }

2
app/models/validations/property_validations.rb

@ -41,6 +41,8 @@ module Validations::PropertyValidations
def validate_property_postcode(record)
postcode = record.postcode_full
return unless postcode
if record.postcode_known? && (postcode.blank? || !postcode.match(POSTCODE_REGEXP))
error_message = I18n.t("validations.lettings.property.postcode_full.invalid")
record.errors.add :postcode_full, :wrong_format, message: error_message

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

@ -31,6 +31,8 @@ module Validations::Sales::PropertyValidations
def validate_property_postcode(record)
postcode = record.postcode_full
return unless postcode
if record.postcode_known? && (postcode.blank? || !postcode.match(POSTCODE_REGEXP))
error_message = I18n.t("validations.sales.property_information.postcode_full.invalid")
record.errors.add :postcode_full, :wrong_format, message: error_message

4
app/models/validations/soft_validations.rb

@ -84,6 +84,8 @@ module Validations::SoftValidations
end
def all_tenants_age_and_gender_information_completed?
return false if hhmemb.present? && hhmemb > 8
person_count = hhmemb || 8
(1..person_count).all? do |n|
@ -235,6 +237,8 @@ private
end
def all_male_tenants_in_the_household?
return false if hhmemb.present? && hhmemb > 8
person_count = hhmemb || 8
(1..person_count).all? do |n|

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

@ -16,9 +16,7 @@ class BulkUpload::Lettings::LogCreator
row_parser.log.blank_invalid_non_setup_fields!
row_parser.log.bulk_upload = bulk_upload
row_parser.log.creation_method = "bulk upload"
row_parser.log.skip_update_status = true
row_parser.log.status = "pending"
row_parser.log.status_cache = row_parser.log.calculate_status
begin
row_parser.log.save!

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

@ -15,9 +15,7 @@ class BulkUpload::Sales::LogCreator
row_parser.log.blank_invalid_non_setup_fields!
row_parser.log.bulk_upload = bulk_upload
row_parser.log.creation_method = "bulk upload"
row_parser.log.skip_update_status = true
row_parser.log.status = "pending"
row_parser.log.status_cache = row_parser.log.calculate_status
begin
row_parser.log.save!

16
app/services/feature_toggle.rb

@ -11,10 +11,6 @@ class FeatureToggle
!Rails.env.development?
end
def self.duplicate_summary_enabled?
true
end
def self.service_unavailable?
false
end
@ -23,18 +19,6 @@ class FeatureToggle
false
end
def self.delete_scheme_enabled?
true
end
def self.delete_location_enabled?
true
end
def self.delete_user_enabled?
true
end
def self.local_storage?
Rails.env.development?
end

21
app/views/bulk_upload_lettings_logs/forms/prepare_your_file_2024.html.erb

@ -19,22 +19,17 @@
<h2 class="govuk-heading-s">Create your file</h2>
<ul class="govuk-list govuk-list--bullet">
<li>Fill in the template with data from your housing management system. Your data should go below the headers, with one row per log. Leave column A blank - the bulk upload fields start in column B.</li>
<li>Make sure each column of your data aligns with the matching headers above. You may need to reorder your data.</li>
<li>Use the <%= govuk_link_to "Lettings bulk upload Specification (2024 to 2025)", @form.specification_path %> to check your data is in the correct format.</li>
<li><strong>Username field:</strong> To assign a log to someone else, enter the email address they use to log into CORE.</li>
<li>If you have reordered the headers, keep the headers in the file.</li>
</ul>
<%= govuk_inset_text(text: "You can upload both general needs and supported housing logs in the same file for 2024 to 2025 data.") %>
<%= govuk_list [
"Fill in the template with data from your housing management system. Your data should go below the headers, with one row per log. Leave column A blank - the bulk upload fields start in column B.",
"Make sure each column of your data aligns with the matching headers above. You may need to reorder your data.",
"Use the #{govuk_link_to 'Lettings bulk upload Specification (2024 to 2025)', @form.specification_path} to check your data is in the correct format.".html_safe,
"<strong>Username field:</strong> To assign a log to someone else, enter the email address they use to log into CORE.".html_safe,
"If you have reordered the headers, keep the headers in the file.",
], type: :bullet %>
<h2 class="govuk-heading-s">Save your file</h2>
<ul class="govuk-list govuk-list--bullet">
<li>Save your file as a CSV.</li>
<li>Your file should now be ready to upload.</li>
</ul>
<%= govuk_list ["Save your file as a CSV.", "Your file should now be ready to upload."], type: :bullet %>
<%= f.govuk_submit class: "govuk-!-margin-top-7" %>
<% end %>

2
app/views/bulk_upload_lettings_resume/deletion_report.html.erb

@ -28,6 +28,6 @@
<div class="govuk-button-group">
<%= form_with model: @form, scope: :form, url: page_bulk_upload_lettings_resume_path(@bulk_upload, "confirm"), method: :patch do |f| %>
<%= f.govuk_submit "Clear this data and upload the logs" %>
<%= govuk_button_link_to "I have fixed these errors and I want to reupload the file", start_bulk_upload_lettings_logs_path, secondary: true %>
<%= govuk_button_link_to "I have fixed these errors and I want to upload the file again", start_bulk_upload_lettings_logs_path, secondary: true %>
<% end %>
</div>

12
app/views/bulk_upload_lettings_resume/fix_choice.html.erb

@ -24,17 +24,9 @@
<%= govuk_details(summary_text: "How to choose between fixing errors on the CORE site or in the CSV") do %>
<p class="govuk-body govuk-!-margin-bottom-2">You may find it easier to fix the errors in the CSV file if:</p>
<ul class="govuk-list govuk-list--bullet">
<li>you have a lot of errors</li>
<li>the CSV file is formatted incorrectly and you can see where the errors are</li>
<li>you need to fix multiple errors at once</li>
</ul>
<%= govuk_list ["you have a lot of errors", "the CSV file is formatted incorrectly and you can see where the errors are", "you need to fix multiple errors at once"], type: :bullet %>
<p class="govuk-body govuk-!-margin-bottom-2">You may find it easier to fix the errors on the CORE site if:</p>
<ul class="govuk-list govuk-list--bullet">
<li>you need to see the data in context</li>
<li>you have a smaller file, with a few errors</li>
<li>you are not sure where the errors are</li>
</ul>
<%= govuk_list ["you need to see the data in context", "you have a smaller file, with a few errors", "you are not sure where the errors are"], type: :bullet %>
<% end %>
<%= f.govuk_collection_radio_buttons :choice,

2
app/views/bulk_upload_lettings_soft_validations_check/confirm.html.erb

@ -5,7 +5,7 @@
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<span class="govuk-caption-l">Upload lettings logs in bulk (<%= @bulk_upload.year_combo %>)</span>
<h1 class="govuk-heading-l">You have chosen to upload all logs from this bulk upload.</h1>
<h1 class="govuk-heading-l">Are you sure you want to upload all logs from this bulk upload?</h1>
<p class="govuk-body"><%= logs_and_soft_validations_warning(@bulk_upload) %></p>

19
app/views/bulk_upload_sales_logs/forms/prepare_your_file_2024.html.erb

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

2
app/views/bulk_upload_sales_resume/deletion_report.html.erb

@ -28,6 +28,6 @@
<div class="govuk-button-group">
<%= form_with model: @form, scope: :form, url: page_bulk_upload_sales_resume_path(@bulk_upload, "confirm"), method: :patch do |f| %>
<%= f.govuk_submit "Clear this data and upload the logs" %>
<%= govuk_button_link_to "I have fixed these errors and I want to reupload the file", start_bulk_upload_sales_logs_path, secondary: true %>
<%= govuk_button_link_to "I have fixed these errors and I want to upload the file again", start_bulk_upload_sales_logs_path, secondary: true %>
<% end %>
</div>

14
app/views/bulk_upload_sales_resume/fix_choice.html.erb

@ -23,18 +23,10 @@
</div>
<%= govuk_details(summary_text: "How to choose between fixing errors on the CORE site or in the CSV") do %>
<p class="govuk-body govuk-!-margin-bottom-2">You may find it easier to fix the errors in the CSV file if:</p>
<ul class="govuk-list govuk-list--bullet">
<li>you have a lot of errors</li>
<li>the CSV file is formatted incorrectly and you can see where the errors are</li>
<li>you need to fix multiple errors at once</li>
</ul>
<p class="govuk-body govuk-!-margin-bottom-2">You may find it easier to fix the errors in the CSV file if:</p>
<%= govuk_list ["you have a lot of errors", "the CSV file is formatted incorrectly and you can see where the errors are", "you need to fix multiple errors at once"], type: :bullet %>
<p class="govuk-body govuk-!-margin-bottom-2">You may find it easier to fix the errors on the CORE site if:</p>
<ul class="govuk-list govuk-list--bullet">
<li>you need to see the data in context</li>
<li>you have a smaller file, with a few errors</li>
<li>you are not sure where the errors are</li>
</ul>
<%= govuk_list ["you need to see the data in context", "you have a smaller file, with a few errors", "you are not sure where the errors are"], type: :bullet %>
<% end %>
<%= f.govuk_collection_radio_buttons :choice,

2
app/views/bulk_upload_sales_soft_validations_check/confirm.html.erb

@ -5,7 +5,7 @@
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<span class="govuk-caption-l">Upload sales logs in bulk (<%= @bulk_upload.year_combo %>)</span>
<h1 class="govuk-heading-l">You have chosen to upload all logs from this bulk upload.</h1>
<h1 class="govuk-heading-l">Are you sure you want to upload all logs from this bulk upload?</h1>
<p class="govuk-body"><%= logs_and_soft_validations_warning(@bulk_upload) %></p>

4
app/views/bulk_upload_shared/_moved_user_banner.html.erb

@ -4,9 +4,9 @@
This error report is out of date.
<p>
<% if current_user.id == @bulk_upload.moved_user_id %>
You moved to a different organisation since this file was uploaded. Reupload the file to get an accurate error report.
You moved to a different organisation since this file was uploaded. Upload the file again to get an accurate error report.
<% else %>
Some logs in this upload are assigned to <%= @bulk_upload.moved_user_name %>, who has moved to a different organisation since this file was uploaded. Reupload the file to get an accurate error report.
Some logs in this upload are assigned to <%= @bulk_upload.moved_user_name %>, who has moved to a different organisation since this file was uploaded. Upload the file again to get an accurate error report.
<% end %>
<% end %>
<% end %>

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

@ -34,12 +34,7 @@
<p class="govuk-body">The bulk upload templates contain 8 rows of ‘headers’ with information about how to fill in the template, including:</p>
<% end %>
<ul class="govuk-list govuk-list--bullet">
<li>the CORE form questions and their field numbers</li>
<li>each field’s valid responses</li>
<li>if/when certain fields can be left blank</li>
<li>which fields are used to check for duplicate logs</li>
</ul>
<%= govuk_list ["the CORE form questions and their field numbers", "each field’s valid responses", "if/when certain fields can be left blank", "which fields are used to check for duplicate logs"], type: :bullet %>
<p class="govuk-body">You can paste your data below the headers or copy the headers and insert them above the data in your file. The bulk upload fields start at column B. Leave column A blank.</p>
<p class="govuk-body">Make sure that each column of data aligns with the corresponding question in the headers. We recommend ordering your data to match the headers, but you can also reorder the headers to match your data. When processing the file, we check what each column of data represents based on the headers above.</p>
@ -80,7 +75,7 @@
<p class="govuk-body">Once you've saved your CSV file, you can upload it via a button at the top of the lettings and sales logs pages.</p>
<p class="govuk-body">When your file is done processing, you will receive an email explaining your next steps. If all your data is valid, your logs will be created. If some data is invalid, you’ll receive an email with instructions about how to resolve the errors.</p>
<p class="govuk-body">If your file has errors on fields 1 through 15 for lettings, or 1 through 18 for sales, you must fix these in the CSV. This is because we need to know these answers to validate the rest of the data. Any errors in these fields will be featured in the error report’s summary tab.</p>
<p class="govuk-body">If none of your errors are in fields 1 through 15 for lettings, or 1 through 18 for sales, you can choose how to fix the errors. You can either fix them in the CSV and reupload, or create partially complete logs and answer the remaining questions on the CORE site. Any errors that affect a significant number of logs will be featured in the error report’s summary tab to help you decide.</p>
<p class="govuk-body">If none of your errors are in fields 1 through 15 for lettings, or 1 through 18 for sales, you can choose how to fix the errors. You can either fix them in the CSV and upload the file again, or create partially complete logs and answer the remaining questions on the CORE site. Any errors that affect a significant number of logs will be featured in the error report’s summary tab to help you decide.</p>
<p class="govuk-body"></p>
<% end %>

6
app/views/cookies/show.html.erb

@ -37,11 +37,7 @@
<p>Google is not allowed to use or share our analytics data with anyone.</p>
<p>Google Analytics stores anonymised information about:</p>
<ul class="govuk-list govuk-list--bullet">
<li>how you got to the service</li>
<li>the pages you visit on the service and how long you spend on them</li>
<li>any errors you see while using the service</li>
</ul>
<%= govuk_list ["how you got to the service", "the pages you visit on the service and how long you spend on them", "any errors you see while using the service"], type: :bullet %>
<table class="govuk-table">
<caption class="govuk-visually-hidden">Google Analytics cookies</caption>

2
app/views/devise/sessions/new.html.erb

@ -22,7 +22,7 @@
<%= f.govuk_password_field :password,
autocomplete: "current-password" %>
<%= f.hidden_field :start, value: request["start"] %>
<%= f.hidden_field :start, value: request.params["start"] %>
<%= f.govuk_submit "Sign in" %>
</div>
</div>

3
app/views/form/page.html.erb

@ -16,7 +16,6 @@
<%= form_with model: @log, url: request.original_url, method: "post", local: true do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<% all_questions_with_errors = all_questions_affected_by_errors(@log) %>
<% remove_other_page_errors(@log, @page) %>
<%= f.govuk_error_summary %>
@ -76,7 +75,7 @@
<%= f.hidden_field :check_errors, value: @check_errors %>
<% end %>
<% if all_questions_with_errors.count > 1 %>
<% if @pages_with_errors_count > 1 %>
<div class="govuk-button-group">
<%= f.submit "See all related answers", name: "check_errors", class: "govuk-body govuk-link submit-button-link" %>
</div>

2
app/views/layouts/application.html.erb

@ -103,7 +103,7 @@
<% feedback_link = govuk_link_to "giving us your feedback (opens in a new tab)", t("feedback_form"), rel: "noreferrer noopener", target: "_blank" %>
<%= govuk_phase_banner(
classes: "govuk-width-container",
classes: "#{current_user.present? ? 'no-bottom-border ' : ''}govuk-width-container",
tag: govuk_phase_banner_tag(current_user),
text: "This is a new service – help us improve it by #{feedback_link}".html_safe,
) %>

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

@ -42,7 +42,7 @@
<% if LocationPolicy.new(current_user, @location).create? %>
<div class="govuk-button-group">
<%= govuk_button_to "Save and return to locations", scheme_location_confirm_path(@scheme, @location, route: params[:route]), method: :patch %>
<% if LocationPolicy.new(current_user, @location).delete? && FeatureToggle.delete_location_enabled? %>
<% if LocationPolicy.new(current_user, @location).delete? %>
<%= delete_location_link(@location) %>
<% end %>
<%= govuk_button_link_to "Cancel", scheme_locations_path(@scheme), secondary: true %>

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

@ -51,6 +51,6 @@
<%= toggle_location_link(@location) %>
<% end %>
<% if LocationPolicy.new(current_user, @location).delete? && FeatureToggle.delete_location_enabled? %>
<% if LocationPolicy.new(current_user, @location).delete? %>
<%= delete_location_link(@location) %>
<% end %>

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

@ -15,15 +15,7 @@
<p class="govuk-body govuk-!-margin-bottom-2">
<%= @duplicate_logs.count == 1 ? "This log" : "These logs" %> will be deleted:
</p>
<ul class="govuk-list govuk-!-margin-bottom-6">
<% @duplicate_logs.each do |duplicate_log| %>
<li>
<strong>
<%= govuk_link_to "Log #{duplicate_log.id}", url_for(duplicate_log) %>
</strong>
</li>
<% end %>
</ul>
<%= govuk_list(@duplicate_logs.map { |log| "<strong>#{govuk_link_to "Log #{log.id}", url_for(log)}</strong>".html_safe }) %>
<div class="govuk-button-group">
<%= govuk_button_to @duplicate_logs.count == 1 ? "Delete this log" : "Delete these logs",

30
app/views/merge_requests/merge_request.html.erb

@ -14,13 +14,13 @@
<h2 class="govuk-heading-m">Before you start</h2>
<p class="govuk-body">Regardless of the merge type, you’ll be asked for:</p>
<ul class="govuk-list govuk-list--bullet">
<li>the organisations merging in CORE</li>
<li>the merge type: for example one organisation absorbing the rest, or them all creating a new organisation</li>
<li>to confirm or update the telephone number</li>
<li>the merge date</li>
<li>if user email addresses will change with this merge</li>
</ul>
<%= govuk_list [
"the organisations merging in CORE",
"the merge type: for example one organisation absorbing the rest, or them all creating a new organisation",
"to confirm or update the telephone number",
"the merge date",
"if user email addresses will change with this merge",
], type: :bullet %>
<h2 class="govuk-heading-m">If email addresses are changing</h2>
<%= govuk_inset_text(text: "Update all user email addresses on CORE as soon as they change, so that everyone can still access their CORE account. ") %>
@ -30,14 +30,14 @@
<h2 class="govuk-heading-m">If merging into a new organisation</h2>
<p class="govuk-body">You'll also be asked for the new organisation’s:</p>
<ul class="govuk-list govuk-list--bullet">
<li>name</li>
<li>address</li>
<li>telephone number</li>
<li>housing provider type</li>
<li>Regulator of Social Housing registration number</li>
<li>held stock details</li>
</ul>
<%= govuk_list [
"name",
"address",
"telephone number",
"housing provider type",
"Regulator of Social Housing registration number",
"held stock details",
], type: :bullet %>
<%= govuk_warning_text text: "You will not be able to submit your request without the above information. Do not start the form until you have obtained all of the information. " %>

12
app/views/organisation_relationships/add_managing_agent.html.erb

@ -28,12 +28,10 @@
<%= govuk_button_link_to("Cancel", managing_agents_organisation_path(@organisation), secondary: true) %>
</div>
<%= govuk_details(summary_text: "Can't find the managing agent you're looking for?") do %>
<ul class="govuk-list govuk-list--bullet">
<li>Double check the spelling and try again</li>
<li>Type the first few letters to see the suggestions</li>
<li>If you still can't find it,
<%= govuk_link_to("contact the MHCLG service desk", GlobalConstants::HELPDESK_URL, rel: "noreferrer noopener", target: "_blank") %>
</li>
</ul>
<%= govuk_list [
"Double check the spelling and try again",
"Type the first few letters to see the suggestions",
"If you still can't find it, #{govuk_link_to('contact the MHCLG service desk', GlobalConstants::HELPDESK_URL, rel: 'noreferrer noopener', target: '_blank')}",
], type: :bullet %>
<% end %>
<% end %>

12
app/views/organisation_relationships/add_stock_owner.html.erb

@ -28,12 +28,10 @@
<%= govuk_button_link_to("Cancel", stock_owners_organisation_path(@organisation), secondary: true) %>
</div>
<%= govuk_details(summary_text: "Can't find the stock owner you're looking for?") do %>
<ul class="govuk-list govuk-list--bullet">
<li>Double check the spelling and try again</li>
<li>Type the first few letters to see the suggestions</li>
<li>If you still can't find it,
<%= govuk_link_to("contact the MHCLG service desk", GlobalConstants::HELPDESK_URL, rel: "noreferrer noopener", target: "_blank") %>
</li>
</ul>
<%= govuk_list [
"Double check the spelling and try again",
"Type the first few letters to see the suggestions",
"If you still can't find it, #{govuk_link_to('contact the MHCLG service desk', GlobalConstants::HELPDESK_URL, rel: 'noreferrer noopener', target: '_blank')}",
], type: :bullet %>
<% end %>
<% end %>

65
app/views/organisations/data_sharing_agreement.html.erb

@ -35,30 +35,26 @@
<p class="govuk-body-m"><strong>It is now agreed</strong> as follows:</p>
<h3 id="2-definitions-and-interpretation" class="govuk-heading-m">2. Definitions and interpretation</h3>
<p class="govuk-body-m">2.1. In this Agreement the following words and phrases shall have the following meanings, unless expressly stated to the contrary:</p>
<ul class="govuk-list govuk-list--bullet">
<li>“Act” means the Data Protection Act 2018;</li>
<li>“Authorised Representatives” means the nominated lead officer representing each of the
parties with delegated authority to handle the day-to-day matters arising from this Agreement;</li>
<li>“Data Subject” means social housing lettings tenants and participants in discounted sales where their data is reported via the CORE system.</li>
<li>“Data Controller” has the meaning in Article 4(7) of the GDPR and section 5(2) of the Act.</li>
<li>“Data Processor” has the meaning in Article 4(8) of the GDPR.</li>
<li>“Data Protection Legislation” means the Data Protection Act 2018 and all applicable laws and regulations relating to the processing of personal data and privacy, including where applicable the guidance and codes of practice issued by the Information Commissioner; it includes the General Data Protection Regulation (GDPR).</li>
<li>“Data” means the data supplied by the CORE data providers via the CORE system and the data that is calculated or derived via the CORE system based on that initial data;</li>
<li>“GDPR” means the General Data Protection Regulation.</li>
<li>“Parties” means the parties to this Agreement, namely MHCLG and the CORE data providers. CORE data providers include social housing providers and managing organisations that provide data on behalf of the social housing providers.</li>
<li>“Personal Data” has the meaning in Article 4(1) of the GDPR. “Processing” has the meaning in Article 4(2) of the GDPR.</li>
<li>“Request for Information” means a request for information or a request under the Freedom of Information Act 2000.</li>
<li>“Special category personal data” has the meaning in Article 9(1) of the GDPR. In this Agreement:
<ul class="govuk-list govuk-list--bullet">
<li>A. The masculine includes the feminine and neuter;</li>
<li>B. Person means a natural person;</li>
<li>C. The singular includes the plural and vice versa;</li>
<li>D. A reference to any statute, enactment, order, regulation or other similar instrument
shall be construed as a reference to the statute, enactment, order, regulation or instrument as amended by any subsequent statute, enactment, order, regulation or instrument or as contained in any subsequent re-enactment.</li>
</ul></li>
</ul>
<%= govuk_list [
"“Act” means the Data Protection Act 2018;",
"“Authorised Representatives” means the nominated lead officer representing each of the parties with delegated authority to handle the day-to-day matters arising from this Agreement;",
"“Data Subject” means social housing lettings tenants and participants in discounted sales where their data is reported via the CORE system.",
"“Data Controller” has the meaning in Article 4(7) of the GDPR and section 5(2) of the Act.",
"“Data Processor” has the meaning in Article 4(8) of the GDPR.",
"“Data Protection Legislation” means the Data Protection Act 2018 and all applicable laws and regulations relating to the processing of personal data and privacy, including where applicable the guidance and codes of practice issued by the Information Commissioner; it includes the General Data Protection Regulation (GDPR).",
"“Data” means the data supplied by the CORE data providers via the CORE system and the data that is calculated or derived via the CORE system based on that initial data;",
"“GDPR” means the General Data Protection Regulation.",
"“Parties” means the parties to this Agreement, namely MHCLG and the CORE data providers. CORE data providers include social housing providers and managing organisations that provide data on behalf of the social housing providers.",
"“Personal Data” has the meaning in Article 4(1) of the GDPR. “Processing” has the meaning in Article 4(2) of the GDPR.",
"“Request for Information” means a request for information or a request under the Freedom of Information Act 2000.",
"“Special category personal data” has the meaning in Article 9(1) of the GDPR. In this Agreement:
#{ govuk_list([
'A. The masculine includes the feminine and neuter;',
'B. Person means a natural person;',
'C. The singular includes the plural and vice versa;',
'D. A reference to any statute, enactment, order, regulation or other similar instrument shall be construed as a reference to the statute, enactment, order, regulation or instrument as amended by any subsequent statute, enactment, order, regulation or instrument or as contained in any subsequent re-enactment.',
], type: :bullet)}".html_safe,
], type: :bullet %>
<p class="govuk-body-m">2.2. Headings are included in this Agreement for ease of reference only and shall not affect the interpretation or construction of this Agreement.</p>
<p class="govuk-body-m">2.3. References in this Agreement to Clauses, Paragraphs and Annexes are, unless otherwise provided, references to the Clauses, Paragraphs and Annexes of this Agreement.</p>
<p class="govuk-body-m">2.4. In the event and to the extent only of any conflict or inconsistency between the provisions of this Agreement and the provisions of any document referred to or referenced herein, the provisions of this Agreement shall prevail.</p>
@ -95,11 +91,13 @@
<p class="govuk-body-m">8.5. All work carried out by MHCLG will follow appropriate security measures and procedures to ensure the protection of the data.</p>
<h3 id="9-protection-of-personal-data" class="govuk-heading-m">9. Protection of personal data</h3>
<p class="govuk-body-m">9.1. CORE data providers and MHCLG agree that they shall:</p>
<ul class="govuk-list govuk-list--bullet">
<li>A. Implement appropriate technical and organisational measures to protect the Personal Data against unauthorised or unlawful Processing and against accidental loss, destruction, damage, alteration or disclosure. These measures shall ensure a level of security appropriate to the harm which might result from any unauthorised or unlawful Processing, accidental loss, destruction or damage to the Personal Data and having regard to the nature of the Personal Data which is to be protected;</li>
<li>B. Take reasonable steps to ensure the reliability of any personnel who have access to the Personal Data. MHCLG and Data providers will ensure such personnel will be a limited number of analysts assigned to the data collection.</li>
</ul>
<%= govuk_list [
"A. Implement appropriate technical and organisational measures to protect the Personal Data against unauthorised or unlawful Processing and against accidental loss, destruction, damage, alteration or disclosure. These measures shall ensure a level of security appropriate to the harm which might result from any unauthorised or unlawful Processing, accidental loss, destruction or damage to the Personal Data and having regard to the nature of the Personal Data which is to be protected;",
"B. Take reasonable steps to ensure the reliability of any personnel who have access to the Personal Data. MHCLG and Data providers will ensure such personnel will be a limited number of analysts assigned to the data collection.",
],
type: :bullet %>
<p class="govuk-body-m">9.2. The data providers and MHCLG shall comply at all times with the Data Protection Legislation and shall ensure that they each perform their obligations under this agreement in full compliance with the Data Protection Legislation and any other applicable law, in particular the Human Rights Act 1998 and the common law duty of confidentiality.</p>
<p class="govuk-body-m">9.3. CORE data providers should limit access to CORE to a small number of individuals who can be named on request. CORE access is limited to registered users only via password, but it is the responsibility of the CORE data providers to ensure that all individuals granted access to the datasets should be briefed on the legal requirements around handling and storing the Data from CORE.</p>
<h3 id="10-freedom-of-information" class="govuk-heading-m">10. Freedom of information</h3>
@ -128,15 +126,10 @@
<p class="govuk-body-m">16.1. The Parties shall comply with all relevant legislation, regulations, orders, statutory instruments and any amendments or re-enactments thereof from the commencement of this agreement.</p>
<p class="govuk-body-m">As witness of which the parties have set their hands on the day and year first above written
signed for and on behalf of the Data Protection Officer for <%= org_name_for_data_sharing_agreement(@data_protection_confirmation, current_user) %>, by:</p>
<ul class="govuk-list govuk-list--bullet">
<li>Name: <%= name_for_data_sharing_agreement(@data_protection_confirmation, current_user) %></li>
<li>Title: Data Protection Officer</li>
</ul>
<%= govuk_list ["Name: #{name_for_data_sharing_agreement(@data_protection_confirmation, current_user)}".html_safe, "Title: Data Protection Officer"], type: :bullet %>
<p class="govuk-body-m">SIGNED for and on behalf of the deputy director of the data, analytics &amp; statistics in the Ministry of Housing, Communities and Local Government, by:</p>
<ul class="govuk-list govuk-list--bullet">
<li>Name: Sandra Tudor</li>
<li>Title: Deputy Director</li>
</ul>
<%= govuk_list ["Name: Sandra Tudor", "Title: Deputy Director"], type: :bullet %>
<% if current_user.is_dpo? && !(@organisation.data_protection_confirmed?) %>
<div class="govuk-button-group govuk-!-margin-top-9">

53
app/views/organisations/duplicate_schemes.html.erb

@ -29,11 +29,11 @@
<p class="govuk-body">Since your organisation recently merged, we’ve reviewed your schemes for possible duplicates.</p>
<p class="govuk-body">These sets of schemes and locations might be duplicates because they have the same answers for certain fields.</p>
<h2 class="govuk-heading-m">What you need to do</h2>
<ul class="govuk-list govuk-list--bullet">
<li>Review each set of schemes or locations and decide if they are duplicates.</li>
<li>If they are, choose one to keep and deactivate the others on the date your organisation merged.</li>
<li>When you have resolved all duplicates, confirm below.</li>
</ul>
<%= govuk_list [
"Review each set of schemes or locations and decide if they are duplicates.",
"If they are, choose one to keep and deactivate the others on the date your organisation merged.",
"When you have resolved all duplicates, confirm below.",
], type: :bullet %>
<p class="govuk-body">If you need help with this, <%= govuk_link_to "contact the helpdesk (opens in a new tab)", GlobalConstants::HELPDESK_URL, target: "#" %>.</p>
<% if @duplicate_schemes.any? %>
@ -43,17 +43,17 @@
<p class="govuk-body">
These schemes have the same answers for the following fields:
</p>
<ul class="govuk-list govuk-list--bullet">
<li>Type of scheme</li>
<li>Registered under Care Standards Act 2000</li>
<li>Housing stock owned by</li>
<li>Support services provided by</li>
<li>Primary client group</li>
<li>Has another client group</li>
<li>Secondary client group</li>
<li>Level of support given</li>
<li>Intended length of stay</li>
</ul>
<%= govuk_list [
"Type of scheme",
"Registered under Care Standards Act 2000",
"Housing stock owned by",
"Support services provided by",
"Primary client group",
"Has another client group",
"Secondary client group",
"Level of support given",
"Intended length of stay",
], type: :bullet %>
<% end %>
<p class="govuk-body">The links below open in a new tab.</p>
@ -68,13 +68,7 @@
<% @duplicate_schemes.each do |duplicate_set| %>
<% body.with_row do |row| %>
<% row.with_cell do %>
<ol class="govuk-list govuk-list--number">
<% duplicate_set.each do |scheme| %>
<li>
<%= govuk_link_to scheme.service_name, scheme, target: "#" %>
</li>
<% end %>
</ol>
<%= govuk_list duplicate_set.map { |scheme| govuk_link_to(scheme.service_name, scheme, target: "#") }, type: :number %>
<% end %>
<% end %>
<% end %>
@ -89,10 +83,7 @@
<p class="govuk-body">
These locations belong to the same scheme and have the same answers for the following fields:
</p>
<ul class="govuk-list govuk-list--bullet">
<li>Postcode</li>
<li>Mobility standards</li>
</ul>
<%= govuk_list ["Postcode", "Mobility standards"], type: :bullet %>
<% end %>
<p class="govuk-body">The links below open in a new tab.</p>
@ -108,13 +99,7 @@
<% @duplicate_locations.each do |duplicate_set| %>
<% body.with_row do |row| %>
<% row.with_cell do %>
<ol class="govuk-list govuk-list--number">
<% duplicate_set[:locations].each do |location| %>
<li>
<%= govuk_link_to location.name, scheme_location_path(location), target: "#" %>
</li>
<% end %>
</ol>
<%= govuk_list duplicate_set[:locations].map { |location| govuk_link_to(location.name, scheme_location_path(location), target: "#") }, type: :number %>
<% end %>
<% row.with_cell do %>
<%= govuk_link_to duplicate_set[:scheme].service_name, duplicate_set[:scheme], target: "#" %>

10
app/views/schemes/changes.html.erb

@ -15,11 +15,11 @@
<p class="govuk-body">We have restructured the way we group supported housing scheme data.</p>
<p class="govuk-body">On old CORE, a scheme’s data was split into management group and scheme. On new CORE, the data is split into scheme and location.</p>
<p class="govuk-body">These are the main changes:</p>
<ul class="govuk-list govuk-list--bullet">
<li>Schemes now store data about the stock owner and support provider. This was previously stored in management group.</li>
<li>Schemes still store data about the type of support, as they did in old CORE.</li>
<li>Schemes no longer store data about postcodes where support is provided. These are now in locations.</li>
</ul>
<%= govuk_list [
"Schemes now store data about the stock owner and support provider. This was previously stored in management group.",
"Schemes still store data about the type of support, as they did in old CORE.",
"Schemes no longer store data about postcodes where support is provided. These are now in locations.",
], type: :bullet %>
<p class="govuk-body">This new structure means data coordinators only needs to fill in support details once.</p>
<h2 class="govuk-heading-m">How schemes are migrated from old CORE</h2>
<p class="govuk-body">If your organisation has migrated from old CORE to new CORE, your existing schemes are on the <%= govuk_link_to("schemes page", schemes_path) %>, with the same details.</p>

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

@ -24,7 +24,7 @@
<%= f.govuk_submit button_label %>
<% end %>
<% if SchemePolicy.new(current_user, @scheme).delete? && FeatureToggle.delete_scheme_enabled? %>
<% if SchemePolicy.new(current_user, @scheme).delete? %>
<%= delete_scheme_link(@scheme) %>
<% end %>
<% end %>

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

@ -56,6 +56,6 @@
<%= toggle_scheme_link(@scheme) %>
<% end %>
<% if SchemePolicy.new(current_user, @scheme).delete? && FeatureToggle.delete_scheme_enabled? %>
<% if SchemePolicy.new(current_user, @scheme).delete? %>
<%= delete_scheme_link(@scheme) %>
<% end %>

68
app/views/start/guidance.html.erb

@ -21,46 +21,50 @@
<%= accordion.with_section(heading_text: "Types of lettings you should create logs for") do %>
<p class="govuk-body">You’ll need to create a log for:</p>
<ul class="govuk-list govuk-list--bullet">
<li>Tenants in general needs housing allocated a new letting. This includes tenants moving into the social rented sector from outside, existing social tenants moving between properties or landlords, and existing social tenants renewing lettings in the same property. If fixed-term and social or affordable rent, only include tenancies of 2 years or more.</li>
<li>Tenants in supported housing (social housing, sheltered accommodation and care homes) allocated a new letting. This includes tenants moving into the social rented sector from outside, existing social tenants moving between properties or landlords, and existing social tenants renewing lettings in the same property. All supported housing tenancies should be reported regardless of length.</li>
<li>Starter tenancies provided by local authorities (LAs) and lettings with an introductory period provided by private registered providers (PRPs) should be completed in CORE at the beginning of the starter or introductory period. The tenancy type and length entered should be based on the tenancy the tenant will roll onto once the starter or introductory period has been completed. You do not need to submit another CORE log once the period has been completed.</li>
<li>Room moves within a shared housing unit that result in a different property type or support needs – this is classed as an internal transfer of an existing social tenant to another property.</li>
<li>Existing tenants who are issued with a new tenancy agreement when stock is acquired, transferred or permanently decanted.</li>
<li>Tenants under the Rough Sleepers Initiative or Rough Sleeping Accommodation Programme, where accommodation is permanent.</li>
<li>Households previously provided with temporary accommodation to meet a duty under the homelessness legislation who are allocated a tenancy as a settled home ending the duty (this may be the same property).</li>
<li>Refugees and asylum seekers who have been granted indefinite leave to remain, humanitarian protection or exceptional leave to remain.</li>
<li>Affordable Rent lettings – where up to 80% of market rent can be charged and a new supply agreement is signed.</li>
<li>London Affordable Rent lettings – a type of Affordable Rent available in London through the Greater London Authority (GLA).</li>
<li>Intermediate Rent lettings – where the rent must not exceed 80% of the current market rate (including any service charges).</li>
<li>Rent to Buy lettings – where a discount of up to 20% market rent is charged for a single rental period for a minimum of 5 years. After that period, the tenant is offered the chance to purchase the property (either shared ownership or outright) at full market value.</li>
<li>London Living Rent lettings – a type of Intermediate Rent available in London through the Greater London Authority (GLA).</li>
</ul>
<%= govuk_list [
"Tenants in general needs housing allocated a new letting. This includes tenants moving into the social rented sector from outside, existing social tenants moving between properties or landlords, and existing social tenants renewing lettings in the same property. If fixed-term and social or affordable rent, only include tenancies of 2 years or more.",
"Tenants in supported housing (social housing, sheltered accommodation and care homes) allocated a new letting. This includes tenants moving into the social rented sector from outside, existing social tenants moving between properties or landlords, and existing social tenants renewing lettings in the same property. All supported housing tenancies should be reported regardless of length.",
"Starter tenancies provided by local authorities (LAs) and lettings with an introductory period provided by private registered providers (PRPs) should be completed in CORE at the beginning of the starter or introductory period. The tenancy type and length entered should be based on the tenancy the tenant will roll onto once the starter or introductory period has been completed. You do not need to submit another CORE log once the period has been completed.",
"Room moves within a shared housing unit that result in a different property type or support needs – this is classed as an internal transfer of an existing social tenant to another property.",
"Existing tenants who are issued with a new tenancy agreement when stock is acquired, transferred or permanently decanted.",
"Tenants under the Rough Sleepers Initiative or Rough Sleeping Accommodation Programme, where accommodation is permanent.",
"Households previously provided with temporary accommodation to meet a duty under the homelessness legislation who are allocated a tenancy as a settled home ending the duty (this may be the same property).",
"Refugees and asylum seekers who have been granted indefinite leave to remain, humanitarian protection or exceptional leave to remain.",
"Affordable Rent lettings – where up to 80% of market rent can be charged and a new supply agreement is signed.",
"London Affordable Rent lettings – a type of Affordable Rent available in London through the Greater London Authority (GLA).",
"Intermediate Rent lettings – where the rent must not exceed 80% of the current market rate (including any service charges).",
"Rent to Buy lettings – where a discount of up to 20% market rent is charged for a single rental period for a minimum of 5 years. After that period, the tenant is offered the chance to purchase the property (either shared ownership or outright) at full market value.",
"London Living Rent lettings – a type of Intermediate Rent available in London through the Greater London Authority (GLA).",
],
type: :bullet %>
<% end %>
<%= accordion.with_section(heading_text: "Types of lettings you should not create logs for") do %>
<p class="govuk-body">You don’t need to create a log for:</p>
<ul class="govuk-list govuk-list--bullet">
<li>Temporary general needs housing with a fixed period of less than 2 years if they are social or affordable rent. (Temporary lettings for intermediate rent and supported housing should be recorded).</li>
<li>Starter tenancies or lettings with an introductory period that roll onto or convert into the main tenancy. The CORE log should be completed at the beginning of this period.</li>
<li>Changes from sole to joint or joint to sole tenancies, where the number of people in the household has not changed.</li>
<li>Moves within a shared housing unit resulting in the same support needs or property type, even if a new tenancy or licence agreement is issued.</li>
<li>Lettings where no new tenancy agreement is signed.</li>
<li>Where stock is acquired, transferred or permanently decanted and the existing tenants are not issued with a new tenancy agreement.</li>
<li>Mutual exchanges including lettings where registered provider tenants have exchanged homes, for example through the national HOMESWAP system.</li>
<li>Successions and assignments.</li>
<li>Demotion of a secure or assured tenancy, and any subsequent conversion of the demoted tenancy to a secure or assured tenancy.</li>
<li>Lettings made to asylum seekers who are awaiting a decision on their applications for asylum under the Immigration and Asylum Act 1999.</li>
<li>Non-social lettings, including market-rented properties, employer-provided housing where the employer provides financial support, homes for staff of social landlords linked to employment, homes social landlords manage for organisations who are not social landlords, homes social landlords own but lease in entirety to organisations who are not social landlords, and freehold housing with variable charges for services and communal facilities.</li>
</ul>
<%= govuk_list [
"Temporary general needs housing with a fixed period of less than 2 years if they are social or affordable rent. (Temporary lettings for intermediate rent and supported housing should be recorded).",
"Starter tenancies or lettings with an introductory period that roll onto or convert into the main tenancy. The CORE log should be completed at the beginning of this period.",
"Changes from sole to joint or joint to sole tenancies, where the number of people in the household has not changed.",
"Moves within a shared housing unit resulting in the same support needs or property type, even if a new tenancy or licence agreement is issued.",
"Lettings where no new tenancy agreement is signed.",
"Where stock is acquired, transferred or permanently decanted and the existing tenants are not issued with a new tenancy agreement.",
"Mutual exchanges including lettings where registered provider tenants have exchanged homes, for example through the national HOMESWAP system.",
"Successions and assignments.",
"Demotion of a secure or assured tenancy, and any subsequent conversion of the demoted tenancy to a secure or assured tenancy.",
"Lettings made to asylum seekers who are awaiting a decision on their applications for asylum under the Immigration and Asylum Act 1999.",
"Non-social lettings, including market-rented properties, employer-provided housing where the employer provides financial support, homes for staff of social landlords linked to employment, homes social landlords manage for organisations who are not social landlords, homes social landlords own but lease in entirety to organisations who are not social landlords, and freehold housing with variable charges for services and communal facilities.",
],
type: :bullet %>
<% end %>
<%= accordion.with_section(heading_text: "What if someone is reluctant to answer any questions?") do %>
<p class="govuk-body">If a tenant or buyer is reluctant to answer questions as part of a log, you should explain that:</p>
<ul class="govuk-list govuk-list--bullet">
<li>all information they provide is anonymous and will not affect their housing, benefits or other services they receive.</li>
<li>the data they provide is vital in helping to build a complete picture of social housing in England and is used to inform social housing policy.</li>
</ul>
<%= govuk_list [
"all information they provide is anonymous and will not affect their housing, benefits or other services they receive.",
"the data they provide is vital in helping to build a complete picture of social housing in England and is used to inform social housing policy.",
],
type: :bullet %>
<p class="govuk-body">If a tenant or buyer is still unwilling or unable to answer questions, select the ‘Don’t know’ or ‘Tenant/person prefers not to say’ options.</p>
<% end %>
<% end %>

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

@ -163,7 +163,7 @@
</span>
<% end %>
<% end %>
<% if UserPolicy.new(current_user, @user).delete? && FeatureToggle.delete_user_enabled? %>
<% if UserPolicy.new(current_user, @user).delete? %>
<%= delete_user_link(@user) %>
<% end %>
</div>

8
bin/rubocop

@ -0,0 +1,8 @@
#!/usr/bin/env ruby
require "rubygems"
require "bundler/setup"
# explicit rubocop config increases performance slightly while avoiding config confusion.
ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__))
load Gem.bin_path("rubocop", "rubocop")

3
bin/setup

@ -1,11 +1,10 @@
#!/usr/bin/env ruby
require "fileutils"
# path to your application root.
APP_ROOT = File.expand_path("..", __dir__)
def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
system(*args, exception: true)
end
FileUtils.chdir APP_ROOT do

13
config/application.rb

@ -12,7 +12,6 @@ require "action_mailbox/engine"
# require "action_text/engine"
require "action_view/railtie"
# require "action_cable/engine"
# require "sprockets/railtie"
# require "rails/test_unit/railtie"
# Require the gems listed in Gemfile, including any gems
@ -22,8 +21,16 @@ Bundler.require(*Rails.groups)
module DataCollector
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 7.0
config.load_defaults 7.2
# Please, add to the `ignore` list any other `lib` subdirectories that do
# not contain `.rb` files, or that should not be reloaded or eager loaded.
# Common ones are `templates`, `generators`, or `middleware`, for example.
config.autoload_lib(ignore: %w[assets tasks])
# it's strongly discouraged using add_autoload_paths_to_load_path, but rack_attack initializer can't load config files without it
config.add_autoload_paths_to_load_path = true
# Configuration for the application, engines, and railties goes here.
#
# These settings can be overridden in specific environments using the files
@ -32,6 +39,8 @@ module DataCollector
config.time_zone = "London"
# config.eager_load_paths << Rails.root.join("extras")
# Don't generate system test files.
config.generators.system_tests = nil
config.exceptions_app = routes
config.active_job.queue_adapter = :sidekiq

19
config/environments/development.rb

@ -6,7 +6,7 @@ Rails.application.configure do
# In the development environment your application's code is reloaded any time
# it changes. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
config.enable_reloading = true
# Do not eager load code on boot.
config.eager_load = false
@ -14,7 +14,7 @@ Rails.application.configure do
# Show full error reports.
config.consider_all_requests_local = true
# Enable server timing
# Enable server timing.
config.server_timing = true
# Enable/disable caching. By default caching is disabled.
@ -24,9 +24,7 @@ Rails.application.configure do
config.action_controller.enable_fragment_cache_logging = true
config.cache_store = :memory_store
config.public_file_server.headers = {
"Cache-Control" => "public, max-age=#{2.days.to_i}",
}
config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" }
else
config.action_controller.perform_caching = false
@ -39,6 +37,8 @@ Rails.application.configure do
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
# Disable caching for Action Mailer templates even if Action Controller
# caching is enabled.
config.action_mailer.perform_caching = false
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
@ -72,15 +72,20 @@ Rails.application.configure do
# Highlight code that triggered database queries in logs.
config.active_record.verbose_query_logs = true
# Highlight code that enqueued background job in logs.
config.active_job.verbose_enqueue_logs = true
# Raises error for missing translations.
config.i18n.raise_on_missing_translations = true
# Annotate rendered view with file names.
# config.action_view.annotate_rendered_view_with_filenames = true
# Uncomment if you wish to allow Action Cable access from any origin.
# config.action_cable.disable_request_forgery_protection = true
# Raise error when a before_action's only/except options reference missing actions.
# config.action_controller.raise_on_missing_callback_actions = true
# Apply autocorrection by RuboCop to files generated by `bin/rails generate`.
# config.generators.apply_rubocop_autocorrect_after_generate!
Faker::Config.locale = "en-GB"
# see https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017

78
config/environments/production.rb

@ -4,7 +4,7 @@ Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# Code is not reloaded between requests.
config.cache_classes = true
config.enable_reloading = false
# Eager load code on boot. This eager loads most of Rails and
# your application in memory, allowing both threaded web servers
@ -13,15 +13,14 @@ Rails.application.configure do
config.eager_load = true
# Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
# Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
# or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
# Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment
# key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files).
# config.require_master_key = true
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
# Disable serving static files from `public/`, relying on NGINX/Apache to do so instead.
config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
@ -34,28 +33,38 @@ Rails.application.configure do
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
# Mount Action Cable outside main process or domain.
# config.action_cable.mount_path = nil
# config.action_cable.url = "wss://example.com/cable"
# config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ]
# Assume all access to the app is happening through a SSL-terminating reverse proxy.
# Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies.
# config.assume_ssl = true
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
config.force_ssl = true
# Include generic and useful information about system operation, but avoid logging too much
# information to avoid inadvertent exposure of personally identifiable information (PII).
config.log_level = :info
# Skip http-to-https redirect for the default health check endpoint.
# config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } }
# Log to STDOUT by default
config.logger = ActiveSupport::Logger.new($stdout)
.tap { |logger| logger.formatter = ::Logger::Formatter.new }
.then { |logger| ActiveSupport::TaggedLogging.new(logger) }
# Prepend all log lines with the following tags.
config.log_tags = [:request_id]
# "info" includes generic and useful information about system operation, but avoids logging too much
# information to avoid inadvertent exposure of personally identifiable information (PII). If you
# want to log everything, set the level to "debug".
config.log_level = :info
# Use a different cache store in production.
# config.cache_store = :mem_cache_store
# Use a real queuing backend for Active Job (and separate queues per environment).
# config.active_job.queue_adapter = :resque
# config.active_job.queue_adapter = :resque
# config.active_job.queue_name_prefix = "data_collector_production"
# Disable caching for Action Mailer templates even if Action Controller
# caching is enabled.
config.action_mailer.perform_caching = false
config.action_mailer.default_url_options = { host: ENV["APP_HOST"] }
@ -98,37 +107,16 @@ Rails.application.configure do
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false
# Inserts middleware to perform automatic connection switching.
# The `database_selector` hash is used to pass options to the DatabaseSelector
# middleware. The `delay` is used to determine how long to wait after a write
# to send a subsequent read to the primary.
#
# The `database_resolver` class is used by the middleware to determine which
# database is appropriate to use based on the time delay.
#
# The `database_resolver_context` class is used by the middleware to set
# timestamps for the last write to the primary. The resolver uses the context
# class timestamps to determine how long to wait before reading from the
# replica.
#
# By default Rails will store a last write timestamp in the session. The
# DatabaseSelector middleware is designed as such you can define your own
# strategy for connection switching and pass that into the middleware through
# these configuration options.
# config.active_record.database_selector = { delay: 2.seconds }
# config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
# config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
# Inserts middleware to perform automatic shard swapping. The `shard_selector` hash
# can be used to pass options to the `ShardSelector` middleware. The `lock` option is
# used to determine whether shard swapping should be prohibited for the request.
#
# The `shard_resolver` option is used by the middleware to determine which shard
# to switch to. The application must provide a mechanism for finding the shard name
# in a proc. See guides for an example.
# config.active_record.shard_selector = { lock: true }
# config.active_record.shard_resolver = ->(request) { Tenant.find_by!(host: request.host).shard }
# Only use :id for inspections in production.
config.active_record.attributes_for_inspect = [:id]
# Enable DNS rebinding protection and other `Host` header attacks.
# config.hosts = [
# "example.com", # Allow requests from example.com
# /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
# ]
# Skip DNS rebinding protection for the default health check endpoint.
# config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
# see https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017
config.active_record.yaml_column_permitted_classes = [Time, BigDecimal]

34
config/environments/test.rb

@ -1,5 +1,4 @@
require "active_support/core_ext/integer/time"
require "faker"
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
@ -9,26 +8,25 @@ require "faker"
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# Turn false under Spring and add config.action_view.cache_template_loading = true
config.cache_classes = true
# While tests run files are not watched, reloading is not necessary.
config.enable_reloading = false
# Eager loading loads your whole application. When running a single test locally,
# this probably isn't necessary. It's a good idea to do in a continuous integration
# system, or in some way before deploying your code.
# Eager loading loads your entire application. When running a single test locally,
# this is usually not necessary, and can slow down your test suite. However, it's
# recommended that you enable it in continuous integration systems to ensure eager
# loading is working properly before deploying your code.
config.eager_load = ENV["CI"].present?
# Configure public file server for tests with Cache-Control for performance.
config.public_file_server.enabled = true
config.public_file_server.headers = {
"Cache-Control" => "public, max-age=#{1.hour.to_i}",
}
config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{1.hour.to_i}" }
# Show full error reports and disable caching.
config.consider_all_requests_local = true
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
config.cache_store = :null_store
# Raise exceptions instead of rendering exception templates.
# Render exception templates for rescuable exceptions and raise for other exceptions.
config.action_dispatch.show_exceptions = false
# Disable request forgery protection in test environment.
@ -37,16 +35,20 @@ Rails.application.configure do
# Store uploaded files on the local file system in a temporary directory.
config.active_storage.service = :test
# Disable caching for Action Mailer templates even if Action Controller
# caching is enabled.
config.action_mailer.perform_caching = false
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
config.action_mailer.default_options = { from: "test@gmail.com" }
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
# Unlike controllers, the mailer instance doesn't have any context about the
# incoming request so you'll need to provide the :host parameter yourself.
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
config.action_mailer.default_options = { from: "test@gmail.com" }
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
@ -61,6 +63,10 @@ Rails.application.configure do
# Annotate rendered view with file names.
# config.action_view.annotate_rendered_view_with_filenames = true
# Raise error when a before_action's only/except options reference missing actions.
# config.action_controller.raise_on_missing_callback_actions = true
Faker::Config.locale = "en-GB"
# see https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017

6
config/initializers/assets.rb

@ -1 +1,7 @@
# Be sure to restart your server when you modify this file.
# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = "1.0"
# Add additional assets to the asset load path.
Rails.application.config.assets.paths << Rails.root.join("node_modules/@fortawesome/fontawesome-free/webfonts")

13
config/initializers/content_security_policy.rb

@ -1,8 +1,8 @@
# Be sure to restart your server when you modify this file.
# Define an application-wide content security policy
# For further information see the following documentation
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
# Define an application-wide content security policy.
# See the Securing Rails Applications Guide for more information:
# https://guides.rubyonrails.org/security.html#content-security-policy-header
# Rails.application.configure do
# config.content_security_policy do |policy|
@ -16,11 +16,10 @@
# # policy.report_uri "/csp-violation-report-endpoint"
# end
#
# # Generate session nonces for permitted importmap and inline scripts
# # Generate session nonces for permitted importmap, inline scripts, and inline styles.
# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
# config.content_security_policy_nonce_directives = %w(script-src)
# config.content_security_policy_nonce_directives = %w(script-src style-src)
#
# # Report CSP violations to a specified URI. See:
# # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
# # Report violations without enforcing the policy.
# # config.content_security_policy_report_only = true
# end

6
config/initializers/filter_parameter_logging.rb

@ -1,6 +1,8 @@
# Be sure to restart your server when you modify this file.
# Configure sensitive parameters which will be filtered from the log file.
# Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file.
# Use this to limit dissemination of sensitive information.
# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors.
Rails.application.config.filter_parameters += %i[
passw secret token crypt salt certificate otp ssn reset_password_token
passw secret email secret token _key crypt salt certificate otp ssn reset_password_token
]

20
config/initializers/permissions_policy.rb

@ -1,11 +1,13 @@
# Be sure to restart your server when you modify this file.
# Define an application-wide HTTP permissions policy. For further
# information see https://developers.google.com/web/updates/2018/06/feature-policy
#
# Rails.application.config.permissions_policy do |f|
# f.camera :none
# f.gyroscope :none
# f.microphone :none
# f.usb :none
# f.fullscreen :self
# f.payment :self, "https://secure.example.com"
# information see: https://developers.google.com/web/updates/2018/06/feature-policy
# Rails.application.config.permissions_policy do |policy|
# policy.camera :none
# policy.gyroscope :none
# policy.microphone :none
# policy.usb :none
# policy.fullscreen :self
# policy.payment :self, "https://secure.example.com"
# end

4
config/locales/en.yml

@ -382,6 +382,10 @@ en:
organisation_part_of_another_merge: "This organisation is part of another merge - select a different one."
organisation_part_of_another_incomplete_merge: "Another merge request records %{organisation} as merging into %{absorbing_organisation} on %{merge_date}. Select another organisation or remove this organisation from the other merge request."
organisation_not_selected: "Select an organisation from the search list."
merge_request_id:
blank: "Select a merge request."
merging_organisation_id:
blank: "Select an organisation to merge."
soft_validations:
retirement:

17
config/locales/test.en.yml

@ -0,0 +1,17 @@
en:
forms:
2021:
lettings:
guidance:
what_counts_as_income:
title: "What counts as income?"
content: "What counts as income?"
finding_scheme:
title: "Can’t find your scheme?"
content: "<p>Schemes are attached to the organisation that owns the property. Check you have correctly answered question 1 \"Which organisation owns this property?\"</p>
<p>If your organisation’s schemes were migrated from old CORE, they may have new names and codes. Search by postcode to find your scheme.</p>"
scheme_changes_link_text: "Read more about how schemes have changed"
view_schemes_link_text: "View your organisation’s schemes"
soft_validations:
net_income:
hint_text: "hint text"

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

@ -18,8 +18,8 @@ en:
owning_organisation:
not_found: "The owning organisation code is incorrect."
not_stock_owner: "The owning organisation code provided is for an organisation that does not own stock."
not_permitted:
support: "This owning organisation is not affiliated with %(name)."
not_permitted:
support: "This owning organisation is not affiliated with %{name}."
not_support: "You do not have permission to add logs for this owning organisation."
assigned_to:
not_found: "User with the specified email could not be found."

48
config/puma.rb

@ -1,9 +1,25 @@
# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum; this matches the default thread size of Active Record.
# This configuration file will be evaluated by Puma. The top-level methods that
# are invoked here are part of Puma's configuration DSL. For more information
# about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html.
# Puma starts a configurable number of processes (workers) and each process
# serves each request in a thread from an internal thread pool.
#
# The ideal number of threads per worker depends both on how much time the
# application spends waiting for IO operations and on how much you wish to
# to prioritize throughput over latency.
#
# As a rule of thumb, increasing the number of threads will increase how much
# traffic a given process can handle (throughput), but due to CRuby's
# Global VM Lock (GVL) it has diminishing returns and will degrade the
# response time (latency) of the application.
#
# The default is set to 3 threads as it's deemed a decent compromise between
# throughput and latency for the average Rails application.
#
# Any libraries that use a connection pool or another resource pool should
# be configured to provide at least as many connections as the number of
# threads. This includes Active Record's `pool` parameter in `database.yml`.
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count
@ -14,30 +30,16 @@ threads min_threads_count, max_threads_count
worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development"
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
#
port ENV.fetch("PORT") { 3000 }
# Specifies the `environment` that Puma will run in.
#
environment ENV.fetch("RAILS_ENV") { "development" }
# Specifies the `pidfile` that Puma will use.
# Specify the PID file. Defaults to tmp/pids/server.pid in development.
# In other environments, only set the PID file if requested.
pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked web server processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory.
#
# preload_app!
# Allow puma to be restarted by `rails restart` command.
# Allow puma to be restarted by `bin/rails restart` command.
plugin :tmp_restart

22
db/migrate/20241206142942_add_service_name_to_active_storage_blobs.active_storage.rb

@ -0,0 +1,22 @@
# This migration comes from active_storage (originally 20190112182829)
class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0]
def up
return unless table_exists?(:active_storage_blobs)
unless column_exists?(:active_storage_blobs, :service_name)
add_column :active_storage_blobs, :service_name, :string
if configured_service = ActiveStorage::Blob.service.name # rubocop:disable Lint/AssignmentInCondition
ActiveStorage::Blob.unscoped.update_all(service_name: configured_service)
end
change_column :active_storage_blobs, :service_name, :string, null: false
end
end
def down
return unless table_exists?(:active_storage_blobs)
remove_column :active_storage_blobs, :service_name
end
end

28
db/migrate/20241206142943_create_active_storage_variant_records.active_storage.rb

@ -0,0 +1,28 @@
# This migration comes from active_storage (originally 20191206030411)
class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0]
def change
return unless table_exists?(:active_storage_blobs)
# Use Active Record's configured type for primary key
create_table :active_storage_variant_records, id: primary_key_type, if_not_exists: true do |t| # rubocop:disable Rails/CreateTableWithTimestamps
t.belongs_to :blob, null: false, index: false, type: blobs_primary_key_type
t.string :variation_digest, null: false
t.index %i[blob_id variation_digest], name: "index_active_storage_variant_records_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
private
def primary_key_type
config = Rails.configuration.generators
config.options[config.orm][:primary_key_type] || :primary_key
end
def blobs_primary_key_type
pkey_name = connection.primary_key(:active_storage_blobs)
pkey_column = connection.columns(:active_storage_blobs).find { |c| c.name == pkey_name }
pkey_column.bigint? ? :bigint : pkey_column.type
end
end

8
db/migrate/20241206142944_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb

@ -0,0 +1,8 @@
# This migration comes from active_storage (originally 20211119233751)
class RemoveNotNullOnActiveStorageBlobsChecksum < ActiveRecord::Migration[6.0]
def change
return unless table_exists?(:active_storage_blobs)
change_column_null(:active_storage_blobs, :checksum, true)
end
end

2
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: 2024_12_04_100518) do
ActiveRecord::Schema[7.2].define(version: 2024_12_06_142944) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"

12
db/seeds.rb

@ -12,13 +12,13 @@ def find_or_create_user(organisation, email, name, role)
end
end
unless Rails.env.test?
if LocalAuthority.count.zero?
la_path = "config/local_authorities_data/initial_local_authorities.csv"
service = Imports::LocalAuthoritiesService.new(path: la_path)
service.call
end
if LocalAuthority.count.zero?
la_path = "config/local_authorities_data/initial_local_authorities.csv"
service = Imports::LocalAuthoritiesService.new(path: la_path)
service.call
end
unless Rails.env.test?
if LaRentRange.count.zero?
Dir.glob("config/rent_range_data/*.csv").each do |path|
start_year = File.basename(path, ".csv")

1
lib/tasks/correct_rent_type_value.rake

@ -9,7 +9,6 @@ task correct_rent_type_value: :environment do
new_rent_type_value = BulkUpload::Lettings::Year2024::RowParser::RENT_TYPE_BU_MAPPING[rent_type_at_upload]
log.rent_type = new_rent_type_value
log.skip_update_status = true if log.status == "pending"
if log.save
Rails.logger.info("Log #{log.id} rent_type updated from #{rent_type_at_upload} to #{log.rent_type}")
else

1
lib/tasks/recalculate_status_after_sales_over_retirement_age_validation.rake

@ -3,7 +3,6 @@ task recalculate_status_over_retirement: :environment do
validation_trigger_condition = "(ecstat1 != 5 AND age1 > 66) OR (ecstat2 != 5 AND age2 > 66) OR (ecstat3 != 5 AND age3 > 66) OR (ecstat4 != 5 AND age4 > 66) OR (ecstat5 != 5 AND age5 > 66) OR (ecstat6 != 5 AND age6 > 66)"
SalesLog.filter_by_year(2024).where(status: "pending", status_cache: "completed").where(validation_trigger_condition).find_each do |log|
log.status_cache = log.calculate_status
log.skip_update_status = true
unless log.save
Rails.logger.info "Could not save changes to pending sales log #{log.id}"

4
package.json

@ -20,11 +20,11 @@
"css-loader": "^6.7.1",
"custom-event-polyfill": "^1.0.7",
"file-loader": "^6.2.0",
"govuk-frontend": "5.1.0",
"govuk-frontend": "5.7.1",
"html5shiv": "^3.7.3",
"intersection-observer": "^0.12.0",
"mini-css-extract-plugin": "^2.6.0",
"rails_admin": "3.1.3",
"rails_admin": "3.3.0",
"regenerator-runtime": "^0.13.9",
"sass": "^1.49.9",
"sass-loader": "^12.6.0",

2
spec/components/primary_navigation_component_spec.rb

@ -14,7 +14,7 @@ RSpec.describe PrimaryNavigationComponent, type: :component do
it "then that item appears as selected" do
result = render_inline(described_class.new(items:))
expect(result.css('.app-primary-navigation__link[aria-current="page"]').text).to include("Organisations")
expect(result.css('.govuk-service-navigation__link[aria-current="page"]').text).to include("Organisations")
end
end

2
spec/controllers/errors_controller_spec.rb

@ -18,7 +18,7 @@ RSpec.describe ErrorsController, type: :controller do
describe "GET #unprocessable_entity" do
it "returns unprocessable_entity" do
get :unprocessable_entity
expect(response).to have_http_status(:unprocessable_entity)
expect(response).to have_http_status(:unprocessable_content)
end
end
end

2
spec/factories/organisation.rb

@ -1,6 +1,6 @@
FactoryBot.define do
factory :organisation do
name { Faker::Company.name }
sequence(:name) { |n| "#{Faker::Company.name} #{n}" }
address_line1 { Faker::Address.street_address }
address_line2 { Faker::Address.city }
provider_type { "LA" }

16
spec/features/form/page_routing_spec.rb

@ -92,7 +92,7 @@ RSpec.describe "Form Page Routing" do
expect(find("#lettings-log-postcode-full-field-error").value).to eq("FAKE_POSTCODE")
end
it "does not reset the displayed date" do
it "does not reset the displayed date if it's an invalid date" do
lettings_log.update!(startdate: "2021/10/13")
visit("/lettings-logs/#{id}/tenancy-start-date")
fill_in("lettings_log[startdate(1i)]", with: "202")
@ -106,6 +106,20 @@ RSpec.describe "Form Page Routing" do
expect(find_field("lettings_log[startdate(1i)]").value).to eq("2021")
end
it "displays the entered date if it's in a valid format" do
lettings_log.update!(startdate: "2021/10/13")
visit("/lettings-logs/#{id}/tenancy-start-date")
fill_in("lettings_log[startdate(1i)]", with: "202")
fill_in("lettings_log[startdate(2i)]", with: "12")
fill_in("lettings_log[startdate(3i)]", with: "1")
click_button("Save and continue")
expect(page).to have_current_path("/lettings-logs/#{id}/tenancy-start-date")
expect(find_field("lettings_log[startdate(3i)]").value).to eq("1")
expect(find_field("lettings_log[startdate(2i)]").value).to eq("12")
expect(find_field("lettings_log[startdate(1i)]").value).to eq("202")
end
it "does not reset the displayed date if it's empty" do
lettings_log.update!(startdate: nil)
visit("/lettings-logs/#{id}/tenancy-start-date")

6
spec/features/organisation_spec.rb

@ -206,14 +206,14 @@ RSpec.describe "User Features" do
it "can filter lettings logs by year" do
check("years-2022-field")
click_button("Apply filters")
expect(page).to have_current_path("/organisations/#{org_id}/lettings-logs?years[]=&years[]=2022&status[]=&needstypes[]=&assigned_to=all&user_text_search=&user=&owning_organisation_select=all&owning_organisation_text_search=&owning_organisation=&managing_organisation_select=all&managing_organisation_text_search=&managing_organisation=")
expect(page).to have_current_path("/organisations/#{org_id}/lettings-logs?%5Byears%5D[]=&years[]=2022&%5Bstatus%5D[]=&%5Bneedstypes%5D[]=&assigned_to=all&user_text_search=&user=&owning_organisation_select=all&owning_organisation_text_search=&owning_organisation=&managing_organisation_select=all&managing_organisation_text_search=&managing_organisation=")
expect(page).not_to have_link first_log.id.to_s, href: "/lettings-logs/#{first_log.id}"
end
it "can filter lettings logs by needstype" do
check("needstypes-1-field")
click_button("Apply filters")
expect(page).to have_current_path("/organisations/#{org_id}/lettings-logs?years[]=&status[]=&needstypes[]=&needstypes[]=1&assigned_to=all&user_text_search=&user=&owning_organisation_select=all&owning_organisation_text_search=&owning_organisation=&managing_organisation_select=all&managing_organisation_text_search=&managing_organisation=")
expect(page).to have_current_path("/organisations/#{org_id}/lettings-logs?%5Byears%5D[]=&%5Bstatus%5D[]=&%5Bneedstypes%5D[]=&needstypes[]=1&assigned_to=all&user_text_search=&user=&owning_organisation_select=all&owning_organisation_text_search=&owning_organisation=&managing_organisation_select=all&managing_organisation_text_search=&managing_organisation=")
other_general_needs_logs.each do |general_needs_log|
expect(page).to have_link general_needs_log.id.to_s, href: "/lettings-logs/#{general_needs_log.id}"
end
@ -256,7 +256,7 @@ RSpec.describe "User Features" do
end
check("years-2022-field")
click_button("Apply filters")
expect(page).to have_current_path("/organisations/#{org_id}/sales-logs?years[]=&years[]=2022&status[]=&assigned_to=all&user_text_search=&user=&owning_organisation_select=all&owning_organisation_text_search=&owning_organisation=&managing_organisation_select=all&managing_organisation_text_search=&managing_organisation=")
expect(page).to have_current_path("/organisations/#{org_id}/sales-logs?%5Byears%5D[]=&years[]=2022&%5Bstatus%5D[]=&assigned_to=all&user_text_search=&user=&owning_organisation_select=all&owning_organisation_text_search=&owning_organisation=&managing_organisation_select=all&managing_organisation_text_search=&managing_organisation=")
expect(page).not_to have_link first_log.id.to_s, href: "/sales-logs/#{first_log.id}"
end
end

4
spec/features/schemes_spec.rb

@ -87,7 +87,7 @@ RSpec.describe "Schemes scheme Features" do
it "displays the filters component with a correct count and clear button" do
expect(page).to have_content("2 filters applied")
expect(page).to have_link("Clear", href: /clear-filters\?filter_type=schemes/)
expect(page).to have_link("Clear", href: /clear-filters\?.*filter_type=schemes/)
end
context "when clearing the filters" do
@ -326,7 +326,7 @@ RSpec.describe "Schemes scheme Features" do
it "displays the filters component with a correct count and clear button" do
expect(page).to have_content("2 filters applied")
expect(page).to have_link("Clear", href: /\/clear-filters\?filter_type=scheme_locations/)
expect(page).to have_link("Clear", href: /\/clear-filters\?.*filter_type=scheme_locations/)
end
context "when clearing the filters" do

6
spec/features/user_spec.rb

@ -269,7 +269,7 @@ RSpec.describe "User Features" do
it "displays the filters component with a correct count and clear button" do
expect(page).to have_content("2 filters applied")
expect(page).to have_link("Clear", href: /clear-filters\?filter_type=users/)
expect(page).to have_link("Clear", href: /clear-filters\?.*filter_type=users/)
end
context "when clearing the filters" do
@ -678,7 +678,7 @@ RSpec.describe "User Features" do
fill_in("code", with: otp)
click_button("Submit")
expect(page).to have_content("Check your email")
expect(page).to have_http_status(:unprocessable_entity)
expect(page).to have_http_status(:unprocessable_content)
expect(page).to have_title("Error")
expect(page).to have_selector(".govuk-error-summary__title")
end
@ -691,7 +691,7 @@ RSpec.describe "User Features" do
fill_in("code", with: otp)
click_button("Submit")
expect(page).to have_content("Check your email")
expect(page).to have_http_status(:unprocessable_entity)
expect(page).to have_http_status(:unprocessable_content)
expect(page).to have_title("Error")
expect(page).to have_selector(".govuk-error-summary__title")
end

2
spec/lib/tasks/correct_rent_type_value_spec.rb

@ -35,7 +35,6 @@ RSpec.describe "correct_rent_type_value" do
it "updates the rent_type value on a pending log where it was set to 1 on create" do
log = build(:lettings_log, :completed, rent_type: 1, bulk_upload:, status: "pending")
log.skip_update_status = true
log.save!
initial_updated_at = log.updated_at
expect(log.status).to eq("pending")
@ -166,7 +165,6 @@ RSpec.describe "correct_rent_type_value" do
it "updates the rent_type value on a pending log where it was set to 2 on create" do
log = build(:lettings_log, :completed, rent_type: 2, bulk_upload:, status: "pending")
log.skip_update_status = true
log.save!
initial_updated_at = log.updated_at
expect(log.status).to eq("pending")

2
spec/lib/tasks/recalculate_status_after_sales_over_retirement_age_validation_spec.rb

@ -17,7 +17,6 @@ RSpec.describe "recalculate_status_after_sales_over_retirement_age_validation" d
before do
log.status = "completed"
log.skip_update_status = true
log.save!
end
@ -34,7 +33,6 @@ RSpec.describe "recalculate_status_after_sales_over_retirement_age_validation" d
before do
log.status = "pending"
log.status_cache = "completed"
log.skip_update_status = true
log.save!
end

29
spec/models/lettings_log_spec.rb

@ -5,8 +5,8 @@ require "shared/shared_log_examples"
# rubocop:disable RSpec/MessageChain
RSpec.describe LettingsLog do
let(:different_managing_organisation) { create(:organisation) }
let(:assigned_to_user) { create(:user) }
let(:owning_organisation) { assigned_to_user.organisation }
let(:owning_organisation) { create(:organisation, rent_periods: [2]) }
let(:assigned_to_user) { create(:user, organisation: owning_organisation) }
let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") }
around do |example|
@ -220,6 +220,31 @@ RSpec.describe LettingsLog do
expect(completed_lettings_log.completed?).to be(true)
end
end
it "sets the correct status for a completed lettings log" do
complete_lettings_log = build(:lettings_log, :completed, assigned_to: assigned_to_user)
complete_lettings_log.save!
expect(complete_lettings_log.reload.status).to eq "completed"
end
it "returns the correct status for an in progress lettings log" do
in_progress_lettings_log = build(:lettings_log, :in_progress, assigned_to: assigned_to_user)
in_progress_lettings_log.save!
expect(in_progress_lettings_log.reload.status).to eq "in_progress"
end
it "recalculates the status if it's currently set incorrectly" do
complete_lettings_log = build(:lettings_log, :completed, assigned_to: assigned_to_user, status: "in_progress")
complete_lettings_log.save!
expect(complete_lettings_log.reload.status).to eq "completed"
end
it "recalculates status_cache if the log is pending" do
complete_lettings_log = build(:lettings_log, :completed, assigned_to: assigned_to_user, status_cache: "in_progress", status: "pending")
complete_lettings_log.save!
expect(complete_lettings_log.reload.status_cache).to eq "completed"
expect(complete_lettings_log.status).to eq "pending"
end
end
describe "weekly_net_income" do

11
spec/models/log_spec.rb

@ -29,6 +29,17 @@ RSpec.describe Log, type: :model do
in_progress_lettings_log = build(:lettings_log, :in_progress, assigned_to: user)
expect(in_progress_lettings_log.calculate_status).to eq "in_progress"
end
it "recalculates the status if it's currently set incorrectly" do
complete_lettings_log = build(:lettings_log, :completed, assigned_to: user, status: "in_progress")
expect(complete_lettings_log.calculate_status).to eq "completed"
end
it "recalculates status_cache if the log is pending" do
complete_lettings_log = build(:lettings_log, :completed, assigned_to: user, status_cache: "in_progress", status: "pending")
expect(complete_lettings_log.calculate_status).to eq "completed"
expect(complete_lettings_log.calculate_status).to eq "completed"
end
end
describe "#blank_invalid_non_setup_fields!" do

11
spec/models/user_spec.rb

@ -726,15 +726,8 @@ RSpec.describe User, type: :model do
context "and the user has pending logs assigned to them" do
let(:lettings_bu) { create(:bulk_upload, :lettings) }
let(:sales_bu) { create(:bulk_upload, :sales) }
let!(:pending_lettings_log) { build(:lettings_log, status: "pending", assigned_to: user, bulk_upload: lettings_bu) }
let!(:pending_sales_log) { build(:sales_log, status: "pending", assigned_to: user, bulk_upload: sales_bu) }
before do
pending_lettings_log.skip_update_status = true
pending_lettings_log.save!
pending_sales_log.skip_update_status = true
pending_sales_log.save!
end
let!(:pending_lettings_log) { create(:lettings_log, status: "pending", assigned_to: user, bulk_upload: lettings_bu) }
let!(:pending_sales_log) { create(:sales_log, status: "pending", assigned_to: user, bulk_upload: sales_bu) }
it "sets choice for fixing the logs to cancelled-by-moved-user" do
user.reassign_logs_and_update_organisation(new_organisation, "reassign_all")

28
spec/models/validations/soft_validations_spec.rb

@ -174,6 +174,20 @@ RSpec.describe Validations::SoftValidations do
end
end
context "when all tenants are male and household members are over 8" do
it "does not show the interruption screen" do
(1..8).each do |n|
record.send("sex#{n}=", "M")
record.send("age#{n}=", 30)
record.send("age#{n}_known=", 0)
record.send("details_known_#{n}=", 0) unless n == 1
end
record.preg_occ = 1
record.hhmemb = 9
expect(record.all_male_tenants_in_a_pregnant_household?).to be false
end
end
context "when female tenants are under 16" do
it "shows the interruption screen" do
record.age2 = 14
@ -219,6 +233,20 @@ RSpec.describe Validations::SoftValidations do
expect(record.female_in_pregnant_household_in_soft_validation_range?).to be false
end
end
context "when number of household members is over 8" do
it "does not show the interruption screen" do
(1..8).each do |n|
record.send("sex#{n}=", "F")
record.send("age#{n}=", 50)
record.send("age#{n}_known=", 0)
record.send("details_known_#{n}=", 0) unless n == 1
end
record.preg_occ = 1
record.hhmemb = 9
expect(record.female_in_pregnant_household_in_soft_validation_range?).to be false
end
end
end
describe "major repairs date soft validations" do

2
spec/rails_helper.rb

@ -51,7 +51,7 @@ rescue ActiveRecord::PendingMigrationError => e
end
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.fixture_paths = ["#{::Rails.root}/spec/fixtures"]
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false

4
spec/requests/auth/passwords_controller_spec.rb

@ -70,7 +70,7 @@ RSpec.describe Auth::PasswordsController, type: :request do
put "/account/password", params: update_password_params
# Devise redirects once after re-sign in with new password and then root redirects as well.
follow_redirect!
expect(page).to have_css("div", class: "govuk-notification-banner__heading", text: message)
expect(page).to have_css("p", class: "govuk-notification-banner__heading", text: message)
end
end
end
@ -107,7 +107,7 @@ RSpec.describe Auth::PasswordsController, type: :request do
it "shows an error on the same page" do
put "/account/password", headers: headers, params: params
expect(response).to have_http_status(:unprocessable_entity)
expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_css("h1", text: "Reset your password")
expect(page).to have_content("passwords you entered do not match")
end

8
spec/requests/bulk_upload_lettings_results_controller_spec.rb

@ -68,7 +68,7 @@ RSpec.describe BulkUploadLettingsResultsController, type: :request do
get "/lettings-logs/bulk-upload-results/#{bulk_upload.id}/summary"
expect(response.body).to include("This error report is out of date.")
expect(response.body).to include("Some logs in this upload are assigned to #{user.name}, who has moved to a different organisation since this file was uploaded. Reupload the file to get an accurate error report.")
expect(CGI.unescapeHTML(response.body)).to include("Some logs in this upload are assigned to #{user.name}, who has moved to a different organisation since this file was uploaded. Upload the file again to get an accurate error report.")
end
end
@ -79,7 +79,7 @@ RSpec.describe BulkUploadLettingsResultsController, type: :request do
get "/lettings-logs/bulk-upload-results/#{bulk_upload.id}/summary"
expect(response.body).to include("This error report is out of date.")
expect(response.body).to include("You moved to a different organisation since this file was uploaded. Reupload the file to get an accurate error report.")
expect(response.body).to include("You moved to a different organisation since this file was uploaded. Upload the file again to get an accurate error report.")
end
end
end
@ -138,7 +138,7 @@ RSpec.describe BulkUploadLettingsResultsController, type: :request do
get "/lettings-logs/bulk-upload-results/#{bulk_upload.id}/summary"
expect(response.body).to include("This error report is out of date.")
expect(response.body).to include("Some logs in this upload are assigned to #{other_user.name}, who has moved to a different organisation since this file was uploaded. Reupload the file to get an accurate error report.")
expect(response.body).to include("Some logs in this upload are assigned to #{other_user.name}, who has moved to a different organisation since this file was uploaded. Upload the file again to get an accurate error report.")
end
end
@ -149,7 +149,7 @@ RSpec.describe BulkUploadLettingsResultsController, type: :request do
get "/lettings-logs/bulk-upload-results/#{bulk_upload.id}/summary"
expect(response.body).to include("This error report is out of date.")
expect(response.body).to include("You moved to a different organisation since this file was uploaded. Reupload the file to get an accurate error report.")
expect(response.body).to include("You moved to a different organisation since this file was uploaded. Upload the file again to get an accurate error report.")
end
end
end

2
spec/requests/bulk_upload_lettings_soft_validations_check_controller_spec.rb

@ -98,7 +98,7 @@ RSpec.describe BulkUploadLettingsSoftValidationsCheckController, type: :request
expect(response).to be_successful
expect(response.body).to include("You have chosen to upload all logs from this bulk upload.")
expect(response.body).to include("Are you sure you want to upload all logs from this bulk upload?")
expect(response.body).to include("You will upload 2 logs. There are unexpected answers in 2 logs, and 2 unexpected answers in total. These unexpected answers will be marked as correct.")
expect(response.body).not_to include("You’ve successfully uploaded")
end

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

Loading…
Cancel
Save