diff --git a/.github/workflows/production_pipeline.yml b/.github/workflows/production_pipeline.yml
index 333e1f87b..23bb69c00 100644
--- a/.github/workflows/production_pipeline.yml
+++ b/.github/workflows/production_pipeline.yml
@@ -238,6 +238,8 @@ jobs:
RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
IMPORT_PAAS_INSTANCE: ${{ secrets.IMPORT_PAAS_INSTANCE }}
EXPORT_PAAS_INSTANCE: ${{ secrets.EXPORT_PAAS_INSTANCE }}
+ S3_CONFIG: ${{ secrets.S3_CONFIG }}
+ CSV_DOWNLOAD_PAAS_INSTANCE: ${{ secrets.CSV_DOWNLOAD_PAAS_INSTANCE }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
run: |
cf api $CF_API_ENDPOINT
@@ -248,5 +250,7 @@ jobs:
cf set-env $APP_NAME RAILS_MASTER_KEY $RAILS_MASTER_KEY
cf set-env $APP_NAME IMPORT_PAAS_INSTANCE $IMPORT_PAAS_INSTANCE
cf set-env $APP_NAME EXPORT_PAAS_INSTANCE $EXPORT_PAAS_INSTANCE
+ cf set-env $APP_NAME S3_CONFIG $S3_CONFIG
+ cf set-env $APP_NAME CSV_DOWNLOAD_PAAS_INSTANCE $CSV_DOWNLOAD_PAAS_INSTANCE
cf set-env $APP_NAME SENTRY_DSN $SENTRY_DSN
cf push $APP_NAME --strategy rolling
diff --git a/.github/workflows/staging_pipeline.yml b/.github/workflows/staging_pipeline.yml
index 4c588e919..d59024cd3 100644
--- a/.github/workflows/staging_pipeline.yml
+++ b/.github/workflows/staging_pipeline.yml
@@ -214,6 +214,8 @@ jobs:
RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
IMPORT_PAAS_INSTANCE: ${{ secrets.IMPORT_PAAS_INSTANCE }}
EXPORT_PAAS_INSTANCE: ${{ secrets.EXPORT_PAAS_INSTANCE }}
+ S3_CONFIG: ${{ secrets.S3_CONFIG }}
+ CSV_DOWNLOAD_PAAS_INSTANCE: ${{ secrets.CSV_DOWNLOAD_PAAS_INSTANCE }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
run: |
cf api $CF_API_ENDPOINT
@@ -226,5 +228,7 @@ jobs:
cf set-env $APP_NAME RAILS_MASTER_KEY $RAILS_MASTER_KEY
cf set-env $APP_NAME IMPORT_PAAS_INSTANCE $IMPORT_PAAS_INSTANCE
cf set-env $APP_NAME EXPORT_PAAS_INSTANCE $EXPORT_PAAS_INSTANCE
+ cf set-env $APP_NAME S3_CONFIG $S3_CONFIG
+ cf set-env $APP_NAME CSV_DOWNLOAD_PAAS_INSTANCE $CSV_DOWNLOAD_PAAS_INSTANCE
cf set-env $APP_NAME SENTRY_DSN $SENTRY_DSN
cf push $APP_NAME --strategy rolling
diff --git a/.gitignore b/.gitignore
index 65bf729e6..93a5a3a05 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,3 +55,5 @@ yarn-debug.log*
/app/assets/builds/*
!/app/assets/builds/.keep
+
+spec/examples.txt
diff --git a/.rubocop.yml b/.rubocop.yml
index cf0c1e933..059bc2a84 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -20,3 +20,10 @@ AllCops:
Style/Documentation:
Enabled: false
+
+Rails/UnknownEnv:
+ Environments:
+ - production
+ - staging
+ - development
+ - test
diff --git a/Gemfile b/Gemfile
index ea68fd2d0..254d8a5a9 100644
--- a/Gemfile
+++ b/Gemfile
@@ -58,8 +58,8 @@ gem "sentry-ruby"
gem "possessive"
# Strip whitespace from active record attributes
gem "auto_strip_attributes"
-# Background job processing
-gem 'resque', '~> 2.4'
+# Use sidekiq for background processing
+gem "sidekiq"
gem 'wisper', '~> 2.0'
group :development, :test do
diff --git a/Gemfile.lock b/Gemfile.lock
index d1a064220..c21cb813a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -13,67 +13,67 @@ GIT
GEM
remote: https://rubygems.org/
specs:
- actioncable (7.0.3.1)
- actionpack (= 7.0.3.1)
- activesupport (= 7.0.3.1)
+ actioncable (7.0.4)
+ actionpack (= 7.0.4)
+ activesupport (= 7.0.4)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
- actionmailbox (7.0.3.1)
- actionpack (= 7.0.3.1)
- activejob (= 7.0.3.1)
- activerecord (= 7.0.3.1)
- activestorage (= 7.0.3.1)
- activesupport (= 7.0.3.1)
+ actionmailbox (7.0.4)
+ actionpack (= 7.0.4)
+ activejob (= 7.0.4)
+ activerecord (= 7.0.4)
+ activestorage (= 7.0.4)
+ activesupport (= 7.0.4)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
- actionmailer (7.0.3.1)
- actionpack (= 7.0.3.1)
- actionview (= 7.0.3.1)
- activejob (= 7.0.3.1)
- activesupport (= 7.0.3.1)
+ actionmailer (7.0.4)
+ actionpack (= 7.0.4)
+ actionview (= 7.0.4)
+ activejob (= 7.0.4)
+ activesupport (= 7.0.4)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0)
- actionpack (7.0.3.1)
- actionview (= 7.0.3.1)
- activesupport (= 7.0.3.1)
+ actionpack (7.0.4)
+ actionview (= 7.0.4)
+ activesupport (= 7.0.4)
rack (~> 2.0, >= 2.2.0)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
- actiontext (7.0.3.1)
- actionpack (= 7.0.3.1)
- activerecord (= 7.0.3.1)
- activestorage (= 7.0.3.1)
- activesupport (= 7.0.3.1)
+ actiontext (7.0.4)
+ actionpack (= 7.0.4)
+ activerecord (= 7.0.4)
+ activestorage (= 7.0.4)
+ activesupport (= 7.0.4)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
- actionview (7.0.3.1)
- activesupport (= 7.0.3.1)
+ actionview (7.0.4)
+ activesupport (= 7.0.4)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
- activejob (7.0.3.1)
- activesupport (= 7.0.3.1)
+ activejob (7.0.4)
+ activesupport (= 7.0.4)
globalid (>= 0.3.6)
- activemodel (7.0.3.1)
- activesupport (= 7.0.3.1)
- activerecord (7.0.3.1)
- activemodel (= 7.0.3.1)
- activesupport (= 7.0.3.1)
- activestorage (7.0.3.1)
- actionpack (= 7.0.3.1)
- activejob (= 7.0.3.1)
- activerecord (= 7.0.3.1)
- activesupport (= 7.0.3.1)
+ activemodel (7.0.4)
+ activesupport (= 7.0.4)
+ activerecord (7.0.4)
+ activemodel (= 7.0.4)
+ activesupport (= 7.0.4)
+ activestorage (7.0.4)
+ actionpack (= 7.0.4)
+ activejob (= 7.0.4)
+ activerecord (= 7.0.4)
+ activesupport (= 7.0.4)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
- activesupport (7.0.3.1)
+ activesupport (7.0.4)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
@@ -84,8 +84,8 @@ GEM
auto_strip_attributes (2.6.0)
activerecord (>= 4.0)
aws-eventstream (1.2.0)
- aws-partitions (1.627.0)
- aws-sdk-core (3.143.0)
+ aws-partitions (1.638.0)
+ aws-sdk-core (3.156.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1)
@@ -127,7 +127,7 @@ GEM
rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
- capybara-lockstep (1.1.1)
+ capybara-lockstep (1.2.1)
activesupport (>= 3.2)
capybara (>= 2.0)
ruby2_keywords
@@ -135,7 +135,7 @@ GEM
childprocess (4.1.0)
coderay (1.1.3)
concurrent-ruby (1.1.10)
- connection_pool (2.2.5)
+ connection_pool (2.3.0)
crack (0.4.5)
rexml
crass (1.0.6)
@@ -161,7 +161,7 @@ GEM
rubocop
smart_properties
erubi (1.11.0)
- excon (0.92.4)
+ excon (0.92.5)
factory_bot (6.2.1)
activesupport (>= 5.0.0)
factory_bot_rails (6.2.0)
@@ -203,7 +203,7 @@ GEM
listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
- loofah (2.18.0)
+ loofah (2.19.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
@@ -214,11 +214,7 @@ GEM
method_source (1.0.0)
mini_mime (1.1.2)
minitest (5.16.3)
- mono_logger (1.1.1)
msgpack (1.5.6)
- multi_json (1.15.0)
- mustermann (3.0.0)
- ruby2_keywords (~> 0.0.1)
net-imap (0.2.3)
digest
net-protocol
@@ -229,17 +225,11 @@ GEM
timeout
net-protocol (0.1.3)
timeout
- net-smtp (0.3.1)
- digest
+ net-smtp (0.3.2)
net-protocol
- timeout
nio4r (2.5.8)
- nokogiri (1.13.8-arm64-darwin)
- racc (~> 1.4)
nokogiri (1.13.8-x86_64-darwin)
racc (~> 1.4)
- nokogiri (1.13.8-x86_64-linux)
- racc (~> 1.4)
notifications-ruby-client (5.3.0)
jwt (>= 1.5, < 3)
orm_adapter (0.5.0)
@@ -280,34 +270,32 @@ GEM
rack (2.2.4)
rack-attack (6.6.1)
rack (>= 1.0, < 3)
- rack-mini-profiler (2.3.4)
+ rack-mini-profiler (3.0.0)
rack (>= 1.2.0)
- rack-protection (3.0.1)
- rack
rack-test (2.0.2)
rack (>= 1.3)
- rails (7.0.3.1)
- actioncable (= 7.0.3.1)
- actionmailbox (= 7.0.3.1)
- actionmailer (= 7.0.3.1)
- actionpack (= 7.0.3.1)
- actiontext (= 7.0.3.1)
- actionview (= 7.0.3.1)
- activejob (= 7.0.3.1)
- activemodel (= 7.0.3.1)
- activerecord (= 7.0.3.1)
- activestorage (= 7.0.3.1)
- activesupport (= 7.0.3.1)
+ rails (7.0.4)
+ actioncable (= 7.0.4)
+ actionmailbox (= 7.0.4)
+ actionmailer (= 7.0.4)
+ actionpack (= 7.0.4)
+ actiontext (= 7.0.4)
+ actionview (= 7.0.4)
+ activejob (= 7.0.4)
+ activemodel (= 7.0.4)
+ activerecord (= 7.0.4)
+ activestorage (= 7.0.4)
+ activesupport (= 7.0.4)
bundler (>= 1.15.0)
- railties (= 7.0.3.1)
+ railties (= 7.0.4)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.4.3)
loofah (~> 2.3)
- railties (7.0.3.1)
- actionpack (= 7.0.3.1)
- activesupport (= 7.0.3.1)
+ railties (7.0.4)
+ actionpack (= 7.0.4)
+ activesupport (= 7.0.4)
method_source
rake (>= 12.2)
thor (~> 1.0)
@@ -319,23 +307,16 @@ GEM
rb-inotify (0.10.1)
ffi (~> 1.0)
redcarpet (3.5.1)
- redis (5.0.3)
- redis-client (>= 0.7.4)
- redis-client (0.8.0)
+ redis (5.0.5)
+ redis-client (>= 0.9.0)
+ redis-client (0.9.0)
connection_pool
- redis-namespace (1.9.0)
- redis (>= 4)
- regexp_parser (2.5.0)
+ regexp_parser (2.6.0)
request_store (1.5.1)
rack (>= 1.4)
responders (3.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
- resque (2.4.0)
- mono_logger (~> 1.0)
- multi_json (~> 1.0)
- redis-namespace (~> 1.6)
- sinatra (>= 0.9.2)
rexml (3.2.5)
roo (2.9.0)
nokogiri (~> 1)
@@ -343,7 +324,7 @@ GEM
rotp (6.2.0)
rspec-core (3.11.0)
rspec-support (~> 3.11.0)
- rspec-expectations (3.11.0)
+ rspec-expectations (3.11.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.11.0)
rspec-mocks (3.11.1)
@@ -357,7 +338,7 @@ GEM
rspec-expectations (~> 3.10)
rspec-mocks (~> 3.10)
rspec-support (~> 3.10)
- rspec-support (3.11.0)
+ rspec-support (3.11.1)
rubocop (1.25.0)
parallel (~> 1.10)
parser (>= 3.1.0.0)
@@ -375,7 +356,7 @@ GEM
rubocop-rails (= 2.13.2)
rubocop-rake (= 0.6.0)
rubocop-rspec (= 2.7.0)
- rubocop-performance (1.14.3)
+ rubocop-performance (1.15.0)
rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0)
rubocop-rails (2.13.2)
@@ -399,30 +380,28 @@ GEM
sentry-ruby (~> 5.4.2)
sentry-ruby (5.4.2)
concurrent-ruby (~> 1.0, >= 1.0.2)
+ sidekiq (6.5.5)
+ connection_pool (>= 2.2.2)
+ rack (~> 2.0)
+ redis (>= 4.5.0)
simplecov (0.21.2)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.4)
- sinatra (3.0.1)
- mustermann (~> 3.0)
- rack (~> 2.2, >= 2.2.4)
- rack-protection (= 3.0.1)
- tilt (~> 2.0)
smart_properties (1.17.0)
stackprof (0.2.21)
stimulus-rails (1.1.0)
railties (>= 6.0.0)
strscan (3.0.4)
thor (1.2.1)
- tilt (2.0.11)
timecop (0.9.5)
timeout (0.3.0)
tzinfo (2.0.5)
concurrent-ruby (~> 1.0)
uk_postcode (2.1.8)
- unicode-display_width (2.2.0)
+ unicode-display_width (2.3.0)
uniform_notifier (1.16.0)
view_component (2.69.0)
activesupport (>= 5.0.0, < 8.0)
@@ -450,11 +429,7 @@ GEM
zeitwerk (2.6.0)
PLATFORMS
- arm64-darwin-21
- x86_64-darwin-19
- x86_64-darwin-20
x86_64-darwin-21
- x86_64-linux
DEPENDENCIES
auto_strip_attributes
@@ -493,7 +468,6 @@ DEPENDENCIES
rack-mini-profiler
rails (~> 7.0.2)
redis
- resque (~> 2.4)
roo
rspec-rails
rubocop-govuk (= 4.3.0)
@@ -502,6 +476,7 @@ DEPENDENCIES
selenium-webdriver
sentry-rails
sentry-ruby
+ sidekiq
simplecov
stackprof
stimulus-rails
@@ -518,4 +493,4 @@ RUBY VERSION
ruby 3.1.2p20
BUNDLED WITH
- 2.3.14
+ 2.3.22
diff --git a/app/components/check_answers_summary_list_card_component.html.erb b/app/components/check_answers_summary_list_card_component.html.erb
index 73e9335c8..f299674b2 100644
--- a/app/components/check_answers_summary_list_card_component.html.erb
+++ b/app/components/check_answers_summary_list_card_component.html.erb
@@ -16,18 +16,18 @@
<% row.key { question.check_answer_label.to_s.presence || question.header.to_s } %>
<% row.value do %>
<%= get_answer_label(question) %>
- <% extra_value = question.get_extra_check_answer_value(lettings_log) %>
+ <% extra_value = question.get_extra_check_answer_value(log) %>
<% if extra_value %>
<%= extra_value %>
<% end %>
- <% question.get_inferred_answers(lettings_log).each do |inferred_answer| %>
+ <% question.get_inferred_answers(log).each do |inferred_answer| %>
<%= inferred_answer %>
<% end %>
<% end %>
<% row.action(
- text: question.action_text(lettings_log),
- href: question.action_href(lettings_log, question.page.id),
+ text: question.action_text(log),
+ href: question.action_href(log, question.page.id),
visually_hidden_text: question.check_answer_label.to_s.downcase,
) %>
<% end %>
diff --git a/app/components/check_answers_summary_list_card_component.rb b/app/components/check_answers_summary_list_card_component.rb
index bc7d8cdb9..de7fe9685 100644
--- a/app/components/check_answers_summary_list_card_component.rb
+++ b/app/components/check_answers_summary_list_card_component.rb
@@ -1,18 +1,18 @@
class CheckAnswersSummaryListCardComponent < ViewComponent::Base
- attr_reader :questions, :lettings_log, :user
+ attr_reader :questions, :log, :user
- def initialize(questions:, lettings_log:, user:)
+ def initialize(questions:, log:, user:)
@questions = questions
- @lettings_log = lettings_log
+ @log = log
@user = user
super
end
def applicable_questions
- questions.reject { |q| q.hidden_in_check_answers?(lettings_log, user) }
+ questions.reject { |q| q.hidden_in_check_answers?(log, user) }
end
def get_answer_label(question)
- question.answer_label(lettings_log).presence || "You didn’t answer this question".html_safe
+ question.answer_label(log).presence || "You didn’t answer this question".html_safe
end
end
diff --git a/app/components/log_summary_component.html.erb b/app/components/log_summary_component.html.erb
index 07203b26b..65d2fc045 100644
--- a/app/components/log_summary_component.html.erb
+++ b/app/components/log_summary_component.html.erb
@@ -3,11 +3,11 @@
<% if log.needstype? %>
<%= log.is_general_needs? ? "General needs" : "Supported housing" %>
diff --git a/app/controllers/bulk_upload_controller.rb b/app/controllers/bulk_upload_controller.rb
index ffc22d028..ed2ac2627 100644
--- a/app/controllers/bulk_upload_controller.rb
+++ b/app/controllers/bulk_upload_controller.rb
@@ -3,7 +3,7 @@ class BulkUploadController < ApplicationController
def show
@bulk_upload = BulkUpload.new(nil, nil)
- render "lettings_logs/bulk_upload"
+ render "logs/bulk_upload"
end
def bulk_upload
@@ -12,7 +12,7 @@ class BulkUploadController < ApplicationController
@bulk_upload = BulkUpload.new(file, content_type)
@bulk_upload.process(current_user)
if @bulk_upload.errors.present?
- render "lettings_logs/bulk_upload", status: :unprocessable_entity
+ render "logs/bulk_upload", status: :unprocessable_entity
else
redirect_to(lettings_logs_path)
end
diff --git a/app/controllers/form_controller.rb b/app/controllers/form_controller.rb
index a7ee469b8..6e71de6c6 100644
--- a/app/controllers/form_controller.rb
+++ b/app/controllers/form_controller.rb
@@ -4,22 +4,22 @@ class FormController < ApplicationController
before_action :find_resource_by_named_id, except: %i[submit_form review]
def submit_form
- if @lettings_log
- @page = @lettings_log.form.get_page(params[:lettings_log][:page])
+ if @log
+ @page = @log.form.get_page(params[@log.model_name.param_key][:page])
responses_for_page = responses_for_page(@page)
mandatory_questions_with_no_response = mandatory_questions_with_no_response(responses_for_page)
- if mandatory_questions_with_no_response.empty? && @lettings_log.update(responses_for_page)
+ if mandatory_questions_with_no_response.empty? && @log.update(responses_for_page)
session[:errors] = session[:fields] = nil
redirect_to(successful_redirect_path)
else
- redirect_path = "lettings_log_#{@page.id}_path"
+ redirect_path = "#{@log.model_name.param_key}_#{@page.id}_path"
mandatory_questions_with_no_response.map do |question|
- @lettings_log.errors.add question.id.to_sym, question.unanswered_error_message
+ @log.errors.add question.id.to_sym, question.unanswered_error_message
end
- session[:errors] = @lettings_log.errors.to_json
- Rails.logger.info "User triggered validation(s) on: #{@lettings_log.errors.map(&:attribute).join(', ')}"
- redirect_to(send(redirect_path, @lettings_log))
+ session[:errors] = @log.errors.to_json
+ Rails.logger.info "User triggered validation(s) on: #{@log.errors.map(&:attribute).join(', ')}"
+ redirect_to(send(redirect_path, @log))
end
else
render_not_found
@@ -27,9 +27,9 @@ class FormController < ApplicationController
end
def check_answers
- if @lettings_log
+ if @log
current_url = request.env["PATH_INFO"]
- subsection = @lettings_log.form.get_subsection(current_url.split("/")[-2])
+ subsection = @log.form.get_subsection(current_url.split("/")[-2])
render "form/check_answers", locals: { subsection:, current_user: }
else
render_not_found
@@ -37,29 +37,26 @@ class FormController < ApplicationController
end
def review
- if @lettings_log
+ if @log
render "form/review"
else
render_not_found
end
end
- FormHandler.instance.forms.each do |_key, form|
- form.pages.map do |page|
- define_method(page.id) do |_errors = {}|
- if @lettings_log
- restore_error_field_values
- @subsection = @lettings_log.form.subsection_for_page(page)
- @page = @lettings_log.form.get_page(page.id)
- if @page.routed_to?(@lettings_log, current_user)
- render "form/page"
- else
- redirect_to lettings_log_path(@lettings_log)
- end
- else
- render_not_found
- end
+ def show_page
+ if @log
+ restore_error_field_values
+ page_id = request.path.split("/")[-1].underscore
+ @page = @log.form.get_page(page_id)
+ @subsection = @log.form.subsection_for_page(@page)
+ if @page.routed_to?(@log, current_user)
+ render "form/page"
+ else
+ redirect_to lettings_log_path(@log)
end
+ else
+ render_not_found
end
end
@@ -68,13 +65,13 @@ private
def restore_error_field_values
if session["errors"]
JSON(session["errors"]).each do |field, messages|
- messages.each { |message| @lettings_log.errors.add field.to_sym, message }
+ messages.each { |message| @log.errors.add field.to_sym, message }
end
end
if session["fields"]
session["fields"].each do |field, value|
- unless @lettings_log.form.get_question(field, @lettings_log)&.type == "date"
- @lettings_log[field] = value
+ if @log.form.get_question(field, @log)&.type != "date" && @log.respond_to?(field)
+ @log[field] = value
end
end
end
@@ -82,11 +79,11 @@ private
def responses_for_page(page)
page.questions.each_with_object({}) do |question, result|
- question_params = params["lettings_log"][question.id]
+ question_params = params[@log.model_name.param_key][question.id]
if question.type == "date"
- day = params["lettings_log"]["#{question.id}(3i)"]
- month = params["lettings_log"]["#{question.id}(2i)"]
- year = params["lettings_log"]["#{question.id}(1i)"]
+ day = params[@log.model_name.param_key]["#{question.id}(3i)"]
+ month = params[@log.model_name.param_key]["#{question.id}(2i)"]
+ year = params[@log.model_name.param_key]["#{question.id}(1i)"]
next unless [day, month, year].any?(&:present?)
result[question.id] = if Date.valid_date?(year.to_i, month.to_i, day.to_i) && year.to_i.between?(2000, 2200)
@@ -109,11 +106,19 @@ private
end
def find_resource
- @lettings_log = current_user.lettings_logs.find_by(id: params[:id])
+ @log = if params.key?("sales_log")
+ current_user.sales_logs.find_by(id: params[:id])
+ else
+ current_user.lettings_logs.find_by(id: params[:id])
+ end
end
def find_resource_by_named_id
- @lettings_log = current_user.lettings_logs.find_by(id: params[:lettings_log_id])
+ @log = if params[:sales_log_id].present?
+ current_user.sales_logs.find_by(id: params[:sales_log_id])
+ else
+ current_user.lettings_logs.find_by(id: params[:lettings_log_id])
+ end
end
def is_referrer_check_answers?
@@ -123,18 +128,18 @@ private
def successful_redirect_path
if is_referrer_check_answers?
- page_ids = @lettings_log.form.subsection_for_page(@page).pages.map(&:id)
+ page_ids = @log.form.subsection_for_page(@page).pages.map(&:id)
page_index = page_ids.index(@page.id)
- next_page = @lettings_log.form.next_page(@page, @lettings_log, current_user)
- previous_page = @lettings_log.form.previous_page(page_ids, page_index, @lettings_log, current_user)
+ next_page = @log.form.next_page(@page, @log, current_user)
+ previous_page = @log.form.previous_page(page_ids, page_index, @log, current_user)
if next_page.to_s.include?("value_check") || next_page == previous_page
- return "/logs/#{@lettings_log.id}/#{next_page.dasherize}?referrer=check_answers"
+ return send("#{@log.class.name.underscore}_#{next_page}_path", @log, { referrer: "check_answers" })
else
- return send("lettings_log_#{@lettings_log.form.subsection_for_page(@page).id}_check_answers_path", @lettings_log)
+ return send("#{@log.model_name.param_key}_#{@log.form.subsection_for_page(@page).id}_check_answers_path", @log)
end
end
- redirect_path = @lettings_log.form.next_page_redirect_path(@page, @lettings_log, current_user)
- send(redirect_path, @lettings_log)
+ redirect_path = @log.form.next_page_redirect_path(@page, @log, current_user)
+ send(redirect_path, @log)
end
def mandatory_questions_with_no_response(responses_for_page)
@@ -148,12 +153,12 @@ private
end
def question_is_required?(question)
- LettingsLog::OPTIONAL_FIELDS.exclude?(question.id) && required_questions.include?(question.id)
+ @log.class::OPTIONAL_FIELDS.exclude?(question.id) && required_questions.include?(question.id)
end
def required_questions
@required_questions ||= begin
- log = @lettings_log
+ log = @log
log.assign_attributes(responses_for_page(@page))
@page.subsection.applicable_questions(log).select { |q| q.enabled?(log) }.map(&:id)
end
@@ -162,12 +167,12 @@ private
def question_missing_response?(responses_for_page, question)
if %w[checkbox validation_override].include?(question.type)
answered = question.answer_options.keys.reject { |x| x.match(/divider/) }.map do |option|
- session["fields"][option] = @lettings_log[option] = params["lettings_log"][question.id].include?(option) ? 1 : 0
- params["lettings_log"][question.id].exclude?(option)
+ session["fields"][option] = @log[option] = params[@log.model_name.param_key][question.id].include?(option) ? 1 : 0
+ params[@log.model_name.param_key][question.id].exclude?(option)
end
answered.all?
else
- session["fields"][question.id] = @lettings_log[question.id] = responses_for_page[question.id]
+ session["fields"][question.id] = @log[question.id] = responses_for_page[question.id]
responses_for_page[question.id].nil? || responses_for_page[question.id].blank?
end
end
diff --git a/app/controllers/lettings_logs_controller.rb b/app/controllers/lettings_logs_controller.rb
index a19fdbe6d..eaec5a604 100644
--- a/app/controllers/lettings_logs_controller.rb
+++ b/app/controllers/lettings_logs_controller.rb
@@ -1,55 +1,33 @@
-class LettingsLogsController < ApplicationController
- include Pagy::Backend
- include Modules::LettingsLogsFilter
- include Modules::SearchFilter
-
- skip_before_action :verify_authenticity_token, if: :json_api_request?
- before_action :authenticate, if: :json_api_request?
- before_action :authenticate_user!, unless: :json_api_request?
+class LettingsLogsController < LogsController
before_action :find_resource, except: %i[create index edit]
+ before_action :session_filters, if: :current_user
+ before_action :set_session_filters, if: :current_user
def index
- set_session_filters
-
- all_logs = current_user.lettings_logs
- unpaginated_filtered_logs = filtered_lettings_logs(filtered_collection(all_logs, search_term))
-
respond_to do |format|
format.html do
- @pagy, @lettings_logs = pagy(unpaginated_filtered_logs)
+ all_logs = current_user.lettings_logs
+ unpaginated_filtered_logs = filtered_logs(all_logs, search_term, @session_filters)
+
+ @search_term = search_term
+ @pagy, @logs = pagy(unpaginated_filtered_logs)
@searched = search_term.presence
@total_count = all_logs.size
- end
-
- format.csv do
- send_data byte_order_mark + unpaginated_filtered_logs.to_csv(current_user), filename: "logs-#{Time.zone.now}.csv"
+ render "logs/index"
end
end
end
def create
- lettings_log = LettingsLog.new(lettings_log_params)
- respond_to do |format|
- format.html do
- lettings_log.save!
- redirect_to lettings_log_url(lettings_log)
- end
- format.json do
- if lettings_log.save
- render json: lettings_log, status: :created
- else
- render json: { errors: lettings_log.errors.messages }, status: :unprocessable_entity
- end
- end
- end
+ super { LettingsLog.new(log_params) }
end
def update
- if @lettings_log
- if @lettings_log.update(api_lettings_log_params)
- render json: @lettings_log, status: :ok
+ if @log
+ if @log.update(api_log_params)
+ render json: @log, status: :ok
else
- render json: { errors: @lettings_log.errors.messages }, status: :unprocessable_entity
+ render json: { errors: @log.errors.messages }, status: :unprocessable_entity
end
else
render_not_found_json("Log", params[:id])
@@ -61,8 +39,8 @@ class LettingsLogsController < ApplicationController
# We don't have a dedicated non-editable show view
format.html { edit }
format.json do
- if @lettings_log
- render json: @lettings_log, status: :ok
+ if @log
+ render json: @log, status: :ok
else
render_not_found_json("Log", params[:id])
end
@@ -71,68 +49,51 @@ class LettingsLogsController < ApplicationController
end
def edit
- @lettings_log = current_user.lettings_logs.find_by(id: params[:id])
- if @lettings_log
- render :edit, locals: { current_user: }
+ @log = current_user.lettings_logs.find_by(id: params[:id])
+ if @log
+ render "logs/edit", locals: { current_user: }
else
render_not_found
end
end
def destroy
- if @lettings_log
- if @lettings_log.delete
+ if @log
+ if @log.delete
head :no_content
else
- render json: { errors: @lettings_log.errors.messages }, status: :unprocessable_entity
+ render json: { errors: @log.errors.messages }, status: :unprocessable_entity
end
else
render_not_found_json("Log", params[:id])
end
end
-private
-
- API_ACTIONS = %w[create show update destroy].freeze
+ def download_csv
+ unpaginated_filtered_logs = filtered_logs(current_user.lettings_logs, search_term, @session_filters)
- def search_term
- params["search"]
+ render "download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: email_csv_lettings_logs_path }
end
- def json_api_request?
- API_ACTIONS.include?(request["action"]) && request.format.json?
+ def email_csv
+ all_orgs = params["organisation_select"] == "all"
+ EmailCsvJob.perform_later(current_user, search_term, @session_filters, all_orgs)
+ redirect_to csv_confirmation_lettings_logs_path
end
- def authenticate
- http_basic_authenticate_or_request_with name: ENV["API_USER"], password: ENV["API_KEY"]
- end
+ def csv_confirmation; end
- def lettings_log_params
- if current_user && !current_user.support?
- org_params.merge(api_lettings_log_params)
- else
- api_lettings_log_params
- end
- end
+private
- def org_params
- {
- "owning_organisation_id" => current_user.organisation.id,
- "managing_organisation_id" => current_user.organisation.id,
- "created_by_id" => current_user.id,
- }
+ def permitted_log_params
+ params.require(:lettings_log).permit(LettingsLog.editable_fields)
end
- def api_lettings_log_params
- return {} unless params[:lettings_log]
-
- permitted = params.require(:lettings_log).permit(LettingsLog.editable_fields)
- owning_id = permitted["owning_organisation_id"]
- permitted["owning_organisation"] = Organisation.find(owning_id) if owning_id
- permitted
+ def find_resource
+ @log = LettingsLog.find_by(id: params[:id])
end
- def find_resource
- @lettings_log = LettingsLog.find_by(id: params[:id])
+ def post_create_redirect_url(log)
+ lettings_log_url(log)
end
end
diff --git a/app/controllers/logs_controller.rb b/app/controllers/logs_controller.rb
new file mode 100644
index 000000000..3982d4a54
--- /dev/null
+++ b/app/controllers/logs_controller.rb
@@ -0,0 +1,73 @@
+class LogsController < ApplicationController
+ include Pagy::Backend
+ include Modules::LogsFilter
+ include Modules::SearchFilter
+
+ skip_before_action :verify_authenticity_token, if: :json_api_request?
+ before_action :authenticate, if: :json_api_request?
+ before_action :authenticate_user!, unless: :json_api_request?
+
+private
+
+ def create
+ log = yield
+ raise "Caller must pass a block that implements model creation" if log.blank?
+
+ respond_to do |format|
+ format.html do
+ log.save!
+ redirect_to post_create_redirect_url(log)
+ end
+ format.json do
+ if log.save
+ render json: log, status: :created
+ else
+ render json: { errors: log.errors.messages }, status: :unprocessable_entity
+ end
+ end
+ end
+ end
+
+ def post_create_redirect_url
+ raise "implement in sub class"
+ end
+
+ API_ACTIONS = %w[create show update destroy].freeze
+
+ def json_api_request?
+ API_ACTIONS.include?(request["action"]) && request.format.json?
+ end
+
+ def authenticate
+ http_basic_authenticate_or_request_with name: ENV["API_USER"], password: ENV["API_KEY"]
+ end
+
+ def log_params
+ if current_user && !current_user.support?
+ org_params.merge(api_log_params)
+ else
+ api_log_params
+ end
+ end
+
+ def api_log_params
+ return {} unless params[:lettings_log] || params[:sales_log]
+
+ permitted = permitted_log_params
+ owning_id = permitted["owning_organisation_id"]
+ permitted["owning_organisation"] = Organisation.find(owning_id) if owning_id
+ permitted
+ end
+
+ def org_params
+ {
+ "owning_organisation_id" => current_user.organisation.id,
+ "managing_organisation_id" => current_user.organisation.id,
+ "created_by_id" => current_user.id,
+ }
+ end
+
+ def search_term
+ params["search"]
+ end
+end
diff --git a/app/controllers/modules/lettings_logs_filter.rb b/app/controllers/modules/lettings_logs_filter.rb
deleted file mode 100644
index c074e48bb..000000000
--- a/app/controllers/modules/lettings_logs_filter.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-module Modules::LettingsLogsFilter
- def filtered_lettings_logs(logs)
- if session[:lettings_logs_filters].present?
- filters = JSON.parse(session[:lettings_logs_filters])
- filters.each do |category, values|
- next if Array(values).reject(&:empty?).blank?
- next if category == "organisation" && params["organisation_select"] == "all"
-
- logs = logs.public_send("filter_by_#{category}", values, current_user)
- end
- end
- logs = logs.order(created_at: :desc)
- current_user.support? ? logs.all.includes(:owning_organisation, :managing_organisation) : logs
- end
-
- def set_session_filters(specific_org: false)
- new_filters = session[:lettings_logs_filters].present? ? JSON.parse(session[:lettings_logs_filters]) : {}
- current_user.lettings_logs_filters(specific_org:).each { |filter| new_filters[filter] = params[filter] if params[filter].present? }
- new_filters = new_filters.except("organisation") if params["organisation_select"] == "all"
-
- session[:lettings_logs_filters] = new_filters.to_json
- end
-end
diff --git a/app/controllers/modules/logs_filter.rb b/app/controllers/modules/logs_filter.rb
new file mode 100644
index 000000000..7c60bb027
--- /dev/null
+++ b/app/controllers/modules/logs_filter.rb
@@ -0,0 +1,21 @@
+module Modules::LogsFilter
+ def filtered_logs(logs, search_term, filters)
+ all_orgs = params["organisation_select"] == "all"
+ FilterService.filter_logs(logs, search_term, filters, all_orgs, current_user)
+ end
+
+ def load_session_filters(specific_org: false)
+ current_filters = session[:logs_filters]
+ new_filters = current_filters.present? ? JSON.parse(current_filters) : {}
+ current_user.logs_filters(specific_org:).each { |filter| new_filters[filter] = params[filter] if params[filter].present? }
+ params["organisation_select"] == "all" ? new_filters.except("organisation") : new_filters
+ end
+
+ def session_filters(specific_org: false)
+ @session_filters ||= load_session_filters(specific_org:)
+ end
+
+ def set_session_filters
+ session[:logs_filters] = @session_filters.to_json
+ end
+end
diff --git a/app/controllers/modules/search_filter.rb b/app/controllers/modules/search_filter.rb
index c32298987..82bf0d6c0 100644
--- a/app/controllers/modules/search_filter.rb
+++ b/app/controllers/modules/search_filter.rb
@@ -1,13 +1,9 @@
module Modules::SearchFilter
def filtered_collection(base_collection, search_term = nil)
- if search_term.present?
- base_collection.search_by(search_term)
- else
- base_collection
- end
+ FilterService.filter_by_search(base_collection, search_term)
end
def filtered_users(base_collection, search_term = nil)
- filtered_collection(base_collection, search_term).includes(:organisation)
+ FilterService.filter_by_search(base_collection, search_term).includes(:organisation)
end
end
diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb
index 1cf181788..7f2094926 100644
--- a/app/controllers/organisations_controller.rb
+++ b/app/controllers/organisations_controller.rb
@@ -1,11 +1,13 @@
class OrganisationsController < ApplicationController
include Pagy::Backend
- include Modules::LettingsLogsFilter
+ include Modules::LogsFilter
include Modules::SearchFilter
before_action :authenticate_user!
before_action :find_resource, except: %i[index new create]
before_action :authenticate_scope!, except: [:index]
+ before_action -> { session_filters(specific_org: true) }, if: -> { current_user.support? }
+ before_action :set_session_filters, if: -> { current_user.support? }
def index
redirect_to organisation_path(current_user.organisation) unless current_user.support?
@@ -87,27 +89,49 @@ class OrganisationsController < ApplicationController
end
end
- def logs
- if current_user.support?
- set_session_filters(specific_org: true)
+ def lettings_logs
+ organisation_logs = LettingsLog.where(owning_organisation_id: @organisation.id)
+ unpaginated_filtered_logs = filtered_logs(organisation_logs, search_term, @session_filters)
- organisation_logs = LettingsLog.all.where(owning_organisation_id: @organisation.id)
- unpaginated_filtered_logs = filtered_lettings_logs(filtered_collection(organisation_logs, search_term))
+ respond_to do |format|
+ format.html do
+ @search_term = search_term
+ @pagy, @logs = pagy(unpaginated_filtered_logs)
+ @searched = search_term.presence
+ @total_count = organisation_logs.size
+ render "logs", layout: "application"
+ end
+ end
+ end
- respond_to do |format|
- format.html do
- @pagy, @lettings_logs = pagy(unpaginated_filtered_logs)
- @searched = search_term.presence
- @total_count = organisation_logs.size
- render "logs", layout: "application"
- end
+ def download_csv
+ organisation_logs = LettingsLog.all.where(owning_organisation_id: @organisation.id)
+ unpaginated_filtered_logs = filtered_logs(organisation_logs, search_term, @session_filters)
- format.csv do
- send_data byte_order_mark + unpaginated_filtered_logs.to_csv, filename: "logs-#{@organisation.name}-#{Time.zone.now}.csv"
- end
+ render "logs/download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: logs_email_csv_organisation_path }
+ end
+
+ def email_csv
+ EmailCsvJob.perform_later(current_user, search_term, @session_filters, false, @organisation)
+ redirect_to logs_csv_confirmation_organisation_path
+ end
+
+ def sales_logs
+ organisation_logs = SalesLog.where(owning_organisation_id: @organisation.id)
+ unpaginated_filtered_logs = filtered_logs(organisation_logs, search_term, @session_filters)
+
+ respond_to do |format|
+ format.html do
+ @search_term = search_term
+ @pagy, @logs = pagy(unpaginated_filtered_logs)
+ @searched = search_term.presence
+ @total_count = organisation_logs.size
+ render "logs", layout: "application"
+ end
+
+ format.csv do
+ send_data byte_order_mark + unpaginated_filtered_logs.to_csv, filename: "sales-logs-#{@organisation.name}-#{Time.zone.now}.csv"
end
- else
- redirect_to(lettings_logs_path)
end
end
@@ -122,7 +146,7 @@ private
end
def authenticate_scope!
- if %w[create new].include? action_name
+ if %w[create new lettings_logs download_csv email_csv].include? action_name
head :unauthorized and return unless current_user.support?
elsif current_user.organisation != @organisation && !current_user.support?
render_not_found
diff --git a/app/controllers/sales_logs_controller.rb b/app/controllers/sales_logs_controller.rb
new file mode 100644
index 000000000..8a6c9937f
--- /dev/null
+++ b/app/controllers/sales_logs_controller.rb
@@ -0,0 +1,46 @@
+class SalesLogsController < LogsController
+ before_action :session_filters, if: :current_user
+ before_action :set_session_filters, if: :current_user
+
+ def create
+ super { SalesLog.new(log_params) }
+ end
+
+ def index
+ respond_to do |format|
+ format.html do
+ all_logs = current_user.sales_logs
+ unpaginated_filtered_logs = filtered_logs(all_logs, search_term, @session_filters)
+
+ @search_term = search_term
+ @pagy, @logs = pagy(unpaginated_filtered_logs)
+ @searched = search_term.presence
+ @total_count = all_logs.size
+ render "logs/index"
+ end
+ end
+ end
+
+ def show
+ respond_to do |format|
+ format.html { edit }
+ end
+ end
+
+ def edit
+ @log = current_user.sales_logs.find_by(id: params[:id])
+ if @log
+ render "logs/edit", locals: { current_user: }
+ else
+ render_not_found
+ end
+ end
+
+ def post_create_redirect_url(log)
+ sales_log_url(log)
+ end
+
+ def permitted_log_params
+ params.require(:sales_log).permit(SalesLog.editable_fields)
+ end
+end
diff --git a/app/helpers/check_answers_helper.rb b/app/helpers/check_answers_helper.rb
index ebb4bb034..67f582c1c 100644
--- a/app/helpers/check_answers_helper.rb
+++ b/app/helpers/check_answers_helper.rb
@@ -28,6 +28,10 @@ module CheckAnswersHelper
subsection.applicable_questions(lettings_log).map(&:check_answers_card_number).compact.length.positive?
end
+ def next_incomplete_section_path(log, redirect_path)
+ "#{log.class.name.underscore}_#{redirect_path.underscore.tr('/', '_')}_path"
+ end
+
private
def answered_questions_count(subsection, lettings_log, current_user)
diff --git a/app/helpers/filters_helper.rb b/app/helpers/filters_helper.rb
index a8f5582a7..507274be0 100644
--- a/app/helpers/filters_helper.rb
+++ b/app/helpers/filters_helper.rb
@@ -1,8 +1,8 @@
module FiltersHelper
def filter_selected?(filter, value)
- return false unless session[:lettings_logs_filters]
+ return false unless session[:logs_filters]
- selected_filters = JSON.parse(session[:lettings_logs_filters])
+ selected_filters = JSON.parse(session[:logs_filters])
return true if selected_filters.blank? && filter == "user" && value == :all
return true if !selected_filters.key?("organisation") && filter == "organisation_select" && value == :all
return true if selected_filters["organisation"].present? && filter == "organisation_select" && value == :specific_org
@@ -18,8 +18,8 @@ module FiltersHelper
end
def selected_option(filter)
- return false unless session[:lettings_logs_filters]
+ return false unless session[:logs_filters]
- JSON.parse(session[:lettings_logs_filters])[filter] || ""
+ JSON.parse(session[:logs_filters])[filter] || ""
end
end
diff --git a/app/helpers/navigation_items_helper.rb b/app/helpers/navigation_items_helper.rb
index 244aaf1ab..3ac422c2d 100644
--- a/app/helpers/navigation_items_helper.rb
+++ b/app/helpers/navigation_items_helper.rb
@@ -6,39 +6,44 @@ module NavigationItemsHelper
[
NavigationItem.new("Organisations", organisations_path, organisations_current?(path)),
NavigationItem.new("Users", "/users", users_current?(path)),
- NavigationItem.new("Logs", lettings_logs_path, logs_current?(path)),
+ NavigationItem.new("Lettings logs", lettings_logs_path, lettings_logs_current?(path)),
+ FeatureToggle.sales_log_enabled? ? NavigationItem.new("Sales logs", sales_logs_path, sales_logs_current?(path)) : nil,
NavigationItem.new("Schemes", "/schemes", supported_housing_schemes_current?(path)),
- ]
+ ].compact
elsif current_user.data_coordinator? && current_user.organisation.holds_own_stock?
[
- NavigationItem.new("Logs", lettings_logs_path, logs_current?(path)),
+ NavigationItem.new("Lettings logs", lettings_logs_path, lettings_logs_current?(path)),
+ FeatureToggle.sales_log_enabled? ? NavigationItem.new("Sales logs", sales_logs_path, sales_logs_current?(path)) : nil,
NavigationItem.new("Schemes", "/schemes", subnav_supported_housing_schemes_path?(path)),
NavigationItem.new("Users", users_organisation_path(current_user.organisation), subnav_users_path?(path)),
NavigationItem.new("About your organisation", "/organisations/#{current_user.organisation.id}", subnav_details_path?(path)),
- ]
+ ].compact
else
[
- NavigationItem.new("Logs", lettings_logs_path, logs_current?(path)),
+ NavigationItem.new("Lettings logs", lettings_logs_path, lettings_logs_current?(path)),
+ FeatureToggle.sales_log_enabled? ? NavigationItem.new("Sales logs", sales_logs_path, sales_logs_current?(path)) : nil,
NavigationItem.new("Users", users_organisation_path(current_user.organisation), subnav_users_path?(path)),
NavigationItem.new("About your organisation", "/organisations/#{current_user.organisation.id}", subnav_details_path?(path)),
- ]
+ ].compact
end
end
def secondary_items(path, current_organisation_id)
if current_user.organisation.holds_own_stock?
[
- NavigationItem.new("Logs", "/organisations/#{current_organisation_id}/logs", subnav_logs_path?(path)),
+ NavigationItem.new("Lettings logs", "/organisations/#{current_organisation_id}/lettings-logs", subnav_logs_path?(path)),
+ FeatureToggle.sales_log_enabled? ? NavigationItem.new("Sales logs", "/organisations/#{current_organisation_id}/sales-logs", sales_logs_current?(path)) : nil,
NavigationItem.new("Schemes", "/organisations/#{current_organisation_id}/schemes", subnav_supported_housing_schemes_path?(path)),
NavigationItem.new("Users", "/organisations/#{current_organisation_id}/users", subnav_users_path?(path)),
NavigationItem.new("About this organisation", "/organisations/#{current_organisation_id}", subnav_details_path?(path)),
- ]
+ ].compact
else
[
- NavigationItem.new("Logs", "/organisations/#{current_organisation_id}/logs", subnav_logs_path?(path)),
+ NavigationItem.new("Lettings logs", "/organisations/#{current_organisation_id}/lettings-logs", subnav_logs_path?(path)),
+ FeatureToggle.sales_log_enabled? ? NavigationItem.new("Sales logs", "/organisations/#{current_organisation_id}/sales-logs", sales_logs_current?(path)) : nil,
NavigationItem.new("Users", "/organisations/#{current_organisation_id}/users", subnav_users_path?(path)),
NavigationItem.new("About this organisation", "/organisations/#{current_organisation_id}", subnav_details_path?(path)),
- ]
+ ].compact
end
end
@@ -51,8 +56,12 @@ module NavigationItemsHelper
private
- def logs_current?(path)
- path == "/logs"
+ def lettings_logs_current?(path)
+ path == "/lettings-logs"
+ end
+
+ def sales_logs_current?(path)
+ path == "/sales-logs"
end
def users_current?(path)
@@ -76,7 +85,7 @@ private
end
def subnav_logs_path?(path)
- path.include?("/organisations") && path.include?("/logs")
+ path.include?("/organisations") && path.include?("/lettings-logs")
end
def subnav_details_path?(path)
diff --git a/app/helpers/tasklist_helper.rb b/app/helpers/tasklist_helper.rb
index e4308d97e..7ab955a2e 100644
--- a/app/helpers/tasklist_helper.rb
+++ b/app/helpers/tasklist_helper.rb
@@ -1,36 +1,36 @@
module TasklistHelper
include GovukLinkHelper
- def get_next_incomplete_section(lettings_log)
- lettings_log.form.subsections.find { |subsection| subsection.is_incomplete?(lettings_log) }
+ def get_next_incomplete_section(log)
+ log.form.subsections.find { |subsection| subsection.is_incomplete?(log) }
end
- def get_subsections_count(lettings_log, status = :all)
- return lettings_log.form.subsections.count { |subsection| subsection.applicable_questions(lettings_log).count.positive? } if status == :all
+ def get_subsections_count(log, status = :all)
+ return log.form.subsections.count { |subsection| subsection.applicable_questions(log).count.positive? } if status == :all
- lettings_log.form.subsections.count { |subsection| subsection.status(lettings_log) == status && subsection.applicable_questions(lettings_log).count.positive? }
+ log.form.subsections.count { |subsection| subsection.status(log) == status && subsection.applicable_questions(log).count.positive? }
end
- def next_page_or_check_answers(subsection, lettings_log, current_user)
- path = if subsection.is_started?(lettings_log)
- "lettings_log_#{subsection.id}_check_answers_path"
+ def next_page_or_check_answers(subsection, log, current_user)
+ path = if subsection.is_started?(log)
+ "#{log.class.name.underscore}_#{subsection.id}_check_answers_path"
else
- "lettings_log_#{next_question_page(subsection, lettings_log, current_user)}_path"
+ "#{log.class.name.underscore}_#{next_question_page(subsection, log, current_user)}_path"
end
- send(path, lettings_log)
+ send(path, log)
end
- def next_question_page(subsection, lettings_log, current_user)
- if subsection.pages.first.routed_to?(lettings_log, current_user)
+ def next_question_page(subsection, log, current_user)
+ if subsection.pages.first.routed_to?(log, current_user)
subsection.pages.first.id
else
- lettings_log.form.next_page(subsection.pages.first, lettings_log, current_user)
+ log.form.next_page(subsection.pages.first, log, current_user)
end
end
- def subsection_link(subsection, lettings_log, current_user)
- if subsection.status(lettings_log) != :cannot_start_yet
- next_page_path = next_page_or_check_answers(subsection, lettings_log, current_user).to_s
+ def subsection_link(subsection, log, current_user)
+ if subsection.status(log) != :cannot_start_yet
+ next_page_path = next_page_or_check_answers(subsection, log, current_user).to_s
govuk_link_to(subsection.label, next_page_path.dasherize, aria: { describedby: subsection.id.dasherize })
else
subsection.label
diff --git a/app/jobs/email_csv_job.rb b/app/jobs/email_csv_job.rb
new file mode 100644
index 000000000..c7009645f
--- /dev/null
+++ b/app/jobs/email_csv_job.rb
@@ -0,0 +1,21 @@
+class EmailCsvJob < ApplicationJob
+ queue_as :default
+
+ BYTE_ORDER_MARK = "\uFEFF".freeze # Required to ensure Excel always reads CSV as UTF-8
+
+ EXPIRATION_TIME = 3.hours.to_i
+
+ def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil) # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params
+ unfiltered_logs = organisation.present? && user.support? ? LettingsLog.where(owning_organisation_id: organisation.id) : user.lettings_logs
+ filtered_logs = FilterService.filter_logs(unfiltered_logs, search_term, filters, all_orgs, user)
+
+ filename = organisation.present? ? "logs-#{organisation.name}-#{Time.zone.now}.csv" : "logs-#{Time.zone.now}.csv"
+
+ storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"])
+ storage_service.write_file(filename, BYTE_ORDER_MARK + filtered_logs.to_csv(user))
+
+ url = storage_service.get_presigned_url(filename, EXPIRATION_TIME)
+
+ CsvDownloadMailer.new.send_csv_download_mail(user, url, EXPIRATION_TIME)
+ end
+end
diff --git a/app/mailers/csv_download_mailer.rb b/app/mailers/csv_download_mailer.rb
new file mode 100644
index 000000000..619d5e922
--- /dev/null
+++ b/app/mailers/csv_download_mailer.rb
@@ -0,0 +1,11 @@
+class CsvDownloadMailer < NotifyMailer
+ CSV_DOWNLOAD_TEMPLATE_ID = "7890e3b9-8c0d-4d08-bafe-427fd7cd95bf".freeze
+
+ def send_csv_download_mail(user, link, duration)
+ send_email(
+ user.email,
+ CSV_DOWNLOAD_TEMPLATE_ID,
+ { name: user.name, link:, duration: ActiveSupport::Duration.build(duration).inspect },
+ )
+ end
+end
diff --git a/app/mailers/notify_mailer.rb b/app/mailers/notify_mailer.rb
new file mode 100644
index 000000000..506df5818
--- /dev/null
+++ b/app/mailers/notify_mailer.rb
@@ -0,0 +1,37 @@
+class NotifyMailer
+ require "notifications/client"
+
+ def notify_client
+ @notify_client ||= ::Notifications::Client.new(ENV["GOVUK_NOTIFY_API_KEY"])
+ end
+
+ def send_email(email, template_id, personalisation)
+ return true if intercept_send?(email)
+
+ notify_client.send_email(
+ email_address: email,
+ template_id:,
+ personalisation:,
+ )
+ end
+
+ def personalisation(record, token, url, username: false)
+ {
+ name: record.name || record.email,
+ email: username || record.email,
+ organisation: record.respond_to?(:organisation) ? record.organisation.name : "",
+ link: "#{url}#{token}",
+ }
+ end
+
+ def intercept_send?(email)
+ return false unless email_allowlist
+
+ email_domain = email.split("@").last.downcase
+ !(Rails.env.production? || Rails.env.test?) && email_allowlist.exclude?(email_domain)
+ end
+
+ def email_allowlist
+ Rails.application.credentials[:email_allowlist]
+ end
+end
diff --git a/app/models/bulk_upload.rb b/app/models/bulk_upload.rb
index edafadbfa..183dc274e 100644
--- a/app/models/bulk_upload.rb
+++ b/app/models/bulk_upload.rb
@@ -184,7 +184,6 @@ class BulkUpload
rent_type: row[130],
irproduct_other: row[131],
# data_protection: row[132],
- sale_or_letting: "letting",
declaration: 1,
}
end
diff --git a/app/models/derived_variables/lettings_log_variables.rb b/app/models/derived_variables/lettings_log_variables.rb
index 112ab420f..f192033f0 100644
--- a/app/models/derived_variables/lettings_log_variables.rb
+++ b/app/models/derived_variables/lettings_log_variables.rb
@@ -58,16 +58,12 @@ module DerivedVariables::LettingsLogVariables
self.hhtype = household_type
self.new_old = new_or_existing_tenant
- self.vacdays = property_vacant_days
- if is_supported_housing?
- if location
- self.wchair = location.mobility_type_before_type_cast == "W" ? 1 : 2
- end
- if is_renewal?
- self.voiddate = startdate
- end
+ if is_supported_housing? && location
+ self.wchair = location.mobility_type_before_type_cast == "W" ? 1 : 2
end
+ self.voiddate = startdate if is_renewal?
+ self.vacdays = property_vacant_days
set_housingneeds_fields if housingneeds?
end
diff --git a/app/models/form.rb b/app/models/form.rb
index 6fa92bba0..22321f431 100644
--- a/app/models/form.rb
+++ b/app/models/form.rb
@@ -3,22 +3,38 @@ class Form
:start_date, :end_date, :type, :name, :setup_definition,
:setup_sections, :form_sections
- include Form::Setup
-
- def initialize(form_path, name)
- raise "No form definition file exists for given year".freeze unless File.exist?(form_path)
-
- @name = name
- @setup_sections = [Form::Setup::Sections::Setup.new(nil, nil, self)]
- @form_definition = JSON.parse(File.open(form_path).read)
- @form_sections = form_definition["sections"].map { |id, s| Form::Section.new(id, s, self) }
- @type = form_definition["form_type"]
- @sections = setup_sections + form_sections
- @subsections = sections.flat_map(&:subsections)
- @pages = subsections.flat_map(&:pages)
- @questions = pages.flat_map(&:questions)
- @start_date = Time.iso8601(form_definition["start_date"])
- @end_date = Time.iso8601(form_definition["end_date"])
+ def initialize(form_path, start_year = "", sections_in_form = [], type = "lettings")
+ if type == "sales"
+ @setup_sections = [Form::Sales::Sections::Setup.new(nil, nil, self)]
+ @form_sections = sections_in_form.map { |sec| sec.new(nil, nil, self) }
+ @type = "sales"
+ @sections = setup_sections + form_sections
+ @subsections = sections.flat_map(&:subsections)
+ @pages = subsections.flat_map(&:pages)
+ @questions = pages.flat_map(&:questions)
+ @start_date = Time.zone.local(start_year, 4, 1)
+ @end_date = Time.zone.local(start_year + 1, 7, 1)
+ @form_definition = {
+ "form_type" => type,
+ "start_date" => start_date,
+ "end_date" => end_date,
+ "sections" => sections,
+ }
+ else
+ raise "No form definition file exists for given year".freeze unless File.exist?(form_path)
+
+ @setup_sections = [Form::Lettings::Sections::Setup.new(nil, nil, self)]
+ @form_definition = JSON.parse(File.open(form_path).read)
+ @form_sections = form_definition["sections"].map { |id, s| Form::Section.new(id, s, self) }
+ @type = form_definition["form_type"]
+ @sections = setup_sections + form_sections
+ @subsections = sections.flat_map(&:subsections)
+ @pages = subsections.flat_map(&:pages)
+ @questions = pages.flat_map(&:questions)
+ @start_date = Time.iso8601(form_definition["start_date"])
+ @end_date = Time.iso8601(form_definition["end_date"])
+ end
+ @name = "#{start_date.year}_#{end_date.year}_#{type}"
end
def get_subsection(id)
@@ -29,9 +45,9 @@ class Form
pages.find { |p| p.id == id.to_s.underscore }
end
- def get_question(id, lettings_log, current_user = nil)
+ def get_question(id, log, current_user = nil)
all_questions = questions.select { |q| q.id == id.to_s.underscore }
- routed_question = all_questions.find { |q| q.page.routed_to?(lettings_log, current_user) } if lettings_log
+ routed_question = all_questions.find { |q| q.page.routed_to?(log, current_user) } if log
routed_question || all_questions[0]
end
@@ -39,47 +55,51 @@ class Form
subsections.find { |s| s.pages.find { |p| p.id == page.id } }
end
- def next_page(page, lettings_log, current_user)
+ def next_page(page, log, current_user)
page_ids = subsection_for_page(page).pages.map(&:id)
page_index = page_ids.index(page.id)
- page_id = if page.id.include?("value_check") && lettings_log[page.questions[0].id] == 1 && page.routed_to?(lettings_log, current_user)
- previous_page(page_ids, page_index, lettings_log, current_user)
+ page_id = if page.id.include?("value_check") && log[page.questions[0].id] == 1 && page.routed_to?(log, current_user)
+ previous_page(page_ids, page_index, log, current_user)
else
page_ids[page_index + 1]
end
nxt_page = get_page(page_id)
return :check_answers if nxt_page.nil?
- return nxt_page.id if nxt_page.routed_to?(lettings_log, current_user)
+ return nxt_page.id if nxt_page.routed_to?(log, current_user)
- next_page(nxt_page, lettings_log, current_user)
+ next_page(nxt_page, log, current_user)
end
- def next_page_redirect_path(page, lettings_log, current_user)
- nxt_page = next_page(page, lettings_log, current_user)
+ def next_page_redirect_path(page, log, current_user)
+ nxt_page = next_page(page, log, current_user)
if nxt_page == :check_answers
- "lettings_log_#{subsection_for_page(page).id}_check_answers_path"
+ "#{type}_log_#{subsection_for_page(page).id}_check_answers_path"
else
- "lettings_log_#{nxt_page}_path"
+ "#{type}_log_#{nxt_page}_path"
end
end
- def next_incomplete_section_redirect_path(subsection, lettings_log)
+ def cancel_path(page, log)
+ "#{log.class.name.underscore}_#{page.subsection.id}_check_answers_path"
+ end
+
+ def next_incomplete_section_redirect_path(subsection, log)
subsection_ids = subsections.map(&:id)
- if lettings_log.status == "completed"
+ if log.status == "completed"
return first_question_in_last_subsection(subsection_ids)
end
- next_subsection = next_subsection(subsection, lettings_log, subsection_ids)
+ next_subsection = next_subsection(subsection, log, subsection_ids)
- case next_subsection.status(lettings_log)
+ case next_subsection.status(log)
when :completed
- next_incomplete_section_redirect_path(next_subsection, lettings_log)
+ next_incomplete_section_redirect_path(next_subsection, log)
when :in_progress
"#{next_subsection.id}/check_answers".dasherize
when :not_started
- first_question_in_subsection = next_subsection.pages.find { |page| page.routed_to?(lettings_log, nil) }.id
+ first_question_in_subsection = next_subsection.pages.find { |page| page.routed_to?(log, nil) }.id
first_question_in_subsection.to_s.dasherize
else
"error"
@@ -92,21 +112,21 @@ class Form
first_question_in_subsection.to_s.dasherize
end
- def next_subsection(subsection, lettings_log, subsection_ids)
+ def next_subsection(subsection, log, subsection_ids)
next_subsection_id_index = subsection_ids.index(subsection.id) + 1
next_subsection = get_subsection(subsection_ids[next_subsection_id_index])
- if subsection_ids[subsection_ids.length - 1] == subsection.id && lettings_log.status != "completed"
+ if subsection_ids[subsection_ids.length - 1] == subsection.id && log.status != "completed"
next_subsection = get_subsection(subsection_ids[0])
end
next_subsection
end
- def all_subsections_except_declaration_completed?(lettings_log)
+ def all_subsections_except_declaration_completed?(log)
subsection_ids = subsections.map(&:id)
subsection_ids.delete_at(subsection_ids.length - 1)
- return true if subsection_ids.all? { |subsection_id| get_subsection(subsection_id).status(lettings_log) == :completed }
+ return true if subsection_ids.all? { |subsection_id| get_subsection(subsection_id).status(log) == :completed }
false
end
@@ -118,26 +138,50 @@ class Form
}.flatten
end
- def invalidated_pages(lettings_log, current_user = nil)
- pages.reject { |p| p.routed_to?(lettings_log, current_user) }
+ def invalidated_pages(log, current_user = nil)
+ pages.reject { |p| p.routed_to?(log, current_user) }
end
- def invalidated_questions(lettings_log)
- invalidated_page_questions(lettings_log) + invalidated_conditional_questions(lettings_log)
+ def invalidated_questions(log)
+ invalidated_page_questions(log) + invalidated_conditional_questions(log)
end
- def invalidated_page_questions(lettings_log, current_user = nil)
- # we're already treating these fields as a special case and reset their values upon saving a lettings_log
+ def invalidated_page_questions(log, current_user = nil)
+ # we're already treating these fields as a special case and reset their values upon saving a log
callback_questions = %w[postcode_known la ppcodenk previous_la_known prevloc postcode_full ppostcode_full location_id]
- questions.reject { |q| q.page.routed_to?(lettings_log, current_user) || q.derived? || callback_questions.include?(q.id) } || []
+ questions.reject { |q| q.page.routed_to?(log, current_user) || q.derived? || callback_questions.include?(q.id) } || []
+ end
+
+ def reset_not_routed_questions(log)
+ enabled_questions = enabled_page_questions(log)
+ enabled_question_ids = enabled_questions.map(&:id)
+
+ invalidated_page_questions(log).each do |question|
+ if %w[radio checkbox].include?(question.type)
+ enabled_answer_options = enabled_question_ids.include?(question.id) ? enabled_questions.find { |q| q.id == question.id }.answer_options : {}
+ current_answer_option_valid = enabled_answer_options.present? ? enabled_answer_options.key?(log.public_send(question.id).to_s) : false
+ if !current_answer_option_valid && log.respond_to?(question.id.to_s)
+ Rails.logger.debug("Cleared #{question.id} value")
+ log.public_send("#{question.id}=", nil)
+ else
+ (question.answer_options.keys - enabled_answer_options.keys).map do |invalid_answer_option|
+ Rails.logger.debug("Cleared #{invalid_answer_option} value")
+ log.public_send("#{invalid_answer_option}=", nil) if log.respond_to?(invalid_answer_option)
+ end
+ end
+ else
+ Rails.logger.debug("Cleared #{question.id} value")
+ log.public_send("#{question.id}=", nil) unless enabled_question_ids.include?(question.id)
+ end
+ end
end
- def enabled_page_questions(lettings_log)
- questions - invalidated_page_questions(lettings_log)
+ def enabled_page_questions(log)
+ questions - invalidated_page_questions(log)
end
- def invalidated_conditional_questions(lettings_log)
- questions.reject { |q| q.enabled?(lettings_log) } || []
+ def invalidated_conditional_questions(log)
+ questions.reject { |q| q.enabled?(log) } || []
end
def readonly_questions
@@ -148,18 +192,18 @@ class Form
questions.select { |q| q.type == "numeric" }
end
- def previous_page(page_ids, page_index, lettings_log, current_user)
+ def previous_page(page_ids, page_index, log, current_user)
prev_page = get_page(page_ids[page_index - 1])
- return prev_page.id if prev_page.routed_to?(lettings_log, current_user)
+ return prev_page.id if prev_page.routed_to?(log, current_user)
- previous_page(page_ids, page_index - 1, lettings_log, current_user)
+ previous_page(page_ids, page_index - 1, log, current_user)
end
- def send_chain(arr, lettings_log)
- Array(arr).inject(lettings_log) { |o, a| o.public_send(*a) }
+ def send_chain(arr, log)
+ Array(arr).inject(log) { |o, a| o.public_send(*a) }
end
- def depends_on_met(depends_on, lettings_log)
+ def depends_on_met(depends_on, log)
return true unless depends_on
depends_on.any? do |conditions_set|
@@ -169,12 +213,12 @@ class Form
if value.is_a?(Hash) && value.key?("operator")
operator = value["operator"]
operand = value["operand"]
- lettings_log[question]&.send(operator, operand)
+ log[question]&.send(operator, operand)
else
parts = question.split(".")
- lettings_log_value = send_chain(parts, lettings_log)
+ log_value = send_chain(parts, log)
- value.nil? ? lettings_log_value == value : !lettings_log_value.nil? && lettings_log_value == value
+ value.nil? ? log_value == value : !log_value.nil? && log_value == value
end
end
end
diff --git a/app/models/form/setup/pages/created_by.rb b/app/models/form/common/pages/created_by.rb
similarity index 59%
rename from app/models/form/setup/pages/created_by.rb
rename to app/models/form/common/pages/created_by.rb
index 8878e9b75..e02f97740 100644
--- a/app/models/form/setup/pages/created_by.rb
+++ b/app/models/form/common/pages/created_by.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Pages::CreatedBy < ::Form::Page
+class Form::Common::Pages::CreatedBy < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "created_by"
@@ -9,11 +9,11 @@ class Form::Setup::Pages::CreatedBy < ::Form::Page
def questions
@questions ||= [
- Form::Setup::Questions::CreatedById.new(nil, nil, self),
+ Form::Common::Questions::CreatedById.new(nil, nil, self),
]
end
- def routed_to?(_lettings_log, current_user)
+ def routed_to?(_log, current_user)
!!current_user&.support?
end
end
diff --git a/app/models/form/setup/pages/organisation.rb b/app/models/form/common/pages/organisation.rb
similarity index 57%
rename from app/models/form/setup/pages/organisation.rb
rename to app/models/form/common/pages/organisation.rb
index 2b0c56d12..48b157a56 100644
--- a/app/models/form/setup/pages/organisation.rb
+++ b/app/models/form/common/pages/organisation.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Pages::Organisation < ::Form::Page
+class Form::Common::Pages::Organisation < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "organisation"
@@ -9,11 +9,11 @@ class Form::Setup::Pages::Organisation < ::Form::Page
def questions
@questions ||= [
- Form::Setup::Questions::OwningOrganisationId.new(nil, nil, self),
+ Form::Common::Questions::OwningOrganisationId.new(nil, nil, self),
]
end
- def routed_to?(_lettings_log, current_user)
+ def routed_to?(_log, current_user)
!!current_user&.support?
end
end
diff --git a/app/models/form/setup/questions/created_by_id.rb b/app/models/form/common/questions/created_by_id.rb
similarity index 67%
rename from app/models/form/setup/questions/created_by_id.rb
rename to app/models/form/common/questions/created_by_id.rb
index 85567f882..b0558e5e2 100644
--- a/app/models/form/setup/questions/created_by_id.rb
+++ b/app/models/form/common/questions/created_by_id.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Questions::CreatedById < ::Form::Question
+class Form::Common::Questions::CreatedById < ::Form::Question
def initialize(id, hsh, page)
super
@id = "created_by_id"
@@ -19,10 +19,10 @@ class Form::Setup::Questions::CreatedById < ::Form::Question
end
end
- def displayed_answer_options(lettings_log)
- return answer_options unless lettings_log.owning_organisation
+ def displayed_answer_options(log)
+ return answer_options unless log.owning_organisation
- user_ids = lettings_log.owning_organisation.users.pluck(:id) + [""]
+ user_ids = log.owning_organisation.users.pluck(:id) + [""]
answer_options.select { |k, _v| user_ids.include?(k) }
end
@@ -32,7 +32,7 @@ class Form::Setup::Questions::CreatedById < ::Form::Question
answer_options[value]
end
- def hidden_in_check_answers?(_lettings_log, current_user)
+ def hidden_in_check_answers?(_log, current_user)
!current_user.support?
end
@@ -42,7 +42,7 @@ class Form::Setup::Questions::CreatedById < ::Form::Question
private
- def selected_answer_option_is_derived?(_lettings_log)
+ def selected_answer_option_is_derived?(_log)
false
end
end
diff --git a/app/models/form/setup/questions/owning_organisation_id.rb b/app/models/form/common/questions/owning_organisation_id.rb
similarity index 76%
rename from app/models/form/setup/questions/owning_organisation_id.rb
rename to app/models/form/common/questions/owning_organisation_id.rb
index a8fd15e50..84eefbf21 100644
--- a/app/models/form/setup/questions/owning_organisation_id.rb
+++ b/app/models/form/common/questions/owning_organisation_id.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Questions::OwningOrganisationId < ::Form::Question
+class Form::Common::Questions::OwningOrganisationId < ::Form::Question
def initialize(id, hsh, page)
super
@id = "owning_organisation_id"
@@ -19,7 +19,7 @@ class Form::Setup::Questions::OwningOrganisationId < ::Form::Question
end
end
- def displayed_answer_options(_lettings_log)
+ def displayed_answer_options(_log)
answer_options
end
@@ -29,7 +29,7 @@ class Form::Setup::Questions::OwningOrganisationId < ::Form::Question
answer_options[value]
end
- def hidden_in_check_answers?(_lettings_log, current_user)
+ def hidden_in_check_answers?(_log, current_user)
!current_user.support?
end
@@ -39,7 +39,7 @@ class Form::Setup::Questions::OwningOrganisationId < ::Form::Question
private
- def selected_answer_option_is_derived?(_lettings_log)
+ def selected_answer_option_is_derived?(_log)
false
end
end
diff --git a/app/models/form/setup/pages/location.rb b/app/models/form/lettings/pages/location.rb
similarity index 70%
rename from app/models/form/setup/pages/location.rb
rename to app/models/form/lettings/pages/location.rb
index 7acbc1bbe..982f4c109 100644
--- a/app/models/form/setup/pages/location.rb
+++ b/app/models/form/lettings/pages/location.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Pages::Location < ::Form::Page
+class Form::Lettings::Pages::Location < ::Form::Page
def initialize(_id, hsh, subsection)
super("location", hsh, subsection)
@header = ""
@@ -11,7 +11,7 @@ class Form::Setup::Pages::Location < ::Form::Page
def questions
@questions ||= [
- Form::Setup::Questions::LocationId.new(nil, nil, self),
+ Form::Lettings::Questions::LocationId.new(nil, nil, self),
]
end
end
diff --git a/app/models/form/setup/pages/needs_type.rb b/app/models/form/lettings/pages/needs_type.rb
similarity index 62%
rename from app/models/form/setup/pages/needs_type.rb
rename to app/models/form/lettings/pages/needs_type.rb
index 2625d6cda..0a67d831b 100644
--- a/app/models/form/setup/pages/needs_type.rb
+++ b/app/models/form/lettings/pages/needs_type.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Pages::NeedsType < ::Form::Page
+class Form::Lettings::Pages::NeedsType < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "needs_type"
@@ -9,7 +9,7 @@ class Form::Setup::Pages::NeedsType < ::Form::Page
def questions
@questions ||= [
- Form::Setup::Questions::NeedsType.new(nil, nil, self),
+ Form::Lettings::Questions::NeedsType.new(nil, nil, self),
]
end
end
diff --git a/app/models/form/setup/pages/property_reference.rb b/app/models/form/lettings/pages/property_reference.rb
similarity index 60%
rename from app/models/form/setup/pages/property_reference.rb
rename to app/models/form/lettings/pages/property_reference.rb
index 14fafade2..150974ee8 100644
--- a/app/models/form/setup/pages/property_reference.rb
+++ b/app/models/form/lettings/pages/property_reference.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Pages::PropertyReference < ::Form::Page
+class Form::Lettings::Pages::PropertyReference < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "property_reference"
@@ -9,7 +9,7 @@ class Form::Setup::Pages::PropertyReference < ::Form::Page
def questions
@questions ||= [
- Form::Setup::Questions::PropertyReference.new(nil, nil, self),
+ Form::Lettings::Questions::PropertyReference.new(nil, nil, self),
]
end
end
diff --git a/app/models/form/setup/pages/renewal.rb b/app/models/form/lettings/pages/renewal.rb
similarity index 63%
rename from app/models/form/setup/pages/renewal.rb
rename to app/models/form/lettings/pages/renewal.rb
index 8a627d7c9..873ff8822 100644
--- a/app/models/form/setup/pages/renewal.rb
+++ b/app/models/form/lettings/pages/renewal.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Pages::Renewal < ::Form::Page
+class Form::Lettings::Pages::Renewal < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "renewal"
@@ -9,7 +9,7 @@ class Form::Setup::Pages::Renewal < ::Form::Page
def questions
@questions ||= [
- Form::Setup::Questions::Renewal.new(nil, nil, self),
+ Form::Lettings::Questions::Renewal.new(nil, nil, self),
]
end
end
diff --git a/app/models/form/setup/pages/rent_type.rb b/app/models/form/lettings/pages/rent_type.rb
similarity index 51%
rename from app/models/form/setup/pages/rent_type.rb
rename to app/models/form/lettings/pages/rent_type.rb
index 5e112c3c6..d1ac4de99 100644
--- a/app/models/form/setup/pages/rent_type.rb
+++ b/app/models/form/lettings/pages/rent_type.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Pages::RentType < ::Form::Page
+class Form::Lettings::Pages::RentType < ::Form::Page
def initialize(_id, hsh, subsection)
super("rent_type", hsh, subsection)
@header = ""
@@ -8,8 +8,8 @@ class Form::Setup::Pages::RentType < ::Form::Page
def questions
@questions ||= [
- Form::Setup::Questions::RentType.new(nil, nil, self),
- Form::Setup::Questions::IrproductOther.new(nil, nil, self),
+ Form::Lettings::Questions::RentType.new(nil, nil, self),
+ Form::Lettings::Questions::IrproductOther.new(nil, nil, self),
]
end
end
diff --git a/app/models/form/setup/pages/scheme.rb b/app/models/form/lettings/pages/scheme.rb
similarity index 66%
rename from app/models/form/setup/pages/scheme.rb
rename to app/models/form/lettings/pages/scheme.rb
index e71a8d424..c17eaab04 100644
--- a/app/models/form/setup/pages/scheme.rb
+++ b/app/models/form/lettings/pages/scheme.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Pages::Scheme < ::Form::Page
+class Form::Lettings::Pages::Scheme < ::Form::Page
def initialize(_id, hsh, subsection)
super("scheme", hsh, subsection)
@header = ""
@@ -10,7 +10,7 @@ class Form::Setup::Pages::Scheme < ::Form::Page
def questions
@questions ||= [
- Form::Setup::Questions::SchemeId.new(nil, nil, self),
+ Form::Lettings::Questions::SchemeId.new(nil, nil, self),
]
end
end
diff --git a/app/models/form/setup/pages/tenancy_start_date.rb b/app/models/form/lettings/pages/tenancy_start_date.rb
similarity index 59%
rename from app/models/form/setup/pages/tenancy_start_date.rb
rename to app/models/form/lettings/pages/tenancy_start_date.rb
index 117ef8452..562153dc2 100644
--- a/app/models/form/setup/pages/tenancy_start_date.rb
+++ b/app/models/form/lettings/pages/tenancy_start_date.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Pages::TenancyStartDate < ::Form::Page
+class Form::Lettings::Pages::TenancyStartDate < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "tenancy_start_date"
@@ -8,7 +8,7 @@ class Form::Setup::Pages::TenancyStartDate < ::Form::Page
def questions
@questions ||= [
- Form::Setup::Questions::TenancyStartDate.new(nil, nil, self),
+ Form::Lettings::Questions::TenancyStartDate.new(nil, nil, self),
]
end
end
diff --git a/app/models/form/setup/pages/tenant_code.rb b/app/models/form/lettings/pages/tenant_code.rb
similarity index 62%
rename from app/models/form/setup/pages/tenant_code.rb
rename to app/models/form/lettings/pages/tenant_code.rb
index f7b8350d1..67771514a 100644
--- a/app/models/form/setup/pages/tenant_code.rb
+++ b/app/models/form/lettings/pages/tenant_code.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Pages::TenantCode < ::Form::Page
+class Form::Lettings::Pages::TenantCode < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "tenant_code"
@@ -9,7 +9,7 @@ class Form::Setup::Pages::TenantCode < ::Form::Page
def questions
@questions ||= [
- Form::Setup::Questions::TenantCode.new(nil, nil, self),
+ Form::Lettings::Questions::TenantCode.new(nil, nil, self),
]
end
end
diff --git a/app/models/form/setup/questions/irproduct_other.rb b/app/models/form/lettings/questions/irproduct_other.rb
similarity index 74%
rename from app/models/form/setup/questions/irproduct_other.rb
rename to app/models/form/lettings/questions/irproduct_other.rb
index 10c44d1fc..7607d6d9c 100644
--- a/app/models/form/setup/questions/irproduct_other.rb
+++ b/app/models/form/lettings/questions/irproduct_other.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Questions::IrproductOther < ::Form::Question
+class Form::Lettings::Questions::IrproductOther < ::Form::Question
def initialize(id, hsh, page)
super
@id = "irproduct_other"
diff --git a/app/models/form/setup/questions/location_id.rb b/app/models/form/lettings/questions/location_id.rb
similarity index 95%
rename from app/models/form/setup/questions/location_id.rb
rename to app/models/form/lettings/questions/location_id.rb
index 58afbe011..cae6463a9 100644
--- a/app/models/form/setup/questions/location_id.rb
+++ b/app/models/form/lettings/questions/location_id.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Questions::LocationId < ::Form::Question
+class Form::Lettings::Questions::LocationId < ::Form::Question
def initialize(_id, hsh, page)
super("location_id", hsh, page)
@check_answer_label = "Location"
diff --git a/app/models/form/setup/questions/needs_type.rb b/app/models/form/lettings/questions/needs_type.rb
similarity index 90%
rename from app/models/form/setup/questions/needs_type.rb
rename to app/models/form/lettings/questions/needs_type.rb
index 01c52b8cc..46949a2bf 100644
--- a/app/models/form/setup/questions/needs_type.rb
+++ b/app/models/form/lettings/questions/needs_type.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Questions::NeedsType < ::Form::Question
+class Form::Lettings::Questions::NeedsType < ::Form::Question
def initialize(id, hsh, page)
super
@id = "needstype"
diff --git a/app/models/form/setup/questions/property_reference.rb b/app/models/form/lettings/questions/property_reference.rb
similarity index 81%
rename from app/models/form/setup/questions/property_reference.rb
rename to app/models/form/lettings/questions/property_reference.rb
index c52bf1a16..40517b9d3 100644
--- a/app/models/form/setup/questions/property_reference.rb
+++ b/app/models/form/lettings/questions/property_reference.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Questions::PropertyReference < ::Form::Question
+class Form::Lettings::Questions::PropertyReference < ::Form::Question
def initialize(id, hsh, page)
super
@id = "propcode"
diff --git a/app/models/form/setup/questions/renewal.rb b/app/models/form/lettings/questions/renewal.rb
similarity index 85%
rename from app/models/form/setup/questions/renewal.rb
rename to app/models/form/lettings/questions/renewal.rb
index 53b68fb68..06b5f0ff4 100644
--- a/app/models/form/setup/questions/renewal.rb
+++ b/app/models/form/lettings/questions/renewal.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Questions::Renewal < ::Form::Question
+class Form::Lettings::Questions::Renewal < ::Form::Question
def initialize(id, hsh, page)
super
@id = "renewal"
diff --git a/app/models/form/setup/questions/rent_type.rb b/app/models/form/lettings/questions/rent_type.rb
similarity index 91%
rename from app/models/form/setup/questions/rent_type.rb
rename to app/models/form/lettings/questions/rent_type.rb
index c69b1a100..c7c23c74b 100644
--- a/app/models/form/setup/questions/rent_type.rb
+++ b/app/models/form/lettings/questions/rent_type.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Questions::RentType < ::Form::Question
+class Form::Lettings::Questions::RentType < ::Form::Question
def initialize(id, hsh, page)
super
@id = "rent_type"
diff --git a/app/models/form/setup/questions/scheme_id.rb b/app/models/form/lettings/questions/scheme_id.rb
similarity index 96%
rename from app/models/form/setup/questions/scheme_id.rb
rename to app/models/form/lettings/questions/scheme_id.rb
index 9a71e1559..064be8032 100644
--- a/app/models/form/setup/questions/scheme_id.rb
+++ b/app/models/form/lettings/questions/scheme_id.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Questions::SchemeId < ::Form::Question
+class Form::Lettings::Questions::SchemeId < ::Form::Question
def initialize(_id, hsh, page)
super("scheme_id", hsh, page)
@check_answer_label = "Scheme name"
diff --git a/app/models/form/setup/questions/tenancy_start_date.rb b/app/models/form/lettings/questions/tenancy_start_date.rb
similarity index 74%
rename from app/models/form/setup/questions/tenancy_start_date.rb
rename to app/models/form/lettings/questions/tenancy_start_date.rb
index 8e05771f7..f7e2622ac 100644
--- a/app/models/form/setup/questions/tenancy_start_date.rb
+++ b/app/models/form/lettings/questions/tenancy_start_date.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Questions::TenancyStartDate < ::Form::Question
+class Form::Lettings::Questions::TenancyStartDate < ::Form::Question
def initialize(id, hsh, page)
super
@id = "startdate"
diff --git a/app/models/form/setup/questions/tenant_code.rb b/app/models/form/lettings/questions/tenant_code.rb
similarity index 82%
rename from app/models/form/setup/questions/tenant_code.rb
rename to app/models/form/lettings/questions/tenant_code.rb
index e299a3991..dcbbb72a8 100644
--- a/app/models/form/setup/questions/tenant_code.rb
+++ b/app/models/form/lettings/questions/tenant_code.rb
@@ -1,4 +1,4 @@
-class Form::Setup::Questions::TenantCode < ::Form::Question
+class Form::Lettings::Questions::TenantCode < ::Form::Question
def initialize(id, hsh, page)
super
@id = "tenancycode"
diff --git a/app/models/form/setup/sections/setup.rb b/app/models/form/lettings/sections/setup.rb
similarity index 51%
rename from app/models/form/setup/sections/setup.rb
rename to app/models/form/lettings/sections/setup.rb
index 9e41eb2bb..e278ad1e3 100644
--- a/app/models/form/setup/sections/setup.rb
+++ b/app/models/form/lettings/sections/setup.rb
@@ -1,10 +1,10 @@
-class Form::Sections::Setup < ::Form::Section
+class Form::Lettings::Sections::Setup < ::Form::Section
def initialize(id, hsh, form)
super
@id = "setup"
@label = "Before you start"
@description = ""
@form = form
- @subsections = [Form::Setup::Subsections::Setup.new(nil, nil, self)]
+ @subsections = [Form::Lettings::Subsections::Setup.new(nil, nil, self)]
end
end
diff --git a/app/models/form/lettings/subsections/setup.rb b/app/models/form/lettings/subsections/setup.rb
new file mode 100644
index 000000000..79d346599
--- /dev/null
+++ b/app/models/form/lettings/subsections/setup.rb
@@ -0,0 +1,37 @@
+class Form::Lettings::Subsections::Setup < ::Form::Subsection
+ def initialize(id, hsh, section)
+ super
+ @id = "setup"
+ @label = "Set up this lettings log"
+ @section = section
+ end
+
+ def pages
+ @pages ||= [
+ Form::Common::Pages::Organisation.new(nil, nil, self),
+ Form::Common::Pages::CreatedBy.new(nil, nil, self),
+ Form::Lettings::Pages::NeedsType.new(nil, nil, self),
+ Form::Lettings::Pages::Scheme.new(nil, nil, self),
+ Form::Lettings::Pages::Location.new(nil, nil, self),
+ Form::Lettings::Pages::Renewal.new(nil, nil, self),
+ Form::Lettings::Pages::TenancyStartDate.new(nil, nil, self),
+ Form::Lettings::Pages::RentType.new(nil, nil, self),
+ Form::Lettings::Pages::TenantCode.new(nil, nil, self),
+ Form::Lettings::Pages::PropertyReference.new(nil, nil, self),
+ ]
+ end
+
+ def applicable_questions(lettings_log)
+ questions.select { |q| support_only_questions.include?(q.id) } + super
+ end
+
+ def enabled?(_lettings_log)
+ true
+ end
+
+private
+
+ def support_only_questions
+ %w[owning_organisation_id created_by_id].freeze
+ end
+end
diff --git a/app/models/form/page.rb b/app/models/form/page.rb
index 031b812a6..3ee1932cc 100644
--- a/app/models/form/page.rb
+++ b/app/models/form/page.rb
@@ -18,10 +18,10 @@ class Form::Page
delegate :form, to: :subsection
- def routed_to?(lettings_log, _current_user)
+ def routed_to?(log, _current_user)
return true unless depends_on || subsection.depends_on
- subsection.enabled?(lettings_log) && form.depends_on_met(depends_on, lettings_log)
+ subsection.enabled?(log) && form.depends_on_met(depends_on, log)
end
def non_conditional_questions
diff --git a/app/models/form/question.rb b/app/models/form/question.rb
index fc05efe03..4478f63fd 100644
--- a/app/models/form/question.rb
+++ b/app/models/form/question.rb
@@ -44,31 +44,31 @@ class Form::Question
delegate :subsection, to: :page
delegate :form, to: :subsection
- def answer_label(lettings_log)
- return checkbox_answer_label(lettings_log) if type == "checkbox"
- return lettings_log[id]&.to_formatted_s(:govuk_date).to_s if type == "date"
+ def answer_label(log)
+ return checkbox_answer_label(log) if type == "checkbox"
+ return log[id]&.to_formatted_s(:govuk_date).to_s if type == "date"
- answer = label_from_value(lettings_log[id]) if lettings_log[id].present?
- answer_label = [prefix, format_value(answer), suffix_label(lettings_log)].join("") if answer
+ answer = label_from_value(log[id]) if log[id].present?
+ answer_label = [prefix, format_value(answer), suffix_label(log)].join("") if answer
return answer_label if answer_label
- has_inferred_check_answers_value?(lettings_log) ? inferred_check_answers_value["value"] : ""
+ has_inferred_check_answers_value?(log) ? inferred_check_answers_value["value"] : ""
end
- def get_inferred_answers(lettings_log)
+ def get_inferred_answers(log)
return [] unless inferred_answers
- enabled_inferred_answers(inferred_answers, lettings_log).keys.map do |question_id|
- question = form.get_question(question_id, lettings_log)
+ enabled_inferred_answers(inferred_answers, log).keys.map do |question_id|
+ question = form.get_question(question_id, log)
if question.present?
- question.label_from_value(lettings_log[question_id])
+ question.label_from_value(log[question_id])
else
- Array(question_id.to_s.split(".")).inject(lettings_log) { |log, method| log.present? ? log.public_send(*method) : "" }
+ Array(question_id.to_s.split(".")).inject(log) { |l, method| l.present? ? l.public_send(*method) : "" }
end
end
end
- def get_extra_check_answer_value(_lettings_log)
+ def get_extra_check_answer_value(_log)
nil
end
@@ -76,59 +76,59 @@ class Form::Question
!!readonly
end
- def enabled?(lettings_log)
+ def enabled?(log)
return true if conditional_on.blank?
- conditional_on.all? { |condition| evaluate_condition(condition, lettings_log) }
+ conditional_on.all? { |condition| evaluate_condition(condition, log) }
end
- def hidden_in_check_answers?(lettings_log, _current_user = nil)
+ def hidden_in_check_answers?(log, _current_user = nil)
if hidden_in_check_answers.is_a?(Hash)
- form.depends_on_met(hidden_in_check_answers["depends_on"], lettings_log)
+ form.depends_on_met(hidden_in_check_answers["depends_on"], log)
else
hidden_in_check_answers
end
end
- def displayed_to_user?(lettings_log)
- page.routed_to?(lettings_log, nil) && enabled?(lettings_log)
+ def displayed_to_user?(log)
+ page.routed_to?(log, nil) && enabled?(log)
end
def derived?
!!derived
end
- def has_inferred_check_answers_value?(lettings_log)
- return true if selected_answer_option_is_derived?(lettings_log)
- return inferred_check_answers_value["condition"].values[0] == lettings_log[inferred_check_answers_value["condition"].keys[0]] if inferred_check_answers_value.present?
+ def has_inferred_check_answers_value?(log)
+ return true if selected_answer_option_is_derived?(log)
+ return inferred_check_answers_value["condition"].values[0] == log[inferred_check_answers_value["condition"].keys[0]] if inferred_check_answers_value.present?
false
end
- def displayed_answer_options(lettings_log)
+ def displayed_answer_options(log)
answer_options.select do |_key, val|
- !val.is_a?(Hash) || !val["depends_on"] || form.depends_on_met(val["depends_on"], lettings_log)
+ !val.is_a?(Hash) || !val["depends_on"] || form.depends_on_met(val["depends_on"], log)
end
end
- def action_text(lettings_log)
- if has_inferred_check_answers_value?(lettings_log)
+ def action_text(log)
+ if has_inferred_check_answers_value?(log)
"Change"
elsif type == "checkbox"
- answer_options.keys.any? { |key| value_is_yes?(lettings_log[key]) } ? "Change" : "Answer"
+ answer_options.keys.any? { |key| value_is_yes?(log[key]) } ? "Change" : "Answer"
else
- lettings_log[id].blank? ? "Answer" : "Change"
+ log[id].blank? ? "Answer" : "Change"
end
end
- def action_href(lettings_log, page_id)
- "/logs/#{lettings_log.id}/#{page_id.to_s.dasherize}?referrer=check_answers"
+ def action_href(log, page_id)
+ "/#{log.model_name.param_key.dasherize}s/#{log.id}/#{page_id.to_s.dasherize}?referrer=check_answers"
end
- def completed?(lettings_log)
- return answer_options.keys.any? { |key| value_is_yes?(lettings_log[key]) } if type == "checkbox"
+ def completed?(log)
+ return answer_options.keys.any? { |key| value_is_yes?(log[key]) } if type == "checkbox"
- lettings_log[id].present? || !lettings_log.respond_to?(id.to_sym) || has_inferred_display_value?(lettings_log)
+ log[id].present? || !log.respond_to?(id.to_sym) || has_inferred_display_value?(log)
end
def value_from_label(label)
@@ -203,7 +203,7 @@ class Form::Question
I18n.t("validations.not_answered", question: display_label.downcase)
end
- def suffix_label(lettings_log)
+ def suffix_label(log)
return "" unless suffix
return suffix if suffix.is_a?(String)
@@ -213,7 +213,7 @@ class Form::Question
condition = s["depends_on"]
next unless condition
- answer = lettings_log.send(condition.keys.first)
+ answer = log.send(condition.keys.first)
if answer == condition.values.first
label = s["label"]
end
@@ -239,10 +239,10 @@ class Form::Question
resource.hint
end
- def answer_selected?(lettings_log, answer)
+ def answer_selected?(log, answer)
return false unless type == "select"
- lettings_log[id].to_s == answer.id.to_s
+ log[id].to_s == answer.id.to_s
end
def top_guidance?
@@ -255,20 +255,20 @@ class Form::Question
private
- def selected_answer_option_is_derived?(lettings_log)
- selected_option = answer_options&.dig(lettings_log[id].to_s.presence)
- selected_option.is_a?(Hash) && selected_option["depends_on"] && form.depends_on_met(selected_option["depends_on"], lettings_log)
+ def selected_answer_option_is_derived?(log)
+ selected_option = answer_options&.dig(log[id].to_s.presence)
+ selected_option.is_a?(Hash) && selected_option["depends_on"] && form.depends_on_met(selected_option["depends_on"], log)
end
- def has_inferred_display_value?(lettings_log)
- inferred_check_answers_value.present? && lettings_log[inferred_check_answers_value["condition"].keys.first] == inferred_check_answers_value["condition"].values.first
+ def has_inferred_display_value?(log)
+ inferred_check_answers_value.present? && log[inferred_check_answers_value["condition"].keys.first] == inferred_check_answers_value["condition"].values.first
end
- def checkbox_answer_label(lettings_log)
+ def checkbox_answer_label(log)
answer = []
- return "Yes" if id == "declaration" && value_is_yes?(lettings_log["declaration"])
+ return "Yes" if id == "declaration" && value_is_yes?(log["declaration"])
- answer_options.each { |key, options| value_is_yes?(lettings_log[key]) ? answer << options["value"] : nil }
+ answer_options.each { |key, options| value_is_yes?(log[key]) ? answer << options["value"] : nil }
answer.join(", ")
end
@@ -282,21 +282,21 @@ private
end
end
- def evaluate_condition(condition, lettings_log)
+ def evaluate_condition(condition, log)
case page.questions.find { |q| q.id == condition[:from] }.type
when "numeric"
operator = condition[:cond][/[<>=]+/].to_sym
operand = condition[:cond][/\d+/].to_i
- lettings_log[condition[:from]].present? && lettings_log[condition[:from]].send(operator, operand)
+ log[condition[:from]].present? && log[condition[:from]].send(operator, operand)
when "text", "radio", "select"
- lettings_log[condition[:from]].present? && condition[:cond].include?(lettings_log[condition[:from]])
+ log[condition[:from]].present? && condition[:cond].include?(log[condition[:from]])
else
raise "Not implemented yet"
end
end
- def enabled_inferred_answers(inferred_answers, lettings_log)
- inferred_answers.filter { |_key, value| value.all? { |condition_key, condition_value| lettings_log[condition_key] == condition_value } }
+ def enabled_inferred_answers(inferred_answers, log)
+ inferred_answers.filter { |_key, value| value.all? { |condition_key, condition_value| log[condition_key] == condition_value } }
end
RADIO_YES_VALUE = {
diff --git a/app/models/form/sales/pages/age1.rb b/app/models/form/sales/pages/age1.rb
new file mode 100644
index 000000000..833758056
--- /dev/null
+++ b/app/models/form/sales/pages/age1.rb
@@ -0,0 +1,16 @@
+class Form::Sales::Pages::Age1 < ::Form::Page
+ def initialize(id, hsh, subsection)
+ super
+ @id = "buyer_1_age"
+ @header = ""
+ @description = ""
+ @subsection = subsection
+ end
+
+ def questions
+ @questions ||= [
+ Form::Sales::Questions::Buyer1AgeKnown.new(nil, nil, self),
+ Form::Sales::Questions::Age1.new(nil, nil, self),
+ ]
+ end
+end
diff --git a/app/models/form/sales/pages/buyer1_live_in_property.rb b/app/models/form/sales/pages/buyer1_live_in_property.rb
new file mode 100644
index 000000000..6146f6782
--- /dev/null
+++ b/app/models/form/sales/pages/buyer1_live_in_property.rb
@@ -0,0 +1,15 @@
+class Form::Sales::Pages::Buyer1LiveInProperty < ::Form::Page
+ def initialize(id, hsh, subsection)
+ super
+ @id = "buyer_1_live_in_property"
+ @header = ""
+ @description = ""
+ @subsection = subsection
+ end
+
+ def questions
+ @questions ||= [
+ Form::Sales::Questions::Buyer1LiveInProperty.new(nil, nil, self),
+ ]
+ end
+end
diff --git a/app/models/form/sales/pages/buyer_live.rb b/app/models/form/sales/pages/buyer_live.rb
new file mode 100644
index 000000000..bf1302058
--- /dev/null
+++ b/app/models/form/sales/pages/buyer_live.rb
@@ -0,0 +1,18 @@
+class Form::Sales::Pages::BuyerLive < ::Form::Page
+ def initialize(id, hsh, subsection)
+ super
+ @id = "buyer_live"
+ @header = ""
+ @description = ""
+ @subsection = subsection
+ @depends_on = [{
+ "companybuy" => 2,
+ }]
+ end
+
+ def questions
+ @questions ||= [
+ Form::Sales::Questions::BuyerLive.new(nil, nil, self),
+ ]
+ end
+end
diff --git a/app/models/form/sales/pages/discounted_ownership_type.rb b/app/models/form/sales/pages/discounted_ownership_type.rb
new file mode 100644
index 000000000..031c5c620
--- /dev/null
+++ b/app/models/form/sales/pages/discounted_ownership_type.rb
@@ -0,0 +1,18 @@
+class Form::Sales::Pages::DiscountedOwnershipType < ::Form::Page
+ def initialize(id, hsh, subsection)
+ super
+ @id = "discounted_ownership_type"
+ @header = ""
+ @description = ""
+ @subsection = subsection
+ @depends_on = [{
+ "ownershipsch" => 2,
+ }]
+ end
+
+ def questions
+ @questions ||= [
+ Form::Sales::Questions::DiscountedOwnershipType.new(nil, nil, self),
+ ]
+ end
+end
diff --git a/app/models/form/sales/pages/gender_identity1.rb b/app/models/form/sales/pages/gender_identity1.rb
new file mode 100644
index 000000000..9a92a16e9
--- /dev/null
+++ b/app/models/form/sales/pages/gender_identity1.rb
@@ -0,0 +1,15 @@
+class Form::Sales::Pages::GenderIdentity1 < ::Form::Page
+ def initialize(id, hsh, subsection)
+ super
+ @id = "buyer_1_gender_identity"
+ @header = ""
+ @description = ""
+ @subsection = subsection
+ end
+
+ def questions
+ @questions ||= [
+ Form::Sales::Questions::GenderIdentity1.new(nil, nil, self),
+ ]
+ end
+end
diff --git a/app/models/form/sales/pages/joint_purchase.rb b/app/models/form/sales/pages/joint_purchase.rb
new file mode 100644
index 000000000..fecbff757
--- /dev/null
+++ b/app/models/form/sales/pages/joint_purchase.rb
@@ -0,0 +1,15 @@
+class Form::Sales::Pages::JointPurchase < ::Form::Page
+ def initialize(id, hsh, subsection)
+ super
+ @id = "joint_purchase"
+ @header = ""
+ @description = ""
+ @subsection = subsection
+ end
+
+ def questions
+ @questions ||= [
+ Form::Sales::Questions::JointPurchase.new(nil, nil, self),
+ ]
+ end
+end
diff --git a/app/models/form/sales/pages/number_joint_buyers.rb b/app/models/form/sales/pages/number_joint_buyers.rb
new file mode 100644
index 000000000..2d57326e1
--- /dev/null
+++ b/app/models/form/sales/pages/number_joint_buyers.rb
@@ -0,0 +1,18 @@
+class Form::Sales::Pages::NumberJointBuyers < ::Form::Page
+ def initialize(id, hsh, subsection)
+ super
+ @id = "number_joint_buyers"
+ @header = ""
+ @description = ""
+ @subsection = subsection
+ @depends_on = [{
+ "jointpur" => 1,
+ }]
+ end
+
+ def questions
+ @questions ||= [
+ Form::Sales::Questions::NumberJointBuyers.new(nil, nil, self),
+ ]
+ end
+end
diff --git a/app/models/form/sales/pages/outright_ownership_type.rb b/app/models/form/sales/pages/outright_ownership_type.rb
new file mode 100644
index 000000000..6c48e7107
--- /dev/null
+++ b/app/models/form/sales/pages/outright_ownership_type.rb
@@ -0,0 +1,19 @@
+class Form::Sales::Pages::OutrightOwnershipType < ::Form::Page
+ def initialize(id, hsh, subsection)
+ super
+ @id = "outright_ownership_type"
+ @header = ""
+ @description = ""
+ @subsection = subsection
+ @depends_on = [{
+ "ownershipsch" => 3,
+ }]
+ end
+
+ def questions
+ @questions ||= [
+ Form::Sales::Questions::OutrightOwnershipType.new(nil, nil, self),
+ Form::Sales::Questions::OtherOwnershipType.new(nil, nil, self),
+ ]
+ end
+end
diff --git a/app/models/form/sales/pages/ownership_scheme.rb b/app/models/form/sales/pages/ownership_scheme.rb
new file mode 100644
index 000000000..224bc52a2
--- /dev/null
+++ b/app/models/form/sales/pages/ownership_scheme.rb
@@ -0,0 +1,15 @@
+class Form::Sales::Pages::OwnershipScheme < ::Form::Page
+ def initialize(id, hsh, subsection)
+ super
+ @id = "ownership_scheme"
+ @header = ""
+ @description = ""
+ @subsection = subsection
+ end
+
+ def questions
+ @questions ||= [
+ Form::Sales::Questions::OwnershipScheme.new(nil, nil, self),
+ ]
+ end
+end
diff --git a/app/models/form/sales/pages/property_number_of_bedrooms.rb b/app/models/form/sales/pages/property_number_of_bedrooms.rb
new file mode 100644
index 000000000..5efb6b0f4
--- /dev/null
+++ b/app/models/form/sales/pages/property_number_of_bedrooms.rb
@@ -0,0 +1,15 @@
+class Form::Sales::Pages::PropertyNumberOfBedrooms < ::Form::Page
+ def initialize(id, hsh, subsection)
+ super
+ @id = "property_number_of_bedrooms"
+ @header = ""
+ @description = ""
+ @subsection = subsection
+ end
+
+ def questions
+ @questions ||= [
+ Form::Sales::Questions::PropertyNumberOfBedrooms.new(nil, nil, self),
+ ]
+ end
+end
diff --git a/app/models/form/sales/pages/purchaser_code.rb b/app/models/form/sales/pages/purchaser_code.rb
new file mode 100644
index 000000000..79723e0b0
--- /dev/null
+++ b/app/models/form/sales/pages/purchaser_code.rb
@@ -0,0 +1,15 @@
+class Form::Sales::Pages::PurchaserCode < ::Form::Page
+ def initialize(id, hsh, subsection)
+ super
+ @id = "purchaser_code"
+ @header = ""
+ @description = ""
+ @subsection = subsection
+ end
+
+ def questions
+ @questions ||= [
+ Form::Sales::Questions::PurchaserCode.new(nil, nil, self),
+ ]
+ end
+end
diff --git a/app/models/form/sales/pages/sale_date.rb b/app/models/form/sales/pages/sale_date.rb
new file mode 100644
index 000000000..518e9b5ff
--- /dev/null
+++ b/app/models/form/sales/pages/sale_date.rb
@@ -0,0 +1,15 @@
+class Form::Sales::Pages::SaleDate < ::Form::Page
+ def initialize(id, hsh, subsection)
+ super
+ @id = "completion_date"
+ @header = ""
+ @description = ""
+ @subsection = subsection
+ end
+
+ def questions
+ @questions ||= [
+ Form::Sales::Questions::SaleDate.new(nil, nil, self),
+ ]
+ end
+end
diff --git a/app/models/form/sales/pages/shared_ownership_type.rb b/app/models/form/sales/pages/shared_ownership_type.rb
new file mode 100644
index 000000000..c90074bae
--- /dev/null
+++ b/app/models/form/sales/pages/shared_ownership_type.rb
@@ -0,0 +1,18 @@
+class Form::Sales::Pages::SharedOwnershipType < ::Form::Page
+ def initialize(id, hsh, subsection)
+ super
+ @id = "shared_ownership_type"
+ @header = ""
+ @description = ""
+ @subsection = subsection
+ @depends_on = [{
+ "ownershipsch" => 1,
+ }]
+ end
+
+ def questions
+ @questions ||= [
+ Form::Sales::Questions::SharedOwnershipType.new(nil, nil, self),
+ ]
+ end
+end
diff --git a/app/models/form/sales/questions/age1.rb b/app/models/form/sales/questions/age1.rb
new file mode 100644
index 000000000..390fba3a6
--- /dev/null
+++ b/app/models/form/sales/questions/age1.rb
@@ -0,0 +1,11 @@
+class Form::Sales::Questions::Age1 < ::Form::Question
+ def initialize(id, hsh, page)
+ super
+ @id = "age1"
+ @check_answer_label = "Lead buyer’s age"
+ @header = "Age"
+ @type = "numeric"
+ @page = page
+ @width = 2
+ end
+end
diff --git a/app/models/form/sales/questions/buyer1_age_known.rb b/app/models/form/sales/questions/buyer1_age_known.rb
new file mode 100644
index 000000000..65e021815
--- /dev/null
+++ b/app/models/form/sales/questions/buyer1_age_known.rb
@@ -0,0 +1,21 @@
+class Form::Sales::Questions::Buyer1AgeKnown < ::Form::Question
+ def initialize(id, hsh, page)
+ super
+ @id = "age1_known"
+ @check_answer_label = "Buyer 1’s age"
+ @header = "Do you know buyer 1’s age?"
+ @type = "radio"
+ @answer_options = ANSWER_OPTIONS
+ @page = page
+ @hint_text = "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest."
+ @conditional_for = {
+ "age1" => [0],
+ }
+ end
+
+ ANSWER_OPTIONS = {
+ "0" => { "value" => "Yes" },
+ "1" => { "value" => "No" },
+ "2" => { "value" => "Buyer prefers not to say" },
+ }.freeze
+end
diff --git a/app/models/form/sales/questions/buyer1_live_in_property.rb b/app/models/form/sales/questions/buyer1_live_in_property.rb
new file mode 100644
index 000000000..b80736282
--- /dev/null
+++ b/app/models/form/sales/questions/buyer1_live_in_property.rb
@@ -0,0 +1,17 @@
+class Form::Sales::Questions::Buyer1LiveInProperty < ::Form::Question
+ def initialize(id, hsh, page)
+ super
+ @id = "buy1livein"
+ @check_answer_label = "Will buyer 1 live in the property?"
+ @header = "Will buyer 1 live in the property?"
+ @type = "radio"
+ @answer_options = ANSWER_OPTIONS
+ @page = page
+ @hint_text = "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest."
+ end
+
+ ANSWER_OPTIONS = {
+ "1" => { "value" => "Yes" },
+ "2" => { "value" => "No" },
+ }.freeze
+end
diff --git a/app/models/form/sales/questions/buyer_live.rb b/app/models/form/sales/questions/buyer_live.rb
new file mode 100644
index 000000000..ad5ec7f22
--- /dev/null
+++ b/app/models/form/sales/questions/buyer_live.rb
@@ -0,0 +1,16 @@
+class Form::Sales::Questions::BuyerLive < ::Form::Question
+ def initialize(id, hsh, page)
+ super
+ @id = "buylivein"
+ @check_answer_label = "Buyers living in property"
+ @header = "Will the buyers live in the property?"
+ @type = "radio"
+ @answer_options = ANSWER_OPTIONS
+ @page = page
+ end
+
+ ANSWER_OPTIONS = {
+ "1" => { "value" => "Yes" },
+ "2" => { "value" => "No" },
+ }.freeze
+end
diff --git a/app/models/form/sales/questions/discounted_ownership_type.rb b/app/models/form/sales/questions/discounted_ownership_type.rb
new file mode 100644
index 000000000..b04e76c1e
--- /dev/null
+++ b/app/models/form/sales/questions/discounted_ownership_type.rb
@@ -0,0 +1,21 @@
+class Form::Sales::Questions::DiscountedOwnershipType < ::Form::Question
+ def initialize(id, hsh, page)
+ super
+ @id = "type"
+ @check_answer_label = "Type of discounted ownership sale"
+ @header = "What is the type of discounted ownership sale?"
+ @type = "radio"
+ @answer_options = ANSWER_OPTIONS
+ @page = page
+ end
+
+ ANSWER_OPTIONS = {
+ "8" => { "value" => "Right to Aquire (RTA)" },
+ "14" => { "value" => "Preserved Right to Buy (PRTB)" },
+ "27" => { "value" => "Voluntary Right to Buy (VRTB)" },
+ "9" => { "value" => "Right to Buy (RTB)" },
+ "29" => { "value" => "Rent to Buy - Full Ownership" },
+ "21" => { "value" => "Social HomeBuy for outright purchase" },
+ "22" => { "value" => "Any other equity loan scheme" },
+ }.freeze
+end
diff --git a/app/models/form/sales/questions/gender_identity1.rb b/app/models/form/sales/questions/gender_identity1.rb
new file mode 100644
index 000000000..f5b6edbb6
--- /dev/null
+++ b/app/models/form/sales/questions/gender_identity1.rb
@@ -0,0 +1,19 @@
+class Form::Sales::Questions::GenderIdentity1 < ::Form::Question
+ def initialize(id, hsh, page)
+ super
+ @id = "sex1"
+ @check_answer_label = "Buyer 1’s gender identity"
+ @header = "Which of these best describes buyer 1’s gender identity?"
+ @type = "radio"
+ @hint_text = "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest."
+ @page = page
+ @answer_options = ANSWER_OPTIONS
+ end
+
+ ANSWER_OPTIONS = {
+ "F" => { "value" => "Female" },
+ "M" => { "value" => "Male" },
+ "X" => { "value" => "Non-binary" },
+ "R" => { "value" => "Prefers not to say " },
+ }.freeze
+end
diff --git a/app/models/form/sales/questions/joint_purchase.rb b/app/models/form/sales/questions/joint_purchase.rb
new file mode 100644
index 000000000..893398399
--- /dev/null
+++ b/app/models/form/sales/questions/joint_purchase.rb
@@ -0,0 +1,17 @@
+class Form::Sales::Questions::JointPurchase < ::Form::Question
+ def initialize(id, hsh, page)
+ super
+ @id = "jointpur"
+ @check_answer_label = "Joint purchase"
+ @header = "Is this a joint purchase?"
+ @hint_text = ""
+ @type = "radio"
+ @answer_options = ANSWER_OPTIONS
+ @page = page
+ end
+
+ ANSWER_OPTIONS = {
+ "1" => { "value" => "Yes" },
+ "2" => { "value" => "No" },
+ }.freeze
+end
diff --git a/app/models/form/sales/questions/number_joint_buyers.rb b/app/models/form/sales/questions/number_joint_buyers.rb
new file mode 100644
index 000000000..2370d03c6
--- /dev/null
+++ b/app/models/form/sales/questions/number_joint_buyers.rb
@@ -0,0 +1,18 @@
+class Form::Sales::Questions::NumberJointBuyers < ::Form::Question
+ def initialize(id, hsh, page)
+ super
+ @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
+ @page = page
+ end
+
+ ANSWER_OPTIONS = {
+ "1" => { "value" => "Yes" },
+ "2" => { "value" => "No" },
+ "3" => { "value" => "Don’t know" },
+ }.freeze
+end
diff --git a/app/models/form/sales/questions/other_ownership_type.rb b/app/models/form/sales/questions/other_ownership_type.rb
new file mode 100644
index 000000000..23995bb9e
--- /dev/null
+++ b/app/models/form/sales/questions/other_ownership_type.rb
@@ -0,0 +1,11 @@
+class Form::Sales::Questions::OtherOwnershipType < ::Form::Question
+ def initialize(id, hsh, page)
+ super
+ @id = "othtype"
+ @check_answer_label = "Type of other sale"
+ @header = "What type of sale is it?"
+ @type = "text"
+ @width = 10
+ @page = page
+ end
+end
diff --git a/app/models/form/sales/questions/outright_ownership_type.rb b/app/models/form/sales/questions/outright_ownership_type.rb
new file mode 100644
index 000000000..494722031
--- /dev/null
+++ b/app/models/form/sales/questions/outright_ownership_type.rb
@@ -0,0 +1,19 @@
+class Form::Sales::Questions::OutrightOwnershipType < ::Form::Question
+ def initialize(id, hsh, page)
+ super
+ @id = "type"
+ @check_answer_label = "Type of outright sale"
+ @header = "What is the type of outright sale?"
+ @type = "radio"
+ @answer_options = ANSWER_OPTIONS
+ @page = page
+ @conditional_for = {
+ "othtype" => [12],
+ }
+ end
+
+ ANSWER_OPTIONS = {
+ "10" => { "value" => "Outright" },
+ "12" => { "value" => "Other sale" },
+ }.freeze
+end
diff --git a/app/models/form/sales/questions/ownership_scheme.rb b/app/models/form/sales/questions/ownership_scheme.rb
new file mode 100644
index 000000000..6db17550a
--- /dev/null
+++ b/app/models/form/sales/questions/ownership_scheme.rb
@@ -0,0 +1,17 @@
+class Form::Sales::Questions::OwnershipScheme < ::Form::Question
+ def initialize(id, hsh, page)
+ super
+ @id = "ownershipsch"
+ @check_answer_label = "Purchase made under ownership scheme"
+ @header = "Was this purchase made through an ownership scheme?"
+ @type = "radio"
+ @answer_options = ANSWER_OPTIONS
+ @page = page
+ end
+
+ ANSWER_OPTIONS = {
+ "1" => { "value" => "Yes - a shared ownership scheme" },
+ "2" => { "value" => "Yes - a discounted ownership scheme" },
+ "3" => { "value" => "No - this is an outright or other sale" },
+ }.freeze
+end
diff --git a/app/models/form/sales/questions/property_number_of_bedrooms.rb b/app/models/form/sales/questions/property_number_of_bedrooms.rb
new file mode 100644
index 000000000..34b319240
--- /dev/null
+++ b/app/models/form/sales/questions/property_number_of_bedrooms.rb
@@ -0,0 +1,12 @@
+class Form::Sales::Questions::PropertyNumberOfBedrooms < ::Form::Question
+ def initialize(id, hsh, page)
+ super
+ @id = "beds"
+ @check_answer_label = "Number of bedrooms"
+ @header = "How many bedrooms does the property have?"
+ @hint_text = "A bedsit has 1 bedroom"
+ @type = "text"
+ @width = 10
+ @page = page
+ end
+end
diff --git a/app/models/form/sales/questions/purchaser_code.rb b/app/models/form/sales/questions/purchaser_code.rb
new file mode 100644
index 000000000..1f9551d05
--- /dev/null
+++ b/app/models/form/sales/questions/purchaser_code.rb
@@ -0,0 +1,12 @@
+class Form::Sales::Questions::PurchaserCode < ::Form::Question
+ def initialize(id, hsh, page)
+ super
+ @id = "purchid"
+ @check_answer_label = "Purchaser code"
+ @header = "What is the purchaser code?"
+ @hint_text = "This is how you usually refer to the purchaser on your own systems."
+ @type = "text"
+ @width = 10
+ @page = page
+ end
+end
diff --git a/app/models/form/sales/questions/sale_date.rb b/app/models/form/sales/questions/sale_date.rb
new file mode 100644
index 000000000..2b0a3d171
--- /dev/null
+++ b/app/models/form/sales/questions/sale_date.rb
@@ -0,0 +1,10 @@
+class Form::Sales::Questions::SaleDate < ::Form::Question
+ def initialize(id, hsh, page)
+ super
+ @id = "saledate"
+ @check_answer_label = "Sale completion date"
+ @header = "What is the sale completion date?"
+ @type = "date"
+ @page = page
+ end
+end
diff --git a/app/models/form/sales/questions/shared_ownership_type.rb b/app/models/form/sales/questions/shared_ownership_type.rb
new file mode 100644
index 000000000..27d6ea074
--- /dev/null
+++ b/app/models/form/sales/questions/shared_ownership_type.rb
@@ -0,0 +1,22 @@
+class Form::Sales::Questions::SharedOwnershipType < ::Form::Question
+ def initialize(id, hsh, page)
+ super
+ @id = "type"
+ @check_answer_label = "Type of shared ownership sale"
+ @header = "What is the type of shared ownership sale?"
+ @hint_text = "A shared ownership sale is when the purchaser buys up to 75% of the property value and pays rent to the Private Registered Provider (PRP) on the remaining portion"
+ @type = "radio"
+ @answer_options = ANSWER_OPTIONS
+ @page = page
+ end
+
+ ANSWER_OPTIONS = {
+ "2" => { "value" => "Shared Ownership" },
+ "24" => { "value" => "Old Persons Shared Ownership" },
+ "18" => { "value" => "Social HomeBuy (shared ownership purchase)" },
+ "16" => { "value" => "Home Ownership for people with Long Term Disabilities (HOLD)" },
+ "28" => { "value" => "Rent to Buy - Shared Ownership" },
+ "31" => { "value" => "Right to Shared Ownership" },
+ "30" => { "value" => "Shared Ownership - 2021 model lease" },
+ }.freeze
+end
diff --git a/app/models/form/sales/sections/household.rb b/app/models/form/sales/sections/household.rb
new file mode 100644
index 000000000..b3432522e
--- /dev/null
+++ b/app/models/form/sales/sections/household.rb
@@ -0,0 +1,10 @@
+class Form::Sales::Sections::Household < ::Form::Section
+ def initialize(id, hsh, form)
+ super
+ @id = "household"
+ @label = "About the household"
+ @description = ""
+ @form = form
+ @subsections = [Form::Sales::Subsections::HouseholdCharacteristics.new(nil, nil, self)] || []
+ end
+end
diff --git a/app/models/form/sales/sections/property_information.rb b/app/models/form/sales/sections/property_information.rb
new file mode 100644
index 000000000..39f6609ae
--- /dev/null
+++ b/app/models/form/sales/sections/property_information.rb
@@ -0,0 +1,10 @@
+class Form::Sales::Sections::PropertyInformation < ::Form::Section
+ def initialize(id, hsh, form)
+ super
+ @id = "property_information"
+ @label = "Property information"
+ @description = ""
+ @form = form
+ @subsections = [Form::Sales::Subsections::PropertyInformation.new(nil, nil, self)] || []
+ end
+end
diff --git a/app/models/form/sales/sections/setup.rb b/app/models/form/sales/sections/setup.rb
new file mode 100644
index 000000000..d90c72036
--- /dev/null
+++ b/app/models/form/sales/sections/setup.rb
@@ -0,0 +1,10 @@
+class Form::Sales::Sections::Setup < ::Form::Section
+ def initialize(id, hsh, form)
+ super
+ @id = "setup"
+ @label = "Before you start"
+ @description = ""
+ @form = form
+ @subsections = [Form::Sales::Subsections::Setup.new(nil, nil, self)] || []
+ end
+end
diff --git a/app/models/form/sales/subsections/household_characteristics.rb b/app/models/form/sales/subsections/household_characteristics.rb
new file mode 100644
index 000000000..4612a80f2
--- /dev/null
+++ b/app/models/form/sales/subsections/household_characteristics.rb
@@ -0,0 +1,17 @@
+class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection
+ def initialize(id, hsh, section)
+ super
+ @id = "household_characteristics"
+ @label = "Household characteristics"
+ @section = section
+ @depends_on = [{ "setup" => "completed" }]
+ end
+
+ def pages
+ @pages ||= [
+ Form::Sales::Pages::Age1.new(nil, nil, self),
+ Form::Sales::Pages::GenderIdentity1.new(nil, nil, self),
+ Form::Sales::Pages::Buyer1LiveInProperty.new(nil, nil, self),
+ ]
+ end
+end
diff --git a/app/models/form/sales/subsections/property_information.rb b/app/models/form/sales/subsections/property_information.rb
new file mode 100644
index 000000000..22983049b
--- /dev/null
+++ b/app/models/form/sales/subsections/property_information.rb
@@ -0,0 +1,15 @@
+class Form::Sales::Subsections::PropertyInformation < ::Form::Subsection
+ def initialize(id, hsh, section)
+ super
+ @id = "property_information"
+ @label = "Property information"
+ @section = section
+ @depends_on = [{ "setup" => "completed" }]
+ end
+
+ def pages
+ @pages ||= [
+ Form::Sales::Pages::PropertyNumberOfBedrooms.new(nil, nil, self),
+ ]
+ end
+end
diff --git a/app/models/form/sales/subsections/setup.rb b/app/models/form/sales/subsections/setup.rb
new file mode 100644
index 000000000..2cc6f6886
--- /dev/null
+++ b/app/models/form/sales/subsections/setup.rb
@@ -0,0 +1,24 @@
+class Form::Sales::Subsections::Setup < ::Form::Subsection
+ def initialize(id, hsh, section)
+ super
+ @id = "setup"
+ @label = "Set up this sales log"
+ @section = section
+ end
+
+ def pages
+ @pages ||= [
+ Form::Common::Pages::Organisation.new(nil, nil, self),
+ Form::Common::Pages::CreatedBy.new(nil, nil, self),
+ Form::Sales::Pages::SaleDate.new(nil, nil, self),
+ Form::Sales::Pages::PurchaserCode.new(nil, nil, self),
+ Form::Sales::Pages::OwnershipScheme.new(nil, nil, self),
+ Form::Sales::Pages::SharedOwnershipType.new(nil, nil, self),
+ Form::Sales::Pages::DiscountedOwnershipType.new(nil, nil, self),
+ Form::Sales::Pages::OutrightOwnershipType.new(nil, nil, self),
+ Form::Sales::Pages::BuyerLive.new(nil, nil, self),
+ Form::Sales::Pages::JointPurchase.new(nil, nil, self),
+ Form::Sales::Pages::NumberJointBuyers.new(nil, nil, self),
+ ]
+ end
+end
diff --git a/app/models/form/setup/subsections/setup.rb b/app/models/form/setup/subsections/setup.rb
deleted file mode 100644
index 7bc4a079d..000000000
--- a/app/models/form/setup/subsections/setup.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-class Form::Subsections::Setup < ::Form::Subsection
- def initialize(id, hsh, section)
- super
- @id = "setup"
- @label = "Set up this lettings log"
- @section = section
- end
-
- def pages
- @pages ||= [
- Form::Setup::Pages::Organisation.new(nil, nil, self),
- Form::Setup::Pages::CreatedBy.new(nil, nil, self),
- Form::Setup::Pages::NeedsType.new(nil, nil, self),
- Form::Setup::Pages::Scheme.new(nil, nil, self),
- Form::Setup::Pages::Location.new(nil, nil, self),
- Form::Setup::Pages::Renewal.new(nil, nil, self),
- Form::Setup::Pages::TenancyStartDate.new(nil, nil, self),
- Form::Setup::Pages::RentType.new(nil, nil, self),
- Form::Setup::Pages::TenantCode.new(nil, nil, self),
- Form::Setup::Pages::PropertyReference.new(nil, nil, self),
- ]
- end
-
- def applicable_questions(lettings_log)
- questions.select { |q| support_only_questions.include?(q.id) } + super
- end
-
- def enabled?(_lettings_log)
- true
- end
-
-private
-
- def support_only_questions
- %w[owning_organisation_id created_by_id].freeze
- end
-end
diff --git a/app/models/form/subsection.rb b/app/models/form/subsection.rb
index d42f8dadb..d3ab0ddd2 100644
--- a/app/models/form/subsection.rb
+++ b/app/models/form/subsection.rb
@@ -17,40 +17,40 @@ class Form::Subsection
@questions ||= pages.flat_map(&:questions)
end
- def enabled?(lettings_log)
+ def enabled?(log)
return true unless depends_on
depends_on.any? do |conditions_set|
conditions_set.all? do |subsection_id, dependent_status|
- form.get_subsection(subsection_id).status(lettings_log) == dependent_status.to_sym
+ form.get_subsection(subsection_id).status(log) == dependent_status.to_sym
end
end
end
- def status(lettings_log)
- unless enabled?(lettings_log)
+ def status(log)
+ unless enabled?(log)
return :cannot_start_yet
end
- qs = applicable_questions(lettings_log)
- qs_optional_removed = qs.reject { |q| lettings_log.optional_fields.include?(q.id) }
- return :not_started if qs.count.positive? && qs.all? { |question| lettings_log[question.id].blank? || question.read_only? || question.derived? }
- return :completed if qs_optional_removed.all? { |question| question.completed?(lettings_log) }
+ qs = applicable_questions(log)
+ qs_optional_removed = qs.reject { |q| log.optional_fields.include?(q.id) }
+ return :not_started if qs.count.positive? && qs.all? { |question| log[question.id].blank? || question.read_only? || question.derived? }
+ return :completed if qs_optional_removed.all? { |question| question.completed?(log) }
:in_progress
end
- def is_incomplete?(lettings_log)
- %i[not_started in_progress].include?(status(lettings_log))
+ def is_incomplete?(log)
+ %i[not_started in_progress].include?(status(log))
end
- def is_started?(lettings_log)
- %i[in_progress completed].include?(status(lettings_log))
+ def is_started?(log)
+ %i[in_progress completed].include?(status(log))
end
- def applicable_questions(lettings_log)
+ def applicable_questions(log)
questions.select do |q|
- (q.displayed_to_user?(lettings_log) && !q.derived?) || q.has_inferred_check_answers_value?(lettings_log)
+ (q.displayed_to_user?(log) && !q.derived?) || q.has_inferred_check_answers_value?(log)
end
end
end
diff --git a/app/models/form_handler.rb b/app/models/form_handler.rb
index 13efcb9ca..c9d45e876 100644
--- a/app/models/form_handler.rb
+++ b/app/models/form_handler.rb
@@ -10,23 +10,55 @@ class FormHandler
@forms[form]
end
- def current_form
- forms[forms.keys.max_by(&:to_i)]
+ def current_lettings_form
+ forms["current_lettings"]
end
-private
+ def current_sales_form
+ forms["current_sales"]
+ end
- def get_all_forms
+ def sales_forms
+ sales_sections = [
+ Form::Sales::Sections::PropertyInformation,
+ Form::Sales::Sections::Household,
+ ]
+ current_form = Form.new(nil, current_collection_start_year, sales_sections, "sales")
+ previous_form = Form.new(nil, current_collection_start_year - 1, sales_sections, "sales")
+ { "current_sales" => current_form,
+ "previous_sales" => previous_form }
+ end
+
+ def lettings_forms
forms = {}
directories.each do |directory|
Dir.glob("#{directory}/*.json").each do |form_path|
- form_name = File.basename(form_path, ".json")
- forms[form_name] = Form.new(form_path, form_name)
+ form = Form.new(form_path)
+
+ form_to_set = form_name_from_start_year(form.start_date.year, "lettings")
+ forms[form_to_set] = form if forms[form_to_set].blank?
end
end
forms
end
+ def current_collection_start_year
+ today = Time.zone.now
+ window_end_date = Time.zone.local(today.year, 4, 1)
+ today < window_end_date ? today.year - 1 : today.year
+ end
+
+ def form_name_from_start_year(year, type)
+ form_mappings = { 0 => "current_#{type}", 1 => "previous_#{type}", -1 => "next_#{type}" }
+ form_mappings[current_collection_start_year - year]
+ end
+
+private
+
+ def get_all_forms
+ lettings_forms.merge(sales_forms)
+ end
+
def directories
Rails.env.test? ? ["spec/fixtures/forms"] : ["config/forms"]
end
diff --git a/app/models/legacy_user.rb b/app/models/legacy_user.rb
new file mode 100644
index 000000000..dcab274a6
--- /dev/null
+++ b/app/models/legacy_user.rb
@@ -0,0 +1,5 @@
+class LegacyUser < ApplicationRecord
+ belongs_to :user
+
+ validates :old_user_id, uniqueness: true
+end
diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb
index d49856c89..272281df6 100644
--- a/app/models/lettings_log.rb
+++ b/app/models/lettings_log.rb
@@ -15,7 +15,7 @@ class LettingsLogValidator < ActiveModel::Validator
end
end
-class LettingsLog < ApplicationRecord
+class LettingsLog < Log
include Validations::SoftValidations
include DerivedVariables::LettingsLogVariables
@@ -32,31 +32,11 @@ class LettingsLog < ApplicationRecord
before_validation :reset_location_fields!, unless: :postcode_known?
before_validation :reset_previous_location_fields!, unless: :previous_postcode_known?
before_validation :set_derived_fields!
- before_save :update_status!
- belongs_to :owning_organisation, class_name: "Organisation", optional: true
- belongs_to :managing_organisation, class_name: "Organisation", optional: true
- belongs_to :created_by, class_name: "User", optional: true
belongs_to :scheme, optional: true
belongs_to :location, optional: true
- scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org).or(where(managing_organisation: org)) }
- scope :filter_by_status, ->(status, _user = nil) { where status: }
- scope :filter_by_years, lambda { |years, _user = nil|
- first_year = years.shift
- query = filter_by_year(first_year)
- years.each { |year| query = query.or(filter_by_year(year)) }
- query.all
- }
scope :filter_by_year, ->(year) { where(startdate: Time.zone.local(year.to_i, 4, 1)...Time.zone.local(year.to_i + 1, 4, 1)) }
-
- scope :filter_by_user, lambda { |selected_user, user|
- if !selected_user.include?("all") && user.present?
- where(created_by: user)
- end
- }
-
- scope :filter_by_id, ->(id) { where(id:) }
scope :filter_by_tenant_code, ->(tenant_code) { where("tenancycode ILIKE ?", "%#{tenant_code}%") }
scope :filter_by_propcode, ->(propcode) { where("propcode ILIKE ?", "%#{propcode}%") }
scope :filter_by_postcode, ->(postcode_full) { where("REPLACE(postcode_full, ' ', '') ILIKE ?", "%#{postcode_full.delete(' ')}%") }
@@ -73,22 +53,12 @@ class LettingsLog < ApplicationRecord
OPTIONAL_FIELDS = %w[first_time_property_let_as_social_housing tenancycode propcode].freeze
RENT_TYPE_MAPPING_LABELS = { 1 => "Social Rent", 2 => "Affordable Rent", 3 => "Intermediate Rent" }.freeze
HAS_BENEFITS_OPTIONS = [1, 6, 8, 7].freeze
- STATUS = { "not_started" => 0, "in_progress" => 1, "completed" => 2 }.freeze
NUM_OF_WEEKS_FROM_PERIOD = { 2 => 26, 3 => 13, 4 => 12, 5 => 50, 6 => 49, 7 => 48, 8 => 47, 9 => 46, 1 => 52 }.freeze
SUFFIX_FROM_PERIOD = { 2 => "every 2 weeks", 3 => "every 4 weeks", 4 => "every month" }.freeze
RETIREMENT_AGES = { "M" => 67, "F" => 60, "X" => 67 }.freeze
- enum status: STATUS
def form
- FormHandler.instance.get_form(form_name) || FormHandler.instance.forms.first.second
- end
-
- def collection_start_year
- return @start_year if @start_year
- return unless startdate
-
- window_end_date = Time.zone.local(startdate.year, 4, 1)
- @start_year = startdate < window_end_date ? startdate.year - 1 : startdate.year
+ FormHandler.instance.get_form(form_name) || FormHandler.instance.current_lettings_form
end
def recalculate_start_year!
@@ -99,7 +69,7 @@ class LettingsLog < ApplicationRecord
def form_name
return unless startdate
- "#{collection_start_year}_#{collection_start_year + 1}"
+ FormHandler.instance.form_name_from_start_year(collection_start_year, "lettings")
end
def self.editable_fields
@@ -530,44 +500,18 @@ class LettingsLog < ApplicationRecord
location.type_of_unit_before_type_cast if location
end
+ def lettings?
+ true
+ end
+
+ def rent_type_detail
+ form.get_question("rent_type", self)&.label_from_value(rent_type)
+ end
+
private
PIO = PostcodeService.new
- def update_status!
- self.status = if all_fields_completed? && errors.empty?
- "completed"
- elsif all_fields_nil?
- "not_started"
- else
- "in_progress"
- end
- end
-
- def reset_not_routed_questions
- enabled_questions = form.enabled_page_questions(self)
- enabled_question_ids = enabled_questions.map(&:id)
-
- form.invalidated_page_questions(self).each do |question|
- if %w[radio checkbox].include?(question.type)
- enabled_answer_options = enabled_question_ids.include?(question.id) ? enabled_questions.find { |q| q.id == question.id }.answer_options : {}
- current_answer_option_valid = enabled_answer_options.present? ? enabled_answer_options.key?(public_send(question.id).to_s) : false
- if !current_answer_option_valid && respond_to?(question.id.to_s)
- Rails.logger.debug("Cleared #{question.id} value")
- public_send("#{question.id}=", nil)
- else
- (question.answer_options.keys - enabled_answer_options.keys).map do |invalid_answer_option|
- Rails.logger.debug("Cleared #{invalid_answer_option} value")
- public_send("#{invalid_answer_option}=", nil) if respond_to?(invalid_answer_option)
- end
- end
- else
- Rails.logger.debug("Cleared #{question.id} value")
- public_send("#{question.id}=", nil) unless enabled_question_ids.include?(question.id)
- end
- end
- end
-
def reset_derived_questions
dependent_questions = { waityear: [{ key: :renewal, value: 0 }],
referral: [{ key: :renewal, value: 0 }],
@@ -585,12 +529,6 @@ private
end
end
- def reset_created_by
- return unless created_by && owning_organisation
-
- self.created_by = nil if created_by.organisation != owning_organisation
- end
-
def reset_scheme
return unless scheme && owning_organisation
@@ -598,11 +536,10 @@ private
end
def reset_invalidated_dependent_fields!
- return unless form
+ super
reset_created_by
reset_scheme
- reset_not_routed_questions
reset_derived_questions
end
@@ -695,17 +632,6 @@ private
end
end
- def all_fields_completed?
- subsection_statuses = form.subsections.map { |subsection| subsection.status(self) }.uniq
- subsection_statuses == [:completed]
- end
-
- def all_fields_nil?
- not_started_statuses = %i[not_started cannot_start_yet]
- subsection_statuses = form.subsections.map { |subsection| subsection.status(self) }.uniq
- subsection_statuses.all? { |status| not_started_statuses.include?(status) }
- end
-
def age_refused?
[age1_known, age2_known, age3_known, age4_known, age5_known, age6_known, age7_known, age8_known].any?(1)
end
diff --git a/app/models/log.rb b/app/models/log.rb
new file mode 100644
index 000000000..e24a205fc
--- /dev/null
+++ b/app/models/log.rb
@@ -0,0 +1,73 @@
+class Log < ApplicationRecord
+ self.abstract_class = true
+
+ belongs_to :owning_organisation, class_name: "Organisation", optional: true
+ belongs_to :managing_organisation, class_name: "Organisation", optional: true
+ belongs_to :created_by, class_name: "User", optional: true
+ before_save :update_status!
+
+ STATUS = { "not_started" => 0, "in_progress" => 1, "completed" => 2 }.freeze
+ enum status: STATUS
+
+ scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org).or(where(managing_organisation: org)) }
+ scope :filter_by_status, ->(status, _user = nil) { where status: }
+ scope :filter_by_years, lambda { |years, _user = nil|
+ first_year = years.shift
+ query = filter_by_year(first_year)
+ years.each { |year| query = query.or(filter_by_year(year)) }
+ query.all
+ }
+ scope :filter_by_id, ->(id) { where(id:) }
+ scope :filter_by_user, lambda { |selected_user, user|
+ if !selected_user.include?("all") && user.present?
+ where(created_by: user)
+ end
+ }
+
+ def collection_start_year
+ return @start_year if @start_year
+ return unless startdate
+
+ window_end_date = Time.zone.local(startdate.year, 4, 1)
+ @start_year = startdate < window_end_date ? startdate.year - 1 : startdate.year
+ end
+
+ def lettings?
+ false
+ end
+
+private
+
+ def update_status!
+ self.status = if all_fields_completed? && errors.empty?
+ "completed"
+ elsif all_fields_nil?
+ "not_started"
+ else
+ "in_progress"
+ end
+ end
+
+ def all_fields_completed?
+ subsection_statuses = form.subsections.map { |subsection| subsection.status(self) }.uniq
+ subsection_statuses == [:completed]
+ end
+
+ def all_fields_nil?
+ not_started_statuses = %i[not_started cannot_start_yet]
+ subsection_statuses = form.subsections.map { |subsection| subsection.status(self) }.uniq
+ subsection_statuses.all? { |status| not_started_statuses.include?(status) }
+ end
+
+ def reset_created_by
+ return unless created_by && owning_organisation
+
+ self.created_by = nil if created_by.organisation != owning_organisation
+ end
+
+ def reset_invalidated_dependent_fields!
+ return unless form
+
+ form.reset_not_routed_questions(self)
+ end
+end
diff --git a/app/models/organisation.rb b/app/models/organisation.rb
index afaf6de35..9eb039ab1 100644
--- a/app/models/organisation.rb
+++ b/app/models/organisation.rb
@@ -2,6 +2,8 @@ class Organisation < ApplicationRecord
has_many :users, dependent: :delete_all
has_many :owned_lettings_logs, class_name: "LettingsLog", foreign_key: "owning_organisation_id", dependent: :delete_all
has_many :managed_lettings_logs, class_name: "LettingsLog", foreign_key: "managing_organisation_id"
+ has_many :owned_sales_logs, class_name: "SalesLog", foreign_key: "owning_organisation_id", dependent: :delete_all
+ has_many :managed_sales_logs, class_name: "SalesLog", foreign_key: "managing_organisation_id"
has_many :data_protection_confirmations
has_many :organisation_rent_periods
has_many :owned_schemes, class_name: "Scheme", foreign_key: "owning_organisation_id", dependent: :delete_all
@@ -32,6 +34,10 @@ class Organisation < ApplicationRecord
LettingsLog.filter_by_organisation(self)
end
+ def sales_logs
+ SalesLog.filter_by_organisation(self)
+ end
+
def completed_lettings_logs
lettings_logs.completed
end
diff --git a/app/models/rent_period.rb b/app/models/rent_period.rb
index 77e154f4c..d48fe20ea 100644
--- a/app/models/rent_period.rb
+++ b/app/models/rent_period.rb
@@ -1,5 +1,5 @@
class RentPeriod
def self.rent_period_mappings
- FormHandler.instance.current_form.get_question("period", nil).answer_options
+ FormHandler.instance.current_lettings_form.get_question("period", nil).answer_options
end
end
diff --git a/app/models/sales_log.rb b/app/models/sales_log.rb
new file mode 100644
index 000000000..0f0ca54c7
--- /dev/null
+++ b/app/models/sales_log.rb
@@ -0,0 +1,45 @@
+class SalesLogValidator < ActiveModel::Validator
+ def validate(record); end
+end
+
+class SalesLog < Log
+ self.inheritance_column = :_type_disabled
+ has_paper_trail
+
+ validates_with SalesLogValidator
+
+ scope :filter_by_year, ->(year) { where(saledate: Time.zone.local(year.to_i, 4, 1)...Time.zone.local(year.to_i + 1, 4, 1)) }
+ scope :search_by, ->(param) { filter_by_id(param) }
+
+ OPTIONAL_FIELDS = %w[purchid].freeze
+
+ def startdate
+ saledate
+ end
+
+ def self.editable_fields
+ attribute_names
+ end
+
+ def form_name
+ return unless saledate
+
+ FormHandler.instance.form_name_from_start_year(collection_start_year, "sales")
+ end
+
+ def form
+ FormHandler.instance.get_form(form_name) || FormHandler.instance.current_sales_form
+ end
+
+ def optional_fields
+ []
+ end
+
+ def not_started?
+ status == "not_started"
+ end
+
+ def completed?
+ status == "completed"
+ end
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index 22cc14b77..40a430489 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -7,8 +7,11 @@ class User < ApplicationRecord
# Marked as optional because we validate organisation_id below instead so that
# the error message is linked to the right field on the form
belongs_to :organisation, optional: true
- has_many :owned_lettings_logs, through: :organisation, dependent: :delete_all
+ has_many :owned_lettings_logs, through: :organisation
has_many :managed_lettings_logs, through: :organisation
+ has_many :owned_sales_logs, through: :organisation
+ has_many :managed_sales_logs, through: :organisation
+ has_many :legacy_users
validates :name, presence: true
validates :email, presence: true
@@ -58,6 +61,14 @@ class User < ApplicationRecord
end
end
+ def sales_logs
+ if support?
+ SalesLog.all
+ else
+ SalesLog.filter_by_organisation(organisation)
+ end
+ end
+
def completed_lettings_logs
lettings_logs.completed
end
@@ -103,7 +114,7 @@ class User < ApplicationRecord
end
def was_migrated_from_softwire?
- old_user_id.present?
+ legacy_users.any? || old_user_id.present?
end
def send_confirmation_instructions
@@ -131,7 +142,7 @@ class User < ApplicationRecord
ROLES.except(:support)
end
- def lettings_logs_filters(specific_org: false)
+ def logs_filters(specific_org: false)
if support? && !specific_org
%w[status years user organisation]
else
diff --git a/app/models/validations/date_validations.rb b/app/models/validations/date_validations.rb
index 8f8d87220..18267ffdb 100644
--- a/app/models/validations/date_validations.rb
+++ b/app/models/validations/date_validations.rb
@@ -53,26 +53,22 @@ module Validations::DateValidations
end
end
- def validate_sale_completion_date(record)
- date_valid?("sale_completion_date", record)
- end
-
private
def first_collection_start_date
- @first_collection_start_date ||= FormHandler.instance.forms.map { |form| form.second.start_date }.compact.min
+ @first_collection_start_date ||= FormHandler.instance.forms.map { |_name, form| form.start_date }.compact.min
end
def first_collection_end_date
- @first_collection_end_date ||= FormHandler.instance.forms.map { |form| form.second.end_date }.compact.min
+ @first_collection_end_date ||= FormHandler.instance.forms.map { |_name, form| form.end_date }.compact.min
end
def second_collection_start_date
- @second_collection_start_date ||= FormHandler.instance.forms.map { |form| form.second.start_date }.compact.max
+ @second_collection_start_date ||= FormHandler.instance.forms.map { |_name, form| form.start_date }.compact.max
end
def second_collection_end_date
- @second_collection_end_date ||= FormHandler.instance.forms.map { |form| form.second.end_date }.compact.max
+ @second_collection_end_date ||= FormHandler.instance.forms.map { |_name, form| form.end_date }.compact.max
end
def date_valid?(question, record)
diff --git a/app/services/csv/lettings_log_csv_service.rb b/app/services/csv/lettings_log_csv_service.rb
index 6070e91c2..66a98a20f 100644
--- a/app/services/csv/lettings_log_csv_service.rb
+++ b/app/services/csv/lettings_log_csv_service.rb
@@ -1,6 +1,6 @@
module Csv
class LettingsLogCsvService
- CSV_FIELDS_TO_OMIT = %w[hhmemb net_income_value_check sale_or_letting first_time_property_let_as_social_housing renttype needstype postcode_known is_la_inferred totchild totelder totadult net_income_known is_carehome previous_la_known is_previous_la_inferred age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known letting_allocation_unknown details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 rent_type 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 vacdays].freeze
+ CSV_FIELDS_TO_OMIT = %w[hhmemb net_income_value_check first_time_property_let_as_social_housing renttype needstype postcode_known is_la_inferred totchild totelder totadult net_income_known is_carehome previous_la_known is_previous_la_inferred age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known letting_allocation_unknown details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 rent_type_detail 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 vacdays la prevloc].freeze
def initialize(user)
@user = user
@@ -13,13 +13,7 @@ module Csv
LettingsLog.all.find_each do |record|
csv << @attributes.map do |att|
- if %w[la prevloc].include? att
- label_from_value(record.send(att))
- elsif %w[la_label prevloc_label].include? att
- record.form.get_question(att.remove("_label"), record)&.label_from_value(record.send(att.remove("_label"))) || label_from_value(record.send(att.remove("_label")))
- else
- record.form.get_question(att, record)&.label_from_value(record.send(att)) || label_from_value(record.send(att))
- end
+ label_from_value(record, att)
end
end
end
@@ -27,7 +21,17 @@ module Csv
private
- def label_from_value(value)
+ def label_from_value(record, att)
+ if %w[la prevloc].include? att
+ label_from_boolean_value(record.send(att))
+ elsif %w[mrcdate startdate voiddate].include? att
+ record.send(att)&.to_formatted_s(:govuk_date)
+ else
+ record.form.get_question(att.remove("_label"), record)&.label_from_value(record.send(att.remove("_label"))) || label_from_boolean_value(record.send(att.remove("_label")))
+ end
+ end
+
+ def label_from_boolean_value(value)
return "Yes" if value == true
return "No" if value == false
@@ -35,7 +39,7 @@ module Csv
end
def set_csv_attributes
- metadata_fields = %w[id status created_at updated_at created_by_name is_dpo owning_organisation_name managing_organisation_name]
+ metadata_fields = %w[id status created_at updated_at created_by_name is_dpo owning_organisation_name managing_organisation_name collection_start_year]
metadata_id_fields = %w[managing_organisation_id owning_organisation_id created_by_id]
scheme_and_location_ids = %w[scheme_id location_id]
scheme_attributes = %w[scheme_code scheme_service_name scheme_sensitive scheme_type scheme_registered_under_care_act scheme_owning_organisation_name scheme_managing_organisation_name scheme_primary_client_group scheme_has_other_client_group scheme_secondary_client_group scheme_support_type scheme_intended_stay scheme_created_at]
@@ -45,13 +49,20 @@ module Csv
@attributes = (metadata_fields + intersecting_attributes + remaining_attributes - metadata_id_fields + %w[unittype_sh] + scheme_attributes + location_attributes).uniq
move_la_fields
+ rename_attributes
@attributes -= CSV_FIELDS_TO_OMIT if @user.present? && !@user.support?
end
def ordered_form_questions
downloaded_form_years = LettingsLog.all.map(&:collection_start_year).uniq.compact
- downloaded_form_fields = downloaded_form_years.count == 1 && downloaded_form_years[0].present? ? FormHandler.instance.get_form("#{downloaded_form_years[0]}_#{downloaded_form_years[0] + 1}").questions : FormHandler.instance.forms.first.second.questions
+
+ if downloaded_form_years.count == 1 && downloaded_form_years[0].present?
+ form_name = FormHandler.instance.form_name_from_start_year(downloaded_form_years[0], "lettings")
+ downloaded_form_fields = FormHandler.instance.get_form(form_name).questions
+ else
+ downloaded_form_fields = FormHandler.instance.current_lettings_form.questions
+ end
move_checkbox_answer_options(downloaded_form_fields)
end
@@ -75,5 +86,12 @@ module Csv
end
end
end
+
+ def rename_attributes
+ { "rent_type" => "rent_type_detail" }.each do |original_field, new_field|
+ @attributes.insert(@attributes.find_index(original_field), new_field)
+ @attributes.delete(original_field)
+ end
+ end
end
end
diff --git a/app/services/filter_service.rb b/app/services/filter_service.rb
new file mode 100644
index 000000000..8fe1030de
--- /dev/null
+++ b/app/services/filter_service.rb
@@ -0,0 +1,22 @@
+class FilterService
+ def self.filter_by_search(base_collection, search_term = nil)
+ if search_term.present?
+ base_collection.search_by(search_term)
+ else
+ base_collection
+ end
+ end
+
+ def self.filter_logs(logs, search_term, filters, all_orgs, user)
+ logs = filter_by_search(logs, search_term)
+
+ filters.each do |category, values|
+ next if Array(values).reject(&:empty?).blank?
+ next if category == "organisation" && all_orgs
+
+ logs = logs.public_send("filter_by_#{category}", values, user)
+ end
+ logs = logs.order(created_at: :desc)
+ user.support? ? logs.all.includes(:owning_organisation, :managing_organisation) : logs
+ end
+end
diff --git a/app/services/imports/lettings_logs_import_service.rb b/app/services/imports/lettings_logs_import_service.rb
deleted file mode 100644
index 00d81b2ca..000000000
--- a/app/services/imports/lettings_logs_import_service.rb
+++ /dev/null
@@ -1,744 +0,0 @@
-require 'securerandom'
-require 'json'
-
-module Imports
- class LettingsLogsImportService < ImportService
- include Wisper::Publisher
-
- def initialize(storage_service, logger = Rails.logger)
- super
-
- Wisper.subscribe(LettingsLogImportListener.new, prefix: :on)
- end
-
- def create_logs(folder)
- import_from(folder, :create_log)
- if @logs_with_discrepancies.count.positive?
- @logger.warn("The following lettings logs had status discrepancies: [#{@logs_with_discrepancies.join(', ')}]")
- end
- end
-
- def import_from(folder, create_method)
- filenames = @storage_service.list_files(folder)
- filenames.each do |filename|
- file_io = @storage_service.get_file_io(filename)
- xml_document = Nokogiri::XML(file_io)
- send(create_method, xml_document)
- rescue StandardError => e
- @logger.error "#{e.class} in #{filename}: #{e.message}. Caller: #{e.backtrace.first}"
- end
- end
-
- def local_load(folder)
- filenames = Dir["#{folder}/**/*.xml"]
- puts "FILENAMES (#{filenames.size}): #{filenames}"
-
- @run_id = SecureRandom.uuid.to_s
- logs_import = LogsImport.create!(
- run_id: @run_id,
- started_at: Time.zone.now,
- total: filenames.size,
- discrepancies: [],
- filenames: filenames
- )
-
- redis = Redis.new
- redis.set(@run_id, Marshal.dump(logs_import))
-
-
- broadcast(::Import::STARTED, @run_id)
-
- filenames.each do |filename|
- puts "Loading filename: #{filename}"
- Rack::MiniProfiler.step("Start Processing file #{filename}") do
- # Generate background job to process file completely
- xml_document = Nokogiri::XML(File.open(filename))
-
-
- LettingsLogImportJob.perform_now(@run_id, xml_document.to_s)
- #send(:create_log, xml_document)
- end
- rescue StandardError => e
- @logger.error "#{e.class} in #{filename}: #{e.message}. Caller: #{e.backtrace.first}"
- end
-
- if @logs_with_discrepancies.count.positive?
- @logger.warn("The following lettings logs had status discrepancies: [#{@logs_with_discrepancies.join(', ')}]")
- end
- end
- end
-
-
- class LettingsLogsImportProcessor
- FORM_NAME_INDEX = {
- start_year: 0,
- rent_type: 2,
- needs_type: 3,
- }.freeze
-
- GN_SH = {
- general_needs: 1,
- supported_housing: 2,
- }.freeze
-
- SR_AR_IR = {
- social_rent: 1,
- affordable_rent: 2,
- intermediate_rent: 3,
- }.freeze
-
- # For providertype, values are reversed!!!
- PRP_LA = {
- private_registered_provider: 1,
- local_authority: 2,
- }.freeze
-
- IRPRODUCT = {
- rent_to_buy: 1,
- london_living_rent: 2,
- other_intermediate_rent_product: 3,
- }.freeze
-
- # These must match our form
- RENT_TYPE = {
- social_rent: 0,
- affordable_rent: 1,
- london_affordable_rent: 2,
- rent_to_buy: 3,
- london_living_rent: 4,
- other_intermediate_rent_product: 5,
- }.freeze
-
- attr_reader :xml_doc, :logs_with_discrepancies, :logs_overridden, :discrepancy, :old_id
-
- def initialize(xml_doc)
- @xml_doc = xml_doc
- @discrepancy = false
- @old_id = ''
-
- @logs_with_discrepancies = Set.new
- @logs_overridden = Set.new
-
- end
-
- def create_log
- attributes = {}
- lettings_log = nil
- previous_status = field_value(xml_doc, "meta", "status")
-
- Rack::MiniProfiler.step("Loading attributes") do
- # Required fields for status complete or logic to work
- # Note: order matters when we derive from previous values (attributes parameter)
- attributes["startdate"] = compose_date(xml_doc, "DAY", "MONTH", "YEAR")
- attributes["owning_organisation_id"] = find_organisation_id(xml_doc, "OWNINGORGID")
- attributes["managing_organisation_id"] = find_organisation_id(xml_doc, "MANINGORGID")
- attributes["joint"] = unsafe_string_as_integer(xml_doc, "joint")
- attributes["startertenancy"] = unsafe_string_as_integer(xml_doc, "_2a")
- attributes["tenancy"] = unsafe_string_as_integer(xml_doc, "Q2b")
- attributes["tenancycode"] = string_or_nil(xml_doc, "_2bTenCode")
- attributes["tenancyother"] = string_or_nil(xml_doc, "Q2ba")
- attributes["tenancylength"] = safe_string_as_integer(xml_doc, "_2cYears")
- attributes["needstype"] = needs_type(xml_doc)
- attributes["lar"] = london_affordable_rent(xml_doc)
- attributes["irproduct"] = unsafe_string_as_integer(xml_doc, "IRProduct")
- attributes["irproduct_other"] = string_or_nil(xml_doc, "IRProductOther")
- attributes["rent_type"] = rent_type(xml_doc, attributes["lar"], attributes["irproduct"])
- attributes["hhmemb"] = household_members(xml_doc, previous_status)
- (1..8).each do |index|
- attributes["age#{index}"] = safe_string_as_integer(xml_doc, "P#{index}Age")
- attributes["age#{index}_known"] = age_known(xml_doc, index, attributes["hhmemb"])
- attributes["sex#{index}"] = sex(xml_doc, index)
- attributes["ecstat#{index}"] = unsafe_string_as_integer(xml_doc, "P#{index}Eco")
- end
- (2..8).each do |index|
- attributes["relat#{index}"] = relat(xml_doc, index)
- attributes["details_known_#{index}"] = details_known(index, attributes)
- end
- attributes["ethnic"] = unsafe_string_as_integer(xml_doc, "P1Eth")
- attributes["ethnic_group"] = ethnic_group(attributes["ethnic"])
- attributes["national"] = unsafe_string_as_integer(xml_doc, "P1Nat")
- attributes["preg_occ"] = unsafe_string_as_integer(xml_doc, "Preg")
-
- attributes["armedforces"] = unsafe_string_as_integer(xml_doc, "ArmedF")
- attributes["leftreg"] = unsafe_string_as_integer(xml_doc, "LeftAF")
- attributes["reservist"] = unsafe_string_as_integer(xml_doc, "Inj")
-
- attributes["hb"] = unsafe_string_as_integer(xml_doc, "Q6Ben")
- attributes["benefits"] = unsafe_string_as_integer(xml_doc, "Q7Ben")
- attributes["earnings"] = safe_string_as_decimal(xml_doc, "Q8Money")
- attributes["net_income_known"] = net_income_known(xml_doc, attributes["earnings"])
- attributes["incfreq"] = unsafe_string_as_integer(xml_doc, "Q8a")
-
- attributes["reason"] = unsafe_string_as_integer(xml_doc, "Q9a")
- attributes["reasonother"] = string_or_nil(xml_doc, "Q9aa")
- attributes["underoccupation_benefitcap"] = unsafe_string_as_integer(xml_doc, "_9b")
- %w[a b c f g h].each do |letter|
- attributes["housingneeds_#{letter}"] = housing_needs(xml_doc, letter)
- end
- attributes["housingneeds"] = 1 if [attributes["housingneeds_a"], attributes["housingneeds_b"], attributes["housingneeds_c"], attributes["housingneeds_f"]].any? { |housingneed| housingneed == 1 }
- attributes["housingneeds"] = 2 if attributes["housingneeds_g"] == 1
- attributes["housingneeds"] = 3 if attributes["housingneeds_h"] == 1
- attributes["housingneeds_type"] = 0 if attributes["housingneeds_a"] == 1
- attributes["housingneeds_type"] = 1 if attributes["housingneeds_b"] == 1
- attributes["housingneeds_type"] = 2 if attributes["housingneeds_c"] == 1
- attributes["housingneeds_type"] = 3 if attributes["housingneeds_f"] == 1 && [attributes["housingneeds_a"], attributes["housingneeds_b"], attributes["housingneeds_c"]].all? { |housingneed| housingneed != 1 }
- attributes["housingneeds_other"] = attributes["housingneeds_f"] == 1 ? 1 : 0
-
- attributes["illness"] = unsafe_string_as_integer(xml_doc, "Q10ia")
- (1..10).each do |index|
- attributes["illness_type_#{index}"] = illness_type(xml_doc, index, attributes["illness"])
- end
- attributes["illness_type_0"] = 1 if (1..10).all? { |idx| attributes["illness_type_#{idx}"].nil? || attributes["illness_type_#{idx}"].zero? }
-
- attributes["prevten"] = unsafe_string_as_integer(xml_doc, "Q11")
- attributes["prevloc"] = string_or_nil(xml_doc, "Q12aONS")
- attributes["ppostcode_full"] = compose_postcode(xml_doc, "PPOSTC1", "PPOSTC2")
- attributes["ppcodenk"] = previous_postcode_known(xml_doc, attributes["ppostcode_full"], attributes["prevloc"])
- attributes["layear"] = unsafe_string_as_integer(xml_doc, "Q12c")
- attributes["waityear"] = unsafe_string_as_integer(xml_doc, "Q12d")
- attributes["homeless"] = unsafe_string_as_integer(xml_doc, "Q13")
-
- attributes["reasonpref"] = unsafe_string_as_integer(xml_doc, "Q14a")
- attributes["rp_homeless"] = unsafe_string_as_integer(xml_doc, "Q14b1").present? ? 1 : nil
- attributes["rp_insan_unsat"] = unsafe_string_as_integer(xml_doc, "Q14b2").present? ? 1 : nil
- attributes["rp_medwel"] = unsafe_string_as_integer(xml_doc, "Q14b3").present? ? 1 : nil
- attributes["rp_hardship"] = unsafe_string_as_integer(xml_doc, "Q14b4").present? ? 1 : nil
- attributes["rp_dontknow"] = unsafe_string_as_integer(xml_doc, "Q14b5").present? ? 1 : nil
-
- attributes["cbl"] = allocation_system(unsafe_string_as_integer(xml_doc, "Q15CBL"))
- attributes["chr"] = allocation_system(unsafe_string_as_integer(xml_doc, "Q15CHR"))
- attributes["cap"] = allocation_system(unsafe_string_as_integer(xml_doc, "Q15CAP"))
- attributes["letting_allocation_unknown"] = allocation_system_unknown(attributes["cbl"], attributes["chr"], attributes["cap"])
-
- attributes["referral"] = unsafe_string_as_integer(xml_doc, "Q16")
- attributes["period"] = unsafe_string_as_integer(xml_doc, "Q17")
-
- attributes["brent"] = safe_string_as_decimal(xml_doc, "Q18ai")
- attributes["scharge"] = safe_string_as_decimal(xml_doc, "Q18aii")
- attributes["pscharge"] = safe_string_as_decimal(xml_doc, "Q18aiii")
- attributes["supcharg"] = safe_string_as_decimal(xml_doc, "Q18aiv")
- attributes["tcharge"] = safe_string_as_decimal(xml_doc, "Q18av")
-
- attributes["hbrentshortfall"] = unsafe_string_as_integer(xml_doc, "Q18d")
- attributes["tshortfall"] = safe_string_as_decimal(xml_doc, "Q18dyes")
- attributes["tshortfall_known"] = tshortfall_known?(xml_doc, attributes)
-
- attributes["voiddate"] = compose_date(xml_doc, "VDAY", "VMONTH", "VYEAR")
- attributes["mrcdate"] = compose_date(xml_doc, "MRCDAY", "MRCMONTH", "MRCYEAR")
- attributes["majorrepairs"] = if attributes["mrcdate"].present? && previous_status.include?("submitted")
- 1
- elsif previous_status.include?("submitted")
- 0
- end
-
- attributes["offered"] = safe_string_as_integer(xml_doc, "Q20")
- attributes["propcode"] = string_or_nil(xml_doc, "Q21a")
- attributes["beds"] = safe_string_as_integer(xml_doc, "Q22")
- attributes["unittype_gn"] = unsafe_string_as_integer(xml_doc, "Q23")
- attributes["builtype"] = unsafe_string_as_integer(xml_doc, "Q24")
- attributes["wchair"] = unsafe_string_as_integer(xml_doc, "Q25")
- attributes["unitletas"] = unsafe_string_as_integer(xml_doc, "Q26")
- attributes["rsnvac"] = unsafe_string_as_integer(xml_doc, "Q27")
- attributes["renewal"] = renewal(attributes["rsnvac"])
-
- attributes["la"] = string_or_nil(xml_doc, "Q28ONS")
- attributes["postcode_full"] = compose_postcode(xml_doc, "POSTCODE", "POSTCOD2")
- attributes["postcode_known"] = postcode_known(attributes)
-
- # Not specific to our form but required for consistency (present in import)
- attributes["old_form_id"] = safe_string_as_integer(xml_doc, "FORM")
- attributes["created_at"] = Time.zone.parse(field_value(xml_doc, "meta", "created-date"))
- attributes["updated_at"] = Time.zone.parse(field_value(xml_doc, "meta", "modified-date"))
- attributes["old_id"] = field_value(xml_doc, "meta", "document-id")
- @old_id = attributes["old_id"]
-
- # Required for our form invalidated questions (not present in import)
- attributes["previous_la_known"] = attributes["prevloc"].nil? ? 0 : 1
- attributes["is_la_inferred"] = attributes["postcode_full"].present?
- attributes["first_time_property_let_as_social_housing"] = first_time_let(attributes["rsnvac"])
- attributes["declaration"] = declaration(xml_doc)
-
- set_partial_charges_to_zero(attributes)
-
- # Supported Housing fields
- if attributes["needstype"] == GN_SH[:supported_housing]
- location_old_visible_id = safe_string_as_integer(xml_doc, "_1cschemecode")
- scheme_old_visible_id = safe_string_as_integer(xml_doc, "_1cmangroupcode")
-
- schemes = Scheme.where(old_visible_id: scheme_old_visible_id, owning_organisation_id: attributes["owning_organisation_id"])
- location = Location.find_by(old_visible_id: location_old_visible_id, scheme: schemes)
- raise "No matching location for scheme #{scheme_old_visible_id} and location #{location_old_visible_id} (visible IDs)" if location.nil?
-
- # Set the scheme via location, because the scheme old visible ID can be duplicated at import
- attributes["location_id"] = location.id
- attributes["scheme_id"] = location.scheme.id
- attributes["sheltered"] = unsafe_string_as_integer(xml_doc, "Q1e")
- attributes["chcharge"] = safe_string_as_decimal(xml_doc, "Q18b")
- attributes["household_charge"] = household_charge(xml_doc)
- attributes["is_carehome"] = is_carehome(location.scheme)
- end
-
- # Handles confidential schemes
- if attributes["postcode_full"] == "******"
- attributes["postcode_known"] = 0
- attributes["postcode_full"] = nil
- end
-
- # Soft validations can become required answers, set them to yes by default
- attributes["pregnancy_value_check"] = 0
- attributes["major_repairs_date_value_check"] = 0
- attributes["void_date_value_check"] = 0
- attributes["retirement_value_check"] = 0
- attributes["rent_value_check"] = 0
- attributes["net_income_value_check"] = 0
-
- # Sets the log creator
- owner_id = field_value(xml_doc, "meta", "owner-user-id").strip
- if owner_id.present?
- attributes["created_by"] = User.find_by(old_user_id: owner_id)
- end
-
- apply_date_consistency!(attributes)
- apply_household_consistency!(attributes)
- end # ENDPROFILER
-
-
- lettings_log = save_lettings_log(attributes)
-
- compute_differences(lettings_log, attributes)
- check_status_completed(lettings_log, previous_status) unless @logs_overridden.include?(lettings_log.old_id)
-
- end
-
- def save_lettings_log(attributes)
- lettings_log = LettingsLog.new(attributes)
- begin
- Rack::MiniProfiler.step("Saving...") do
- lettings_log.save!(validate: true)
- end
- lettings_log
- rescue ActiveRecord::RecordNotUnique
- legacy_id = attributes["old_id"]
- record = LettingsLog.find_by(old_id: legacy_id)
- @logger.info "Updating lettings log #{record.id} with legacy ID #{legacy_id}"
- record.update!(attributes)
- record
- rescue ActiveRecord::RecordInvalid => e
- rescue_validation_or_raise(lettings_log, attributes, e)
- end
- end
-
- def rescue_validation_or_raise(lettings_log, attributes, exception)
- if lettings_log.errors.of_kind?(:referral, :internal_transfer_non_social_housing)
- @logger.warn("Log #{lettings_log.old_id}: Removing internal transfer referral since previous tenancy is a non social housing")
- @logs_overridden << lettings_log.old_id
- attributes.delete("referral")
- save_lettings_log(attributes)
- elsif lettings_log.errors.of_kind?(:referral, :internal_transfer_fixed_or_lifetime)
- @logger.warn("Log #{lettings_log.old_id}: Removing internal transfer referral since previous tenancy is fixed terms or lifetime")
- @logs_overridden << lettings_log.old_id
- attributes.delete("referral")
- save_lettings_log(attributes)
- else
- raise exception
- end
- end
-
- def compute_differences(lettings_log, attributes)
- differences = []
- attributes.each do |key, value|
- lettings_log_value = lettings_log.send(key.to_sym)
- next if fields_not_present_in_softwire_data.include?(key)
-
- if value != lettings_log_value
- differences.push("#{key} #{value.inspect} #{lettings_log_value.inspect}")
- end
- end
- @logger.warn "Differences found when saving log #{lettings_log.old_id}: #{differences}" unless differences.empty?
- end
-
- def fields_not_present_in_softwire_data
- %w[majorrepairs illness_type_0 tshortfall_known pregnancy_value_check retirement_value_check rent_value_check net_income_value_check major_repairs_date_value_check void_date_value_check housingneeds_type housingneeds_other]
- end
-
- def check_status_completed(lettings_log, previous_status)
- if previous_status.include?("submitted") && lettings_log.status != "completed"
- @logger.warn "lettings log #{lettings_log.id} is not completed"
- @logger.warn "lettings log with old id:#{lettings_log.old_id} is incomplete but status should be complete"
- @logs_with_discrepancies << lettings_log.old_id
- @discrepancy = true
- end
- end
-
- # Safe: A string that represents only an integer (or empty/nil)
- def safe_string_as_integer(xml_doc, attribute)
- str = field_value(xml_doc, "xmlns", attribute)
- Integer(str, exception: false)
- end
-
- # Safe: A string that represents only a decimal (or empty/nil)
- def safe_string_as_decimal(xml_doc, attribute)
- str = string_or_nil(xml_doc, attribute)
- if str.nil?
- nil
- else
- BigDecimal(str, exception: false)
- end
- end
-
- # Unsafe: A string that has more than just the integer value
- def unsafe_string_as_integer(xml_doc, attribute)
- str = string_or_nil(xml_doc, attribute)
- if str.nil?
- nil
- else
- str.to_i
- end
- end
-
- def compose_date(xml_doc, day_str, month_str, year_str)
- day = Integer(field_value(xml_doc, "xmlns", day_str), exception: false)
- month = Integer(field_value(xml_doc, "xmlns", month_str), exception: false)
- year = Integer(field_value(xml_doc, "xmlns", year_str), exception: false)
- if day.nil? || month.nil? || year.nil?
- nil
- else
- Time.zone.local(year, month, day)
- end
- end
-
- def get_form_name_component(xml_doc, index)
- form_name = field_value(xml_doc, "meta", "form-name")
- form_type_components = form_name.split("-")
- form_type_components[index]
- end
-
- def needs_type(xml_doc)
- gn_sh = get_form_name_component(xml_doc, FORM_NAME_INDEX[:needs_type])
- case gn_sh
- when "GN"
- GN_SH[:general_needs]
- when "SH"
- GN_SH[:supported_housing]
- else
- raise "Unknown needstype value: #{gn_sh}"
- end
- end
-
- # This does not match renttype (CDS) which is derived by lettings log logic
- def rent_type(xml_doc, lar, irproduct)
- sr_ar_ir = get_form_name_component(xml_doc, FORM_NAME_INDEX[:rent_type])
-
- case sr_ar_ir
- when "SR"
- RENT_TYPE[:social_rent]
- when "AR"
- if lar == 1
- RENT_TYPE[:london_affordable_rent]
- else
- RENT_TYPE[:affordable_rent]
- end
- when "IR"
- if irproduct == IRPRODUCT[:rent_to_buy]
- RENT_TYPE[:rent_to_buy]
- elsif irproduct == IRPRODUCT[:london_living_rent]
- RENT_TYPE[:london_living_rent]
- elsif irproduct == IRPRODUCT[:other_intermediate_rent_product]
- RENT_TYPE[:other_intermediate_rent_product]
- end
- else
- raise "Could not infer rent type with '#{sr_ar_ir}'"
- end
- end
-
- def find_organisation_id(xml_doc, id_field)
- old_visible_id = unsafe_string_as_integer(xml_doc, id_field)
- organisation = Organisation.find_by(old_visible_id:)
- raise "Organisation not found with legacy ID #{old_visible_id}" if organisation.nil?
-
- organisation.id
- end
-
- def sex(xml_doc, index)
- sex = string_or_nil(xml_doc, "P#{index}Sex")
- case sex
- when "Male"
- "M"
- when "Female"
- "F"
- when "Other", "Non-binary"
- "X"
- when "Refused"
- "R"
- end
- end
-
- def relat(xml_doc, index)
- relat = string_or_nil(xml_doc, "P#{index}Rel")
- case relat
- when "Child"
- "C"
- when "Partner"
- "P"
- when "Other", "Non-binary"
- "X"
- when "Refused"
- "R"
- end
- end
-
- def age_known(xml_doc, index, hhmemb)
- return nil if hhmemb.present? && index > hhmemb
-
- age_refused = string_or_nil(xml_doc, "P#{index}AR")
- if age_refused.present?
- if age_refused.casecmp?("AGE_REFUSED") || age_refused.casecmp?("No")
- return 1 # No
- else
- return 0 # Yes
- end
- end
- 0
- end
-
- def details_known(index, attributes)
- return nil if attributes["hhmemb"].nil? || index > attributes["hhmemb"]
-
- if attributes["age#{index}_known"] == 1 &&
- attributes["sex#{index}"] == "R" &&
- attributes["relat#{index}"] == "R" &&
- attributes["ecstat#{index}"] == 10
- 1 # No
- else
- 0 # Yes
- end
- end
-
- def previous_postcode_known(xml_doc, previous_postcode, prevloc)
- previous_postcode_known = string_or_nil(xml_doc, "Q12bnot")
- if previous_postcode_known == "Temporary_or_Unknown" || (previous_postcode.nil? && prevloc.present?)
- 0
- elsif previous_postcode.nil?
- nil
- else
- 1
- end
- end
-
- def compose_postcode(xml_doc, outcode, incode)
- outcode_value = string_or_nil(xml_doc, outcode)
- incode_value = string_or_nil(xml_doc, incode)
- if outcode_value.nil? || incode_value.nil? || !"#{outcode_value} #{incode_value}".match(POSTCODE_REGEXP)
- nil
- else
- "#{outcode_value} #{incode_value}"
- end
- end
-
- def london_affordable_rent(xml_doc)
- lar = unsafe_string_as_integer(xml_doc, "LAR")
- if lar == 1
- 1
- else
- # We default to No for any other values (nil, not known)
- 2
- end
- end
-
- def renewal(rsnvac)
- # Relet – renewal of fixed-term tenancy
- if rsnvac == 14
- 1
- else
- 0
- end
- end
-
- def string_or_nil(xml_doc, attribute)
- str = field_value(xml_doc, "xmlns", attribute)
- str.presence
- end
-
- def ethnic_group(ethnic)
- case ethnic
- when 1, 2, 3, 18
- # White
- 0
- when 4, 5, 6, 7
- # Mixed
- 1
- when 8, 9, 10, 11, 15
- # Asian
- 2
- when 12, 13, 14
- # Black
- 3
- when 16, 19
- # Others
- 4
- when 17
- # Refused
- 17
- end
- end
-
- # Letters should be lowercase to match case
- def housing_needs(xml_doc, letter)
- housing_need = string_or_nil(xml_doc, "Q10-#{letter}")
- if housing_need == "Yes"
- 1
- else
- 0
- end
- end
-
- def net_income_known(xml_doc, earnings)
- incref = string_or_nil(xml_doc, "Q8Refused")
- if incref == "Refused"
- # Tenant prefers not to say
- 2
- elsif earnings.nil?
- # No
- 1
- else
- # Yes
- 0
- end
- end
-
- def illness_type(xml_doc, index, illness)
- illness_type = string_or_nil(xml_doc, "Q10ib-#{index}")
- if illness_type == "Yes" && illness == 1
- 1
- elsif illness == 1
- 0
- end
- end
-
- def first_time_let(rsnvac)
- if [15, 16, 17].include?(rsnvac)
- 1
- else
- 0
- end
- end
-
- def declaration(xml_doc)
- declaration = string_or_nil(xml_doc, "Qdp")
- if declaration == "Yes"
- 1
- end
- end
-
- def postcode_known(attributes)
- if attributes["postcode_full"].nil? && attributes["la"].nil?
- nil
- elsif attributes["postcode_full"].nil?
- 0 # Assumes we selected No in the form since the LA is present
- else
- 1
- end
- end
-
- def household_members(xml_doc, previous_status)
- hhmemb = safe_string_as_integer(xml_doc, "HHMEMB")
- if previous_status.include?("submitted") && hhmemb.nil?
- hhmemb = people_with_details(xml_doc).count
- return nil if hhmemb.zero?
- end
- hhmemb
- end
-
- def people_with_details(xml_doc)
- ((2..8).map { |x| string_or_nil(xml_doc, "P#{x}Rel") } + [string_or_nil(xml_doc, "P1Sex")]).compact
- end
-
- def tshortfall_known?(xml_doc, attributes)
- if attributes["tshortfall"].blank? && attributes["hbrentshortfall"] == 1 && overridden?(xml_doc, "xmlns", "Q18dyes")
- 1
- else
- 0
- end
- end
-
- def allocation_system(value)
- case value
- when 1
- 1
- when 2
- 0
- end
- end
-
- def allocation_system_unknown(cbl, chr, cap)
- allocation_values = [cbl, chr, cap]
- if allocation_values.all?(&:nil?)
- nil
- elsif allocation_values.all? { |att| att&.zero? }
- 1
- else
- 0
- end
- end
-
- def apply_date_consistency!(attributes)
- return if attributes["voiddate"].nil? || attributes["startdate"].nil?
-
- if attributes["voiddate"] > attributes["startdate"]
- attributes.delete("voiddate")
- end
- end
-
- def apply_household_consistency!(attributes)
- (2..8).each do |index|
- next if attributes["age#{index}"].nil?
-
- if attributes["age#{index}"] < 16 && attributes["relat#{index}"] == "R"
- attributes["relat#{index}"] = "C"
- end
- end
- end
-
- def household_charge(xml_doc)
- value = string_or_nil(xml_doc, "Q18c")
- start_year = Integer(get_form_name_component(xml_doc, FORM_NAME_INDEX[:start_year]))
-
- if start_year <= 2021
- # Yes means that there are no charges (2021 or earlier)
- value && value.include?("Yes") ? 1 : 0
- else
- # Yes means that there are charges (2022 onwards)
- value && value.include?("Yes") ? 0 : 1
- end
- end
-
- def set_partial_charges_to_zero(attributes)
- unless attributes["brent"].nil? &&
- attributes["scharge"].nil? &&
- attributes["pscharge"].nil? &&
- attributes["supcharg"].nil?
- attributes["brent"] ||= BigDecimal("0.0")
- attributes["scharge"] ||= BigDecimal("0.0")
- attributes["pscharge"] ||= BigDecimal("0.0")
- attributes["supcharg"] ||= BigDecimal("0.0")
- end
- end
-
- def is_carehome(scheme)
- return nil unless scheme
-
- if [2, 3, 4].include?(scheme.registered_under_care_act_before_type_cast)
- 1
- else
- 0
- end
- end
-
- def discrepancy?
- !!@discrepancy
- end
- end
-
-
-
-end
diff --git a/app/services/imports/user_import_service.rb b/app/services/imports/user_import_service.rb
index 1d1fbc84e..f5a730479 100644
--- a/app/services/imports/user_import_service.rb
+++ b/app/services/imports/user_import_service.rb
@@ -17,7 +17,7 @@ module Imports
name = user_field_value(xml_document, "full-name") || email
deleted = user_field_value(xml_document, "deleted")
- if User.find_by(old_user_id:, organisation:)
+ if LegacyUser.find_by(old_user_id:)
@logger.warn("User #{name} with old user id #{old_user_id} is already present, skipping.")
elsif deleted == "true"
@logger.warn("User #{name} with old user id #{old_user_id} is deleted, skipping.")
@@ -25,20 +25,22 @@ module Imports
is_dpo = user.is_data_protection_officer? || is_dpo?(user_field_value(xml_document, "user-type"))
role = highest_role(user.role, role(user_field_value(xml_document, "user-type")))
user.update!(role:, is_dpo:)
+ user.legacy_users.create!(old_user_id:)
@logger.info("Found duplicated email, updating user #{user.id} with role #{role} and is_dpo #{is_dpo}")
else
- User.create!(
+ user = User.create!(
email:,
name:,
password: Devise.friendly_token,
phone: user_field_value(xml_document, "telephone-no"),
- old_user_id:,
organisation:,
role: role(user_field_value(xml_document, "user-type")),
is_dpo: is_dpo?(user_field_value(xml_document, "user-type")),
is_key_contact: is_key_contact?(user_field_value(xml_document, "contact-priority-id")),
active: user_field_value(xml_document, "active"),
)
+ user.legacy_users.create!(old_user_id:)
+ user
end
end
diff --git a/app/services/storage/s3_service.rb b/app/services/storage/s3_service.rb
index aee76398d..c972225a1 100644
--- a/app/services/storage/s3_service.rb
+++ b/app/services/storage/s3_service.rb
@@ -20,6 +20,12 @@ module Storage
response.key_count == 1
end
+ def get_presigned_url(file_name, duration)
+ Aws::S3::Presigner
+ .new({ client: @client })
+ .presigned_url(:get_object, bucket: @configuration.bucket_name, key: file_name, expires_in: duration)
+ end
+
def get_file_io(file_name)
@client.get_object(bucket: @configuration.bucket_name, key: file_name)
.body
diff --git a/app/views/form/_check_answers_summary_list.html.erb b/app/views/form/_check_answers_summary_list.html.erb
index 85c29beca..68a9cf00a 100644
--- a/app/views/form/_check_answers_summary_list.html.erb
+++ b/app/views/form/_check_answers_summary_list.html.erb
@@ -1,21 +1,21 @@
<%= govuk_summary_list do |summary_list| %>
- <% total_applicable_questions(subsection, @lettings_log, current_user).each do |question| %>
+ <% total_applicable_questions(subsection, @log, current_user).each do |question| %>
<% summary_list.row do |row| %>
<% row.key { question.check_answer_label.to_s.presence || question.header.to_s } %>
<% row.value do %>
- <%= get_answer_label(question, @lettings_log) %>
- <% extra_value = question.get_extra_check_answer_value(@lettings_log) %>
+ <%= get_answer_label(question, @log) %>
+ <% extra_value = question.get_extra_check_answer_value(@log) %>
<% if extra_value %>
<%= extra_value %>
<% end %>
- <% question.get_inferred_answers(@lettings_log).each do |inferred_answer| %>
+ <% question.get_inferred_answers(@log).each do |inferred_answer| %>
<%= inferred_answer %>
<% end %>
<% end %>
<% row.action(
- text: question.action_text(@lettings_log),
- href: question.action_href(@lettings_log, question.page.id),
+ text: question.action_text(@log),
+ href: question.action_href(@log, question.page.id),
visually_hidden_text: question.check_answer_label.to_s.downcase,
) %>
<% end %>
diff --git a/app/views/form/_checkbox_question.html.erb b/app/views/form/_checkbox_question.html.erb
index 245056c39..c855e2d88 100644
--- a/app/views/form/_checkbox_question.html.erb
+++ b/app/views/form/_checkbox_question.html.erb
@@ -6,7 +6,7 @@
hint: { text: question.hint_text&.html_safe } do %>
<% after_divider = false %>
- <% question.displayed_answer_options(@lettings_log).map do |key, options| %>
+ <% question.displayed_answer_options(@log).map do |key, options| %>
<% if key.starts_with?("divider") %>
<% after_divider = true %>
<%= f.govuk_check_box_divider %>
@@ -14,7 +14,7 @@
<%= f.govuk_check_box question.id, key,
label: { text: options["value"] },
hint: { text: options["hint"] },
- checked: @lettings_log[key] == 1,
+ checked: @log[key] == 1,
exclusive: after_divider,
**stimulus_html_attributes(question) %>
<% end %>
diff --git a/app/views/form/_numeric_question.html.erb b/app/views/form/_numeric_question.html.erb
index fc6aa274a..0aeb63801 100644
--- a/app/views/form/_numeric_question.html.erb
+++ b/app/views/form/_numeric_question.html.erb
@@ -8,7 +8,7 @@
width: question.width,
readonly: question.read_only?,
prefix_text: question.prefix.to_s,
- suffix_text: question.suffix_label(@lettings_log),
+ suffix_text: question.suffix_label(@log),
**stimulus_html_attributes(question) %>
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.bottom_guidance? %>
diff --git a/app/views/form/_radio_question.html.erb b/app/views/form/_radio_question.html.erb
index f153a838b..ed5e71a0b 100644
--- a/app/views/form/_radio_question.html.erb
+++ b/app/views/form/_radio_question.html.erb
@@ -5,7 +5,7 @@
legend: legend(question, page_header, conditional),
hint: { text: question.hint_text&.html_safe } do %>
- <% question.displayed_answer_options(@lettings_log).map do |key, options| %>
+ <% question.displayed_answer_options(@log).map do |key, options| %>
<% if key.starts_with?("divider") %>
<%= f.govuk_radio_divider %>
<% else %>
diff --git a/app/views/form/_select_question.html.erb b/app/views/form/_select_question.html.erb
index fbdb20e9a..63f50fb94 100644
--- a/app/views/form/_select_question.html.erb
+++ b/app/views/form/_select_question.html.erb
@@ -1,7 +1,7 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.top_guidance? %>
-<% selected = @lettings_log.public_send(question.id) || "" %>
-<% answers = question.displayed_answer_options(@lettings_log).map { |key, value| OpenStruct.new(id: key, name: value.respond_to?(:service_name) ? value.service_name : nil, resource: value) } %>
+<% selected = @log.public_send(question.id) || "" %>
+<% answers = question.displayed_answer_options(@log).map { |key, value| OpenStruct.new(id: key, name: value.respond_to?(:service_name) ? value.service_name : nil, resource: value) } %>
<%= f.govuk_select(question.id.to_sym,
label: legend(question, page_header, conditional),
"data-controller": "accessible-autocomplete",
@@ -12,7 +12,7 @@
data-synonyms="<%= question.answer_option_synonyms(answer.resource) %>"
data-append="<%= question.answer_option_append(answer.resource) %>"
data-hint="<%= question.answer_option_hint(answer.resource) %>"
- <%= question.answer_selected?(@lettings_log, answer) ? "selected" : "" %>
+ <%= question.answer_selected?(@log, answer) ? "selected" : "" %>
<%= answer.id == "" ? "disabled" : "" %>><%= answer.name || answer.resource %>
<% end %>
<% end %>
diff --git a/app/views/form/check_answers.html.erb b/app/views/form/check_answers.html.erb
index a2bcb09bc..46a945017 100644
--- a/app/views/form/check_answers.html.erb
+++ b/app/views/form/check_answers.html.erb
@@ -1,9 +1,9 @@
<% content_for :title, "#{subsection.id.humanize} - Check your answers" %>
<% content_for :breadcrumbs, govuk_breadcrumbs(breadcrumbs: {
"Logs" => "/logs",
- "Log #{@lettings_log.id}" => "/logs/#{@lettings_log.id}",
+ "Log #{@log.id}" => send("#{@log.class.name.underscore}_path", @log),
subsection.label => "",
-}) %>
+ }) %>
- You can review and make changes to this log until 2nd June <%= @lettings_log.collection_start_year.present? ? @lettings_log.collection_start_year + 1 : "" %>. + You can review and make changes to this log until 2nd June <%= @log.collection_start_year.present? ? @log.collection_start_year + 1 : "" %>.
- <% @lettings_log.form.sections.map do |section| %> + <% @log.form.sections.map do |section| %>It should arrive in a few minutes, but it could take longer.
+ +Open your email inbox and click the link to download your CSV file.
+ ++ <%= govuk_link_to "Return to logs", lettings_logs_path %> +
+We'll send a secure download link to your email address <%= @current_user.email %>.
+You've selected <%= count %> logs.
+ + <%= govuk_button_to "Send email", post_path, method: :post, params: { search: search_term } %> +<%= get_subsections_count(@lettings_log, :completed) %> of <%= get_subsections_count(@lettings_log, :all) %> sections completed.
+ <% if @log.status == "in_progress" %> +<%= get_subsections_count(@log, :completed) %> of <%= get_subsections_count(@log, :all) %> sections completed.
- <% next_incomplete_section = get_next_incomplete_section(@lettings_log) %> + <% next_incomplete_section = get_next_incomplete_section(@log) %>
<% if next_incomplete_section.present? %> @@ -22,14 +22,14 @@ <% end %>
- <% elsif @lettings_log.status == "not_started" %> + <% elsif @log.status == "not_started" %>This log has not been started.
- <% elsif @lettings_log.status == "completed" %> + <% elsif @log.status == "completed" %>- <%= status_tag(@lettings_log.status) %> + <%= status_tag(@log.status) %>
- You can <%= govuk_link_to "review and make changes to this log", "/logs/#{@lettings_log.id}/review" %> until 2nd June <%= @lettings_log.collection_start_year.present? ? @lettings_log.collection_start_year + 1 : "" %>. + You can <%= govuk_link_to "review and make changes to this log", "/lettings-logs/#{@log.id}/review" %> until 2nd June <%= @log.collection_start_year.present? ? @log.collection_start_year + 1 : "" %>.
<% end %> <%= render "tasklist" %> diff --git a/app/views/lettings_logs/index.html.erb b/app/views/logs/index.html.erb similarity index 61% rename from app/views/lettings_logs/index.html.erb rename to app/views/logs/index.html.erb index 9378aee09..7c75230d4 100644 --- a/app/views/lettings_logs/index.html.erb +++ b/app/views/logs/index.html.erb @@ -7,7 +7,12 @@