diff --git a/Gemfile b/Gemfile index aae99d51e..d80a159dd 100644 --- a/Gemfile +++ b/Gemfile @@ -18,7 +18,7 @@ gem "jsbundling-rails" # Reduces boot times through caching; required in config/boot.rb gem "bootsnap", ">= 1.4.4", require: false # GOV UK frontend components -gem "govuk-components", "~> 5.0" +gem "govuk-components", "~> 5.1" # GOV UK component form builder DSL gem "govuk_design_system_formbuilder", "~> 5.0" # Convert Markdown into GOV.UK frontend-styled HTML diff --git a/Gemfile.lock b/Gemfile.lock index 0970a7721..47895d4f1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,134 +1,137 @@ GEM remote: https://rubygems.org/ specs: - actioncable (7.0.7.2) - actionpack (= 7.0.7.2) - activesupport (= 7.0.7.2) + actioncable (7.0.8.1) + actionpack (= 7.0.8.1) + activesupport (= 7.0.8.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.7.2) - actionpack (= 7.0.7.2) - activejob (= 7.0.7.2) - activerecord (= 7.0.7.2) - activestorage (= 7.0.7.2) - activesupport (= 7.0.7.2) + actionmailbox (7.0.8.1) + actionpack (= 7.0.8.1) + activejob (= 7.0.8.1) + activerecord (= 7.0.8.1) + activestorage (= 7.0.8.1) + activesupport (= 7.0.8.1) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.7.2) - actionpack (= 7.0.7.2) - actionview (= 7.0.7.2) - activejob (= 7.0.7.2) - activesupport (= 7.0.7.2) + actionmailer (7.0.8.1) + actionpack (= 7.0.8.1) + actionview (= 7.0.8.1) + activejob (= 7.0.8.1) + activesupport (= 7.0.8.1) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.0) - actionpack (7.0.7.2) - actionview (= 7.0.7.2) - activesupport (= 7.0.7.2) + actionpack (7.0.8.1) + actionview (= 7.0.8.1) + activesupport (= 7.0.8.1) rack (~> 2.0, >= 2.2.4) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.7.2) - actionpack (= 7.0.7.2) - activerecord (= 7.0.7.2) - activestorage (= 7.0.7.2) - activesupport (= 7.0.7.2) + actiontext (7.0.8.1) + actionpack (= 7.0.8.1) + activerecord (= 7.0.8.1) + activestorage (= 7.0.8.1) + activesupport (= 7.0.8.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.7.2) - activesupport (= 7.0.7.2) + actionview (7.0.8.1) + activesupport (= 7.0.8.1) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (7.0.7.2) - activesupport (= 7.0.7.2) + activejob (7.0.8.1) + activesupport (= 7.0.8.1) globalid (>= 0.3.6) - activemodel (7.0.7.2) - activesupport (= 7.0.7.2) - activerecord (7.0.7.2) - activemodel (= 7.0.7.2) - activesupport (= 7.0.7.2) - activestorage (7.0.7.2) - actionpack (= 7.0.7.2) - activejob (= 7.0.7.2) - activerecord (= 7.0.7.2) - activesupport (= 7.0.7.2) + activemodel (7.0.8.1) + activesupport (= 7.0.8.1) + activerecord (7.0.8.1) + activemodel (= 7.0.8.1) + activesupport (= 7.0.8.1) + activestorage (7.0.8.1) + actionpack (= 7.0.8.1) + activejob (= 7.0.8.1) + activerecord (= 7.0.8.1) + activesupport (= 7.0.8.1) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (7.0.7.2) + activesupport (7.0.8.1) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - addressable (2.8.1) + addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) ast (2.4.2) auto_strip_attributes (2.6.0) activerecord (>= 4.0) - aws-eventstream (1.2.0) - aws-partitions (1.714.0) - aws-sdk-core (3.170.0) - aws-eventstream (~> 1, >= 1.0.2) + aws-eventstream (1.3.0) + aws-partitions (1.894.0) + aws-sdk-core (3.191.3) + aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.5) + aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.62.0) - aws-sdk-core (~> 3, >= 3.165.0) + aws-sdk-kms (1.77.0) + aws-sdk-core (~> 3, >= 3.191.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.119.1) - aws-sdk-core (~> 3, >= 3.165.0) + aws-sdk-s3 (1.143.0) + aws-sdk-core (~> 3, >= 3.191.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.4) - aws-sigv4 (1.5.2) + aws-sigv4 (~> 1.8) + aws-sigv4 (1.8.0) aws-eventstream (~> 1, >= 1.0.2) - bcrypt (3.1.18) - better_html (2.0.1) + base64 (0.2.0) + bcrypt (3.1.20) + better_html (2.0.2) actionview (>= 6.0) activesupport (>= 6.0) ast (~> 2.0) erubi (~> 1.4) parser (>= 2.4) smart_properties + bigdecimal (3.1.6) bindex (0.8.1) - bootsnap (1.16.0) + bootsnap (1.18.3) msgpack (~> 1.2) builder (3.2.4) bundler-audit (0.9.1) bundler (>= 1.2.0, < 3) thor (~> 1.0) byebug (11.1.3) - capybara (3.38.0) + capybara (3.40.0) addressable matrix mini_mime (>= 0.1.3) - nokogiri (~> 1.8) + nokogiri (~> 1.11) rack (>= 1.6.0) rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - capybara-lockstep (1.3.0) - activesupport (>= 3.2) - capybara (>= 2.0) + capybara-lockstep (2.2.0) + activesupport (>= 4.2) + capybara (>= 3.0) ruby2_keywords - selenium-webdriver (>= 3) + selenium-webdriver (>= 4.0) capybara-screenshot (1.0.26) capybara (>= 1.0, < 4) launchy - childprocess (4.1.0) + childprocess (5.0.0) coderay (1.1.3) - concurrent-ruby (1.2.2) + concurrent-ruby (1.2.3) connection_pool (2.4.1) - crack (0.4.5) + crack (1.0.0) + bigdecimal rexml crass (1.0.6) - date (3.3.3) - devise (4.8.1) + date (3.3.4) + devise (4.9.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0) @@ -140,14 +143,14 @@ GEM rails (>= 3.1.1) randexp rotp (>= 4.0.0) - diff-lcs (1.5.0) + diff-lcs (1.5.1) docile (1.4.0) - dotenv (2.8.1) - dotenv-rails (2.8.1) - dotenv (= 2.8.1) - railties (>= 3.2) + dotenv (3.0.2) + dotenv-rails (3.0.2) + dotenv (= 3.0.2) + railties (>= 6.1) encryptor (3.0.0) - erb_lint (0.3.1) + erb_lint (0.5.0) activesupport better_html (>= 2.0.1) parser (>= 2.7.1.4) @@ -157,47 +160,48 @@ GEM erubi (1.12.0) et-orbi (1.2.7) tzinfo - excon (0.99.0) - factory_bot (6.2.1) + excon (0.109.0) + factory_bot (6.4.6) activesupport (>= 5.0.0) - factory_bot_rails (6.2.0) - factory_bot (~> 6.2.0) + factory_bot_rails (6.4.3) + factory_bot (~> 6.4) railties (>= 5.0.0) - faker (3.1.1) + faker (3.2.3) i18n (>= 1.8.11, < 2) - ffi (1.15.5) - fugit (1.8.1) + ffi (1.16.3) + fugit (1.10.0) et-orbi (~> 1, >= 1.2.7) raabro (~> 1.4) - globalid (1.1.0) - activesupport (>= 5.0) - govuk-components (5.0.2) + globalid (1.2.1) + activesupport (>= 6.1) + govuk-components (5.2.1) html-attributes-utils (~> 1.0.0, >= 1.0.0) pagy (~> 6.0) - view_component (>= 3.9, < 3.10) - govuk_design_system_formbuilder (5.0.0) + view_component (>= 3.9, < 3.11) + govuk_design_system_formbuilder (5.2.0) actionview (>= 6.1) activemodel (>= 6.1) activesupport (>= 6.1) html-attributes-utils (~> 1) - govuk_markdown (2.0.0) + govuk_markdown (2.0.1) activesupport redcarpet - hashdiff (1.0.1) + hashdiff (1.1.0) html-attributes-utils (1.0.2) activesupport (>= 6.1.4.4) i18n (1.14.1) concurrent-ruby (~> 1.0) iniparse (1.5.0) jmespath (1.6.2) - jsbundling-rails (1.1.1) + jsbundling-rails (1.3.0) railties (>= 6.0.0) - json-schema (3.0.0) + json-schema (4.1.1) addressable (>= 2.8) - jwt (2.7.0) + jwt (2.8.0) + base64 launchy (2.5.2) addressable (~> 2.8) - listen (3.8.0) + listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) loofah (2.22.0) @@ -212,16 +216,16 @@ GEM matrix (0.4.2) method_source (1.0.0) mini_mime (1.1.5) - minitest (5.20.0) - msgpack (1.6.0) - net-imap (0.3.7) + minitest (5.22.2) + msgpack (1.7.2) + net-imap (0.4.10) date net-protocol net-pop (0.1.2) net-protocol - net-protocol (0.2.1) + net-protocol (0.2.2) timeout - net-smtp (0.3.3) + net-smtp (0.4.0.1) net-protocol nio4r (2.7.0) nokogiri (1.16.2-arm64-darwin) @@ -230,30 +234,31 @@ GEM racc (~> 1.4) nokogiri (1.16.2-x86_64-linux) racc (~> 1.4) - notifications-ruby-client (5.4.0) + notifications-ruby-client (6.0.0) jwt (>= 1.5, < 3) orm_adapter (0.5.0) - overcommit (0.60.0) - childprocess (>= 0.6.3, < 5) + overcommit (0.63.0) + childprocess (>= 0.6.3, < 6) iniparse (~> 1.4) rexml (~> 3.2) - pagy (6.2.0) - paper_trail (14.0.0) - activerecord (>= 6.0) + pagy (6.5.0) + paper_trail (15.1.0) + activerecord (>= 6.1) request_store (~> 1.4) paper_trail-globalid (0.2.0) globalid paper_trail (>= 3.0.0) - parallel (1.22.1) - parallel_tests (4.2.0) + parallel (1.24.0) + parallel_tests (4.5.1) parallel - parser (3.2.1.0) + parser (3.3.0.5) ast (~> 2.4.1) - pg (1.4.5) + racc + pg (1.5.5) possessive (1.0.1) postcodes_io (0.4.0) excon (~> 0.39) - propshaft (0.6.4) + propshaft (0.8.0) actionpack (>= 7.0.0) activesupport (>= 7.0.0) rack @@ -264,34 +269,34 @@ GEM pry-byebug (3.10.1) byebug (~> 11.0) pry (>= 0.13, < 0.15) - public_suffix (5.0.1) + public_suffix (5.0.4) puma (5.6.8) nio4r (~> 2.0) - pundit (2.3.0) + pundit (2.3.1) activesupport (>= 3.0.0) raabro (1.4.0) racc (1.7.3) - rack (2.2.8) - rack-attack (6.6.1) - rack (>= 1.0, < 3) + rack (2.2.8.1) + rack-attack (6.7.0) + rack (>= 1.0, < 4) rack-mini-profiler (2.3.4) rack (>= 1.2.0) rack-test (2.1.0) rack (>= 1.3) - rails (7.0.7.2) - actioncable (= 7.0.7.2) - actionmailbox (= 7.0.7.2) - actionmailer (= 7.0.7.2) - actionpack (= 7.0.7.2) - actiontext (= 7.0.7.2) - actionview (= 7.0.7.2) - activejob (= 7.0.7.2) - activemodel (= 7.0.7.2) - activerecord (= 7.0.7.2) - activestorage (= 7.0.7.2) - activesupport (= 7.0.7.2) + rails (7.0.8.1) + actioncable (= 7.0.8.1) + actionmailbox (= 7.0.8.1) + actionmailer (= 7.0.8.1) + actionpack (= 7.0.8.1) + actiontext (= 7.0.8.1) + actionview (= 7.0.8.1) + activejob (= 7.0.8.1) + activemodel (= 7.0.8.1) + activerecord (= 7.0.8.1) + activestorage (= 7.0.8.1) + activesupport (= 7.0.8.1) bundler (>= 1.15.0) - railties (= 7.0.7.2) + railties (= 7.0.8.1) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest @@ -299,51 +304,51 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - railties (7.0.7.2) - actionpack (= 7.0.7.2) - activesupport (= 7.0.7.2) + railties (7.0.8.1) + actionpack (= 7.0.8.1) + activesupport (= 7.0.8.1) method_source rake (>= 12.2) thor (~> 1.0) zeitwerk (~> 2.5) rainbow (3.1.1) - rake (13.0.6) + rake (13.1.0) randexp (0.1.7) rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) redcarpet (3.6.0) redis (4.8.1) - redis-client (0.17.0) + redis-client (0.20.0) connection_pool - regexp_parser (2.7.0) - request_store (1.5.1) + regexp_parser (2.9.0) + request_store (1.6.0) rack (>= 1.4) - responders (3.1.0) + responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) - rexml (3.2.5) - roo (2.10.0) + rexml (3.2.6) + roo (2.10.1) nokogiri (~> 1) rubyzip (>= 1.3.0, < 3.0.0) - rotp (6.2.2) - rspec-core (3.12.1) - rspec-support (~> 3.12.0) - rspec-expectations (3.12.2) + rotp (6.3.0) + rspec-core (3.13.0) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-mocks (3.12.3) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-rails (6.0.1) + rspec-support (~> 3.13.0) + rspec-rails (6.1.1) actionpack (>= 6.1) activesupport (>= 6.1) railties (>= 6.1) - rspec-core (~> 3.11) - rspec-expectations (~> 3.11) - rspec-mocks (~> 3.11) - rspec-support (~> 3.11) - rspec-support (3.12.0) + rspec-core (~> 3.12) + rspec-expectations (~> 3.12) + rspec-mocks (~> 3.12) + rspec-support (~> 3.12) + rspec-support (3.13.1) rubocop (1.25.0) parallel (~> 1.10) parser (>= 3.1.0.0) @@ -361,7 +366,7 @@ GEM rubocop-rails (= 2.13.2) rubocop-rake (= 0.6.0) rubocop-rspec (= 2.7.0) - rubocop-performance (1.16.0) + rubocop-performance (1.19.1) rubocop (>= 1.7.0, < 2.0) rubocop-ast (>= 0.4.0) rubocop-rails (2.13.2) @@ -372,26 +377,28 @@ GEM rubocop (~> 1.0) rubocop-rspec (2.7.0) rubocop (~> 1.19) - ruby-progressbar (1.11.0) + ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) rubyzip (2.3.2) - selenium-webdriver (4.8.1) + selenium-webdriver (4.18.1) + base64 (~> 0.2) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) - sentry-rails (5.8.0) + sentry-rails (5.16.1) railties (>= 5.0) - sentry-ruby (~> 5.8.0) - sentry-ruby (5.8.0) + sentry-ruby (~> 5.16.1) + sentry-ruby (5.16.1) concurrent-ruby (~> 1.0, >= 1.0.2) - sidekiq (7.1.3) + sidekiq (7.2.2) concurrent-ruby (< 2) connection_pool (>= 2.3.0) rack (>= 2.2.4) - redis-client (>= 0.14.0) - sidekiq-cron (1.9.1) + redis-client (>= 0.19.0) + sidekiq-cron (1.12.0) fugit (~> 1.8) - sidekiq (>= 4.2.1) + globalid (>= 1.0.1) + sidekiq (>= 6) simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) @@ -399,39 +406,39 @@ GEM simplecov-html (0.12.3) simplecov_json_formatter (0.1.4) smart_properties (1.17.0) - stimulus-rails (1.2.1) + stimulus-rails (1.3.3) railties (>= 6.0.0) - thor (1.2.2) - timecop (0.9.6) - timeout (0.4.0) + thor (1.3.0) + timecop (0.9.8) + timeout (0.4.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) uk_postcode (2.1.8) - unicode-display_width (2.4.2) - unread (0.13.0) + unicode-display_width (2.5.0) + unread (0.13.1) activerecord (>= 6.1) - view_component (3.9.0) + view_component (3.10.0) activesupport (>= 5.2.0, < 8.0) concurrent-ruby (~> 1.0) method_source (~> 1.0) warden (1.2.9) rack (>= 2.0.9) - web-console (4.2.0) + web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) - webmock (3.18.1) + webmock (3.23.0) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - websocket (1.2.9) + websocket (1.2.10) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.11) + zeitwerk (2.6.13) PLATFORMS arm64-darwin-21 @@ -458,7 +465,7 @@ DEPENDENCIES erb_lint factory_bot_rails faker - govuk-components (~> 5.0) + govuk-components (~> 5.1) govuk_design_system_formbuilder (~> 5.0) govuk_markdown jsbundling-rails diff --git a/app/components/sales_log_summary_component.html.erb b/app/components/sales_log_summary_component.html.erb index 82acc6e14..5c0668301 100644 --- a/app/components/sales_log_summary_component.html.erb +++ b/app/components/sales_log_summary_component.html.erb @@ -32,7 +32,7 @@
<%= log.owning_organisation&.name %>
<% end %> - <% if log.managing_organisation && FeatureToggle.sales_managing_organisation_enabled? %> + <% if log.managing_organisation %>
Reported by
<%= log.managing_organisation&.name %>
diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb index 0952d12ac..4612e6bc9 100644 --- a/app/controllers/organisations_controller.rb +++ b/app/controllers/organisations_controller.rb @@ -33,7 +33,7 @@ class OrganisationsController < ApplicationController organisation_schemes = Scheme.where(owning_organisation: [@organisation] + @organisation.parent_organisations) unpaginated_filtered_schemes = filter_manager.filtered_schemes(organisation_schemes, search_term, session_filters) - render "schemes/download_csv", locals: { search_term:, post_path: email_csv_schemes_path, download_type: params[:download_type], schemes: unpaginated_filtered_schemes } + render "schemes/download_csv", locals: { search_term:, post_path: schemes_email_csv_organisation_path, download_type: params[:download_type], schemes: unpaginated_filtered_schemes } end def email_schemes_csv diff --git a/app/helpers/collection_time_helper.rb b/app/helpers/collection_time_helper.rb index 6f8ef62fc..4381aa43a 100644 --- a/app/helpers/collection_time_helper.rb +++ b/app/helpers/collection_time_helper.rb @@ -46,6 +46,10 @@ module CollectionTimeHelper current_collection_start_date - 1.year end + def archived_collection_start_year + current_collection_start_year - 2 + end + def quarter_for_date(date: Time.zone.now) quarters = [ { quarter: "Q3", cutoff_date: Time.zone.local(2024, 1, 12), start_date: Time.zone.local(2023, 10, 1), end_date: Time.zone.local(2023, 12, 31) }, diff --git a/app/helpers/filters_helper.rb b/app/helpers/filters_helper.rb index f35caf31f..8841a0d36 100644 --- a/app/helpers/filters_helper.rb +++ b/app/helpers/filters_helper.rb @@ -1,4 +1,6 @@ module FiltersHelper + include CollectionTimeHelper + def filter_selected?(filter, value, filter_type) return false unless session[session_name_for(filter_type)] @@ -93,7 +95,11 @@ module FiltersHelper end def collection_year_options - { "2023": "2023/24", "2022": "2022/23", "2021": "2021/22" } + { + current_collection_start_year.to_s => year_combo(current_collection_start_year), + previous_collection_start_year.to_s => year_combo(previous_collection_start_year), + archived_collection_start_year.to_s => year_combo(archived_collection_start_year), + } end def filters_applied_text(filter_type) @@ -174,4 +180,8 @@ private end end end + + def year_combo(year) + "#{year}/#{year - 2000 + 1}" + end end diff --git a/app/jobs/scheme_email_csv_job.rb b/app/jobs/scheme_email_csv_job.rb index 1dad752c5..9c3060cb9 100644 --- a/app/jobs/scheme_email_csv_job.rb +++ b/app/jobs/scheme_email_csv_job.rb @@ -6,7 +6,11 @@ class SchemeEmailCsvJob < ApplicationJob EXPIRATION_TIME = 24.hours.to_i def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil, download_type = "combined") # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params - unfiltered_schemes = organisation.present? && user.support? ? Scheme.where(owning_organisation_id: organisation.id) : user.schemes + unfiltered_schemes = if organisation.present? && user.support? + Scheme.where(owning_organisation: [organisation] + organisation.parent_organisations) + else + user.schemes + end filtered_schemes = FilterManager.filter_schemes(unfiltered_schemes, search_term, filters, all_orgs, user) csv_string = Csv::SchemeCsvService.new(download_type:).prepare_csv(filtered_schemes) diff --git a/app/models/bulk_upload.rb b/app/models/bulk_upload.rb index 5ddc485f5..ababa6dd8 100644 --- a/app/models/bulk_upload.rb +++ b/app/models/bulk_upload.rb @@ -1,5 +1,6 @@ class BulkUpload < ApplicationRecord enum log_type: { lettings: "lettings", sales: "sales" } + enum noint_fix_status: { not_applied: "not_applied", applied: "applied", not_needed: "not_needed" } belongs_to :user diff --git a/app/models/form/lettings/pages/reasonother_value_check.rb b/app/models/form/lettings/pages/reasonother_value_check.rb new file mode 100644 index 000000000..ccda997a7 --- /dev/null +++ b/app/models/form/lettings/pages/reasonother_value_check.rb @@ -0,0 +1,23 @@ +class Form::Lettings::Pages::ReasonotherValueCheck < ::Form::Page + def initialize(id, hsh, subsection) + super + @id = "reasonother_value_check" + @depends_on = [{ "reasonother_might_be_existing_category?" => true }] + @title_text = { + "translation" => "soft_validations.reasonother.title_text", + "arguments" => [{ "key" => "reasonother", "i18n_template" => "reasonother" }], + } + @informative_text = { + "translation" => "soft_validations.reasonother.informative_text", + "arguments" => [], + } + end + + def questions + @questions ||= [Form::Lettings::Questions::ReasonotherValueCheck.new(nil, nil, self)] + end + + def interruption_screen_question_ids + %w[reason reasonother] + end +end diff --git a/app/models/form/lettings/pages/stock_owner.rb b/app/models/form/lettings/pages/stock_owner.rb index 07fe01327..a653e7fe2 100644 --- a/app/models/form/lettings/pages/stock_owner.rb +++ b/app/models/form/lettings/pages/stock_owner.rb @@ -14,16 +14,10 @@ class Form::Lettings::Pages::StockOwner < ::Form::Page return false unless current_user return true if current_user.support? - stock_owners = if FeatureToggle.merge_organisations_enabled? - current_user.organisation.stock_owners + current_user.organisation.absorbed_organisations.where(holds_own_stock: true) - else - current_user.organisation.stock_owners - end + stock_owners = current_user.organisation.stock_owners + current_user.organisation.absorbed_organisations.where(holds_own_stock: true) if current_user.organisation.holds_own_stock? - if FeatureToggle.merge_organisations_enabled? && current_user.organisation.absorbed_organisations.any?(&:holds_own_stock?) - return true - end + return true if current_user.organisation.absorbed_organisations.any?(&:holds_own_stock?) return true if stock_owners.count >= 1 log.update!(owning_organisation: current_user.organisation) diff --git a/app/models/form/lettings/questions/person_working_situation.rb b/app/models/form/lettings/questions/person_working_situation.rb index 74bd3406e..dce08af4f 100644 --- a/app/models/form/lettings/questions/person_working_situation.rb +++ b/app/models/form/lettings/questions/person_working_situation.rb @@ -12,8 +12,8 @@ class Form::Lettings::Questions::PersonWorkingSituation < ::Form::Question end ANSWER_OPTIONS = { - "2" => { "value" => "Part-time – Less than 30 hours" }, "1" => { "value" => "Full-time – 30 hours or more" }, + "2" => { "value" => "Part-time – Less than 30 hours" }, "7" => { "value" => "Full-time student" }, "3" => { "value" => "In government training into work, such as New Deal" }, "4" => { "value" => "Jobseeker" }, diff --git a/app/models/form/lettings/questions/reasonother_value_check.rb b/app/models/form/lettings/questions/reasonother_value_check.rb new file mode 100644 index 000000000..865f38764 --- /dev/null +++ b/app/models/form/lettings/questions/reasonother_value_check.rb @@ -0,0 +1,14 @@ +class Form::Lettings::Questions::ReasonotherValueCheck < ::Form::Question + def initialize(id, hsh, page) + super + @id = "reasonother_value_check" + @check_answer_label = "Reason other confirmation" + @header = "Are you sure this doesn’t fit an existing category?" + @type = "interruption_screen" + @check_answers_card_number = 0 + @answer_options = ANSWER_OPTIONS + @hidden_in_check_answers = { "depends_on" => [{ "reasonother_value_check" => 0 }, { "reasonother_value_check" => 1 }] } + end + + ANSWER_OPTIONS = { "0" => { "value" => "Yes" }, "1" => { "value" => "No" } }.freeze +end diff --git a/app/models/form/lettings/questions/stock_owner.rb b/app/models/form/lettings/questions/stock_owner.rb index c78c4a080..6b5e77c6d 100644 --- a/app/models/form/lettings/questions/stock_owner.rb +++ b/app/models/form/lettings/questions/stock_owner.rb @@ -67,11 +67,7 @@ class Form::Lettings::Questions::StockOwner < ::Form::Question def hidden_in_check_answers?(_log, user = nil) return false if user.support? - stock_owners = if FeatureToggle.merge_organisations_enabled? - user.organisation.stock_owners + user.organisation.absorbed_organisations.where(holds_own_stock: true) - else - user.organisation.stock_owners - end + stock_owners = user.organisation.stock_owners + user.organisation.absorbed_organisations.where(holds_own_stock: true) if user.organisation.holds_own_stock? stock_owners.count.zero? diff --git a/app/models/form/lettings/subsections/household_situation.rb b/app/models/form/lettings/subsections/household_situation.rb index 9db7c1f04..6646d7230 100644 --- a/app/models/form/lettings/subsections/household_situation.rb +++ b/app/models/form/lettings/subsections/household_situation.rb @@ -12,6 +12,7 @@ class Form::Lettings::Subsections::HouseholdSituation < ::Form::Subsection Form::Lettings::Pages::TimeOnWaitingList.new(nil, nil, self), Form::Lettings::Pages::ReasonForLeavingLastSettledHome.new(nil, nil, self), Form::Lettings::Pages::ReasonForLeavingLastSettledHomeRenewal.new(nil, nil, self), + (Form::Lettings::Pages::ReasonotherValueCheck.new(nil, nil, self) if form.start_year_after_2024?), Form::Lettings::Pages::PreviousHousingSituation.new(nil, nil, self), Form::Lettings::Pages::PreviousHousingSituationRenewal.new(nil, nil, self), Form::Lettings::Pages::Homelessness.new("homelessness", nil, self), diff --git a/app/models/form/sales/pages/managing_organisation.rb b/app/models/form/sales/pages/managing_organisation.rb index 0c9ed7337..3d8e59383 100644 --- a/app/models/form/sales/pages/managing_organisation.rb +++ b/app/models/form/sales/pages/managing_organisation.rb @@ -12,10 +12,20 @@ class Form::Sales::Pages::ManagingOrganisation < ::Form::Page def routed_to?(log, current_user) return false unless current_user - return false unless current_user.support? - return false unless FeatureToggle.sales_managing_organisation_enabled? - return false unless log.owning_organisation - log.owning_organisation.managing_agents.count >= 1 + if form.start_year_after_2024? + organisation = current_user.support? ? log.owning_organisation : current_user.organisation + + return false unless organisation + return false if log.owning_organisation != organisation && !organisation.holds_own_stock? + return true unless organisation.holds_own_stock? + + organisation.managing_agents.count >= 1 + else + return false unless current_user.support? + return false unless log.owning_organisation + + log.owning_organisation.managing_agents.count >= 1 + end end end diff --git a/app/models/form/sales/pages/owning_organisation.rb b/app/models/form/sales/pages/owning_organisation.rb index d8787a456..f0c9e4e68 100644 --- a/app/models/form/sales/pages/owning_organisation.rb +++ b/app/models/form/sales/pages/owning_organisation.rb @@ -13,19 +13,12 @@ class Form::Sales::Pages::OwningOrganisation < ::Form::Page def routed_to?(log, current_user) return false unless current_user return true if current_user.support? - return false unless FeatureToggle.sales_managing_organisation_enabled? return true if has_multiple_stock_owners_with_own_stock?(current_user) - stock_owners = if FeatureToggle.merge_organisations_enabled? - current_user.organisation.stock_owners.where(holds_own_stock: true) + current_user.organisation.absorbed_organisations.where(holds_own_stock: true) - else - current_user.organisation.stock_owners.where(holds_own_stock: true) - end + stock_owners = current_user.organisation.stock_owners.where(holds_own_stock: true) + current_user.organisation.absorbed_organisations.where(holds_own_stock: true) if current_user.organisation.holds_own_stock? - if FeatureToggle.merge_organisations_enabled? && current_user.organisation.absorbed_organisations.any?(&:holds_own_stock?) - return true - end + return true if current_user.organisation.absorbed_organisations.any?(&:holds_own_stock?) return true if stock_owners.count >= 1 log.update!(owning_organisation: current_user.organisation) diff --git a/app/models/form/sales/questions/age1.rb b/app/models/form/sales/questions/age1.rb index 0ae3bf682..75445bebf 100644 --- a/app/models/form/sales/questions/age1.rb +++ b/app/models/form/sales/questions/age1.rb @@ -2,7 +2,7 @@ class Form::Sales::Questions::Age1 < ::Form::Question def initialize(id, hsh, page) super @id = "age1" - @check_answer_label = "Lead buyer’s age" + @check_answer_label = "Buyer 1’s age" @header = "Age" @type = "numeric" @width = 2 diff --git a/app/models/form/sales/questions/buyer1_age_known.rb b/app/models/form/sales/questions/buyer1_age_known.rb index 566ae8f7d..b2756870f 100644 --- a/app/models/form/sales/questions/buyer1_age_known.rb +++ b/app/models/form/sales/questions/buyer1_age_known.rb @@ -2,7 +2,7 @@ class Form::Sales::Questions::Buyer1AgeKnown < ::Form::Question def initialize(id, hsh, page) super @id = "age1_known" - @check_answer_label = "Lead buyer’s age" + @check_answer_label = "Buyer 1’s age" @header = "Do you know buyer 1’s age?" @type = "radio" @answer_options = ANSWER_OPTIONS diff --git a/app/models/form/sales/questions/buyer1_working_situation.rb b/app/models/form/sales/questions/buyer1_working_situation.rb index 8d88e2422..be01aaf1c 100644 --- a/app/models/form/sales/questions/buyer1_working_situation.rb +++ b/app/models/form/sales/questions/buyer1_working_situation.rb @@ -18,8 +18,8 @@ class Form::Sales::Questions::Buyer1WorkingSituation < ::Form::Question end ANSWER_OPTIONS = { - "2" => { "value" => "Part-time - Less than 30 hours" }, "1" => { "value" => "Full-time - 30 hours or more" }, + "2" => { "value" => "Part-time - Less than 30 hours" }, "3" => { "value" => "In government training into work, such as New Deal" }, "4" => { "value" => "Jobseeker" }, "6" => { "value" => "Not seeking work" }, diff --git a/app/models/form/sales/questions/buyer2_working_situation.rb b/app/models/form/sales/questions/buyer2_working_situation.rb index 9cd82cd68..5a7a87573 100644 --- a/app/models/form/sales/questions/buyer2_working_situation.rb +++ b/app/models/form/sales/questions/buyer2_working_situation.rb @@ -17,8 +17,8 @@ class Form::Sales::Questions::Buyer2WorkingSituation < ::Form::Question end ANSWER_OPTIONS = { - "2" => { "value" => "Part-time - Less than 30 hours" }, "1" => { "value" => "Full-time - 30 hours or more" }, + "2" => { "value" => "Part-time - Less than 30 hours" }, "3" => { "value" => "In government training into work, such as New Deal" }, "4" => { "value" => "Jobseeker" }, "6" => { "value" => "Not seeking work" }, diff --git a/app/models/form/sales/questions/number_joint_buyers.rb b/app/models/form/sales/questions/number_joint_buyers.rb index bd8a6ef3a..228cebab1 100644 --- a/app/models/form/sales/questions/number_joint_buyers.rb +++ b/app/models/form/sales/questions/number_joint_buyers.rb @@ -4,7 +4,6 @@ class Form::Sales::Questions::NumberJointBuyers < ::Form::Question @id = "jointmore" @check_answer_label = "More than 2 joint buyers" @header = "Are there more than 2 joint buyers of this property?" - @hint_text = "You should still try to answer all questions even if the buyer wasn't interviewed in person" @type = "radio" @answer_options = ANSWER_OPTIONS @question_number = 10 @@ -15,4 +14,12 @@ class Form::Sales::Questions::NumberJointBuyers < ::Form::Question "2" => { "value" => "No" }, "3" => { "value" => "Don’t know" }, }.freeze + + def hint_text + if form.start_year_after_2024? + nil + else + "You should still try to answer all questions even if the buyer wasn't interviewed in person" + end + end end diff --git a/app/models/form/sales/questions/owning_organisation_id.rb b/app/models/form/sales/questions/owning_organisation_id.rb index 47a10993d..a4c700e05 100644 --- a/app/models/form/sales/questions/owning_organisation_id.rb +++ b/app/models/form/sales/questions/owning_organisation_id.rb @@ -14,7 +14,7 @@ class Form::Sales::Questions::OwningOrganisationId < ::Form::Question return answer_opts unless user return answer_opts unless log - if FeatureToggle.sales_managing_organisation_enabled? && !user.support? + unless user.support? if log.owning_organisation_id.present? answer_opts[log.owning_organisation.id] = log.owning_organisation.name end @@ -28,43 +28,36 @@ class Form::Sales::Questions::OwningOrganisationId < ::Form::Question end end - if FeatureToggle.merge_organisations_enabled? - if log.owning_organisation_id.present? - answer_opts[log.owning_organisation.id] = log.owning_organisation.name - end + if log.owning_organisation_id.present? + answer_opts[log.owning_organisation.id] = log.owning_organisation.name + end - recently_absorbed_organisations = user.organisation.absorbed_organisations.merged_during_open_collection_period - if !user.support? && user.organisation.holds_own_stock? - answer_opts[user.organisation.id] = if recently_absorbed_organisations.exists? && user.organisation.available_from.present? - "#{user.organisation.name} (Your organisation, active as of #{user.organisation.available_from.to_fs(:govuk_date)})" - else - "#{user.organisation.name} (Your organisation)" - end - end + recently_absorbed_organisations = user.organisation.absorbed_organisations.merged_during_open_collection_period + if !user.support? && user.organisation.holds_own_stock? + answer_opts[user.organisation.id] = if recently_absorbed_organisations.exists? && user.organisation.available_from.present? + "#{user.organisation.name} (Your organisation, active as of #{user.organisation.available_from.to_fs(:govuk_date)})" + else + "#{user.organisation.name} (Your organisation)" + end + end - if user.support? - Organisation.where(holds_own_stock: true).find_each do |org| - if org.merge_date.present? - answer_opts[org.id] = "#{org.name} (inactive as of #{org.merge_date.to_fs(:govuk_date)})" if org.merge_date >= FormHandler.instance.start_date_of_earliest_open_for_editing_collection_period - elsif org.absorbed_organisations.merged_during_open_collection_period.exists? && org.available_from.present? - answer_opts[org.id] = "#{org.name} (active as of #{org.available_from.to_fs(:govuk_date)})" - else - answer_opts[org.id] = org.name - end - end - else - recently_absorbed_organisations.each do |absorbed_org| - answer_opts[absorbed_org.id] = merged_organisation_label(absorbed_org.name, absorbed_org.merge_date) if absorbed_org.holds_own_stock? + if user.support? + Organisation.where(holds_own_stock: true).find_each do |org| + if org.merge_date.present? + answer_opts[org.id] = "#{org.name} (inactive as of #{org.merge_date.to_fs(:govuk_date)})" if org.merge_date >= FormHandler.instance.start_date_of_earliest_open_for_editing_collection_period + elsif org.absorbed_organisations.merged_during_open_collection_period.exists? && org.available_from.present? + answer_opts[org.id] = "#{org.name} (active as of #{org.available_from.to_fs(:govuk_date)})" + else + answer_opts[org.id] = org.name end end - - answer_opts else - Organisation.select(:id, :name).each_with_object(answer_opts) do |organisation, hsh| - hsh[organisation.id] = organisation.name - hsh + recently_absorbed_organisations.each do |absorbed_org| + answer_opts[absorbed_org.id] = merged_organisation_label(absorbed_org.name, absorbed_org.merge_date) if absorbed_org.holds_own_stock? end end + + answer_opts end def displayed_answer_options(log, user = nil) @@ -82,18 +75,14 @@ class Form::Sales::Questions::OwningOrganisationId < ::Form::Question end def hidden_in_check_answers?(_log, user = nil) - if FeatureToggle.merge_organisations_enabled? - return false if user.support? + return false if user.support? - stock_owners = user.organisation.stock_owners + user.organisation.absorbed_organisations.where(holds_own_stock: true) + stock_owners = user.organisation.stock_owners + user.organisation.absorbed_organisations.where(holds_own_stock: true) - if user.organisation.holds_own_stock? - stock_owners.count.zero? - else - stock_owners.count <= 1 - end + if user.organisation.holds_own_stock? + stock_owners.count.zero? else - !current_user.support? + stock_owners.count <= 1 end end @@ -104,11 +93,7 @@ class Form::Sales::Questions::OwningOrganisationId < ::Form::Question private def selected_answer_option_is_derived?(_log) - if FeatureToggle.merge_organisations_enabled? - true - else - false - end + true end def merged_organisation_label(name, merge_date) diff --git a/app/models/form/sales/questions/person_working_situation.rb b/app/models/form/sales/questions/person_working_situation.rb index d74c38f14..b98351f1c 100644 --- a/app/models/form/sales/questions/person_working_situation.rb +++ b/app/models/form/sales/questions/person_working_situation.rb @@ -16,8 +16,8 @@ class Form::Sales::Questions::PersonWorkingSituation < ::Form::Question end ANSWER_OPTIONS = { - "2" => { "value" => "Part-time - Less than 30 hours" }, "1" => { "value" => "Full-time - 30 hours or more" }, + "2" => { "value" => "Part-time - Less than 30 hours" }, "3" => { "value" => "In government training into work, such as New Deal" }, "4" => { "value" => "Jobseeker" }, "6" => { "value" => "Not seeking work" }, diff --git a/app/models/form/sales/questions/property_number_of_bedrooms.rb b/app/models/form/sales/questions/property_number_of_bedrooms.rb index b37253244..3ab192dec 100644 --- a/app/models/form/sales/questions/property_number_of_bedrooms.rb +++ b/app/models/form/sales/questions/property_number_of_bedrooms.rb @@ -6,7 +6,7 @@ class Form::Sales::Questions::PropertyNumberOfBedrooms < ::Form::Question @header = "How many bedrooms does the property have?" @hint_text = "A bedsit has 1 bedroom" @type = "numeric" - @width = 10 + @width = 2 @min = 1 @max = 9 @step = 1 diff --git a/app/models/form/sales/questions/property_wheelchair_accessible.rb b/app/models/form/sales/questions/property_wheelchair_accessible.rb index 588f350a2..7a7cac0d2 100644 --- a/app/models/form/sales/questions/property_wheelchair_accessible.rb +++ b/app/models/form/sales/questions/property_wheelchair_accessible.rb @@ -2,8 +2,8 @@ class Form::Sales::Questions::PropertyWheelchairAccessible < ::Form::Question def initialize(id, hsh, page) super @id = "wchair" - @check_answer_label = "Property build or adapted to wheelchair-user standards" - @header = "Is the property build or adapted to wheelchair-user standards?" + @check_answer_label = "Property built or adapted to wheelchair-user standards" + @header = "Is the property built or adapted to wheelchair-user standards?" @type = "radio" @answer_options = ANSWER_OPTIONS @question_number = 17 diff --git a/app/models/log.rb b/app/models/log.rb index d99399da1..e15869634 100644 --- a/app/models/log.rb +++ b/app/models/log.rb @@ -179,6 +179,7 @@ class Log < ApplicationRecord def blank_compound_invalid_non_setup_fields! self.ppcodenk = nil if errors.attribute_names.include? :ppostcode_full + self.previous_la_known = nil if errors.attribute_names.include? :prevloc if errors.of_kind?(:uprn, :uprn_error) self.uprn_known = nil diff --git a/app/models/user.rb b/app/models/user.rb index aba948425..0fc21172a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -113,7 +113,7 @@ class User < ApplicationRecord if support? Scheme.all else - Scheme.filter_by_owning_organisation(organisation.absorbed_organisations + [organisation]) + Scheme.filter_by_owning_organisation(organisation.absorbed_organisations + [organisation] + organisation.parent_organisations) end end diff --git a/app/models/validations/household_validations.rb b/app/models/validations/household_validations.rb index cd15d09cf..c7e034e94 100644 --- a/app/models/validations/household_validations.rb +++ b/app/models/validations/household_validations.rb @@ -9,6 +9,20 @@ module Validations::HouseholdValidations end end + PHRASES_INDICATING_HOMELESSNESS = [ + "Homeless", + "Homelessness", + "Temporary accommodation", + "Temp accommodation", + "TA", + "Sleeping rough", + "Rough sleeping", + ].freeze + + PHRASES_INDICATING_HOMELESSNESS_REGEX = Regexp.union( + PHRASES_INDICATING_HOMELESSNESS.map { |phrase| Regexp.new("\\A[^[:alpha:]]*#{phrase}[^[:alpha:]]*\\Z", Regexp::IGNORECASE) }, + ) + def validate_reason_for_leaving_last_settled_home(record) if record.reason == 32 && record.underoccupation_benefitcap != 4 record.errors.add :underoccupation_benefitcap, I18n.t("validations.household.underoccupation_benefitcap.dont_know_required") @@ -20,6 +34,12 @@ module Validations::HouseholdValidations record.errors.add :referral, I18n.t("validations.household.referral.reason_permanently_decanted") record.errors.add :reason, I18n.t("validations.household.reason.not_internal_transfer") end + + return unless record.form.start_year_after_2024? + + if record.reason == 20 && PHRASES_INDICATING_HOMELESSNESS_REGEX.match?(record.reasonother) + record.errors.add :reason, I18n.t("validations.household.reason.other_not_settled") + end end def validate_armed_forces(record) diff --git a/app/models/validations/sales/soft_validations.rb b/app/models/validations/sales/soft_validations.rb index ae9ee66cc..14c0afae4 100644 --- a/app/models/validations/sales/soft_validations.rb +++ b/app/models/validations/sales/soft_validations.rb @@ -89,9 +89,11 @@ module Validations::Sales::SoftValidations return unless cashdis || !is_type_discount? return unless deposit && value && equity - cash_discount = cashdis || 0 - mortgage_value = mortgage || 0 - mortgage_value + deposit + cash_discount != value * equity / 100 + !within_tolerance?(mortgage_deposit_and_discount_total, value * equity / 100, 1) + end + + def within_tolerance?(expected, actual, tolerance) + (expected - actual).abs <= tolerance end def mortgage_plus_deposit_less_than_discounted_value? diff --git a/app/models/validations/soft_validations.rb b/app/models/validations/soft_validations.rb index 7275e0439..da3e050c5 100644 --- a/app/models/validations/soft_validations.rb +++ b/app/models/validations/soft_validations.rb @@ -150,6 +150,44 @@ module Validations::SoftValidations weekly_supcharg > soft_max && weekly_supcharg <= hard_max end + PHRASES_LIKELY_TO_INDICATE_EXISTING_REASON_CATEGORY = [ + "Decant", + "Decanted", + "Refugee", + "Asylum", + "Ukraine", + "Ukrainian", + "Army", + "Military", + "Domestic Abuse", + "Domestic Violence", + "DA", + "DV", + "Relationship breakdown", + "Overcrowding", + "Overcrowded", + "Too small", + "More space", + "Bigger property", + "Damp", + "Mould", + "Fire", + "Repossession", + "Death", + "Deceased", + "Passed away", + "Prison", + "Hospital", + ].freeze + + PHRASES_LIKELY_TO_INDICATE_EXISTING_REASON_CATEGORY_REGEX = Regexp.union( + PHRASES_LIKELY_TO_INDICATE_EXISTING_REASON_CATEGORY.map { |phrase| Regexp.new("\\b[^[:alpha]]*#{phrase}[^[:alpha:]]*\\b", Regexp::IGNORECASE) }, + ) + + def reasonother_might_be_existing_category? + PHRASES_LIKELY_TO_INDICATE_EXISTING_REASON_CATEGORY_REGEX.match?(reasonother) + end + private def details_known_or_lead_tenant?(tenant_number) diff --git a/app/services/bulk_upload/lettings/year2023/row_parser.rb b/app/services/bulk_upload/lettings/year2023/row_parser.rb index eb5716511..9fda600b5 100644 --- a/app/services/bulk_upload/lettings/year2023/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2023/row_parser.rb @@ -391,6 +391,7 @@ class BulkUpload::Lettings::Year2023::RowParser validate :validate_correct_intermediate_rent_type, on: :after_log, if: proc { renttype == :intermediate } validate :validate_correct_affordable_rent_type, on: :after_log, if: proc { renttype == :affordable } + validate :validate_all_charges_given, on: :after_log, if: proc { is_carehome.zero? } def self.question_for_field(field) QUESTIONS[field] @@ -854,6 +855,20 @@ private end end + def validate_all_charges_given + return if supported_housing? && field_125 == 1 + + { field_128: "basic rent", + field_129: "service charge", + field_130: "personal service charge", + field_131: "support charge", + field_132: "total charge" }.each do |field, charge| + if public_send(field.to_sym).blank? + errors.add(field, I18n.t("validations.financial.charges.missing_charges", question: charge)) + end + end + end + def setup_question?(question) log.form.setup_sections[0].subsections[0].questions.include?(question) end @@ -1192,7 +1207,7 @@ private attributes["supcharg"] = field_131 attributes["tcharge"] = field_132 attributes["chcharge"] = field_127 - attributes["is_carehome"] = field_127.present? ? 1 : 0 + attributes["is_carehome"] = is_carehome attributes["household_charge"] = supported_housing? ? field_125 : nil attributes["hbrentshortfall"] = field_133 attributes["tshortfall_known"] = tshortfall_known @@ -1574,4 +1589,8 @@ private 0 end end + + def is_carehome + field_127.present? ? 1 : 0 + end end diff --git a/app/services/bulk_upload/lettings/year2024/row_parser.rb b/app/services/bulk_upload/lettings/year2024/row_parser.rb index f38e777c7..d907d9b14 100644 --- a/app/services/bulk_upload/lettings/year2024/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2024/row_parser.rb @@ -382,6 +382,7 @@ class BulkUpload::Lettings::Year2024::RowParser validate :validate_uprn_exists_if_any_key_address_fields_are_blank, on: :after_log, unless: -> { supported_housing? } validate :validate_incomplete_soft_validations, on: :after_log + validate :validate_all_charges_given, on: :after_log, if: proc { is_carehome.zero? } def self.question_for_field(field) QUESTIONS[field] @@ -810,6 +811,19 @@ private end end + def validate_all_charges_given + return if supported_housing? && field_125 == 1 + + { field_125: "basic rent", + field_126: "service charge", + field_127: "personal service charge", + field_128: "support charge" }.each do |field, charge| + if public_send(field.to_sym).blank? + errors.add(field, I18n.t("validations.financial.charges.missing_charges", question: charge)) + end + end + end + def setup_question?(question) log.form.setup_sections[0].subsections[0].questions.include?(question) end @@ -1152,7 +1166,7 @@ private attributes["pscharge"] = field_127 attributes["supcharg"] = field_128 attributes["chcharge"] = field_124 - attributes["is_carehome"] = field_124.present? ? 1 : 0 + attributes["is_carehome"] = is_carehome attributes["household_charge"] = supported_housing? ? field_122 : nil attributes["hbrentshortfall"] = field_129 attributes["tshortfall_known"] = tshortfall_known @@ -1479,4 +1493,8 @@ private 12 end + + def is_carehome + field_124.present? ? 1 : 0 + end end diff --git a/app/services/bulk_upload/sales/log_creator.rb b/app/services/bulk_upload/sales/log_creator.rb index 2d0888e4d..9f3a00fc9 100644 --- a/app/services/bulk_upload/sales/log_creator.rb +++ b/app/services/bulk_upload/sales/log_creator.rb @@ -7,6 +7,7 @@ class BulkUpload::Sales::LogCreator end def call + @bulk_upload.update!(noint_fix_status: BulkUpload.noint_fix_statuses[:not_needed]) row_parsers.each do |row_parser| row_parser.valid? diff --git a/app/services/bulk_upload/sales/validator.rb b/app/services/bulk_upload/sales/validator.rb index 91fa50096..32e9f7533 100644 --- a/app/services/bulk_upload/sales/validator.rb +++ b/app/services/bulk_upload/sales/validator.rb @@ -5,6 +5,7 @@ class BulkUpload::Sales::Validator attr_reader :bulk_upload, :path validate :validate_file_not_empty + validate :validate_field_numbers_count validate :validate_max_columns validate :validate_missing_required_headers validate :validate_correct_template @@ -166,6 +167,14 @@ private if csv_parser.missing_required_headers? errors.add :base, I18n.t("activemodel.errors.models.bulk_upload/sales/validator.attributes.base.no_headers", guidance_link: bulk_upload_sales_log_url(id: "guidance", form: { year: bulk_upload.year }, host: ENV["APP_HOST"], anchor: "using-the-bulk-upload-template")) + end + end + + def validate_field_numbers_count + return if halt_validations? + + unless csv_parser.correct_field_count? + errors.add(:base, :wrong_field_numbers_count) halt_validations! end end diff --git a/app/services/bulk_upload/sales/year2023/csv_parser.rb b/app/services/bulk_upload/sales/year2023/csv_parser.rb index 42b0764d9..c6c07e1d5 100644 --- a/app/services/bulk_upload/sales/year2023/csv_parser.rb +++ b/app/services/bulk_upload/sales/year2023/csv_parser.rb @@ -4,6 +4,7 @@ class BulkUpload::Sales::Year2023::CsvParser include CollectionTimeHelper MAX_COLUMNS = 142 + FIELDS = 135 FORM_YEAR = 2023 attr_reader :path @@ -59,6 +60,12 @@ class BulkUpload::Sales::Year2023::CsvParser false end + def correct_field_count? + valid_field_numbers_count = field_numbers.count { |f| f != "field_blank" } + + valid_field_numbers_count == FIELDS + end + private def default_field_numbers diff --git a/app/services/bulk_upload/sales/year2023/row_parser.rb b/app/services/bulk_upload/sales/year2023/row_parser.rb index 93f15395f..a8f322828 100644 --- a/app/services/bulk_upload/sales/year2023/row_parser.rb +++ b/app/services/bulk_upload/sales/year2023/row_parser.rb @@ -455,12 +455,12 @@ class BulkUpload::Sales::Year2023::RowParser validate :validate_owning_org_data_given, on: :after_log validate :validate_owning_org_exists, on: :after_log - validate :validate_owning_org_owns_stock, on: :after_log if FeatureToggle.sales_managing_organisation_enabled? + validate :validate_owning_org_owns_stock, on: :after_log validate :validate_owning_org_permitted, on: :after_log validate :validate_created_by_exists, on: :after_log validate :validate_created_by_related, on: :after_log - validate :validate_managing_org_related, on: :after_log if FeatureToggle.sales_managing_organisation_enabled? + validate :validate_managing_org_related, on: :after_log validate :validate_relevant_collection_window, on: :after_log validate :validate_incomplete_soft_validations, on: :after_log @@ -791,7 +791,7 @@ private attributes["purchid"] = purchaser_code attributes["saledate"] = saledate - attributes["noint"] = 2 if field_28 == 1 + attributes["noint"] = field_28 attributes["age1_known"] = age1_known? attributes["age1"] = field_30 if attributes["age1_known"]&.zero? && field_30&.match(/\A\d{1,3}\z|\AR\z/) diff --git a/app/services/bulk_upload/sales/year2024/csv_parser.rb b/app/services/bulk_upload/sales/year2024/csv_parser.rb index e53f0ec28..2dc9d38a1 100644 --- a/app/services/bulk_upload/sales/year2024/csv_parser.rb +++ b/app/services/bulk_upload/sales/year2024/csv_parser.rb @@ -3,6 +3,7 @@ require "csv" class BulkUpload::Sales::Year2024::CsvParser include CollectionTimeHelper + FIELDS = 131 MAX_COLUMNS = 142 FORM_YEAR = 2024 @@ -59,6 +60,12 @@ class BulkUpload::Sales::Year2024::CsvParser !with_headers? end + def correct_field_count? + valid_field_numbers_count = field_numbers.count { |f| f != "field_blank" } + + valid_field_numbers_count == FIELDS + end + private def default_field_numbers diff --git a/app/services/bulk_upload/sales/year2024/row_parser.rb b/app/services/bulk_upload/sales/year2024/row_parser.rb index 80748c0bc..2c1b6089a 100644 --- a/app/services/bulk_upload/sales/year2024/row_parser.rb +++ b/app/services/bulk_upload/sales/year2024/row_parser.rb @@ -455,12 +455,12 @@ class BulkUpload::Sales::Year2024::RowParser validate :validate_owning_org_data_given, on: :after_log validate :validate_owning_org_exists, on: :after_log - validate :validate_owning_org_owns_stock, on: :after_log if FeatureToggle.sales_managing_organisation_enabled? + validate :validate_owning_org_owns_stock, on: :after_log validate :validate_owning_org_permitted, on: :after_log validate :validate_created_by_exists, on: :after_log validate :validate_created_by_related, on: :after_log - validate :validate_managing_org_related, on: :after_log if FeatureToggle.sales_managing_organisation_enabled? + validate :validate_managing_org_related, on: :after_log validate :validate_relevant_collection_window, on: :after_log validate :validate_incomplete_soft_validations, on: :after_log @@ -732,6 +732,7 @@ private discount: %i[field_116], othtype: %i[field_12], owning_organisation_id: %i[field_1], + managing_organisation_id: [:field_2], created_by: %i[field_3], hhregres: %i[field_72], hhregresstill: %i[field_73], @@ -785,7 +786,7 @@ private attributes["purchid"] = purchaser_code attributes["saledate"] = saledate - attributes["noint"] = 2 if field_17 == 1 + attributes["noint"] = field_17 attributes["age1_known"] = age1_known? attributes["age1"] = field_31 if attributes["age1_known"]&.zero? && field_31&.match(/\A\d{1,3}\z|\AR\z/) @@ -1211,9 +1212,7 @@ private end def managing_organisation - return owning_organisation if created_by&.organisation&.absorbed_organisations&.include?(owning_organisation) - - created_by&.organisation || bulk_upload.user.organisation + Organisation.find_by_id_on_multiple_fields(field_2) end def nationality_group(nationality_value) @@ -1228,8 +1227,8 @@ private if owning_organisation && managing_organisation && !owning_organisation.can_be_managed_by?(organisation: managing_organisation) block_log_creation! - if errors[:field_3].blank? - errors.add(:field_3, "This user belongs to an organisation that does not have a relationship with the owning organisation", category: :setup) + if errors[:field_2].blank? + errors.add(:field_2, "This organisation does not have a relationship with the owning organisation", category: :setup) end end end diff --git a/app/services/csv/lettings_log_csv_service.rb b/app/services/csv/lettings_log_csv_service.rb index e9ebd875a..e67363188 100644 --- a/app/services/csv/lettings_log_csv_service.rb +++ b/app/services/csv/lettings_log_csv_service.rb @@ -296,7 +296,7 @@ module Csv "letting_allocation_unknown" => %w[letting_allocation_none], }.freeze - SUPPORT_ONLY_ATTRIBUTES = %w[net_income_value_check first_time_property_let_as_social_housing postcode_known is_la_inferred totchild totelder totadult net_income_known previous_la_known is_previous_la_inferred age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 wrent wscharge wpschrge wsupchrg wtcharge wtshortfall rent_value_check old_form_id old_id retirement_value_check tshortfall_known pregnancy_value_check hhtype new_old la prevloc updated_by_id bulk_upload_id uprn_confirmed].freeze + SUPPORT_ONLY_ATTRIBUTES = %w[net_income_value_check first_time_property_let_as_social_housing postcode_known is_la_inferred totchild totelder totadult net_income_known previous_la_known is_previous_la_inferred age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 wrent wscharge wpschrge wsupchrg wtcharge wtshortfall rent_value_check old_form_id old_id retirement_value_check tshortfall_known pregnancy_value_check hhtype new_old la prevloc updated_by_id bulk_upload_id uprn_confirmed reasonother_value_check].freeze def lettings_log_attributes ordered_questions = FormHandler.instance.ordered_lettings_questions_for_all_years diff --git a/app/services/feature_toggle.rb b/app/services/feature_toggle.rb index 38c1372e1..e00e89b90 100644 --- a/app/services/feature_toggle.rb +++ b/app/services/feature_toggle.rb @@ -28,10 +28,6 @@ class FeatureToggle false end - def self.merge_organisations_enabled? - true - end - def self.deduplication_flow_enabled? true end @@ -47,8 +43,4 @@ class FeatureToggle def self.service_moved? false end - - def self.sales_managing_organisation_enabled? - true - end end diff --git a/app/views/logs/_log_filters.html.erb b/app/views/logs/_log_filters.html.erb index 16053c805..ea7496bdd 100644 --- a/app/views/logs/_log_filters.html.erb +++ b/app/views/logs/_log_filters.html.erb @@ -93,7 +93,7 @@ } %> <% end %> - <% if (current_user.support? || non_support_with_managing_orgs?) && (user_or_org_lettings_path? || FeatureToggle.sales_managing_organisation_enabled?) %> + <% if current_user.support? || non_support_with_managing_orgs? %> <%= render partial: "filters/radio_filter", locals: { f:, options: { diff --git a/app/views/organisations/show.html.erb b/app/views/organisations/show.html.erb index 21f722920..e2e07e28c 100644 --- a/app/views/organisations/show.html.erb +++ b/app/views/organisations/show.html.erb @@ -36,9 +36,7 @@ <% end %> <%= data_sharing_agreement_row(organisation: @organisation, user: current_user, summary_list:) %> <% end %> - <% if FeatureToggle.merge_organisations_enabled? %> -

To report a merge or update your organisation details, <%= govuk_link_to "contact the helpdesk", GlobalConstants::HELPDESK_URL %>.

- <% end %> +

To report a merge or update your organisation details, <%= govuk_link_to "contact the helpdesk", GlobalConstants::HELPDESK_URL %>.

<%= render partial: "organisations/merged_organisation_details" %>
diff --git a/config/environments/development.rb b/config/environments/development.rb index 6261c29c8..7e1890b02 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -82,7 +82,7 @@ Rails.application.configure do # config.action_cable.disable_request_forgery_protection = true # see https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017 - config.active_record.yaml_column_permitted_classes = [Time] + config.active_record.yaml_column_permitted_classes = [Time, BigDecimal] config.active_job.queue_adapter = :inline end diff --git a/config/environments/production.rb b/config/environments/production.rb index 8e2a817ef..55f442b3d 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -130,7 +130,7 @@ Rails.application.configure do # config.active_record.shard_resolver = ->(request) { Tenant.find_by!(host: request.host).shard } # see https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017 - config.active_record.yaml_column_permitted_classes = [Time] + config.active_record.yaml_column_permitted_classes = [Time, BigDecimal] # From https://github.com/paper-trail-gem/paper_trail/wiki/Setting-whodunnit-in-the-rails-console console do diff --git a/config/environments/review.rb b/config/environments/review.rb index ec4c67726..f7438fdb6 100644 --- a/config/environments/review.rb +++ b/config/environments/review.rb @@ -126,5 +126,5 @@ Rails.application.configure do # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session # see https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017 - config.active_record.yaml_column_permitted_classes = [Time] + config.active_record.yaml_column_permitted_classes = [Time, BigDecimal] end diff --git a/config/environments/staging.rb b/config/environments/staging.rb index 27b4a5c50..56352d3bb 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -126,5 +126,5 @@ Rails.application.configure do # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session # see https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017 - config.active_record.yaml_column_permitted_classes = [Time] + config.active_record.yaml_column_permitted_classes = [Time, BigDecimal] end diff --git a/config/environments/test.rb b/config/environments/test.rb index 1c399b19d..aa3fe7bba 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -64,7 +64,7 @@ Rails.application.configure do Faker::Config.locale = "en-GB" # see https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017 - config.active_record.yaml_column_permitted_classes = [Time] + config.active_record.yaml_column_permitted_classes = [Time, BigDecimal] config.active_job.queue_adapter = :test end diff --git a/config/locales/en.yml b/config/locales/en.yml index 90962e08d..9746ba13f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -418,6 +418,7 @@ en: above_hard_max: "Rent is higher than the absolute maximum expected for a property of this type based on this period" charges: complete_1_of_3: "Answer either the ‘household rent and charges’ question or ‘is this accommodation a care home‘, or select ‘no’ for ‘does the household pay rent or charges for the accommodation?’" + missing_charges: "Please enter the %{question}. If there is no %{question}, please enter '0'." tcharge: under_10: "Enter a total charge that is at least £10.00 per week" rent_period: @@ -543,6 +544,7 @@ en: reason: not_internal_transfer: "Answer cannot be ‘permanently decanted from another property owned by this landlord’ as you told us the source of referral for this tenancy was not an internal transfer" renewal_reason_needed: 'The reason for leaving must be "End of assured shorthold tenancy - no fault" or "End of fixed term tenancy - no fault" if the letting is a renewal' + other_not_settled: "Please give the reason for the tenant leaving their last settled home. This is where they were living before they became homeless, were living in temporary accommodation or sleeping rough" condition_effects: no_choices: "You cannot answer this question as you told us nobody in the household has a physical or mental health condition (or other illness) expected to last 12 months or more" postcode: @@ -740,6 +742,11 @@ Make sure these answers are correct." deposit_and_mortgage: title_text: "You told us the mortgage amount was %{mortgage}, the cash deposit was %{deposit} and the discount was %{discount}." hint_text: "We would expect the mortgage amount and the deposit added together to be the same as the purchase price minus the discount." + 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." devise: email: diff --git a/db/migrate/20240209153215_add_reasonother_value_check_to_lettings_logs.rb b/db/migrate/20240209153215_add_reasonother_value_check_to_lettings_logs.rb new file mode 100644 index 000000000..54486b3ae --- /dev/null +++ b/db/migrate/20240209153215_add_reasonother_value_check_to_lettings_logs.rb @@ -0,0 +1,5 @@ +class AddReasonotherValueCheckToLettingsLogs < ActiveRecord::Migration[7.0] + def change + add_column :lettings_logs, :reasonother_value_check, :integer + end +end diff --git a/db/migrate/20240216163519_add_no_int_fix_marker_to_bulk_uploads.rb b/db/migrate/20240216163519_add_no_int_fix_marker_to_bulk_uploads.rb new file mode 100644 index 000000000..745cc1fc9 --- /dev/null +++ b/db/migrate/20240216163519_add_no_int_fix_marker_to_bulk_uploads.rb @@ -0,0 +1,5 @@ +class AddNoIntFixMarkerToBulkUploads < ActiveRecord::Migration[7.0] + def change + add_column :bulk_uploads, :noint_fix_status, :string, default: "not_applied" + end +end diff --git a/db/schema.rb b/db/schema.rb index 658e82190..b5b9c9fc0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2024_01_30_084707) do +ActiveRecord::Schema[7.0].define(version: 2024_02_16_163519) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -41,6 +41,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_01_30_084707) do t.integer "needstype" t.text "choice" t.integer "total_logs_count" + t.string "noint_fix_status", default: "not_applied" t.index ["identifier"], name: "index_bulk_uploads_on_identifier", unique: true t.index ["user_id"], name: "index_bulk_uploads_on_user_id" end @@ -305,6 +306,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_01_30_084707) do t.integer "duplicate_set_id" t.integer "nationality_all" t.integer "nationality_all_group" + t.integer "reasonother_value_check" t.integer "accessible_register" 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" diff --git a/lib/tasks/correct_noint_value.rake b/lib/tasks/correct_noint_value.rake new file mode 100644 index 000000000..354daa8c1 --- /dev/null +++ b/lib/tasks/correct_noint_value.rake @@ -0,0 +1,24 @@ +desc "Alter noint values for bulk uploaded sales logs where these have not been set in the service" +task correct_noint_value: :environment do + update_counts = { + in_progress: 0, + completed: 0, + pending: 0, + deleted: 0, + } + affected_uploads = BulkUpload.where(log_type: "sales", noint_fix_status: BulkUpload.noint_fix_statuses[:not_applied]) + affected_uploads.each do |upload| + upload.logs.where(noint: 2).each do |log| + noint_at_upload = log.versions.length == 1 ? log.noint : log.versions.first.next.reify.noint + next unless noint_at_upload == 2 + + Rails.logger.info("Updating noint value on log #{log.id}, owning org #{log.owning_organisation_id}") + update_counts[log.status.to_sym] += 1 + log.noint = 1 + log.skip_update_status = true + log.save! + end + upload.update!(noint_fix_status: BulkUpload.noint_fix_statuses[:applied]) + end + Rails.logger.info("Logs updated; #{update_counts}") +end diff --git a/package.json b/package.json index 5b34103fe..dedf7b6b7 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "css-loader": "^6.7.1", "custom-event-polyfill": "^1.0.7", "file-loader": "^6.2.0", - "govuk-frontend": "5.0.0", + "govuk-frontend": "5.1.0", "html5shiv": "^3.7.3", "intersection-observer": "^0.12.0", "mini-css-extract-plugin": "^2.6.0", diff --git a/spec/factories/bulk_upload.rb b/spec/factories/bulk_upload.rb index 90a56a26b..6ac1a0c91 100644 --- a/spec/factories/bulk_upload.rb +++ b/spec/factories/bulk_upload.rb @@ -8,6 +8,7 @@ FactoryBot.define do identifier { SecureRandom.uuid } sequence(:filename) { |n| "bulk-upload-#{n}.csv" } needstype { 1 } + noint_fix_status { BulkUpload.noint_fix_statuses.values.sample } trait(:sales) do log_type { BulkUpload.log_types[:sales] } diff --git a/spec/factories/lettings_log.rb b/spec/factories/lettings_log.rb index 23ba06bf1..6f4cf951c 100644 --- a/spec/factories/lettings_log.rb +++ b/spec/factories/lettings_log.rb @@ -10,6 +10,7 @@ FactoryBot.define do renewal { 0 } needstype { 1 } rent_type { 1 } + declaration { 1 } end trait :in_progress do status { 1 } diff --git a/spec/features/organisation_spec.rb b/spec/features/organisation_spec.rb index 43c0a9d8e..598be9e05 100644 --- a/spec/features/organisation_spec.rb +++ b/spec/features/organisation_spec.rb @@ -196,9 +196,9 @@ RSpec.describe "User Features" do end it "can filter lettings logs by year" do - check("years-2021-field") + check("years-2022-field") click_button("Apply filters") - expect(page).to have_current_path("/organisations/#{org_id}/lettings-logs?years[]=&years[]=2021&status[]=&needstypes[]=&assigned_to=all&user=&owning_organisation_select=all&owning_organisation=&managing_organisation_select=all&managing_organisation=") + expect(page).to have_current_path("/organisations/#{org_id}/lettings-logs?years[]=&years[]=2022&status[]=&needstypes[]=&assigned_to=all&user=&owning_organisation_select=all&owning_organisation=&managing_organisation_select=all&managing_organisation=") expect(page).not_to have_link first_log.id.to_s, href: "/lettings-logs/#{first_log.id}" end @@ -241,9 +241,9 @@ RSpec.describe "User Features" do organisation.sales_logs.map(&:id).each do |sales_log_id| expect(page).to have_link sales_log_id.to_s, href: "/sales-logs/#{sales_log_id}" end - check("years-2021-field") + check("years-2022-field") click_button("Apply filters") - expect(page).to have_current_path("/organisations/#{org_id}/sales-logs?years[]=&years[]=2021&status[]=&assigned_to=all&user=&owning_organisation_select=all&owning_organisation=&managing_organisation_select=all&managing_organisation=") + expect(page).to have_current_path("/organisations/#{org_id}/sales-logs?years[]=&years[]=2022&status[]=&assigned_to=all&user=&owning_organisation_select=all&owning_organisation=&managing_organisation_select=all&managing_organisation=") expect(page).not_to have_link first_log.id.to_s, href: "/sales-logs/#{first_log.id}" end end @@ -327,6 +327,19 @@ RSpec.describe "User Features" do end end + context "when viewing schemes for specific organisation" do + before do + create(:scheme, owning_organisation: organisation) + visit("/organisations/#{org_id}/schemes") + end + + it "allows downloading schemes csv for the specific org" do + click_link("Download schemes (CSV)") + click_button("Send email") + expect(page).to have_current_path("/organisations/#{org_id}/schemes/csv-confirmation") + end + end + context "and the organisation does not hold housing stock" do before do organisation.update!(holds_own_stock: false) diff --git a/spec/fixtures/files/lettings_log_csv_export_codes_23.csv b/spec/fixtures/files/lettings_log_csv_export_codes_23.csv index 873c703b0..086f250e0 100644 --- a/spec/fixtures/files/lettings_log_csv_export_codes_23.csv +++ b/spec/fixtures/files/lettings_log_csv_export_codes_23.csv @@ -1,2 +1,2 @@ -id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,postcode_known,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,nationality_all,national,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate -,completed,,s.port@jeemayle.com,false,2023-11-26T00:00:00+00:00,,2023-11-26T00:00:00+00:00,1,,,2023,DLUHC,DLUHC,1,7,0,2023-11-26,2,2,1,,2,HIJKLMN,ABCDEFG,1,0,,,fake address,,London,,NW9 5LL,false,Barnet,E09000003,0,2,6,2,2,7,1,1,3,2023-11-24,,,1,2023-11-25,,3,1,4,,2,,1,4,,1,4,0,0,2,35,,F,0,2,,13,0,0,P,32,M,6,1,R,-9,R,10,0,R,-9,R,10,,,,,,,,,,,,,,,,,,,,,1,4,1,2,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,7,4,,6,2,1,0,TN23 6LZ,1,false,Ashford,E07000105,1,0,1,0,0,0,0,0,1,,2,,0,0,268,1,,6,1,1,,0,2,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,1,0,12.0,6.0,,,,,,,,,,,,,,,,,,,, +id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,postcode_known,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,nationality_all,national,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,reasonother_value_check,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate +,completed,,s.port@jeemayle.com,false,2023-11-26T00:00:00+00:00,,2023-11-26T00:00:00+00:00,1,,,2023,DLUHC,DLUHC,1,7,0,2023-11-26,2,2,1,,2,HIJKLMN,ABCDEFG,1,0,,,fake address,,London,,NW9 5LL,false,Barnet,E09000003,0,2,6,2,2,7,1,1,3,2023-11-24,,,1,2023-11-25,,3,1,4,,2,,1,4,,1,4,0,0,2,35,,F,0,2,,13,0,0,P,32,M,6,1,R,-9,R,10,0,R,-9,R,10,,,,,,,,,,,,,,,,,,,,,1,4,1,2,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,7,4,,,6,2,1,0,TN23 6LZ,1,false,Ashford,E07000105,1,0,1,0,0,0,0,0,1,,2,,0,0,268,1,,6,1,1,,0,2,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,1,0,12.0,6.0,,,,,,,,,,,,,,,,,,,, diff --git a/spec/fixtures/files/lettings_log_csv_export_codes_24.csv b/spec/fixtures/files/lettings_log_csv_export_codes_24.csv index 832ceed45..e752f4237 100644 --- a/spec/fixtures/files/lettings_log_csv_export_codes_24.csv +++ b/spec/fixtures/files/lettings_log_csv_export_codes_24.csv @@ -1,2 +1,2 @@ -id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,declaration,postcode_known,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,accessible_register,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate -,completed,,s.port@jeemayle.com,false,2023-11-26T00:00:00+00:00,,2024-04-01T00:00:00+01:00,1,,,2023,DLUHC,DLUHC,1,7,0,2023-11-26,2,2,1,,2,HIJKLMN,ABCDEFG,1,1,0,,,fake address,,London,,NW9 5LL,false,Barnet,E09000003,0,2,6,2,2,7,1,1,3,2023-11-24,,,1,2023-11-25,,3,1,4,,2,,4,,1,4,0,0,2,35,,F,0,2,13,,0,0,P,32,M,6,1,R,-9,R,10,0,R,-9,R,10,,,,,,,,,,,,,,,,,,,,,1,4,1,2,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,7,4,,6,2,1,0,TN23 6LZ,1,false,Ashford,E07000105,1,0,1,0,0,0,0,0,1,0,,2,,0,0,268,1,,6,1,1,,0,2,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,1,0,12.0,6.0,,,,,,,,,,,,,,,,,,,, +id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,declaration,postcode_known,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,reasonother_value_check,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,accessible_register,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate +,completed,,s.port@jeemayle.com,false,2023-11-26T00:00:00+00:00,,2024-04-01T00:00:00+01:00,1,,,2023,DLUHC,DLUHC,1,7,0,2023-11-26,2,2,1,,2,HIJKLMN,ABCDEFG,1,1,0,,,fake address,,London,,NW9 5LL,false,Barnet,E09000003,0,2,6,2,2,7,1,1,3,2023-11-24,,,1,2023-11-25,,3,1,4,,2,,4,,1,4,0,0,2,35,,F,0,2,13,,0,0,P,32,M,6,1,R,-9,R,10,0,R,-9,R,10,,,,,,,,,,,,,,,,,,,,,1,4,1,2,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,7,4,,,6,2,1,0,TN23 6LZ,1,false,Ashford,E07000105,1,0,1,0,0,0,0,0,1,0,,2,,0,0,268,1,,6,1,1,,0,2,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,1,0,12.0,6.0,,,,,,,,,,,,,,,,,,,, diff --git a/spec/fixtures/files/lettings_log_csv_export_labels_23.csv b/spec/fixtures/files/lettings_log_csv_export_labels_23.csv index a9c491ed9..4f2c43d5d 100644 --- a/spec/fixtures/files/lettings_log_csv_export_labels_23.csv +++ b/spec/fixtures/files/lettings_log_csv_export_labels_23.csv @@ -1,2 +1,2 @@ -id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,postcode_known,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,nationality_all,national,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate -,completed,,s.port@jeemayle.com,false,2023-11-26T00:00:00+00:00,,2023-11-26T00:00:00+00:00,single log,,,2023,DLUHC,DLUHC,General needs,Affordable rent general needs local authority,No,2023-11-26,Affordable Rent,Affordable Rent,Rent to Buy,,No,HIJKLMN,ABCDEFG,Yes,No,,,fake address,,London,,NW9 5LL,No,Barnet,E09000003,No,Affordable rent basis,Tenant abandoned property,No,2,House,Purpose built,Yes,3,2023-11-24,,,Yes,2023-11-25,,Don’t know,Yes,Assured Shorthold Tenancy (AST) – Fixed term,,2,,Yes,4,,Yes,4,0,0,2,35,,Female,White,Irish,,Tenant prefers not to say,Other,Yes,Partner,32,Male,Not seeking work,No,Prefers not to say,Not known,Prefers not to say,Prefers not to say,Yes,Person prefers not to say,Not known,Person prefers not to say,Person prefers not to say,,,,,,,,,,,,,,,,,,,,,Yes – the person is a current or former regular,No – they left up to and including 5 years ago,Yes,No,Yes,Fully wheelchair accessible housing,Yes,No,No,No,No,No,No,Yes,No,No,Yes,No,No,No,No,No,No,No,Less than 1 year,1 year but under 2 years,Loss of tied accommodation,,Other supported housing,2,No,Yes,TN23 6LZ,Yes,No,Ashford,E07000105,Yes,,Yes,,,,No,No,Yes,,Tenant applied directly (no referral or nomination),,Yes,No,268,Weekly,,Universal Credit housing element,Yes,All,,No,Every 2 weeks,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,Yes,Yes,12.0,6.0,,,,,,,,,,,,,,,,,,,, +id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,postcode_known,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,nationality_all,national,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,reasonother_value_check,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate +,completed,,s.port@jeemayle.com,false,2023-11-26T00:00:00+00:00,,2023-11-26T00:00:00+00:00,single log,,,2023,DLUHC,DLUHC,General needs,Affordable rent general needs local authority,No,2023-11-26,Affordable Rent,Affordable Rent,Rent to Buy,,No,HIJKLMN,ABCDEFG,Yes,No,,,fake address,,London,,NW9 5LL,No,Barnet,E09000003,No,Affordable rent basis,Tenant abandoned property,No,2,House,Purpose built,Yes,3,2023-11-24,,,Yes,2023-11-25,,Don’t know,Yes,Assured Shorthold Tenancy (AST) – Fixed term,,2,,Yes,4,,Yes,4,0,0,2,35,,Female,White,Irish,,Tenant prefers not to say,Other,Yes,Partner,32,Male,Not seeking work,No,Prefers not to say,Not known,Prefers not to say,Prefers not to say,Yes,Person prefers not to say,Not known,Person prefers not to say,Person prefers not to say,,,,,,,,,,,,,,,,,,,,,Yes – the person is a current or former regular,No – they left up to and including 5 years ago,Yes,No,Yes,Fully wheelchair accessible housing,Yes,No,No,No,No,No,No,Yes,No,No,Yes,No,No,No,No,No,No,No,Less than 1 year,1 year but under 2 years,Loss of tied accommodation,,,Other supported housing,2,No,Yes,TN23 6LZ,Yes,No,Ashford,E07000105,Yes,,Yes,,,,No,No,Yes,,Tenant applied directly (no referral or nomination),,Yes,No,268,Weekly,,Universal Credit housing element,Yes,All,,No,Every 2 weeks,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,Yes,Yes,12.0,6.0,,,,,,,,,,,,,,,,,,,, diff --git a/spec/fixtures/files/lettings_log_csv_export_labels_24.csv b/spec/fixtures/files/lettings_log_csv_export_labels_24.csv index 0e345b837..2d15ea4b3 100644 --- a/spec/fixtures/files/lettings_log_csv_export_labels_24.csv +++ b/spec/fixtures/files/lettings_log_csv_export_labels_24.csv @@ -1,2 +1,2 @@ -id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,declaration,postcode_known,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,accessible_register,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate -,completed,,s.port@jeemayle.com,false,2023-11-26T00:00:00+00:00,,2024-04-01T00:00:00+01:00,single log,,,2023,DLUHC,DLUHC,General needs,Affordable rent general needs local authority,No,2023-11-26,Affordable Rent,Affordable Rent,Rent to Buy,,No,HIJKLMN,ABCDEFG,Yes,Yes,No,,,fake address,,London,,NW9 5LL,No,Barnet,E09000003,No,Affordable rent basis,Tenant abandoned property,No,2,House,Purpose built,Yes,3,2023-11-24,,,Yes,2023-11-25,,Don’t know,Yes,Assured Shorthold Tenancy (AST) – Fixed term,,2,,4,,Yes,4,0,0,2,35,,Female,White,Irish,Tenant prefers not to say,,Other,Yes,Partner,32,Male,Not seeking work,No,Prefers not to say,Not known,Prefers not to say,Prefers not to say,Yes,Person prefers not to say,Not known,Person prefers not to say,Person prefers not to say,,,,,,,,,,,,,,,,,,,,,Yes – the person is a current or former regular,No – they left up to and including 5 years ago,Yes,No,Yes,Fully wheelchair accessible housing,Yes,No,No,No,No,No,No,Yes,No,No,Yes,No,No,No,No,No,No,No,Less than 1 year,1 year but under 2 years,Loss of tied accommodation,,Other supported housing,2,No,Yes,TN23 6LZ,Yes,No,Ashford,E07000105,Yes,,Yes,,,,No,No,Yes,No,,Tenant applied directly (no referral or nomination),,Yes,No,268,Weekly,,Universal Credit housing element,Yes,All,,No,Every 2 weeks,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,Yes,Yes,12.0,6.0,,,,,,,,,,,,,,,,,,,, +id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,declaration,postcode_known,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,reasonother_value_check,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,accessible_register,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate +,completed,,s.port@jeemayle.com,false,2023-11-26T00:00:00+00:00,,2024-04-01T00:00:00+01:00,single log,,,2023,DLUHC,DLUHC,General needs,Affordable rent general needs local authority,No,2023-11-26,Affordable Rent,Affordable Rent,Rent to Buy,,No,HIJKLMN,ABCDEFG,Yes,Yes,No,,,fake address,,London,,NW9 5LL,No,Barnet,E09000003,No,Affordable rent basis,Tenant abandoned property,No,2,House,Purpose built,Yes,3,2023-11-24,,,Yes,2023-11-25,,Don’t know,Yes,Assured Shorthold Tenancy (AST) – Fixed term,,2,,4,,Yes,4,0,0,2,35,,Female,White,Irish,Tenant prefers not to say,,Other,Yes,Partner,32,Male,Not seeking work,No,Prefers not to say,Not known,Prefers not to say,Prefers not to say,Yes,Person prefers not to say,Not known,Person prefers not to say,Person prefers not to say,,,,,,,,,,,,,,,,,,,,,Yes – the person is a current or former regular,No – they left up to and including 5 years ago,Yes,No,Yes,Fully wheelchair accessible housing,Yes,No,No,No,No,No,No,Yes,No,No,Yes,No,No,No,No,No,No,No,Less than 1 year,1 year but under 2 years,Loss of tied accommodation,,,Other supported housing,2,No,Yes,TN23 6LZ,Yes,No,Ashford,E07000105,Yes,,Yes,,,,No,No,Yes,No,,Tenant applied directly (no referral or nomination),,Yes,No,268,Weekly,,Universal Credit housing element,Yes,All,,No,Every 2 weeks,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,Yes,Yes,12.0,6.0,,,,,,,,,,,,,,,,,,,, diff --git a/spec/helpers/filters_helper_spec.rb b/spec/helpers/filters_helper_spec.rb index f60cc483e..52a9595fc 100644 --- a/spec/helpers/filters_helper_spec.rb +++ b/spec/helpers/filters_helper_spec.rb @@ -240,12 +240,38 @@ RSpec.describe FiltersHelper do end describe "#collection_year_options" do - it "includes 2023/2024 option" do - expect(collection_year_options).to eq( - { - "2021": "2021/22", "2022": "2022/23", "2023": "2023/24" - }, - ) + context "with 23/24 as the current collection year" do + around do |example| + Timecop.freeze(Time.zone.local(2023, 5, 1)) do + example.run + end + Timecop.return + end + + it "has the correct options" do + expect(collection_year_options).to eq( + { + "2023" => "2023/24", "2022" => "2022/23", "2021" => "2021/22" + }, + ) + end + end + + context "with 24/25 as the current collection year" do + around do |example| + Timecop.freeze(Time.zone.local(2024, 5, 1)) do + example.run + end + Timecop.return + end + + it "has the correct options" do + expect(collection_year_options).to eq( + { + "2024" => "2024/25", "2023" => "2023/24", "2022" => "2022/23" + }, + ) + end end end diff --git a/spec/jobs/scheme_email_csv_job_spec.rb b/spec/jobs/scheme_email_csv_job_spec.rb index d23177ab3..5ddaa91a6 100644 --- a/spec/jobs/scheme_email_csv_job_spec.rb +++ b/spec/jobs/scheme_email_csv_job_spec.rb @@ -6,41 +6,52 @@ describe SchemeEmailCsvJob do test_url = :test_url let(:job) { described_class.new } + let(:storage_service) { instance_double(Storage::S3Service, write_file: nil, get_presigned_url: test_url) } + let(:mailer) { instance_double(CsvDownloadMailer, send_csv_download_mail: nil) } let(:user) { FactoryBot.create(:user) } - let(:storage_service) { instance_double(Storage::S3Service) } - let(:mailer) { instance_double(CsvDownloadMailer) } - let(:scheme_csv_service) { instance_double(Csv::SchemeCsvService) } - let(:search_term) { "meaning" } - let(:filters) { { "owning_organisation" => organisation.id, "status" => %w[active] } } - let(:all_orgs) { false } - let(:organisation) { build(:organisation) } - let(:download_type) { "combined" } - let(:schemes) { build_list(:scheme, 5, owning_organisation: organisation) } - let(:locations) { build_list(:location, 5, scheme: schemes.first) } before do allow(Storage::S3Service).to receive(:new).and_return(storage_service) - allow(storage_service).to receive(:write_file) - allow(storage_service).to receive(:get_presigned_url).and_return(test_url) - - allow(Csv::SchemeCsvService).to receive(:new).and_return(scheme_csv_service) - allow(scheme_csv_service).to receive(:prepare_csv).and_return("") - allow(CsvDownloadMailer).to receive(:new).and_return(mailer) - allow(mailer).to receive(:send_csv_download_mail) end context "when exporting" do + let(:scheme_csv_service) { instance_double(Csv::SchemeCsvService) } + let(:organisation) { user.organisation } + let(:download_type) { "combined" } + let(:schemes) { create_list(:scheme, 1, owning_organisation: organisation) } + before do - allow(FilterManager).to receive(:filter_schemes).and_return(schemes) + create_list(:location, 2, scheme: schemes.first) end context "when download type schemes" do let(:download_type) { "schemes" } - it "uses an appropriate filename in S3" do - expect(storage_service).to receive(:write_file).with(/schemes-.*\.csv/, anything) - job.perform(user) + it "uses an appropriate filename in S3 and exports the correct schemes" do + expect(storage_service).to receive(:write_file).with(/schemes-.*\.csv/, anything) do |_, content| + expect(content).not_to be_nil + expect(content).not_to be_nil + expect(CSV.parse(content).count).to eq(2) + end + job.perform(user, nil, {}, nil, nil, download_type) + end + + context "and there are stock owner schemes" do + let(:parent_organisation) { create(:organisation) } + + before do + create(:scheme, owning_organisation: parent_organisation) + create(:organisation_relationship, parent_organisation:, child_organisation: organisation) + end + + it "exports the correct number of schemes" do + expect(storage_service).to receive(:write_file).with(/schemes-.*\.csv/, anything) do |_, content| + expect(content).not_to be_nil + expect(CSV.parse(content).count).to eq(3) + end + job.perform(user, nil, {}, nil, nil, download_type) + end end end @@ -49,7 +60,7 @@ describe SchemeEmailCsvJob do it "uses an appropriate filename in S3" do expect(storage_service).to receive(:write_file).with(/locations-.*\.csv/, anything) - job.perform(user) + job.perform(user, nil, {}, nil, nil, download_type) end end @@ -58,7 +69,7 @@ describe SchemeEmailCsvJob do it "uses an appropriate filename in S3" do expect(storage_service).to receive(:write_file).with(/schemes-and-locations.*\.csv/, anything) - job.perform(user) + job.perform(user, nil, {}, nil, nil, download_type) end end @@ -67,12 +78,27 @@ describe SchemeEmailCsvJob do job.perform(user, nil, {}, nil, organisation, "combined") end - it "calls the filter manager with the arguments provided" do - expect(FilterManager).to receive(:filter_schemes).with(a_kind_of(ActiveRecord::Relation), search_term, filters, all_orgs, user) - job.perform(user, search_term, filters, all_orgs, organisation, "combined") + context "when resources are filtered" do + let(:search_term) { "meaning" } + let(:filters) { { "owning_organisation" => organisation.id, "status" => %w[active] } } + let(:all_orgs) { false } + + before do + allow(Csv::SchemeCsvService).to receive(:new).and_return(scheme_csv_service) + allow(scheme_csv_service).to receive(:prepare_csv).and_return("") + allow(FilterManager).to receive(:filter_schemes).and_return(schemes) + end + + it "calls the filter manager with the arguments provided" do + expect(FilterManager).to receive(:filter_schemes).with(a_kind_of(ActiveRecord::Relation), search_term, filters, all_orgs, user) + job.perform(user, search_term, filters, all_orgs, organisation, "combined") + end end it "creates a SchemeCsvService with the correct download type" do + allow(Csv::SchemeCsvService).to receive(:new).and_return(scheme_csv_service) + allow(scheme_csv_service).to receive(:prepare_csv).and_return("") + expect(Csv::SchemeCsvService).to receive(:new).with(download_type: "schemes") job.perform(user, nil, {}, nil, nil, "schemes") expect(Csv::SchemeCsvService).to receive(:new).with(download_type: "locations") @@ -82,6 +108,9 @@ describe SchemeEmailCsvJob do end it "passes the schemes returned by the filter manager to the csv service" do + allow(Csv::SchemeCsvService).to receive(:new).and_return(scheme_csv_service) + allow(scheme_csv_service).to receive(:prepare_csv).and_return("") + expect(scheme_csv_service).to receive(:prepare_csv).with(schemes) job.perform(user, nil, {}, nil, nil, "combined") end diff --git a/spec/lib/tasks/correct_noint_value_spec.rb b/spec/lib/tasks/correct_noint_value_spec.rb new file mode 100644 index 000000000..51dd7779c --- /dev/null +++ b/spec/lib/tasks/correct_noint_value_spec.rb @@ -0,0 +1,87 @@ +require "rails_helper" +require "rake" + +RSpec.describe "correct_noint_value" do + describe ":correct_noint_value", type: :task do + subject(:task) { Rake::Task["correct_noint_value"] } + + before do + Rake.application.rake_require("tasks/correct_noint_value") + Rake::Task.define_task(:environment) + task.reenable + end + + context "when the rake task is run" do + context "and there is a sales bulk upload with the fix needed" do + let(:bulk_upload) { create(:bulk_upload, :sales, noint_fix_status: BulkUpload.noint_fix_statuses[:not_applied]) } + + before do + bulk_upload.save! + end + + it "updates the noint value on a log with noint = 2 where it was set to 2 on create" do + log = create(:sales_log, :completed, noint: 2, bulk_upload:) + + task.invoke + log.reload + + expect(log.noint).to be(1) + end + + it "updates the noint value on a log with noint = 2 where it was set to 2 on create and other fields have since changed" do + log = create(:sales_log, :in_progress, noint: 2, bulk_upload:) + log.update!(status: Log.statuses[:completed]) + + task.invoke + log.reload + + expect(log.noint).to be(1) + end + + it "does not update the noint value on a log that has noint = 1" do + log = create(:sales_log, :completed, noint: 2, bulk_upload:) + log.update!(noint: 1) + + task.invoke + log.reload + + expect(log.noint).to be(1) + end + + it "does not update the noint value on a log with noint = 2 where noint was nil on create" do + log = create(:sales_log, :completed, noint: nil, bulk_upload:) + log.update!(noint: 2) + + task.invoke + log.reload + + expect(log.noint).to be(2) + end + + it "updates the noint_fix_status value on the bulk upload" do + task.invoke + bulk_upload.reload + + expect(bulk_upload.noint_fix_status).to eq(BulkUpload.noint_fix_statuses[:applied]) + end + end + + context "and there is a sales bulk upload with the fix marked as not needed" do + let(:bulk_upload) { create(:bulk_upload, :sales, noint_fix_status: BulkUpload.noint_fix_statuses[:not_needed]) } + + before do + bulk_upload.save! + end + + it "does not update the noint values on logs" do + log = create(:sales_log, :completed, noint: 2, bulk_upload:) + + task.invoke + log.reload + + expect(log.noint).to be(2) + end + end + end + end +end diff --git a/spec/models/form/lettings/subsections/household_situation_spec.rb b/spec/models/form/lettings/subsections/household_situation_spec.rb index 803f873f8..dd6f20d89 100644 --- a/spec/models/form/lettings/subsections/household_situation_spec.rb +++ b/spec/models/form/lettings/subsections/household_situation_spec.rb @@ -6,33 +6,75 @@ RSpec.describe Form::Lettings::Subsections::HouseholdSituation, type: :model do let(:subsection_id) { nil } let(:subsection_definition) { nil } let(:section) { instance_double(Form::Lettings::Sections::Household) } + let(:form) { instance_double(Form) } + + before do + allow(section).to receive(:form).and_return(form) + end it "has correct section" do expect(household_situation.section).to eq(section) end - it "has correct pages" do - expect(household_situation.pages.map(&:id)).to eq( - %w[ - time_lived_in_local_authority - time_on_waiting_list - reason_for_leaving_last_settled_home - reason_for_leaving_last_settled_home_renewal - previous_housing_situation - previous_housing_situation_renewal - homelessness - previous_postcode - previous_local_authority - reasonable_preference - reasonable_preference_reason - allocation_system - referral - referral_prp - referral_supported_housing - referral_supported_housing_prp - referral_value_check - ], - ) + context "with form year before 2024" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(false) + end + + it "has correct pages" do + expect(household_situation.pages.map(&:id)).to eq( + %w[ + time_lived_in_local_authority + time_on_waiting_list + reason_for_leaving_last_settled_home + reason_for_leaving_last_settled_home_renewal + previous_housing_situation + previous_housing_situation_renewal + homelessness + previous_postcode + previous_local_authority + reasonable_preference + reasonable_preference_reason + allocation_system + referral + referral_prp + referral_supported_housing + referral_supported_housing_prp + referral_value_check + ], + ) + end + end + + context "with form year >= 2024" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(true) + end + + it "has correct pages" do + expect(household_situation.pages.map(&:id)).to eq( + %w[ + time_lived_in_local_authority + time_on_waiting_list + reason_for_leaving_last_settled_home + reason_for_leaving_last_settled_home_renewal + reasonother_value_check + previous_housing_situation + previous_housing_situation_renewal + homelessness + previous_postcode + previous_local_authority + reasonable_preference + reasonable_preference_reason + allocation_system + referral + referral_prp + referral_supported_housing + referral_supported_housing_prp + referral_value_check + ], + ) + end end it "has the correct id" do diff --git a/spec/models/form/sales/pages/managing_organisation_spec.rb b/spec/models/form/sales/pages/managing_organisation_spec.rb index 0a34554cb..8c411401d 100644 --- a/spec/models/form/sales/pages/managing_organisation_spec.rb +++ b/spec/models/form/sales/pages/managing_organisation_spec.rb @@ -5,9 +5,13 @@ RSpec.describe Form::Sales::Pages::ManagingOrganisation, type: :model do let(:page_id) { nil } let(:page_definition) { nil } - let(:subsection) { instance_double(Form::Subsection) } + let(:subsection) { instance_double(Form::Subsection, form:) } let(:form) { instance_double(Form) } + before do + allow(form).to receive(:start_year_after_2024?).and_return(false) + end + it "has correct subsection" do expect(page.subsection).to eq(subsection) end @@ -32,8 +36,8 @@ RSpec.describe Form::Sales::Pages::ManagingOrganisation, type: :model do expect(page.depends_on).to be nil end - describe "#routed_to?" do - let(:log) { create(:lettings_log) } + describe "#routed_to? with 2023 logs" do + let(:log) { create(:sales_log) } let(:organisation) { create(:organisation) } context "when user nil" do @@ -54,7 +58,7 @@ RSpec.describe Form::Sales::Pages::ManagingOrganisation, type: :model do let(:user) { create(:user, :support) } context "when owning_organisation not set" do - let(:log) { create(:lettings_log, owning_organisation: nil) } + let(:log) { create(:sales_log, owning_organisation: nil) } it "is not shown" do expect(page.routed_to?(log, user)).to eq(false) @@ -103,4 +107,146 @@ RSpec.describe Form::Sales::Pages::ManagingOrganisation, type: :model do end end end + + describe "#routed_to? with 2024 logs" do + let(:log) { create(:sales_log) } + let(:organisation) { create(:organisation) } + + before do + allow(form).to receive(:start_year_after_2024?).and_return(true) + end + + context "when user nil" do + it "is not shown" do + expect(page.routed_to?(log, nil)).to eq(false) + end + end + + context "when support" do + context "when does not hold own stock" do + let(:user) do + create(:user, :support, organisation: create(:organisation, holds_own_stock: false)) + end + let(:log) { create(:sales_log, owning_organisation: user.organisation) } + + it "is shown" do + expect(page.routed_to?(log, user)).to eq(true) + end + end + + context "when owning_organisation not set" do + let(:user) { create(:user, :support) } + let(:log) { create(:sales_log, owning_organisation: nil) } + + it "is not shown" do + expect(page.routed_to?(log, user)).to eq(false) + end + end + + context "when holds own stock" do + let(:user) do + create(:user, :support, organisation: create(:organisation, holds_own_stock: true)) + end + + context "with 0 managing_agents" do + it "is not shown" do + expect(page.routed_to?(log, user)).to eq(false) + end + end + + context "with >1 managing_agents" do + before do + create(:organisation_relationship, parent_organisation: log.owning_organisation) + create(:organisation_relationship, parent_organisation: log.owning_organisation) + end + + it "is shown" do + expect(page.routed_to?(log, user)).to eq(true) + end + end + + context "with 1 managing_agents" do + let(:managing_agent) { create(:organisation) } + + before do + create( + :organisation_relationship, + child_organisation: managing_agent, + parent_organisation: log.owning_organisation, + ) + end + + it "is shown" do + expect(page.routed_to?(log, user)).to eq(true) + end + end + end + end + + context "when not support" do + context "when does not hold own stock" do + let(:user) { create(:user, :data_coordinator, organisation: create(:organisation, holds_own_stock: false)) } + + context "and the user's organisation is selected as owning organisation" do + let(:log) { create(:sales_log, owning_organisation: user.organisation) } + + it "is shown" do + expect(page.routed_to?(log, user)).to eq(true) + end + end + + context "and a different than the user's organisation is selected as owning organisation" do + let(:stock_owner) { create(:organisation, holds_own_stock: true) } + let(:log) { create(:sales_log, owning_organisation: stock_owner) } + + before do + create(:organisation_relationship, parent_organisation: stock_owner, child_organisation: user.organisation) + end + + it "is not shown" do + expect(page.routed_to?(log, user)).to eq(false) + end + end + end + + context "when holds own stock" do + let(:user) do + create(:user, :data_coordinator, organisation: create(:organisation, holds_own_stock: true)) + end + + context "with 0 managing_agents" do + it "is not shown" do + expect(page.routed_to?(log, user)).to eq(false) + end + end + + context "with >1 managing_agents" do + before do + create(:organisation_relationship, parent_organisation: user.organisation) + create(:organisation_relationship, parent_organisation: user.organisation) + end + + it "is shown" do + expect(page.routed_to?(log, user)).to eq(true) + end + end + + context "with 1 managing_agents" do + let(:managing_agent) { create(:organisation) } + + before do + create( + :organisation_relationship, + child_organisation: managing_agent, + parent_organisation: user.organisation, + ) + end + + it "is shown" do + expect(page.routed_to?(log, user)).to eq(true) + end + end + end + end + end end diff --git a/spec/models/form/sales/questions/age1_spec.rb b/spec/models/form/sales/questions/age1_spec.rb index 33fe91c82..b21277c54 100644 --- a/spec/models/form/sales/questions/age1_spec.rb +++ b/spec/models/form/sales/questions/age1_spec.rb @@ -20,7 +20,7 @@ RSpec.describe Form::Sales::Questions::Age1, type: :model do end it "has the correct check_answer_label" do - expect(question.check_answer_label).to eq("Lead buyer’s age") + expect(question.check_answer_label).to eq("Buyer 1’s age") end it "has the correct type" do diff --git a/spec/models/form/sales/questions/buyer1_age_known_spec.rb b/spec/models/form/sales/questions/buyer1_age_known_spec.rb index 4337d8cb1..92af1bea5 100644 --- a/spec/models/form/sales/questions/buyer1_age_known_spec.rb +++ b/spec/models/form/sales/questions/buyer1_age_known_spec.rb @@ -20,7 +20,7 @@ RSpec.describe Form::Sales::Questions::Buyer1AgeKnown, type: :model do end it "has the correct check_answer_label" do - expect(question.check_answer_label).to eq("Lead buyer’s age") + expect(question.check_answer_label).to eq("Buyer 1’s age") end it "has the correct type" do diff --git a/spec/models/form/sales/questions/buyer1_working_situation_spec.rb b/spec/models/form/sales/questions/buyer1_working_situation_spec.rb index fdcc6ce36..ba7dcf00b 100644 --- a/spec/models/form/sales/questions/buyer1_working_situation_spec.rb +++ b/spec/models/form/sales/questions/buyer1_working_situation_spec.rb @@ -33,8 +33,8 @@ RSpec.describe Form::Sales::Questions::Buyer1WorkingSituation, type: :model do it "has the correct answer_options" do expect(question.answer_options).to eq({ - "2" => { "value" => "Part-time - Less than 30 hours" }, "1" => { "value" => "Full-time - 30 hours or more" }, + "2" => { "value" => "Part-time - Less than 30 hours" }, "3" => { "value" => "In government training into work, such as New Deal" }, "4" => { "value" => "Jobseeker" }, "6" => { "value" => "Not seeking work" }, diff --git a/spec/models/form/sales/questions/buyer2_working_situation_spec.rb b/spec/models/form/sales/questions/buyer2_working_situation_spec.rb index 7905f0df1..8c864e8d7 100644 --- a/spec/models/form/sales/questions/buyer2_working_situation_spec.rb +++ b/spec/models/form/sales/questions/buyer2_working_situation_spec.rb @@ -37,8 +37,8 @@ RSpec.describe Form::Sales::Questions::Buyer2WorkingSituation, type: :model do it "has the correct answer_options" do expect(question.answer_options).to eq({ - "2" => { "value" => "Part-time - Less than 30 hours" }, "1" => { "value" => "Full-time - 30 hours or more" }, + "2" => { "value" => "Part-time - Less than 30 hours" }, "3" => { "value" => "In government training into work, such as New Deal" }, "4" => { "value" => "Jobseeker" }, "6" => { "value" => "Not seeking work" }, diff --git a/spec/models/form/sales/questions/number_joint_buyers_spec.rb b/spec/models/form/sales/questions/number_joint_buyers_spec.rb index a16502f9f..bfad4a796 100644 --- a/spec/models/form/sales/questions/number_joint_buyers_spec.rb +++ b/spec/models/form/sales/questions/number_joint_buyers_spec.rb @@ -6,6 +6,12 @@ RSpec.describe Form::Sales::Questions::NumberJointBuyers, type: :model do let(:question_id) { nil } let(:question_definition) { nil } let(:page) { instance_double(Form::Page) } + let(:subsection) { instance_double(Form::Subsection) } + + before do + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: false)) + end it "has correct page" do expect(question.page).to eq(page) @@ -42,4 +48,14 @@ RSpec.describe Form::Sales::Questions::NumberJointBuyers, type: :model do "3" => { "value" => "Don’t know" }, }) end + + context "with 2024 form" do + before do + allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: true)) + end + + it "has no hint_text" do + expect(question.hint_text).to be_nil + end + end end diff --git a/spec/models/form/sales/questions/person_working_situation_spec.rb b/spec/models/form/sales/questions/person_working_situation_spec.rb index 274d199c9..4c2d1f85d 100644 --- a/spec/models/form/sales/questions/person_working_situation_spec.rb +++ b/spec/models/form/sales/questions/person_working_situation_spec.rb @@ -22,8 +22,8 @@ RSpec.describe Form::Sales::Questions::PersonWorkingSituation, type: :model do it "has the correct answer_options" do expect(question.answer_options).to eq({ - "2" => { "value" => "Part-time - Less than 30 hours" }, "1" => { "value" => "Full-time - 30 hours or more" }, + "2" => { "value" => "Part-time - Less than 30 hours" }, "3" => { "value" => "In government training into work, such as New Deal" }, "4" => { "value" => "Jobseeker" }, "6" => { "value" => "Not seeking work" }, diff --git a/spec/models/form/sales/questions/property_wheelchair_accessible_spec.rb b/spec/models/form/sales/questions/property_wheelchair_accessible_spec.rb index bea482aca..9fd7be4a6 100644 --- a/spec/models/form/sales/questions/property_wheelchair_accessible_spec.rb +++ b/spec/models/form/sales/questions/property_wheelchair_accessible_spec.rb @@ -22,11 +22,11 @@ RSpec.describe Form::Sales::Questions::PropertyWheelchairAccessible, type: :mode end it "has the correct header" do - expect(question.header).to eq("Is the property build or adapted to wheelchair-user standards?") + expect(question.header).to eq("Is the property built or adapted to wheelchair-user standards?") end it "has the correct check_answer_label" do - expect(question.check_answer_label).to eq("Property build or adapted to wheelchair-user standards") + expect(question.check_answer_label).to eq("Property built or adapted to wheelchair-user standards") end it "has the correct type" do diff --git a/spec/models/lettings_log_spec.rb b/spec/models/lettings_log_spec.rb index 5c55d87cb..f27a7f7e5 100644 --- a/spec/models/lettings_log_spec.rb +++ b/spec/models/lettings_log_spec.rb @@ -3382,26 +3382,6 @@ RSpec.describe LettingsLog do end end - describe "#blank_invalid_non_setup_fields!" do - context "when a setup field is invalid" do - subject(:model) { described_class.new(needstype: 404) } - - it "does not blank it" do - model.valid? - expect { model.blank_invalid_non_setup_fields! }.not_to change(model, :needstype) - end - end - - context "when a non setup field is invalid" do - subject(:model) { build(:lettings_log, :completed, offered: 234) } - - it "blanks it" do - model.valid? - expect { model.blank_invalid_non_setup_fields! }.to change(model, :offered) - end - end - end - describe "#beds_for_la_rent_range" do context "when beds nil" do let(:lettings_log) { build(:lettings_log, beds: nil) } diff --git a/spec/models/log_spec.rb b/spec/models/log_spec.rb index fb686e744..3494f152f 100644 --- a/spec/models/log_spec.rb +++ b/spec/models/log_spec.rb @@ -27,4 +27,54 @@ RSpec.describe Log, type: :model do expect(in_progress_lettings_log.calculate_status).to eq "in_progress" end end + + describe "#blank_invalid_non_setup_fields!" do + context "when a setup field is invalid for a lettings log" do + subject(:model) { build(:lettings_log, needstype: 404) } + + it "does not blank it" do + model.valid? + expect { model.blank_invalid_non_setup_fields! }.not_to change(model, :needstype) + end + end + + context "when a setup field is invalid for a sales log" do + subject(:model) { build(:sales_log, companybuy: 404) } + + it "does not blank it" do + model.valid? + expect { model.blank_invalid_non_setup_fields! }.not_to change(model, :companybuy) + end + end + + context "when a non setup field is invalid for a lettings log" do + subject(:model) { build(:lettings_log, :completed, offered: 234) } + + it "blanks it" do + model.valid? + model.blank_invalid_non_setup_fields! + expect(model.offered).to be_nil + end + end + + context "when a non setup field is invalid for a sales log" do + subject(:model) { build(:sales_log, :completed, age1: 10) } + + it "blanks it" do + model.valid? + model.blank_invalid_non_setup_fields! + expect(model.age1).to be_nil + end + end + + context "when prevloc is invalid for a lettings log" do + subject(:model) { build(:lettings_log, :completed, previous_la_known: 1, prevloc: nil) } + + it "blanks previous_la_known" do + model.valid? + model.blank_invalid_non_setup_fields! + expect(model.previous_la_known).to be_nil + end + end + end end diff --git a/spec/models/validations/household_validations_spec.rb b/spec/models/validations/household_validations_spec.rb index 849046c65..5a48d4b3f 100644 --- a/spec/models/validations/household_validations_spec.rb +++ b/spec/models/validations/household_validations_spec.rb @@ -4,11 +4,15 @@ RSpec.describe Validations::HouseholdValidations do subject(:household_validator) { validator_class.new } let(:validator_class) { Class.new { include Validations::HouseholdValidations } } - let(:record) { FactoryBot.create(:lettings_log) } - let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } + let(:log_date) { Time.zone.now } + let(:record) { FactoryBot.create(:lettings_log, :setup_completed, startdate: log_date) } before do - allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) + Timecop.freeze(log_date + 1) + end + + after do + Timecop.return end describe "reasonable preference validations" do @@ -53,6 +57,44 @@ RSpec.describe Validations::HouseholdValidations do household_validator.validate_reason_for_leaving_last_settled_home(record) expect(record.errors["reasonother"]).to be_empty end + + context "when form year is before 2024" do + let(:log_date) { Time.zone.local(2024, 1, 1) } + + it "does not validate the content of reasonother for phrases indicating homelessness" do + record.reason = 20 + record.reasonother = "Temp accommodation" + household_validator.validate_reason_for_leaving_last_settled_home(record) + expect(record.errors["reason"]).to be_empty + end + end + + context "when form year is >= 2024" do + let(:log_date) { Time.zone.local(2024, 4, 1) } + + context "when checking the content of reasonother" do + it "validates that the reason doesn't match phrase indicating homelessness" do + record.reason = 20 + record.reasonother = "Temp accommodation" + household_validator.validate_reason_for_leaving_last_settled_home(record) + expect(record.errors["reason"]).to include(I18n.t("validations.household.reason.other_not_settled")) + end + + it "allows reasons that don't exactly match a phrase indicating homelessness" do + record.reason = 20 + record.reasonother = "Not quite homeless but some other reason" + household_validator.validate_reason_for_leaving_last_settled_home(record) + expect(record.errors["reason"]).to be_empty + end + + it "ignores surrounding non-alphabet characters and casing when determining a match" do + record.reason = 20 + record.reasonother = " 0homelessness ! " + household_validator.validate_reason_for_leaving_last_settled_home(record) + expect(record.errors["reason"]).to include(I18n.t("validations.household.reason.other_not_settled")) + end + end + end end context "when reason is not other" do @@ -231,6 +273,26 @@ RSpec.describe Validations::HouseholdValidations do end describe "household member validations" do + it "validates that the number of household members cannot be less than 1" do + record.hhmemb = 0 + household_validator.validate_numeric_min_max(record) + expect(record.errors["hhmemb"]) + .to include(match I18n.t("validations.numeric.within_range", field: "Number of household members", min: 1, max: 8)) + end + + it "validates that the number of household members cannot be more than 8" do + record.hhmemb = 9 + household_validator.validate_numeric_min_max(record) + expect(record.errors["hhmemb"]) + .to include(match I18n.t("validations.numeric.within_range", field: "Number of household members", min: 1, max: 8)) + end + + it "expects that the number of other household members is between the min and max" do + record.hhmemb = 5 + household_validator.validate_numeric_min_max(record) + expect(record.errors["hhmemb"]).to be_empty + end + it "validates that only 1 partner exists" do record.relat2 = "P" record.relat3 = "P" @@ -394,26 +456,6 @@ RSpec.describe Validations::HouseholdValidations do expect(record.errors["sex2"]).to be_empty expect(record.errors["age2"]).to be_empty end - - it "validates that the number of household members cannot be less than 0" do - record.hhmemb = -1 - household_validator.validate_numeric_min_max(record) - expect(record.errors["hhmemb"]) - .to include(match I18n.t("validations.numeric.within_range", field: "Number of Household Members", min: 0, max: 8)) - end - - it "validates that the number of household members cannot be more than 8" do - record.hhmemb = 9 - household_validator.validate_numeric_min_max(record) - expect(record.errors["hhmemb"]) - .to include(match I18n.t("validations.numeric.within_range", field: "Number of Household Members", min: 0, max: 8)) - end - - it "expects that the number of other household members is between the min and max" do - record.hhmemb = 5 - household_validator.validate_numeric_min_max(record) - expect(record.errors["hhmemb"]).to be_empty - end end context "when the household contains a retired female" do @@ -651,24 +693,30 @@ RSpec.describe Validations::HouseholdValidations do .to be_empty end - it "prevten cannot be 3" do - record.referral = 1 - record.prevten = 3 - household_validator.validate_previous_housing_situation(record) - expect(record.errors["prevten"]) - .to include(match I18n.t("validations.household.prevten.internal_transfer", prevten: "")) - expect(record.errors["referral"]) - .to include(match I18n.t("validations.household.referral.prevten_invalid", prevten: "")) - end - - it "prevten cannot be 4, 10, 13, 19, 23, 24, 25, 26, 28, 29" do - record.referral = 1 - record.prevten = 4 - household_validator.validate_previous_housing_situation(record) - expect(record.errors["prevten"]) - .to include(match I18n.t("validations.household.prevten.internal_transfer", prevten: "")) - expect(record.errors["referral"]) - .to include(match I18n.t("validations.household.referral.prevten_invalid", prevten: "")) + [ + { code: 3, label: "Private sector tenancy" }, + { code: 4, label: "Tied housing or rented with job" }, + { code: 7, label: "Direct access hostel" }, + { code: 10, label: "Hospital" }, + { code: 13, label: "Children’s home or foster care" }, + { code: 14, label: "Bed and breakfast" }, + { code: 19, label: "Rough sleeping" }, + { code: 23, label: "Mobile home or caravan" }, + { code: 24, label: "Home Office Asylum Support" }, + { code: 25, label: "Any other accommodation" }, + { code: 26, label: "Owner occupation (private)" }, + { code: 28, label: "Living with friends or family" }, + { code: 29, label: "Prison or approved probation hostel" }, + ].each do |prevten| + it "prevten cannot be #{prevten[:code]}" do + record.referral = 1 + record.prevten = prevten[:code] + household_validator.validate_previous_housing_situation(record) + expect(record.errors["prevten"]) + .to include(match I18n.t("validations.household.prevten.internal_transfer", prevten: prevten[:label])) + expect(record.errors["referral"]) + .to include(match I18n.t("validations.household.referral.prevten_invalid", prevten: "")) + end end end end diff --git a/spec/models/validations/sales/soft_validations_spec.rb b/spec/models/validations/sales/soft_validations_spec.rb index e417fbd8e..299968436 100644 --- a/spec/models/validations/sales/soft_validations_spec.rb +++ b/spec/models/validations/sales/soft_validations_spec.rb @@ -415,6 +415,17 @@ RSpec.describe Validations::Sales::SoftValidations do .not_to be_shared_ownership_deposit_invalid end + it "returns false if MORTGAGE + DEPOSIT + CASHDIS are within 1£ of VALUE * EQUITY/100" do + record.mortgage = 500 + record.deposit = 500 + record.cashdis = 500 + record.value = 3001 + record.equity = 50 + + expect(record) + .not_to be_shared_ownership_deposit_invalid + end + it "returns false if mortgage is used and no mortgage is given" do record.mortgage = nil record.deposit = 1000 @@ -473,6 +484,18 @@ RSpec.describe Validations::Sales::SoftValidations do .to be_shared_ownership_deposit_invalid end + it "returns false if no cashdis not routed to and MORTGAGE + DEPOSIT are within 1£ of VALUE * EQUITY/100" do + record.mortgage = 500 + record.deposit = 500 + record.type = 2 + record.cashdis = nil + record.value = 1999 + record.equity = 50 + + expect(record) + .not_to be_shared_ownership_deposit_invalid + end + it "returns false if no value is given" do record.mortgage = 1000 record.deposit = 1000 diff --git a/spec/models/validations/soft_validations_spec.rb b/spec/models/validations/soft_validations_spec.rb index 0e234ae02..5c3d76f6f 100644 --- a/spec/models/validations/soft_validations_spec.rb +++ b/spec/models/validations/soft_validations_spec.rb @@ -1233,4 +1233,42 @@ RSpec.describe Validations::SoftValidations do end end end + + describe "reasonother_might_be_existing_category?" do + it "returns true if reasonother is exactly in the 'likely existing category' list" do + record.reasonother = "Domestic Abuse" + + expect(record).to be_reasonother_might_be_existing_category + end + + it "returns true if any word of reasonother is exactly in the 'likely existing category' list" do + record.reasonother = "Was decanted from somewhere" + + expect(record).to be_reasonother_might_be_existing_category + end + + it "is not case sensitive when matching" do + record.reasonother = "domestic abuse" + + expect(record).to be_reasonother_might_be_existing_category + end + + it "returns false if no part of reasonother is in the 'likely existing category' list" do + record.reasonother = "other" + + expect(record).not_to be_reasonother_might_be_existing_category + end + + it "returns false if match to the 'likely existing category' list is only part of a word" do + record.reasonother = "wasdecanted" + + expect(record).not_to be_reasonother_might_be_existing_category + end + + it "ignores neighbouring non-alphabet for matching" do + record.reasonother = "1Domestic abuse." + + expect(record).to be_reasonother_might_be_existing_category + end + end end diff --git a/spec/requests/duplicate_logs_controller_spec.rb b/spec/requests/duplicate_logs_controller_spec.rb index 822d74fdb..08dd17444 100644 --- a/spec/requests/duplicate_logs_controller_spec.rb +++ b/spec/requests/duplicate_logs_controller_spec.rb @@ -169,7 +169,7 @@ RSpec.describe DuplicateLogsController, type: :request do it "displays check your answers for each log with correct questions" do expect(page).to have_content("Q1 - Sale completion date", count: 3) expect(page).to have_content("Q2 - Purchaser code", count: 3) - expect(page).to have_content("Q20 - Lead buyer’s age", count: 3) + expect(page).to have_content("Q20 - Buyer 1’s age", count: 3) expect(page).to have_content("Q21 - Buyer 1’s gender identity", count: 3) expect(page).to have_content("Q25 - Buyer 1's working situation", count: 3) expect(page).to have_content("Q15 - Postcode", count: 3) @@ -187,7 +187,7 @@ RSpec.describe DuplicateLogsController, type: :request do expect(page).to have_content("Q1 - Sale completion date", count: 3) expect(page).to have_content("Q2 - Purchaser code", count: 3) - expect(page).to have_content("Q20 - Lead buyer’s age", count: 3) + expect(page).to have_content("Q20 - Buyer 1’s age", count: 3) expect(page).to have_content("Q21 - Buyer 1’s gender identity", count: 3) expect(page).to have_content("Q25 - Buyer 1's working situation", count: 3) expect(page).to have_content("Postcode (from UPRN)", count: 3) @@ -215,7 +215,7 @@ RSpec.describe DuplicateLogsController, type: :request do it "displays check your answers for each log with correct questions" do expect(page).to have_content("Q1 - Sale completion date", count: 1) expect(page).to have_content("Q2 - Purchaser code", count: 1) - expect(page).to have_content("Q20 - Lead buyer’s age", count: 1) + expect(page).to have_content("Q20 - Buyer 1’s age", count: 1) expect(page).to have_content("Q21 - Buyer 1’s gender identity", count: 1) expect(page).to have_content("Q25 - Buyer 1's working situation", count: 1) expect(page).to have_content("Q15 - Postcode", count: 1) @@ -241,7 +241,7 @@ RSpec.describe DuplicateLogsController, type: :request do it "displays check your answers for each log with correct questions" do expect(page).to have_content("Q1 - Sale completion date", count: 1) expect(page).to have_content("Q2 - Purchaser code", count: 1) - expect(page).to have_content("Q20 - Lead buyer’s age", count: 1) + expect(page).to have_content("Q20 - Buyer 1’s age", count: 1) expect(page).to have_content("Q21 - Buyer 1’s gender identity", count: 1) expect(page).to have_content("Q25 - Buyer 1's working situation", count: 1) expect(page).to have_content("Q15 - Postcode", count: 1) @@ -379,7 +379,7 @@ RSpec.describe DuplicateLogsController, type: :request do it "displays check your answers for each log with correct questions" do expect(page).to have_content("Q1 - Sale completion date", count: 3) expect(page).to have_content("Q2 - Purchaser code", count: 3) - expect(page).to have_content("Q20 - Lead buyer’s age", count: 3) + expect(page).to have_content("Q20 - Buyer 1’s age", count: 3) expect(page).to have_content("Q21 - Buyer 1’s gender identity", count: 3) expect(page).to have_content("Q25 - Buyer 1's working situation", count: 3) expect(page).to have_content("Q15 - Postcode", count: 3) @@ -407,7 +407,7 @@ RSpec.describe DuplicateLogsController, type: :request do it "displays check your answers for each log with correct questions" do expect(page).to have_content("Q1 - Sale completion date", count: 1) expect(page).to have_content("Q2 - Purchaser code", count: 1) - expect(page).to have_content("Q20 - Lead buyer’s age", count: 1) + expect(page).to have_content("Q20 - Buyer 1’s age", count: 1) expect(page).to have_content("Q21 - Buyer 1’s gender identity", count: 1) expect(page).to have_content("Q25 - Buyer 1's working situation", count: 1) expect(page).to have_content("Q15 - Postcode", count: 1) @@ -433,7 +433,7 @@ RSpec.describe DuplicateLogsController, type: :request do it "displays check your answers for each log with correct questions" do expect(page).to have_content("Q1 - Sale completion date", count: 1) expect(page).to have_content("Q2 - Purchaser code", count: 1) - expect(page).to have_content("Q20 - Lead buyer’s age", count: 1) + expect(page).to have_content("Q20 - Buyer 1’s age", count: 1) expect(page).to have_content("Q21 - Buyer 1’s gender identity", count: 1) expect(page).to have_content("Q25 - Buyer 1's working situation", count: 1) expect(page).to have_content("Q15 - Postcode", count: 1) diff --git a/spec/requests/sales_logs_controller_spec.rb b/spec/requests/sales_logs_controller_spec.rb index a9bf07883..6ad991934 100644 --- a/spec/requests/sales_logs_controller_spec.rb +++ b/spec/requests/sales_logs_controller_spec.rb @@ -161,30 +161,6 @@ RSpec.describe SalesLogsController, type: :request do expect(sales_log.managing_organisation.name).to eq("User org") end end - - context "when the user's org doesn't hold stock and merge_organisations_enabled is false" do - let(:organisation) { FactoryBot.create(:organisation, name: "User org", holds_own_stock: false) } - let(:user) { FactoryBot.create(:user, :data_coordinator, organisation:) } - - before do - RequestHelper.stub_http_requests - sign_in user - allow(FeatureToggle).to receive(:merge_organisations_enabled?).and_return(false) - post "/sales-logs", headers: - end - - it "does not set owning organisation" do - created_id = response.location.match(/[0-9]+/)[0] - sales_log = SalesLog.find_by(id: created_id) - expect(sales_log.owning_organisation).to be_nil - end - - it "sets managing organisation as the user organisation" do - created_id = response.location.match(/[0-9]+/)[0] - sales_log = SalesLog.find_by(id: created_id) - expect(sales_log.managing_organisation.name).to eq("User org") - end - end end end end diff --git a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb index cb816ef82..7435a3d74 100644 --- a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb @@ -2294,19 +2294,76 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do end describe "#chcharge" do - let(:attributes) { { bulk_upload:, field_127: "123.45" } } + let(:attributes) { { bulk_upload:, field_127: "123.45", field_131: "123.45", field_130: "123.45", field_129: "123.45", field_128: "123.45" } } it "sets value given" do expect(parser.log.chcharge).to eq(123.45) end + + it "sets is care home to yes" do + expect(parser.log.is_carehome).to eq(1) + end + + it "clears any other given charges" do + parser.log.save! + expect(parser.log.tcharge).to be_nil + expect(parser.log.brent).to be_nil + expect(parser.log.supcharg).to be_nil + expect(parser.log.pscharge).to be_nil + expect(parser.log.scharge).to be_nil + end end describe "#tcharge" do - let(:attributes) { { bulk_upload:, field_132: "123.45" } } + let(:attributes) { { bulk_upload:, field_132: "123.45", field_127: "123.45", field_128: "123.45", field_129: "123.45", field_130: "123.45", field_131: "123.45" } } it "sets value given" do expect(parser.log.tcharge).to eq(123.45) end + + context "when other charges are not given" do + context "and it is carehome" do + let(:attributes) { { bulk_upload:, field_132: "123.45", field_127: "123.45", field_128: nil, field_129: nil, field_130: nil, field_131: nil } } + + it "does not set charges values" do + parser.log.save! + expect(parser.log.tcharge).to be_nil + expect(parser.log.brent).to be_nil + expect(parser.log.supcharg).to be_nil + expect(parser.log.pscharge).to be_nil + expect(parser.log.scharge).to be_nil + end + + it "does not add errors to missing charges" do + parser.valid? + expect(parser.errors[:field_128]).to be_empty + expect(parser.errors[:field_129]).to be_empty + expect(parser.errors[:field_130]).to be_empty + expect(parser.errors[:field_131]).to be_empty + end + end + + context "and it is not carehome" do + let(:attributes) { { bulk_upload:, field_132: "123.45", field_127: nil, field_128: nil, field_129: nil, field_130: nil, field_131: nil } } + + it "does not set charges values" do + parser.log.save! + expect(parser.log.tcharge).to be_nil + expect(parser.log.brent).to be_nil + expect(parser.log.supcharg).to be_nil + expect(parser.log.pscharge).to be_nil + expect(parser.log.scharge).to be_nil + end + + it "adds an error to all missing charges" do + parser.valid? + expect(parser.errors[:field_128]).to eql(["Please enter the basic rent. If there is no basic rent, please enter '0'."]) + expect(parser.errors[:field_129]).to eql(["Please enter the service charge. If there is no service charge, please enter '0'."]) + expect(parser.errors[:field_130]).to eql(["Please enter the personal service charge. If there is no personal service charge, please enter '0'."]) + expect(parser.errors[:field_131]).to eql(["Please enter the support charge. If there is no support charge, please enter '0'."]) + end + end + end end describe "#supcharg" do @@ -2315,6 +2372,50 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do it "sets value given" do expect(parser.log.supcharg).to eq(123.45) end + + context "when other charges are not given" do + context "and it is carehome" do + let(:attributes) { { bulk_upload:, field_132: nil, field_127: "123.45", field_128: nil, field_129: nil, field_130: nil, field_131: "123.45" } } + + it "does not set charges values" do + parser.log.save! + expect(parser.log.tcharge).to be_nil + expect(parser.log.brent).to be_nil + expect(parser.log.supcharg).to be_nil + expect(parser.log.pscharge).to be_nil + expect(parser.log.scharge).to be_nil + end + + it "does not add errors to missing charges" do + parser.valid? + expect(parser.errors[:field_128]).to be_empty + expect(parser.errors[:field_129]).to be_empty + expect(parser.errors[:field_130]).to be_empty + expect(parser.errors[:field_131]).to be_empty + end + end + + context "and it is not carehome" do + let(:attributes) { { bulk_upload:, field_132: "123.45", field_127: nil, field_128: nil, field_129: nil, field_130: nil, field_131: nil } } + + it "does not set charges values" do + parser.log.save! + expect(parser.log.tcharge).to be_nil + expect(parser.log.brent).to be_nil + expect(parser.log.supcharg).to be_nil + expect(parser.log.pscharge).to be_nil + expect(parser.log.scharge).to be_nil + end + + it "adds an error to all missing charges" do + parser.valid? + expect(parser.errors[:field_128]).to eql(["Please enter the basic rent. If there is no basic rent, please enter '0'."]) + expect(parser.errors[:field_129]).to eql(["Please enter the service charge. If there is no service charge, please enter '0'."]) + expect(parser.errors[:field_130]).to eql(["Please enter the personal service charge. If there is no personal service charge, please enter '0'."]) + expect(parser.errors[:field_131]).to eql(["Please enter the support charge. If there is no support charge, please enter '0'."]) + end + end + end end describe "#pscharge" do diff --git a/spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb index 29adddf67..4b2b08d7e 100644 --- a/spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb @@ -2159,11 +2159,24 @@ RSpec.describe BulkUpload::Lettings::Year2024::RowParser do end describe "#chcharge" do - let(:attributes) { { bulk_upload:, field_124: "123.45" } } + let(:attributes) { { bulk_upload:, field_124: "123.45", field_125: "123.45", field_126: "123.45", field_127: "123.45", field_128: "123.45" } } it "sets value given" do expect(parser.log.chcharge).to eq(123.45) end + + it "sets is care home to yes" do + expect(parser.log.is_carehome).to eq(1) + end + + it "clears any other given charges" do + parser.log.save! + expect(parser.log.tcharge).to be_nil + expect(parser.log.brent).to be_nil + expect(parser.log.supcharg).to be_nil + expect(parser.log.pscharge).to be_nil + expect(parser.log.scharge).to be_nil + end end describe "#supcharg" do @@ -2172,6 +2185,50 @@ RSpec.describe BulkUpload::Lettings::Year2024::RowParser do it "sets value given" do expect(parser.log.supcharg).to eq(123.45) end + + context "when other charges are not given" do + context "and it is carehome" do + let(:attributes) { { bulk_upload:, field_128: "123.45", field_124: "123.45", field_125: nil, field_126: nil, field_127: nil } } + + it "does not set charges values" do + parser.log.save! + expect(parser.log.tcharge).to be_nil + expect(parser.log.brent).to be_nil + expect(parser.log.supcharg).to be_nil + expect(parser.log.pscharge).to be_nil + expect(parser.log.scharge).to be_nil + end + + it "does not add errors to missing charges" do + parser.valid? + expect(parser.errors[:field_125]).to be_empty + expect(parser.errors[:field_126]).to be_empty + expect(parser.errors[:field_127]).to be_empty + expect(parser.errors[:field_128]).to be_empty + end + end + + context "and it is not carehome" do + let(:attributes) { { bulk_upload:, field_128: "123.45", field_124: nil, field_125: nil, field_126: nil, field_127: nil } } + + it "does not set charges values" do + parser.log.save! + expect(parser.log.tcharge).to be_nil + expect(parser.log.brent).to be_nil + expect(parser.log.supcharg).to be_nil + expect(parser.log.pscharge).to be_nil + expect(parser.log.scharge).to be_nil + end + + it "adds an error to all missing charges" do + parser.valid? + expect(parser.errors[:field_125]).to eql(["Please enter the basic rent. If there is no basic rent, please enter '0'."]) + expect(parser.errors[:field_126]).to eql(["Please enter the service charge. If there is no service charge, please enter '0'."]) + expect(parser.errors[:field_127]).to eql(["Please enter the personal service charge. If there is no personal service charge, please enter '0'."]) + expect(parser.errors[:field_128]).to be_empty + end + end + end end describe "#pscharge" do diff --git a/spec/services/bulk_upload/sales/year2023/csv_parser_spec.rb b/spec/services/bulk_upload/sales/year2023/csv_parser_spec.rb index 72c19e5d1..6738eb3f3 100644 --- a/spec/services/bulk_upload/sales/year2023/csv_parser_spec.rb +++ b/spec/services/bulk_upload/sales/year2023/csv_parser_spec.rb @@ -28,6 +28,10 @@ RSpec.describe BulkUpload::Sales::Year2023::CsvParser do it "parses csv correctly" do expect(service.row_parsers[0].field_19).to eql(log.uprn) end + + it "counts the number of valid field numbers correctly" do + expect(service).to be_correct_field_count + end end context "when parsing csv with headers in arbitrary order" do diff --git a/spec/services/bulk_upload/sales/year2023/row_parser_spec.rb b/spec/services/bulk_upload/sales/year2023/row_parser_spec.rb index 1056d4402..c4f4a26a6 100644 --- a/spec/services/bulk_upload/sales/year2023/row_parser_spec.rb +++ b/spec/services/bulk_upload/sales/year2023/row_parser_spec.rb @@ -1001,6 +1001,24 @@ RSpec.describe BulkUpload::Sales::Year2023::RowParser do end describe "#log" do + describe "#noint" do + context "when field is set to 1" do + let(:attributes) { valid_attributes.merge({ field_28: 1 }) } + + it "is correctly set" do + expect(parser.log.noint).to be(1) + end + end + + context "when field is set to 2" do + let(:attributes) { valid_attributes.merge({ field_28: 2 }) } + + it "is correctly set" do + expect(parser.log.noint).to be(2) + end + end + end + describe "#uprn" do let(:attributes) { setup_section_params.merge({ field_19: "12" }) } diff --git a/spec/services/bulk_upload/sales/year2024/csv_parser_spec.rb b/spec/services/bulk_upload/sales/year2024/csv_parser_spec.rb index e4391212a..ef90bd834 100644 --- a/spec/services/bulk_upload/sales/year2024/csv_parser_spec.rb +++ b/spec/services/bulk_upload/sales/year2024/csv_parser_spec.rb @@ -28,6 +28,10 @@ RSpec.describe BulkUpload::Sales::Year2024::CsvParser do it "parses csv correctly" do expect(service.row_parsers[0].field_22).to eql(log.uprn) end + + it "counts the number of valid field numbers correctly" do + expect(service).to be_correct_field_count + end end context "when parsing csv with headers in arbitrary order" do diff --git a/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb b/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb index 2656ff1c0..5a1a9e4df 100644 --- a/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb +++ b/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb @@ -9,11 +9,13 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do let(:bulk_upload) { create(:bulk_upload, :sales, user:, year: 2024) } let(:user) { create(:user, organisation: owning_org) } let(:owning_org) { create(:organisation, :with_old_visible_id) } + let(:managing_org) { create(:organisation, :with_old_visible_id) } + let(:setup_section_params) do { bulk_upload:, field_1: owning_org.old_visible_id, # organisation - field_2: owning_org.old_visible_id, # organisation + field_2: managing_org.old_visible_id, # organisation field_3: user.email, # user field_4: now.day.to_s, # sale day field_5: now.month.to_s, # sale month @@ -31,7 +33,7 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do { bulk_upload:, field_1: owning_org.old_visible_id, - field_2: owning_org.old_visible_id, + field_2: managing_org.old_visible_id, field_4: "12", field_5: "5", @@ -114,6 +116,8 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do end around do |example| + create(:organisation_relationship, parent_organisation: owning_org, child_organisation: managing_org) + Timecop.freeze(Time.zone.local(2025, 2, 22)) do Singleton.__init__(FormHandler) example.run @@ -287,7 +291,7 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do it "has errors on correct setup fields" do errors = parser.errors.select { |e| e.options[:category] == :setup }.map(&:attribute).sort - expect(errors).to eql(%i[field_1 field_17 field_18 field_4 field_5 field_6 field_8]) + expect(errors).to eql(%i[field_1 field_17 field_18 field_2 field_4 field_5 field_6 field_8]) end end @@ -303,7 +307,7 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do it "has errors on correct setup fields" do errors = parser.errors.select { |e| e.options[:category] == :setup }.map(&:attribute).sort - expect(errors).to eql(%i[field_1 field_15 field_17 field_18 field_4 field_5 field_6 field_9]) + expect(errors).to eql(%i[field_1 field_15 field_17 field_18 field_2 field_4 field_5 field_6 field_9]) end end @@ -321,7 +325,7 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do it "has errors on correct setup fields" do errors = parser.errors.select { |e| e.options[:category] == :setup }.map(&:attribute).sort - expect(errors).to eql(%i[field_1 field_16 field_17 field_18 field_4 field_5 field_6]) + expect(errors).to eql(%i[field_1 field_16 field_17 field_18 field_2 field_4 field_5 field_6]) end end @@ -338,7 +342,7 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do it "has errors on correct setup fields" do errors = parser.errors.select { |e| e.options[:category] == :setup }.map(&:attribute).sort - expect(errors).to eql(%i[field_1 field_10 field_15 field_17 field_18 field_4 field_5 field_6]) + expect(errors).to eql(%i[field_1 field_10 field_15 field_17 field_18 field_2 field_4 field_5 field_6]) end end @@ -346,7 +350,6 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do let(:attributes) do { bulk_upload:, - field_17: "test id", field_31: "2", field_46: "8", field_39: "1", @@ -356,7 +359,7 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do it "has errors on correct setup fields" do errors = parser.errors.select { |e| e.options[:category] == :setup }.map(&:attribute).sort - expect(errors).to eql(%i[field_1 field_17 field_18 field_4 field_5 field_6 field_8]) + expect(errors).to eql(%i[field_1 field_17 field_18 field_2 field_4 field_5 field_6 field_8]) end end @@ -372,7 +375,7 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do it "has errors on correct setup fields" do errors = parser.errors.select { |e| e.options[:category] == :setup }.map(&:attribute).sort - expect(errors).to eql(%i[field_1 field_11 field_13 field_14 field_17 field_18 field_4 field_5 field_6]) + expect(errors).to eql(%i[field_1 field_11 field_13 field_14 field_17 field_18 field_2 field_4 field_5 field_6]) end end @@ -390,7 +393,7 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do it "has errors on correct setup fields" do errors = parser.errors.select { |e| e.options[:category] == :setup }.map(&:attribute).sort - expect(errors).to eql(%i[field_1 field_12 field_14 field_15 field_17 field_18 field_4 field_5 field_6]) + expect(errors).to eql(%i[field_1 field_12 field_14 field_15 field_17 field_18 field_2 field_4 field_5 field_6]) end end @@ -1023,6 +1026,24 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do end describe "#log" do + describe "#noint" do + context "when field is set to 1" do + let(:attributes) { valid_attributes.merge({ field_17: 1 }) } + + it "is correctly set" do + expect(parser.log.noint).to be(1) + end + end + + context "when field is set to 2" do + let(:attributes) { valid_attributes.merge({ field_17: 2 }) } + + it "is correctly set" do + expect(parser.log.noint).to be(2) + end + end + end + describe "#uprn" do let(:attributes) { setup_section_params.merge({ field_22: "12" }) } @@ -1446,39 +1467,55 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do let(:attributes) { setup_section_params } context "when user is part of the owning organisation" do - it "sets managing organisation to the users organisation" do + it "sets managing organisation to the correct organisation" do parser.valid? expect(parser.log.owning_organisation_id).to be(owning_org.id) - expect(parser.log.managing_organisation_id).to be(owning_org.id) + expect(parser.log.managing_organisation_id).to be(managing_org.id) end end - context "when user is part of an organisation affiliated with owning org" do - let(:managing_agent) { create(:organisation) } - let(:user) { create(:user, organisation: managing_agent) } - let(:attributes) { setup_section_params } + context "when blank" do + let(:attributes) { { bulk_upload:, field_2: "" } } - before do - create(:organisation_relationship, child_organisation: managing_agent, parent_organisation: owning_org) + it "is not permitted as setup error" do + parser.valid? + setup_errors = parser.errors.select { |e| e.options[:category] == :setup } + + expect(setup_errors.find { |e| e.attribute == :field_2 }.message).to eql("You must answer reported by") end + it "blocks log creation" do + parser.valid? + expect(parser).to be_block_log_creation + end + end + + context "when cannot find managing org" do + let(:attributes) { { bulk_upload:, field_2: "donotexist" } } + it "is not permitted as setup error" do parser.valid? - expect(parser.log.owning_organisation_id).to be(owning_org.id) - expect(parser.log.managing_organisation_id).to be(managing_agent.id) + setup_errors = parser.errors.select { |e| e.options[:category] == :setup } + + expect(setup_errors.find { |e| e.attribute == :field_2 }.message).to eql("You must answer reported by") + end + + it "blocks log creation" do + parser.valid? + expect(parser).to be_block_log_creation end end - context "when user is part of an organisation not affiliated with owning org" do - let(:unaffiliated_org) { create(:organisation) } - let(:user) { create(:user, organisation: unaffiliated_org) } - let(:attributes) { setup_section_params } + context "when not affiliated with managing org" do + let(:unaffiliated_org) { create(:organisation, :with_old_visible_id) } + + let(:attributes) { { bulk_upload:, field_1: owning_org.old_visible_id, field_2: unaffiliated_org.old_visible_id } } it "is not permitted as setup error" do parser.valid? setup_errors = parser.errors.select { |e| e.options[:category] == :setup } - expect(setup_errors.find { |e| e.attribute == :field_3 }.message).to eql("This user belongs to an organisation that does not have a relationship with the owning organisation") + expect(setup_errors.find { |e| e.attribute == :field_2 }.message).to eql("This organisation does not have a relationship with the owning organisation") end it "blocks log creation" do diff --git a/yarn.lock b/yarn.lock index d6e84caac..1286245d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3245,10 +3245,10 @@ govuk-frontend@4.7.0: resolved "https://registry.yarnpkg.com/govuk-frontend/-/govuk-frontend-4.7.0.tgz#69950b6c2e69f435ffe9aa60d8dee232dac977de" integrity sha512-0OsdCusF5qvLWwKziU8zqxiC0nq6WP0ZQuw51ymZ/1V0tO71oIKMlSLN2S9bm8RcEGSoidPt2A34gKxePrLjvg== -govuk-frontend@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/govuk-frontend/-/govuk-frontend-5.0.0.tgz#c08a4d1115fb31eb39b6d19979c627f816185dd7" - integrity sha512-3WSfvQ+3kw/q/m8jrq/t8XnMUA8D2r0uhGyZaDbIh1gWTJBQzJBHbHiKYI9nc9ixIXdCFsc9RozkgEm57a795g== +govuk-frontend@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/govuk-frontend/-/govuk-frontend-5.1.0.tgz#55e520940b587ddd023e96251efaa076acc9bd5f" + integrity sha512-Dc3J+uOI4i2VR3BVyfxbf6qVjTT4n4bBqbD0/Io6feP8pt/4IfKdP1vWimZf+BwMKKMXacw10hmdy5UcD6Cr8w== govuk-prototype-kit@^13.14.1: version "13.16.0"