Browse Source

Merge branch 'CLDC-4173-new-building-height-question-sales' into CLDC-4178-add-gender-same-as-sex-question-for-sales

pull/3188/head
Nat Dean-Lewis 3 months ago
parent
commit
059da597af
  1. 0
      .erb_lint.yml
  2. 5
      .rubocop.yml
  3. 2
      .ruby-version
  4. 4
      Dockerfile
  5. 11
      Gemfile
  6. 111
      Gemfile.lock
  7. 2
      app/controllers/csv_downloads_controller.rb
  8. 2
      app/controllers/lettings_logs_controller.rb
  9. 2
      app/controllers/lettings_logs_filters_controller.rb
  10. 2
      app/controllers/merge_requests_controller.rb
  11. 2
      app/controllers/sales_logs_filters_controller.rb
  12. 2
      app/helpers/bulk_upload/lettings_log_to_csv.rb
  13. 2
      app/helpers/bulk_upload/sales_log_to_csv.rb
  14. 2
      app/helpers/data_sharing_agreement_helper.rb
  15. 2
      app/helpers/form_page_error_helper.rb
  16. 3
      app/helpers/locations_helper.rb
  17. 4
      app/helpers/merge_requests_helper.rb
  18. 4
      app/helpers/notifications_helper.rb
  19. 3
      app/helpers/question_view_helper.rb
  20. 3
      app/helpers/schemes_helper.rb
  21. 3
      app/mailers/devise_notify_mailer.rb
  22. 10
      app/models/derived_variables/lettings_log_variables.rb
  23. 2
      app/models/derived_variables/shared_logic.rb
  24. 24
      app/models/form/lettings/pages/tenancyother_value_check.rb
  25. 13
      app/models/form/lettings/questions/tenancyother_value_check.rb
  26. 1
      app/models/form/lettings/subsections/tenancy_information.rb
  27. 3
      app/models/form/sales/pages/deposit.rb
  28. 6
      app/models/form/sales/questions/deposit_amount.rb
  29. 3
      app/models/form/sales/questions/mortgage_amount.rb
  30. 3
      app/models/form/sales/questions/mortgageused.rb
  31. 3
      app/models/form/sales/questions/purchase_price.rb
  32. 2
      app/models/form/subsection.rb
  33. 6
      app/models/lettings_log.rb
  34. 2
      app/models/sales_log.rb
  35. 2
      app/models/validations/sales/sale_information_validations.rb
  36. 2
      app/models/validations/sales/soft_validations.rb
  37. 10
      app/models/validations/shared_validations.rb
  38. 10
      app/models/validations/soft_validations.rb
  39. 2
      app/policies/location_policy.rb
  40. 2
      app/policies/organisation_policy.rb
  41. 2
      app/policies/scheme_policy.rb
  42. 22
      app/services/bulk_upload/lettings/year2023/row_parser.rb
  43. 10
      app/services/bulk_upload/lettings/year2024/row_parser.rb
  44. 10
      app/services/bulk_upload/lettings/year2025/row_parser.rb
  45. 10
      app/services/bulk_upload/lettings/year2026/row_parser.rb
  46. 43
      app/services/bulk_upload/sales/year2023/row_parser.rb
  47. 40
      app/services/bulk_upload/sales/year2024/row_parser.rb
  48. 39
      app/services/bulk_upload/sales/year2025/row_parser.rb
  49. 39
      app/services/bulk_upload/sales/year2026/row_parser.rb
  50. 6
      app/services/csv/lettings_log_csv_service.rb
  51. 3
      app/services/csv/sales_log_csv_service.rb
  52. 2
      app/services/documentation_generator.rb
  53. 2
      app/services/exports/organisation_export_constants.rb
  54. 2
      app/services/filter_manager.rb
  55. 2
      app/views/form/_checkbox_question.html.erb
  56. 4
      app/views/form/_interruption_screen_question.html.erb
  57. 4
      app/views/form/_radio_question.html.erb
  58. 2
      app/views/rails_admin/main/_submit_buttons.html.erb
  59. 2
      app/views/rails_admin/main/dashboard.html.erb
  60. 2
      app/views/rails_admin/main/delete.html.erb
  61. 8
      config/initializers/multi_logger.rb
  62. 2
      config/initializers/sidekiq.rb
  63. 17
      config/locales/forms/2026/lettings/soft_validations.en.yml
  64. 4
      config/routes.rb
  65. 2
      db/migrate/20241206142943_create_active_storage_variant_records.active_storage.rb
  66. 5
      db/migrate/20260212091506_add_tenancyother_value_check_to_lettings_logs.rb
  67. 1
      db/schema.rb
  68. 4
      docs/setup.md
  69. 2
      lib/tasks/generate_lettings_documentation.rake
  70. 2
      lib/tasks/generate_sales_documentation.rake
  71. 4
      lib/tasks/handle_unpended_logs.rake
  72. 2
      lib/tasks/lint.rake
  73. 2
      lib/tasks/set_log_validation_collection_year.rake
  74. 2
      spec/components/bulk_upload_error_row_component_spec.rb
  75. 4
      spec/components/check_answers_summary_list_card_component_spec.rb
  76. 16
      spec/components/create_log_actions_component_spec.rb
  77. 28
      spec/components/data_protection_confirmation_banner_component_spec.rb
  78. 3
      spec/db/seeds_spec.rb
  79. 2
      spec/factories/data_protection_confirmation.rb
  80. 2
      spec/features/accessibility_spec.rb
  81. 4
      spec/features/bulk_upload_lettings_logs_spec.rb
  82. 4
      spec/features/bulk_upload_sales_logs_spec.rb
  83. 20
      spec/features/form/accessible_autocomplete_spec.rb
  84. 6
      spec/features/form/address_search_spec.rb
  85. 2
      spec/features/form/checkboxes_spec.rb
  86. 6
      spec/features/form/conditional_questions_spec.rb
  87. 28
      spec/features/form/page_routing_spec.rb
  88. 4
      spec/features/form/progressive_total_field_spec.rb
  89. 2
      spec/features/form/saving_data_spec.rb
  90. 2
      spec/features/form/validations_spec.rb
  91. 6
      spec/features/lettings_log_spec.rb
  92. 4
      spec/features/sales_log_spec.rb
  93. 4
      spec/features/schemes_spec.rb
  94. 10
      spec/features/user_spec.rb
  95. 6
      spec/fixtures/files/lettings_log_csv_export_codes_26.csv
  96. 6
      spec/fixtures/files/lettings_log_csv_export_labels_26.csv
  97. 2
      spec/helpers/collection_resources_helper_spec.rb
  98. 4
      spec/helpers/question_attribute_helper_spec.rb
  99. 2
      spec/helpers/tag_helper_spec.rb
  100. 8
      spec/helpers/tasklist_helper_spec.rb
  101. Some files were not shown because too many files have changed in this diff Show More

0
.erb-lint.yml → .erb_lint.yml

5
.rubocop.yml

@ -1,5 +1,7 @@
require: require:
- rubocop-performance - rubocop-performance
plugins:
- rubocop-rails - rubocop-rails
- rubocop-rspec - rubocop-rspec
@ -28,3 +30,6 @@ Rails/UnknownEnv:
- development - development
- test - test
- review - review
RSpec/IndexedLet:
Enabled: false

2
.ruby-version

@ -1 +1 @@
3.1.6 3.4.4

4
Dockerfile

@ -1,4 +1,4 @@
FROM ruby:3.1.6-alpine3.20 as base FROM ruby:3.4.4-alpine3.20 as base
WORKDIR /app WORKDIR /app
@ -10,7 +10,7 @@ RUN apk add --update --no-cache tzdata && \
# build-base: compilation tools for bundle # build-base: compilation tools for bundle
# yarn: node package manager # yarn: node package manager
# postgresql-dev: postgres driver and libraries # postgresql-dev: postgres driver and libraries
RUN apk add --no-cache build-base=0.5-r3 busybox=1.36.1-r29 nodejs=20.15.1-r0 yarn=1.22.22-r0 bash=5.2.26-r0 libpq-dev RUN apk add --no-cache build-base=0.5-r3 busybox=1.36.1-r29 nodejs=20.15.1-r0 yarn=1.22.22-r0 bash=5.2.26-r0 libpq-dev yaml-dev linux-headers
# Bundler version should be the same version as what the Gemfile.lock was bundled with # Bundler version should be the same version as what the Gemfile.lock was bundled with
RUN gem install bundler:2.6.4 --no-document RUN gem install bundler:2.6.4 --no-document

11
Gemfile

@ -3,7 +3,7 @@
source "https://rubygems.org" source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" } git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby "3.1.6" ruby "3.4.4"
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails', branch: 'main' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails', branch: 'main'
gem "rails", "~> 7.2.2" gem "rails", "~> 7.2.2"
@ -51,7 +51,7 @@ gem "paper_trail-globalid"
gem "pundit" gem "pundit"
# Request rate limiting # Request rate limiting
gem "rack", ">= 2.2.6.3" gem "rack", "~> 3.1.20"
gem "rack-attack" gem "rack-attack"
gem "redis", "~> 4.8" gem "redis", "~> 4.8"
# Receive exceptions and configure alerts # Receive exceptions and configure alerts
@ -72,9 +72,12 @@ gem "sidekiq-cron"
gem "unread" gem "unread"
# Pinning versions to address vulnerabilities # Pinning versions to address vulnerabilities
gem "nokogiri", "~> 1.18.9" gem "nokogiri", "~> 1.19.1"
gem "thor", "~> 1.4.0" gem "thor", "~> 1.4.0"
# Pinning until activesupport is updated to v8.1.2
gem "connection_pool", "~> 2.5"
group :development, :test do group :development, :test do
# Check gems for known vulnerabilities # Check gems for known vulnerabilities
gem "bundler-audit" gem "bundler-audit"
@ -95,7 +98,7 @@ group :development do
# Can be configured to work on production as well see: https://github.com/MiniProfiler/rack-mini-profiler/blob/master/README.md # Can be configured to work on production as well see: https://github.com/MiniProfiler/rack-mini-profiler/blob/master/README.md
gem "erb_lint", require: false gem "erb_lint", require: false
gem "rack-mini-profiler", "~> 3.3.0" gem "rack-mini-profiler", "~> 3.3.0"
gem "rubocop-govuk", "4.3.0", require: false gem "rubocop-govuk", "5.2.0", require: false
gem "rubocop-performance", require: false gem "rubocop-performance", require: false
gem "rubocop-rails", require: false gem "rubocop-rails", require: false
end end

111
Gemfile.lock

@ -78,7 +78,7 @@ GEM
tzinfo (~> 2.0, >= 2.0.5) tzinfo (~> 2.0, >= 2.0.5)
addressable (2.8.6) addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0) public_suffix (>= 2.0.2, < 6.0)
ast (2.4.2) ast (2.4.3)
auto_strip_attributes (2.6.0) auto_strip_attributes (2.6.0)
activerecord (>= 4.0) activerecord (>= 4.0)
aws-eventstream (1.4.0) aws-eventstream (1.4.0)
@ -113,15 +113,15 @@ GEM
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
base64 (0.3.0) base64 (0.3.0)
bcrypt (3.1.20) bcrypt (3.1.20)
benchmark (0.4.1) benchmark (0.5.0)
better_html (2.0.2) better_html (2.2.0)
actionview (>= 6.0) actionview (>= 7.0)
activesupport (>= 6.0) activesupport (>= 7.0)
ast (~> 2.0) ast (~> 2.0)
erubi (~> 1.4) erubi (~> 1.4)
parser (>= 2.4) parser (>= 2.4)
smart_properties smart_properties
bigdecimal (3.2.2) bigdecimal (4.0.1)
bindex (0.8.1) bindex (0.8.1)
bootsnap (1.18.3) bootsnap (1.18.3)
msgpack (~> 1.2) msgpack (~> 1.2)
@ -151,7 +151,7 @@ GEM
coderay (1.1.3) coderay (1.1.3)
coercible (1.0.0) coercible (1.0.0)
descendants_tracker (~> 0.0.1) descendants_tracker (~> 0.0.1)
concurrent-ruby (1.3.5) concurrent-ruby (1.3.6)
connection_pool (2.5.3) connection_pool (2.5.3)
crack (1.0.0) crack (1.0.0)
bigdecimal bigdecimal
@ -184,12 +184,12 @@ GEM
drb (2.2.3) drb (2.2.3)
dumb_delegator (1.0.0) dumb_delegator (1.0.0)
encryptor (3.0.0) encryptor (3.0.0)
erb_lint (0.5.0) erb_lint (0.9.0)
activesupport activesupport
better_html (>= 2.0.1) better_html (>= 2.0.1)
parser (>= 2.7.1.4) parser (>= 2.7.1.4)
rainbow rainbow
rubocop rubocop (>= 1)
smart_properties smart_properties
erubi (1.13.1) erubi (1.13.1)
et-orbi (1.2.11) et-orbi (1.2.11)
@ -232,7 +232,7 @@ GEM
hashdiff (1.1.0) hashdiff (1.1.0)
html-attributes-utils (1.0.2) html-attributes-utils (1.0.2)
activesupport (>= 6.1.4.4) activesupport (>= 6.1.4.4)
i18n (1.14.7) i18n (1.14.8)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
ice_nine (0.11.2) ice_nine (0.11.2)
iniparse (1.5.0) iniparse (1.5.0)
@ -261,13 +261,15 @@ GEM
activerecord activerecord
kaminari-core (= 1.2.2) kaminari-core (= 1.2.2)
kaminari-core (1.2.2) kaminari-core (1.2.2)
language_server-protocol (3.17.0.5)
launchy (2.5.2) launchy (2.5.2)
addressable (~> 2.8) addressable (~> 2.8)
lint_roller (1.1.0)
listen (3.9.0) listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3) rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10) rb-inotify (~> 0.9, >= 0.9.10)
logger (1.7.0) logger (1.7.0)
loofah (2.24.0) loofah (2.25.0)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.12.0) nokogiri (>= 1.12.0)
mail (2.8.1) mail (2.8.1)
@ -279,7 +281,9 @@ GEM
matrix (0.4.2) matrix (0.4.2)
method_source (1.1.0) method_source (1.1.0)
mini_mime (1.1.5) mini_mime (1.1.5)
minitest (5.25.5) minitest (6.0.2)
drb (~> 2.0)
prism (~> 1.5)
msgpack (1.7.2) msgpack (1.7.2)
multipart-post (2.4.1) multipart-post (2.4.1)
nested_form (0.3.2) nested_form (0.3.2)
@ -295,13 +299,13 @@ GEM
net-smtp (0.5.1) net-smtp (0.5.1)
net-protocol net-protocol
nio4r (2.7.4) nio4r (2.7.4)
nokogiri (1.18.9-arm64-darwin) nokogiri (1.19.1-arm64-darwin)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.9-x86_64-darwin) nokogiri (1.19.1-x86_64-darwin)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.9-x86_64-linux-gnu) nokogiri (1.19.1-x86_64-linux-gnu)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.9-x86_64-linux-musl) nokogiri (1.19.1-x86_64-linux-musl)
racc (~> 1.4) racc (~> 1.4)
notifications-ruby-client (6.0.0) notifications-ruby-client (6.0.0)
jwt (>= 1.5, < 3) jwt (>= 1.5, < 3)
@ -317,10 +321,10 @@ GEM
paper_trail-globalid (0.2.0) paper_trail-globalid (0.2.0)
globalid globalid
paper_trail (>= 3.0.0) paper_trail (>= 3.0.0)
parallel (1.24.0) parallel (1.27.0)
parallel_tests (4.5.1) parallel_tests (4.5.1)
parallel parallel
parser (3.3.0.5) parser (3.3.10.2)
ast (~> 2.4.1) ast (~> 2.4.1)
racc racc
pg (1.5.5) pg (1.5.5)
@ -328,6 +332,7 @@ GEM
pp (0.6.2) pp (0.6.2)
prettyprint prettyprint
prettyprint (0.2.0) prettyprint (0.2.0)
prism (1.9.0)
propshaft (0.8.0) propshaft (0.8.0)
actionpack (>= 7.0.0) actionpack (>= 7.0.0)
activesupport (>= 7.0.0) activesupport (>= 7.0.0)
@ -349,7 +354,7 @@ GEM
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
raabro (1.4.0) raabro (1.4.0)
racc (1.8.1) racc (1.8.1)
rack (3.1.18) rack (3.1.20)
rack-attack (6.7.0) rack-attack (6.7.0)
rack (>= 1.0, < 4) rack (>= 1.0, < 4)
rack-mini-profiler (3.3.1) rack-mini-profiler (3.3.1)
@ -375,7 +380,7 @@ GEM
activesupport (= 7.2.2.2) activesupport (= 7.2.2.2)
bundler (>= 1.15.0) bundler (>= 1.15.0)
railties (= 7.2.2.2) railties (= 7.2.2.2)
rails-dom-testing (2.2.0) rails-dom-testing (2.3.0)
activesupport (>= 5.0.0) activesupport (>= 5.0.0)
minitest minitest
nokogiri (>= 1.6) nokogiri (>= 1.6)
@ -409,7 +414,7 @@ GEM
redis (4.8.1) redis (4.8.1)
redis-client (0.22.1) redis-client (0.22.1)
connection_pool connection_pool
regexp_parser (2.9.0) regexp_parser (2.11.3)
reline (0.6.0) reline (0.6.0)
io-console (~> 0.5) io-console (~> 0.5)
request_store (1.7.0) request_store (1.7.0)
@ -439,34 +444,45 @@ GEM
rspec-mocks (~> 3.12) rspec-mocks (~> 3.12)
rspec-support (~> 3.12) rspec-support (~> 3.12)
rspec-support (3.13.1) rspec-support (3.13.1)
rubocop (1.25.0) rubocop (1.82.1)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (~> 1.10) parallel (~> 1.10)
parser (>= 3.1.0.0) parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0) regexp_parser (>= 2.9.3, < 3.0)
rexml rubocop-ast (>= 1.48.0, < 2.0)
rubocop-ast (>= 1.15.1, < 2.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0) unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.15.1) rubocop-ast (1.49.0)
parser (>= 3.0.1.1) parser (>= 3.3.7.2)
rubocop-govuk (4.3.0) prism (~> 1.7)
rubocop (= 1.25.0) rubocop-capybara (2.22.1)
rubocop-ast (= 1.15.1) lint_roller (~> 1.1)
rubocop-rails (= 2.13.2) rubocop (~> 1.72, >= 1.72.1)
rubocop-rake (= 0.6.0) rubocop-govuk (5.2.0)
rubocop-rspec (= 2.7.0) rubocop (= 1.82.1)
rubocop-ast (= 1.49.0)
rubocop-capybara (= 2.22.1)
rubocop-rails (= 2.34.3)
rubocop-rake (= 0.7.1)
rubocop-rspec (= 3.9.0)
rubocop-performance (1.19.1) rubocop-performance (1.19.1)
rubocop (>= 1.7.0, < 2.0) rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0) rubocop-ast (>= 0.4.0)
rubocop-rails (2.13.2) rubocop-rails (2.34.3)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
lint_roller (~> 1.1)
rack (>= 1.1) rack (>= 1.1)
rubocop (>= 1.7.0, < 2.0) rubocop (>= 1.75.0, < 2.0)
rubocop-rake (0.6.0) rubocop-ast (>= 1.44.0, < 2.0)
rubocop (~> 1.0) rubocop-rake (0.7.1)
rubocop-rspec (2.7.0) lint_roller (~> 1.1)
rubocop (~> 1.19) rubocop (>= 1.72.1)
rubocop-rspec (3.9.0)
lint_roller (~> 1.1)
rubocop (~> 1.81)
ruby-openai (7.0.1) ruby-openai (7.0.1)
event_stream_parser (>= 0.3.0, < 2.0.0) event_stream_parser (>= 0.3.0, < 2.0.0)
faraday (>= 1) faraday (>= 1)
@ -514,7 +530,9 @@ GEM
tzinfo (2.0.6) tzinfo (2.0.6)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
uk_postcode (2.1.8) uk_postcode (2.1.8)
unicode-display_width (2.5.0) unicode-display_width (3.2.0)
unicode-emoji (~> 4.1)
unicode-emoji (4.2.0)
unread (0.14.0) unread (0.14.0)
activerecord (>= 6.1) activerecord (>= 6.1)
uri (1.0.4) uri (1.0.4)
@ -563,6 +581,7 @@ DEPENDENCIES
capybara capybara
capybara-lockstep capybara-lockstep
capybara-screenshot capybara-screenshot
connection_pool (~> 2.5)
cssbundling-rails cssbundling-rails
devise devise
devise_two_factor_authentication devise_two_factor_authentication
@ -579,7 +598,7 @@ DEPENDENCIES
json-schema json-schema
listen (~> 3.3) listen (~> 3.3)
method_source (~> 1.1) method_source (~> 1.1)
nokogiri (~> 1.18.9) nokogiri (~> 1.19.1)
notifications-ruby-client notifications-ruby-client
overcommit (>= 0.37.0) overcommit (>= 0.37.0)
paper_trail (~> 15.2) paper_trail (~> 15.2)
@ -591,7 +610,7 @@ DEPENDENCIES
pry-byebug pry-byebug
puma (~> 6.4) puma (~> 6.4)
pundit pundit
rack (>= 2.2.6.3) rack (~> 3.1.20)
rack-attack rack-attack
rack-mini-profiler (~> 3.3.0) rack-mini-profiler (~> 3.3.0)
rails (~> 7.2.2) rails (~> 7.2.2)
@ -600,7 +619,7 @@ DEPENDENCIES
redis (~> 4.8) redis (~> 4.8)
roo roo
rspec-rails rspec-rails
rubocop-govuk (= 4.3.0) rubocop-govuk (= 5.2.0)
rubocop-performance rubocop-performance
rubocop-rails rubocop-rails
ruby-openai ruby-openai
@ -621,7 +640,7 @@ DEPENDENCIES
webmock webmock
RUBY VERSION RUBY VERSION
ruby 3.1.6p260 ruby 3.4.4p0
BUNDLED WITH BUNDLED WITH
2.6.4 2.6.4

2
app/controllers/csv_downloads_controller.rb

@ -5,7 +5,7 @@ class CsvDownloadsController < ApplicationController
@csv_download = CsvDownload.find(params[:id]) @csv_download = CsvDownload.find(params[:id])
authorize @csv_download authorize @csv_download
return render "errors/download_link_expired" if @csv_download.expired? render "errors/download_link_expired" if @csv_download.expired?
end end
def download def download

2
app/controllers/lettings_logs_controller.rb

@ -190,7 +190,7 @@ private
end end
def resolve_logs! def resolve_logs!
if @log&.unresolved && @log.location.present? && @log.scheme.present? && @log&.resolve! if @log&.unresolved && @log.location.present? && @log.scheme.present? && @log.resolve!
unresolved_logs_count_for_user = current_user.lettings_logs.unresolved.assigned_to(current_user).count unresolved_logs_count_for_user = current_user.lettings_logs.unresolved.assigned_to(current_user).count
flash.now[:notice] = helpers.flash_notice_for_resolved_logs(unresolved_logs_count_for_user) flash.now[:notice] = helpers.flash_notice_for_resolved_logs(unresolved_logs_count_for_user)
end end

2
app/controllers/lettings_logs_filters_controller.rb

@ -52,8 +52,6 @@ class LettingsLogsFiltersController < ApplicationController
end end
end end
private
def lettings_session_filters def lettings_session_filters
params["years"] = [params["years"]] if params["years"].present? params["years"] = [params["years"]] if params["years"].present?
lettings_filter_manager.session_filters lettings_filter_manager.session_filters

2
app/controllers/merge_requests_controller.rb

@ -105,7 +105,7 @@ private
answer_options = { "" => "Select an option" } answer_options = { "" => "Select an option" }
if current_user.support? if current_user.support?
Organisation.all.each do |organisation| Organisation.all.find_each do |organisation|
date = @merge_request.merge_date || Time.zone.today date = @merge_request.merge_date || Time.zone.today
answer_options[organisation.id] = organisation.name(date:) answer_options[organisation.id] = organisation.name(date:)
end end

2
app/controllers/sales_logs_filters_controller.rb

@ -53,8 +53,6 @@ class SalesLogsFiltersController < ApplicationController
end end
end end
private
def sales_session_filters def sales_session_filters
params["years"] = [params["years"]] if params["years"].present? params["years"] = [params["years"]] if params["years"].present?
sales_filter_manager.session_filters sales_filter_manager.session_filters

2
app/helpers/bulk_upload/lettings_log_to_csv.rb

@ -2,12 +2,10 @@ class BulkUpload::LettingsLogToCsv
attr_reader :log, :line_ending, :col_offset, :overrides attr_reader :log, :line_ending, :col_offset, :overrides
def initialize(log:, line_ending: "\n", col_offset: 1, overrides: {}) def initialize(log:, line_ending: "\n", col_offset: 1, overrides: {})
# rubocop:disable Rails/HelperInstanceVariable
@log = log @log = log
@line_ending = line_ending @line_ending = line_ending
@col_offset = col_offset @col_offset = col_offset
@overrides = overrides @overrides = overrides
# rubocop:enable Rails/HelperInstanceVariable
end end
def row_prefix def row_prefix

2
app/helpers/bulk_upload/sales_log_to_csv.rb

@ -2,12 +2,10 @@ class BulkUpload::SalesLogToCsv
attr_reader :log, :line_ending, :col_offset, :overrides attr_reader :log, :line_ending, :col_offset, :overrides
def initialize(log:, line_ending: "\n", col_offset: 1, overrides: {}) def initialize(log:, line_ending: "\n", col_offset: 1, overrides: {})
# rubocop:disable Rails/HelperInstanceVariable
@log = log @log = log
@line_ending = line_ending @line_ending = line_ending
@col_offset = col_offset @col_offset = col_offset
@overrides = overrides @overrides = overrides
# rubocop:enable Rails/HelperInstanceVariable
end end
def row_prefix def row_prefix

2
app/helpers/data_sharing_agreement_helper.rb

@ -67,7 +67,7 @@ module DataSharingAgreementHelper
"12.2. For #{@org_name}: Name: #{@dpo_name}, Postal Address: #{@org_address}, E-mail address: #{@dpo_email}, Telephone number: #{@org_phone}" "12.2. For #{@org_name}: Name: #{@dpo_name}, Postal Address: #{@org_address}, E-mail address: #{@dpo_email}, Telephone number: #{@org_phone}"
end end
end end
# rubocop:enable Rails/HelperInstanceVariable # rubocop:enable Rails/HelperInstanceVariable
private private

2
app/helpers/form_page_error_helper.rb

@ -5,7 +5,7 @@ module FormPageErrorHelper
end end
def remove_duplicate_page_errors(lettings_log) def remove_duplicate_page_errors(lettings_log)
lettings_log.errors.group_by(&:message).each do |_, errors| lettings_log.errors.group_by(&:message).each_value do |errors|
next if errors.size == 1 next if errors.size == 1
errors.shift errors.shift

3
app/helpers/locations_helper.rb

@ -91,7 +91,8 @@ module LocationsHelper
def toggle_location_link(location) def toggle_location_link(location)
return govuk_button_link_to "Deactivate this location", scheme_location_new_deactivation_path(location.scheme, location), warning: true if location.active? || location.deactivates_in_a_long_time? return govuk_button_link_to "Deactivate this location", scheme_location_new_deactivation_path(location.scheme, location), warning: true if location.active? || location.deactivates_in_a_long_time?
return govuk_button_link_to "Reactivate this location", scheme_location_new_reactivation_path(location.scheme, location) if location.deactivated? && !location.deactivated_by_scheme?
govuk_button_link_to "Reactivate this location", scheme_location_new_reactivation_path(location.scheme, location) if location.deactivated? && !location.deactivated_by_scheme?
end end
def delete_location_link(location) def delete_location_link(location)

4
app/helpers/merge_requests_helper.rb

@ -101,13 +101,13 @@ module MergeRequestsHelper
attribute = page if attribute.nil? attribute = page if attribute.nil?
return nil unless value_exists?(merge_request, attribute) return nil unless value_exists?(merge_request, attribute)
unless merge_request.status == "request_merged" || merge_request.status == "processing" unless %w[request_merged processing].include?(merge_request.status)
{ text: merge_request_action_text(merge_request, attribute), href: send("#{page}_merge_request_path", merge_request, referrer: "check_answers"), visually_hidden_text: page.humanize } { text: merge_request_action_text(merge_request, attribute), href: send("#{page}_merge_request_path", merge_request, referrer: "check_answers"), visually_hidden_text: page.humanize }
end end
end end
def merge_outcome_action(merge_request, page) def merge_outcome_action(merge_request, page)
unless merge_request.status == "request_merged" || merge_request.status == "processing" unless %w[request_merged processing].include?(merge_request.status)
{ text: "View", href: send("#{page}_merge_request_path", merge_request), visually_hidden_text: page.humanize } { text: "View", href: send("#{page}_merge_request_path", merge_request), visually_hidden_text: page.humanize }
end end
end end

4
app/helpers/notifications_helper.rb

@ -57,7 +57,7 @@ class NotificationRenderer < Redcarpet::Render::HTML
def initialize(options = {}) def initialize(options = {})
link_class = "govuk-link" link_class = "govuk-link"
link_class += " govuk-link--inverse" if options[:invert_link_colour] link_class += " govuk-link--inverse" if options[:invert_link_colour]
@bold = options[:bold_all_text] # rubocop:disable Rails/HelperInstanceVariable @bold = options[:bold_all_text]
base_options = { escape_html: true, safe_links_only: true, link_attributes: { class: link_class } } base_options = { escape_html: true, safe_links_only: true, link_attributes: { class: link_class } }
super base_options super base_options
end end
@ -78,7 +78,7 @@ class NotificationRenderer < Redcarpet::Render::HTML
end end
def paragraph(text) def paragraph(text)
return %(<p class="govuk-!-font-weight-bold">#{text}</p>) if @bold # rubocop:disable Rails/HelperInstanceVariable return %(<p class="govuk-!-font-weight-bold">#{text}</p>) if @bold
%(<p class="govuk-body-m">#{text}</p>) %(<p class="govuk-body-m">#{text}</p>)
end end

3
app/helpers/question_view_helper.rb

@ -44,7 +44,8 @@ module QuestionViewHelper
def select_option_name(value) def select_option_name(value)
return value.service_name if value.respond_to?(:service_name) return value.service_name if value.respond_to?(:service_name)
return value["name"] if value.is_a?(Hash) && value["name"].present? return value["name"] if value.is_a?(Hash) && value["name"].present?
return value["postcode"] if value.is_a?(Location)
value["postcode"] if value.is_a?(Location)
end end
private private

3
app/helpers/schemes_helper.rb

@ -12,7 +12,8 @@ module SchemesHelper
def toggle_scheme_link(scheme) def toggle_scheme_link(scheme)
return govuk_button_link_to "Deactivate this scheme", scheme_new_deactivation_path(scheme), warning: true if scheme.active? || scheme.deactivates_in_a_long_time? return govuk_button_link_to "Deactivate this scheme", scheme_new_deactivation_path(scheme), warning: true if scheme.active? || scheme.deactivates_in_a_long_time?
return govuk_button_link_to "Reactivate this scheme", scheme_new_reactivation_path(scheme) if scheme.deactivated? || scheme.deactivating_soon?
govuk_button_link_to "Reactivate this scheme", scheme_new_reactivation_path(scheme) if scheme.deactivated? || scheme.deactivating_soon?
end end
def delete_scheme_link(scheme) def delete_scheme_link(scheme)

3
app/mailers/devise_notify_mailer.rb

@ -92,8 +92,7 @@ class DeviseNotifyMailer < Devise::Mailer
def email_changed?(record) def email_changed?(record)
( (
record.confirmable_template == User::CONFIRMABLE_TEMPLATE_ID && ( record.confirmable_template == User::CONFIRMABLE_TEMPLATE_ID && record.unconfirmed_email.present? && record.unconfirmed_email != record.email
record.unconfirmed_email.present? && record.unconfirmed_email != record.email)
) || ( ) || (
record.versions.last.changeset.key?("unconfirmed_email") && record.versions.last.changeset.key?("unconfirmed_email") &&
record.confirmed? record.confirmed?

10
app/models/derived_variables/lettings_log_variables.rb

@ -254,7 +254,7 @@ private
def clear_inapplicable_derived_values! def clear_inapplicable_derived_values!
reset_invalidated_derived_values!(dependencies) reset_invalidated_derived_values!(dependencies)
if (startdate_changed? || renewal_changed?) && (renewal_was == 1 && startdate_was&.between?(Time.zone.local(2021, 4, 1), Time.zone.local(2022, 3, 31))) if (startdate_changed? || renewal_changed?) && renewal_was == 1 && startdate_was&.between?(Time.zone.local(2021, 4, 1), Time.zone.local(2022, 3, 31))
self.underoccupation_benefitcap = nil self.underoccupation_benefitcap = nil
end end
if renewal_changed? && renewal_was == 1 if renewal_changed? && renewal_was == 1
@ -270,7 +270,7 @@ private
self.wchair = nil self.wchair = nil
self.location_id = nil self.location_id = nil
end end
if form.start_year_2024_or_later? && (unittype_gn_changed? && unittype_gn_was == 2) if form.start_year_2024_or_later? && unittype_gn_changed? && unittype_gn_was == 2
self.beds = nil self.beds = nil
end end
end end
@ -439,13 +439,15 @@ private
def get_lar def get_lar
return 1 if rent_type == 2 return 1 if rent_type == 2
return 2 if rent_type == 1
2 if rent_type == 1
end end
def get_irproduct def get_irproduct
return 1 if rent_type == 3 return 1 if rent_type == 3
return 2 if rent_type == 4 return 2 if rent_type == 4
return 3 if rent_type == 5
3 if rent_type == 5
end end
def clear_gender_description_unless_gender_not_same_as_sex! def clear_gender_description_unless_gender_not_same_as_sex!

2
app/models/derived_variables/shared_logic.rb

@ -9,7 +9,7 @@ private
previously_in_derived_state = dependency[:conditions].all? { |attribute, value| send("#{attribute}_was") == value } previously_in_derived_state = dependency[:conditions].all? { |attribute, value| send("#{attribute}_was") == value }
next unless previously_in_derived_state next unless previously_in_derived_state
dependency[:derived_values].each do |derived_attribute, _derived_value| dependency[:derived_values].each_key do |derived_attribute|
Rails.logger.debug("Cleared derived #{derived_attribute} value") Rails.logger.debug("Cleared derived #{derived_attribute} value")
send("#{derived_attribute}=", nil) send("#{derived_attribute}=", nil)
end end

24
app/models/form/lettings/pages/tenancyother_value_check.rb

@ -0,0 +1,24 @@
class Form::Lettings::Pages::TenancyotherValueCheck < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "tenancyother_value_check"
@copy_key = "lettings.soft_validations.tenancyother_value_check"
@depends_on = [{ "tenancyother_might_be_introductory_or_starter_period?" => true }]
@title_text = {
"translation" => "forms.#{form.start_date.year}.#{@copy_key}.title_text",
"arguments" => [{ "key" => "tenancyother", "i18n_template" => "tenancyother" }],
}
@informative_text = {
"translation" => "forms.#{form.start_date.year}.#{@copy_key}.informative_text",
"arguments" => [],
}
end
def questions
@questions ||= [Form::Lettings::Questions::TenancyotherValueCheck.new(nil, nil, self)]
end
def interruption_screen_question_ids
%w[startertenancy tenancy tenancyother]
end
end

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

@ -0,0 +1,13 @@
class Form::Lettings::Questions::TenancyotherValueCheck < ::Form::Question
def initialize(id, hsh, page)
super
@id = "tenancyother_value_check"
@copy_key = "lettings.soft_validations.tenancyother_value_check"
@type = "interruption_screen"
@check_answers_card_number = 0
@answer_options = ANSWER_OPTIONS
@hidden_in_check_answers = { "depends_on" => [{ "tenancyother_value_check" => 0 }, { "tenancyother_value_check" => 1 }] }
end
ANSWER_OPTIONS = { "0" => { "value" => "Yes" }, "1" => { "value" => "No" } }.freeze
end

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

@ -12,6 +12,7 @@ class Form::Lettings::Subsections::TenancyInformation < ::Form::Subsection
Form::Lettings::Pages::StarterTenancy.new("starter_tenancy", nil, self), Form::Lettings::Pages::StarterTenancy.new("starter_tenancy", nil, self),
Form::Lettings::Pages::TenancyType.new(nil, nil, self), Form::Lettings::Pages::TenancyType.new(nil, nil, self),
Form::Lettings::Pages::StarterTenancyType.new(nil, nil, self), Form::Lettings::Pages::StarterTenancyType.new(nil, nil, self),
(Form::Lettings::Pages::TenancyotherValueCheck.new(nil, nil, self) if form.start_year_2026_or_later?),
Form::Lettings::Pages::TenancyLength.new(nil, nil, self), Form::Lettings::Pages::TenancyLength.new(nil, nil, self),
Form::Lettings::Pages::TenancyLengthAffordableRent.new(nil, nil, self), Form::Lettings::Pages::TenancyLengthAffordableRent.new(nil, nil, self),
Form::Lettings::Pages::TenancyLengthIntermediateRent.new(nil, nil, self), Form::Lettings::Pages::TenancyLengthIntermediateRent.new(nil, nil, self),

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

@ -23,6 +23,7 @@ class Form::Sales::Pages::Deposit < ::Form::Page
def copy_key def copy_key
return "sales.sale_information.deposit.shared_ownership" if @ownershipsch == 1 return "sales.sale_information.deposit.shared_ownership" if @ownershipsch == 1
return "sales.sale_information.deposit.discounted_ownership" if @ownershipsch == 2 return "sales.sale_information.deposit.discounted_ownership" if @ownershipsch == 2
return "sales.sale_information.deposit.outright_sale" if @ownershipsch == 3
"sales.sale_information.deposit.outright_sale" if @ownershipsch == 3
end end
end end

6
app/models/form/sales/questions/deposit_amount.rb

@ -29,12 +29,14 @@ class Form::Sales::Questions::DepositAmount < ::Form::Question
def top_guidance_partial def top_guidance_partial
return "financial_calculations_shared_ownership" if @ownershipsch == 1 return "financial_calculations_shared_ownership" if @ownershipsch == 1
return "financial_calculations_discounted_ownership" if @ownershipsch == 2 return "financial_calculations_discounted_ownership" if @ownershipsch == 2
return "financial_calculations_outright_sale" if @ownershipsch == 3
"financial_calculations_outright_sale" if @ownershipsch == 3
end end
def copy_key def copy_key
return "sales.sale_information.deposit.shared_ownership" if @ownershipsch == 1 return "sales.sale_information.deposit.shared_ownership" if @ownershipsch == 1
return "sales.sale_information.deposit.discounted_ownership" if @ownershipsch == 2 return "sales.sale_information.deposit.discounted_ownership" if @ownershipsch == 2
return "sales.sale_information.deposit.outright_sale" if @ownershipsch == 3
"sales.sale_information.deposit.outright_sale" if @ownershipsch == 3
end end
end end

3
app/models/form/sales/questions/mortgage_amount.rb

@ -26,6 +26,7 @@ class Form::Sales::Questions::MortgageAmount < ::Form::Question
def top_guidance_partial def top_guidance_partial
return "financial_calculations_shared_ownership" if @ownershipsch == 1 return "financial_calculations_shared_ownership" if @ownershipsch == 1
return "financial_calculations_discounted_ownership" if @ownershipsch == 2 return "financial_calculations_discounted_ownership" if @ownershipsch == 2
return "financial_calculations_outright_sale" if @ownershipsch == 3
"financial_calculations_outright_sale" if @ownershipsch == 3
end end
end end

3
app/models/form/sales/questions/mortgageused.rb

@ -41,6 +41,7 @@ class Form::Sales::Questions::Mortgageused < ::Form::Question
def top_guidance_partial def top_guidance_partial
return "financial_calculations_shared_ownership" if @ownershipsch == 1 return "financial_calculations_shared_ownership" if @ownershipsch == 1
return "financial_calculations_discounted_ownership" if @ownershipsch == 2 return "financial_calculations_discounted_ownership" if @ownershipsch == 2
return "financial_calculations_outright_sale" if @ownershipsch == 3
"financial_calculations_outright_sale" if @ownershipsch == 3
end end
end end

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

@ -30,6 +30,7 @@ class Form::Sales::Questions::PurchasePrice < ::Form::Question
def top_guidance_partial def top_guidance_partial
return "financial_calculations_discounted_ownership" if @ownership_sch == 2 return "financial_calculations_discounted_ownership" if @ownership_sch == 2
return "financial_calculations_outright_sale" if @ownership_sch == 3
"financial_calculations_outright_sale" if @ownership_sch == 3
end end
end end

2
app/models/form/subsection.rb

@ -1,5 +1,5 @@
class Form::Subsection class Form::Subsection
attr_accessor :id, :label, :section, :pages, :depends_on, :form attr_accessor :id, :label, :section, :pages, :depends_on
def initialize(id, hsh, section) def initialize(id, hsh, section)
@id = id @id = id

6
app/models/lettings_log.rb

@ -482,7 +482,7 @@ class LettingsLog < Log
def is_london_rent? def is_london_rent?
# 2: London Affordable Rent # 2: London Affordable Rent
# 4: London Living Rent # 4: London Living Rent
rent_type == 2 || rent_type == 4 [2, 4].include?(rent_type)
end end
def previous_tenancy_was_foster_care? def previous_tenancy_was_foster_care?
@ -714,7 +714,7 @@ class LettingsLog < Log
end end
def affordable_or_social_rent? def affordable_or_social_rent?
renttype == 1 || renttype == 2 [1, 2].include?(renttype)
end end
def no_or_unknown_other_housing_needs? def no_or_unknown_other_housing_needs?
@ -931,7 +931,7 @@ private
num_of_weeks = NUM_OF_WEEKS_FROM_PERIOD[period] num_of_weeks = NUM_OF_WEEKS_FROM_PERIOD[period]
return "" unless value && num_of_weeks return "" unless value && num_of_weeks
format_as_currency((value * 52 / num_of_weeks)) format_as_currency(value * 52 / num_of_weeks)
end end
def fully_wheelchair_accessible? def fully_wheelchair_accessible?

2
app/models/sales_log.rb

@ -533,7 +533,7 @@ class SalesLog < Log
end end
def is_not_staircasing? def is_not_staircasing?
staircase == 2 || staircase == 3 [2, 3].include?(staircase)
end end
def stairowned_100? def stairowned_100?

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

@ -136,7 +136,7 @@ module Validations::Sales::SaleInformationValidations
def validate_grant_amount(record) def validate_grant_amount(record)
return unless record.saledate && record.form.start_year_2024_or_later? return unless record.saledate && record.form.start_year_2024_or_later?
return unless record.grant && (record.type == 8 || record.type == 21) return unless record.grant && [8, 21].include?(record.type)
unless record.grant.between?(9_000, 16_000) unless record.grant.between?(9_000, 16_000)
record.errors.add :grant, I18n.t("validations.sales.sale_information.grant.out_of_range") record.errors.add :grant, I18n.t("validations.sales.sale_information.grant.out_of_range")

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

@ -144,7 +144,7 @@ module Validations::Sales::SoftValidations
def grant_outside_common_range? def grant_outside_common_range?
return unless grant && type && saledate return unless grant && type && saledate
return if form.start_year_2024_or_later? && (type == 21 || type == 8) return if form.start_year_2024_or_later? && [21, 8].include?(type)
!grant.between?(9_000, 16_000) !grant.between?(9_000, 16_000)
end end

10
app/models/validations/shared_validations.rb

@ -56,11 +56,11 @@ module Validations::SharedValidations
next unless incorrect_accuracy next unless incorrect_accuracy
case question.step case question.step.to_d
when 0.01 then record.errors.add question.id.to_sym, I18n.t("validations.shared.numeric.nearest_hundredth", field:) when BigDecimal("0.01") then record.errors.add question.id.to_sym, I18n.t("validations.shared.numeric.nearest_hundredth", field:)
when 0.1 then record.errors.add question.id.to_sym, I18n.t("validations.shared.numeric.nearest_tenth", field:) when BigDecimal("0.1") then record.errors.add question.id.to_sym, I18n.t("validations.shared.numeric.nearest_tenth", field:)
when 1 then record.errors.add question.id.to_sym, :not_integer, message: I18n.t("validations.shared.numeric.whole_number", field:) when BigDecimal("1") then record.errors.add question.id.to_sym, :not_integer, message: I18n.t("validations.shared.numeric.whole_number", field:)
when 10 then record.errors.add question.id.to_sym, I18n.t("validations.shared.numeric.nearest_ten", field:) when BigDecimal("10") then record.errors.add question.id.to_sym, I18n.t("validations.shared.numeric.nearest_ten", field:)
else else
record.errors.add question.id.to_sym, I18n.t("validations.shared.numeric.nearest_step", field:, step: question.step) record.errors.add question.id.to_sym, I18n.t("validations.shared.numeric.nearest_step", field:, step: question.step)
end end

10
app/models/validations/soft_validations.rb

@ -194,6 +194,16 @@ module Validations::SoftValidations
PHRASES_LIKELY_TO_INDICATE_EXISTING_REASON_CATEGORY_REGEX.match?(reasonother) PHRASES_LIKELY_TO_INDICATE_EXISTING_REASON_CATEGORY_REGEX.match?(reasonother)
end end
PHRASES_LIKELY_TO_INDICATE_INTRODUCTORY_OR_STARTER_PERIOD = %w[introductory intro starter].freeze
PHRASES_LIKELY_TO_INDICATE_INTRODUCTORY_OR_STARTER_PERIOD_REGEX = Regexp.union(
PHRASES_LIKELY_TO_INDICATE_INTRODUCTORY_OR_STARTER_PERIOD.map { |phrase| Regexp.new("\\b[^[:alpha]]*#{phrase}[^[:alpha:]]*\\b", Regexp::IGNORECASE) },
)
def tenancyother_might_be_introductory_or_starter_period?
PHRASES_LIKELY_TO_INDICATE_INTRODUCTORY_OR_STARTER_PERIOD_REGEX.match?(tenancyother)
end
def multiple_partners? def multiple_partners?
return unless hhmemb return unless hhmemb

2
app/policies/location_policy.rb

@ -32,7 +32,7 @@ class LocationPolicy
def delete? def delete?
return false unless user.support? return false unless user.support?
return false unless location.status == :incomplete || location.status == :deactivated return false unless %i[incomplete deactivated].include?(location.status)
!has_any_logs_in_editable_collection_period !has_any_logs_in_editable_collection_period
end end

2
app/policies/organisation_policy.rb

@ -20,7 +20,7 @@ class OrganisationPolicy
def delete? def delete?
return false unless user.support? return false unless user.support?
return false unless organisation.status == :deactivated || organisation.status == :merged return false unless %i[deactivated merged].include?(organisation.status)
!has_any_logs_in_editable_collection_period !has_any_logs_in_editable_collection_period
end end

2
app/policies/scheme_policy.rb

@ -71,7 +71,7 @@ class SchemePolicy
def delete? def delete?
return false unless user.support? return false unless user.support?
return false unless scheme.status == :incomplete || scheme.status == :deactivated return false unless %i[incomplete deactivated].include?(scheme.status)
!has_any_logs_in_editable_collection_period !has_any_logs_in_editable_collection_period
end end

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

@ -536,7 +536,7 @@ private
end end
def validate_valid_radio_option def validate_valid_radio_option
log.attributes.each do |question_id, _v| log.attributes.each_key do |question_id|
question = log.form.get_question(question_id, log) question = log.form.get_question(question_id, log)
next unless question&.type == "radio" next unless question&.type == "radio"
@ -1321,22 +1321,26 @@ private
def scheme_field def scheme_field
return :field_16 if log_uses_new_scheme_id? return :field_16 if log_uses_new_scheme_id?
return :field_15 if log_uses_old_scheme_id?
:field_15 if log_uses_old_scheme_id?
end end
def scheme_id def scheme_id
return field_16.strip if log_uses_new_scheme_id? return field_16.strip if log_uses_new_scheme_id?
return field_15 if log_uses_old_scheme_id?
field_15 if log_uses_old_scheme_id?
end end
def location_field def location_field
return :field_17 if log_uses_new_scheme_id? return :field_17 if log_uses_new_scheme_id?
return :field_16 if log_uses_old_scheme_id?
:field_16 if log_uses_old_scheme_id?
end end
def location_id def location_id
return field_17 if log_uses_new_scheme_id? return field_17 if log_uses_new_scheme_id?
return field_16 if log_uses_old_scheme_id?
field_16 if log_uses_old_scheme_id?
end end
def scheme_or_management_group def scheme_or_management_group
@ -1424,7 +1428,8 @@ private
].each do |hash| ].each do |hash|
define_method("age#{hash[:person]}_known?") do define_method("age#{hash[:person]}_known?") do
return 1 if public_send(hash[:field]) == "R" return 1 if public_send(hash[:field]) == "R"
return 0 if send("person_#{hash[:person]}_present?")
0 if send("person_#{hash[:person]}_present?")
end end
end end
@ -1488,7 +1493,8 @@ private
def housingneeds_other def housingneeds_other
return 1 if field_86 == 1 return 1 if field_86 == 1
return 0 if [field_83, field_84, field_85].include?(1)
0 if [field_83, field_84, field_85].include?(1)
end end
def prevloc def prevloc
@ -1555,7 +1561,7 @@ private
end end
def earnings def earnings
field_122.round if field_122.present? field_122.presence&.round
end end
def tshortfall_known def tshortfall_known

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

@ -562,7 +562,7 @@ class BulkUpload::Lettings::Year2024::RowParser
private private
def validate_valid_radio_option def validate_valid_radio_option
log.attributes.each do |question_id, _v| log.attributes.each_key do |question_id|
question = log.form.get_question(question_id, log) question = log.form.get_question(question_id, log)
next unless question&.type == "radio" next unless question&.type == "radio"
@ -1455,7 +1455,8 @@ private
].each do |hash| ].each do |hash|
define_method("age#{hash[:person]}_known?") do define_method("age#{hash[:person]}_known?") do
return 1 if public_send(hash[:field]) == "R" return 1 if public_send(hash[:field]) == "R"
return 0 if send("person_#{hash[:person]}_present?")
0 if send("person_#{hash[:person]}_present?")
end end
end end
@ -1519,7 +1520,8 @@ private
def housingneeds_other def housingneeds_other
return 1 if field_82 == 1 return 1 if field_82 == 1
return 0 if [field_79, field_80, field_81].include?(1)
0 if [field_79, field_80, field_81].include?(1)
end end
def prevloc def prevloc
@ -1595,7 +1597,7 @@ private
end end
def earnings def earnings
field_119.round if field_119.present? field_119.presence&.round
end end
def tshortfall_known def tshortfall_known

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

@ -561,7 +561,7 @@ class BulkUpload::Lettings::Year2025::RowParser
private private
def validate_valid_radio_option def validate_valid_radio_option
log.attributes.each do |question_id, _v| log.attributes.each_key do |question_id|
question = log.form.get_question(question_id, log) question = log.form.get_question(question_id, log)
next unless question&.type == "radio" next unless question&.type == "radio"
@ -1452,7 +1452,8 @@ private
].each do |hash| ].each do |hash|
define_method("age#{hash[:person]}_known?") do define_method("age#{hash[:person]}_known?") do
return 1 if public_send(hash[:field]) == "R" return 1 if public_send(hash[:field]) == "R"
return 0 if send("person_#{hash[:person]}_present?")
0 if send("person_#{hash[:person]}_present?")
end end
end end
@ -1516,7 +1517,8 @@ private
def housingneeds_other def housingneeds_other
return 1 if field_82 == 1 return 1 if field_82 == 1
return 0 if [field_79, field_80, field_81].include?(1)
0 if [field_79, field_80, field_81].include?(1)
end end
def prevloc def prevloc
@ -1592,7 +1594,7 @@ private
end end
def earnings def earnings
field_119.round if field_119.present? field_119.presence&.round
end end
def tshortfall_known def tshortfall_known

10
app/services/bulk_upload/lettings/year2026/row_parser.rb

@ -606,7 +606,7 @@ class BulkUpload::Lettings::Year2026::RowParser
private private
def validate_valid_radio_option def validate_valid_radio_option
log.attributes.each do |question_id, _v| log.attributes.each_key do |question_id|
question = log.form.get_question(question_id, log) question = log.form.get_question(question_id, log)
next unless question&.type == "radio" next unless question&.type == "radio"
@ -1573,7 +1573,8 @@ private
].each do |hash| ].each do |hash|
define_method("age#{hash[:person]}_known?") do define_method("age#{hash[:person]}_known?") do
return 1 if public_send(hash[:field]) == "R" return 1 if public_send(hash[:field]) == "R"
return 0 if send("person_#{hash[:person]}_present?")
0 if send("person_#{hash[:person]}_present?")
end end
end end
@ -1637,7 +1638,8 @@ private
def housingneeds_other def housingneeds_other
return 1 if field_82 == 1 return 1 if field_82 == 1
return 0 if [field_79, field_80, field_81].include?(1)
0 if [field_79, field_80, field_81].include?(1)
end end
def prevloc def prevloc
@ -1713,7 +1715,7 @@ private
end end
def earnings def earnings
field_119.round if field_119.present? field_119.presence&.round
end end
def tshortfall_known def tshortfall_known

43
app/services/bulk_upload/sales/year2023/row_parser.rb

@ -915,7 +915,7 @@ private
attributes["mortlen"] = mortlen attributes["mortlen"] = mortlen
attributes["proplen"] = proplen if proplen&.positive? attributes["proplen"] = proplen if proplen&.positive?
attributes["proplen_asked"] = attributes["proplen"]&.present? ? 0 : 1 attributes["proplen_asked"] = attributes["proplen"].present? ? 0 : 1
attributes["jointmore"] = field_15 attributes["jointmore"] = field_15
attributes["staircase"] = field_87 attributes["staircase"] = field_87
attributes["privacynotice"] = field_29 attributes["privacynotice"] = field_29
@ -992,7 +992,8 @@ private
].each do |hash| ].each do |hash|
define_method("age#{hash[:person]}_known?") do define_method("age#{hash[:person]}_known?") do
return 1 if public_send(hash[:field]) == "R" return 1 if public_send(hash[:field]) == "R"
return 0 if send("person_#{hash[:person]}_present?")
0 if send("person_#{hash[:person]}_present?")
end end
end end
@ -1050,72 +1051,84 @@ private
def sale_type def sale_type
return field_8 if shared_ownership? return field_8 if shared_ownership?
return field_9 if discounted_ownership? return field_9 if discounted_ownership?
return field_10 if outright_sale?
field_10 if outright_sale?
end end
def value def value
return field_103 if shared_ownership? return field_103 if shared_ownership?
return field_116 if discounted_ownership? return field_116 if discounted_ownership?
return field_127 if outright_sale?
field_127 if outright_sale?
end end
def mortgage def mortgage
return field_106 if shared_ownership? return field_106 if shared_ownership?
return field_120 if discounted_ownership? return field_120 if discounted_ownership?
return field_129 if outright_sale?
field_129 if outright_sale?
end end
def extrabor def extrabor
return field_110 if shared_ownership? return field_110 if shared_ownership?
return field_124 if discounted_ownership? return field_124 if discounted_ownership?
return field_133 if outright_sale?
field_133 if outright_sale?
end end
def deposit def deposit
return field_111 if shared_ownership? return field_111 if shared_ownership?
return field_125 if discounted_ownership? return field_125 if discounted_ownership?
return field_134 if outright_sale?
field_134 if outright_sale?
end end
def mscharge def mscharge
return field_114 if shared_ownership? return field_114 if shared_ownership?
return field_126 if discounted_ownership? return field_126 if discounted_ownership?
return field_135 if outright_sale?
field_135 if outright_sale?
end end
def mortgagelender def mortgagelender
return field_107 if shared_ownership? return field_107 if shared_ownership?
return field_121 if discounted_ownership? return field_121 if discounted_ownership?
return field_130 if outright_sale?
field_130 if outright_sale?
end end
def mortgagelenderother def mortgagelenderother
return field_108 if shared_ownership? return field_108 if shared_ownership?
return field_122 if discounted_ownership? return field_122 if discounted_ownership?
return field_131 if outright_sale?
field_131 if outright_sale?
end end
def mortlen def mortlen
return field_109 if shared_ownership? return field_109 if shared_ownership?
return field_123 if discounted_ownership? return field_123 if discounted_ownership?
return field_132 if outright_sale?
field_132 if outright_sale?
end end
def proplen def proplen
return field_86 if shared_ownership? return field_86 if shared_ownership?
return field_115 if discounted_ownership?
field_115 if discounted_ownership?
end end
def mortgageused def mortgageused
return field_105 if shared_ownership? return field_105 if shared_ownership?
return field_119 if discounted_ownership? return field_119 if discounted_ownership?
return field_128 if outright_sale?
field_128 if outright_sale?
end end
def mortgageused_field def mortgageused_field
return :field_105 if shared_ownership? return :field_105 if shared_ownership?
return :field_119 if discounted_ownership? return :field_119 if discounted_ownership?
return :field_128 if outright_sale?
:field_128 if outright_sale?
end end
def owning_organisation def owning_organisation
@ -1263,7 +1276,7 @@ private
end end
def validate_valid_radio_option def validate_valid_radio_option
log.attributes.each do |question_id, _v| log.attributes.each_key do |question_id|
question = log.form.get_question(question_id, log) question = log.form.get_question(question_id, log)
next if question_id == "type" next if question_id == "type"

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

@ -938,7 +938,7 @@ private
attributes["mortlen"] = mortlen attributes["mortlen"] = mortlen
attributes["proplen"] = proplen if proplen&.positive? attributes["proplen"] = proplen if proplen&.positive?
attributes["proplen_asked"] = attributes["proplen"]&.present? ? 0 : 1 attributes["proplen_asked"] = attributes["proplen"].present? ? 0 : 1
attributes["jointmore"] = field_16 attributes["jointmore"] = field_16
attributes["staircase"] = field_86 attributes["staircase"] = field_86
attributes["privacynotice"] = field_18 attributes["privacynotice"] = field_18
@ -1026,7 +1026,8 @@ private
].each do |hash| ].each do |hash|
define_method("age#{hash[:person]}_known?") do define_method("age#{hash[:person]}_known?") do
return 1 if public_send(hash[:field]) == "R" return 1 if public_send(hash[:field]) == "R"
return 0 if send("person_#{hash[:person]}_present?")
0 if send("person_#{hash[:person]}_present?")
end end
end end
@ -1084,64 +1085,75 @@ private
def sale_type def sale_type
return field_9 if shared_ownership? return field_9 if shared_ownership?
return field_10 if discounted_ownership? return field_10 if discounted_ownership?
return field_11 if outright_sale?
field_11 if outright_sale?
end end
def value def value
return field_101 if shared_ownership? return field_101 if shared_ownership?
return field_114 if discounted_ownership? return field_114 if discounted_ownership?
return field_125 if outright_sale?
field_125 if outright_sale?
end end
def mortgage def mortgage
return field_104 if shared_ownership? return field_104 if shared_ownership?
return field_118 if discounted_ownership? return field_118 if discounted_ownership?
return field_127 if outright_sale?
field_127 if outright_sale?
end end
def extrabor def extrabor
return field_108 if shared_ownership? return field_108 if shared_ownership?
return field_122 if discounted_ownership? return field_122 if discounted_ownership?
return field_129 if outright_sale?
field_129 if outright_sale?
end end
def deposit def deposit
return field_109 if shared_ownership? return field_109 if shared_ownership?
return field_123 if discounted_ownership? return field_123 if discounted_ownership?
return field_130 if outright_sale?
field_130 if outright_sale?
end end
def mscharge def mscharge
return field_112 if shared_ownership? return field_112 if shared_ownership?
return field_124 if discounted_ownership? return field_124 if discounted_ownership?
return field_131 if outright_sale?
field_131 if outright_sale?
end end
def mortgagelender def mortgagelender
return field_105 if shared_ownership? return field_105 if shared_ownership?
return field_119 if discounted_ownership?
field_119 if discounted_ownership?
end end
def mortgagelenderother def mortgagelenderother
return field_106 if shared_ownership? return field_106 if shared_ownership?
return field_120 if discounted_ownership?
field_120 if discounted_ownership?
end end
def mortlen def mortlen
return field_107 if shared_ownership? return field_107 if shared_ownership?
return field_121 if discounted_ownership? return field_121 if discounted_ownership?
return field_128 if outright_sale?
field_128 if outright_sale?
end end
def proplen def proplen
return field_85 if shared_ownership? return field_85 if shared_ownership?
return field_113 if discounted_ownership?
field_113 if discounted_ownership?
end end
def mortgageused def mortgageused
return field_103 if shared_ownership? return field_103 if shared_ownership?
return field_117 if discounted_ownership? return field_117 if discounted_ownership?
return field_126 if outright_sale?
field_126 if outright_sale?
end end
def value_fields def value_fields
@ -1393,7 +1405,7 @@ private
end end
def validate_valid_radio_option def validate_valid_radio_option
log.attributes.each do |question_id, _v| log.attributes.each_key do |question_id|
question = log.form.get_question(question_id, log) question = log.form.get_question(question_id, log)
next if question_id == "type" next if question_id == "type"

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

@ -906,7 +906,7 @@ private
attributes["mortlen"] = mortlen attributes["mortlen"] = mortlen
attributes["proplen"] = proplen if proplen&.positive? attributes["proplen"] = proplen if proplen&.positive?
attributes["proplen_asked"] = attributes["proplen"]&.present? ? 0 : 1 attributes["proplen_asked"] = attributes["proplen"].present? ? 0 : 1
attributes["jointmore"] = field_13 attributes["jointmore"] = field_13
attributes["staircase"] = field_10 attributes["staircase"] = field_10
attributes["privacynotice"] = field_15 attributes["privacynotice"] = field_15
@ -1008,7 +1008,8 @@ private
].each do |hash| ].each do |hash|
define_method("age#{hash[:person]}_known?") do define_method("age#{hash[:person]}_known?") do
return 1 if public_send(hash[:field]) == "R" return 1 if public_send(hash[:field]) == "R"
return 0 if send("person_#{hash[:person]}_present?")
0 if send("person_#{hash[:person]}_present?")
end end
end end
@ -1076,58 +1077,68 @@ private
def sale_type def sale_type
return field_9 if shared_ownership? return field_9 if shared_ownership?
return field_11 if discounted_ownership?
field_11 if discounted_ownership?
end end
def value def value
return field_86 if shared_ownership_initial_purchase? return field_86 if shared_ownership_initial_purchase?
return field_113 if discounted_ownership? return field_113 if discounted_ownership?
return field_107 if staircasing?
field_107 if staircasing?
end end
def equity def equity
return field_87 if shared_ownership_initial_purchase? return field_87 if shared_ownership_initial_purchase?
return field_108 if staircasing?
field_108 if staircasing?
end end
def mortgage def mortgage
return field_89 if shared_ownership? return field_89 if shared_ownership?
return field_117 if discounted_ownership?
field_117 if discounted_ownership?
end end
def extrabor def extrabor
return field_119 if discounted_ownership? field_119 if discounted_ownership?
end end
def deposit def deposit
return field_91 if shared_ownership? return field_91 if shared_ownership?
return field_120 if discounted_ownership?
field_120 if discounted_ownership?
end end
def mrent def mrent
return field_93 if shared_ownership_initial_purchase? return field_93 if shared_ownership_initial_purchase?
return field_111 if staircasing?
field_111 if staircasing?
end end
def mscharge def mscharge
return field_94 if shared_ownership? return field_94 if shared_ownership?
return field_121 if discounted_ownership?
field_121 if discounted_ownership?
end end
def mortlen def mortlen
return field_90 if shared_ownership? return field_90 if shared_ownership?
return field_118 if discounted_ownership?
field_118 if discounted_ownership?
end end
def proplen def proplen
return field_79 if shared_ownership? return field_79 if shared_ownership?
return field_112 if discounted_ownership?
field_112 if discounted_ownership?
end end
def mortgageused def mortgageused
return field_88 if shared_ownership_initial_purchase? return field_88 if shared_ownership_initial_purchase?
return field_116 if discounted_ownership? return field_116 if discounted_ownership?
return field_109 if staircasing?
field_109 if staircasing?
end end
def value_fields def value_fields
@ -1369,7 +1380,7 @@ private
end end
def validate_valid_radio_option def validate_valid_radio_option
log.attributes.each do |question_id, _v| log.attributes.each_key do |question_id|
question = log.form.get_question(question_id, log) question = log.form.get_question(question_id, log)
next if question_id == "type" next if question_id == "type"

39
app/services/bulk_upload/sales/year2026/row_parser.rb

@ -990,7 +990,7 @@ private
attributes["mortlen"] = mortlen attributes["mortlen"] = mortlen
attributes["proplen"] = proplen if proplen&.positive? attributes["proplen"] = proplen if proplen&.positive?
attributes["proplen_asked"] = attributes["proplen"]&.present? ? 0 : 1 attributes["proplen_asked"] = attributes["proplen"].present? ? 0 : 1
attributes["jointmore"] = field_13 attributes["jointmore"] = field_13
attributes["staircase"] = field_10 attributes["staircase"] = field_10
attributes["privacynotice"] = field_15 attributes["privacynotice"] = field_15
@ -1092,7 +1092,8 @@ private
].each do |hash| ].each do |hash|
define_method("age#{hash[:person]}_known?") do define_method("age#{hash[:person]}_known?") do
return 1 if public_send(hash[:field]) == "R" return 1 if public_send(hash[:field]) == "R"
return 0 if send("person_#{hash[:person]}_present?")
0 if send("person_#{hash[:person]}_present?")
end end
end end
@ -1160,58 +1161,68 @@ private
def sale_type def sale_type
return field_9 if shared_ownership? return field_9 if shared_ownership?
return field_11 if discounted_ownership?
field_11 if discounted_ownership?
end end
def value def value
return field_86 if shared_ownership_initial_purchase? return field_86 if shared_ownership_initial_purchase?
return field_113 if discounted_ownership? return field_113 if discounted_ownership?
return field_107 if staircasing?
field_107 if staircasing?
end end
def equity def equity
return field_87 if shared_ownership_initial_purchase? return field_87 if shared_ownership_initial_purchase?
return field_108 if staircasing?
field_108 if staircasing?
end end
def mortgage def mortgage
return field_89 if shared_ownership? return field_89 if shared_ownership?
return field_117 if discounted_ownership?
field_117 if discounted_ownership?
end end
def extrabor def extrabor
return field_119 if discounted_ownership? field_119 if discounted_ownership?
end end
def deposit def deposit
return field_91 if shared_ownership? return field_91 if shared_ownership?
return field_120 if discounted_ownership?
field_120 if discounted_ownership?
end end
def mrent def mrent
return field_93 if shared_ownership_initial_purchase? return field_93 if shared_ownership_initial_purchase?
return field_111 if staircasing?
field_111 if staircasing?
end end
def mscharge def mscharge
return field_94 if shared_ownership? return field_94 if shared_ownership?
return field_121 if discounted_ownership?
field_121 if discounted_ownership?
end end
def mortlen def mortlen
return field_90 if shared_ownership? return field_90 if shared_ownership?
return field_118 if discounted_ownership?
field_118 if discounted_ownership?
end end
def proplen def proplen
return field_79 if shared_ownership? return field_79 if shared_ownership?
return field_112 if discounted_ownership?
field_112 if discounted_ownership?
end end
def mortgageused def mortgageused
return field_88 if shared_ownership_initial_purchase? return field_88 if shared_ownership_initial_purchase?
return field_116 if discounted_ownership? return field_116 if discounted_ownership?
return field_109 if staircasing?
field_109 if staircasing?
end end
def value_fields def value_fields
@ -1454,7 +1465,7 @@ private
end end
def validate_valid_radio_option def validate_valid_radio_option
log.attributes.each do |question_id, _v| log.attributes.each_key do |question_id|
question = log.form.get_question(question_id, log) question = log.form.get_question(question_id, log)
next if question_id == "type" next if question_id == "type"

6
app/services/csv/lettings_log_csv_service.rb

@ -398,12 +398,14 @@ module Csv
def label_if_boolean_value(value) def label_if_boolean_value(value)
return "Yes" if value == true return "Yes" if value == true
return "No" if value == false
"No" if value == false
end end
def conventional_yes_no_label(value) def conventional_yes_no_label(value)
return "Yes" if value == 1 return "Yes" if value == 1
return "No" if value&.zero?
"No" if value&.zero?
end end
end end
end end

3
app/services/csv/sales_log_csv_service.rb

@ -409,7 +409,8 @@ module Csv
def label_if_boolean_value(value) def label_if_boolean_value(value)
return "Yes" if value == true return "Yes" if value == true
return "No" if value == false
"No" if value == false
end end
end end
end end

2
app/services/documentation_generator.rb

@ -93,7 +93,7 @@ class DocumentationGenerator
interruption_screen_pages = form.pages.select { |page| page.questions.first.type == "interruption_screen" } interruption_screen_pages = form.pages.select { |page| page.questions.first.type == "interruption_screen" }
interruption_screen_pages_grouped_by_question = interruption_screen_pages.group_by { |page| page.questions.first.id } interruption_screen_pages_grouped_by_question = interruption_screen_pages.group_by { |page| page.questions.first.id }
interruption_screen_pages_grouped_by_question.each do |_question_id, pages| interruption_screen_pages_grouped_by_question.each_value do |pages|
pages.map do |page| pages.map do |page|
save_soft_validation(form, page, validation_descriptions, log_type) save_soft_validation(form, page, validation_descriptions, log_type)
end end

2
app/services/exports/organisation_export_constants.rb

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

2
app/services/filter_manager.rb

@ -121,7 +121,7 @@ class FilterManager
new_filters = new_filters.except("user") if params["assigned_to"] == "all" new_filters = new_filters.except("user") if params["assigned_to"] == "all"
new_filters["user"] = current_user.id.to_s if params["assigned_to"] == "you" new_filters["user"] = current_user.id.to_s if params["assigned_to"] == "you"
new_filters = new_filters.except("user_text_search") if params["assigned_to"] == "all" || params["assigned_to"] == "you" new_filters = new_filters.except("user_text_search") if %w[all you].include?(params["assigned_to"])
new_filters = new_filters.except("owning_organisation_text_search") if params["owning_organisation_select"] == "all" new_filters = new_filters.except("owning_organisation_text_search") if params["owning_organisation_select"] == "all"
new_filters = new_filters.except("managing_organisation_text_search") if params["managing_organisation_select"] == "all" new_filters = new_filters.except("managing_organisation_text_search") if params["managing_organisation_select"] == "all"
end end

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

@ -16,7 +16,7 @@
hint: { text: option["hint"] }, hint: { text: option["hint"] },
checked: @log[key] == 1, checked: @log[key] == 1,
exclusive: after_divider, exclusive: after_divider,
link_errors: index.zero? ? true : nil, link_errors: index.zero? || nil,
**stimulus_html_attributes(question) %> **stimulus_html_attributes(question) %>
<% end %> <% end %>
<% end %> <% end %>

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

@ -18,7 +18,7 @@
<div class="govuk-button-group"> <div class="govuk-button-group">
<%= f.govuk_submit "Confirm and continue" %> <%= f.govuk_submit "Confirm and continue" %>
<%= govuk_link_to( <%= govuk_link_to(
(@page.skip_text || "Skip for now"), @page.skip_text || "Skip for now",
(@page.skip_href(@log) || send(@log.form.next_page_redirect_path(@page, @log, current_user, ignore_answered: true), @log)), @page.skip_href(@log) || send(@log.form.next_page_redirect_path(@page, @log, current_user, ignore_answered: true), @log),
) %> ) %>
</div> </div>

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

@ -28,14 +28,14 @@
key, key,
label: { text: options["value"] }, label: { text: options["value"] },
hint: { text: options["hint"] }, hint: { text: options["hint"] },
link_errors: index.zero? ? true : nil, link_errors: index.zero? || nil,
**stimulus_html_attributes(question) %> **stimulus_html_attributes(question) %>
<% else %> <% else %>
<%= f.govuk_radio_button question.id.to_sym, <%= f.govuk_radio_button question.id.to_sym,
key, key,
label: { text: options["value"] }, label: { text: options["value"] },
hint: { text: options["hint"] }, hint: { text: options["hint"] },
link_errors: index.zero? ? true : nil, link_errors: index.zero? || nil,
**stimulus_html_attributes(question) do %> **stimulus_html_attributes(question) do %>
<%= render partial: "#{conditional_question.type}_question", locals: { <%= render partial: "#{conditional_question.type}_question", locals: {
question: conditional_question, question: conditional_question,

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

@ -1,6 +1,6 @@
<div class="form-actions row justify-content-end my-3"> <div class="form-actions row justify-content-end my-3">
<div class="col-sm-10"> <div class="col-sm-10">
<input name="return_to" type="<%= :hidden %>" value="<%= (params[:return_to].presence || request.referer) %>"> <input name="return_to" type="<%= :hidden %>" value="<%= params[:return_to].presence || request.referer %>">
<button class="govuk-button" data-disable-with="<%= t("admin.form.save") %>" name="_save" type="submit"<%= " disabled" unless @action.enabled? %>> <button class="govuk-button" data-disable-with="<%= t("admin.form.save") %>" name="_save" type="submit"<%= " disabled" unless @action.enabled? %>>
<i class="fas fa-check"></i> <i class="fas fa-check"></i>
<%= t("admin.form.save") %> <%= t("admin.form.save") %>

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

@ -40,7 +40,7 @@
-1 -1
end %> end %>
<div class="<%= active ? "active progress-bar-striped " : "" %>progress" style="margin-bottom:0px"> <div class="<%= active ? "active progress-bar-striped " : "" %>progress" style="margin-bottom:0px">
<div class="bg-<%= get_indicator(percent) %> progress-bar animate-width-to" data-animate-length="<%= ([1.0, percent].max.to_i * 20) %>" data-animate-width-to="<%= [2.0, percent].max.to_i %>%" style="width:2%"> <div class="bg-<%= get_indicator(percent) %> progress-bar animate-width-to" data-animate-length="<%= [1.0, percent].max.to_i * 20 %>" data-animate-width-to="<%= [2.0, percent].max.to_i %>%" style="width:2%">
<%= @count[abstract_model.model.name] %> <%= @count[abstract_model.model.name] %>
</div> </div>
</div> </div>

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

@ -7,7 +7,7 @@
<%= render partial: "delete_notice", object: @object %> <%= render partial: "delete_notice", object: @object %>
</ul> </ul>
<%= form_for(@object, url: delete_path(model_name: @abstract_model.to_param, id: @object.id), html: { method: "delete" }) do %> <%= form_for(@object, url: delete_path(model_name: @abstract_model.to_param, id: @object.id), html: { method: "delete" }) do %>
<input name="return_to" type="<%= :hidden %>" value="<%= (params[:return_to].presence || request.referer) %>"> <input name="return_to" type="<%= :hidden %>" value="<%= params[:return_to].presence || request.referer %>">
<div class="form-actions"> <div class="form-actions">
<button class="govuk-button govuk-button--warning" data-disable-with="<%= t("admin.form.confirmation") %>" type="submit"> <button class="govuk-button govuk-button--warning" data-disable-with="<%= t("admin.form.confirmation") %>" type="submit">
<i class="fas fa-check"></i> <i class="fas fa-check"></i>

8
config/initializers/multi_logger.rb

@ -4,13 +4,9 @@ class MultiLogger
@file_logger = file_logger @file_logger = file_logger
end end
def info(data) delegate :info, to: :@rails_logger
@rails_logger.info(data)
end
def warn(data) delegate :warn, to: :@rails_logger
@rails_logger.warn(data)
end
def error(data) def error(data)
@rails_logger.error(data) @rails_logger.error(data)

2
config/initializers/sidekiq.rb

@ -32,7 +32,7 @@ Redis.silence_deprecations = true
Sidekiq.configure_server do |config| Sidekiq.configure_server do |config|
config.on(:startup) do config.on(:startup) do
Sidekiq::Cron::Job.all.each(&:destroy) Sidekiq::Cron::Job.all.find_each(&:destroy)
unless FeatureToggle.service_moved? || FeatureToggle.service_unavailable? unless FeatureToggle.service_moved? || FeatureToggle.service_unavailable?
Sidekiq::Cron::Job.load_from_hash YAML.load_file("config/sidekiq_cron_schedule.yml") Sidekiq::Cron::Job.load_from_hash YAML.load_file("config/sidekiq_cron_schedule.yml")
end end

17
config/locales/forms/2026/lettings/soft_validations.en.yml

@ -57,6 +57,23 @@ en:
title_text: "You told us that the tenant’s main reason for leaving their last settled home was %{reasonother}." title_text: "You told us that the tenant’s main reason for leaving their last settled home was %{reasonother}."
informative_text: "The reason you have entered looks very similar to one of the existing response categories. Please check the categories and select the appropriate one. If the existing categories are not suitable, please confirm here to move onto the next question." informative_text: "The reason you have entered looks very similar to one of the existing response categories. Please check the categories and select the appropriate one. If the existing categories are not suitable, please confirm here to move onto the next question."
tenancyother_value_check:
page_header: ""
check_answer_label: "Tenancy other confirmation"
check_answer_prompt: "Confirm tenancy type"
hint_text: ""
question_text: ""
title_text: "You told us that the type of tenancy was %{tenancyother}."
informative_text: "We already know whether the tenancy is introductory or has a starter period from your previous answer. Your answer to the type of main tenancy question should be the type of tenancy that the tenant will roll onto after any introductory or starter period. Choose from the categories or write a description if no category is suitable."
referral_value_check:
page_header: ""
check_answer_label: "Referral confirmation"
check_answer_prompt: "Confirm referral type"
hint_text: ""
question_text: "Are you sure?"
title_text: "Are you sure?"
informative_text: "This is a general needs log, and this referral type is for supported housing."
net_income_value_check: net_income_value_check:
page_header: "" page_header: ""
check_answer_label: "Net income confirmation" check_answer_label: "Net income confirmation"

4
config/routes.rb

@ -301,7 +301,7 @@ Rails.application.routes.draw do
get "review", to: "form#review" get "review", to: "form#review"
end end
FormHandler.instance.lettings_forms.each do |_key, form| FormHandler.instance.lettings_forms.each_value do |form|
form.pages.map do |page| form.pages.map do |page|
get page.id.to_s.dasherize, to: "form#show_page" get page.id.to_s.dasherize, to: "form#show_page"
post page.id.to_s.dasherize, to: "form#submit_form" post page.id.to_s.dasherize, to: "form#submit_form"
@ -377,7 +377,7 @@ Rails.application.routes.draw do
get "review", to: "form#review" get "review", to: "form#review"
end end
FormHandler.instance.sales_forms.each do |_key, form| FormHandler.instance.sales_forms.each_value do |form|
form.pages.map do |page| form.pages.map do |page|
get page.id.to_s.dasherize, to: "form#show_page" get page.id.to_s.dasherize, to: "form#show_page"
post page.id.to_s.dasherize, to: "form#submit_form" post page.id.to_s.dasherize, to: "form#submit_form"

2
db/migrate/20241206142943_create_active_storage_variant_records.active_storage.rb

@ -4,7 +4,7 @@ class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0]
return unless table_exists?(:active_storage_blobs) return unless table_exists?(:active_storage_blobs)
# Use Active Record's configured type for primary key # 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 create_table :active_storage_variant_records, id: primary_key_type, if_not_exists: true do |t|
t.belongs_to :blob, null: false, index: false, type: blobs_primary_key_type t.belongs_to :blob, null: false, index: false, type: blobs_primary_key_type
t.string :variation_digest, null: false t.string :variation_digest, null: false

5
db/migrate/20260212091506_add_tenancyother_value_check_to_lettings_logs.rb

@ -0,0 +1,5 @@
class AddTenancyotherValueCheckToLettingsLogs < ActiveRecord::Migration[7.0]
def change
add_column :lettings_logs, :tenancyother_value_check, :integer
end
end

1
db/schema.rb

@ -403,6 +403,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_20_141000) do
t.integer "referral_register" t.integer "referral_register"
t.integer "referral_noms" t.integer "referral_noms"
t.integer "referral_org" t.integer "referral_org"
t.integer "tenancyother_value_check"
t.index ["assigned_to_id"], name: "index_lettings_logs_on_assigned_to_id" t.index ["assigned_to_id"], name: "index_lettings_logs_on_assigned_to_id"
t.index ["bulk_upload_id"], name: "index_lettings_logs_on_bulk_upload_id" t.index ["bulk_upload_id"], name: "index_lettings_logs_on_bulk_upload_id"
t.index ["created_by_id"], name: "index_lettings_logs_on_created_by_id" t.index ["created_by_id"], name: "index_lettings_logs_on_created_by_id"

4
docs/setup.md

@ -70,8 +70,8 @@ We recommend using [nvm](https://github.com/nvm-sh/nvm) to manage NodeJS version
4. Install Ruby and Bundler 4. Install Ruby and Bundler
```bash ```bash
rbenv install 3.1.6 rbenv install 3.4.4
rbenv global 3.1.6 rbenv global 3.4.4
source ~/.bashrc source ~/.bashrc
gem install bundler gem install bundler
``` ```

2
lib/tasks/generate_lettings_documentation.rake

@ -50,7 +50,7 @@ namespace :generate_lettings_documentation do
all_validation_methods = row_parser_class.private_instance_methods.select { |method| method.starts_with?("validate_") } all_validation_methods = row_parser_class.private_instance_methods.select { |method| method.starts_with?("validate_") }
all_helper_methods = row_parser_class.private_instance_methods(false) + row_parser_class.instance_methods(false) - all_validation_methods all_helper_methods = row_parser_class.private_instance_methods(false) + row_parser_class.instance_methods(false) - all_validation_methods
field_mapping_for_errors = row_parser_class.new.send("field_mapping_for_errors") field_mapping_for_errors = row_parser_class.new.send("field_mapping_for_errors")
DocumentationGenerator.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, "lettings") DocumentationGenerator.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, "lettings")

2
lib/tasks/generate_sales_documentation.rake

@ -47,7 +47,7 @@ namespace :generate_sales_documentation do
row_parser_class = "BulkUpload::Sales::Year#{form_year}::RowParser".constantize row_parser_class = "BulkUpload::Sales::Year#{form_year}::RowParser".constantize
client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"]) client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"])
all_validation_methods = row_parser_class.private_instance_methods.select { |method| method.starts_with?("validate_") } all_validation_methods = row_parser_class.private_instance_methods.select { |method| method.starts_with?("validate_") }
all_helper_methods = row_parser_class.private_instance_methods(false) + row_parser_class.instance_methods(false) - all_validation_methods all_helper_methods = row_parser_class.private_instance_methods(false) + row_parser_class.instance_methods(false) - all_validation_methods
field_mapping_for_errors = row_parser_class.new.send("field_mapping_for_errors") field_mapping_for_errors = row_parser_class.new.send("field_mapping_for_errors")
DocumentationGenerator.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, "sales") DocumentationGenerator.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, "sales")

4
lib/tasks/handle_unpended_logs.rake

@ -62,13 +62,13 @@ task :handle_unpended_logs, %i[perform_updates] => :environment do |_task, args|
visible_duplicates = duplicates.where(status: %w[in_progress completed]) visible_duplicates = duplicates.where(status: %w[in_progress completed])
deleted_duplicates = duplicates.where(status: %w[deleted]) deleted_duplicates = duplicates.where(status: %w[deleted])
if visible_duplicates.length.zero? && deleted_duplicates.any? { |dup| dup.discarded_at > result["created_at"] } if visible_duplicates.empty? && deleted_duplicates.any? { |dup| dup.discarded_at > result["created_at"] }
seen.add(id) seen.add(id)
csv << [id, log.collection_start_year, log.status, log.owning_organisation_name, log.assigned_to_id, log.assigned_to.email, "Leave", "Log has no visible duplicates and at least one duplicate has been deleted since being affected"] csv << [id, log.collection_start_year, log.status, log.owning_organisation_name, log.assigned_to_id, log.assigned_to.email, "Leave", "Log has no visible duplicates and at least one duplicate has been deleted since being affected"]
next next
end end
if visible_duplicates.length.zero? if visible_duplicates.empty?
seen.add(id) seen.add(id)
csv << [id, log.collection_start_year, log.status, log.owning_organisation_name, log.assigned_to_id, log.assigned_to.email, "Leave", "Log has no visible duplicates"] csv << [id, log.collection_start_year, log.status, log.owning_organisation_name, log.assigned_to_id, log.assigned_to.email, "Leave", "Log has no visible duplicates"]
next next

2
lib/tasks/lint.rake

@ -5,7 +5,7 @@ end
desc "Run ERB Lint" desc "Run ERB Lint"
task erblint: :environment do task erblint: :environment do
sh "bundle exec erblint --lint-all" sh "bundle exec erb_lint --lint-all"
end end
desc "Run Standard" desc "Run Standard"

2
lib/tasks/set_log_validation_collection_year.rake

@ -1,6 +1,6 @@
desc "Sets value for collection_year log validations depending on the from value" desc "Sets value for collection_year log validations depending on the from value"
task set_log_validation_collection_year: :environment do task set_log_validation_collection_year: :environment do
LogValidation.all.each do |log_validation| LogValidation.all.find_each do |log_validation|
log_validation.update(collection_year: "#{log_validation.from.year}/#{log_validation.from.year + 1}") log_validation.update(collection_year: "#{log_validation.from.year}/#{log_validation.from.year + 1}")
end end
end end

2
spec/components/bulk_upload_error_row_component_spec.rb

@ -46,7 +46,7 @@ RSpec.describe BulkUploadErrorRowComponent, type: :component do
end end
context "when the bulk upload is for 2024" do context "when the bulk upload is for 2024" do
context "with a lettings bulk upload" do context "with a lettings bulk upload" do
let(:bulk_upload) { build(:bulk_upload, :lettings, year: 2024) } let(:bulk_upload) { build(:bulk_upload, :lettings, year: 2024) }
let(:field) { :field_130 } let(:field) { :field_130 }

4
spec/components/check_answers_summary_list_card_component_spec.rb

@ -17,7 +17,7 @@ RSpec.describe CheckAnswersSummaryListCardComponent, type: :component do
end end
it "applicable questions doesn't return questions that are hidden in check answers" do it "applicable questions doesn't return questions that are hidden in check answers" do
expect(component.applicable_questions.map(&:id).include?("retirement_value_check")).to eq(false) expect(component.applicable_questions.map(&:id).include?("retirement_value_check")).to be(false)
end end
it "has the correct answer label for a question" do it "has the correct answer label for a question" do
@ -45,7 +45,7 @@ RSpec.describe CheckAnswersSummaryListCardComponent, type: :component do
context "when log was not created via a bulk upload and has an unanswered question" do context "when log was not created via a bulk upload and has an unanswered question" do
let(:log) { create(:lettings_log, :in_progress) } let(:log) { create(:lettings_log, :in_progress) }
it "displays normal copy with muted colour " do it "displays normal copy with muted colour" do
expect(rendered).to have_link(log.form.get_question("sex1", log).check_answer_prompt, href: "/lettings-logs/#{log.id}/lead-tenant-gender-identity?referrer=check_answers_new_answer", class: "govuk-link govuk-link--no-visited-state") expect(rendered).to have_link(log.form.get_question("sex1", log).check_answer_prompt, href: "/lettings-logs/#{log.id}/lead-tenant-gender-identity?referrer=check_answers_new_answer", class: "govuk-link govuk-link--no-visited-state")
end end
end end

16
spec/components/create_log_actions_component_spec.rb

@ -14,7 +14,7 @@ RSpec.describe CreateLogActionsComponent, type: :component do
let(:bulk_upload) { true } let(:bulk_upload) { true }
it "does not render actions" do it "does not render actions" do
expect(component.display_actions?).to eq(false) expect(component.display_actions?).to be(false)
end end
end end
@ -25,7 +25,7 @@ RSpec.describe CreateLogActionsComponent, type: :component do
let(:user) { create(:user, :support) } let(:user) { create(:user, :support) }
it "renders actions" do it "renders actions" do
expect(component.display_actions?).to eq(true) expect(component.display_actions?).to be(true)
end end
it "returns create button copy" do it "returns create button copy" do
@ -60,7 +60,7 @@ RSpec.describe CreateLogActionsComponent, type: :component do
let(:log_type) { "sales" } let(:log_type) { "sales" }
it "renders actions" do it "renders actions" do
expect(component.display_actions?).to eq(true) expect(component.display_actions?).to be(true)
end end
it "returns create button copy" do it "returns create button copy" do
@ -97,7 +97,7 @@ RSpec.describe CreateLogActionsComponent, type: :component do
let(:user) { create(:user) } let(:user) { create(:user) }
it "renders actions" do it "renders actions" do
expect(component.display_actions?).to eq(true) expect(component.display_actions?).to be(true)
end end
it "returns create button copy" do it "returns create button copy" do
@ -122,7 +122,7 @@ RSpec.describe CreateLogActionsComponent, type: :component do
let(:log_type) { "sales" } let(:log_type) { "sales" }
it "renders actions" do it "renders actions" do
expect(component.display_actions?).to eq(true) expect(component.display_actions?).to be(true)
end end
it "returns create button copy" do it "returns create button copy" do
@ -147,7 +147,7 @@ RSpec.describe CreateLogActionsComponent, type: :component do
end end
it "renders actions" do it "renders actions" do
expect(component.display_actions?).to eq(true) expect(component.display_actions?).to be(true)
end end
end end
@ -159,7 +159,7 @@ RSpec.describe CreateLogActionsComponent, type: :component do
end end
it "renders actions" do it "renders actions" do
expect(component.display_actions?).to eq(false) expect(component.display_actions?).to be(false)
end end
end end
@ -170,7 +170,7 @@ RSpec.describe CreateLogActionsComponent, type: :component do
end end
it "does not render actions" do it "does not render actions" do
expect(component.display_actions?).to eq(false) expect(component.display_actions?).to be(false)
end end
end end
end end

28
spec/components/data_protection_confirmation_banner_component_spec.rb

@ -13,7 +13,7 @@ RSpec.describe DataProtectionConfirmationBannerComponent, type: :component do
let(:organisation) { nil } let(:organisation) { nil }
it "does not display banner" do it "does not display banner" do
expect(component.display_banner?).to eq(false) expect(component.display_banner?).to be(false)
expect(render.content).to be_empty expect(render.content).to be_empty
end end
end end
@ -24,7 +24,7 @@ RSpec.describe DataProtectionConfirmationBannerComponent, type: :component do
end end
it "displays the banner" do it "displays the banner" do
expect(component.display_banner?).to eq(true) expect(component.display_banner?).to be(true)
expect(render).to have_link( expect(render).to have_link(
"Contact helpdesk to assign a data protection officer", "Contact helpdesk to assign a data protection officer",
href: "https://mhclgdigital.atlassian.net/servicedesk/customer/portal/6/group/11", href: "https://mhclgdigital.atlassian.net/servicedesk/customer/portal/6/group/11",
@ -41,7 +41,7 @@ RSpec.describe DataProtectionConfirmationBannerComponent, type: :component do
let!(:dpo) { create(:user, :data_protection_officer, organisation:, with_dsa: false) } let!(:dpo) { create(:user, :data_protection_officer, organisation:, with_dsa: false) }
it "displays the banner and shows DPOs" do it "displays the banner and shows DPOs" do
expect(component.display_banner?).to eq(true) expect(component.display_banner?).to be(true)
expect(render.css("a")).to be_empty expect(render.css("a")).to be_empty
expect(render).to have_selector("p", text: "Your data protection officer must accept the Data Sharing Agreement on CORE before you can create any logs.") expect(render).to have_selector("p", text: "Your data protection officer must accept the Data Sharing Agreement on CORE before you can create any logs.")
expect(render).to have_selector("p", text: "You can ask: #{dpo.name}") expect(render).to have_selector("p", text: "You can ask: #{dpo.name}")
@ -53,7 +53,7 @@ RSpec.describe DataProtectionConfirmationBannerComponent, type: :component do
let(:user) { create(:user, :data_protection_officer, organisation:, with_dsa: false) } let(:user) { create(:user, :data_protection_officer, organisation:, with_dsa: false) }
it "displays the banner and asks to sign" do it "displays the banner and asks to sign" do
expect(component.display_banner?).to eq(true) expect(component.display_banner?).to be(true)
expect(render).to have_link( expect(render).to have_link(
"Read the Data Sharing Agreement", "Read the Data Sharing Agreement",
href: "/organisations/#{organisation.id}/data-sharing-agreement", href: "/organisations/#{organisation.id}/data-sharing-agreement",
@ -71,7 +71,7 @@ RSpec.describe DataProtectionConfirmationBannerComponent, type: :component do
end end
it "displays the banner and asks to sign" do it "displays the banner and asks to sign" do
expect(component.display_banner?).to eq(true) expect(component.display_banner?).to be(true)
expect(render).to have_link( expect(render).to have_link(
"Read the Data Sharing Agreement", "Read the Data Sharing Agreement",
href: "/organisations/#{organisation.id}/data-sharing-agreement", href: "/organisations/#{organisation.id}/data-sharing-agreement",
@ -83,7 +83,7 @@ RSpec.describe DataProtectionConfirmationBannerComponent, type: :component do
context "when org has a signed data sharing agremeent" do context "when org has a signed data sharing agremeent" do
it "does not display banner" do it "does not display banner" do
expect(component.display_banner?).to eq(false) expect(component.display_banner?).to be(false)
expect(render.content).to be_empty expect(render.content).to be_empty
end end
@ -99,7 +99,7 @@ RSpec.describe DataProtectionConfirmationBannerComponent, type: :component do
end end
it "does not display banner" do it "does not display banner" do
expect(component.display_banner?).to eq(false) expect(component.display_banner?).to be(false)
expect(render.content).to be_empty expect(render.content).to be_empty
end end
end end
@ -111,7 +111,7 @@ RSpec.describe DataProtectionConfirmationBannerComponent, type: :component do
end end
it "displays the banner and asks to create stock owners" do it "displays the banner and asks to create stock owners" do
expect(component.display_banner?).to eq(true) expect(component.display_banner?).to be(true)
expect(render).to have_link( expect(render).to have_link(
"View or add stock owners", "View or add stock owners",
href: "/organisations/#{organisation.id}/stock-owners", href: "/organisations/#{organisation.id}/stock-owners",
@ -128,7 +128,7 @@ RSpec.describe DataProtectionConfirmationBannerComponent, type: :component do
end end
it "displays the banner" do it "displays the banner" do
expect(component.display_banner?).to eq(true) expect(component.display_banner?).to be(true)
expect(render).to have_link( expect(render).to have_link(
"Contact helpdesk to assign a data protection officer", "Contact helpdesk to assign a data protection officer",
href: "https://mhclgdigital.atlassian.net/servicedesk/customer/portal/6/group/11", href: "https://mhclgdigital.atlassian.net/servicedesk/customer/portal/6/group/11",
@ -145,7 +145,7 @@ RSpec.describe DataProtectionConfirmationBannerComponent, type: :component do
let!(:dpo) { create(:user, :data_protection_officer, organisation:, with_dsa: false) } let!(:dpo) { create(:user, :data_protection_officer, organisation:, with_dsa: false) }
it "displays the banner and shows DPOs" do it "displays the banner and shows DPOs" do
expect(component.display_banner?).to eq(true) expect(component.display_banner?).to be(true)
expect(render.css("a")).to be_empty expect(render.css("a")).to be_empty
expect(render).to have_selector("p", text: "Your data protection officer must accept the Data Sharing Agreement on CORE before you can create any logs.") expect(render).to have_selector("p", text: "Your data protection officer must accept the Data Sharing Agreement on CORE before you can create any logs.")
expect(render).to have_selector("p", text: "You can ask: #{dpo.name}") expect(render).to have_selector("p", text: "You can ask: #{dpo.name}")
@ -158,7 +158,7 @@ RSpec.describe DataProtectionConfirmationBannerComponent, type: :component do
end end
it "displays the banner and shows DPOs" do it "displays the banner and shows DPOs" do
expect(component.display_banner?).to eq(true) expect(component.display_banner?).to be(true)
expect(render.css("a")).to be_empty expect(render.css("a")).to be_empty
expect(render).to have_selector("p", text: "Your data protection officer must accept the Data Sharing Agreement on CORE before you can create any logs.") expect(render).to have_selector("p", text: "Your data protection officer must accept the Data Sharing Agreement on CORE before you can create any logs.")
expect(render).to have_selector("p", text: "You can ask: #{dpo.name}") expect(render).to have_selector("p", text: "You can ask: #{dpo.name}")
@ -171,7 +171,7 @@ RSpec.describe DataProtectionConfirmationBannerComponent, type: :component do
let(:user) { create(:user, :data_protection_officer, organisation:, with_dsa: false) } let(:user) { create(:user, :data_protection_officer, organisation:, with_dsa: false) }
it "displays the banner and asks to sign" do it "displays the banner and asks to sign" do
expect(component.display_banner?).to eq(true) expect(component.display_banner?).to be(true)
expect(render).to have_link( expect(render).to have_link(
"Read the Data Sharing Agreement", "Read the Data Sharing Agreement",
href: "/organisations/#{organisation.id}/data-sharing-agreement", href: "/organisations/#{organisation.id}/data-sharing-agreement",
@ -186,7 +186,7 @@ RSpec.describe DataProtectionConfirmationBannerComponent, type: :component do
end end
it "displays the banner and asks to sign" do it "displays the banner and asks to sign" do
expect(component.display_banner?).to eq(true) expect(component.display_banner?).to be(true)
expect(render).to have_link( expect(render).to have_link(
"Read the Data Sharing Agreement", "Read the Data Sharing Agreement",
href: "/organisations/#{organisation.id}/data-sharing-agreement", href: "/organisations/#{organisation.id}/data-sharing-agreement",
@ -199,7 +199,7 @@ RSpec.describe DataProtectionConfirmationBannerComponent, type: :component do
context "when org has a signed data sharing agremeent" do context "when org has a signed data sharing agremeent" do
it "does not display banner" do it "does not display banner" do
expect(component.display_banner?).to eq(false) expect(component.display_banner?).to be(false)
expect(render.content).to be_empty expect(render.content).to be_empty
end end
end end

3
spec/db/seeds_spec.rb

@ -17,8 +17,7 @@ RSpec.describe "seeding process", type: task do
Rake.application.rake_require("tasks/rent_ranges") Rake.application.rake_require("tasks/rent_ranges")
Rake::Task.define_task(:environment) Rake::Task.define_task(:environment)
allow(Rails.env).to receive(:test?).and_return(false) allow(Rails.env).to receive_messages(test?: false, review?: true)
allow(Rails.env).to receive(:review?).and_return(true)
end end
# Doing this in one test should save ~2 minutes # Doing this in one test should save ~2 minutes

2
spec/factories/data_protection_confirmation.rb

@ -1,7 +1,7 @@
FactoryBot.define do FactoryBot.define do
factory :data_protection_confirmation do factory :data_protection_confirmation do
organisation { association :organisation, data_protection_confirmation: instance } organisation { association :organisation, data_protection_confirmation: instance }
data_protection_officer { association :user, :data_protection_officer, organisation: (instance.organisation || organisation) } data_protection_officer { association :user, :data_protection_officer, organisation: instance.organisation || organisation }
organisation_name { organisation.name } organisation_name { organisation.name }
organisation_address { organisation.address_row } organisation_address { organisation.address_row }

2
spec/features/accessibility_spec.rb

@ -1,6 +1,6 @@
require "rails_helper" require "rails_helper"
RSpec.describe "Accessibility", js: true do RSpec.describe "Accessibility", :js do
let(:user) { create(:user, :support) } let(:user) { create(:user, :support) }
let!(:other_user) { create(:user, name: "new user", organisation: user.organisation, email: "new_user@example.com", confirmation_token: "abc") } let!(:other_user) { create(:user, name: "new user", organisation: user.organisation, email: "new_user@example.com", confirmation_token: "abc") }
let(:storage_service) { instance_double(Storage::S3Service, get_file_metadata: nil) } let(:storage_service) { instance_double(Storage::S3Service, get_file_metadata: nil) }

4
spec/features/bulk_upload_lettings_logs_spec.rb

@ -9,7 +9,7 @@ RSpec.describe "Bulk upload lettings log" do
let(:stub_file_upload) do let(:stub_file_upload) do
vcap_services = { "aws-s3-bucket" => {} } vcap_services = { "aws-s3-bucket" => {} }
mock_storage_service = instance_double("S3Service") mock_storage_service = instance_double(Storage::S3Service)
allow(ENV).to receive(:[]) allow(ENV).to receive(:[])
allow(ENV).to receive(:[]).with("VCAP_SERVICES").and_return(vcap_services.to_json) allow(ENV).to receive(:[]).with("VCAP_SERVICES").and_return(vcap_services.to_json)
@ -76,7 +76,7 @@ RSpec.describe "Bulk upload lettings log" do
end end
it "shows file to large error" do it "shows file to large error" do
stub_const("Forms::BulkUploadForm::UploadYourFile::MAX_FILE_SIZE", 1.bytes) stub_const("Forms::BulkUploadForm::UploadYourFile::MAX_FILE_SIZE", 1.byte)
visit("/lettings-logs") visit("/lettings-logs")
click_link("Upload lettings logs in bulk") click_link("Upload lettings logs in bulk")

4
spec/features/bulk_upload_sales_logs_spec.rb

@ -8,7 +8,7 @@ RSpec.describe "Bulk upload sales log" do
let(:stub_file_upload) do let(:stub_file_upload) do
vcap_services = { "aws-s3-bucket" => {} } vcap_services = { "aws-s3-bucket" => {} }
mock_storage_service = instance_double("S3Service") mock_storage_service = instance_double(Storage::S3Service)
allow(ENV).to receive(:[]) allow(ENV).to receive(:[])
allow(ENV).to receive(:[]).with("VCAP_SERVICES").and_return(vcap_services.to_json) allow(ENV).to receive(:[]).with("VCAP_SERVICES").and_return(vcap_services.to_json)
@ -71,7 +71,7 @@ RSpec.describe "Bulk upload sales log" do
end end
it "shows file to large error" do it "shows file to large error" do
stub_const("Forms::BulkUploadForm::UploadYourFile::MAX_FILE_SIZE", 1.bytes) stub_const("Forms::BulkUploadForm::UploadYourFile::MAX_FILE_SIZE", 1.byte)
visit("/sales-logs") visit("/sales-logs")
click_link("Upload sales logs in bulk") click_link("Upload sales logs in bulk")

20
spec/features/form/accessible_autocomplete_spec.rb

@ -25,38 +25,38 @@ RSpec.describe "Accessible Autocomplete" do
visit("/lettings-logs/#{lettings_log.id}/previous-local-authority") visit("/lettings-logs/#{lettings_log.id}/previous-local-authority")
end end
it "allows type ahead filtering", js: true do it "allows type ahead filtering", :js do
find("#lettings-log-prevloc-field").click.native.send_keys("T", "h", "a", "n", :down, :enter) find("#lettings-log-prevloc-field").click.native.send_keys("T", "h", "a", "n", :down, :enter)
expect(find("#lettings-log-prevloc-field").value).to eq("Thanet") expect(find("#lettings-log-prevloc-field").value).to eq("Thanet")
end end
it "ignores punctuation", js: true do it "ignores punctuation", :js do
find("#lettings-log-prevloc-field").click.native.send_keys("T", "h", "a", "'", "n", :down, :enter) find("#lettings-log-prevloc-field").click.native.send_keys("T", "h", "a", "'", "n", :down, :enter)
expect(find("#lettings-log-prevloc-field").value).to eq("Thanet") expect(find("#lettings-log-prevloc-field").value).to eq("Thanet")
end end
it "ignores stop words", js: true do it "ignores stop words", :js do
find("#lettings-log-prevloc-field").click.native.send_keys("t", "h", "e", " ", "W", "e", "s", "t", "m", :down, :enter) find("#lettings-log-prevloc-field").click.native.send_keys("t", "h", "e", " ", "W", "e", "s", "t", "m", :down, :enter)
expect(find("#lettings-log-prevloc-field").value).to eq("Westmorland and Furness") expect(find("#lettings-log-prevloc-field").value).to eq("Westmorland and Furness")
end end
it "does not perform an exact match", js: true do it "does not perform an exact match", :js do
find("#lettings-log-prevloc-field").click.native.send_keys("K", "i", "n", "g", "s", "t", "o", "n", " ", "T", "h", "a", "m", "e", "s", :down, :enter) find("#lettings-log-prevloc-field").click.native.send_keys("K", "i", "n", "g", "s", "t", "o", "n", " ", "T", "h", "a", "m", "e", "s", :down, :enter)
expect(find("#lettings-log-prevloc-field").value).to eq("Kingston upon Thames") expect(find("#lettings-log-prevloc-field").value).to eq("Kingston upon Thames")
end end
it "maintains enhancement state across back navigation", js: true do it "maintains enhancement state across back navigation", :js do
find("#lettings-log-prevloc-field").click.native.send_keys("T", "h", "a", "n", :down, :enter) find("#lettings-log-prevloc-field").click.native.send_keys("T", "h", "a", "n", :down, :enter)
click_button("Save and continue") click_button("Save and continue")
page.go_back page.go_back
expect(page).to have_selector("input", class: "autocomplete__input", count: 1) expect(page).to have_selector("input", class: "autocomplete__input", count: 1)
end end
it "displays the placeholder text", js: true do it "displays the placeholder text", :js do
expect(find("#lettings-log-prevloc-field")["placeholder"]).to eq("Start typing to search") expect(find("#lettings-log-prevloc-field")["placeholder"]).to eq("Start typing to search")
end end
context "and multiple schemes with same names", js: true do context "and multiple schemes with same names", :js do
let(:lettings_log) { FactoryBot.create(:lettings_log, :sh, assigned_to: user) } let(:lettings_log) { FactoryBot.create(:lettings_log, :sh, assigned_to: user) }
let!(:schemes) { FactoryBot.create_list(:scheme, 2, owning_organisation_id: user.organisation_id, service_name: "Scheme", primary_client_group: "O", secondary_client_group: "O") } let!(:schemes) { FactoryBot.create_list(:scheme, 2, owning_organisation_id: user.organisation_id, service_name: "Scheme", primary_client_group: "O", secondary_client_group: "O") }
@ -98,18 +98,18 @@ RSpec.describe "Accessible Autocomplete" do
visit("/lettings-logs/#{lettings_log.id}/scheme") visit("/lettings-logs/#{lettings_log.id}/scheme")
end end
it "can match on synonyms", js: true do it "can match on synonyms", :js do
find("#lettings-log-scheme-id-field").click.native.send_keys("w", "6", :down, :enter) find("#lettings-log-scheme-id-field").click.native.send_keys("w", "6", :down, :enter)
expect(find("#lettings-log-scheme-id-field").value).to include(scheme.service_name) expect(find("#lettings-log-scheme-id-field").value).to include(scheme.service_name)
end end
it "displays appended text next to the options", js: true do it "displays appended text next to the options", :js do
find("#lettings-log-scheme-id-field").click.native.send_keys("w", "6", :down, :enter) find("#lettings-log-scheme-id-field").click.native.send_keys("w", "6", :down, :enter)
expect(find(".autocomplete__option", visible: :hidden, text: scheme.service_name)).to be_present expect(find(".autocomplete__option", visible: :hidden, text: scheme.service_name)).to be_present
expect(find("span", visible: :hidden, text: "2 completed locations, 1 incomplete location")).to be_present expect(find("span", visible: :hidden, text: "2 completed locations, 1 incomplete location")).to be_present
end end
it "displays hint text under the options", js: true do it "displays hint text under the options", :js do
find("#lettings-log-scheme-id-field").click.native.send_keys("w", "6", :down, :enter) find("#lettings-log-scheme-id-field").click.native.send_keys("w", "6", :down, :enter)
expect(find(".autocomplete__option__hint", visible: :hidden, text: /Young people at risk, Young people leaving care/)).to be_present expect(find(".autocomplete__option__hint", visible: :hidden, text: /Young people at risk, Young people leaving care/)).to be_present
end end

6
spec/features/form/address_search_spec.rb

@ -22,17 +22,17 @@ RSpec.describe "Address Search" do
visit("/sales-logs/#{sales_log.id}/address-search") visit("/sales-logs/#{sales_log.id}/address-search")
end end
it "allows searching by a UPRN", js: true do it "allows searching by a UPRN", :js do
find("#sales-log-uprn-field").click.native.send_keys("1", "0", "0", "3", "3", "5", "4", "4", "6", "1", "4", :down) find("#sales-log-uprn-field").click.native.send_keys("1", "0", "0", "3", "3", "5", "4", "4", "6", "1", "4", :down)
expect(find("#sales-log-uprn-field").value).to eq("10033544614") expect(find("#sales-log-uprn-field").value).to eq("10033544614")
end end
it "allows searching by address", js: true do it "allows searching by address", :js do
find("#sales-log-uprn-field").click.native.send_keys("S", "W", "1", "5", :down, :enter) find("#sales-log-uprn-field").click.native.send_keys("S", "W", "1", "5", :down, :enter)
expect(find("#sales-log-uprn-field").value).to eq("SW15") expect(find("#sales-log-uprn-field").value).to eq("SW15")
end end
it "displays the placeholder text", js: true do it "displays the placeholder text", :js do
expect(find("#sales-log-uprn-field")["placeholder"]).to eq("Start typing to search") expect(find("#sales-log-uprn-field")["placeholder"]).to eq("Start typing to search")
end end

2
spec/features/form/checkboxes_spec.rb

@ -29,7 +29,7 @@ RSpec.describe "Checkboxes" do
sign_in user sign_in user
end end
context "when exclusive checkbox is selected", js: true do context "when exclusive checkbox is selected", :js do
it "deselects all other checkboxes" do it "deselects all other checkboxes" do
visit("/lettings-logs/#{id}/accessibility-requirements") visit("/lettings-logs/#{id}/accessibility-requirements")
page.check("lettings-log-accessibility-requirements-housingneeds-a-field", allow_label_click: true) page.check("lettings-log-accessibility-requirements-housingneeds-a-field", allow_label_click: true)

6
spec/features/form/conditional_questions_spec.rb

@ -47,7 +47,7 @@ RSpec.describe "Form Conditional Questions" do
expect(page).not_to have_selector("#armed_forces_injured_div") expect(page).not_to have_selector("#armed_forces_injured_div")
end end
it "shows conditional questions if the required answer is selected and hides it again when a different answer option is selected", js: true do it "shows conditional questions if the required answer is selected and hides it again when a different answer option is selected", :js do
visit("/lettings-logs/#{id}/armed-forces") visit("/lettings-logs/#{id}/armed-forces")
# Something about our styling makes the selenium webdriver think the actual radio buttons are not visible so we allow label click here # Something about our styling makes the selenium webdriver think the actual radio buttons are not visible so we allow label click here
choose("lettings-log-armedforces-1-field", allow_label_click: true) choose("lettings-log-armedforces-1-field", allow_label_click: true)
@ -59,7 +59,7 @@ RSpec.describe "Form Conditional Questions" do
end end
end end
context "when a conditional question has a saved answer", js: true do context "when a conditional question has a saved answer", :js do
before do before do
allow(sales_log.form).to receive(:new_logs_end_date).and_return(Time.zone.today + 1.day) allow(sales_log.form).to receive(:new_logs_end_date).and_return(Time.zone.today + 1.day)
allow(lettings_log.form).to receive(:new_logs_end_date).and_return(Time.zone.today + 1.day) allow(lettings_log.form).to receive(:new_logs_end_date).and_return(Time.zone.today + 1.day)
@ -96,7 +96,7 @@ RSpec.describe "Form Conditional Questions" do
FormHandler.instance.use_real_forms! FormHandler.instance.use_real_forms!
end end
it "shows conditional questions if the required answer is selected and hides it again when a different answer option is selected", js: true do it "shows conditional questions if the required answer is selected and hides it again when a different answer option is selected", :js do
visit("/lettings-logs/#{id}/lead-tenant-age") visit("/lettings-logs/#{id}/lead-tenant-age")
choose("lettings-log-age1-known-0-field", allow_label_click: true) choose("lettings-log-age1-known-0-field", allow_label_click: true)
fill_in("lettings-log-age1-field", with: "200") fill_in("lettings-log-age1-field", with: "200")

28
spec/features/form/page_routing_spec.rb

@ -29,7 +29,7 @@ RSpec.describe "Form Page Routing" do
Singleton.__init__(FormHandler) Singleton.__init__(FormHandler)
end end
it "can route the user to a different page based on their answer on the current page", js: true do it "can route the user to a different page based on their answer on the current page", :js do
visit("/lettings-logs/#{id}/conditional-question") visit("/lettings-logs/#{id}/conditional-question")
# using a question name that is already in the db to avoid # using a question name that is already in the db to avoid
# having to add a new column to the db for this test # having to add a new column to the db for this test
@ -43,7 +43,7 @@ RSpec.describe "Form Page Routing" do
expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question-no-page") expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question-no-page")
end end
it "can route based on multiple conditions", js: true do it "can route based on multiple conditions", :js do
visit("/lettings-logs/#{id}/person-1-gender") visit("/lettings-logs/#{id}/person-1-gender")
choose("lettings-log-sex1-f-field", allow_label_click: true) choose("lettings-log-sex1-f-field", allow_label_click: true)
click_button("Save and continue") click_button("Save and continue")
@ -57,7 +57,7 @@ RSpec.describe "Form Page Routing" do
expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question/check-answers") expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question/check-answers")
end end
context "when the answers are inferred", js: true do context "when the answers are inferred", :js do
it "shows question if the answer could not be inferred" do it "shows question if the answer could not be inferred" do
visit("/lettings-logs/#{id}/property-postcode") visit("/lettings-logs/#{id}/property-postcode")
fill_in("lettings-log-postcode-full-field", with: "PO5 3TE") fill_in("lettings-log-postcode-full-field", with: "PO5 3TE")
@ -119,7 +119,7 @@ RSpec.describe "Form Page Routing" do
click_button("Save and continue") click_button("Save and continue")
expect(page).to have_current_path("/lettings-logs/#{id}/tenancy-start-date") expect(page).to have_current_path("/lettings-logs/#{id}/tenancy-start-date")
expect(find_field("lettings_log[startdate]").value).to eq(nil) expect(find_field("lettings_log[startdate]").value).to be_nil
end end
it "does not show see all related answers link if only 1 field has an error" do it "does not show see all related answers link if only 1 field has an error" do
@ -170,21 +170,21 @@ RSpec.describe "Form Page Routing" do
it "returns true if there is no depends_on" do it "returns true if there is no depends_on" do
depends_on = nil depends_on = nil
expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to eq(true) expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to be(true)
end end
it "returns true if the depends_on is met" do it "returns true if the depends_on is met" do
depends_on = [{ "armedforces" => 1 }] depends_on = [{ "armedforces" => 1 }]
lettings_log.armedforces = 1 lettings_log.armedforces = 1
expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to eq(true) expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to be(true)
end end
it "returns false if the depends_on is not met" do it "returns false if the depends_on is not met" do
depends_on = [{ "armedforces" => 1 }] depends_on = [{ "armedforces" => 1 }]
lettings_log.armedforces = 0 lettings_log.armedforces = 0
expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to eq(false) expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to be(false)
end end
it "returns true if a complex depends_on is met" do it "returns true if a complex depends_on is met" do
@ -192,7 +192,7 @@ RSpec.describe "Form Page Routing" do
lettings_log.is_la_inferred = false lettings_log.is_la_inferred = false
lettings_log.needstype = 1 lettings_log.needstype = 1
expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to eq(true) expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to be(true)
end end
it "returns false if any part of a complex depends_on is not met" do it "returns false if any part of a complex depends_on is not met" do
@ -200,7 +200,7 @@ RSpec.describe "Form Page Routing" do
lettings_log.is_la_inferred = false lettings_log.is_la_inferred = false
lettings_log.needstype = 2 lettings_log.needstype = 2
expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to eq(false) expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to be(false)
end end
it "returns true if the first of multiple depends_ons are met" do it "returns true if the first of multiple depends_ons are met" do
@ -208,7 +208,7 @@ RSpec.describe "Form Page Routing" do
lettings_log.is_la_inferred = false lettings_log.is_la_inferred = false
lettings_log.needstype = 2 lettings_log.needstype = 2
expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to eq(true) expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to be(true)
end end
it "returns true if the last of multiple depends_ons are met" do it "returns true if the last of multiple depends_ons are met" do
@ -216,7 +216,7 @@ RSpec.describe "Form Page Routing" do
lettings_log.is_la_inferred = true lettings_log.is_la_inferred = true
lettings_log.needstype = 1 lettings_log.needstype = 1
expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to eq(true) expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to be(true)
end end
context "with operator-based depends_ons" do context "with operator-based depends_ons" do
@ -234,7 +234,7 @@ RSpec.describe "Form Page Routing" do
lettings_log.details_known_2 = 0 lettings_log.details_known_2 = 0
lettings_log.age2 = 16 lettings_log.age2 = 16
expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to eq(true) expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to be(true)
end end
it "returns false if an operator-based depends_on is not met" do it "returns false if an operator-based depends_on is not met" do
@ -251,7 +251,7 @@ RSpec.describe "Form Page Routing" do
lettings_log.details_known_2 = 0 lettings_log.details_known_2 = 0
lettings_log.age2 = 15 lettings_log.age2 = 15
expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to eq(false) expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to be(false)
end end
it "returns true if an operator-based depends_on is met on an inequality threshold" do it "returns true if an operator-based depends_on is met on an inequality threshold" do
@ -268,7 +268,7 @@ RSpec.describe "Form Page Routing" do
lettings_log.details_known_2 = 0 lettings_log.details_known_2 = 0
lettings_log.age2 = 15 lettings_log.age2 = 15
expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to eq(true) expect(lettings_log.form.depends_on_met(depends_on, lettings_log)).to be(true)
end end
end end
end end

4
spec/features/form/progressive_total_field_spec.rb

@ -31,7 +31,7 @@ RSpec.describe "Accessible Autocomplete" do
expect(page).to have_selector("#tcharge_div", visible: :all) expect(page).to have_selector("#tcharge_div", visible: :all)
end end
it "does show when js is enabled and calculates the total", js: true do it "does show when js is enabled and calculates the total", :js do
visit("/lettings-logs/#{lettings_log.id}/rent") visit("/lettings-logs/#{lettings_log.id}/rent")
expect(page).to have_selector("#tcharge_div") expect(page).to have_selector("#tcharge_div")
fill_in("lettings-log-brent-field", with: 5) fill_in("lettings-log-brent-field", with: 5)
@ -40,7 +40,7 @@ RSpec.describe "Accessible Autocomplete" do
expect(find("#lettings-log-tcharge-field").value).to eq("8.00") expect(find("#lettings-log-tcharge-field").value).to eq("8.00")
end end
it "total displays despite error message", js: true do it "total displays despite error message", :js do
visit("/lettings-logs/#{lettings_log.id}/rent") visit("/lettings-logs/#{lettings_log.id}/rent")
choose("lettings-log-period-1-field", allow_label_click: true) choose("lettings-log-period-1-field", allow_label_click: true)
fill_in("lettings-log-brent-field", with: 500) fill_in("lettings-log-brent-field", with: 500)

2
spec/features/form/saving_data_spec.rb

@ -62,7 +62,7 @@ RSpec.describe "Form Saving Data" do
end end
end end
it "updates total value of the rent", js: true do it "updates total value of the rent", :js do
visit("/lettings-logs/#{id}/rent") visit("/lettings-logs/#{id}/rent")
fill_in("lettings-log-brent-field", with: 3.02) fill_in("lettings-log-brent-field", with: 3.02)

2
spec/features/form/validations_spec.rb

@ -60,7 +60,7 @@ RSpec.describe "validations" do
end end
end end
describe "date validation", js: true do describe "date validation", :js do
def fill_in_date(lettings_log_id, question, day, month, year, path) def fill_in_date(lettings_log_id, question, day, month, year, path)
visit("/lettings-logs/#{lettings_log_id}/#{path}") visit("/lettings-logs/#{lettings_log_id}/#{path}")
fill_in("lettings_log[#{question}]", with: [day, month, year].join("/")) fill_in("lettings_log[#{question}]", with: [day, month, year].join("/"))

6
spec/features/lettings_log_spec.rb

@ -398,7 +398,7 @@ RSpec.describe "Lettings Log Features" do
rows = page.find_all "tbody tr" rows = page.find_all "tbody tr"
expect(rows.count).to be 2 expect(rows.count).to be 2
id_to_delete, id_to_keep = rows.map { |row| row.first("td").text.to_i } id_to_delete, id_to_keep = rows.map { |row| row.first("td").text.to_i }
expect([id_to_delete, id_to_keep]).to match_array [lettings_log_1.id, lettings_log_2.id] expect([id_to_delete, id_to_keep]).to contain_exactly(lettings_log_1.id, lettings_log_2.id)
check "forms-delete-logs-form-selected-ids-#{id_to_delete}-field" check "forms-delete-logs-form-selected-ids-#{id_to_delete}-field"
uncheck "forms-delete-logs-form-selected-ids-#{id_to_keep}-field" uncheck "forms-delete-logs-form-selected-ids-#{id_to_keep}-field"
click_button "Continue" click_button "Continue"
@ -413,13 +413,13 @@ RSpec.describe "Lettings Log Features" do
expect(page.find("article.app-log-summary h2").text).to eq "Log #{id_to_keep}" expect(page.find("article.app-log-summary h2").text).to eq "Log #{id_to_keep}"
deleted_log = LettingsLog.find(id_to_delete) deleted_log = LettingsLog.find(id_to_delete)
expect(deleted_log.status).to eq "deleted" expect(deleted_log.status).to eq "deleted"
expect(deleted_log.discarded_at).not_to be nil expect(deleted_log.discarded_at).not_to be_nil
end end
context "when visiting the bulk uploads page" do context "when visiting the bulk uploads page" do
let(:bulk_upload_errors) { create_list(:bulk_upload_error, 2, category: nil) } let(:bulk_upload_errors) { create_list(:bulk_upload_error, 2, category: nil) }
let(:bulk_upload) { create(:bulk_upload, :lettings, user: support_user, bulk_upload_errors:, total_logs_count: 10) } let(:bulk_upload) { create(:bulk_upload, :lettings, user: support_user, bulk_upload_errors:, total_logs_count: 10) }
let(:mock_storage_service) { instance_double("S3Service") } let(:mock_storage_service) { instance_double(Storage::S3Service) }
before do before do
allow(Storage::S3Service).to receive(:new).and_return(mock_storage_service) allow(Storage::S3Service).to receive(:new).and_return(mock_storage_service)

4
spec/features/sales_log_spec.rb

@ -91,7 +91,7 @@ RSpec.describe "Sales Log Features" do
expect(page.find("article.app-log-summary h2").text).to eq "Log #{id_to_keep}" expect(page.find("article.app-log-summary h2").text).to eq "Log #{id_to_keep}"
deleted_log = SalesLog.find(id_to_delete) deleted_log = SalesLog.find(id_to_delete)
expect(deleted_log.status).to eq "deleted" expect(deleted_log.status).to eq "deleted"
expect(deleted_log.discarded_at).not_to be nil expect(deleted_log.discarded_at).not_to be_nil
end end
end end
end end
@ -322,7 +322,7 @@ RSpec.describe "Sales Log Features" do
context "when visiting the bulk uploads page" do context "when visiting the bulk uploads page" do
let(:bulk_upload_errors) { create_list(:bulk_upload_error, 2, category: nil) } let(:bulk_upload_errors) { create_list(:bulk_upload_error, 2, category: nil) }
let(:bulk_upload) { create(:bulk_upload, :sales, user:, bulk_upload_errors:, total_logs_count: 10) } let(:bulk_upload) { create(:bulk_upload, :sales, user:, bulk_upload_errors:, total_logs_count: 10) }
let(:mock_storage_service) { instance_double("S3Service") } let(:mock_storage_service) { instance_double(Storage::S3Service) }
before do before do
allow(Storage::S3Service).to receive(:new).and_return(mock_storage_service) allow(Storage::S3Service).to receive(:new).and_return(mock_storage_service)

4
spec/features/schemes_spec.rb

@ -297,7 +297,7 @@ RSpec.describe "Schemes scheme Features" do
it "displays all schemes after I clear the search results" do it "displays all schemes after I clear the search results" do
click_link("Clear search") click_link("Clear search")
Location.all.each do |location| Location.all.find_each do |location|
expect(page).to have_content(location.name) expect(page).to have_content(location.name)
end end
end end
@ -953,7 +953,7 @@ RSpec.describe "Schemes scheme Features" do
it "displays all schemes after I clear the search results" do it "displays all schemes after I clear the search results" do
click_link("Clear search") click_link("Clear search")
Location.all.each do |location| Location.all.find_each do |location|
expect(page).to have_content(location.name) expect(page).to have_content(location.name)
end end
end end

10
spec/features/user_spec.rb

@ -37,7 +37,7 @@ RSpec.describe "User Features" do
expect(page).to have_current_path("/lettings-logs") expect(page).to have_current_path("/lettings-logs")
end end
it "can log out again", js: true do it "can log out again", :js do
visit("/lettings-logs") visit("/lettings-logs")
fill_in("user[email]", with: user.email) fill_in("user[email]", with: user.email)
fill_in("user[password]", with: "pAssword1") fill_in("user[password]", with: "pAssword1")
@ -157,8 +157,7 @@ RSpec.describe "User Features" do
end end
it "does not show 'Sign in' link when both the service_moved? and service_unavailable? feature toggles are on" do it "does not show 'Sign in' link when both the service_moved? and service_unavailable? feature toggles are on" do
allow(FeatureToggle).to receive(:service_moved?).and_return(true) allow(FeatureToggle).to receive_messages(service_moved?: true, service_unavailable?: true)
allow(FeatureToggle).to receive(:service_unavailable?).and_return(true)
visit("/lettings-logs") visit("/lettings-logs")
expect(page).not_to have_link("Sign in") expect(page).not_to have_link("Sign in")
end end
@ -363,8 +362,7 @@ RSpec.describe "User Features" do
end end
it "does not show 'Your account' or 'Sign out' links when both the service_moved? and service_unavailable? feature toggles are on" do it "does not show 'Your account' or 'Sign out' links when both the service_moved? and service_unavailable? feature toggles are on" do
allow(FeatureToggle).to receive(:service_moved?).and_return(true) allow(FeatureToggle).to receive_messages(service_moved?: true, service_unavailable?: true)
allow(FeatureToggle).to receive(:service_unavailable?).and_return(true)
visit("/lettings-logs") visit("/lettings-logs")
expect(page).not_to have_link("Your account") expect(page).not_to have_link("Your account")
expect(page).not_to have_link("Sign out") expect(page).not_to have_link("Sign out")
@ -796,7 +794,7 @@ RSpec.describe "User Features" do
end end
context "when viewing logs" do context "when viewing logs" do
context "when filtering by owning organisation and then switching back to all organisations", js: true do context "when filtering by owning organisation and then switching back to all organisations", :js do
let!(:organisation) { create(:organisation) } let!(:organisation) { create(:organisation) }
let(:parent_organisation) { create(:organisation, name: "Filtered Org") } let(:parent_organisation) { create(:organisation, name: "Filtered Org") }

6
spec/fixtures/files/lettings_log_csv_export_codes_26.csv vendored

File diff suppressed because one or more lines are too long

6
spec/fixtures/files/lettings_log_csv_export_labels_26.csv vendored

File diff suppressed because one or more lines are too long

2
spec/helpers/collection_resources_helper_spec.rb

@ -62,7 +62,7 @@ RSpec.describe CollectionResourcesHelper do
let(:current_date) { Time.zone.local(2025, 2, 1) } let(:current_date) { Time.zone.local(2025, 2, 1) }
it "returns current and next years" do it "returns current and next years" do
expect(editable_collection_resource_years).to match_array([2024, 2025]) expect(editable_collection_resource_years).to contain_exactly(2024, 2025)
end end
end end

4
spec/helpers/question_attribute_helper_spec.rb

@ -11,12 +11,12 @@ RSpec.describe QuestionAttributeHelper do
end end
describe "html attributes" do describe "html attributes" do
it "returns empty hash if fields-to-add or result-field are empty " do it "returns empty hash if fields-to-add or result-field are empty" do
question = form.get_page("weekly_net_income").questions.find { |q| q.id == "earnings" } question = form.get_page("weekly_net_income").questions.find { |q| q.id == "earnings" }
expect(stimulus_html_attributes(question)).to eq({}) expect(stimulus_html_attributes(question)).to eq({})
end end
it "returns html attributes if fields-to-add or result-field are not empty " do it "returns html attributes if fields-to-add or result-field are not empty" do
brent = questions.find { |q| q.id == "brent" } brent = questions.find { |q| q.id == "brent" }
expect(stimulus_html_attributes(brent)).to eq({ expect(stimulus_html_attributes(brent)).to eq({
"data-controller": "numeric-question", "data-controller": "numeric-question",

2
spec/helpers/tag_helper_spec.rb

@ -11,7 +11,7 @@ RSpec.describe TagHelper do
it "returns tag with correct status text and colour and custom class" do it "returns tag with correct status text and colour and custom class" do
expect(status_tag("not_started", "app-tag--small")).to eq("<strong class=\"govuk-tag govuk-tag--light-blue app-tag--small\">Not started</strong>") expect(status_tag("not_started", "app-tag--small")).to eq("<strong class=\"govuk-tag govuk-tag--light-blue app-tag--small\">Not started</strong>")
expect(status_tag("cannot_start_yet", "app-tag--small")).to eq(nil) expect(status_tag("cannot_start_yet", "app-tag--small")).to be_nil
expect(status_tag("in_progress", "app-tag--small")).to eq("<strong class=\"govuk-tag govuk-tag--blue app-tag--small\">In progress</strong>") expect(status_tag("in_progress", "app-tag--small")).to eq("<strong class=\"govuk-tag govuk-tag--blue app-tag--small\">In progress</strong>")
expect(status_tag("completed", "app-tag--small")).to eq("<strong class=\"govuk-tag govuk-tag--green app-tag--small\">Completed</strong>") expect(status_tag("completed", "app-tag--small")).to eq("<strong class=\"govuk-tag govuk-tag--green app-tag--small\">Completed</strong>")
expect(status_tag("active", "app-tag--small")).to eq("<strong class=\"govuk-tag govuk-tag--green app-tag--small\">Active</strong>") expect(status_tag("active", "app-tag--small")).to eq("<strong class=\"govuk-tag govuk-tag--green app-tag--small\">Active</strong>")

8
spec/helpers/tasklist_helper_spec.rb

@ -17,8 +17,8 @@ RSpec.describe TasklistHelper do
end end
describe "get sections count" do describe "get sections count" do
let(:completed_subsection) { instance_double("Subsection", status: :completed, displayed_in_tasklist?: true, applicable_questions: [{ id: "question" }]) } let(:completed_subsection) { instance_double(Form::Subsection, status: :completed, displayed_in_tasklist?: true, applicable_questions: [{ id: "question" }]) }
let(:incomplete_subsection) { instance_double("Subsection", status: :not_started, displayed_in_tasklist?: true, applicable_questions: []) } let(:incomplete_subsection) { instance_double(Form::Subsection, status: :not_started, displayed_in_tasklist?: true, applicable_questions: []) }
context "with an empty lettings log" do context "with an empty lettings log" do
before do before do
@ -79,8 +79,8 @@ RSpec.describe TasklistHelper do
describe "with sales" do describe "with sales" do
let(:empty_sales_log) { build(:sales_log, owning_organisation: nil) } let(:empty_sales_log) { build(:sales_log, owning_organisation: nil) }
let(:completed_sales_log) { build(:sales_log, :completed) } let(:completed_sales_log) { build(:sales_log, :completed) }
let(:completed_subsection) { instance_double("Subsection", status: :completed, displayed_in_tasklist?: true, applicable_questions: [{ id: "question" }]) } let(:completed_subsection) { instance_double(Form::Subsection, status: :completed, displayed_in_tasklist?: true, applicable_questions: [{ id: "question" }]) }
let(:incomplete_subsection) { instance_double("Subsection", status: :not_started, displayed_in_tasklist?: true, applicable_questions: []) } let(:incomplete_subsection) { instance_double(Form::Subsection, status: :not_started, displayed_in_tasklist?: true, applicable_questions: []) }
describe "get sections count" do describe "get sections count" do
context "with an empty sales log" do context "with an empty sales log" do

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

Loading…
Cancel
Save