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 31c3bffac..4be77aff0 100644 --- a/app/components/check_answers_summary_list_card_component.html.erb +++ b/app/components/check_answers_summary_list_card_component.html.erb @@ -36,7 +36,7 @@ <% if @log.collection_period_open? %> <% row.action( text: question.action_text(log), - href: question.action_href(log, question.page.id), + href: 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 62469e324..c9409462a 100644 --- a/app/components/check_answers_summary_list_card_component.rb +++ b/app/components/check_answers_summary_list_card_component.rb @@ -28,6 +28,10 @@ class CheckAnswersSummaryListCardComponent < ViewComponent::Base "Person #{question.check_answers_card_number}" end + def action_href(log, page_id, referrer = "check_answers") + send("#{log.model_name.param_key}_#{page_id}_path", log, referrer:) + end + private def unanswered_value diff --git a/app/controllers/form_controller.rb b/app/controllers/form_controller.rb index dbc5ba490..5bd151181 100644 --- a/app/controllers/form_controller.rb +++ b/app/controllers/form_controller.rb @@ -11,6 +11,7 @@ class FormController < ApplicationController mandatory_questions_with_no_response = mandatory_questions_with_no_response(responses_for_page) if mandatory_questions_with_no_response.empty? && @log.update(responses_for_page.merge(updated_by: current_user)) + flash[:notice] = "You have successfully updated #{@page.questions.map(&:check_answer_label).first.downcase}" if previous_interruption_screen_page_id.present? redirect_to(successful_redirect_path) else mandatory_questions_with_no_response.map do |question| @@ -45,11 +46,16 @@ class FormController < ApplicationController end def show_page + if request.params["referrer"] == "interruption_screen" + @interruption_page_id = URI.parse(request.headers["HTTP_REFERER"]).path.split("/").last.underscore + @interruption_page_referrer_type = referrer_from_query + end + if @log page_id = request.path.split("/")[-1].underscore @page = form.get_page(page_id) @subsection = form.subsection_for_page(@page) - if @page.routed_to?(@log, current_user) + if @page.routed_to?(@log, current_user) || is_referrer_type?("interruption_screen") render "form/page" else redirect_to @log.lettings? ? lettings_log_path(@log) : sales_log_path(@log) @@ -115,13 +121,33 @@ private end end - def is_referrer_check_answers? - referrer = request.headers["HTTP_REFERER"].presence || "" - referrer.present? && CGI.parse(referrer.split("?")[-1]).present? && CGI.parse(referrer.split("?")[-1])["referrer"][0] == "check_answers" + def is_referrer_type?(referrer_type) + referrer_from_query == referrer_type + end + + def referrer_from_query + referrer = request.headers["HTTP_REFERER"] + return unless referrer + + query_params = URI.parse(referrer).query + return unless query_params + + parsed_params = CGI.parse(query_params) + return unless parsed_params["referrer"] + + parsed_params["referrer"][0] + end + + def previous_interruption_screen_page_id + params[@log.model_name.param_key]["interruption_page_id"] + end + + def previous_interruption_screen_referrer + params[@log.model_name.param_key]["interruption_page_referrer_type"].presence end def successful_redirect_path - if is_referrer_check_answers? + if is_referrer_type?("check_answers") next_page_id = form.next_page_id(@page, @log, current_user) next_page = form.get_page(next_page_id) previous_page = form.previous_page_id(@page, @log, current_user) @@ -132,6 +158,10 @@ private return send("#{@log.model_name.param_key}_#{form.subsection_for_page(@page).id}_check_answers_path", @log) end end + if previous_interruption_screen_page_id.present? + return send("#{@log.class.name.underscore}_#{previous_interruption_screen_page_id}_path", @log, { referrer: previous_interruption_screen_referrer }.compact) + end + redirect_path = form.next_page_redirect_path(@page, @log, current_user) send(redirect_path, @log) end diff --git a/app/controllers/lettings_logs_controller.rb b/app/controllers/lettings_logs_controller.rb index c97f5f45d..169ff42e3 100644 --- a/app/controllers/lettings_logs_controller.rb +++ b/app/controllers/lettings_logs_controller.rb @@ -7,11 +7,6 @@ class LettingsLogsController < LogsController before_action :extract_bulk_upload_from_session_filters, only: [:index] before_action :redirect_if_bulk_upload_resolved, only: [:index] - def authenticate_scope! - codes_only_export = codes_only_export?(params) - head :unauthorized and return unless current_user.support? || !codes_only_export - end - def index respond_to do |format| format.html do @@ -86,19 +81,13 @@ class LettingsLogsController < LogsController def download_csv unpaginated_filtered_logs = filtered_logs(current_user.lettings_logs, search_term, @session_filters) - codes_only = codes_only_export?(params) - - render "download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: email_csv_lettings_logs_path, codes_only: } - end - def codes_only_export?(params) - params.require(:codes_only) == "true" + render "download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: email_csv_lettings_logs_path, codes_only: codes_only_export? } end def email_csv all_orgs = params["organisation_select"] == "all" - codes_only_export = params.require(:codes_only) == "true" - EmailCsvJob.perform_later(current_user, search_term, @session_filters, all_orgs, nil, codes_only_export) + EmailCsvJob.perform_later(current_user, search_term, @session_filters, all_orgs, nil, codes_only_export?) redirect_to csv_confirmation_lettings_logs_path end @@ -124,6 +113,10 @@ class LettingsLogsController < LogsController private + def authenticate_scope! + head :unauthorized and return if codes_only_export? && !current_user.support? + end + def redirect_if_bulk_upload_resolved if @bulk_upload && @bulk_upload.lettings_logs.in_progress.count.zero? redirect_to resume_bulk_upload_lettings_result_path(@bulk_upload) diff --git a/app/controllers/logs_controller.rb b/app/controllers/logs_controller.rb index 603608241..12156d5aa 100644 --- a/app/controllers/logs_controller.rb +++ b/app/controllers/logs_controller.rb @@ -28,6 +28,10 @@ private end end + def codes_only_export? + params.require(:codes_only) == "true" + end + def post_create_redirect_url raise "implement in sub class" end diff --git a/app/controllers/merge_requests_controller.rb b/app/controllers/merge_requests_controller.rb index 17959e264..da7b9ae75 100644 --- a/app/controllers/merge_requests_controller.rb +++ b/app/controllers/merge_requests_controller.rb @@ -8,6 +8,7 @@ class MergeRequestsController < ApplicationController confirm_telephone_number new_organisation_name new_organisation_address + new_organisation_telephone_number merge_date ] before_action :authenticate_user! @@ -17,6 +18,7 @@ class MergeRequestsController < ApplicationController def confirm_telephone_number; end def new_organisation_name; end def new_organisation_address; end + def new_organisation_telephone_number; end def merge_date; end def create @@ -79,6 +81,8 @@ private merge_date_merge_request_path(@merge_request) when "new_organisation_name" new_organisation_address_merge_request_path(@merge_request) + when "new_organisation_address" + new_organisation_telephone_number_merge_request_path(@merge_request) end end @@ -108,6 +112,9 @@ private :telephone_number_correct, :new_telephone_number, :new_organisation_name, + :new_organisation_address_line1, + :new_organisation_address_line2, + :new_organisation_postcode, ) if merge_params[:requesting_organisation_id].present? && (current_user.data_coordinator? || current_user.data_provider?) diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb index 70943063f..b430136ae 100644 --- a/app/controllers/organisations_controller.rb +++ b/app/controllers/organisations_controller.rb @@ -6,8 +6,8 @@ class OrganisationsController < ApplicationController before_action :authenticate_user! before_action :find_resource, except: %i[index new create] before_action :authenticate_scope!, except: [:index] - before_action -> { session_filters(specific_org: true) }, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_csv download_csv] - before_action :set_session_filters, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_csv download_csv] + before_action -> { session_filters(specific_org: true) }, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_lettings_csv download_lettings_csv email_sales_csv download_sales_csv] + before_action :set_session_filters, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_lettings_csv download_lettings_csv email_sales_csv download_sales_csv] def index redirect_to organisation_path(current_user.organisation) unless current_user.support? @@ -99,23 +99,23 @@ class OrganisationsController < ApplicationController @pagy, @logs = pagy(unpaginated_filtered_logs) @searched = search_term.presence @total_count = organisation_logs.size + @log_type = :lettings render "logs", layout: "application" end end end - def download_csv + def download_lettings_csv organisation_logs = LettingsLog.visible.where(owning_organisation_id: @organisation.id) unpaginated_filtered_logs = filtered_logs(organisation_logs, search_term, @session_filters) codes_only = params.require(:codes_only) == "true" - render "logs/download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: logs_email_csv_organisation_path, codes_only: } + render "logs/download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: lettings_logs_email_csv_organisation_path, codes_only: } end - def email_csv - codes_only_export = params.require(:codes_only) == "true" - EmailCsvJob.perform_later(current_user, search_term, @session_filters, false, @organisation, codes_only_export) - redirect_to logs_csv_confirmation_organisation_path + def email_lettings_csv + EmailCsvJob.perform_later(current_user, search_term, @session_filters, false, @organisation, codes_only_export?) + redirect_to lettings_logs_csv_confirmation_organisation_path end def sales_logs @@ -128,6 +128,7 @@ class OrganisationsController < ApplicationController @pagy, @logs = pagy(unpaginated_filtered_logs) @searched = search_term.presence @total_count = organisation_logs.size + @log_type = :sales render "logs", layout: "application" end @@ -137,6 +138,19 @@ class OrganisationsController < ApplicationController end end + def download_sales_csv + organisation_logs = SalesLog.visible.where(owning_organisation_id: @organisation.id) + unpaginated_filtered_logs = filtered_logs(organisation_logs, search_term, @session_filters) + codes_only = params.require(:codes_only) == "true" + + render "logs/download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: sales_logs_email_csv_organisation_path, codes_only: } + end + + def email_sales_csv + EmailCsvJob.perform_later(current_user, search_term, @session_filters, false, @organisation, codes_only_export?, "sales") + redirect_to sales_logs_csv_confirmation_organisation_path + end + def merge_request @merge_request = MergeRequest.new end @@ -147,12 +161,16 @@ private params.require(:organisation).permit(:name, :address_line1, :address_line2, :postcode, :phone, :holds_own_stock, :provider_type, :housing_registration_no) end + def codes_only_export? + params.require(:codes_only) == "true" + end + def search_term params["search"] end def authenticate_scope! - if %w[create new lettings_logs download_csv email_csv].include? action_name + if %w[create new lettings_logs sales_logs download_lettings_csv email_lettings_csv email_sales_csv download_sales_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 index 155f606ab..5e9777a3c 100644 --- a/app/controllers/sales_logs_controller.rb +++ b/app/controllers/sales_logs_controller.rb @@ -1,6 +1,7 @@ class SalesLogsController < LogsController before_action :session_filters, if: :current_user, only: %i[index email_csv download_csv] before_action :set_session_filters, if: :current_user, only: %i[index email_csv download_csv] + before_action :authenticate_scope!, only: %i[download_csv email_csv] def create super { SalesLog.new(log_params) } @@ -36,6 +37,20 @@ class SalesLogsController < LogsController end end + def download_csv + unpaginated_filtered_logs = filtered_logs(current_user.sales_logs, search_term, @session_filters) + + render "download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: email_csv_sales_logs_path, codes_only: codes_only_export? } + end + + def email_csv + all_orgs = params["organisation_select"] == "all" + EmailCsvJob.perform_later(current_user, search_term, @session_filters, all_orgs, nil, codes_only_export?, "sales") + redirect_to csv_confirmation_sales_logs_path + end + + def csv_confirmation; end + def post_create_redirect_url(log) sales_log_url(log) end @@ -43,4 +58,10 @@ class SalesLogsController < LogsController def permitted_log_params params.require(:sales_log).permit(SalesLog.editable_fields) end + +private + + def authenticate_scope! + head :unauthorized and return if codes_only_export? && !current_user.support? + end end diff --git a/app/helpers/form_page_helper.rb b/app/helpers/form_page_helper.rb new file mode 100644 index 000000000..e12d7fbb7 --- /dev/null +++ b/app/helpers/form_page_helper.rb @@ -0,0 +1,5 @@ +module FormPageHelper + def action_href(log, page_id, referrer = "check_answers") + send("#{log.model_name.param_key}_#{page_id}_path", log, referrer:) + end +end diff --git a/app/helpers/interruption_screen_helper.rb b/app/helpers/interruption_screen_helper.rb index 939f70bb2..fc75f6f59 100644 --- a/app/helpers/interruption_screen_helper.rb +++ b/app/helpers/interruption_screen_helper.rb @@ -29,6 +29,10 @@ module InterruptionScreenHelper I18n.t(title_text["translation"], **translation_params).to_s end + def soft_validation_affected_questions(question, log) + question.page.interruption_screen_question_ids.map { |question_id| log.form.get_question(question_id, log) }.compact + end + private def get_value_from_argument(log, argument) diff --git a/app/helpers/logs_helper.rb b/app/helpers/logs_helper.rb index 0e988fda9..ca6fa838f 100644 --- a/app/helpers/logs_helper.rb +++ b/app/helpers/logs_helper.rb @@ -1,10 +1,8 @@ module LogsHelper def log_type_for_controller(controller) - case controller.class.to_s - when "LettingsLogsController" - "lettings" - when "SalesLogsController" - "sales" + case controller.class.name + when "LettingsLogsController" then "lettings" + when "SalesLogsController" then "sales" else raise "Log type not found for #{controller.class}" end @@ -12,10 +10,8 @@ module LogsHelper def bulk_upload_path_for_controller(controller, id:) case log_type_for_controller(controller) - when "lettings" - bulk_upload_lettings_log_path(id:) - when "sales" - bulk_upload_sales_log_path(id:) + when "lettings" then bulk_upload_lettings_log_path(id:) + when "sales" then bulk_upload_sales_log_path(id:) end end @@ -26,16 +22,29 @@ module LogsHelper def search_label_for_controller(controller) case log_type_for_controller(controller) - when "lettings" - "Search by log ID, tenant code, property reference or postcode" - when "sales" - "Search by log ID, purchaser code or postcode" + when "lettings" then "Search by log ID, tenant code, property reference or postcode" + when "sales" then "Search by log ID, purchaser code or postcode" end end - def csv_download_url_for_controller(controller_type:, search:, codes_only:) - case log_type_for_controller(controller_type) + def csv_download_url_for_controller(controller:, search:, codes_only:) + case log_type_for_controller(controller) when "lettings" then csv_download_lettings_logs_path(search:, codes_only:) + when "sales" then csv_download_sales_logs_path(search:, codes_only:) + end + end + + def logs_path_for_controller(controller) + case log_type_for_controller(controller) + when "lettings" then lettings_logs_path + when "sales" then sales_logs_path + end + end + + def csv_download_url_by_log_type(log_type, organisation, search:, codes_only:) + case log_type + when :lettings then lettings_logs_csv_download_organisation_path(organisation, search:, codes_only:) + when :sales then sales_logs_csv_download_organisation_path(organisation, search:, codes_only:) end end diff --git a/app/jobs/email_csv_job.rb b/app/jobs/email_csv_job.rb index f313c87b8..63acdbaaa 100644 --- a/app/jobs/email_csv_job.rb +++ b/app/jobs/email_csv_job.rb @@ -5,14 +5,23 @@ class EmailCsvJob < ApplicationJob EXPIRATION_TIME = 3.hours.to_i - def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil, codes_only_export = false) # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params - unfiltered_logs = organisation.present? && user.support? ? LettingsLog.visible.where(owning_organisation_id: organisation.id) : user.lettings_logs.visible - filtered_logs = FilterService.filter_logs(unfiltered_logs, search_term, filters, all_orgs, user) + def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil, codes_only_export = false, log_type = "lettings") # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params + case log_type + when "lettings" + unfiltered_logs = organisation.present? && user.support? ? LettingsLog.visible.where(owning_organisation_id: organisation.id) : user.lettings_logs.visible + filtered_logs = FilterService.filter_logs(unfiltered_logs, search_term, filters, all_orgs, user) + csv_string = filtered_logs.to_csv(user, codes_only_export:) + when "sales" + unfiltered_logs = organisation.present? && user.support? ? SalesLog.visible.where(owning_organisation_id: organisation.id) : user.sales_logs.visible + filtered_logs = FilterService.filter_logs(unfiltered_logs, search_term, filters, all_orgs, user) + export_type = codes_only_export ? "codes" : "labels" + csv_string = Csv::SalesLogCsvService.new(export_type:).prepare_csv(filtered_logs) + end - filename = organisation.present? ? "logs-#{organisation.name}-#{Time.zone.now}.csv" : "logs-#{Time.zone.now}.csv" + filename = "#{[log_type, 'logs', organisation&.name, Time.zone.now].compact.join('-')}.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, codes_only_export:)) + storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string) url = storage_service.get_presigned_url(filename, EXPIRATION_TIME) diff --git a/app/models/derived_variables/lettings_log_variables.rb b/app/models/derived_variables/lettings_log_variables.rb index aae49422d..937dd7c2e 100644 --- a/app/models/derived_variables/lettings_log_variables.rb +++ b/app/models/derived_variables/lettings_log_variables.rb @@ -1,4 +1,6 @@ module DerivedVariables::LettingsLogVariables + include DerivedVariables::SharedLogic + # renttype and unitletas values are different for intermediate rent (3 for renttype and 4 for unitletas) RENT_TYPE_MAPPING = { 0 => 1, # "Social Rent" => "Social Rent" @@ -35,10 +37,12 @@ module DerivedVariables::LettingsLogVariables end def set_derived_fields! + clear_inapplicable_derived_values! + set_encoded_derived_values!(DEPENDENCIES) + if rsnvac.present? self.newprop = has_first_let_vacancy_reason? ? 1 : 2 end - self.incref = 1 if net_income_refused? self.renttype = RENT_TYPE_MAPPING[rent_type] self.lettype = get_lettype self.totchild = get_totchild @@ -69,15 +73,9 @@ module DerivedVariables::LettingsLogVariables self.nocharge = household_charge&.zero? ? 1 : 0 if is_renewal? self.underoccupation_benefitcap = 2 if collection_start_year == 2021 - self.referral = 1 - self.waityear = 2 - self.offered = 0 self.voiddate = startdate - self.first_time_property_let_as_social_housing = 0 - self.rsnvac = 14 self.unitletas = form.start_date.year >= 2023 ? UNITLETAS_MAPPING_23_24[rent_type] : UNITLETAS_MAPPING[rent_type] if is_general_needs? - # fixed term self.prevten = 32 if managing_organisation&.provider_type == "PRP" self.prevten = 30 if managing_organisation&.provider_type == "LA" end @@ -109,6 +107,49 @@ module DerivedVariables::LettingsLogVariables private + DEPENDENCIES = [ + { + conditions: { + renewal: 1, + }, + derived_values: { + referral: 1, + waityear: 2, + offered: 0, + rsnvac: 14, + first_time_property_let_as_social_housing: 0, + }, + }, + { + conditions: { + net_income_known: 2, + }, + derived_values: { + incref: 1, + }, + }, + ].freeze + + def clear_inapplicable_derived_values! + reset_invalidated_derived_values!(DEPENDENCIES) + if (startdate_changed? || renewal_changed?) && (renewal_was == 1 && startdate_was&.between?(Time.zone.local(2021, 4, 1), Time.zone.local(2022, 3, 31))) + self.underoccupation_benefitcap = nil + end + if renewal_changed? && renewal_was == 1 + self.voiddate = nil + self.unitletas = nil + end + if %w[PRP LA].include?(managing_organisation&.provider_type) && + (needstype_changed? || renewal_changed?) && + needstype_was == 1 && renewal_was == 1 + self.prevten = nil + end + if needstype_changed? && needstype_was == 2 + self.wchair = nil + self.location_id = nil + end + end + def get_totelder ages = [age1, age2, age3, age4, age5, age6, age7, age8] ages.count { |x| !x.nil? && x >= 60 } diff --git a/app/models/derived_variables/sales_log_variables.rb b/app/models/derived_variables/sales_log_variables.rb index 9151a72f5..308a62bb8 100644 --- a/app/models/derived_variables/sales_log_variables.rb +++ b/app/models/derived_variables/sales_log_variables.rb @@ -1,6 +1,8 @@ module DerivedVariables::SalesLogVariables + include DerivedVariables::SharedLogic + def set_derived_fields! - reset_invalidated_derived_values! + reset_invalidated_derived_values!(DEPENDENCIES) self.ethnic = 17 if ethnic_refused? self.mscharge = nil if no_monthly_leasehold_charges? @@ -15,7 +17,8 @@ module DerivedVariables::SalesLogVariables self.hoyear = hodate.year end self.deposit = value if outright_sale? && mortgage_not_used? - self.pcode1, self.pcode2 = postcode_full.split(" ") if postcode_full.present? + self.pcode1, self.pcode2 = postcode_full.split if postcode_full.present? + self.ppostc1, self.ppostc2 = ppostcode_full.split if ppostcode_full.present? self.totchild = total_child self.totadult = total_adult + total_elder self.hhmemb = number_of_household_members @@ -30,7 +33,7 @@ module DerivedVariables::SalesLogVariables self.uprn_known = 0 end - set_encoded_derived_values! + set_encoded_derived_values!(DEPENDENCIES) end private @@ -73,30 +76,6 @@ private }, ].freeze - def reset_invalidated_derived_values! - DEPENDENCIES.each do |dependency| - any_conditions_changed = dependency[:conditions].any? { |attribute, _value| send("#{attribute}_changed?") } - next unless any_conditions_changed - - previously_in_derived_state = dependency[:conditions].all? { |attribute, value| send("#{attribute}_was") == value } - next unless previously_in_derived_state - - dependency[:derived_values].each do |derived_attribute, _derived_value| - Rails.logger.debug("Cleared derived #{derived_attribute} value") - send("#{derived_attribute}=", nil) - end - end - end - - def set_encoded_derived_values! - DEPENDENCIES.each do |dependency| - derivation_applies = dependency[:conditions].all? { |attribute, value| send(attribute) == value } - if derivation_applies - dependency[:derived_values].each { |attribute, value| send("#{attribute}=", value) } - end - end - end - def number_of_household_members return unless hholdcount.present? && jointpur.present? diff --git a/app/models/derived_variables/shared_logic.rb b/app/models/derived_variables/shared_logic.rb new file mode 100644 index 000000000..c326cb8c9 --- /dev/null +++ b/app/models/derived_variables/shared_logic.rb @@ -0,0 +1,27 @@ +module DerivedVariables::SharedLogic +private + + def reset_invalidated_derived_values!(dependencies) + dependencies.each do |dependency| + any_conditions_changed = dependency[:conditions].any? { |attribute, _value| send("#{attribute}_changed?") } + next unless any_conditions_changed + + previously_in_derived_state = dependency[:conditions].all? { |attribute, value| send("#{attribute}_was") == value } + next unless previously_in_derived_state + + dependency[:derived_values].each do |derived_attribute, _derived_value| + Rails.logger.debug("Cleared derived #{derived_attribute} value") + send("#{derived_attribute}=", nil) + end + end + end + + def set_encoded_derived_values!(dependencies) + dependencies.each do |dependency| + derivation_applies = dependency[:conditions].all? { |attribute, value| send(attribute) == value } + if derivation_applies + dependency[:derived_values].each { |attribute, value| send("#{attribute}=", value) } + end + end + end +end diff --git a/app/models/form/lettings/pages/care_home_charges_value_check.rb b/app/models/form/lettings/pages/care_home_charges_value_check.rb index 32468e364..2cf253bf6 100644 --- a/app/models/form/lettings/pages/care_home_charges_value_check.rb +++ b/app/models/form/lettings/pages/care_home_charges_value_check.rb @@ -12,4 +12,8 @@ class Form::Lettings::Pages::CareHomeChargesValueCheck < ::Form::Page def questions @questions ||= [Form::Lettings::Questions::CareHomeChargesValueCheck.new(nil, nil, self)] end + + def interruption_screen_question_ids + %w[chcharge is_carehome] + end end diff --git a/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_lead_age_value_check.rb b/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_lead_age_value_check.rb index b143023ca..a7046e88b 100644 --- a/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_lead_age_value_check.rb +++ b/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_lead_age_value_check.rb @@ -16,4 +16,8 @@ class Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdLeadAgeValu def questions @questions ||= [Form::Lettings::Questions::PregnancyValueCheck.new(nil, nil, self)] end + + def interruption_screen_question_ids + %w[preg_occ sex1 sex2 sex3 sex4 sex5 sex6 sex7 sex8 age1 age2 age3 age4 age5 age6 age7 age8] + end end diff --git a/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_lead_hhmemb_value_check.rb b/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_lead_hhmemb_value_check.rb index fa85ee7cb..b4915a92b 100644 --- a/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_lead_hhmemb_value_check.rb +++ b/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_lead_hhmemb_value_check.rb @@ -16,4 +16,8 @@ class Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdLeadHhmembV def questions @questions ||= [Form::Lettings::Questions::PregnancyValueCheck.new(nil, nil, self)] end + + def interruption_screen_question_ids + %w[preg_occ sex1 sex2 sex3 sex4 sex5 sex6 sex7 sex8 age1 age2 age3 age4 age5 age6 age7 age8] + end end diff --git a/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_lead_value_check.rb b/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_lead_value_check.rb index 7eeca3bbd..291fb92b6 100644 --- a/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_lead_value_check.rb +++ b/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_lead_value_check.rb @@ -16,4 +16,8 @@ class Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdLeadValueCh def questions @questions ||= [Form::Lettings::Questions::PregnancyValueCheck.new(nil, nil, self)] end + + def interruption_screen_question_ids + %w[preg_occ sex1 sex2 sex3 sex4 sex5 sex6 sex7 sex8 age1 age2 age3 age4 age5 age6 age7 age8] + end end diff --git a/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_person_age_value_check.rb b/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_person_age_value_check.rb index 29109f7ba..419bcc81b 100644 --- a/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_person_age_value_check.rb +++ b/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_person_age_value_check.rb @@ -33,4 +33,8 @@ class Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeVa def questions @questions ||= [Form::Lettings::Questions::PregnancyValueCheck.new(nil, nil, self)] end + + def interruption_screen_question_ids + %w[preg_occ sex1 sex2 sex3 sex4 sex5 sex6 sex7 sex8 age1 age2 age3 age4 age5 age6 age7 age8] + end end diff --git a/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_person_value_check.rb b/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_person_value_check.rb index b0084d723..75b1b0d35 100644 --- a/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_person_value_check.rb +++ b/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_person_value_check.rb @@ -33,4 +33,8 @@ class Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValue def questions @questions ||= [Form::Lettings::Questions::PregnancyValueCheck.new(nil, nil, self)] end + + def interruption_screen_question_ids + %w[preg_occ sex1 sex2 sex3 sex4 sex5 sex6 sex7 sex8 age1 age2 age3 age4 age5 age6 age7 age8] + end end diff --git a/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_value_check.rb b/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_value_check.rb index 9a4d93de1..9355c24e7 100644 --- a/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_value_check.rb +++ b/app/models/form/lettings/pages/females_in_soft_age_range_in_pregnant_household_value_check.rb @@ -16,4 +16,8 @@ class Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdValueCheck def questions @questions ||= [Form::Lettings::Questions::PregnancyValueCheck.new(nil, nil, self)] end + + def interruption_screen_question_ids + %w[preg_occ sex1 sex2 sex3 sex4 sex5 sex6 sex7 sex8 age1 age2 age3 age4 age5 age6 age7 age8] + end end diff --git a/app/models/form/lettings/pages/lead_tenant_over_retirement_value_check.rb b/app/models/form/lettings/pages/lead_tenant_over_retirement_value_check.rb index 6990bf71e..ca6183f3c 100644 --- a/app/models/form/lettings/pages/lead_tenant_over_retirement_value_check.rb +++ b/app/models/form/lettings/pages/lead_tenant_over_retirement_value_check.rb @@ -27,6 +27,10 @@ class Form::Lettings::Pages::LeadTenantOverRetirementValueCheck < ::Form::Page end def questions - @questions ||= [Form::Lettings::Questions::RetirementValueCheck.new(nil, nil, self)] + @questions ||= [Form::Lettings::Questions::RetirementValueCheck.new(nil, nil, self, person_index: 1)] + end + + def interruption_screen_question_ids + %w[ecstat1 sex1 age1] end end diff --git a/app/models/form/lettings/pages/lead_tenant_under_retirement_value_check.rb b/app/models/form/lettings/pages/lead_tenant_under_retirement_value_check.rb index 34da02205..b7ab02900 100644 --- a/app/models/form/lettings/pages/lead_tenant_under_retirement_value_check.rb +++ b/app/models/form/lettings/pages/lead_tenant_under_retirement_value_check.rb @@ -7,26 +7,20 @@ class Form::Lettings::Pages::LeadTenantUnderRetirementValueCheck < ::Form::Page "translation" => "soft_validations.retirement.min.title", "arguments" => [ { - "key" => "retirement_age_for_person_1", - "label" => false, - "i18n_template" => "age", - }, - ], - } - @informative_text = { - "translation" => "soft_validations.retirement.min.hint_text", - "arguments" => [ - { "key" => "plural_gender_for_person_1", "label" => false, "i18n_template" => "gender" }, - { - "key" => "retirement_age_for_person_1", - "label" => false, + "key" => "age1", + "label" => true, "i18n_template" => "age", }, ], } + @informative_text = {} end def questions - @questions ||= [Form::Lettings::Questions::NoRetirementValueCheck.new(nil, nil, self)] + @questions ||= [Form::Lettings::Questions::NoRetirementValueCheck.new(nil, nil, self, person_index: 1)] + end + + def interruption_screen_question_ids + %w[ecstat1 age1] end end diff --git a/app/models/form/lettings/pages/max_rent_value_check.rb b/app/models/form/lettings/pages/max_rent_value_check.rb index 956e07f44..dcda23c43 100644 --- a/app/models/form/lettings/pages/max_rent_value_check.rb +++ b/app/models/form/lettings/pages/max_rent_value_check.rb @@ -28,4 +28,8 @@ class Form::Lettings::Pages::MaxRentValueCheck < ::Form::Page def questions @questions ||= [Form::Lettings::Questions::RentValueCheck.new(nil, nil, self, check_answers_card_number: @check_answers_card_number)] end + + def interruption_screen_question_ids + %w[brent startdate la beds rent_type needstype] + end end diff --git a/app/models/form/lettings/pages/min_rent_value_check.rb b/app/models/form/lettings/pages/min_rent_value_check.rb index 86b53951c..245158a08 100644 --- a/app/models/form/lettings/pages/min_rent_value_check.rb +++ b/app/models/form/lettings/pages/min_rent_value_check.rb @@ -24,4 +24,8 @@ class Form::Lettings::Pages::MinRentValueCheck < ::Form::Page def questions @questions ||= [Form::Lettings::Questions::RentValueCheck.new(nil, nil, self, check_answers_card_number: @check_answers_card_number)] end + + def interruption_screen_question_ids + %w[brent startdate la beds rent_type needstype] + end end diff --git a/app/models/form/lettings/pages/net_income_value_check.rb b/app/models/form/lettings/pages/net_income_value_check.rb index 0569b86e9..f5ffd78c1 100644 --- a/app/models/form/lettings/pages/net_income_value_check.rb +++ b/app/models/form/lettings/pages/net_income_value_check.rb @@ -3,14 +3,13 @@ class Form::Lettings::Pages::NetIncomeValueCheck < ::Form::Page super @id = "net_income_value_check" @depends_on = [{ "net_income_soft_validation_triggered?" => true }] - @title_text = { "translation" => "soft_validations.net_income.title_text" } - @informative_text = { - "translation" => "soft_validations.net_income.hint_text", + @title_text = { + "translation" => "soft_validations.net_income.title_text", "arguments" => [ { - "key" => "field_formatted_as_currency", - "arguments_for_key" => "ecstat1", - "i18n_template" => "ecstat1", + "key" => "incfreq", + "label" => true, + "i18n_template" => "incfreq", }, { "key" => "field_formatted_as_currency", @@ -19,9 +18,24 @@ class Form::Lettings::Pages::NetIncomeValueCheck < ::Form::Page }, ], } + + @informative_text = { + "translation" => "soft_validations.net_income.hint_text", + "arguments" => [ + { + "key" => "net_income_higher_or_lower_text", + "label" => false, + "i18n_template" => "net_income_higher_or_lower_text", + }, + ], + } end def questions @questions ||= [Form::Lettings::Questions::NetIncomeValueCheck.new(nil, nil, self)] end + + def interruption_screen_question_ids + %w[incfreq earnings ecstat1] + end end diff --git a/app/models/form/lettings/pages/no_females_pregnant_household_lead_age_value_check.rb b/app/models/form/lettings/pages/no_females_pregnant_household_lead_age_value_check.rb index d837ac0cb..570a1bc5a 100644 --- a/app/models/form/lettings/pages/no_females_pregnant_household_lead_age_value_check.rb +++ b/app/models/form/lettings/pages/no_females_pregnant_household_lead_age_value_check.rb @@ -16,4 +16,8 @@ class Form::Lettings::Pages::NoFemalesPregnantHouseholdLeadAgeValueCheck < ::For def questions @questions ||= [Form::Lettings::Questions::PregnancyValueCheck.new(nil, nil, self)] end + + def interruption_screen_question_ids + %w[preg_occ sex1 sex2 sex3 sex4 sex5 sex6 sex7 sex8] + end end diff --git a/app/models/form/lettings/pages/no_females_pregnant_household_lead_hhmemb_value_check.rb b/app/models/form/lettings/pages/no_females_pregnant_household_lead_hhmemb_value_check.rb index 806153074..ea90bd6ff 100644 --- a/app/models/form/lettings/pages/no_females_pregnant_household_lead_hhmemb_value_check.rb +++ b/app/models/form/lettings/pages/no_females_pregnant_household_lead_hhmemb_value_check.rb @@ -16,4 +16,8 @@ class Form::Lettings::Pages::NoFemalesPregnantHouseholdLeadHhmembValueCheck < :: def questions @questions ||= [Form::Lettings::Questions::PregnancyValueCheck.new(nil, nil, self)] end + + def interruption_screen_question_ids + %w[preg_occ sex1 sex2 sex3 sex4 sex5 sex6 sex7 sex8] + end end diff --git a/app/models/form/lettings/pages/no_females_pregnant_household_lead_value_check.rb b/app/models/form/lettings/pages/no_females_pregnant_household_lead_value_check.rb index 44217e7fa..4d0fc0c2c 100644 --- a/app/models/form/lettings/pages/no_females_pregnant_household_lead_value_check.rb +++ b/app/models/form/lettings/pages/no_females_pregnant_household_lead_value_check.rb @@ -16,4 +16,8 @@ class Form::Lettings::Pages::NoFemalesPregnantHouseholdLeadValueCheck < ::Form:: def questions @questions ||= [Form::Lettings::Questions::PregnancyValueCheck.new(nil, nil, self)] end + + def interruption_screen_question_ids + %w[preg_occ sex1 sex2 sex3 sex4 sex5 sex6 sex7 sex8] + end end diff --git a/app/models/form/lettings/pages/no_females_pregnant_household_person_age_value_check.rb b/app/models/form/lettings/pages/no_females_pregnant_household_person_age_value_check.rb index be833d9dc..a58c9bc60 100644 --- a/app/models/form/lettings/pages/no_females_pregnant_household_person_age_value_check.rb +++ b/app/models/form/lettings/pages/no_females_pregnant_household_person_age_value_check.rb @@ -16,4 +16,8 @@ class Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonAgeValueCheck < ::F def questions @questions ||= [Form::Lettings::Questions::PregnancyValueCheck.new(nil, nil, self)] end + + def interruption_screen_question_ids + %w[preg_occ sex1 sex2 sex3 sex4 sex5 sex6 sex7 sex8] + end end diff --git a/app/models/form/lettings/pages/no_females_pregnant_household_person_value_check.rb b/app/models/form/lettings/pages/no_females_pregnant_household_person_value_check.rb index e81e5b339..ede6e61b4 100644 --- a/app/models/form/lettings/pages/no_females_pregnant_household_person_value_check.rb +++ b/app/models/form/lettings/pages/no_females_pregnant_household_person_value_check.rb @@ -16,4 +16,8 @@ class Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck < ::Form def questions @questions ||= [Form::Lettings::Questions::PregnancyValueCheck.new(nil, nil, self)] end + + def interruption_screen_question_ids + %w[preg_occ sex1 sex2 sex3 sex4 sex5 sex6 sex7 sex8] + end end diff --git a/app/models/form/lettings/pages/no_females_pregnant_household_value_check.rb b/app/models/form/lettings/pages/no_females_pregnant_household_value_check.rb index 2438ef0b9..23aaec180 100644 --- a/app/models/form/lettings/pages/no_females_pregnant_household_value_check.rb +++ b/app/models/form/lettings/pages/no_females_pregnant_household_value_check.rb @@ -16,4 +16,8 @@ class Form::Lettings::Pages::NoFemalesPregnantHouseholdValueCheck < ::Form::Page def questions @questions ||= [Form::Lettings::Questions::PregnancyValueCheck.new(nil, nil, self)] end + + def interruption_screen_question_ids + %w[preg_occ sex1 sex2 sex3 sex4 sex5 sex6 sex7 sex8] + end end diff --git a/app/models/form/lettings/pages/person_over_retirement_value_check.rb b/app/models/form/lettings/pages/person_over_retirement_value_check.rb index 3de5246e6..b13a52ed5 100644 --- a/app/models/form/lettings/pages/person_over_retirement_value_check.rb +++ b/app/models/form/lettings/pages/person_over_retirement_value_check.rb @@ -28,9 +28,14 @@ class Form::Lettings::Pages::PersonOverRetirementValueCheck < ::Form::Page }, ], } + @person_index = person_index end def questions - @questions ||= [Form::Lettings::Questions::RetirementValueCheck.new(nil, nil, self)] + @questions ||= [Form::Lettings::Questions::RetirementValueCheck.new(nil, nil, self, person_index: @person_index)] + end + + def interruption_screen_question_ids + ["ecstat#{@person_index}", "sex#{@person_index}", "age#{@person_index}"] end end diff --git a/app/models/form/lettings/pages/person_under_retirement_value_check.rb b/app/models/form/lettings/pages/person_under_retirement_value_check.rb index a1b9253fd..9f7540f56 100644 --- a/app/models/form/lettings/pages/person_under_retirement_value_check.rb +++ b/app/models/form/lettings/pages/person_under_retirement_value_check.rb @@ -7,30 +7,21 @@ class Form::Lettings::Pages::PersonUnderRetirementValueCheck < ::Form::Page "translation" => "soft_validations.retirement.min.title", "arguments" => [ { - "key" => "retirement_age_for_person_#{person_index}", - "label" => false, - "i18n_template" => "age", - }, - ], - } - @informative_text = { - "translation" => "soft_validations.retirement.min.hint_text", - "arguments" => [ - { - "key" => "plural_gender_for_person_#{person_index}", - "label" => false, - "i18n_template" => "gender", - }, - { - "key" => "retirement_age_for_person_#{person_index}", - "label" => false, + "key" => "age#{person_index}", + "label" => true, "i18n_template" => "age", }, ], } + @informative_text = {} + @person_index = person_index end def questions - @questions ||= [Form::Lettings::Questions::NoRetirementValueCheck.new(nil, nil, self)] + @questions ||= [Form::Lettings::Questions::NoRetirementValueCheck.new(nil, nil, self, person_index: @person_index)] + end + + def interruption_screen_question_ids + ["ecstat#{@person_index}", "age#{@person_index}"] end end diff --git a/app/models/form/lettings/pages/property_major_repairs_value_check.rb b/app/models/form/lettings/pages/property_major_repairs_value_check.rb index 21e2b2b51..08672aff6 100644 --- a/app/models/form/lettings/pages/property_major_repairs_value_check.rb +++ b/app/models/form/lettings/pages/property_major_repairs_value_check.rb @@ -4,10 +4,17 @@ class Form::Lettings::Pages::PropertyMajorRepairsValueCheck < ::Form::Page @id = "property_major_repairs_value_check" @depends_on = [{ "major_repairs_date_in_soft_range?" => true }] @title_text = { "translation" => "soft_validations.major_repairs_date.title_text" } - @informative_text = {} + @informative_text = { + "translation" => "soft_validations.major_repairs_date.hint_text", + "arguments" => [], + } end def questions @questions ||= [Form::Lettings::Questions::MajorRepairsDateValueCheck.new(nil, nil, self)] end + + def interruption_screen_question_ids + %w[mrcdate startdate] + end end diff --git a/app/models/form/lettings/pages/void_date_value_check.rb b/app/models/form/lettings/pages/void_date_value_check.rb index d717b38fe..6ebd3a175 100644 --- a/app/models/form/lettings/pages/void_date_value_check.rb +++ b/app/models/form/lettings/pages/void_date_value_check.rb @@ -4,10 +4,17 @@ class Form::Lettings::Pages::VoidDateValueCheck < ::Form::Page @id = "void_date_value_check" @depends_on = [{ "voiddate_in_soft_range?" => true }] @title_text = { "translation" => "soft_validations.void_date.title_text" } - @informative_text = {} + @informative_text = { + "translation" => "soft_validations.void_date.hint_text", + "arguments" => [], + } end def questions @questions ||= [Form::Lettings::Questions::VoidDateValueCheck.new(nil, nil, self)] end + + def interruption_screen_question_ids + %w[voiddate startdate] + end end diff --git a/app/models/form/lettings/questions/no_retirement_value_check.rb b/app/models/form/lettings/questions/no_retirement_value_check.rb index 43fe68e10..0e2033c61 100644 --- a/app/models/form/lettings/questions/no_retirement_value_check.rb +++ b/app/models/form/lettings/questions/no_retirement_value_check.rb @@ -1,11 +1,11 @@ class Form::Lettings::Questions::NoRetirementValueCheck < ::Form::Question - def initialize(id, hsh, page) - super + def initialize(id, hsh, page, person_index:) + super(id, hsh, page) @id = "retirement_value_check" @check_answer_label = "Retirement confirmation" @header = "Are you sure this person is retired?" @type = "interruption_screen" - @check_answers_card_number = 8 + @check_answers_card_number = person_index @answer_options = ANSWER_OPTIONS @hidden_in_check_answers = { "depends_on" => [ diff --git a/app/models/form/lettings/questions/retirement_value_check.rb b/app/models/form/lettings/questions/retirement_value_check.rb index 784693616..63a286d61 100644 --- a/app/models/form/lettings/questions/retirement_value_check.rb +++ b/app/models/form/lettings/questions/retirement_value_check.rb @@ -1,11 +1,11 @@ class Form::Lettings::Questions::RetirementValueCheck < ::Form::Question - def initialize(id, hsh, page) - super + def initialize(id, hsh, page, person_index:) + super(id, hsh, page) @id = "retirement_value_check" @check_answer_label = "Retirement confirmation" @header = "Are you sure this person isn’t retired?" @type = "interruption_screen" - @check_answers_card_number = 8 + @check_answers_card_number = person_index @answer_options = ANSWER_OPTIONS @hidden_in_check_answers = { "depends_on" => [ diff --git a/app/models/form/page.rb b/app/models/form/page.rb index 2ac5a716a..498c69d7b 100644 --- a/app/models/form/page.rb +++ b/app/models/form/page.rb @@ -1,7 +1,7 @@ class Form::Page attr_accessor :id, :header, :header_partial, :description, :questions, :depends_on, :title_text, :informative_text, :subsection, :hide_subsection_label, :next_unresolved_page_id, - :skip_text + :skip_text, :interruption_screen_question_ids def initialize(id, hsh, subsection) @id = id @@ -17,6 +17,7 @@ class Form::Page @hide_subsection_label = hsh["hide_subsection_label"] @next_unresolved_page_id = hsh["next_unresolved_page_id"] @skip_text = hsh["skip_text"] + @interruption_screen_question_ids = hsh["interruption_screen_question_ids"] || [] end end diff --git a/app/models/form/question.rb b/app/models/form/question.rb index d4b7c9d76..82d3a09b5 100644 --- a/app/models/form/question.rb +++ b/app/models/form/question.rb @@ -120,10 +120,6 @@ class Form::Question end end - 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 unanswered?(log) return answer_options.keys.none? { |key| value_is_yes?(log[key]) } if type == "checkbox" diff --git a/app/models/form/sales/pages/about_price_value_check.rb b/app/models/form/sales/pages/about_price_value_check.rb index 94df02243..3ab7520aa 100644 --- a/app/models/form/sales/pages/about_price_value_check.rb +++ b/app/models/form/sales/pages/about_price_value_check.rb @@ -25,8 +25,8 @@ class Form::Sales::Pages::AboutPriceValueCheck < ::Form::Page "i18n_template" => "soft_min_or_soft_max", }, { - "key" => "purchase_price_min_or_max_text", - "i18n_template" => "min_or_max", + "key" => "purchase_price_higher_or_lower_text", + "i18n_template" => "higher_or_lower", }, ], } @@ -37,4 +37,8 @@ class Form::Sales::Pages::AboutPriceValueCheck < ::Form::Page Form::Sales::Questions::AboutPriceValueCheck.new(nil, nil, self), ] end + + def interruption_screen_question_ids + %w[value beds la] + end end diff --git a/app/models/form/sales/pages/buyer1_income_value_check.rb b/app/models/form/sales/pages/buyer1_income_value_check.rb index 48d8f5fff..4394258e6 100644 --- a/app/models/form/sales/pages/buyer1_income_value_check.rb +++ b/app/models/form/sales/pages/buyer1_income_value_check.rb @@ -7,7 +7,7 @@ class Form::Sales::Pages::Buyer1IncomeValueCheck < ::Form::Page }, ] @title_text = { - "translation" => "soft_validations.income.under_soft_min_for_economic_status", + "translation" => "soft_validations.income.under_soft_min_for_economic_status.title_text", "arguments" => [ { "key" => "field_formatted_as_currency", @@ -21,7 +21,10 @@ class Form::Sales::Pages::Buyer1IncomeValueCheck < ::Form::Page }, ], } - @informative_text = {} + @informative_text = { + "translation" => "soft_validations.income.under_soft_min_for_economic_status.hint_text", + "arguments" => [], + } end def questions @@ -29,4 +32,8 @@ class Form::Sales::Pages::Buyer1IncomeValueCheck < ::Form::Page Form::Sales::Questions::Buyer1IncomeValueCheck.new(nil, nil, self), ] end + + def interruption_screen_question_ids + %w[ecstat1 income1] + end end diff --git a/app/models/form/sales/pages/buyer2_income_value_check.rb b/app/models/form/sales/pages/buyer2_income_value_check.rb index 598c4c7a6..a3f7c2541 100644 --- a/app/models/form/sales/pages/buyer2_income_value_check.rb +++ b/app/models/form/sales/pages/buyer2_income_value_check.rb @@ -10,7 +10,7 @@ class Form::Sales::Pages::Buyer2IncomeValueCheck < ::Form::Page }, ] @title_text = { - "translation" => "soft_validations.income.under_soft_min_for_economic_status", + "translation" => "soft_validations.income.under_soft_min_for_economic_status.title_text", "arguments" => [ { "key" => "field_formatted_as_currency", @@ -24,7 +24,10 @@ class Form::Sales::Pages::Buyer2IncomeValueCheck < ::Form::Page }, ], } - @informative_text = {} + @informative_text = { + "translation" => "soft_validations.income.under_soft_min_for_economic_status.hint_text", + "arguments" => [], + } end def questions @@ -32,4 +35,8 @@ class Form::Sales::Pages::Buyer2IncomeValueCheck < ::Form::Page Form::Sales::Questions::Buyer2IncomeValueCheck.new(nil, nil, self), ] end + + def interruption_screen_question_ids + %w[ecstat2 income2] + end end diff --git a/app/models/form/sales/pages/buyer_live_in_value_check.rb b/app/models/form/sales/pages/buyer_live_in_value_check.rb index 067ba96ca..99080ae77 100644 --- a/app/models/form/sales/pages/buyer_live_in_value_check.rb +++ b/app/models/form/sales/pages/buyer_live_in_value_check.rb @@ -10,7 +10,10 @@ class Form::Sales::Pages::BuyerLiveInValueCheck < Form::Sales::Pages::Person "translation" => "soft_validations.buyer#{person_index}_livein_wrong_for_ownership_type.title_text", "arguments" => [{ "key" => "ownership_scheme", "label" => false, "i18n_template" => "ownership_scheme" }], } - @informative_text = {} + @informative_text = { + "translation" => "soft_validations.buyer#{person_index}_livein_wrong_for_ownership_type.hint_text", + "arguments" => [{ "key" => "ownership_scheme", "label" => false, "i18n_template" => "ownership_scheme" }], + } end def questions @@ -18,4 +21,8 @@ class Form::Sales::Pages::BuyerLiveInValueCheck < Form::Sales::Pages::Person Form::Sales::Questions::BuyerLiveInValueCheck.new(nil, nil, self, person_index: @person_index), ] end + + def interruption_screen_question_ids + ["ownershipsch", "buy#{@person_index}livein"] + end end diff --git a/app/models/form/sales/pages/deposit_and_mortgage_value_check.rb b/app/models/form/sales/pages/deposit_and_mortgage_value_check.rb index 338889030..95e9c9dd7 100644 --- a/app/models/form/sales/pages/deposit_and_mortgage_value_check.rb +++ b/app/models/form/sales/pages/deposit_and_mortgage_value_check.rb @@ -14,4 +14,8 @@ class Form::Sales::Pages::DepositAndMortgageValueCheck < ::Form::Page Form::Sales::Questions::DepositAndMortgageValueCheck.new(nil, nil, self), ] end + + def interruption_screen_question_ids + %w[mortgage deposit value discount] + end end diff --git a/app/models/form/sales/pages/deposit_value_check.rb b/app/models/form/sales/pages/deposit_value_check.rb index 8fa76ce1a..b31b2cecb 100644 --- a/app/models/form/sales/pages/deposit_value_check.rb +++ b/app/models/form/sales/pages/deposit_value_check.rb @@ -6,7 +6,25 @@ class Form::Sales::Pages::DepositValueCheck < ::Form::Page "deposit_over_soft_max?" => true, }, ] - @informative_text = {} + @informative_text = { + "translation" => "soft_validations.deposit.hint_text", + "arguments" => [], + } + @title_text = { + "translation" => "soft_validations.deposit.title_text", + "arguments" => [ + { + "key" => "field_formatted_as_currency", + "arguments_for_key" => "deposit", + "i18n_template" => "deposit", + }, + { + "key" => "field_formatted_as_currency", + "arguments_for_key" => "savings", + "i18n_template" => "savings", + }, + ], + } end def questions @@ -14,4 +32,8 @@ class Form::Sales::Pages::DepositValueCheck < ::Form::Page Form::Sales::Questions::DepositValueCheck.new(nil, nil, self), ] end + + def interruption_screen_question_ids + %w[savings deposit] + end end diff --git a/app/models/form/sales/pages/discounted_sale_value_check.rb b/app/models/form/sales/pages/discounted_sale_value_check.rb index 3c1b010f2..dcb820e49 100644 --- a/app/models/form/sales/pages/discounted_sale_value_check.rb +++ b/app/models/form/sales/pages/discounted_sale_value_check.rb @@ -35,4 +35,8 @@ class Form::Sales::Pages::DiscountedSaleValueCheck < ::Form::Page Form::Sales::Questions::DiscountedSaleValueCheck.new(nil, nil, self), ] end + + def interruption_screen_question_ids + %w[value deposit ownershipsch mortgage mortgageused discount grant type] + end end diff --git a/app/models/form/sales/pages/extra_borrowing_value_check.rb b/app/models/form/sales/pages/extra_borrowing_value_check.rb index 5fff74db2..b3cf0b2db 100644 --- a/app/models/form/sales/pages/extra_borrowing_value_check.rb +++ b/app/models/form/sales/pages/extra_borrowing_value_check.rb @@ -7,9 +7,19 @@ class Form::Sales::Pages::ExtraBorrowingValueCheck < Form::Page }, ] @title_text = { - "translation" => "soft_validations.extra_borrowing.title", + "translation" => "soft_validations.extra_borrowing.title_text", + "arguments" => [ + { + "key" => "field_formatted_as_currency", + "arguments_for_key" => "mortgage_and_deposit_total", + "i18n_template" => "mortgage_and_deposit_total", + }, + ], + } + @informative_text = { + "translation" => "soft_validations.extra_borrowing.hint_text", + "arguments" => [], } - @informative_text = {} end def questions @@ -17,4 +27,8 @@ class Form::Sales::Pages::ExtraBorrowingValueCheck < Form::Page Form::Sales::Questions::ExtraBorrowingValueCheck.new(nil, nil, self), ] end + + def interruption_screen_question_ids + %w[extrabor mortgage deposit value discount] + end end diff --git a/app/models/form/sales/pages/grant_value_check.rb b/app/models/form/sales/pages/grant_value_check.rb index a739a1654..6b53d74ee 100644 --- a/app/models/form/sales/pages/grant_value_check.rb +++ b/app/models/form/sales/pages/grant_value_check.rb @@ -7,7 +7,20 @@ class Form::Sales::Pages::GrantValueCheck < ::Form::Page "grant_outside_common_range?" => true, }, ] - @informative_text = {} + @title_text = { + "translation" => "soft_validations.grant.title_text", + "arguments" => [ + { + "key" => "field_formatted_as_currency", + "arguments_for_key" => "grant", + "i18n_template" => "grant", + }, + ], + } + @informative_text = { + "translation" => "soft_validations.grant.hint_text", + "arguments" => [], + } end def questions @@ -15,4 +28,8 @@ class Form::Sales::Pages::GrantValueCheck < ::Form::Page Form::Sales::Questions::GrantValueCheck.new(nil, nil, self), ] end + + def interruption_screen_question_ids + %w[grant] + end end diff --git a/app/models/form/sales/pages/handover_date_check.rb b/app/models/form/sales/pages/handover_date_check.rb index 5d656200b..2a3bfdbf3 100644 --- a/app/models/form/sales/pages/handover_date_check.rb +++ b/app/models/form/sales/pages/handover_date_check.rb @@ -16,4 +16,8 @@ class Form::Sales::Pages::HandoverDateCheck < ::Form::Page Form::Sales::Questions::HandoverDateCheck.new(nil, nil, self), ] end + + def interruption_screen_question_ids + %w[hodate saledate] + end end diff --git a/app/models/form/sales/pages/household_wheelchair_check.rb b/app/models/form/sales/pages/household_wheelchair_check.rb index 0cd457444..72b78cc2b 100644 --- a/app/models/form/sales/pages/household_wheelchair_check.rb +++ b/app/models/form/sales/pages/household_wheelchair_check.rb @@ -7,6 +7,7 @@ class Form::Sales::Pages::HouseholdWheelchairCheck < ::Form::Page }, ] @informative_text = {} + @title_text = { "translation" => "soft_validations.wheelchair.title_text" } end def questions @@ -14,4 +15,8 @@ class Form::Sales::Pages::HouseholdWheelchairCheck < ::Form::Page Form::Sales::Questions::HouseholdWheelchairCheck.new(nil, nil, self), ] end + + def interruption_screen_question_ids + %w[disabled wheel] + end end diff --git a/app/models/form/sales/pages/monthly_charges_value_check.rb b/app/models/form/sales/pages/monthly_charges_value_check.rb index 909212b52..662a048bb 100644 --- a/app/models/form/sales/pages/monthly_charges_value_check.rb +++ b/app/models/form/sales/pages/monthly_charges_value_check.rb @@ -8,9 +8,18 @@ class Form::Sales::Pages::MonthlyChargesValueCheck < ::Form::Page ] @title_text = { "translation" => "soft_validations.monthly_charges_over_soft_max.title_text", + "arguments" => [ + { + "key" => "field_formatted_as_currency", + "arguments_for_key" => "mscharge", + "i18n_template" => "mscharge", + }, + ], + } + @informative_text = { + "translation" => "soft_validations.monthly_charges_over_soft_max.hint_text", "arguments" => [], } - @informative_text = {} end def questions @@ -18,4 +27,8 @@ class Form::Sales::Pages::MonthlyChargesValueCheck < ::Form::Page Form::Sales::Questions::MonthlyChargesValueCheck.new(nil, nil, self), ] end + + def interruption_screen_question_ids + %w[type mscharge proptype] + end end diff --git a/app/models/form/sales/pages/mortgage_value_check.rb b/app/models/form/sales/pages/mortgage_value_check.rb index 4bff3556d..949cea27f 100644 --- a/app/models/form/sales/pages/mortgage_value_check.rb +++ b/app/models/form/sales/pages/mortgage_value_check.rb @@ -4,6 +4,20 @@ class Form::Sales::Pages::MortgageValueCheck < ::Form::Page @depends_on = depends_on @informative_text = {} @person_index = person_index + @title_text = { + "translation" => "soft_validations.mortgage.title_text", + "arguments" => [ + { + "key" => "field_formatted_as_currency", + "arguments_for_key" => "mortgage", + "i18n_template" => "mortgage", + }, + ], + } + @informative_text = { + "translation" => "soft_validations.mortgage.hint_text", + "arguments" => [], + } end def questions @@ -28,4 +42,8 @@ class Form::Sales::Pages::MortgageValueCheck < ::Form::Page ] end end + + def interruption_screen_question_ids + %w[mortgage inc1mort inc2mort jointpur income1 income2 inc1mort inc2mort] + end end diff --git a/app/models/form/sales/pages/old_persons_shared_ownership_value_check.rb b/app/models/form/sales/pages/old_persons_shared_ownership_value_check.rb index 9198af18a..537a6679f 100644 --- a/app/models/form/sales/pages/old_persons_shared_ownership_value_check.rb +++ b/app/models/form/sales/pages/old_persons_shared_ownership_value_check.rb @@ -7,10 +7,13 @@ class Form::Sales::Pages::OldPersonsSharedOwnershipValueCheck < ::Form::Page }, ] @title_text = { - "translation" => "soft_validations.old_persons_shared_ownership", + "translation" => "soft_validations.old_persons_shared_ownership.title_text", + "arguments" => [], + } + @informative_text = { + "translation" => "soft_validations.old_persons_shared_ownership.hint_text", "arguments" => [], } - @informative_text = {} end def questions @@ -18,4 +21,8 @@ class Form::Sales::Pages::OldPersonsSharedOwnershipValueCheck < ::Form::Page Form::Sales::Questions::OldPersonsSharedOwnershipValueCheck.new(nil, nil, self), ] end + + def interruption_screen_question_ids + %w[type jointpur age1 age2] + end end diff --git a/app/models/form/sales/pages/percentage_discount_value_check.rb b/app/models/form/sales/pages/percentage_discount_value_check.rb index 1e3beb3e6..37ff4eea4 100644 --- a/app/models/form/sales/pages/percentage_discount_value_check.rb +++ b/app/models/form/sales/pages/percentage_discount_value_check.rb @@ -5,11 +5,18 @@ class Form::Sales::Pages::PercentageDiscountValueCheck < ::Form::Page "translation" => "soft_validations.percentage_discount_value.title_text", "arguments" => [{ "key" => "discount", "label" => true, "i18n_template" => "discount" }], } - @informative_text = {} + @informative_text = { + "translation" => "soft_validations.percentage_discount_value.hint_text", + "arguments" => [], + } @depends_on = [{ "percentage_discount_invalid?" => true }] end def questions @questions ||= [Form::Sales::Questions::PercentageDiscountValueCheck.new(nil, nil, self)] end + + def interruption_screen_question_ids + %w[discount proptype] + end end diff --git a/app/models/form/sales/pages/person_student_not_child_value_check.rb b/app/models/form/sales/pages/person_student_not_child_value_check.rb index e3513e169..a798c8e16 100644 --- a/app/models/form/sales/pages/person_student_not_child_value_check.rb +++ b/app/models/form/sales/pages/person_student_not_child_value_check.rb @@ -18,4 +18,8 @@ class Form::Sales::Pages::PersonStudentNotChildValueCheck < Form::Sales::Pages:: Form::Sales::Questions::PersonStudentNotChildValueCheck.new(nil, nil, self, person_index: @person_index), ] end + + def interruption_screen_question_ids + ["relat#{@person_index}", "ecstat#{@person_index}", "age#{@person_index}"] + end end diff --git a/app/models/form/sales/pages/retirement_value_check.rb b/app/models/form/sales/pages/retirement_value_check.rb index b1af2a6d4..9e5dbb87f 100644 --- a/app/models/form/sales/pages/retirement_value_check.rb +++ b/app/models/form/sales/pages/retirement_value_check.rb @@ -11,27 +11,13 @@ class Form::Sales::Pages::RetirementValueCheck < Form::Sales::Pages::Person "translation" => "soft_validations.retirement.min.title", "arguments" => [ { - "key" => "retirement_age_for_person_#{person_index}", - "label" => false, - "i18n_template" => "age", - }, - ], - } - @informative_text = { - "translation" => "soft_validations.retirement.min.hint_text", - "arguments" => [ - { - "key" => "plural_gender_for_person_#{person_index}", - "label" => false, - "i18n_template" => "gender", - }, - { - "key" => "retirement_age_for_person_#{person_index}", - "label" => false, + "key" => "age#{person_index}", + "label" => true, "i18n_template" => "age", }, ], } + @informative_text = {} end def questions @@ -39,4 +25,8 @@ class Form::Sales::Pages::RetirementValueCheck < Form::Sales::Pages::Person Form::Sales::Questions::RetirementValueCheck.new(nil, nil, self, person_index: @person_index), ] end + + def interruption_screen_question_ids + ["age#{@person_index}", "ecstat#{@person_index}"] + end end diff --git a/app/models/form/sales/pages/savings_value_check.rb b/app/models/form/sales/pages/savings_value_check.rb index 779bdae30..6a45a1929 100644 --- a/app/models/form/sales/pages/savings_value_check.rb +++ b/app/models/form/sales/pages/savings_value_check.rb @@ -6,7 +6,20 @@ class Form::Sales::Pages::SavingsValueCheck < ::Form::Page "savings_over_soft_max?" => true, }, ] - @informative_text = {} + @title_text = { + "translation" => "soft_validations.savings.title_text", + "arguments" => [ + { + "key" => "field_formatted_as_currency", + "arguments_for_key" => "savings", + "i18n_template" => "savings", + }, + ], + } + @informative_text = { + "translation" => "soft_validations.savings.hint_text", + "arguments" => [], + } end def questions @@ -14,4 +27,8 @@ class Form::Sales::Pages::SavingsValueCheck < ::Form::Page Form::Sales::Questions::SavingsValueCheck.new(nil, nil, self), ] end + + def interruption_screen_question_ids + %w[savings] + end end diff --git a/app/models/form/sales/pages/shared_ownership_deposit_value_check.rb b/app/models/form/sales/pages/shared_ownership_deposit_value_check.rb index 4730248ed..258697161 100644 --- a/app/models/form/sales/pages/shared_ownership_deposit_value_check.rb +++ b/app/models/form/sales/pages/shared_ownership_deposit_value_check.rb @@ -12,8 +12,8 @@ class Form::Sales::Pages::SharedOwnershipDepositValueCheck < ::Form::Page "arguments" => [ { "key" => "field_formatted_as_currency", - "arguments_for_key" => "expected_shared_ownership_deposit_value", - "i18n_template" => "expected_shared_ownership_deposit_value", + "arguments_for_key" => "mortgage_deposit_and_discount_total", + "i18n_template" => "mortgage_deposit_and_discount_total", }, ], } @@ -24,4 +24,8 @@ class Form::Sales::Pages::SharedOwnershipDepositValueCheck < ::Form::Page Form::Sales::Questions::SharedOwnershipDepositValueCheck.new(nil, nil, self), ] end + + def interruption_screen_question_ids + %w[mortgage mortgageused cashdis type deposit value equity] + end end diff --git a/app/models/form/sales/pages/staircase_bought_value_check.rb b/app/models/form/sales/pages/staircase_bought_value_check.rb index b7b8178ff..283261a53 100644 --- a/app/models/form/sales/pages/staircase_bought_value_check.rb +++ b/app/models/form/sales/pages/staircase_bought_value_check.rb @@ -8,7 +8,7 @@ class Form::Sales::Pages::StaircaseBoughtValueCheck < ::Form::Page }, ] @title_text = { - "translation" => "soft_validations.staircase_bought_seems_high", + "translation" => "soft_validations.staircase_bought_seems_high.title_text", "arguments" => [ { "key" => "stairbought", @@ -16,7 +16,10 @@ class Form::Sales::Pages::StaircaseBoughtValueCheck < ::Form::Page }, ], } - @informative_text = {} + @informative_text = { + "translation" => "soft_validations.staircase_bought_seems_high.hint_text", + "arguments" => [], + } end def questions @@ -24,4 +27,8 @@ class Form::Sales::Pages::StaircaseBoughtValueCheck < ::Form::Page Form::Sales::Questions::StaircaseBoughtValueCheck.new(nil, nil, self), ] end + + def interruption_screen_question_ids + %w[stairbought] + end end diff --git a/app/models/form/sales/questions/household_wheelchair_check.rb b/app/models/form/sales/questions/household_wheelchair_check.rb index f2c15b9ac..50498b099 100644 --- a/app/models/form/sales/questions/household_wheelchair_check.rb +++ b/app/models/form/sales/questions/household_wheelchair_check.rb @@ -3,7 +3,7 @@ class Form::Sales::Questions::HouseholdWheelchairCheck < ::Form::Question super @id = "wheel_value_check" @check_answer_label = "Does anyone in the household use a wheelchair?" - @header = "Are you sure? You said previously that somebody in household uses a wheelchair" + @header = "You told us that someone in the household uses a wheelchair." @type = "interruption_screen" @answer_options = { "0" => { "value" => "Yes" }, diff --git a/app/models/form_handler.rb b/app/models/form_handler.rb index ee84c3e32..cdebdc679 100644 --- a/app/models/form_handler.rb +++ b/app/models/form_handler.rb @@ -40,6 +40,28 @@ class FormHandler } end + def ordered_sales_questions_for_all_years + sales_forms = forms.filter { |name, _form| name.end_with? "sales" }.values + ordered_questions = sales_forms.pop.questions.uniq(&:id) + question_ids = ordered_questions.map(&:id) + all_questions_from_previous_forms = sales_forms.flat_map(&:questions) + deprecated_questions_by_preceding_question_id(question_ids, all_questions_from_previous_forms).each do |preceding_question_id, deprecated_question| + index_of_preceding_question = ordered_questions.index { |q| q.id == preceding_question_id } + ordered_questions.insert(index_of_preceding_question + 1, deprecated_question) + end + ordered_questions + end + + def deprecated_questions_by_preceding_question_id(current_form_question_ids, all_questions_from_previous_forms) + deprecated_questions = {} + all_questions_from_previous_forms.each_cons(2) do |preceding_question, question| + next if current_form_question_ids.include?(question.id) || deprecated_questions.values.map(&:id).include?(question.id) + + deprecated_questions[preceding_question.id] = question + end + deprecated_questions + end + def lettings_forms forms = {} directories.each do |directory| @@ -95,9 +117,9 @@ class FormHandler forms.count { |form| now.between?(form.start_date, form.end_date) } > 1 end - def use_fake_forms! + def use_fake_forms!(fake_forms = nil) @directories = ["spec/fixtures/forms"] - @forms = get_all_forms + @forms = fake_forms || get_all_forms end def use_real_forms! diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb index 1afdc3678..13f4ef148 100644 --- a/app/models/lettings_log.rb +++ b/app/models/lettings_log.rb @@ -540,24 +540,6 @@ class LettingsLog < Log private - def reset_derived_questions - dependent_questions = { waityear: [{ key: :renewal, value: 0 }], - referral: [{ key: :renewal, value: 0 }], - rsnvac: [{ key: :renewal, value: 0 }], - underoccupation_benefitcap: [{ key: :renewal, value: 0 }], - wchair: [{ key: :needstype, value: 1 }], - location_id: [{ key: :needstype, value: 1 }] } - - dependent_questions.each do |dependent, conditions| - condition_key = conditions.first[:key] - condition_value = conditions.first[:value] - if public_send("#{condition_key}_changed?") && condition_value == public_send(condition_key) && !public_send("#{dependent}_changed?") - Rails.logger.debug("Cleared derived #{dependent} value") - self[dependent] = nil - end - end - end - def reset_invalid_unresolved_log_fields! return unless unresolved? @@ -590,7 +572,6 @@ private reset_invalid_unresolved_log_fields! reset_scheme - reset_derived_questions end def dynamically_not_required diff --git a/app/models/log.rb b/app/models/log.rb index 5df04a72f..2c6c0254a 100644 --- a/app/models/log.rb +++ b/app/models/log.rb @@ -158,6 +158,10 @@ class Log < ApplicationRecord end end + def creation_method + bulk_upload_id ? "bulk upload" : "single log" + end + private # Handle logs that are older than previous collection start date diff --git a/app/models/sales_log.rb b/app/models/sales_log.rb index ac7af79d1..db31edfbe 100644 --- a/app/models/sales_log.rb +++ b/app/models/sales_log.rb @@ -233,6 +233,20 @@ class SalesLog < Log value * equity / 100 end + def mortgage_deposit_and_discount_total + mortgage_amount = mortgage || 0 + deposit_amount = deposit || 0 + cashdis_amount = cashdis || 0 + + mortgage_amount + deposit_amount + cashdis_amount + end + + def mortgage_and_deposit_total + return unless mortgage && deposit + + mortgage + deposit + end + def process_postcode(postcode, postcode_known_key, la_inferred_key, la_key) return if postcode.blank? diff --git a/app/models/validations/local_authority_validations.rb b/app/models/validations/local_authority_validations.rb new file mode 100644 index 000000000..e69de29bb diff --git a/app/models/validations/sales/financial_validations.rb b/app/models/validations/sales/financial_validations.rb index c3b1446c9..02fceeadc 100644 --- a/app/models/validations/sales/financial_validations.rb +++ b/app/models/validations/sales/financial_validations.rb @@ -7,7 +7,7 @@ module Validations::Sales::FinancialValidations relevant_fields = %i[income1 ownershipsch uprn la postcode_full] if record.london_property? && record.income1 > 90_000 - relevant_fields.each { |field| record.errors.add field, I18n.t("validations.financial.income.over_hard_max_for_london") } + relevant_fields.each { |field| record.errors.add field, :over_hard_max_for_london, message: I18n.t("validations.financial.income.over_hard_max_for_london") } elsif record.property_not_in_london? && record.income1 > 80_000 relevant_fields.each { |field| record.errors.add field, :over_hard_max_for_outside_london, message: I18n.t("validations.financial.income.over_hard_max_for_outside_london") } end @@ -18,9 +18,9 @@ module Validations::Sales::FinancialValidations relevant_fields = %i[income2 ownershipsch uprn la postcode_full] if record.london_property? && record.income2 > 90_000 - relevant_fields.each { |field| record.errors.add field, I18n.t("validations.financial.income.over_hard_max_for_london") } + relevant_fields.each { |field| record.errors.add field, :over_hard_max_for_london, message: I18n.t("validations.financial.income.over_hard_max_for_london") } elsif record.property_not_in_london? && record.income2 > 80_000 - relevant_fields.each { |field| record.errors.add field, I18n.t("validations.financial.income.over_hard_max_for_outside_london") } + relevant_fields.each { |field| record.errors.add field, :over_hard_max_for_outside_london, message: I18n.t("validations.financial.income.over_hard_max_for_outside_london") } end end diff --git a/app/models/validations/sales/soft_validations.rb b/app/models/validations/sales/soft_validations.rb index 19d150c7e..e1545759d 100644 --- a/app/models/validations/sales/soft_validations.rb +++ b/app/models/validations/sales/soft_validations.rb @@ -82,8 +82,8 @@ module Validations::Sales::SoftValidations saledate - hodate >= 3.years end - def purchase_price_min_or_max_text - value < sale_range.soft_min ? "minimum" : "maximum" + def purchase_price_higher_or_lower_text + value < sale_range.soft_min ? "lower" : "higher" end def purchase_price_soft_min_or_soft_max diff --git a/app/models/validations/soft_validations.rb b/app/models/validations/soft_validations.rb index 5c571af6a..303f525cd 100644 --- a/app/models/validations/soft_validations.rb +++ b/app/models/validations/soft_validations.rb @@ -93,6 +93,10 @@ module Validations::SoftValidations voiddate.present? && startdate.present? && voiddate.between?(startdate.to_date - TEN_YEARS_IN_DAYS, startdate.to_date - TWO_YEARS_IN_DAYS) end + def net_income_higher_or_lower_text + net_income_in_soft_max_range? ? "higher" : "lower" + end + private def details_known_or_lead_tenant?(tenant_number) @@ -124,11 +128,9 @@ private def retired_under_soft_min_age?(person_num) age = public_send("age#{person_num}") economic_status = public_send("ecstat#{person_num}") - gender = public_send("sex#{person_num}") - return unless age && economic_status && gender + return unless age && economic_status - %w[M X].include?(gender) && tenant_is_retired?(economic_status) && age < retirement_age_for_person(person_num) || - gender == "F" && tenant_is_retired?(economic_status) && age < 60 + tenant_is_retired?(economic_status) && age < 60 end def not_retired_over_soft_max_age?(person_num) diff --git a/app/models/validations/tenancy_validations.rb b/app/models/validations/tenancy_validations.rb index 2d4c15640..b03d4844b 100644 --- a/app/models/validations/tenancy_validations.rb +++ b/app/models/validations/tenancy_validations.rb @@ -30,6 +30,7 @@ module Validations::TenancyValidations conditions.each do |condition| next unless condition[:condition] + record.errors.add :needstype, condition[:error] record.errors.add :tenancylength, :tenancylength_invalid, message: condition[:error] record.errors.add :tenancy, condition[:error] end diff --git a/app/services/bulk_upload/lettings/year2022/row_parser.rb b/app/services/bulk_upload/lettings/year2022/row_parser.rb index 9d273dab5..953487cd7 100644 --- a/app/services/bulk_upload/lettings/year2022/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2022/row_parser.rb @@ -1,6 +1,7 @@ class BulkUpload::Lettings::Year2022::RowParser include ActiveModel::Model include ActiveModel::Attributes + include InterruptionScreenHelper QUESTIONS = { field_1: "What is the letting type?", @@ -279,9 +280,20 @@ class BulkUpload::Lettings::Year2022::RowParser attribute :field_133, :integer attribute :field_134, :integer - validates :field_1, presence: { message: I18n.t("validations.not_answered", question: "letting type") }, - inclusion: { in: (1..12).to_a, message: I18n.t("validations.invalid_option", question: "letting type") }, on: :after_log - validates :field_4, presence: { if: proc { [2, 4, 6, 8, 10, 12].include?(field_1) } }, on: :after_log + validate :validate_valid_radio_option, on: :before_log + + validates :field_1, + presence: { + message: I18n.t("validations.not_answered", question: "letting type"), + category: :setup, + }, + inclusion: { + in: (1..12).to_a, + message: I18n.t("validations.invalid_option", question: "letting type"), + category: :setup, + unless: -> { field_1.blank? }, + }, + on: :after_log validates :field_12, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 1 must be a number or the letter R" }, on: :after_log validates :field_13, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 2 must be a number or the letter R" }, allow_blank: true, on: :after_log @@ -292,14 +304,33 @@ class BulkUpload::Lettings::Year2022::RowParser validates :field_18, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 7 must be a number or the letter R" }, allow_blank: true, on: :after_log validates :field_19, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 8 must be a number or the letter R" }, allow_blank: true, on: :after_log - validates :field_96, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (day)") }, on: :after_log - validates :field_97, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (month)") }, on: :after_log - validates :field_98, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (year)") }, on: :after_log - - validates :field_98, format: { with: /\A\d{2}\z/, message: I18n.t("validations.setup.startdate.year_not_two_digits") }, on: :after_log + validates :field_96, + presence: { + message: I18n.t("validations.not_answered", question: "tenancy start date (day)"), + category: :setup, + }, on: :after_log + + validates :field_97, + presence: { + message: I18n.t("validations.not_answered", question: "tenancy start date (month)"), + category: :setup, + }, + on: :after_log + + validates :field_98, + presence: { + message: I18n.t("validations.not_answered", question: "tenancy start date (year)"), + category: :setup, + }, + format: { + with: /\A\d{2}\z/, + message: I18n.t("validations.setup.startdate.year_not_two_digits"), + unless: -> { field_98.blank? }, + category: :setup, + }, + on: :after_log validate :validate_data_types, on: :after_log - validate :validate_nulls, on: :after_log validate :validate_relevant_collection_window, on: :after_log validate :validate_la_with_local_housing_referral, on: :after_log validate :validate_cannot_be_la_referral_if_general_needs_and_la, on: :after_log @@ -309,6 +340,7 @@ class BulkUpload::Lettings::Year2022::RowParser validate :validate_no_disabled_needs_conjunction, on: :after_log validate :validate_dont_know_disabled_needs_conjunction, on: :after_log validate :validate_no_and_dont_know_disabled_needs_conjunction, on: :after_log + validate :validate_no_housing_needs_questions_answered, on: :after_log validate :validate_if_log_already_exists, on: :after_log, if: -> { FeatureToggle.bulk_upload_duplicate_log_check_enabled? } validate :validate_owning_org_data_given, on: :after_log @@ -334,7 +366,9 @@ class BulkUpload::Lettings::Year2022::RowParser validate :validate_declaration_acceptance, on: :after_log - validate :validate_valid_radio_option, on: :before_log + validate :validate_incomplete_soft_validations, on: :after_log + + validate :validate_nulls, on: :after_log def self.question_for_field(field) QUESTIONS[field] @@ -422,7 +456,11 @@ private fields = field_mapping_for_errors[question_id.to_sym] || [] fields.each do |field| - errors.add(field, I18n.t("validations.invalid_option", question: QUESTIONS[field])) + if setup_question?(question) + errors.add(field, I18n.t("validations.invalid_option", question: QUESTIONS[field]), category: :setup) + else + errors.add(field, I18n.t("validations.invalid_option", question: QUESTIONS[field])) + end end end end @@ -479,13 +517,13 @@ private def validate_location_exists if scheme && field_5.present? && location.nil? - errors.add(:field_5, "Location could be found with provided scheme code") + errors.add(:field_5, "Location could be found with provided scheme code", category: :setup) end end def validate_location_data_given if bulk_upload.supported_housing? && field_5.blank? - errors.add(:field_5, "The scheme code must be present", category: "setup") + errors.add(:field_5, I18n.t("validations.not_answered", question: "scheme code"), category: :setup) end end @@ -497,19 +535,19 @@ private unless owned_by_owning_org || owned_by_managing_org block_log_creation! - errors.add(:field_4, "This management group code does not belong to your organisation, or any of your stock owners / managing agents") + errors.add(:field_4, "This management group code does not belong to your organisation, or any of your stock owners / managing agents", category: :setup) end end def validate_scheme_exists if field_4.present? && scheme.nil? - errors.add(:field_4, "The management group code is not correct") + errors.add(:field_4, "The management group code is not correct", category: :setup) end end def validate_scheme_data_given if bulk_upload.supported_housing? && field_4.blank? - errors.add(:field_4, "The management group code is not correct", category: "setup") + errors.add(:field_4, I18n.t("validations.not_answered", question: "management group code"), category: :setup) end end @@ -536,7 +574,7 @@ private def validate_managing_org_data_given if field_113.blank? block_log_creation! - errors.add(:field_113, "The managing organisation code is incorrect", category: :setup) + errors.add(:field_113, I18n.t("validations.not_answered", question: "managing organisation"), category: :setup) end end @@ -565,7 +603,7 @@ private block_log_creation! if errors[:field_111].blank? - errors.add(:field_111, "The owning organisation code is incorrect", category: :setup) + errors.add(:field_111, I18n.t("validations.not_answered", question: "owning organisation"), category: :setup) end end end @@ -611,6 +649,16 @@ private end end + def validate_no_housing_needs_questions_answered + if [field_55, field_56, field_57, field_58, field_59, field_60].all?(&:blank?) + errors.add(:field_59, I18n.t("validations.not_answered", question: "anybody with disabled access needs")) + errors.add(:field_58, I18n.t("validations.not_answered", question: "other access needs")) + %i[field_55 field_56 field_57].each do |field| + errors.add(field, I18n.t("validations.not_answered", question: "disabled access needs type")) + end + end + end + def validate_lettings_type_matches_bulk_upload if [1, 3, 5, 7, 9, 11].include?(field_1) && !bulk_upload.general_needs? errors.add(:field_1, I18n.t("validations.setup.lettype.supported_housing_mismatch")) @@ -643,9 +691,9 @@ private return if start_date.blank? || bulk_upload.form.blank? unless bulk_upload.form.valid_start_date_for_form?(start_date) - errors.add(:field_96, I18n.t("validations.date.outside_collection_window")) - errors.add(:field_97, I18n.t("validations.date.outside_collection_window")) - errors.add(:field_98, I18n.t("validations.date.outside_collection_window")) + errors.add(:field_96, I18n.t("validations.date.outside_collection_window"), category: :setup) + errors.add(:field_97, I18n.t("validations.date.outside_collection_window"), category: :setup) + errors.add(:field_98, I18n.t("validations.date.outside_collection_window"), category: :setup) end end @@ -702,7 +750,7 @@ private if setup_question?(question) fields.each do |field| - if errors[field].present? + if errors.select { |e| fields.include?(e.attribute) }.none? errors.add(field, I18n.t("validations.not_answered", question: question.check_answer_label&.downcase), category: :setup) end end @@ -716,6 +764,23 @@ private end end + def validate_incomplete_soft_validations + routed_to_soft_validation_questions = log.form.questions.filter { |q| q.type == "interruption_screen" && q.page.routed_to?(log, nil) } + routed_to_soft_validation_questions.each do |question| + next unless question + next if question.completed?(log) + + question.page.interruption_screen_question_ids.each do |interruption_screen_question_id| + field_mapping_for_errors[interruption_screen_question_id.to_sym].each do |field| + unless errors.any? { |e| e.options[:category] == :soft_validation && field_mapping_for_errors[interruption_screen_question_id.to_sym].include?(e.attribute) } + error_message = [display_title_text(question.page.title_text, log), display_informative_text(question.page.informative_text, log)].reject(&:empty?).join(". ") + errors.add(field, message: error_message, category: :soft_validation) + end + end + end + end + end + def setup_question?(question) log.form.setup_sections[0].subsections[0].questions.include?(question) end diff --git a/app/services/bulk_upload/lettings/year2023/row_parser.rb b/app/services/bulk_upload/lettings/year2023/row_parser.rb index 4393d1af2..85abd180d 100644 --- a/app/services/bulk_upload/lettings/year2023/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2023/row_parser.rb @@ -1,6 +1,7 @@ class BulkUpload::Lettings::Year2023::RowParser include ActiveModel::Model include ActiveModel::Attributes + include InterruptionScreenHelper QUESTIONS = { field_1: "Which organisation owns this property?", @@ -279,9 +280,61 @@ class BulkUpload::Lettings::Year2023::RowParser attribute :field_133, :integer attribute :field_134, :decimal - validates :field_5, presence: { message: I18n.t("validations.not_answered", question: "letting type") }, - inclusion: { in: (1..12).to_a, message: I18n.t("validations.invalid_option", question: "letting type") }, on: :after_log - validates :field_16, presence: { if: proc { [2, 4, 6, 8, 10, 12].include?(field_5) } }, on: :after_log + validate :validate_valid_radio_option, on: :before_log + + validates :field_5, + presence: { + message: I18n.t("validations.not_answered", question: "letting type"), + category: :setup, + }, + inclusion: { + in: (1..12).to_a, + message: I18n.t("validations.invalid_option", question: "letting type"), + unless: -> { field_5.blank? }, + category: :setup, + }, + on: :after_log + + validates :field_6, + presence: { + message: I18n.t("validations.not_answered", question: "property renewal"), + category: :setup, + }, + on: :after_log + + validates :field_7, + presence: { + message: I18n.t("validations.not_answered", question: "tenancy start date (day)"), + category: :setup, + }, + on: :after_log + + validates :field_8, + presence: { + message: I18n.t("validations.not_answered", question: "tenancy start date (month)"), + category: :setup, + }, + on: :after_log + + validates :field_9, + presence: { + message: I18n.t("validations.not_answered", question: "tenancy start date (year)"), + category: :setup, + }, + format: { + with: /\A\d{2}\z/, + message: I18n.t("validations.setup.startdate.year_not_two_digits"), + category: :setup, + unless: -> { field_9.blank? }, + }, + on: :after_log + + validates :field_16, + presence: { + if: proc { [2, 4, 6, 8, 10, 12].include?(field_5) }, + category: :setup, + }, + on: :after_log validates :field_46, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 1 must be a number or the letter R" }, on: :after_log validates :field_52, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 2 must be a number or the letter R" }, allow_blank: true, on: :after_log @@ -292,16 +345,8 @@ class BulkUpload::Lettings::Year2023::RowParser validates :field_72, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 7 must be a number or the letter R" }, allow_blank: true, on: :after_log validates :field_76, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 8 must be a number or the letter R" }, allow_blank: true, on: :after_log - validates :field_4, presence: { message: I18n.t("validations.not_answered", question: "needs type") }, on: :after_log - validates :field_6, presence: { message: I18n.t("validations.not_answered", question: "property renewal") }, on: :after_log - validates :field_7, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (day)") }, on: :after_log - validates :field_8, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (month)") }, on: :after_log - validates :field_9, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (year)") }, on: :after_log - - validates :field_9, format: { with: /\A\d{2}\z/, message: I18n.t("validations.setup.startdate.year_not_two_digits") }, on: :after_log - + validate :validate_needs_type_present, on: :after_log validate :validate_data_types, on: :after_log - validate :validate_nulls, on: :after_log validate :validate_relevant_collection_window, on: :after_log validate :validate_la_with_local_housing_referral, on: :after_log validate :validate_cannot_be_la_referral_if_general_needs_and_la, on: :after_log @@ -311,6 +356,7 @@ class BulkUpload::Lettings::Year2023::RowParser validate :validate_no_disabled_needs_conjunction, on: :after_log validate :validate_dont_know_disabled_needs_conjunction, on: :after_log validate :validate_no_and_dont_know_disabled_needs_conjunction, on: :after_log + validate :validate_no_housing_needs_questions_answered, on: :after_log validate :validate_if_log_already_exists, on: :after_log, if: -> { FeatureToggle.bulk_upload_duplicate_log_check_enabled? } validate :validate_owning_org_data_given, on: :after_log @@ -335,10 +381,12 @@ class BulkUpload::Lettings::Year2023::RowParser validate :validate_declaration_acceptance, on: :after_log - validate :validate_valid_radio_option, on: :before_log + validate :validate_nulls, on: :after_log validate :validate_uprn_exists_if_any_key_adddress_fields_are_blank, on: :after_log + validate :validate_incomplete_soft_validations, on: :after_log + def self.question_for_field(field) QUESTIONS[field] end @@ -425,7 +473,11 @@ private fields = field_mapping_for_errors[question_id.to_sym] || [] fields.each do |field| - errors.add(field, I18n.t("validations.invalid_option", question: QUESTIONS[field])) + if setup_question?(question) + errors.add(field, I18n.t("validations.invalid_option", question: QUESTIONS[field]), category: :setup) + else + errors.add(field, I18n.t("validations.invalid_option", question: QUESTIONS[field])) + end end end end @@ -457,6 +509,23 @@ private end end + def validate_incomplete_soft_validations + routed_to_soft_validation_questions = log.form.questions.filter { |q| q.type == "interruption_screen" && q.page.routed_to?(log, nil) } + routed_to_soft_validation_questions.each do |question| + next unless question + next if question.completed?(log) + + question.page.interruption_screen_question_ids.each do |interruption_screen_question_id| + field_mapping_for_errors[interruption_screen_question_id.to_sym].each do |field| + unless errors.any? { |e| field_mapping_for_errors[interruption_screen_question_id.to_sym].include?(e.attribute) } + error_message = [display_title_text(question.page.title_text, log), display_informative_text(question.page.informative_text, log)].reject(&:empty?).join(". ") + errors.add(field, message: error_message, category: :soft_validation) + end + end + end + end + end + def duplicate_check_fields %w[ startdate @@ -473,7 +542,7 @@ private def validate_needs_type_present if field_4.blank? - errors.add(:field_4, I18n.t("validations.not_answered", question: "needs type")) + errors.add(:field_4, I18n.t("validations.not_answered", question: "needs type"), category: :setup) end end @@ -516,6 +585,16 @@ private end end + def validate_no_housing_needs_questions_answered + if [field_83, field_84, field_85, field_86, field_87, field_88].all?(&:blank?) + errors.add(:field_87, I18n.t("validations.not_answered", question: "anybody with disabled access needs")) + errors.add(:field_86, I18n.t("validations.not_answered", question: "other access needs")) + %i[field_83 field_84 field_85].each do |field| + errors.add(field, I18n.t("validations.not_answered", question: "disabled access needs type")) + end + end + end + def validate_lettings_type_matches_bulk_upload if [1, 3, 5, 7, 9, 11].include?(field_5) && !general_needs? errors.add(:field_5, I18n.t("validations.setup.lettype.supported_housing_mismatch")) @@ -556,9 +635,9 @@ private return if start_date.blank? || bulk_upload.form.blank? unless bulk_upload.form.valid_start_date_for_form?(start_date) - errors.add(:field_7, I18n.t("validations.date.outside_collection_window")) - errors.add(:field_8, I18n.t("validations.date.outside_collection_window")) - errors.add(:field_9, I18n.t("validations.date.outside_collection_window")) + errors.add(:field_7, I18n.t("validations.date.outside_collection_window"), category: :setup) + errors.add(:field_8, I18n.t("validations.date.outside_collection_window"), category: :setup) + errors.add(:field_9, I18n.t("validations.date.outside_collection_window"), category: :setup) end end @@ -579,7 +658,7 @@ private if setup_question?(question) fields.each do |field| - if errors[field].present? + if errors.select { |e| fields.include?(e.attribute) }.none? question_text = question.check_answer_label.presence || question.header.presence || "this question" errors.add(field, I18n.t("validations.not_answered", question: question_text.downcase), category: :setup) end @@ -600,19 +679,19 @@ private unless location.scheme == scheme block_log_creation! - errors.add(:field_17, "Scheme code must relate to a location that is owned by owning organisation or managing organisation") + errors.add(:field_17, "Scheme code must relate to a location that is owned by owning organisation or managing organisation", category: :setup) end end def validate_location_exists if scheme && field_17.present? && location.nil? - errors.add(:field_17, "Location could be found with provided scheme code") + errors.add(:field_17, "Location could be found with provided scheme code", category: :setup) end end def validate_location_data_given if supported_housing? && field_17.blank? - errors.add(:field_17, "The scheme code must be present", category: "setup") + errors.add(:field_17, I18n.t("validations.not_answered", question: "scheme code"), category: "setup") end end @@ -624,19 +703,19 @@ private unless owned_by_owning_org || owned_by_managing_org block_log_creation! - errors.add(:field_16, "This management group code does not belong to your organisation, or any of your stock owners / managing agents") + errors.add(:field_16, "This management group code does not belong to your organisation, or any of your stock owners / managing agents", category: :setup) end end def validate_scheme_exists if field_16.present? && scheme.nil? - errors.add(:field_16, "The management group code is not correct") + errors.add(:field_16, "The management group code is not correct", category: :setup) end end def validate_scheme_data_given if supported_housing? && field_16.blank? - errors.add(:field_16, "The management group code is not correct", category: "setup") + errors.add(:field_16, I18n.t("validations.not_answered", question: "management group code"), category: "setup") end end @@ -690,7 +769,7 @@ private def validate_owning_org_data_given if field_1.blank? block_log_creation! - errors.add(:field_1, "The owning organisation code is incorrect", category: :setup) + errors.add(:field_1, I18n.t("validations.not_answered", question: "owning organisation"), category: :setup) end end diff --git a/app/services/bulk_upload/sales/year2022/row_parser.rb b/app/services/bulk_upload/sales/year2022/row_parser.rb index b08db8585..4383898fb 100644 --- a/app/services/bulk_upload/sales/year2022/row_parser.rb +++ b/app/services/bulk_upload/sales/year2022/row_parser.rb @@ -1,6 +1,7 @@ class BulkUpload::Sales::Year2022::RowParser include ActiveModel::Model include ActiveModel::Attributes + include InterruptionScreenHelper QUESTIONS = { field_1: "What is the purchaser code?", @@ -283,6 +284,7 @@ class BulkUpload::Sales::Year2022::RowParser validate :validate_created_by_exists, on: :after_log validate :validate_created_by_related, on: :after_log validate :validate_relevant_collection_window, on: :after_log + validate :validate_incomplete_soft_validations, on: :after_log def self.question_for_field(field) QUESTIONS[field] @@ -939,9 +941,26 @@ private return if saledate.blank? || bulk_upload.form.blank? unless bulk_upload.form.valid_start_date_for_form?(saledate) - errors.add(:field_2, I18n.t("validations.date.outside_collection_window")) - errors.add(:field_3, I18n.t("validations.date.outside_collection_window")) - errors.add(:field_4, I18n.t("validations.date.outside_collection_window")) + errors.add(:field_2, I18n.t("validations.date.outside_collection_window"), category: :setup) + errors.add(:field_3, I18n.t("validations.date.outside_collection_window"), category: :setup) + errors.add(:field_4, I18n.t("validations.date.outside_collection_window"), category: :setup) + end + end + + def validate_incomplete_soft_validations + routed_to_soft_validation_questions = log.form.questions.filter { |q| q.type == "interruption_screen" && q.page.routed_to?(log, nil) } + routed_to_soft_validation_questions.each do |question| + next unless question + next if question.completed?(log) + + question.page.interruption_screen_question_ids.each do |interruption_screen_question_id| + field_mapping_for_errors[interruption_screen_question_id.to_sym].each do |field| + unless errors.any? { |e| e.options[:category] == :soft_validation && field_mapping_for_errors[interruption_screen_question_id.to_sym].include?(e.attribute) } + error_message = [display_title_text(question.page.title_text, log), display_informative_text(question.page.informative_text, log)].reject(&:empty?).join(". ") + errors.add(field, message: error_message, category: :soft_validation) + end + end + end end end end diff --git a/app/services/csv/sales_log_csv_service.rb b/app/services/csv/sales_log_csv_service.rb new file mode 100644 index 000000000..a9573df41 --- /dev/null +++ b/app/services/csv/sales_log_csv_service.rb @@ -0,0 +1,107 @@ +module Csv + class SalesLogCsvService + def initialize(export_type:) + @export_type = export_type + @attributes = sales_log_attributes + end + + def prepare_csv(logs) + CSV.generate(headers: true) do |csv| + csv << @attributes + + logs.find_each do |log| + csv << @attributes.map { |attribute| value(attribute, log) } + end + end + end + + private + + ATTRIBUTES_OF_RELATED_OBJECTS = { + day: %i[saledate day], + month: %i[saledate month], + year: %i[saledate year], + is_dpo: %i[created_by is_dpo], + created_by_name: %i[created_by name], + owning_organisation_name: %i[owning_organisation name], + }.freeze + + FIELDS_ALWAYS_EXPORTED_AS_CODES = %w[ + la + prevloc + ].freeze + + FIELDS_ALWAYS_EXPORTED_AS_LABELS = { + "la_label" => "la", + "prevloc_label" => "prevloc", + }.freeze + + DATE_FIELDS = %w[ + created_at + updated_at + ].freeze + + def value(attribute, log) + if ATTRIBUTES_OF_RELATED_OBJECTS.key? attribute.to_sym + call_chain = ATTRIBUTES_OF_RELATED_OBJECTS[attribute.to_sym] + call_chain.reduce(log) { |object, next_call| object&.public_send(next_call) } + elsif FIELDS_ALWAYS_EXPORTED_AS_CODES.include? attribute + log.send(attribute) + elsif FIELDS_ALWAYS_EXPORTED_AS_LABELS.key? attribute + attribute = FIELDS_ALWAYS_EXPORTED_AS_LABELS[attribute] + field_value = log.send(attribute) + get_label(field_value, attribute, log) + elsif DATE_FIELDS.include? attribute + log.send(attribute)&.iso8601 + else + value = log.public_send(attribute) + case @export_type + when "codes" + value + when "labels" + answer_label = get_label(value, attribute, log) + answer_label || label_if_boolean_value(value) || value + end + end + end + + def get_label(value, attribute, log) + log.form + .get_question(attribute, log) + &.label_from_value(value) + end + + def label_if_boolean_value(value) + return "Yes" if value == true + return "No" if value == false + end + + ATTRIBUTE_MAPPINGS = { + "saledate" => %w[day month year], + "exdate" => %w[exday exmonth exyear], + "hodate" => %w[hoday homonth hoyear], + "postcode_full" => %w[pcode1 pcode2], + "ppostcode_full" => %w[ppostc1 ppostc2], + "la" => %w[la la_label], + "prevloc" => %w[prevloc prevloc_label], + "created_by_id" => %w[created_by_name], + "owning_organisation_id" => %w[owning_organisation_name], + }.freeze + + def sales_log_attributes + ordered_questions = FormHandler.instance.ordered_sales_questions_for_all_years + ordered_questions.reject! { |q| q.id.match?(/((? %w[ecstat1], %i[tshortfall no_outstanding_charges] => %w[tshortfall hbrentshortfall], %i[beds outside_the_range] => %w[beds], - %i[tcharge complete_1_of_3] => %w[brent scharge pscharge supcharg tcharge], - %i[scharge under_min] => %w[brent scharge pscharge supcharg tcharge], + %i[tcharge complete_1_of_3] => charges_attributes, + %i[scharge under_min] => charges_attributes, %i[tshortfall must_be_positive] => %w[tshortfall tshortfall_known], %i[referral referral_invalid] => %w[referral], - %i[pscharge outside_the_range] => %w[brent scharge pscharge supcharg tcharge], - %i[supcharg outside_the_range] => %w[brent scharge pscharge supcharg tcharge], - %i[scharge outside_the_range] => %w[brent scharge pscharge supcharg tcharge], + %i[pscharge outside_the_range] => charges_attributes, + %i[supcharg outside_the_range] => charges_attributes, + %i[scharge outside_the_range] => charges_attributes, %i[location_id not_active] => %w[location_id scheme_id], - %i[tcharge under_10] => %w[brent scharge pscharge supcharg tcharge], - %i[brent over_hard_max] => %w[brent scharge pscharge supcharg tcharge], + %i[tcharge under_10] => charges_attributes, + %i[brent over_hard_max] => charges_attributes, } (2..8).each do |person| @@ -464,11 +467,11 @@ module Imports 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 + 0 # not known elsif previous_postcode.nil? nil else - 1 + 1 # known end end diff --git a/app/services/imports/sales_logs_import_service.rb b/app/services/imports/sales_logs_import_service.rb index 8702a8846..6e2c11bf3 100644 --- a/app/services/imports/sales_logs_import_service.rb +++ b/app/services/imports/sales_logs_import_service.rb @@ -70,7 +70,7 @@ module Imports attributes["inc1mort"] = unsafe_string_as_integer(xml_doc, "Q2Person1Mortgage") attributes["income2"] = safe_string_as_integer(xml_doc, "Q2Person2Income") attributes["income2nk"] = income_known(unsafe_string_as_integer(xml_doc, "P2IncKnown")) - attributes["savings"] = safe_string_as_integer(xml_doc, "Q3Savings") + attributes["savings"] = safe_string_as_integer(xml_doc, "Q3Savings")&.round(-1) attributes["savingsnk"] = savings_known(xml_doc) attributes["prevown"] = unsafe_string_as_integer(xml_doc, "Q4PrevOwnedProperty") attributes["mortgage"] = safe_string_as_decimal(xml_doc, "CALCMORT") @@ -217,38 +217,46 @@ module Imports attributes.delete("ppcodenk") if error.attribute == :ppostcode_full end @logs_overridden << sales_log.old_id - save_sales_log(attributes, previous_status) - elsif sales_log.errors.of_kind?(:postcode_full, :postcodes_not_matching) - @logger.warn("Log #{sales_log.old_id}: Removing previous postcode known and previous postcode as the postcode is invalid") - @logs_overridden << sales_log.old_id - attributes.delete("ppcodenk") - attributes.delete("ppostcode_full") - save_sales_log(attributes, previous_status) - elsif sales_log.errors.of_kind?(:exdate, :over_a_year_from_saledate) - @logger.warn("Log #{sales_log.old_id}: Removing exchange date as the exchange date is invalid") - @logs_overridden << sales_log.old_id - attributes.delete("exdate") - save_sales_log(attributes, previous_status) - elsif sales_log.errors.of_kind?(:income1, :over_hard_max_for_outside_london) - @logger.warn("Log #{sales_log.old_id}: Removing income1 as the income1 is invalid") - @logs_overridden << sales_log.old_id - attributes.delete("income1") - save_sales_log(attributes, previous_status) - elsif sales_log.errors.of_kind?(:equity, :over_max) || sales_log.errors.of_kind?(:equity, :under_min) - @logger.warn("Log #{sales_log.old_id}: Removing equity as the equity is invalid") + return save_sales_log(attributes, previous_status) + end + + errors = { + %i[postcode_full postcodes_not_matching] => %w[ppcodenk ppostcode_full], + %i[exdate over_a_year_from_saledate] => %w[exdate], + %i[income1 over_hard_max_for_outside_london] => %w[income1], + %i[income1 over_hard_max_for_london] => %w[income1], + %i[income2 over_hard_max_for_outside_london] => %w[income2], + %i[income2 over_hard_max_for_london] => %w[income2], + %i[equity over_max] => %w[equity], + %i[equity under_min] => %w[equity], + %i[mscharge under_min] => %w[mscharge has_mscharge], + %i[mortgage cannot_be_0] => %w[mortgage], + %i[frombeds outside_the_range] => %w[frombeds], + } + + errors.each do |(error, fields)| + next unless sales_log.errors.of_kind?(*error) + + attribute, _type = error + fields.each do |field| + @logger.warn("Log #{sales_log.old_id}: Removing #{field} with error: #{sales_log.errors[attribute].sort.join(', ')}") + attributes.delete(field) + end @logs_overridden << sales_log.old_id - attributes.delete("equity") - save_sales_log(attributes, previous_status) - elsif sales_log.errors.of_kind?(:postcode_full, :wrong_format) + return save_sales_log(attributes, previous_status) + end + + if sales_log.errors.of_kind?(:postcode_full, :wrong_format) @logger.warn("Log #{sales_log.old_id}: Removing postcode as the postcode is invalid") @logs_overridden << sales_log.old_id attributes.delete("postcode_full") attributes["pcodenk"] = attributes["la"].present? ? 1 : nil save_sales_log(attributes, previous_status) - elsif sales_log.errors.of_kind?(:mortgage, :cannot_be_0) - @logger.warn("Log #{sales_log.old_id}: Removing mortgage because it cannot be 0") + elsif sales_log.errors.of_kind?(:ppostcode_full, :wrong_format) + @logger.warn("Log #{sales_log.old_id}: Removing previous postcode as the postcode is invalid") @logs_overridden << sales_log.old_id - attributes.delete("mortgage") + attributes.delete("ppostcode_full") + attributes["ppcodenk"] = attributes["prevloc"].present? ? 1 : nil save_sales_log(attributes, previous_status) elsif sales_log.errors.of_kind?(:uprn, :uprn_error) @logger.warn("Log #{sales_log.old_id}: Setting uprn_known to no with error: #{sales_log.errors[:uprn].join(', ')}") diff --git a/app/views/form/_check_answers_summary_list.html.erb b/app/views/form/_check_answers_summary_list.html.erb index 653cd610d..a008e67d8 100644 --- a/app/views/form/_check_answers_summary_list.html.erb +++ b/app/views/form/_check_answers_summary_list.html.erb @@ -1,5 +1,5 @@ <%= govuk_summary_list do |summary_list| %> - <% total_applicable_questions(subsection, @log, current_user).each do |question| %> + <% questions.each do |question| %> <% summary_list.row do |row| %> <% row.key { get_question_label(question) } %> <% row.value do %> @@ -23,7 +23,7 @@ <% if @log.collection_period_open? %> <% row.action( text: question.action_text(@log), - href: question.action_href(@log, question.page.id), + href: action_href(@log, question.page.id, referrer), visually_hidden_text: question.check_answer_label.to_s.downcase, ) %> <% end %> diff --git a/app/views/form/_interruption_screen_banner.html.erb b/app/views/form/_interruption_screen_banner.html.erb new file mode 100644 index 000000000..14a89b770 --- /dev/null +++ b/app/views/form/_interruption_screen_banner.html.erb @@ -0,0 +1,13 @@ +<% if question.page.routed_to?(@log, current_user) %> + <%= govuk_panel( + classes: "app-panel--interruption", + ) do %> +

<%= display_title_text(title_text, lettings_log) %>

+ <% if informative_text.present? %> +

<%= display_informative_text(informative_text, lettings_log) %>

+ <% end %> + <% if question.hint_text.present? %> +

<%= question.hint_text&.html_safe %>

+ <% end %> + <% end %> +<% end %> diff --git a/app/views/form/_interruption_screen_question.html.erb b/app/views/form/_interruption_screen_question.html.erb index c5da48a92..a37254d1e 100644 --- a/app/views/form/_interruption_screen_question.html.erb +++ b/app/views/form/_interruption_screen_question.html.erb @@ -1,22 +1,24 @@ -<%= govuk_panel( - title_text: display_title_text(title_text, lettings_log), - classes: "app-panel--interruption", -) do %> -

<%= display_informative_text(informative_text, lettings_log) %>

- <%= f.govuk_radio_buttons_fieldset question.id.to_sym, - legend: { text: question.header }, - hint: { text: question.hint_text&.html_safe } do %> - <% question.answer_options.map do |key, options| %> - <% if key.starts_with?("divider") %> - <%= f.govuk_radio_divider %> - <% else %> - <%= f.govuk_radio_button question.id, - key, - label: { text: options["value"] }, - hint: { text: options["hint"] }, - **stimulus_html_attributes(question) %> - <% end %> - <% end %> - <% end %> - <%= f.govuk_submit "Save and continue", accesskey: "s", class: "app-button--inverse govuk-!-margin-bottom-0" %> -<% end %> +<%= render partial: "form/interruption_screen_banner", locals: { question:, title_text:, informative_text:, lettings_log: } %> + +

+ Make sure these answers are correct: +

+
+
+ + <%= render partial: "form/check_answers_summary_list", locals: { + lettings_log:, + questions: soft_validation_affected_questions(question, @log).filter { |q| q.page.routed_to?(@log, current_user) }, + referrer: "interruption_screen", + } %> +
+
+ +<%= f.hidden_field question.id, value: "0" %> +
+ <%= f.govuk_submit "Confirm and continue" %> + <%= govuk_link_to( + (@page.skip_text || "Skip for now"), + (@page.skip_href(@log) || send(@log.form.next_page_redirect_path(@page, @log, current_user), @log)), + ) %> +
diff --git a/app/views/form/check_answers.html.erb b/app/views/form/check_answers.html.erb index 3da071be5..3267b4008 100644 --- a/app/views/form/check_answers.html.erb +++ b/app/views/form/check_answers.html.erb @@ -25,6 +25,8 @@ <%= render partial: "form/check_answers_summary_list", locals: { subsection:, lettings_log: @log, + questions: total_applicable_questions(subsection, @log, current_user), + referrer: "check_answers", } %> <% end %> diff --git a/app/views/form/page.html.erb b/app/views/form/page.html.erb index 9b4d14d9d..b5ce2ead7 100644 --- a/app/views/form/page.html.erb +++ b/app/views/form/page.html.erb @@ -7,7 +7,7 @@
<%= form_with model: @log, url: request.original_url, method: "post", local: true do |f| %>
-
"> +
<% remove_other_page_errors(@log, @page) %> <%= f.govuk_error_summary %> @@ -59,6 +59,8 @@ <% end %> <%= f.hidden_field :page, value: @page.id %> + <%= f.hidden_field :interruption_page_id, value: @interruption_page_id %> + <%= f.hidden_field :interruption_page_referrer_type, value: @interruption_page_referrer_type %>
<% if !@page.interruption_screen? && if request.query_parameters["referrer"] != "check_answers" %> diff --git a/app/views/form/review.html.erb b/app/views/form/review.html.erb index 0e63875d9..e619da2f8 100644 --- a/app/views/form/review.html.erb +++ b/app/views/form/review.html.erb @@ -23,7 +23,7 @@

<%= subsection.label %>

- <%= render partial: "form/check_answers_summary_list", locals: { subsection: } %> + <%= render partial: "form/check_answers_summary_list", locals: { subsection:, questions: total_applicable_questions(subsection, @log, current_user), referrer: "check_answers" } %>
<% end %> diff --git a/app/views/logs/_log_list.html.erb b/app/views/logs/_log_list.html.erb index 9112cde81..e92186d4d 100644 --- a/app/views/logs/_log_list.html.erb +++ b/app/views/logs/_log_list.html.erb @@ -1,6 +1,6 @@

<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "logs", path: request.path)) %> - <% if logs&.first&.lettings? %> + <% if logs&.any? %> <%= govuk_link_to "Download (CSV)", csv_download_url, type: "text/csv", class: "govuk-!-margin-right-4" %> <% if @current_user.support? %> <%= govuk_link_to "Download (CSV, codes only)", csv_codes_only_download_url, type: "text/csv" %> diff --git a/app/views/logs/csv_confirmation.html.erb b/app/views/logs/csv_confirmation.html.erb index 90f90813c..847be9ebf 100644 --- a/app/views/logs/csv_confirmation.html.erb +++ b/app/views/logs/csv_confirmation.html.erb @@ -9,7 +9,7 @@

Open your email inbox and click the link to download your CSV file.

- <%= govuk_link_to "Return to logs", lettings_logs_path %> + <%= govuk_link_to "Return to logs", logs_path_for_controller(controller) %>

diff --git a/app/views/logs/index.html.erb b/app/views/logs/index.html.erb index 04209be73..7a4fcba06 100644 --- a/app/views/logs/index.html.erb +++ b/app/views/logs/index.html.erb @@ -78,8 +78,8 @@ searched: @searched, item_label:, total_count: @total_count, - csv_download_url: csv_download_url_for_controller(controller_type: controller, search: @search_term, codes_only: false), - csv_codes_only_download_url: csv_download_url_for_controller(controller_type: controller, search: @search_term, codes_only: true), + csv_download_url: csv_download_url_for_controller(controller:, search: @search_term, codes_only: false), + csv_codes_only_download_url: csv_download_url_for_controller(controller:, search: @search_term, codes_only: true), } %> <%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %> diff --git a/app/views/merge_requests/confirm_telephone_number.html.erb b/app/views/merge_requests/confirm_telephone_number.html.erb index 0c92a4f28..8b3a354b4 100644 --- a/app/views/merge_requests/confirm_telephone_number.html.erb +++ b/app/views/merge_requests/confirm_telephone_number.html.erb @@ -21,7 +21,12 @@ <% if @merge_request.absorbing_organisation.phone.present? %> <%= f.govuk_radio_buttons_fieldset(:telephone_number_correct, legend: nil) do %> <%= f.govuk_radio_button :telephone_number_correct, true, checked: @merge_request.telephone_number_correct?, label: { text: "This telephone number is correct" }, link_errors: true %> - <%= f.govuk_radio_button :telephone_number_correct, false, checked: @merge_request.new_telephone_number.present?, label: { text: "Enter a new phone number" } do %> + <%= f.govuk_radio_button( + :telephone_number_correct, + false, + checked: (@merge_request.new_telephone_number.present? || @merge_request.errors.key?(:new_telephone_number)), + label: { text: "Enter a new phone number" }, + ) do %> <%= f.govuk_text_field :new_telephone_number, label: { text: "Telephone number" }, width: "two-thirds" %> <% end %> <%= f.hidden_field :page, value: "confirm_telephone_number" %> diff --git a/app/views/merge_requests/new_organisation_address.html.erb b/app/views/merge_requests/new_organisation_address.html.erb index 32dc57c86..7dd331507 100644 --- a/app/views/merge_requests/new_organisation_address.html.erb +++ b/app/views/merge_requests/new_organisation_address.html.erb @@ -1,5 +1,33 @@ <% content_for :before_content do %> <% title = "New organisation address" %> <% content_for :title, title %> - <%= govuk_back_link href: new_organisation_name_merge_request_path(id: @merge_request) %> + <%= govuk_back_link href: new_organisation_name_merge_request_path(@merge_request) %> +<% end %> + +<%= form_with model: @merge_request, url: merge_request_path, method: :patch do |f| %> + <%= f.govuk_error_summary %> + +

What is <%= @merge_request.new_organisation_name.possessive %> address?

+
+
+ <%= f.govuk_text_field :new_organisation_address_line1, + label: { text: "Address line 1", size: "m" }, + autocomplete: "address-line1" %> + + <%= f.govuk_text_field :new_organisation_address_line2, + label: { text: "Address line 2", size: "m" }, + autocomplete: "address-line2" %> + + <%= f.govuk_text_field :new_organisation_postcode, + label: { text: "Postcode", size: "m" }, + autocomplete: "postal-code", + width: 10 %> + + <%= f.hidden_field :page, value: "new_organisation_address" %> +
+ <%= f.govuk_submit %> + <%= govuk_link_to("Skip for now", new_organisation_telephone_number_merge_request_path(@merge_request)) %> +
+
+
<% end %> diff --git a/app/views/merge_requests/new_organisation_telephone_number.html.erb b/app/views/merge_requests/new_organisation_telephone_number.html.erb new file mode 100644 index 000000000..059ca752d --- /dev/null +++ b/app/views/merge_requests/new_organisation_telephone_number.html.erb @@ -0,0 +1,5 @@ +<% content_for :before_content do %> + <% title = "New organisation telephone number" %> + <% content_for :title, title %> + <%= govuk_back_link href: new_organisation_address_merge_request_path(@merge_request) %> +<% end %> diff --git a/app/views/organisations/logs.html.erb b/app/views/organisations/logs.html.erb index bac737e6c..55c130c29 100644 --- a/app/views/organisations/logs.html.erb +++ b/app/views/organisations/logs.html.erb @@ -34,8 +34,8 @@ searched: @searched, item_label:, total_count: @total_count, - csv_download_url: logs_csv_download_organisation_path(@organisation, search: @search_term, codes_only: false), - csv_codes_only_download_url: logs_csv_download_organisation_path(@organisation, search: @search_term, codes_only: true), + csv_download_url: csv_download_url_by_log_type(@log_type, @organisation, search: @search_term, codes_only: false), + csv_codes_only_download_url: csv_download_url_by_log_type(@log_type, @organisation, search: @search_term, codes_only: true), } %> <%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %> diff --git a/config/forms/2022_2023.json b/config/forms/2022_2023.json index c3b4e3341..5a65e6f0e 100644 --- a/config/forms/2022_2023.json +++ b/config/forms/2022_2023.json @@ -789,7 +789,10 @@ "title_text": { "translation": "soft_validations.void_date.title_text" }, - "informative_text": {}, + "informative_text": { + "translation": "soft_validations.void_date.hint_text", + "arguments": [] + }, "questions": { "void_date_value_check": { "check_answer_label": "Void date confirmation", @@ -814,7 +817,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["voiddate", "startdate"] }, "property_major_repairs": { "header": "", @@ -898,7 +902,10 @@ "title_text": { "translation": "soft_validations.major_repairs_date.title_text" }, - "informative_text": {}, + "informative_text": { + "translation": "soft_validations.major_repairs_date.hint_text", + "arguments": [] + }, "questions": { "major_repairs_date_value_check": { "check_answer_label": "Major repairs date confirmation", @@ -923,7 +930,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["mrcdate", "startdate"] } }, "displayed_in_tasklist": [ @@ -1237,7 +1245,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_lead_hhmemb_value_check": { "depends_on": [ @@ -1280,7 +1289,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "lead_tenant_age": { "header": "", @@ -1379,7 +1389,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_lead_age_value_check": { "depends_on": [ @@ -1422,7 +1433,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "lead_tenant_gender_identity": { "header": "", @@ -1500,7 +1512,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_lead_value_check": { "depends_on": [ @@ -1543,7 +1556,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "lead_tenant_ethnic_group": { "header": "", @@ -1839,27 +1853,13 @@ "translation": "soft_validations.retirement.min.title", "arguments": [ { - "key": "retirement_age_for_person_1", - "label": false, - "i18n_template": "age" - } - ] - }, - "informative_text": { - "translation": "soft_validations.retirement.min.hint_text", - "arguments": [ - { - "key": "plural_gender_for_person_1", - "label": false, - "i18n_template": "gender" - }, - { - "key": "retirement_age_for_person_1", - "label": false, + "key": "age1", + "label": true, "i18n_template": "age" } ] }, + "informative_text": {}, "questions": { "retirement_value_check": { "check_answer_label": "Retirement confirmation", @@ -1885,7 +1885,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["ecstat1", "age1"] }, "lead_tenant_over_retirement_value_check": { "depends_on": [ @@ -1943,7 +1944,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["ecstat1", "sex1", "age1"] }, "person_2_known": { "header": "You’ve given us the details for 1 person in the household", @@ -2123,7 +2125,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_person_2_age_value_check": { "depends_on": [ @@ -2167,7 +2170,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "person_2_gender_identity": { "header": "", @@ -2246,7 +2250,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_person_2_value_check": { "depends_on": [ @@ -2290,7 +2295,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "person_2_working_situation": { "header": "", @@ -2377,27 +2383,13 @@ "translation": "soft_validations.retirement.min.title", "arguments": [ { - "key": "retirement_age_for_person_2", - "label": false, - "i18n_template": "age" - } - ] - }, - "informative_text": { - "translation": "soft_validations.retirement.min.hint_text", - "arguments": [ - { - "key": "plural_gender_for_person_2", - "label": false, - "i18n_template": "gender" - }, - { - "key": "retirement_age_for_person_2", - "label": false, + "key": "age2", + "label": true, "i18n_template": "age" } ] }, + "informative_text": {}, "questions": { "retirement_value_check": { "check_answer_label": "Retirement confirmation", @@ -2423,7 +2415,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["ecstat2", "age2"] }, "person_2_over_retirement_value_check": { "depends_on": [ @@ -2481,7 +2474,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["ecstat2", "sex2", "age2"] }, "person_3_known": { "header": "You’ve given us the details for 2 people in the household", @@ -2658,7 +2652,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_person_3_age_value_check": { "depends_on": [ @@ -2702,7 +2697,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "person_3_gender_identity": { "header": "", @@ -2781,7 +2777,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_person_3_value_check": { "depends_on": [ @@ -2825,7 +2822,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "person_3_working_situation": { "header": "", @@ -2912,27 +2910,13 @@ "translation": "soft_validations.retirement.min.title", "arguments": [ { - "key": "retirement_age_for_person_3", - "label": false, - "i18n_template": "age" - } - ] - }, - "informative_text": { - "translation": "soft_validations.retirement.min.hint_text", - "arguments": [ - { - "key": "plural_gender_for_person_3", - "label": false, - "i18n_template": "gender" - }, - { - "key": "retirement_age_for_person_3", - "label": false, + "key": "age3", + "label": true, "i18n_template": "age" } ] }, + "informative_text": {}, "questions": { "retirement_value_check": { "check_answer_label": "Retirement confirmation", @@ -2958,7 +2942,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["ecstat3", "age3"] }, "person_3_over_retirement_value_check": { "depends_on": [ @@ -3016,7 +3001,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["ecstat3", "sex3", "age3"] }, "person_4_known": { "header": "You’ve given us the details for 3 people in the household", @@ -3190,7 +3176,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_person_4_age_value_check": { "depends_on": [ @@ -3234,7 +3221,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "person_4_gender_identity": { "header": "", @@ -3313,7 +3301,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_person_4_value_check": { "depends_on": [ @@ -3357,7 +3346,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "person_4_working_situation": { "header": "", @@ -3444,27 +3434,13 @@ "translation": "soft_validations.retirement.min.title", "arguments": [ { - "key": "retirement_age_for_person_4", - "label": false, - "i18n_template": "age" - } - ] - }, - "informative_text": { - "translation": "soft_validations.retirement.min.hint_text", - "arguments": [ - { - "key": "plural_gender_for_person_4", - "label": false, - "i18n_template": "gender" - }, - { - "key": "retirement_age_for_person_4", - "label": false, + "key": "age4", + "label": true, "i18n_template": "age" } ] }, + "informative_text": {}, "questions": { "retirement_value_check": { "check_answer_label": "Retirement confirmation", @@ -3490,7 +3466,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["ecstat4", "age4"] }, "person_4_over_retirement_value_check": { "depends_on": [ @@ -3548,7 +3525,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["ecstat4", "sex4", "age4"] }, "person_5_known": { "header": "You’ve given us the details for 4 people in the household", @@ -3719,7 +3697,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_person_5_age_value_check": { "depends_on": [ @@ -3763,7 +3742,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "person_5_gender_identity": { "header": "", @@ -3842,7 +3822,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_person_5_value_check": { "depends_on": [ @@ -3886,7 +3867,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "person_5_working_situation": { "header": "", @@ -3973,27 +3955,13 @@ "translation": "soft_validations.retirement.min.title", "arguments": [ { - "key": "retirement_age_for_person_5", - "label": false, - "i18n_template": "age" - } - ] - }, - "informative_text": { - "translation": "soft_validations.retirement.min.hint_text", - "arguments": [ - { - "key": "plural_gender_for_person_5", - "label": false, - "i18n_template": "gender" - }, - { - "key": "retirement_age_for_person_5", - "label": false, + "key": "age5", + "label": true, "i18n_template": "age" } ] }, + "informative_text": {}, "questions": { "retirement_value_check": { "check_answer_label": "Retirement confirmation", @@ -4019,7 +3987,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["ecstat5", "age5"] }, "person_5_over_retirement_value_check": { "depends_on": [ @@ -4077,7 +4046,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["ecstat5", "sex5", "age5"] }, "person_6_known": { "header": "You’ve given us the details for 5 people in the household", @@ -4245,7 +4215,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_person_6_age_value_check": { "depends_on": [ @@ -4289,7 +4260,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "person_6_gender_identity": { "header": "", @@ -4368,7 +4340,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_person_6_value_check": { "depends_on": [ @@ -4412,7 +4385,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "person_6_working_situation": { "header": "", @@ -4499,27 +4473,13 @@ "translation": "soft_validations.retirement.min.title", "arguments": [ { - "key": "retirement_age_for_person_6", - "label": false, - "i18n_template": "age" - } - ] - }, - "informative_text": { - "translation": "soft_validations.retirement.min.hint_text", - "arguments": [ - { - "key": "plural_gender_for_person_6", - "label": false, - "i18n_template": "gender" - }, - { - "key": "retirement_age_for_person_6", - "label": false, + "key": "age6", + "label": true, "i18n_template": "age" } ] }, + "informative_text": {}, "questions": { "retirement_value_check": { "check_answer_label": "Retirement confirmation", @@ -4545,7 +4505,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["ecstat6", "age6"] }, "person_6_over_retirement_value_check": { "depends_on": [ @@ -4603,7 +4564,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["ecstat6", "sex6", "age6"] }, "person_7_known": { "header": "You’ve given us the details for 6 people in the household", @@ -4768,7 +4730,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_person_7_age_value_check": { "depends_on": [ @@ -4812,7 +4775,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "person_7_gender_identity": { "header": "", @@ -4891,7 +4855,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_person_7_value_check": { "depends_on": [ @@ -4935,7 +4900,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "person_7_working_situation": { "header": "", @@ -5022,27 +4988,13 @@ "translation": "soft_validations.retirement.min.title", "arguments": [ { - "key": "retirement_age_for_person_7", - "label": false, - "i18n_template": "age" - } - ] - }, - "informative_text": { - "translation": "soft_validations.retirement.min.hint_text", - "arguments": [ - { - "key": "plural_gender_for_person_7", - "label": false, - "i18n_template": "gender" - }, - { - "key": "retirement_age_for_person_7", - "label": false, + "key": "age7", + "label": true, "i18n_template": "age" } ] }, + "informative_text": {}, "questions": { "retirement_value_check": { "check_answer_label": "Retirement confirmation", @@ -5068,7 +5020,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["ecstat7", "age7"] }, "person_7_over_retirement_value_check": { "depends_on": [ @@ -5126,7 +5079,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["ecstat7", "sex7", "age7"] }, "person_8_known": { "header": "You’ve given us the details for 7 people in the household", @@ -5288,7 +5242,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_person_8_age_value_check": { "depends_on": [ @@ -5332,7 +5287,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "person_8_gender_identity": { "header": "", @@ -5411,7 +5367,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_person_8_value_check": { "depends_on": [ @@ -5455,7 +5412,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "person_8_working_situation": { "header": "", @@ -5542,27 +5500,13 @@ "translation": "soft_validations.retirement.min.title", "arguments": [ { - "key": "retirement_age_for_person_8", - "label": false, - "i18n_template": "age" - } - ] - }, - "informative_text": { - "translation": "soft_validations.retirement.min.hint_text", - "arguments": [ - { - "key": "plural_gender_for_person_8", - "label": false, - "i18n_template": "gender" - }, - { - "key": "retirement_age_for_person_8", - "label": false, + "key": "age8", + "label": true, "i18n_template": "age" } ] }, + "informative_text": {}, "questions": { "retirement_value_check": { "check_answer_label": "Retirement confirmation", @@ -5588,7 +5532,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["ecstat8", "age8"] }, "person_8_over_retirement_value_check": { "depends_on": [ @@ -5646,7 +5591,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["ecstat8", "sex8", "age8"] } } }, @@ -5828,7 +5774,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"] }, "females_in_soft_age_range_in_pregnant_household_value_check": { "depends_on": [ @@ -5880,7 +5827,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"] }, "access_needs_exist": { "header": "", @@ -7351,23 +7299,30 @@ } ], "title_text": { - "translation": "soft_validations.net_income.title_text" - }, - "informative_text": { - "translation": "soft_validations.net_income.hint_text", - "arguments": [ + "translation": "soft_validations.net_income.title_text", + "arguments" :[ { - "key": "ecstat1", + "key": "incfreq", "label": true, - "i18n_template": "ecstat1" + "i18n_template": "incfreq" }, { - "key": "earnings", - "label": true, + "key": "field_formatted_as_currency", + "arguments_for_key": "earnings", "i18n_template": "earnings" } ] }, + "informative_text": { + "translation": "soft_validations.net_income.hint_text", + "arguments": [ + { + "key": "net_income_higher_or_lower_text", + "label": false, + "i18n_template": "net_income_higher_or_lower_text" + } + ] + }, "questions": { "net_income_value_check": { "check_answer_label": "Net income confirmation", @@ -7392,7 +7347,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["incfreq", "earnings", "ecstat1"] }, "housing_benefit": { "header": "", @@ -7754,7 +7710,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["chcharge", "is_carehome"] }, "rent_weekly": { "header": "Household rent and charges", @@ -8268,8 +8225,8 @@ } ] }, - "header": "This rent is lower than expected for this property type, in this area. Check:", - "hint_text": "

Are you sure this is correct?

", + "header": "Are you sure this is correct?", + "hint_text": "This is lower than we would expect. Check:", "type": "interruption_screen", "answer_options": { "0": { @@ -8280,7 +8237,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["brent", "startdate", "la", "beds", "rent_type", "needstype"] }, "max_rent_value_check": { "depends_on": [ @@ -8312,8 +8270,8 @@ } ] }, - "header": "This rent is higher than expected for this property type, in this area. Check:", - "hint_text": "

Are you sure this is correct?

", + "header": "Are you sure this is correct?", + "hint_text": "This is higher than we would expect. Check:", "type": "interruption_screen", "answer_options": { "0": { @@ -8324,7 +8282,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["brent", "startdate", "la", "beds", "rent_type", "needstype"] }, "outstanding": { "header": "", diff --git a/config/locales/en.yml b/config/locales/en.yml index 4ab359ccf..78e3d7890 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -533,44 +533,54 @@ en: soft_validations: net_income: - title_text: "Net income is outside the expected range based on the lead tenant’s working situation" - hint_text: "

You told us the lead tenant’s working situation is: %{ecstat1}.

The household income you have entered is %{earnings}.

" + title_text: "You told us the lead tenant’s income is %{earnings} %{incfreq}." + hint_text: "This is %{net_income_higher_or_lower_text} than we would expect for their working situation." in_soft_min_range: message: "Net income is lower than expected based on the lead tenant’s working situation. Are you sure this is correct?" in_soft_max_range: message: "Net income is higher than expected based on the lead tenant’s working situation. Are you sure this is correct?" income: - under_soft_min_for_economic_status: "You said income was %{income}, which is below this working situation's minimum (%{minimum})" + under_soft_min_for_economic_status: + title_text: "You told us income was %{income}." + hint_text: "This is less than we would expect for someone in this working situation." rent: outside_range_title: "You told us the rent is %{brent}" min_hint_text: "The minimum rent expected for this type of property in this local authority is %{soft_min_for_period}." max_hint_text: "The maximum rent expected for this type of property in this local authority is %{soft_max_for_period}." purchase_price: title_text: "You told us the purchase price is %{value}" - hint_text: "The %{min_or_max} purchase price expected for this type of property in this local authority is %{soft_min_or_soft_max}" + hint_text: "This is %{higher_or_lower} than we would expect" retirement: min: - title: "You told us this person is under %{age} and retired" + title: "You told us this person is aged %{age} years and retired." hint_text: "The minimum expected retirement age for %{gender} in England is %{age}." max: title: "You told us this person is %{age} or over and not retired" hint_text: "The minimum expected retirement age for %{gender} in England is %{age}." extra_borrowing: - title: "The mortgage and deposit are higher than the purchase minus the discount" + title_text: "You told us that the mortgage and deposit total is %{mortgage_and_deposit_total}" + hint_text: "This is higher than the purchase price minus the discount." pregnancy: title: "You told us somebody in the household is pregnant" no_females: "You also told us there are no female tenants living at the property." females_not_in_soft_age_range: "You also told us that any female tenants living at the property are in the following age ranges:" major_repairs_date: - title_text: "You told us the time between the start of the tenancy and the major repairs completion date is more than 2 years" + title_text: "You told us the property has been vacant for 2 years." + hint_text: "This is higher than we would expect." void_date: - title_text: "You told us the time between the start of the tenancy and the void date is more than 2 years" + title_text: "You told us that the property has been vacant for more than 2 years." + hint_text: "This is higher than we would expect." shared_ownership_deposit: - title_text: "Mortgage, deposit and cash discount total should equal %{expected_shared_ownership_deposit_value}" - old_persons_shared_ownership: "At least one buyer should be aged over 64 for Older persons’ shared ownership scheme" - staircase_bought_seems_high: "You said %{percentage}% was bought in this staircasing transaction, which seems high. Are you sure?" + title_text: "You told us that the mortgage, deposit and discount add up to %{mortgage_deposit_and_discount_total}" + old_persons_shared_ownership: + title_text: "You told us the buyer is using the Older Persons Shared Ownership scheme." + hint_text: "At least one buyer must be aged 65 years and over to use this scheme." + staircase_bought_seems_high: + title_text: "You told us that %{percentage}% was bought in this staircasing transaction." + hint_text: "Most staircasing transactions are less than 50%" monthly_charges_over_soft_max: - title_text: "The amount of monthly charges is high for this type of property and sale type" + title_text: "You told us that the monthly charges were %{mscharge}." + hint_text: "This is higher than we would expect." student_not_child: title_text: "You told us this person is a student aged beween 16 and 19" discounted_sale_value: @@ -579,11 +589,28 @@ en: care_home_charges: title_text: "Care home charges should be provided if this is a care home accommodation" buyer1_livein_wrong_for_ownership_type: - title_text: "You told us that buyer 1 will not live in the property. For %{ownership_scheme} types, the buyer usually lives in the property." + title_text: "You told us that buyer 1 will not live in the property." + hint_text: " For %{ownership_scheme} types, the buyer usually lives in the property." buyer2_livein_wrong_for_ownership_type: - title_text: "You told us that buyer 2 will not live in the property. For %{ownership_scheme} types, the buyer usually lives in the property." + title_text: "You told us that buyer 2 will not live in the property." + hint_text: " For %{ownership_scheme} types, the buyer usually lives in the property." percentage_discount_value: - title_text: "You told us that the percentage discount was %{discount}. This seems high for this type of property." + title_text: "You told us that the percentage discount is %{discount}." + hint_text: "This is higher than we would expect." + savings: + title_text: "You told us the buyer’s savings were %{savings}." + hint_text: "This is higher than we would expect." + deposit: + title_text: "You told us the buyer’s deposit was %{deposit} and their savings were %{savings}." + hint_text: "The deposit amount is higher than we would expect for the amount of savings they have." + grant: + title_text: "You told us that the grant amount is %{grant}" + hint_text: "Loans, grants and subsidies are usually between £9,000 and £16,000." + wheelchair: + title_text: "You told us that someone in the household uses a wheelchair." + mortgage: + title_text: "You told us that the mortgage amount is %{mortgage}" + hint_text: "This is more than 5 times the income, which is higher than we would expect." devise: two_factor_authentication: diff --git a/config/routes.rb b/config/routes.rb index 48afc3357..e815b5137 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -113,9 +113,12 @@ Rails.application.routes.draw do get "users/invite", to: "users/account#new" get "lettings-logs", to: "organisations#lettings_logs" get "sales-logs", to: "organisations#sales_logs" - get "logs/csv-download", to: "organisations#download_csv" - post "logs/email-csv", to: "organisations#email_csv" - get "logs/csv-confirmation", to: "lettings_logs#csv_confirmation" + get "lettings-logs/csv-download", to: "organisations#download_lettings_csv" + post "lettings-logs/email-csv", to: "organisations#email_lettings_csv" + get "lettings-logs/csv-confirmation", to: "lettings_logs#csv_confirmation" + get "sales-logs/csv-download", to: "organisations#download_sales_csv" + post "sales-logs/email-csv", to: "organisations#email_sales_csv" + get "sales-logs/csv-confirmation", to: "sales_logs#csv_confirmation" get "schemes", to: "organisations#schemes" get "stock-owners", to: "organisation_relationships#stock_owners" get "stock-owners/add", to: "organisation_relationships#add_stock_owner" @@ -140,6 +143,7 @@ Rails.application.routes.draw do get "confirm-telephone-number" get "new-organisation-name" get "new-organisation-address" + get "new-organisation-telephone-number" get "merge-date" end end @@ -197,6 +201,10 @@ Rails.application.routes.draw do resources :sales_logs, path: "/sales-logs" do collection do + get "csv-download", to: "sales_logs#download_csv" + post "email-csv", to: "sales_logs#email_csv" + get "csv-confirmation", to: "sales_logs#csv_confirmation" + resources :bulk_upload_sales_logs, path: "bulk-upload-logs" do collection do get :start diff --git a/db/migrate/20230504111352_add_new_organisation_address.rb b/db/migrate/20230504111352_add_new_organisation_address.rb new file mode 100644 index 000000000..9b10decba --- /dev/null +++ b/db/migrate/20230504111352_add_new_organisation_address.rb @@ -0,0 +1,9 @@ +class AddNewOrganisationAddress < ActiveRecord::Migration[7.0] + def change + change_table :merge_requests, bulk: true do |t| + t.column :new_organisation_address_line1, :string + t.column :new_organisation_address_line2, :string + t.column :new_organisation_postcode, :string + end + end +end diff --git a/db/schema.rb b/db/schema.rb index cbe185ed4..b75324351 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_05_02_135049) do +ActiveRecord::Schema[7.0].define(version: 2023_05_04_111352) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -372,6 +372,9 @@ ActiveRecord::Schema[7.0].define(version: 2023_05_02_135049) do t.boolean "telephone_number_correct" t.string "new_telephone_number" t.string "new_organisation_name" + t.string "new_organisation_address_line1" + t.string "new_organisation_address_line2" + t.string "new_organisation_postcode" end create_table "organisation_relationships", force: :cascade do |t| diff --git a/package.json b/package.json index 8b6a0dbe1..b4f225668 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,6 @@ "accessible-autocomplete": "^2.0.3", "babel-loader": "^8.2.3", "babel-plugin-macros": "^3.1.0", - "chart.js": "^3.6.0", "copy-webpack-plugin": "^10.2.4", "core-js": "^3.21.1", "css-loader": "^6.7.1", @@ -29,7 +28,7 @@ "sass": "^1.49.9", "sass-loader": "^12.6.0", "unfetch": "^4.2.0", - "webpack": "^5.70.0", + "webpack": "^5.76.0", "webpack-cli": "^4.9.2", "webpack-remove-empty-scripts": "^0.7.3" }, diff --git a/spec/components/check_answers_summary_list_card_component_spec.rb b/spec/components/check_answers_summary_list_card_component_spec.rb index 8bf9f4a45..842f55e14 100644 --- a/spec/components/check_answers_summary_list_card_component_spec.rb +++ b/spec/components/check_answers_summary_list_card_component_spec.rb @@ -62,13 +62,21 @@ RSpec.describe CheckAnswersSummaryListCardComponent, type: :component do context "when in 23/24 collection" do context "when given a set of questions" do - let(:user) { build(:user) } - let(:log) { build(:lettings_log, :completed, age2: 99, startdate: Time.zone.local(2023, 5, 1)) } + let(:user) { create(:user) } + let(:log) { create(:lettings_log, :completed, age2: 99, startdate: Time.zone.local(2023, 5, 1)) } let(:subsection_id) { "household_characteristics" } let(:subsection) { log.form.get_subsection(subsection_id) } let(:questions) { subsection.applicable_questions(log) } - it "renders a summary list card includinq question numbers for the answers to those questions" do + around do |example| + Timecop.freeze(Time.zone.local(2023, 5, 1)) + Singleton.__init__(FormHandler) + example.run + Timecop.return + Singleton.__init__(FormHandler) + end + + it "renders a summary list card including question numbers for the answers to those questions" do expect(rendered).to have_content(questions.first.answer_label(log)) expect(rendered).to have_content("Q32 - Lead tenant’s age") end diff --git a/spec/factories/form.rb b/spec/factories/form.rb new file mode 100644 index 000000000..9694cfaac --- /dev/null +++ b/spec/factories/form.rb @@ -0,0 +1,25 @@ +class FormFixture < Form + attr_accessor :sections, :subsections, :pages, :questions +end + +class FormFactory + def initialize(year:, type:) + @year = year + @type = type + end + + def with_sections(sections) + @sections = sections + self + end + + def build + form = FormFixture.new(nil, @year, [], @type) + @sections.each { |section| section.form = form } + form.sections = @sections + form.subsections = form.sections.flat_map(&:subsections) + form.pages = form.subsections.flat_map(&:pages) + form.questions = form.pages.flat_map(&:questions) + form + end +end diff --git a/spec/factories/page.rb b/spec/factories/page.rb new file mode 100644 index 000000000..2b5b27969 --- /dev/null +++ b/spec/factories/page.rb @@ -0,0 +1,21 @@ +FactoryBot.define do + factory :page, class: "Form::Page" do + id { "page_id" } + initialize_with { new(id, nil, nil) } + trait :with_question do + transient do + question_id { nil } + question { nil } + end + + after :build do |page, evaluator| + page.questions = if (q = evaluator.question) + q.page = page + [q] + else + [build(:question, id: evaluator.question_id, page:)] + end + end + end + end +end diff --git a/spec/factories/question.rb b/spec/factories/question.rb new file mode 100644 index 000000000..e34bc5e75 --- /dev/null +++ b/spec/factories/question.rb @@ -0,0 +1,6 @@ +FactoryBot.define do + factory :question, class: "Form::Question" do + initialize_with { new(id, nil, nil) } + type { "text" } + end +end diff --git a/spec/factories/sales_log.rb b/spec/factories/sales_log.rb index fa5773ef3..b17efec90 100644 --- a/spec/factories/sales_log.rb +++ b/spec/factories/sales_log.rb @@ -120,7 +120,8 @@ FactoryBot.define do has_mscharge { 1 } mscharge { 100 } mortlen { 10 } - pcodenk { 1 } + pcodenk { 0 } + postcode_full { "SW1A 1AA" } is_la_inferred { false } mortgagelender { 5 } extrabor { 1 } diff --git a/spec/factories/section.rb b/spec/factories/section.rb new file mode 100644 index 000000000..71bfd52ae --- /dev/null +++ b/spec/factories/section.rb @@ -0,0 +1,16 @@ +FactoryBot.define do + factory :section, class: "Form::Section" do + id { "section_id" } + initialize_with { new(id, nil, nil) } + trait :with_questions do + transient do + question_ids { nil } + questions { nil } + end + + after :build do |section, evaluator| + section.subsections = [build(:subsection, :with_questions, question_ids: evaluator.question_ids, questions: evaluator.questions, section:)] + end + end + end +end diff --git a/spec/factories/subsection.rb b/spec/factories/subsection.rb new file mode 100644 index 000000000..f4e4b307e --- /dev/null +++ b/spec/factories/subsection.rb @@ -0,0 +1,20 @@ +FactoryBot.define do + factory :subsection, class: "Form::Subsection" do + id { "subsection_id" } + initialize_with { new(id, nil, nil) } + trait :with_questions do + transient do + question_ids { nil } + questions { nil } + end + + after :build do |subsection, evaluator| + subsection.pages = if evaluator.questions + evaluator.questions.map { |question| build(:page, :with_question, question:, subsection:) } + else + evaluator.question_ids.map { |question_id| build(:page, :with_question, question_id:, subsection:) } + end + end + end + end +end diff --git a/spec/features/form/validations_spec.rb b/spec/features/form/validations_spec.rb index 12e5bb657..420114129 100644 --- a/spec/features/form/validations_spec.rb +++ b/spec/features/form/validations_spec.rb @@ -138,28 +138,62 @@ RSpec.describe "validations" do let(:income_over_soft_limit) { 750 } let(:income_under_soft_limit) { 700 } - it "prompts the user to confirm the value is correct with an interruption screen" do + before do visit("/lettings-logs/#{lettings_log.id}/net-income") fill_in("lettings-log-earnings-field", with: income_over_soft_limit) choose("lettings-log-incfreq-1-field", allow_label_click: true) click_button("Save and continue") + end + + it "prompts the user to confirm the value is correct with an interruption screen" do expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check") - expect(page).to have_content("Net income is outside the expected range based on the lead tenant’s working situation") - expect(page).to have_content("You told us the lead tenant’s working situation is: full-time – 30 hours or more") - expect(page).to have_content("The household income you have entered is £750.00 every week") - choose("lettings-log-net-income-value-check-0-field", allow_label_click: true) - click_button("Save and continue") + expect(page).to have_content("You told us the lead tenant’s income is £750.00 weekly.") + expect(page).to have_content("This is higher than we would expect for their working situation.") + click_button("Confirm and continue") expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-uc-proportion") end - it "returns the user to the previous question if they do not confirm the value as correct on the interruption screen" do - visit("/lettings-logs/#{lettings_log.id}/net-income") - fill_in("lettings-log-earnings-field", with: income_over_soft_limit) + it "allows to fix the questions that trigger the soft validation" do + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check") + expect(page).to have_link("Change", href: "/lettings-logs/#{lettings_log.id}/net-income?referrer=interruption_screen").twice + expect(page).to have_link("Change", href: "/lettings-logs/#{lettings_log.id}/person-1-working-situation?referrer=interruption_screen") + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check") + click_link("Change", href: "/lettings-logs/#{lettings_log.id}/net-income?referrer=interruption_screen", match: :first) + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income?referrer=interruption_screen") + fill_in("lettings-log-earnings-field", with: income_under_soft_limit) choose("lettings-log-incfreq-1-field", allow_label_click: true) click_button("Save and continue") - choose("lettings-log-net-income-value-check-1-field", allow_label_click: true) + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check") + expect(page).not_to have_content("You told us the lead tenant’s income is £750.00 weekly.") + expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success") + end + + it "allows to fix the questions from different sections" do + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check") + expect(page).to have_link("Change", href: "/lettings-logs/#{lettings_log.id}/net-income?referrer=interruption_screen").twice + expect(page).to have_link("Change", href: "/lettings-logs/#{lettings_log.id}/person-1-working-situation?referrer=interruption_screen") + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check") + click_link("Change", href: "/lettings-logs/#{lettings_log.id}/person-1-working-situation?referrer=interruption_screen") + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/person-1-working-situation?referrer=interruption_screen") + choose("lettings-log-ecstat1-10-field", allow_label_click: true) + click_button("Save and continue") + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check") + expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success") + end + + it "returns the user back to the check_your_answers after fixing a validation from check_your_anwers" do + lettings_log.update!(earnings: income_over_soft_limit, incfreq: 1) + visit("/lettings-logs/#{lettings_log.id}/income-and-benefits/check-answers") + click_link("Answer", href: "/lettings-logs/#{lettings_log.id}/net-income-value-check?referrer=check_answers") + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check?referrer=check_answers") + click_link("Change", href: "/lettings-logs/#{lettings_log.id}/net-income?referrer=interruption_screen", match: :first) + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income?referrer=interruption_screen") + fill_in("lettings-log-earnings-field", with: income_under_soft_limit) + choose("lettings-log-incfreq-1-field", allow_label_click: true) click_button("Save and continue") - expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income") + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check?referrer=check_answers") + click_button("Confirm and continue") + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/income-and-benefits/check-answers") end end end diff --git a/spec/fixtures/files/sales_logs_csv_export_codes.csv b/spec/fixtures/files/sales_logs_csv_export_codes.csv new file mode 100644 index 000000000..89a6a27bd --- /dev/null +++ b/spec/fixtures/files/sales_logs_csv_export_codes.csv @@ -0,0 +1,2 @@ +id,status,created_at,updated_at,old_id,collection_start_year,creation_method,is_dpo,owning_organisation_name,created_by_name,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,beds,proptype,builtype,pcodenk,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,wchair,noint,privacynotice,age1,sex1,ethnic_group,ethnic,national,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant +,completed,2023-02-08T16:52:15+00:00,2023-02-08T16:52:15+00:00,,2022,single log,false,DLUHC,Danny Rojas,2,2,2023,,2,8,,,,1,1,2,1,1,0,,,,,,,SW1A,1AA,1,E09000003,Barnet,1,2,1,30,X,17,17,18,1,1,P,35,X,,,,1,1,1,C,14,X,9,X,18,X,3,R,40,X,2,R,40,X,1,1,1,,,0,,,1,1,1,1,,,,1,4,5,1,1,0,10000,1,0,10000,1,4,1,,1,2,10,,,,,,,,,,,,,,,,,110000.0,,1,20000.0,5,,10,1,80000.0,1000.0,,1,100.0,,10000.0 diff --git a/spec/fixtures/files/sales_logs_csv_export_labels.csv b/spec/fixtures/files/sales_logs_csv_export_labels.csv new file mode 100644 index 000000000..c17122c18 --- /dev/null +++ b/spec/fixtures/files/sales_logs_csv_export_labels.csv @@ -0,0 +1,2 @@ +id,status,created_at,updated_at,old_id,collection_start_year,creation_method,is_dpo,owning_organisation_name,created_by_name,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,beds,proptype,builtype,pcodenk,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,wchair,noint,privacynotice,age1,sex1,ethnic_group,ethnic,national,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant +,completed,2023-02-08T16:52:15+00:00,2023-02-08T16:52:15+00:00,,2022,single log,false,DLUHC,Danny Rojas,2,2,2023,,Yes - a discounted ownership scheme,Right to Acquire (RTA),,,,Yes,Yes,2,Flat or maisonette,Purpose built,Yes,,,,,,,SW1A,1AA,Yes,E09000003,Barnet,Yes,Yes,1,30,Non-binary,Buyer 1 prefers not to say,17,United Kingdom,Full-time - 30 hours or more,Yes,Partner,35,Non-binary,,,,Full-time - 30 hours or more,Yes,1,Child,14,Non-binary,Child under 16,Other,18,Non-binary,"In government training into work, such as New Deal",Person prefers not to say,40,Non-binary,Part-time - Less than 30 hours,Person prefers not to say,40,Non-binary,Full-time - 30 hours or more,Local authority tenant,No,,,No,,,1,1,1,1,,,,Yes,Yes,No,Yes,Yes,Yes,10000,Yes,Yes,10000,Yes,Don’t know ,No,,Yes,2,10,,,,,,,,,,,,,,,,,110000.0,,Yes,20000.0,Cambridge Building Society,,10,Yes,80000.0,1000.0,,Yes,100.0,,10000.0 diff --git a/spec/fixtures/forms/2021_2022.json b/spec/fixtures/forms/2021_2022.json index 139ac8399..5a400d095 100644 --- a/spec/fixtures/forms/2021_2022.json +++ b/spec/fixtures/forms/2021_2022.json @@ -226,7 +226,8 @@ "operand": 50 } } - ] + ], + "interruption_screen_question_ids": ["age1", "ecstat1"] }, "person_2_working_situation": { "header": "", @@ -740,27 +741,43 @@ } ], "title_text": { - "translation": "soft_validations.net_income.title_text" - }, - "informative_text": { - "translation": "soft_validations.net_income.hint_text", + "translation": "soft_validations.net_income.title_text", "arguments": [ { - "key": "ecstat1", + "key": "incfreq", "label": true, - "i18n_template": "ecstat1" + "i18n_template": "incfreq" }, { - "key": "earnings", - "label": true, + "key": "field_formatted_as_currency", + "arguments_for_key": "earnings", "i18n_template": "earnings" } ] }, + "informative_text": { + "translation": "soft_validations.net_income.hint_text", + "arguments": [ + { + "key": "net_income_higher_or_lower_text", + "label": false, + "i18n_template": "net_income_higher_or_lower_text" + } + ] + }, "questions": { "net_income_value_check": { "check_answer_label": "Net income soft validation", - "hidden_in_check_answers": true, + "hidden_in_check_answers": { + "depends_on": [ + { + "net_income_value_check": 0 + }, + { + "net_income_value_check": 1 + } + ] + }, "header": "Are you sure this is correct?", "type": "interruption_screen", "answer_options": { @@ -772,7 +789,8 @@ } } } - } + }, + "interruption_screen_question_ids": ["ecstat1", "incfreq", "earnings"] }, "net_income_uc_proportion": { "questions": { diff --git a/spec/helpers/form_page_helper_spec.rb b/spec/helpers/form_page_helper_spec.rb new file mode 100644 index 000000000..aeded6f8d --- /dev/null +++ b/spec/helpers/form_page_helper_spec.rb @@ -0,0 +1,28 @@ +require "rails_helper" + +RSpec.describe FormPageHelper do + describe "#action_href" do + let(:lettings_log) { FactoryBot.create(:lettings_log) } + let(:sales_log) { FactoryBot.create(:sales_log) } + + context "with a lettings log" do + it "has an update answer link href helper" do + expect(action_href(lettings_log, "net_income")).to eq("/lettings-logs/#{lettings_log.id}/net-income?referrer=check_answers") + end + + it "returns a correct referrer in the url" do + expect(action_href(lettings_log, "retirement_value_check", "interruption_screen")).to eq("/lettings-logs/#{lettings_log.id}/retirement-value-check?referrer=interruption_screen") + end + end + + context "with a sales log" do + it "has an update answer link href helper" do + expect(action_href(sales_log, "buyer_1_age")).to eq("/sales-logs/#{sales_log.id}/buyer-1-age?referrer=check_answers") + end + + it "returns a correct referrer in the url" do + expect(action_href(sales_log, "grant_value_check", "interruption_screen")).to eq("/sales-logs/#{sales_log.id}/grant-value-check?referrer=interruption_screen") + end + end + end +end diff --git a/spec/helpers/interruption_screen_helper_spec.rb b/spec/helpers/interruption_screen_helper_spec.rb index 33553f39f..6d9122f27 100644 --- a/spec/helpers/interruption_screen_helper_spec.rb +++ b/spec/helpers/interruption_screen_helper_spec.rb @@ -34,14 +34,9 @@ RSpec.describe InterruptionScreenHelper do "translation" => "soft_validations.net_income.hint_text", "arguments" => [ { - "key" => "ecstat1", - "label" => true, - "i18n_template" => "ecstat1", - }, - { - "key" => "earnings", - "label" => true, - "i18n_template" => "earnings", + "key" => "net_income_higher_or_lower_text", + "label" => false, + "i18n_template" => "net_income_higher_or_lower_text", }, ], } @@ -49,8 +44,7 @@ RSpec.describe InterruptionScreenHelper do .to eq( I18n.t( "soft_validations.net_income.hint_text", - ecstat1: lettings_log.form.get_question("ecstat1", lettings_log).answer_label(lettings_log).downcase, - earnings: lettings_log.form.get_question("earnings", lettings_log).answer_label(lettings_log), + net_income_higher_or_lower_text: "higher", ), ) end @@ -241,4 +235,13 @@ RSpec.describe InterruptionScreenHelper do end end end + + describe "soft_validation_affected_questions" do + let(:question) { lettings_log.form.get_question("retirement_value_check", lettings_log) } + + it "returns a list of questions affected by the soft validation" do + expect(soft_validation_affected_questions(question, lettings_log).count).to eq(2) + expect(soft_validation_affected_questions(question, lettings_log).map(&:id)).to eq(%w[age1 ecstat1]) + end + end end diff --git a/spec/jobs/email_csv_job_spec.rb b/spec/jobs/email_csv_job_spec.rb index 29dceff59..40f13e022 100644 --- a/spec/jobs/email_csv_job_spec.rb +++ b/spec/jobs/email_csv_job_spec.rb @@ -26,6 +26,7 @@ describe EmailCsvJob do let(:storage_service) { instance_double(Storage::S3Service) } let(:mailer) { instance_double(CsvDownloadMailer) } + let(:sales_log_csv_service) { instance_double(Csv::SalesLogCsvService) } before do FactoryBot.create(:lettings_log, @@ -39,18 +40,44 @@ describe EmailCsvJob do allow(storage_service).to receive(:write_file) allow(storage_service).to receive(:get_presigned_url).and_return(test_url) + allow(Csv::SalesLogCsvService).to receive(:new).and_return(sales_log_csv_service) + allow(sales_log_csv_service).to receive(:prepare_csv).and_return("") + allow(CsvDownloadMailer).to receive(:new).and_return(mailer) allow(mailer).to receive(:send_csv_download_mail) end - it "uses an appropriate filename in S3" do - expect(storage_service).to receive(:write_file).with(/logs-.*\.csv/, anything) - job.perform(user) + context "when exporting lettings logs" do + it "uses an appropriate filename in S3" do + expect(storage_service).to receive(:write_file).with(/lettings-logs-.*\.csv/, anything) + job.perform(user) + end + + it "includes the organisation name in the filename when one is provided" do + expect(storage_service).to receive(:write_file).with(/lettings-logs-#{organisation.name}-.*\.csv/, anything) + job.perform(user, nil, {}, nil, organisation) + end end - it "includes the organisation name in the filename when one is provided" do - expect(storage_service).to receive(:write_file).with(/logs-#{organisation.name}-.*\.csv/, anything) - job.perform(user, nil, {}, nil, organisation) + context "when exporting sales logs" do + it "uses an appropriate filename in S3" do + expect(storage_service).to receive(:write_file).with(/sales-logs-.*\.csv/, anything) + job.perform(user, nil, {}, nil, nil, nil, "sales") + end + + it "includes the organisation name in the filename when one is provided" do + expect(storage_service).to receive(:write_file).with(/sales-logs-#{organisation.name}-.*\.csv/, anything) + job.perform(user, nil, {}, nil, organisation, nil, "sales") + end + + it "creates a SalesLogCsvService with the correct export type" do + expect(Csv::SalesLogCsvService).to receive(:new).with(export_type: "labels") + codes_only = false + job.perform(user, nil, {}, nil, nil, codes_only, "sales") + expect(Csv::SalesLogCsvService).to receive(:new).with(export_type: "codes") + codes_only = true + job.perform(user, nil, {}, nil, nil, codes_only, "sales") + end end it "sends an E-mail with the presigned URL and duration" do diff --git a/spec/models/form/lettings/pages/care_home_charges_value_check_spec.rb b/spec/models/form/lettings/pages/care_home_charges_value_check_spec.rb index c81538146..6d2b8f7a8 100644 --- a/spec/models/form/lettings/pages/care_home_charges_value_check_spec.rb +++ b/spec/models/form/lettings/pages/care_home_charges_value_check_spec.rb @@ -41,4 +41,8 @@ RSpec.describe Form::Lettings::Pages::CareHomeChargesValueCheck, type: :model do it "has the correct informative_text" do expect(page.informative_text).to eq("") end + + it "has the correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[chcharge is_carehome]) + end end diff --git a/spec/models/form/lettings/pages/max_rent_value_check_spec.rb b/spec/models/form/lettings/pages/max_rent_value_check_spec.rb index 1d712d886..5d803252b 100644 --- a/spec/models/form/lettings/pages/max_rent_value_check_spec.rb +++ b/spec/models/form/lettings/pages/max_rent_value_check_spec.rb @@ -34,4 +34,8 @@ RSpec.describe Form::Lettings::Pages::MaxRentValueCheck, type: :model do it "has the correct informative_text" do expect(page.informative_text).to eq({ "arguments" => [{ "arguments_for_key" => "soft_max_for_period", "i18n_template" => "soft_max_for_period", "key" => "field_formatted_as_currency" }], "translation" => "soft_validations.rent.max_hint_text" }) end + + it "has the correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[brent startdate la beds rent_type needstype]) + end end diff --git a/spec/models/form/lettings/pages/min_rent_value_check_spec.rb b/spec/models/form/lettings/pages/min_rent_value_check_spec.rb index 818599b06..e4b6b5478 100644 --- a/spec/models/form/lettings/pages/min_rent_value_check_spec.rb +++ b/spec/models/form/lettings/pages/min_rent_value_check_spec.rb @@ -52,4 +52,8 @@ RSpec.describe Form::Lettings::Pages::MinRentValueCheck, type: :model do ], }) end + + it "has the correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[brent startdate la beds rent_type needstype]) + end end diff --git a/spec/models/form/lettings/pages/net_income_value_check_spec.rb b/spec/models/form/lettings/pages/net_income_value_check_spec.rb index c43eb3381..f07ec84ab 100644 --- a/spec/models/form/lettings/pages/net_income_value_check_spec.rb +++ b/spec/models/form/lettings/pages/net_income_value_check_spec.rb @@ -28,12 +28,12 @@ RSpec.describe Form::Lettings::Pages::NetIncomeValueCheck, type: :model do end it "has the correct title_text" do - expect(page.title_text).to eq({ "translation" => "soft_validations.net_income.title_text" }) + expect(page.title_text).to eq({ "translation" => "soft_validations.net_income.title_text", "arguments" => [{ "i18n_template" => "incfreq", "key" => "incfreq", "label" => true }, { "arguments_for_key" => "earnings", "i18n_template" => "earnings", "key" => "field_formatted_as_currency" }] }) end it "has the correct informative_text" do expect(page.informative_text).to eq({ - "arguments" => [{ "arguments_for_key" => "ecstat1", "i18n_template" => "ecstat1", "key" => "field_formatted_as_currency" }, { "arguments_for_key" => "earnings", "i18n_template" => "earnings", "key" => "field_formatted_as_currency" }], + "arguments" => [{ "i18n_template" => "net_income_higher_or_lower_text", "key" => "net_income_higher_or_lower_text", "label" => false }], "translation" => "soft_validations.net_income.hint_text", }) end diff --git a/spec/models/form/lettings/pages/person_under_retirement_value_check_spec.rb b/spec/models/form/lettings/pages/person_under_retirement_value_check_spec.rb index a2a6dd20f..929009ce7 100644 --- a/spec/models/form/lettings/pages/person_under_retirement_value_check_spec.rb +++ b/spec/models/form/lettings/pages/person_under_retirement_value_check_spec.rb @@ -39,8 +39,8 @@ RSpec.describe Form::Lettings::Pages::PersonUnderRetirementValueCheck, type: :mo "translation" => "soft_validations.retirement.min.title", "arguments" => [ { - "key" => "retirement_age_for_person_2", - "label" => false, + "key" => "age2", + "label" => true, "i18n_template" => "age", }, ], @@ -48,21 +48,7 @@ RSpec.describe Form::Lettings::Pages::PersonUnderRetirementValueCheck, type: :mo end it "has the correct informative_text" do - expect(page.informative_text).to eq({ - "translation" => "soft_validations.retirement.min.hint_text", - "arguments" => [ - { - "key" => "plural_gender_for_person_2", - "label" => false, - "i18n_template" => "gender", - }, - { - "key" => "retirement_age_for_person_2", - "label" => false, - "i18n_template" => "age", - }, - ], - }) + expect(page.informative_text).to eq({}) end end @@ -84,8 +70,8 @@ RSpec.describe Form::Lettings::Pages::PersonUnderRetirementValueCheck, type: :mo "translation" => "soft_validations.retirement.min.title", "arguments" => [ { - "key" => "retirement_age_for_person_3", - "label" => false, + "key" => "age3", + "label" => true, "i18n_template" => "age", }, ], @@ -93,21 +79,7 @@ RSpec.describe Form::Lettings::Pages::PersonUnderRetirementValueCheck, type: :mo end it "has the correct informative_text" do - expect(page.informative_text).to eq({ - "translation" => "soft_validations.retirement.min.hint_text", - "arguments" => [ - { - "key" => "plural_gender_for_person_3", - "label" => false, - "i18n_template" => "gender", - }, - { - "key" => "retirement_age_for_person_3", - "label" => false, - "i18n_template" => "age", - }, - ], - }) + expect(page.informative_text).to eq({}) end end end diff --git a/spec/models/form/question_spec.rb b/spec/models/form/question_spec.rb index ede6636f7..751523d37 100644 --- a/spec/models/form/question_spec.rb +++ b/spec/models/form/question_spec.rb @@ -257,11 +257,6 @@ RSpec.describe Form::Question, type: :model do expect(question.action_text(lettings_log)).to match(/Change/) end - it "has an update answer link href helper" do - lettings_log.id = 1 - expect(question.action_href(lettings_log, page.id)).to eq("/lettings-logs/1/net-income?referrer=check_answers") - end - context "when the question has an inferred answer" do let(:section_id) { "tenancy_and_property" } let(:subsection_id) { "property_information" } diff --git a/spec/models/form/sales/pages/about_price_value_check_spec.rb b/spec/models/form/sales/pages/about_price_value_check_spec.rb index 6c8d27463..881969703 100644 --- a/spec/models/form/sales/pages/about_price_value_check_spec.rb +++ b/spec/models/form/sales/pages/about_price_value_check_spec.rb @@ -45,10 +45,14 @@ RSpec.describe Form::Sales::Pages::AboutPriceValueCheck, type: :model do "i18n_template" => "soft_min_or_soft_max", }, { - "key" => "purchase_price_min_or_max_text", - "i18n_template" => "min_or_max", + "key" => "purchase_price_higher_or_lower_text", + "i18n_template" => "higher_or_lower", }, ], }) end + + it "has the correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[value beds la]) + end end diff --git a/spec/models/form/sales/pages/buyer1_income_value_check_spec.rb b/spec/models/form/sales/pages/buyer1_income_value_check_spec.rb index f770bd651..8aa6ef233 100644 --- a/spec/models/form/sales/pages/buyer1_income_value_check_spec.rb +++ b/spec/models/form/sales/pages/buyer1_income_value_check_spec.rb @@ -34,4 +34,8 @@ RSpec.describe Form::Sales::Pages::Buyer1IncomeValueCheck, type: :model do it "is interruption screen page" do expect(page.interruption_screen?).to eq(true) end + + it "is has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[ecstat1 income1]) + end end diff --git a/spec/models/form/sales/pages/buyer_live_in_value_check_spec.rb b/spec/models/form/sales/pages/buyer_live_in_value_check_spec.rb index 9d00b086b..22d1433bc 100644 --- a/spec/models/form/sales/pages/buyer_live_in_value_check_spec.rb +++ b/spec/models/form/sales/pages/buyer_live_in_value_check_spec.rb @@ -43,6 +43,10 @@ RSpec.describe Form::Sales::Pages::BuyerLiveInValueCheck, type: :model do }) end + it "has the correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[ownershipsch buy1livein]) + end + context "with buyer 2" do let(:person_index) { 2 } @@ -60,5 +64,9 @@ RSpec.describe Form::Sales::Pages::BuyerLiveInValueCheck, type: :model do "arguments" => [{ "key" => "ownership_scheme", "label" => false, "i18n_template" => "ownership_scheme" }], }) end + + it "has the correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[ownershipsch buy2livein]) + end end end diff --git a/spec/models/form/sales/pages/deposit_value_check_spec.rb b/spec/models/form/sales/pages/deposit_value_check_spec.rb index 91e1cccc5..9f8c99966 100644 --- a/spec/models/form/sales/pages/deposit_value_check_spec.rb +++ b/spec/models/form/sales/pages/deposit_value_check_spec.rb @@ -34,4 +34,8 @@ RSpec.describe Form::Sales::Pages::DepositValueCheck, type: :model do it "is interruption screen page" do expect(page.interruption_screen?).to eq(true) end + + it "is has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[savings deposit]) + end end diff --git a/spec/models/form/sales/pages/discounted_sale_value_check_spec.rb b/spec/models/form/sales/pages/discounted_sale_value_check_spec.rb index d086542dc..ce0d7be8e 100644 --- a/spec/models/form/sales/pages/discounted_sale_value_check_spec.rb +++ b/spec/models/form/sales/pages/discounted_sale_value_check_spec.rb @@ -49,4 +49,8 @@ RSpec.describe Form::Sales::Pages::DiscountedSaleValueCheck, type: :model do }, ]) end + + it "has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[value deposit ownershipsch mortgage mortgageused discount grant type]) + end end diff --git a/spec/models/form/sales/pages/handover_date_check_spec.rb b/spec/models/form/sales/pages/handover_date_check_spec.rb index 6bd3f3874..cd358a2b8 100644 --- a/spec/models/form/sales/pages/handover_date_check_spec.rb +++ b/spec/models/form/sales/pages/handover_date_check_spec.rb @@ -44,4 +44,8 @@ RSpec.describe Form::Sales::Pages::HandoverDateCheck, type: :model do it "is interruption screen page" do expect(page.interruption_screen?).to eq(true) end + + it "is has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[hodate saledate]) + end end diff --git a/spec/models/form/sales/pages/household_wheelchair_check_spec.rb b/spec/models/form/sales/pages/household_wheelchair_check_spec.rb index 4eb6182d9..20d0a325f 100644 --- a/spec/models/form/sales/pages/household_wheelchair_check_spec.rb +++ b/spec/models/form/sales/pages/household_wheelchair_check_spec.rb @@ -34,4 +34,8 @@ RSpec.describe Form::Sales::Pages::HouseholdWheelchairCheck, type: :model do it "is interruption screen page" do expect(page.interruption_screen?).to eq(true) end + + it "is has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[disabled wheel]) + end end diff --git a/spec/models/form/sales/pages/monthly_charges_value_check_spec.rb b/spec/models/form/sales/pages/monthly_charges_value_check_spec.rb index ac378dc82..5add5d8e7 100644 --- a/spec/models/form/sales/pages/monthly_charges_value_check_spec.rb +++ b/spec/models/form/sales/pages/monthly_charges_value_check_spec.rb @@ -38,11 +38,15 @@ RSpec.describe Form::Sales::Pages::MonthlyChargesValueCheck, type: :model do it "has correct title_text" do expect(page.title_text).to eq({ "translation" => "soft_validations.monthly_charges_over_soft_max.title_text", - "arguments" => [], + "arguments" => [{ "arguments_for_key" => "mscharge", "i18n_template" => "mscharge", "key" => "field_formatted_as_currency" }], }) end it "has correct informative_text" do - expect(page.informative_text).to eq({}) + expect(page.informative_text).to eq({ "arguments" => [], "translation" => "soft_validations.monthly_charges_over_soft_max.hint_text" }) + end + + it "has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[type mscharge proptype]) end end diff --git a/spec/models/form/sales/pages/mortgage_value_check_spec.rb b/spec/models/form/sales/pages/mortgage_value_check_spec.rb index 8c7375f81..f33525652 100644 --- a/spec/models/form/sales/pages/mortgage_value_check_spec.rb +++ b/spec/models/form/sales/pages/mortgage_value_check_spec.rb @@ -36,6 +36,10 @@ RSpec.describe Form::Sales::Pages::MortgageValueCheck, type: :model do ]) end + it "has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[mortgage inc1mort inc2mort jointpur income1 income2 inc1mort inc2mort]) + end + context "when checking buyer 2" do let(:index) { 2 } diff --git a/spec/models/form/sales/pages/old_persons_shared_ownership_value_check_spec.rb b/spec/models/form/sales/pages/old_persons_shared_ownership_value_check_spec.rb index 05b6f7734..d9c4beed9 100644 --- a/spec/models/form/sales/pages/old_persons_shared_ownership_value_check_spec.rb +++ b/spec/models/form/sales/pages/old_persons_shared_ownership_value_check_spec.rb @@ -33,12 +33,16 @@ RSpec.describe Form::Sales::Pages::OldPersonsSharedOwnershipValueCheck, type: :m it "has the correct title_text" do expect(page.title_text).to eq({ - "translation" => "soft_validations.old_persons_shared_ownership", + "translation" => "soft_validations.old_persons_shared_ownership.title_text", "arguments" => [], }) end it "has the correct informative_text" do - expect(page.informative_text).to eq({}) + expect(page.informative_text).to eq({ "arguments" => [], "translation" => "soft_validations.old_persons_shared_ownership.hint_text" }) + end + + it "has the correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[type jointpur age1 age2]) end end diff --git a/spec/models/form/sales/pages/percentage_discount_value_check_spec.rb b/spec/models/form/sales/pages/percentage_discount_value_check_spec.rb index d933e3278..6b1fc36a4 100644 --- a/spec/models/form/sales/pages/percentage_discount_value_check_spec.rb +++ b/spec/models/form/sales/pages/percentage_discount_value_check_spec.rb @@ -31,7 +31,10 @@ RSpec.describe Form::Sales::Pages::PercentageDiscountValueCheck, type: :model do end it "has the correct informative_text" do - expect(page.informative_text).to eq({}) + expect(page.informative_text).to eq({ + "translation" => "soft_validations.percentage_discount_value.hint_text", + "arguments" => [], + }) end it "is interruption screen page" do @@ -45,4 +48,8 @@ RSpec.describe Form::Sales::Pages::PercentageDiscountValueCheck, type: :model do }, ]) end + + it "has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[discount proptype]) + end end diff --git a/spec/models/form/sales/pages/person_student_not_child_value_check_spec.rb b/spec/models/form/sales/pages/person_student_not_child_value_check_spec.rb index 3499cda9f..c0d1b2734 100644 --- a/spec/models/form/sales/pages/person_student_not_child_value_check_spec.rb +++ b/spec/models/form/sales/pages/person_student_not_child_value_check_spec.rb @@ -39,6 +39,10 @@ RSpec.describe Form::Sales::Pages::PersonStudentNotChildValueCheck, type: :model expect(page.questions.map(&:id)).to eq(%w[student_not_child_value_check]) end + it "has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[relat2 ecstat2 age2]) + end + context "with person 2" do let(:person_index) { 2 } let(:page_id) { "person_2_student_not_child_value_check" } @@ -50,6 +54,10 @@ RSpec.describe Form::Sales::Pages::PersonStudentNotChildValueCheck, type: :model it "has correct depends_on" do expect(page.depends_on).to eq([{ "person_2_student_not_child?" => true }]) end + + it "has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[relat2 ecstat2 age2]) + end end context "with person 3" do @@ -63,6 +71,10 @@ RSpec.describe Form::Sales::Pages::PersonStudentNotChildValueCheck, type: :model it "has correct depends_on" do expect(page.depends_on).to eq([{ "person_3_student_not_child?" => true }]) end + + it "has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[relat3 ecstat3 age3]) + end end context "with person 4" do @@ -76,6 +88,10 @@ RSpec.describe Form::Sales::Pages::PersonStudentNotChildValueCheck, type: :model it "has correct depends_on" do expect(page.depends_on).to eq([{ "person_4_student_not_child?" => true }]) end + + it "has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[relat4 ecstat4 age4]) + end end context "with person 5" do @@ -89,6 +105,10 @@ RSpec.describe Form::Sales::Pages::PersonStudentNotChildValueCheck, type: :model it "has correct depends_on" do expect(page.depends_on).to eq([{ "person_5_student_not_child?" => true }]) end + + it "has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[relat5 ecstat5 age5]) + end end context "with person 6" do @@ -102,5 +122,9 @@ RSpec.describe Form::Sales::Pages::PersonStudentNotChildValueCheck, type: :model it "has correct depends_on" do expect(page.depends_on).to eq([{ "person_6_student_not_child?" => true }]) end + + it "has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[relat6 ecstat6 age6]) + end end end diff --git a/spec/models/form/sales/pages/retirement_value_check_spec.rb b/spec/models/form/sales/pages/retirement_value_check_spec.rb index 76599d982..b09760ff5 100644 --- a/spec/models/form/sales/pages/retirement_value_check_spec.rb +++ b/spec/models/form/sales/pages/retirement_value_check_spec.rb @@ -46,8 +46,8 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do "translation" => "soft_validations.retirement.min.title", "arguments" => [ { - "key" => "retirement_age_for_person_1", - "label" => false, + "key" => "age1", + "label" => true, "i18n_template" => "age", }, ], @@ -55,21 +55,11 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do end it "has correct informative_text" do - expect(page.informative_text).to eq({ - "translation" => "soft_validations.retirement.min.hint_text", - "arguments" => [ - { - "key" => "plural_gender_for_person_1", - "label" => false, - "i18n_template" => "gender", - }, - { - "key" => "retirement_age_for_person_1", - "label" => false, - "i18n_template" => "age", - }, - ], - }) + expect(page.informative_text).to eq({}) + end + + it "has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[age1 ecstat1]) end end @@ -94,8 +84,8 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do "translation" => "soft_validations.retirement.min.title", "arguments" => [ { - "key" => "retirement_age_for_person_2", - "label" => false, + "key" => "age2", + "label" => true, "i18n_template" => "age", }, ], @@ -103,21 +93,11 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do end it "has correct informative_text" do - expect(page.informative_text).to eq({ - "translation" => "soft_validations.retirement.min.hint_text", - "arguments" => [ - { - "key" => "plural_gender_for_person_2", - "label" => false, - "i18n_template" => "gender", - }, - { - "key" => "retirement_age_for_person_2", - "label" => false, - "i18n_template" => "age", - }, - ], - }) + expect(page.informative_text).to eq({}) + end + + it "has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[age2 ecstat2]) end end @@ -142,8 +122,8 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do "translation" => "soft_validations.retirement.min.title", "arguments" => [ { - "key" => "retirement_age_for_person_3", - "label" => false, + "key" => "age3", + "label" => true, "i18n_template" => "age", }, ], @@ -151,21 +131,11 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do end it "has correct informative_text" do - expect(page.informative_text).to eq({ - "translation" => "soft_validations.retirement.min.hint_text", - "arguments" => [ - { - "key" => "plural_gender_for_person_3", - "label" => false, - "i18n_template" => "gender", - }, - { - "key" => "retirement_age_for_person_3", - "label" => false, - "i18n_template" => "age", - }, - ], - }) + expect(page.informative_text).to eq({}) + end + + it "has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[age3 ecstat3]) end end @@ -190,8 +160,8 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do "translation" => "soft_validations.retirement.min.title", "arguments" => [ { - "key" => "retirement_age_for_person_4", - "label" => false, + "key" => "age4", + "label" => true, "i18n_template" => "age", }, ], @@ -199,21 +169,11 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do end it "has correct informative_text" do - expect(page.informative_text).to eq({ - "translation" => "soft_validations.retirement.min.hint_text", - "arguments" => [ - { - "key" => "plural_gender_for_person_4", - "label" => false, - "i18n_template" => "gender", - }, - { - "key" => "retirement_age_for_person_4", - "label" => false, - "i18n_template" => "age", - }, - ], - }) + expect(page.informative_text).to eq({}) + end + + it "has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[age4 ecstat4]) end end @@ -238,8 +198,8 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do "translation" => "soft_validations.retirement.min.title", "arguments" => [ { - "key" => "retirement_age_for_person_5", - "label" => false, + "key" => "age5", + "label" => true, "i18n_template" => "age", }, ], @@ -247,21 +207,11 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do end it "has correct informative_text" do - expect(page.informative_text).to eq({ - "translation" => "soft_validations.retirement.min.hint_text", - "arguments" => [ - { - "key" => "plural_gender_for_person_5", - "label" => false, - "i18n_template" => "gender", - }, - { - "key" => "retirement_age_for_person_5", - "label" => false, - "i18n_template" => "age", - }, - ], - }) + expect(page.informative_text).to eq({}) + end + + it "has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[age5 ecstat5]) end end @@ -286,8 +236,8 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do "translation" => "soft_validations.retirement.min.title", "arguments" => [ { - "key" => "retirement_age_for_person_6", - "label" => false, + "key" => "age6", + "label" => true, "i18n_template" => "age", }, ], @@ -295,21 +245,11 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do end it "has correct informative_text" do - expect(page.informative_text).to eq({ - "translation" => "soft_validations.retirement.min.hint_text", - "arguments" => [ - { - "key" => "plural_gender_for_person_6", - "label" => false, - "i18n_template" => "gender", - }, - { - "key" => "retirement_age_for_person_6", - "label" => false, - "i18n_template" => "age", - }, - ], - }) + expect(page.informative_text).to eq({}) + end + + it "has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[age6 ecstat6]) end end end diff --git a/spec/models/form/sales/pages/savings_value_check_spec.rb b/spec/models/form/sales/pages/savings_value_check_spec.rb index 80606afb5..cfff9c74d 100644 --- a/spec/models/form/sales/pages/savings_value_check_spec.rb +++ b/spec/models/form/sales/pages/savings_value_check_spec.rb @@ -34,4 +34,8 @@ RSpec.describe Form::Sales::Pages::SavingsValueCheck, type: :model do it "is interruption screen page" do expect(page.interruption_screen?).to eq(true) end + + it "has the correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[savings]) + end end diff --git a/spec/models/form/sales/pages/shared_ownership_deposit_value_check_spec.rb b/spec/models/form/sales/pages/shared_ownership_deposit_value_check_spec.rb index 45744cde4..9a184a6fc 100644 --- a/spec/models/form/sales/pages/shared_ownership_deposit_value_check_spec.rb +++ b/spec/models/form/sales/pages/shared_ownership_deposit_value_check_spec.rb @@ -34,11 +34,15 @@ RSpec.describe Form::Sales::Pages::SharedOwnershipDepositValueCheck, type: :mode it "has the correct title_text" do expect(page.title_text).to eq({ "translation" => "soft_validations.shared_ownership_deposit.title_text", - "arguments" => [{ "arguments_for_key" => "expected_shared_ownership_deposit_value", "i18n_template" => "expected_shared_ownership_deposit_value", "key" => "field_formatted_as_currency" }], + "arguments" => [{ "arguments_for_key" => "mortgage_deposit_and_discount_total", "i18n_template" => "mortgage_deposit_and_discount_total", "key" => "field_formatted_as_currency" }], }) end it "has the correct informative_text" do expect(page.informative_text).to eq({}) end + + it "has the correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[mortgage mortgageused cashdis type deposit value equity]) + end end diff --git a/spec/models/form/sales/questions/household_wheelchair_check_spec.rb b/spec/models/form/sales/questions/household_wheelchair_check_spec.rb index 92f80fdb1..d203ee444 100644 --- a/spec/models/form/sales/questions/household_wheelchair_check_spec.rb +++ b/spec/models/form/sales/questions/household_wheelchair_check_spec.rb @@ -16,7 +16,7 @@ RSpec.describe Form::Sales::Questions::HouseholdWheelchairCheck, type: :model do end it "has the correct header" do - expect(question.header).to eq("Are you sure? You said previously that somebody in household uses a wheelchair") + expect(question.header).to eq("You told us that someone in the household uses a wheelchair.") end it "has the correct check_answer_label" do diff --git a/spec/models/form_handler_spec.rb b/spec/models/form_handler_spec.rb index bbeb56e55..547ed2f21 100644 --- a/spec/models/form_handler_spec.rb +++ b/spec/models/form_handler_spec.rb @@ -219,5 +219,56 @@ RSpec.describe FormHandler do end end end + + describe "#ordered_sales_questions_for_all_years" do + let(:result) { described_class.instance.ordered_sales_questions_for_all_years } + let(:now) { Time.zone.now } + + it "returns an array of questions" do + section = build(:section, :with_questions, question_ids: %w[1 2 3]) + sales_form = FormFactory.new(year: 1936, type: "sales") + .with_sections([section]) + .build + described_class.instance.use_fake_forms!({ only_sales: sales_form }) + expect(result).to(satisfy { |result| result.all? { |element| element.is_a?(Form::Question) } }) + end + + it "does not return multiple questions with the same id" do + first_section = build(:section, :with_questions, question_ids: %w[1 2 3]) + second_section = build(:section, :with_questions, question_ids: %w[2 3 4 5]) + sales_form = FormFactory.new(year: 1936, type: "sales") + .with_sections([first_section, second_section]) + .build + described_class.instance.use_fake_forms!({ only_sales: sales_form }) + expect(result.map(&:id)).to eq %w[1 2 3 4 5] + end + + it "returns the questions in the same order as the form" do + first_section = build(:section, :with_questions, question_ids: %w[1 2 3]) + second_section = build(:section, :with_questions, question_ids: %w[4 5 6]) + sales_form = FormFactory.new(year: 1945, type: "sales") + .with_sections([first_section, second_section]) + .build + described_class.instance.use_fake_forms!({ only_sales: sales_form }) + expect(result.map(&:id)).to eq %w[1 2 3 4 5 6] + end + + it "inserts questions from all years in their correct positions" do + original_section = build(:section, :with_questions, question_ids: %w[1 1a_deprecated 2 3]) + new_section = build(:section, :with_questions, question_ids: %w[1 2 2a_new 3]) + original_form = FormFactory.new(year: 1066, type: "sales") + .with_sections([original_section]) + .build + new_form = FormFactory.new(year: 1485, type: "sales") + .with_sections([new_section]) + .build + fake_forms = { + earlier_sales: original_form, + newer_sales: new_form, + } + described_class.instance.use_fake_forms!(fake_forms) + expect(result.map(&:id)).to eq %w[1 1a_deprecated 2 2a_new 3] + end + end # rubocop:enable RSpec/PredicateMatcher end diff --git a/spec/models/lettings_log_spec.rb b/spec/models/lettings_log_spec.rb index 05da0a5a8..5e661fe16 100644 --- a/spec/models/lettings_log_spec.rb +++ b/spec/models/lettings_log_spec.rb @@ -268,13 +268,6 @@ RSpec.describe LettingsLog do expect(record_from_db["mrcdate"].year).to eq(2021) end - it "correctly derives and saves partial and full major property void date" do - record_from_db = ActiveRecord::Base.connection.execute("select voiddate from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(record_from_db["voiddate"].day).to eq(3) - expect(record_from_db["voiddate"].month).to eq(3) - expect(record_from_db["voiddate"].year).to eq(2021) - end - it "correctly derives and saves incref" do record_from_db = ActiveRecord::Base.connection.execute("select incref from lettings_logs where id=#{lettings_log.id}").to_a[0] expect(record_from_db["incref"]).to eq(1) @@ -1504,334 +1497,329 @@ RSpec.describe LettingsLog do expect(record_from_db["has_benefits"]).to eq(1) end - context "when it is a renewal" do + context "when updating values that derive vacdays" do + let(:lettings_log) { create(:lettings_log, startdate:) } + + context "when start date is set" do + let(:startdate) { Time.zone.now } + + it "correctly derives vacdays when voiddate is set" do + day_count = 3 + expect { lettings_log.update!(voiddate: startdate - day_count.days) }.to change(lettings_log, :vacdays).to day_count + expect { lettings_log.update!(voiddate: nil) }.to change(lettings_log, :vacdays).from(day_count).to nil + end + + it "correctly derives vacdays when mrcdate is set" do + day_count = 3 + expect { lettings_log.update!(mrcdate: startdate - day_count.days) }.to change(lettings_log, :vacdays).to day_count + expect { lettings_log.update!(mrcdate: nil) }.to change(lettings_log, :vacdays).from(day_count).to nil + end + end + + context "when start date is not set" do + let(:startdate) { nil } + + it "correctly derives vacdays when voiddate is set" do + day_count = 3 + lettings_log.update!(voiddate: Time.zone.now - day_count.days) + expect(lettings_log.vacdays).to be nil + end + + it "correctly derives vacdays when mrcdate is set" do + day_count = 3 + lettings_log.update!(mrcdate: Time.zone.now - day_count.days) + expect(lettings_log.vacdays).to be nil + end + end + end + + context "when updating renewal" do let!(:lettings_log) do described_class.create({ managing_organisation: owning_organisation, owning_organisation:, created_by: created_by_user, - renewal: 1, startdate: Time.zone.local(2021, 4, 10), created_at: Time.utc(2022, 2, 8, 16, 52, 15), }) end - it "correctly derives and saves waityear" do - record_from_db = ActiveRecord::Base.connection.execute("select waityear from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(record_from_db["waityear"]).to eq(2) - expect(lettings_log["waityear"]).to eq(2) + it "correctly derives the length of time on local authority waiting list" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :waityear).to 2 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :waityear).from(2).to nil end - it "correctly derives and saves underoccupation_benefitcap if year is 2021" do - record_from_db = ActiveRecord::Base.connection.execute("select underoccupation_benefitcap from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(record_from_db["underoccupation_benefitcap"]).to eq(2) - expect(lettings_log["underoccupation_benefitcap"]).to eq(2) + it "correctly derives the number of times previously offered since becoming available" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :offered).to 0 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :offered).from(0).to nil end - it "correctly derives and saves prevten" do - lettings_log.managing_organisation.update!({ provider_type: "PRP" }) - lettings_log.update!({ needstype: 1 }) - - record_from_db = ActiveRecord::Base.connection.execute("select prevten from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(record_from_db["prevten"]).to eq(32) - expect(lettings_log["prevten"]).to eq(32) + it "correctly derives referral if the letting is a renewal and clears it if it is not" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :referral).to 1 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :referral).from(1).to nil + end - lettings_log.managing_organisation.update!({ provider_type: "LA" }) - lettings_log.update!({ needstype: 1 }) + it "correctly derives voiddate if the letting is a renewal and clears it if it is not" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :voiddate).to lettings_log.startdate + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :voiddate).from(lettings_log.startdate).to nil + end - record_from_db = ActiveRecord::Base.connection.execute("select prevten from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(record_from_db["prevten"]).to eq(30) - expect(lettings_log["prevten"]).to eq(30) + it "correctly derives first_time_property_let_as_social_housing and clears it if it is not" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :first_time_property_let_as_social_housing).to 0 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :first_time_property_let_as_social_housing).from(0).to nil end - it "correctly derives and saves referral" do - record_from_db = ActiveRecord::Base.connection.execute("select referral from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(record_from_db["referral"]).to eq(1) - expect(lettings_log["referral"]).to eq(1) + it "correctly derives vacancy reason and clears it if it is not" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :rsnvac).to 14 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :rsnvac).from(14).to nil end - it "correctly derives and saves vacdays" do - record_from_db = ActiveRecord::Base.connection.execute("select vacdays from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(record_from_db["vacdays"]).to eq(0) - expect(lettings_log["vacdays"]).to eq(0) + it "derives vacdays as 0 if log is renewal" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :vacdays).to 0 end - it "correctly derives and saves first_time_property_let_as_social_housing" do - record_from_db = ActiveRecord::Base.connection.execute( - "select first_time_property_let_as_social_housing" \ - " from lettings_logs where id=#{lettings_log.id}", - ).to_a[0] - expect(record_from_db["first_time_property_let_as_social_housing"]).to eq(0) - expect(lettings_log["first_time_property_let_as_social_housing"]).to eq(0) + it "correctly derives underoccupation_benefitcap if log is a renewal from 2021/22" do + lettings_log.update!(renewal: 1) + expect(lettings_log.underoccupation_benefitcap).to be 2 end - it "derives vacancy reason as relet" do - record_from_db = ActiveRecord::Base.connection.execute("select rsnvac from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(record_from_db["rsnvac"]).to eq(14) - expect(lettings_log["rsnvac"]).to eq(14) + it "clears underoccupation_benefitcap if log is no longer a renewal" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :underoccupation_benefitcap).to 2 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :underoccupation_benefitcap).from(2).to nil end - context "when deriving renttype and unitletas" do - before do - Timecop.freeze(Time.zone.local(2022, 1, 1)) - allow(FeatureToggle).to receive(:startdate_two_week_validation_enabled?).and_return(false) - lettings_log.update!(rent_type:, irproduct_other: "other") - end + it "clears underoccupation_benefitcap if log is no longer in 2021/22" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :underoccupation_benefitcap).to 2 + Timecop.return + expect { lettings_log.update!(startdate: Time.zone.local(2023, 1, 1)) }.to change(lettings_log, :underoccupation_benefitcap).from(2).to nil + end - after do - Timecop.unfreeze - end + context "when the log is general needs" do + context "and the managing organisation is a private registered provider" do + before do + lettings_log.managing_organisation.update!(provider_type: "PRP") + lettings_log.update!(needstype: 1, renewal: 1) + end - context "when the rent_type is Social Rent (0)" do - let(:rent_type) { 0 } + it "correctly derives prevten" do + expect(lettings_log.prevten).to be 32 + end - it "derives and saves renttype and unitletas as Social rent(1)" do - record_from_db = ActiveRecord::Base.connection.execute("select renttype from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.renttype).to eq(1) - expect(record_from_db["renttype"]).to eq(1) + it "clears prevten if the log is marked as supported housing" do + lettings_log.update!(needstype: 2) + expect(lettings_log.prevten).to be nil end - it "derives and saves unitletas as Social rent(1)" do - record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.unitletas).to eq(1) - expect(record_from_db["unitletas"]).to eq(1) + it "clears prevten if renewal is update to no" do + lettings_log.update!(renewal: 0) + expect(lettings_log.prevten).to be nil end + end - context "and it is a 23/24 form" do - before do - Timecop.freeze(Time.zone.local(2023, 5, 1)) - end + context "and the managing organisation is a local authority" do + before do + lettings_log.managing_organisation.update!(provider_type: "LA") + lettings_log.update!(needstype: 1, renewal: 1) + end - after do - Timecop.unfreeze - end + it "correctly derives prevten" do + expect(lettings_log.prevten).to be 30 + end - around do |example| - Timecop.freeze(Time.zone.local(2023, 5, 1)) do - Singleton.__init__(FormHandler) - example.run - end - Timecop.return - Singleton.__init__(FormHandler) - end + it "clears prevten if the log is marked as supported housing" do + expect { lettings_log.update!(needstype: 2) }.to change(lettings_log, :prevten).to nil + end - it "derives and saves unitletas as Social rent(1)" do - lettings_log.update!(startdate: Time.zone.local(2023, 5, 1)) - record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.unitletas).to eq(1) - expect(record_from_db["unitletas"]).to eq(1) - end + it "clears prevten if renewal is update to no" do + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :prevten).to nil end end + end - context "when the rent_type is Affordable Rent(1)" do - let(:rent_type) { 1 } + context "and updating rent_type" do + let(:irproduct_other) { nil } - it "derives and saves renttype as Affordable Rent(2)" do - record_from_db = ActiveRecord::Base.connection.execute("select renttype from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.renttype).to eq(2) - expect(record_from_db["renttype"]).to eq(2) + around do |example| + Timecop.freeze(now) do + Singleton.__init__(FormHandler) + lettings_log.update!(rent_type:, irproduct_other:, startdate: now) + example.run end + end - it "derives and saves unitletas as Affordable Rent(2)" do - record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.unitletas).to eq(2) - expect(record_from_db["unitletas"]).to eq(2) - end + context "when collection year is 2022/23 or earlier" do + let(:now) { Time.zone.local(2023, 1, 1) } - context "and it is a 23/24 form" do - before do - Timecop.freeze(Time.zone.local(2023, 5, 1)) - end + context "when rent_type is Social Rent" do + let(:rent_type) { 0 } - after do - Timecop.unfreeze + it "derives the most recent let type as Social Rent basis if it is a renewal and clears it if it is not" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 1 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(1).to nil end + end - around do |example| - Timecop.freeze(Time.zone.local(2023, 5, 1)) do - Singleton.__init__(FormHandler) - example.run - end - Timecop.return - Singleton.__init__(FormHandler) - end + context "when rent_type is Affordable Rent" do + let(:rent_type) { 1 } - it "derives and saves unitletas as Affordable Rent basis(2)" do - lettings_log.update!(startdate: Time.zone.local(2023, 5, 1)) - record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.unitletas).to eq(2) - expect(record_from_db["unitletas"]).to eq(2) + it "derives the most recent let type as Affordable Rent basis if it is a renewal and clears it if it is not" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 2 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(2).to nil end end - end - context "when the rent_type is London Affordable Rent(2)" do - let(:rent_type) { 2 } + context "when rent_type is London Affordable Rent" do + let(:rent_type) { 2 } - it "derives and saves renttype as London Affordable Rent(2)" do - record_from_db = ActiveRecord::Base.connection.execute("select renttype from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.renttype).to eq(2) - expect(record_from_db["renttype"]).to eq(2) + it "derives the most recent let type as Affordable Rent basis if it is a renewal and clears it if it is not" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 2 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(2).to nil + end end - it "derives and saves unitletas as London Affordable Rent(2)" do - record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.unitletas).to eq(2) - expect(record_from_db["unitletas"]).to eq(2) - end + context "when rent_type is Rent to Buy" do + let(:rent_type) { 3 } - context "and it is a 23/24 form" do - before do - Timecop.freeze(Time.zone.local(2023, 5, 1)) + it "derives the most recent let type as Intermediate Rent basis if it is a renewal and clears it if it is not" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 4 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(4).to nil end + end - after do - Timecop.unfreeze - end + context "when rent_type is London Living Rent" do + let(:rent_type) { 4 } - around do |example| - Timecop.freeze(Time.zone.local(2023, 5, 1)) do - Singleton.__init__(FormHandler) - example.run - end - Timecop.return - Singleton.__init__(FormHandler) + it "derives the most recent let type as Intermediate Rent basis if it is a renewal and clears it if it is not" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 4 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(4).to nil end + end + + context "when rent_type is Other intermediate rent product" do + let(:rent_type) { 5 } + let(:irproduct_other) { "Rent first" } - it "derives and saves unitletas as London Affordable Rent basis(5)" do - lettings_log.update!(startdate: Time.zone.local(2023, 5, 1)) - record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.unitletas).to eq(5) - expect(record_from_db["unitletas"]).to eq(5) + it "derives the most recent let type as Intermediate Rent basis if it is a renewal and clears it if it is not" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 4 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(4).to nil end end end - context "when the rent_type is Rent to Buy(3)" do - let(:rent_type) { 3 } + context "when collection year is 2023/24 or later" do + let(:now) { Time.zone.local(2024, 1, 1) } - it "derives and saves renttype as Intermediate Rent(3)" do - record_from_db = ActiveRecord::Base.connection.execute("select renttype from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.renttype).to eq(3) - expect(record_from_db["renttype"]).to eq(3) - end + context "when rent_type is Social Rent" do + let(:rent_type) { 0 } - it "derives and saves unitletas as Intermediate Rent(4)" do - record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.unitletas).to eq(4) - expect(record_from_db["unitletas"]).to eq(4) + it "derives the most recent let type as Social Rent basis if it is a renewal and clears it if it is not" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 1 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(1).to nil + end end - context "and it is a 23/24 form" do - before do - Timecop.freeze(Time.zone.local(2023, 5, 1)) - end + context "when rent_type is Affordable Rent" do + let(:rent_type) { 1 } - after do - Timecop.unfreeze + it "derives the most recent let type as Affordable Rent basis if it is a renewal and clears it if it is not" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 2 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(2).to nil end + end - around do |example| - Timecop.freeze(Time.zone.local(2023, 5, 1)) do - Singleton.__init__(FormHandler) - example.run - end - Timecop.return - Singleton.__init__(FormHandler) - end + context "when rent_type is London Affordable Rent" do + let(:rent_type) { 2 } - it "derives and saves unitletas as Rent to Buy basis(6)" do - lettings_log.update!(startdate: Time.zone.local(2023, 5, 1)) - record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.unitletas).to eq(6) - expect(record_from_db["unitletas"]).to eq(6) + it "derives the most recent let type as London Affordable Rent basis if it is a renewal and clears it if it is not" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 5 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(5).to nil end end - end - context "when the rent_type is London Living Rent(4)" do - let(:rent_type) { 4 } + context "when rent_type is Rent to Buy" do + let(:rent_type) { 3 } - it "derives and saves renttype as Intermediate Rent(3)" do - record_from_db = ActiveRecord::Base.connection.execute("select renttype from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.renttype).to eq(3) - expect(record_from_db["renttype"]).to eq(3) - end - - it "derives and saves unitletas as Intermediate Rent(4)" do - record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.unitletas).to eq(4) - expect(record_from_db["unitletas"]).to eq(4) + it "derives the most recent let type as Rent to Buy basis if it is a renewal and clears it if it is not" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 6 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(6).to nil + end end - context "and it is a 23/24 form" do - before do - Timecop.freeze(Time.zone.local(2023, 5, 1)) - end + context "when rent_type is London Living Rent" do + let(:rent_type) { 4 } - after do - Timecop.unfreeze + it "derives the most recent let type as London Living Rent basis if it is a renewal and clears it if it is not" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 7 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(7).to nil end + end - around do |example| - Timecop.freeze(Time.zone.local(2023, 5, 1)) do - Singleton.__init__(FormHandler) - example.run - end - Timecop.return - Singleton.__init__(FormHandler) - end + context "when rent_type is Other intermediate rent product" do + let(:rent_type) { 5 } + let(:irproduct_other) { "Rent first" } - it "derives and saves unitletas as London Living Rent basis(7)" do - lettings_log.update!(startdate: Time.zone.local(2023, 5, 1)) - record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.unitletas).to eq(7) - expect(record_from_db["unitletas"]).to eq(7) + it "derives the most recent let type as Another Intermediate Rent basis if it is a renewal and clears it if it is not" do + expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 8 + expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(8).to nil end end end + end + end + + context "when updating rent type" do + let(:irproduct_other) { nil } + + before do + lettings_log.update!(rent_type:, irproduct_other:) + end + + context "when rent_type is Social Rent" do + let(:rent_type) { 0 } + + it "derives renttype as Social Rent" do + expect(lettings_log.renttype).to be 1 + end + end - context "when the rent_type is Other intermediate rent product(5)" do - let(:rent_type) { 5 } + context "when rent_type is Affordable Rent" do + let(:rent_type) { 1 } - it "derives and saves renttype as Intermediate Rent(3)" do - record_from_db = ActiveRecord::Base.connection.execute("select renttype from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.renttype).to eq(3) - expect(record_from_db["renttype"]).to eq(3) - end + it "derives renttype as Affordable Rent" do + expect(lettings_log.renttype).to be 2 + end + end - it "derives and saves unitletas as Intermediate Rent(4)" do - record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.unitletas).to eq(4) - expect(record_from_db["unitletas"]).to eq(4) - end + context "when rent_type is London Affordable Rent" do + let(:rent_type) { 2 } - context "and it is a 23/24 form" do - before do - Timecop.freeze(Time.zone.local(2023, 5, 1)) - end + it "derives renttype as Affordable Rent" do + expect(lettings_log.renttype).to be 2 + end + end - after do - Timecop.unfreeze - end + context "when rent_type is Rent to Buy" do + let(:rent_type) { 3 } - around do |example| - Timecop.freeze(Time.zone.local(2023, 5, 1)) do - Singleton.__init__(FormHandler) - example.run - end - Timecop.return - Singleton.__init__(FormHandler) - end + it "derives renttype as Intermediate Rent" do + expect(lettings_log.renttype).to be 3 + end + end - it "derives and saves unitletas as Other intermediate rent basis(8)" do - lettings_log.update!(startdate: Time.zone.local(2023, 5, 1)) - record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(lettings_log.unitletas).to eq(8) - expect(record_from_db["unitletas"]).to eq(8) - end - end + context "when rent_type is London Living Rent" do + let(:rent_type) { 4 } + + it "derives renttype as Intermediate Rent" do + expect(lettings_log.renttype).to be 3 + end + end + + context "when rent_type is Other intermediate rent product" do + let(:rent_type) { 5 } + let(:irproduct_other) { "Rent first" } + + it "derives renttype as Intermediate Rent" do + expect(lettings_log.renttype).to be 3 end end end @@ -2168,37 +2156,6 @@ RSpec.describe LettingsLog do expect(record_from_db["wchair"]).to eq(1) end end - - context "and renewal" do - before do - Timecop.freeze(Time.zone.local(2022, 4, 2)) - end - - after do - Timecop.unfreeze - end - - let(:scheme) { create(:scheme) } - let(:location) { create(:location, scheme:) } - let!(:supported_housing_lettings_log) do - described_class.create!({ - managing_organisation: owning_organisation, - owning_organisation:, - created_by: created_by_user, - needstype: 2, - scheme_id: scheme.id, - location_id: location.id, - renewal: 1, - startdate: Time.zone.local(2022, 4, 2), - created_at: Time.utc(2022, 2, 8, 16, 52, 15), - }) - end - - it "correctly infers and saves the renewal date" do - record_from_db = ActiveRecord::Base.connection.execute("SELECT voiddate from lettings_logs where id=#{supported_housing_lettings_log.id}").to_a[0] - expect(record_from_db["voiddate"].to_i).to eq(supported_housing_lettings_log.startdate.to_i) - end - end end context "when saving accessibility needs" do @@ -2434,77 +2391,18 @@ RSpec.describe LettingsLog do end end - context "when it changes from a renewal to not a renewal" do - let(:lettings_log) { create(:lettings_log) } - - it "resets inferred waityear value" do - lettings_log.update!({ renewal: 1 }) - - record_from_db = ActiveRecord::Base.connection.execute("select waityear from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(record_from_db["waityear"]).to eq(2) - expect(lettings_log["waityear"]).to eq(2) - - lettings_log.update!({ renewal: 0 }) - record_from_db = ActiveRecord::Base.connection.execute("select waityear from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(record_from_db["waityear"]).to eq(nil) - expect(lettings_log["waityear"]).to eq(nil) - end - - it "resets inferred vacancy reason value" do - vacancy_reason = "rsnvac" - - lettings_log.update!({ renewal: 1 }) - - record_from_db = ActiveRecord::Base.connection.execute("select #{vacancy_reason} from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(record_from_db[vacancy_reason]).to eq(14) - expect(lettings_log[vacancy_reason]).to eq(14) - - lettings_log.update!({ renewal: 0 }) - record_from_db = ActiveRecord::Base.connection.execute("select #{vacancy_reason} from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(record_from_db[vacancy_reason]).to eq(nil) - expect(lettings_log[vacancy_reason]).to eq(nil) - end - end - context "when it changes from a supported housing to not a supported housing" do let(:location) { create(:location, mobility_type: "A", postcode: "SW1P 4DG") } let(:lettings_log) { create(:lettings_log, location:) } it "resets inferred wchair value" do - lettings_log.update!({ needstype: 2 }) - - record_from_db = ActiveRecord::Base.connection.execute("select wchair from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(record_from_db["wchair"]).to eq(2) - expect(lettings_log["wchair"]).to eq(2) - - lettings_log.update!({ needstype: 1 }) - record_from_db = ActiveRecord::Base.connection.execute("select needstype from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(record_from_db["wchair"]).to eq(nil) - expect(lettings_log["wchair"]).to eq(nil) + expect { lettings_log.update!(needstype: 2) }.to change(lettings_log, :wchair).to(2) + expect { lettings_log.update!(needstype: 1) }.to change(lettings_log, :wchair).from(2).to(nil) end it "resets location" do - lettings_log.update!({ needstype: 2 }) - - record_from_db = ActiveRecord::Base.connection.execute("select location_id from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(record_from_db["location_id"]).to eq(location.id) - expect(lettings_log["location_id"]).to eq(location.id) - lettings_log.update!({ needstype: 1 }) - record_from_db = ActiveRecord::Base.connection.execute("select location_id from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(record_from_db["location_id"]).to eq(nil) - expect(lettings_log["location_id"]).to eq(nil) - end - end - - context "when it is not a renewal" do - let(:lettings_log) { create(:lettings_log) } - - it "saves waityear value" do - lettings_log.update!({ renewal: 0, waityear: 2 }) - - record_from_db = ActiveRecord::Base.connection.execute("select waityear from lettings_logs where id=#{lettings_log.id}").to_a[0] - expect(record_from_db["waityear"]).to eq(2) - expect(lettings_log["waityear"]).to eq(2) + lettings_log.update!(needstype: 2) + expect { lettings_log.update!(needstype: 1) }.to change(lettings_log, :location_id).from(location.id).to(nil) end end diff --git a/spec/models/validations/soft_validations_spec.rb b/spec/models/validations/soft_validations_spec.rb index b51a084f5..acffc9cac 100644 --- a/spec/models/validations/soft_validations_spec.rb +++ b/spec/models/validations/soft_validations_spec.rb @@ -84,25 +84,9 @@ RSpec.describe Validations::SoftValidations do describe "retirement soft validations" do context "when the tenant is retired but under the expected retirement age" do - context "when the tenant is female" do - it "shows the interruption screen" do - record.update!(age1: 43, sex1: "F", ecstat1: 5) - expect(record.person_1_retired_under_soft_min_age?).to be true - end - end - - context "when the tenant is male" do - it "shows the interruption screen" do - record.update!(age1: 43, sex1: "M", ecstat1: 5) - expect(record.person_1_retired_under_soft_min_age?).to be true - end - end - - context "when the tenant is non-binary" do - it "shows the interruption screen" do - record.update!(age1: 43, sex1: "X", ecstat1: 5) - expect(record.person_1_retired_under_soft_min_age?).to be true - end + it "shows the interruption screen" do + record.update!(age1: 43, ecstat1: 5) + expect(record.person_1_retired_under_soft_min_age?).to be true end end @@ -130,25 +114,9 @@ RSpec.describe Validations::SoftValidations do end context "when the tenant prefers not to say what their economic status is but is under the expected retirement age" do - context "when the tenant is female" do - it "does not show the interruption screen" do - record.update!(age1: 43, sex1: "F", ecstat1: 10) - expect(record.person_1_retired_under_soft_min_age?).to be false - end - end - - context "when the tenant is male" do - it "does not show the interruption screen" do - record.update!(age1: 43, sex1: "M", ecstat1: 10) - expect(record.person_1_retired_under_soft_min_age?).to be false - end - end - - context "when the tenant is non-binary" do - it "does not show the interruption screen" do - record.update!(age1: 43, sex1: "X", ecstat1: 10) - expect(record.person_1_retired_under_soft_min_age?).to be false - end + it "does not show the interruption screen" do + record.update!(age1: 43, ecstat1: 10) + expect(record.person_1_retired_under_soft_min_age?).to be false end end diff --git a/spec/models/validations/tenancy_validations_spec.rb b/spec/models/validations/tenancy_validations_spec.rb index e2dae02e2..f52c19bb9 100644 --- a/spec/models/validations/tenancy_validations_spec.rb +++ b/spec/models/validations/tenancy_validations_spec.rb @@ -23,6 +23,7 @@ RSpec.describe Validations::TenancyValidations do record.tenancy = 3 record.tenancylength = 10 tenancy_validator.validate_fixed_term_tenancy(record) + expect(record.errors["needstype"]).to include(match(expected_error)) expect(record.errors["tenancylength"]).to include(match(expected_error)) expect(record.errors["tenancy"]).to include(match(expected_error)) end @@ -42,6 +43,7 @@ RSpec.describe Validations::TenancyValidations do it "adds an error" do record.tenancylength = 1 tenancy_validator.validate_fixed_term_tenancy(record) + expect(record.errors["needstype"]).to include(match(expected_error)) expect(record.errors["tenancylength"]).to include(match(expected_error)) expect(record.errors["tenancy"]).to include(match(expected_error)) end @@ -51,6 +53,7 @@ RSpec.describe Validations::TenancyValidations do it "adds an error" do record.tenancylength = 100 tenancy_validator.validate_fixed_term_tenancy(record) + expect(record.errors["needstype"]).to include(match(expected_error)) expect(record.errors["tenancylength"]).to include(match(expected_error)) expect(record.errors["tenancy"]).to include(match(expected_error)) end @@ -60,8 +63,7 @@ RSpec.describe Validations::TenancyValidations do it "does not add an error" do record.tenancylength = 3 tenancy_validator.validate_fixed_term_tenancy(record) - expect(record.errors["tenancylength"]).to be_empty - expect(record.errors["tenancy"]).to be_empty + expect(record.errors).to be_empty end end @@ -69,8 +71,7 @@ RSpec.describe Validations::TenancyValidations do it "does not add an error" do record.tenancylength = nil tenancy_validator.validate_fixed_term_tenancy(record) - expect(record.errors["tenancylength"]).to be_empty - expect(record.errors["tenancy"]).to be_empty + expect(record.errors).to be_empty end end end @@ -90,6 +91,7 @@ RSpec.describe Validations::TenancyValidations do it "adds an error" do record.tenancylength = 1 tenancy_validator.validate_fixed_term_tenancy(record) + expect(record.errors["needstype"]).to include(match(expected_error)) expect(record.errors["tenancylength"]).to include(match(expected_error)) expect(record.errors["tenancy"]).to include(match(expected_error)) end @@ -99,6 +101,7 @@ RSpec.describe Validations::TenancyValidations do it "adds an error" do record.tenancylength = 100 tenancy_validator.validate_fixed_term_tenancy(record) + expect(record.errors["needstype"]).to include(match(expected_error)) expect(record.errors["tenancylength"]).to include(match(expected_error)) expect(record.errors["tenancy"]).to include(match(expected_error)) end @@ -108,8 +111,7 @@ RSpec.describe Validations::TenancyValidations do it "does not add an error" do record.tenancylength = 3 tenancy_validator.validate_fixed_term_tenancy(record) - expect(record.errors["tenancylength"]).to be_empty - expect(record.errors["tenancy"]).to be_empty + expect(record.errors).to be_empty end end @@ -117,8 +119,7 @@ RSpec.describe Validations::TenancyValidations do it "does not add an error" do record.tenancylength = nil tenancy_validator.validate_fixed_term_tenancy(record) - expect(record.errors["tenancylength"]).to be_empty - expect(record.errors["tenancy"]).to be_empty + expect(record.errors).to be_empty end end end @@ -149,6 +150,7 @@ RSpec.describe Validations::TenancyValidations do it "adds an error" do record.tenancylength = 1 tenancy_validator.validate_fixed_term_tenancy(record) + expect(record.errors["needstype"]).to include(match(expected_error)) expect(record.errors["tenancylength"]).to include(match(expected_error)) expect(record.errors["tenancy"]).to include(match(expected_error)) end @@ -158,6 +160,7 @@ RSpec.describe Validations::TenancyValidations do it "adds an error" do record.tenancylength = 100 tenancy_validator.validate_fixed_term_tenancy(record) + expect(record.errors["needstype"]).to include(match(expected_error)) expect(record.errors["tenancylength"]).to include(match(expected_error)) expect(record.errors["tenancy"]).to include(match(expected_error)) end @@ -167,8 +170,7 @@ RSpec.describe Validations::TenancyValidations do it "does not add an error" do record.tenancylength = 3 tenancy_validator.validate_fixed_term_tenancy(record) - expect(record.errors["tenancylength"]).to be_empty - expect(record.errors["tenancy"]).to be_empty + expect(record.errors).to be_empty end end @@ -176,8 +178,7 @@ RSpec.describe Validations::TenancyValidations do it "does not add an error" do record.tenancylength = nil tenancy_validator.validate_fixed_term_tenancy(record) - expect(record.errors["tenancylength"]).to be_empty - expect(record.errors["tenancy"]).to be_empty + expect(record.errors).to be_empty end end end @@ -196,6 +197,7 @@ RSpec.describe Validations::TenancyValidations do it "adds an error" do record.tenancylength = 1 tenancy_validator.validate_fixed_term_tenancy(record) + expect(record.errors["needstype"]).to include(match(expected_error)) expect(record.errors["tenancylength"]).to include(match(expected_error)) expect(record.errors["tenancy"]).to include(match(expected_error)) end @@ -205,6 +207,7 @@ RSpec.describe Validations::TenancyValidations do it "adds an error" do record.tenancylength = 100 tenancy_validator.validate_fixed_term_tenancy(record) + expect(record.errors["needstype"]).to include(match(expected_error)) expect(record.errors["tenancylength"]).to include(match(expected_error)) expect(record.errors["tenancy"]).to include(match(expected_error)) end @@ -214,8 +217,7 @@ RSpec.describe Validations::TenancyValidations do it "does not add an error" do record.tenancylength = 3 tenancy_validator.validate_fixed_term_tenancy(record) - expect(record.errors["tenancylength"]).to be_empty - expect(record.errors["tenancy"]).to be_empty + expect(record.errors).to be_empty end end @@ -223,8 +225,7 @@ RSpec.describe Validations::TenancyValidations do it "does not add an error" do record.tenancylength = nil tenancy_validator.validate_fixed_term_tenancy(record) - expect(record.errors["tenancylength"]).to be_empty - expect(record.errors["tenancy"]).to be_empty + expect(record.errors).to be_empty end end end diff --git a/spec/requests/form_controller_spec.rb b/spec/requests/form_controller_spec.rb index de2e0b571..5d9042c7c 100644 --- a/spec/requests/form_controller_spec.rb +++ b/spec/requests/form_controller_spec.rb @@ -532,6 +532,56 @@ RSpec.describe FormController, type: :request do end end end + + context "when the question was accessed from an interruption screen (soft validation)" do + let(:params) do + { + id: lettings_log.id, + lettings_log: { + page: page_id, + age1: 20, + interruption_page_id: "retirement_value_check", + }, + } + end + + before do + post "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}?referrer=interruption_screen", params: + end + + it "redirects back to the soft validation page" do + expect(response).to redirect_to("/lettings-logs/#{lettings_log.id}/retirement-value-check") + end + + it "displays a success banner" do + follow_redirect! + follow_redirect! + expect(response.body).to include("You have successfully updated lead tenant’s age") + end + end + + context "when requesting a soft validation page for validation that isn't triggering" do + before do + get "/lettings-logs/#{lettings_log.id}/retirement-value-check", headers: headers.merge({ "HTTP_REFERER" => referrer }) + end + + context "when the referrer header has interruption_screen" do + let(:referrer) { "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}?referrer=interruption_screen" } + + it "routes to the soft validation page" do + expect(response.body).to include("Make sure these answers are correct:") + end + end + + context "when the referrer header does not have interruption screen" do + let(:referrer) { "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}" } + + it "skips the soft validation page" do + follow_redirect! + expect(response.body).not_to include("Make sure these answers are correct:") + end + end + end end context "with checkbox questions" do diff --git a/spec/requests/merge_requests_controller_spec.rb b/spec/requests/merge_requests_controller_spec.rb index 3ffe1fe13..b8bc22d55 100644 --- a/spec/requests/merge_requests_controller_spec.rb +++ b/spec/requests/merge_requests_controller_spec.rb @@ -553,6 +553,86 @@ RSpec.describe MergeRequestsController, type: :request do end end end + + describe "#new_organsation_address" do + let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation, new_organisation_name: "New name", new_absorbing_organisation: true) } + + context "when viewing the new organisation name page" do + before do + get "/merge-request/#{merge_request.id}/new-organisation-address", headers: + end + + it "displays the correct question" do + expect(page).to have_content("What is New name’s address?") + end + + it "has the correct back button" do + expect(page).to have_link("Back", href: new_organisation_name_merge_request_path(merge_request)) + end + + it "has a skip link" do + expect(page).to have_link("Skip for now", href: new_organisation_telephone_number_merge_request_path(merge_request)) + end + end + + context "when updating the new organisation address" do + let(:params) do + { merge_request: { + new_organisation_address_line1: "first address line", + new_organisation_address_line2: "second address line", + new_organisation_postcode: "new postcode", + page: "new_organisation_address", + } } + end + + let(:request) do + patch "/merge-request/#{merge_request.id}", headers:, params: + end + + it "redirects to new organisation telephone path" do + request + expect(response).to redirect_to(new_organisation_telephone_number_merge_request_path(merge_request)) + end + + it "updates new organisation address line 1 to correct addess line" do + expect { request }.to change { + merge_request.reload.new_organisation_address_line1 + }.from(nil).to("first address line") + end + + it "updates new organisation address line 2 to correct addess line" do + expect { request }.to change { + merge_request.reload.new_organisation_address_line2 + }.from(nil).to("second address line") + end + + it "updates new organisation postcode to correct addess line" do + expect { request }.to change { + merge_request.reload.new_organisation_postcode + }.from(nil).to("new postcode") + end + end + + context "when address is not provided" do + let(:params) do + { merge_request: { + new_organisation_address_line1: nil, + new_organisation_address_line2: nil, + new_organisation_postcode: nil, + page: "new_organisation_address", + } } + end + + let(:request) do + patch "/merge-request/#{merge_request.id}", headers:, params: + end + + it "does not throw an error" do + request + expect(response).to redirect_to(new_organisation_telephone_number_merge_request_path(merge_request)) + end + end + end end end diff --git a/spec/requests/organisations_controller_spec.rb b/spec/requests/organisations_controller_spec.rb index d526f218f..6013a5813 100644 --- a/spec/requests/organisations_controller_spec.rb +++ b/spec/requests/organisations_controller_spec.rb @@ -367,7 +367,7 @@ RSpec.describe OrganisationsController, type: :request do end end - context "when viewing logs for other organisation" do + context "when viewing lettings logs for other organisation" do it "does not display the lettings logs" do get "/organisations/#{unauthorised_organisation.id}/lettings-logs", headers:, params: {} expect(response).to have_http_status(:unauthorized) @@ -375,13 +375,13 @@ RSpec.describe OrganisationsController, type: :request do it "prevents CSV download" do expect { - post "/organisations/#{unauthorised_organisation.id}/logs/email-csv", headers:, params: {} + post "/organisations/#{unauthorised_organisation.id}/lettings-logs/email-csv", headers:, params: {} }.not_to enqueue_job(EmailCsvJob) expect(response).to have_http_status(:unauthorized) end end - context "when viewing logs for your organisation" do + context "when viewing lettings logs for your organisation" do it "does not display the logs" do get "/organisations/#{organisation.id}/lettings-logs", headers:, params: {} expect(response).to have_http_status(:unauthorized) @@ -389,7 +389,35 @@ RSpec.describe OrganisationsController, type: :request do it "prevents CSV download" do expect { - post "/organisations/#{organisation.id}/logs/email-csv", headers:, params: {} + post "/organisations/#{organisation.id}/lettings-logs/email-csv", headers:, params: {} + }.not_to enqueue_job(EmailCsvJob) + expect(response).to have_http_status(:unauthorized) + end + end + + context "when viewing sales logs for other organisation" do + it "does not display the sales logs" do + get "/organisations/#{unauthorised_organisation.id}/sales-logs", headers:, params: {} + expect(response).to have_http_status(:unauthorized) + end + + it "prevents CSV download" do + expect { + post "/organisations/#{unauthorised_organisation.id}/sales-logs/email-csv", headers:, params: {} + }.not_to enqueue_job(EmailCsvJob) + expect(response).to have_http_status(:unauthorized) + end + end + + context "when viewing sales logs for your organisation" do + it "does not display the logs" do + get "/organisations/#{organisation.id}/sales-logs", headers:, params: {} + expect(response).to have_http_status(:unauthorized) + end + + it "prevents CSV download" do + expect { + post "/organisations/#{organisation.id}/sales-logs/email-csv", headers:, params: {} }.not_to enqueue_job(EmailCsvJob) expect(response).to have_http_status(:unauthorized) end @@ -544,7 +572,7 @@ RSpec.describe OrganisationsController, type: :request do end end - context "when viewing logs for other organisation" do + context "when viewing lettings logs for other organisation" do it "does not display the logs" do get "/organisations/#{unauthorised_organisation.id}/lettings-logs", headers:, params: {} expect(response).to have_http_status(:unauthorized) @@ -552,13 +580,13 @@ RSpec.describe OrganisationsController, type: :request do it "prevents CSV download" do expect { - post "/organisations/#{unauthorised_organisation.id}/logs/email-csv", headers:, params: {} + post "/organisations/#{unauthorised_organisation.id}/lettings-logs/email-csv", headers:, params: {} }.not_to enqueue_job(EmailCsvJob) expect(response).to have_http_status(:unauthorized) end end - context "when viewing logs for your organisation" do + context "when viewing lettings logs for your organisation" do it "does not display the logs" do get "/organisations/#{organisation.id}/lettings-logs", headers:, params: {} expect(response).to have_http_status(:unauthorized) @@ -566,7 +594,35 @@ RSpec.describe OrganisationsController, type: :request do it "prevents CSV download" do expect { - post "/organisations/#{organisation.id}/logs/email-csv", headers:, params: {} + post "/organisations/#{organisation.id}/lettings-logs/email-csv", headers:, params: {} + }.not_to enqueue_job(EmailCsvJob) + expect(response).to have_http_status(:unauthorized) + end + end + + context "when viewing sales logs for other organisation" do + it "does not display the logs" do + get "/organisations/#{unauthorised_organisation.id}/sales-logs", headers:, params: {} + expect(response).to have_http_status(:unauthorized) + end + + it "prevents CSV download" do + expect { + post "/organisations/#{unauthorised_organisation.id}/sales-logs/email-csv", headers:, params: {} + }.not_to enqueue_job(EmailCsvJob) + expect(response).to have_http_status(:unauthorized) + end + end + + context "when viewing sales logs for your organisation" do + it "does not display the logs" do + get "/organisations/#{organisation.id}/sales-logs", headers:, params: {} + expect(response).to have_http_status(:unauthorized) + end + + it "prevents CSV download" do + expect { + post "/organisations/#{organisation.id}/sales-logs/email-csv", headers:, params: {} }.not_to enqueue_job(EmailCsvJob) expect(response).to have_http_status(:unauthorized) end @@ -1176,15 +1232,15 @@ RSpec.describe OrganisationsController, type: :request do sign_in user end - context "when they view the logs tab" do + context "when they view the lettings logs tab" do before do FactoryBot.create(:lettings_log, owning_organisation: organisation) end it "has CSV download buttons with the correct paths if at least 1 log exists" do get "/organisations/#{organisation.id}/lettings-logs" - expect(page).to have_link("Download (CSV)", href: "/organisations/#{organisation.id}/logs/csv-download?codes_only=false") - expect(page).to have_link("Download (CSV, codes only)", href: "/organisations/#{organisation.id}/logs/csv-download?codes_only=true") + expect(page).to have_link("Download (CSV)", href: "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=false") + expect(page).to have_link("Download (CSV, codes only)", href: "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=true") end context "when you download the CSV" do @@ -1197,62 +1253,151 @@ RSpec.describe OrganisationsController, type: :request do end it "only includes logs from that organisation" do - get "/organisations/#{organisation.id}/logs/csv-download?codes_only=false" + get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=false" expect(page).to have_text("You've selected 3 logs.") end it "provides the organisation to the mail job" do expect { - post "/organisations/#{organisation.id}/logs/email-csv?status[]=completed&codes_only=false", headers:, params: {} + post "/organisations/#{organisation.id}/lettings-logs/email-csv?status[]=completed&codes_only=false", headers:, params: {} }.to enqueue_job(EmailCsvJob).with(user, nil, { "status" => %w[completed] }, false, organisation, false) end it "provides the export type to the mail job" do codes_only_export_type = false expect { - post "/organisations/#{organisation.id}/logs/email-csv?codes_only=#{codes_only_export_type}", headers:, params: {} + post "/organisations/#{organisation.id}/lettings-logs/email-csv?codes_only=#{codes_only_export_type}", headers:, params: {} }.to enqueue_job(EmailCsvJob).with(user, nil, {}, false, organisation, codes_only_export_type) codes_only_export_type = true expect { - post "/organisations/#{organisation.id}/logs/email-csv?codes_only=#{codes_only_export_type}", headers:, params: {} + post "/organisations/#{organisation.id}/lettings-logs/email-csv?codes_only=#{codes_only_export_type}", headers:, params: {} }.to enqueue_job(EmailCsvJob).with(user, nil, {}, false, organisation, codes_only_export_type) end end end - describe "GET #download_csv" do + context "when they view the sales logs tab" do + before do + FactoryBot.create(:sales_log, owning_organisation: organisation) + end + + it "has CSV download buttons with the correct paths if at least 1 log exists" do + get "/organisations/#{organisation.id}/sales-logs" + expect(page).to have_link("Download (CSV)", href: "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=false") + expect(page).to have_link("Download (CSV, codes only)", href: "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=true") + end + + context "when you download the CSV" do + let(:other_organisation) { FactoryBot.create(:organisation) } + + before do + FactoryBot.create_list(:sales_log, 2, owning_organisation: organisation) + FactoryBot.create(:sales_log, owning_organisation: organisation, status: "pending", skip_update_status: true) + FactoryBot.create_list(:sales_log, 2, owning_organisation: other_organisation) + end + + it "only includes logs from that organisation" do + get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=false" + + expect(page).to have_text("You've selected 3 logs.") + end + + it "provides the organisation to the mail job" do + expect { + post "/organisations/#{organisation.id}/sales-logs/email-csv?status[]=completed&codes_only=false", headers:, params: {} + }.to enqueue_job(EmailCsvJob).with(user, nil, { "status" => %w[completed] }, false, organisation, false, "sales") + end + + it "provides the log type to the mail job" do + log_type = "sales" + expect { + post "/organisations/#{organisation.id}/sales-logs/email-csv?status[]=completed&codes_only=false", headers:, params: {} + }.to enqueue_job(EmailCsvJob).with(user, nil, { "status" => %w[completed] }, false, organisation, false, log_type) + end + + it "provides the export type to the mail job" do + codes_only_export_type = false + expect { + post "/organisations/#{organisation.id}/sales-logs/email-csv?codes_only=#{codes_only_export_type}", headers:, params: {} + }.to enqueue_job(EmailCsvJob).with(user, nil, {}, false, organisation, codes_only_export_type, "sales") + codes_only_export_type = true + expect { + post "/organisations/#{organisation.id}/sales-logs/email-csv?codes_only=#{codes_only_export_type}", headers:, params: {} + }.to enqueue_job(EmailCsvJob).with(user, nil, {}, false, organisation, codes_only_export_type, "sales") + end + end + end + + describe "GET #download_lettings_csv" do + it "renders a page with the correct header" do + get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=false", headers:, params: {} + header = page.find_css("h1") + expect(header.text).to include("Download CSV") + end + + it "renders a form with the correct target containing a button with the correct text" do + get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=false", headers:, params: {} + form = page.find("form.button_to") + expect(form[:method]).to eq("post") + expect(form[:action]).to eq("/organisations/#{organisation.id}/lettings-logs/email-csv") + expect(form).to have_button("Send email") + end + + it "when codes_only query parameter is false, form contains hidden field with correct value" do + codes_only = false + get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=#{codes_only}", headers:, params: {} + hidden_field = page.find("form.button_to").find_field("codes_only", type: "hidden") + expect(hidden_field.value).to eq(codes_only.to_s) + end + + it "when codes_only query parameter is true, form contains hidden field with correct value" do + codes_only = true + get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=#{codes_only}", headers:, params: {} + hidden_field = page.find("form.button_to").find_field("codes_only", type: "hidden") + expect(hidden_field.value).to eq(codes_only.to_s) + end + + it "when query string contains search parameter, form contains hidden field with correct value" do + search_term = "blam" + get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=true&search=#{search_term}", headers:, params: {} + hidden_field = page.find("form.button_to").find_field("search", type: "hidden") + expect(hidden_field.value).to eq(search_term) + end + end + + describe "GET #download_sales_csv" do it "renders a page with the correct header" do - get "/organisations/#{organisation.id}/logs/csv-download?codes_only=false", headers:, params: {} + get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=false", headers:, params: {} header = page.find_css("h1") expect(header.text).to include("Download CSV") end it "renders a form with the correct target containing a button with the correct text" do - get "/organisations/#{organisation.id}/logs/csv-download?codes_only=false", headers:, params: {} + get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=false", headers:, params: {} form = page.find("form.button_to") expect(form[:method]).to eq("post") - expect(form[:action]).to eq("/organisations/#{organisation.id}/logs/email-csv") + expect(form[:action]).to eq("/organisations/#{organisation.id}/sales-logs/email-csv") expect(form).to have_button("Send email") end it "when codes_only query parameter is false, form contains hidden field with correct value" do codes_only = false - get "/organisations/#{organisation.id}/logs/csv-download?codes_only=#{codes_only}", headers:, params: {} + get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=#{codes_only}", headers:, params: {} hidden_field = page.find("form.button_to").find_field("codes_only", type: "hidden") expect(hidden_field.value).to eq(codes_only.to_s) end it "when codes_only query parameter is true, form contains hidden field with correct value" do codes_only = true - get "/organisations/#{organisation.id}/logs/csv-download?codes_only=#{codes_only}", headers:, params: {} + get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=#{codes_only}", headers:, params: {} hidden_field = page.find("form.button_to").find_field("codes_only", type: "hidden") expect(hidden_field.value).to eq(codes_only.to_s) end it "when query string contains search parameter, form contains hidden field with correct value" do search_term = "blam" - get "/organisations/#{organisation.id}/logs/csv-download?codes_only=true&search=#{search_term}", headers:, params: {} + get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=true&search=#{search_term}", headers:, params: {} hidden_field = page.find("form.button_to").find_field("search", type: "hidden") expect(hidden_field.value).to eq(search_term) end diff --git a/spec/requests/sales_logs_controller_spec.rb b/spec/requests/sales_logs_controller_spec.rb index e400366df..6927d294a 100644 --- a/spec/requests/sales_logs_controller_spec.rb +++ b/spec/requests/sales_logs_controller_spec.rb @@ -148,6 +148,16 @@ RSpec.describe SalesLogsController, type: :request do expect(page).to have_content(other_organisation.name) end + it "shows a link for labelled CSV download of logs" do + get "/sales-logs", headers: headers, params: {} + expect(page).to have_link("Download (CSV)", href: "/sales-logs/csv-download?codes_only=false") + end + + it "shows a link for codes only CSV download of logs" do + get "/sales-logs", headers: headers, params: {} + expect(page).to have_link("Download (CSV, codes only)", href: "/sales-logs/csv-download?codes_only=true") + end + context "when there are no logs in the database" do before do SalesLog.destroy_all @@ -157,6 +167,12 @@ RSpec.describe SalesLogsController, type: :request do get "/sales-logs", headers: headers, params: {} expect(page).to have_title("Logs - Submit social housing lettings and sales data (CORE) - GOV.UK") end + + it "does not show CSV download links" do + get "/sales-logs", headers: headers, params: {} + expect(page).not_to have_link("Download (CSV)") + expect(page).not_to have_link("Download (CSV, codes only)") + end end context "when there is a pending log" do @@ -298,6 +314,19 @@ RSpec.describe SalesLogsController, type: :request do expect(page).not_to have_content("Managing organisation") end + it "displays standard CSV download link only, with the correct path" do + get "/sales-logs", headers:, params: {} + expect(page).to have_link("Download (CSV)", href: "/sales-logs/csv-download?codes_only=false") + expect(page).not_to have_link("Download (CSV, codes only)") + end + + it "does not display CSV download links if there are no logs" do + SalesLog.destroy_all + get "/sales-logs", headers:, params: {} + expect(page).not_to have_link("Download (CSV)") + expect(page).not_to have_link("Download (CSV, codes only)") + end + context "when using a search query" do let(:logs) { FactoryBot.create_list(:sales_log, 3, :completed, owning_organisation: user.organisation, created_by: user) } let(:log_to_search) { FactoryBot.create(:sales_log, :completed, owning_organisation: user.organisation, created_by: user) } @@ -316,6 +345,13 @@ RSpec.describe SalesLogsController, type: :request do end end + it "displays the labelled CSV download link, with the search included in the query params" do + get "/sales-logs?search=#{log_to_search.id}", headers:, params: {} + download_link = page.find_link("Download (CSV)") + download_link_params = CGI.parse(URI.parse(download_link[:href]).query) + expect(download_link_params).to include("search" => [log_to_search.id.to_s]) + end + context "when search query doesn't match any logs" do it "doesn't display any logs" do get "/sales-logs?search=foobar", headers:, params: {} @@ -352,7 +388,7 @@ RSpec.describe SalesLogsController, type: :request do end end - context "when there are less than 20 logs" do + context "when there are fewer than 20 logs" do before do get "/sales-logs", headers:, params: {} end @@ -469,5 +505,151 @@ RSpec.describe SalesLogsController, type: :request do page.assert_selector(".govuk-button", text: "Create a new lettings log", count: 0) end end + + context "when requesting CSV download" do + let(:headers) { { "Accept" => "text/html" } } + let(:search_term) { "foot" } + let(:codes_only) { false } + + before do + allow(user).to receive(:need_two_factor_authentication?).and_return(false) + sign_in user + get "/sales-logs/csv-download?search=#{search_term}&codes_only=#{codes_only}", headers: + end + + it "returns http success" do + expect(response).to have_http_status(:success) + end + + it "shows a confirmation button" do + expect(page).to have_button("Send email") + end + + it "has a hidden field with the search term" do + expect(page).to have_field("search", type: "hidden", with: search_term) + end + + context "when user is not support" do + context "and export type is not codes only" do + it "has a hidden field with the export type" do + expect(page).to have_field("codes_only", type: "hidden", with: codes_only) + end + end + + context "and export type is codes only" do + let(:codes_only) { true } + + it "the user is not authorised" do + expect(response).to have_http_status(:unauthorized) + end + end + end + + context "when user is support" do + let(:user) { FactoryBot.create(:user, :support) } + + context "and export type is not codes only" do + it "has a hidden field with the export type" do + expect(page).to have_field("codes_only", type: "hidden", with: codes_only) + end + end + + context "and export type is codes only" do + let(:codes_only) { true } + + it "has a hidden field with the export type" do + expect(page).to have_field("codes_only", type: "hidden", with: codes_only) + end + end + end + end + + context "when confirming the CSV email" do + let(:headers) { { "Accept" => "text/html" } } + + it "confirms that the user will receive an email with the requested CSV" do + sign_in user + get "/sales-logs/csv-confirmation" + expect(CGI.unescape_html(response.body)).to include("We’re sending you an email") + end + end + end + + describe "POST #email-csv" do + let(:other_organisation) { FactoryBot.create(:organisation) } + let(:user) { FactoryBot.create(:user, :support) } + let!(:sales_log) do + FactoryBot.create( + :sales_log, + created_by: user, + ) + end + + before do + allow(user).to receive(:need_two_factor_authentication?).and_return(false) + sign_in user + FactoryBot.create(:sales_log) + FactoryBot.create(:sales_log, + :completed, + owning_organisation:, + created_by: user) + end + + it "creates an E-mail job with the correct log type" do + expect { + post "/sales-logs/email-csv?codes_only=true", headers:, params: {} + }.to enqueue_job(EmailCsvJob).with(user, nil, {}, false, nil, true, "sales") + end + + it "redirects to the confirmation page" do + post "/sales-logs/email-csv?codes_only=true", headers:, params: {} + expect(response).to redirect_to(csv_confirmation_sales_logs_path) + end + + it "passes the search term" do + expect { + post "/sales-logs/email-csv?search=#{sales_log.id}&codes_only=false", headers:, params: {} + }.to enqueue_job(EmailCsvJob).with(user, sales_log.id.to_s, {}, false, nil, false, "sales") + end + + it "passes filter parameters" do + expect { + post "/sales-logs/email-csv?status[]=completed&codes_only=true", headers:, params: {} + }.to enqueue_job(EmailCsvJob).with(user, nil, { "status" => %w[completed] }, false, nil, true, "sales") + end + + it "passes export type flag" do + expect { + post "/sales-logs/email-csv?codes_only=true", headers:, params: {} + }.to enqueue_job(EmailCsvJob).with(user, nil, {}, false, nil, true, "sales") + expect { + post "/sales-logs/email-csv?codes_only=false", headers:, params: {} + }.to enqueue_job(EmailCsvJob).with(user, nil, {}, false, nil, false, "sales") + end + + it "passes a combination of search term, export type and filter parameters" do + postcode = "XX1 1TG" + + expect { + post "/sales-logs/email-csv?status[]=completed&search=#{postcode}&codes_only=false", headers:, params: {} + }.to enqueue_job(EmailCsvJob).with(user, postcode, { "status" => %w[completed] }, false, nil, false, "sales") + end + + context "when the user is not a support user" do + let(:user) { FactoryBot.create(:user, :data_coordinator) } + + it "has permission to download human readable csv" do + codes_only_export = false + expect { + post "/sales-logs/email-csv?codes_only=#{codes_only_export}", headers:, params: {} + }.to enqueue_job(EmailCsvJob).with(user, nil, {}, false, nil, false, "sales") + end + + it "is not authorized to download codes only csv" do + codes_only_export = true + post "/sales-logs/email-csv?codes_only=#{codes_only_export}", headers:, params: {} + expect(response).to have_http_status(:unauthorized) + end + end end end diff --git a/spec/services/bulk_upload/lettings/validator_spec.rb b/spec/services/bulk_upload/lettings/validator_spec.rb index 10adcb86a..3ba36dfc0 100644 --- a/spec/services/bulk_upload/lettings/validator_spec.rb +++ b/spec/services/bulk_upload/lettings/validator_spec.rb @@ -208,7 +208,7 @@ RSpec.describe BulkUpload::Lettings::Validator do error = BulkUploadError.find_by(row: "7", field: "field_96", category: "setup") expect(error.field).to eql("field_96") - expect(error.error).to eql("You must answer tenancy start date") + expect(error.error).to eql("You must answer tenancy start date (day)") expect(error.tenant_code).to eql("123") expect(error.property_ref).to be_nil expect(error.row).to eql("7") diff --git a/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb index dd9bf4a5b..0587c0f51 100644 --- a/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb @@ -303,9 +303,9 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do let(:attributes) { { bulk_upload:, field_7: "123" } } it "has errors on setup fields" do - errors = parser.errors.select { |e| e.options[:category] == :setup }.map(&:attribute) + errors = parser.errors.select { |e| e.options[:category] == :setup }.map(&:attribute).sort - expect(errors).to eql(%i[field_1 field_98 field_97 field_96 field_111 field_113 field_132]) + expect(errors).to eql(%i[field_1 field_111 field_113 field_132 field_96 field_97 field_98]) end end @@ -403,15 +403,13 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do end end - describe "#field_4" do + describe "#field_4" do # mangement group code context "when nullable not permitted" do let(:bulk_upload) { create(:bulk_upload, :lettings, user:, needstype: 2) } let(:attributes) { { bulk_upload:, field_1: "2", field_4: nil } } it "cannot be nulled" do - setup_errors = parser.errors.select { |e| e.options[:category] == "setup" } - - expect(setup_errors.find { |e| e.attribute == :field_4 }).to be_present + expect(parser.errors.where(:field_4, category: :setup).map(&:message)).to eql(["You must answer management group code"]) end end @@ -427,7 +425,7 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do let(:attributes) { { bulk_upload:, field_1: "1", field_4: "123" } } it "returns an error" do - expect(parser.errors[:field_4]).to be_present + expect(parser.errors.where(:field_4, category: :setup)).to be_present end end @@ -436,7 +434,7 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do let(:attributes) { { bulk_upload:, field_1: "1", field_4: other_scheme.old_visible_id, field_111: owning_org.old_visible_id } } it "returns an error" do - expect(parser.errors[:field_4]).to include("This management group code does not belong to your organisation, or any of your stock owners / managing agents") + expect(parser.errors.where(:field_4, category: :setup).map(&:message)).to include("This management group code does not belong to your organisation, or any of your stock owners / managing agents") end end @@ -459,15 +457,13 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do end end - describe "#field_5" do + describe "#field_5" do # scheme code context "when not nullable" do let(:bulk_upload) { create(:bulk_upload, :lettings, user:, needstype: 2) } let(:attributes) { { bulk_upload:, field_1: "2", field_5: nil } } it "cannot be nulled" do - setup_errors = parser.errors.select { |e| e.options[:category] == "setup" } - - expect(setup_errors.find { |e| e.attribute == :field_5 }).to be_present + expect(parser.errors.where(:field_5, category: :setup).map(&:message)).to eql(["You must answer scheme code"]) end end @@ -517,8 +513,8 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do } end - it "returns an error" do - expect(parser.errors[:field_5]).to be_present + it "returns as setup error" do + expect(parser.errors.where(:field_5, category: :setup).map(&:message)).to eql(["Location could be found with provided scheme code"]) end end end @@ -715,21 +711,21 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do let(:attributes) { { bulk_upload:, field_1: "1", field_96: nil, field_97: nil, field_98: nil } } it "returns them as setup errors" do - setup_errors = parser.errors.select { |e| e.options[:category] == :setup } - - expect(setup_errors.find { |e| e.attribute == :field_96 }).to be_present - expect(setup_errors.find { |e| e.attribute == :field_97 }).to be_present - expect(setup_errors.find { |e| e.attribute == :field_98 }).to be_present + expect(parser.errors.where(:field_96, category: :setup)).to be_present + expect(parser.errors.where(:field_97, category: :setup)).to be_present + expect(parser.errors.where(:field_98, category: :setup)).to be_present end end - context "when one of these fields is blank" do + context "when one of these fields is blank", aggregate_failures: true do let(:attributes) { { bulk_upload:, field_1: "1", field_96: "1", field_97: "1", field_98: nil } } it "returns an error only on blank field" do - expect(parser.errors[:field_96]).to be_blank - expect(parser.errors[:field_97]).to be_blank - expect(parser.errors[:field_98]).to be_present + expect(parser.errors.where(:field_96, category: :setup)).to be_blank + expect(parser.errors.where(:field_97, category: :setup)).to be_blank + + expect(parser.errors.where(:field_98).map(&:message)).to eql(["You must answer tenancy start date (year)"]) + expect(parser.errors.where(:field_98, category: :setup).map(&:message)).to eql(["You must answer tenancy start date (year)"]) end end @@ -737,7 +733,7 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do let(:attributes) { { bulk_upload:, field_98: "2022" } } it "returns an error" do - expect(parser.errors[:field_98]).to include("Tenancy start year must be 2 digits") + expect(parser.errors.where(:field_98, category: :setup).map(&:message)).to include("Tenancy start year must be 2 digits") end end @@ -774,9 +770,9 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do let(:bulk_upload) { create(:bulk_upload, :lettings, user:, year: 2022) } it "returns errors" do - expect(parser.errors[:field_96]).to be_present - expect(parser.errors[:field_97]).to be_present - expect(parser.errors[:field_98]).to be_present + expect(parser.errors.where(:field_96, category: :setup)).to be_present + expect(parser.errors.where(:field_97, category: :setup)).to be_present + expect(parser.errors.where(:field_98, category: :setup)).to be_present end end end @@ -788,7 +784,7 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do it "is not permitted as setup error" do setup_errors = parser.errors.select { |e| e.options[:category] == :setup } - expect(setup_errors.find { |e| e.attribute == :field_111 }.message).to eql("The owning organisation code is incorrect") + expect(setup_errors.find { |e| e.attribute == :field_111 }.message).to eql("You must answer owning organisation") end it "blocks log creation" do @@ -897,12 +893,10 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do describe "#field_113" do # managing org context "when blank" do - let(:attributes) { { bulk_upload:, field_113: "", field_4: 1 } } + let(:attributes) { setup_section_params.merge(field_113: nil) } it "is not permitted as setup error" do - setup_errors = parser.errors.select { |e| e.options[:category] == :setup } - - expect(setup_errors.find { |e| e.attribute == :field_113 }.message).to eql("The managing organisation code is incorrect") + expect(parser.errors.where(:field_113, category: :setup).map(&:message)).to eql(["You must answer managing organisation"]) end it "blocks log creation" do @@ -954,6 +948,50 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do end end end + + describe "#field_134" do # renewal + context "when none possible option selected" do + let(:attributes) { setup_section_params.merge({ field_134: "101" }) } + + it "adds a setup error" do + expect(parser.errors.where(:field_134, category: :setup).map(&:message)).to include("Enter a valid value for Is this letting a renewal?") + end + end + end + + describe "#field_55 - #field_60" do + context "when all blank" do + let(:attributes) { setup_section_params.merge({ field_55: nil, field_56: nil, field_57: nil, field_58: nil, field_59: nil, field_60: nil }) } + + it "adds errors to correct fields" do + expect(parser.errors[:field_55]).to be_present + expect(parser.errors[:field_56]).to be_present + expect(parser.errors[:field_57]).to be_present + expect(parser.errors[:field_58]).to be_present + expect(parser.errors[:field_59]).to be_present + end + end + end + + describe "soft validations" do + context "when soft validation is triggered" do + let(:attributes) { setup_section_params.merge({ field_12: 22, field_35: 5 }) } + + it "adds an error to the relevant fields" do + soft_validation_errors = parser.errors.select { |e| e.options[:category] == :soft_validation } + + expect(soft_validation_errors.find { |e| e.attribute == :field_12 }).to be_present + expect(soft_validation_errors.find { |e| e.attribute == :field_35 }).to be_present + end + + it "populates with correct error message" do + soft_validation_errors = parser.errors.select { |e| e.options[:category] == :soft_validation } + + expect(soft_validation_errors.find { |e| e.attribute == :field_12 }.message).to eql("You told us this person is aged 22 years and retired.") + expect(soft_validation_errors.find { |e| e.attribute == :field_35 }.message).to eql("You told us this person is aged 22 years and retired.") + end + end + end end describe "#log" do diff --git a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb index 58b216e43..d881b972b 100644 --- a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb @@ -31,6 +31,18 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do before do create(:organisation_relationship, parent_organisation: owning_org, child_organisation: managing_org) + + LaRentRange.create!( + ranges_rent_id: "1", + la: "E09000008", + beds: 1, + lettype: 3, + soft_min: 12.41, + soft_max: 118.85, + hard_min: 9.87, + hard_max: 200.99, + start_year: 2023, + ) end around do |example| @@ -489,8 +501,8 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do context "when matching scheme cannot be found" do let(:attributes) { { bulk_upload:, field_5: "1", field_16: "123" } } - it "returns an error" do - expect(parser.errors[:field_16]).to be_present + it "returns a setup error" do + expect(parser.errors.where(:field_16, category: :setup)).to be_present end end @@ -498,8 +510,8 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do let(:other_scheme) { create(:scheme, :with_old_visible_id) } let(:attributes) { { bulk_upload:, field_5: "1", field_16: other_scheme.old_visible_id, field_1: owning_org.old_visible_id } } - it "returns an error" do - expect(parser.errors[:field_16]).to include("This management group code does not belong to your organisation, or any of your stock owners / managing agents") + it "returns a setup error" do + expect(parser.errors.where(:field_16, category: :setup).map(&:message)).to include("This management group code does not belong to your organisation, or any of your stock owners / managing agents") end end @@ -535,8 +547,8 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do } end - it "returns an error" do - expect(parser.errors[:field_17]).to be_present + it "returns a setup error" do + expect(parser.errors.where(:field_17, category: :setup)).to be_present end end @@ -569,8 +581,8 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do } end - it "returns an error" do - expect(parser.errors[:field_17]).to be_present + it "returns a setup error" do + expect(parser.errors.where(:field_17, category: :setup)).to be_present end end end @@ -663,6 +675,20 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do end end + describe "#field_83 - #field_88" do + context "when all blank" do + let(:attributes) { setup_section_params.merge({ field_83: nil, field_84: nil, field_85: nil, field_86: nil, field_87: nil, field_88: nil }) } + + it "adds errors to correct fields" do + expect(parser.errors[:field_83]).to be_present + expect(parser.errors[:field_84]).to be_present + expect(parser.errors[:field_85]).to be_present + expect(parser.errors[:field_86]).to be_present + expect(parser.errors[:field_87]).to be_present + end + end + end + describe "#field_119" do # referral context "when 3 ie PRP nominated by LA and owning org is LA" do let(:attributes) { { bulk_upload:, field_119: "3", field_1: owning_org.old_visible_id } } @@ -705,8 +731,6 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do let(:attributes) { { bulk_upload:, field_5: "1", field_7: nil, field_8: nil, field_9: nil } } it "returns an error" do - parser.valid? - expect(parser.errors[:field_7]).to be_present expect(parser.errors[:field_8]).to be_present expect(parser.errors[:field_9]).to be_present @@ -717,8 +741,6 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do let(:attributes) { { bulk_upload:, field_9: "2022" } } it "returns an error" do - parser.valid? - expect(parser.errors[:field_9]).to include("Tenancy start year must be 2 digits") end end @@ -737,8 +759,6 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do let(:bulk_upload) { create(:bulk_upload, :lettings, user:, year: 2022) } it "does not return errors" do - parser.valid? - expect(parser.errors[:field_7]).not_to be_present expect(parser.errors[:field_8]).not_to be_present expect(parser.errors[:field_9]).not_to be_present @@ -750,19 +770,16 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do Timecop.freeze(Date.new(2022, 4, 2)) do example.run end - Timecop.return end let(:attributes) { { bulk_upload:, field_7: "1", field_8: "1", field_9: "22" } } let(:bulk_upload) { create(:bulk_upload, :lettings, user:, year: 2022) } - it "returns errors" do - parser.valid? - - expect(parser.errors[:field_7]).to be_present - expect(parser.errors[:field_8]).to be_present - expect(parser.errors[:field_9]).to be_present + it "returns setup errors" do + expect(parser.errors.where(:field_7, category: :setup)).to be_present + expect(parser.errors.where(:field_8, category: :setup)).to be_present + expect(parser.errors.where(:field_9, category: :setup)).to be_present end end end @@ -772,9 +789,7 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do let(:attributes) { { bulk_upload:, field_1: "", field_4: 1 } } it "is not permitted as setup error" do - setup_errors = parser.errors.select { |e| e.options[:category] == :setup } - - expect(setup_errors.find { |e| e.attribute == :field_1 }.message).to eql("The owning organisation code is incorrect") + expect(parser.errors.where(:field_1, category: :setup).map(&:message)).to eql(["You must answer owning organisation"]) end it "blocks log creation" do @@ -875,16 +890,12 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do end end - describe "#field_4" do + describe "#field_4" do # needs type context "when blank" do let(:attributes) { { bulk_upload:, field_4: nil, field_13: "123" } } it "is reported as a setup error" do - errors = parser.errors.select { |e| e.options[:category] == :setup } - error = errors.find { |e| e.attribute == :field_4 } - - expect(error).to be_present - expect(error.type).to eql("You must answer needs type") + expect(parser.errors.where(:field_4, category: :setup).map(&:message)).to eql(["You must answer needs type"]) end end end @@ -893,8 +904,16 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do context "when blank" do let(:attributes) { { bulk_upload:, field_1: owning_org.old_visible_id, field_6: "" } } - it "has errors on the field" do - expect(parser.errors[:field_6]).to include("You must answer property renewal") + it "has setup errors on the field" do + expect(parser.errors.where(:field_6, category: :setup).map(&:message)).to eql(["You must answer property renewal"]) + end + end + + context "when none possible option selected" do + let(:attributes) { setup_section_params.merge({ field_6: "101" }) } + + it "adds a setup error" do + expect(parser.errors.where(:field_6, category: :setup).map(&:message)).to include("Enter a valid value for Is this letting a renewal?") end end end @@ -984,6 +1003,43 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do end end end + + describe "soft validations" do + context "when soft validation is triggered" do + let(:attributes) { setup_section_params.merge({ field_46: 22, field_50: 5 }) } + + it "adds an error to the relevant fields" do + soft_validation_errors = parser.errors.select { |e| e.options[:category] == :soft_validation } + + expect(soft_validation_errors.find { |e| e.attribute == :field_46 }).to be_present + expect(soft_validation_errors.find { |e| e.attribute == :field_50 }).to be_present + end + + it "populates with correct error message" do + soft_validation_errors = parser.errors.select { |e| e.options[:category] == :soft_validation } + + expect(soft_validation_errors.find { |e| e.attribute == :field_46 }.message).to eql("You told us this person is aged 22 years and retired.") + expect(soft_validation_errors.find { |e| e.attribute == :field_50 }.message).to eql("You told us this person is aged 22 years and retired.") + end + end + + context "when soft validation is triggered and not required" do + let(:attributes) { setup_section_params.merge({ field_128: 120, field_126: 1, field_32: 1, field_4: 1, field_5: "3", field_25: "E09000008" }) } + + it "adds an error to the relevant fields" do + soft_validation_errors = parser.errors.select { |e| e.options[:category] == :soft_validation } + + expect(soft_validation_errors.find { |e| e.attribute == :field_128 }).to be_present + end + + it "populates with correct error message" do + soft_validation_errors = parser.errors.select { |e| e.options[:category] == :soft_validation } + + expect(soft_validation_errors.count { |e| e.attribute == :field_128 }).to be(1) + expect(soft_validation_errors.find { |e| e.attribute == :field_128 }.message).to eql("You told us the rent is £120.00 every week. The maximum rent expected for this type of property in this local authority is ££118.85 every week.") + end + end + end end describe "#log" do diff --git a/spec/services/bulk_upload/sales/year2022/row_parser_spec.rb b/spec/services/bulk_upload/sales/year2022/row_parser_spec.rb index bd5520ea7..7fdf8da4d 100644 --- a/spec/services/bulk_upload/sales/year2022/row_parser_spec.rb +++ b/spec/services/bulk_upload/sales/year2022/row_parser_spec.rb @@ -506,17 +506,36 @@ RSpec.describe BulkUpload::Sales::Year2022::RowParser do Timecop.freeze(Date.new(2022, 4, 2)) do example.run end - Timecop.return end let(:attributes) { setup_section_params.merge({ field_2: "1", field_3: "1", field_4: "22" }) } let(:bulk_upload) { create(:bulk_upload, :sales, user:, year: 2022) } - it "returns errors" do - expect(parser.errors[:field_2]).to be_present - expect(parser.errors[:field_3]).to be_present - expect(parser.errors[:field_4]).to be_present + it "returns setup errors" do + expect(parser.errors.where(:field_2, category: :setup)).to be_present + expect(parser.errors.where(:field_3, category: :setup)).to be_present + expect(parser.errors.where(:field_4, category: :setup)).to be_present + end + end + end + + describe "soft validations" do + context "when soft validation is triggered" do + let(:attributes) { valid_attributes.merge({ field_7: 22, field_24: 5 }) } + + it "adds an error to the relevant fields" do + soft_validation_errors = parser.errors.select { |e| e.options[:category] == :soft_validation } + + expect(soft_validation_errors.find { |e| e.attribute == :field_7 }).to be_present + expect(soft_validation_errors.find { |e| e.attribute == :field_24 }).to be_present + end + + it "populates with correct error message" do + soft_validation_errors = parser.errors.select { |e| e.options[:category] == :soft_validation } + + expect(soft_validation_errors.find { |e| e.attribute == :field_7 }.message).to eql("You told us this person is aged 22 years and retired.") + expect(soft_validation_errors.find { |e| e.attribute == :field_24 }.message).to eql("You told us this person is aged 22 years and retired.") end end end diff --git a/spec/services/csv/sales_log_csv_service_spec.rb b/spec/services/csv/sales_log_csv_service_spec.rb new file mode 100644 index 000000000..96099aa70 --- /dev/null +++ b/spec/services/csv/sales_log_csv_service_spec.rb @@ -0,0 +1,189 @@ +require "rails_helper" + +RSpec.describe Csv::SalesLogCsvService do + let(:form_handler_mock) { instance_double(FormHandler) } + let(:organisation) { create(:organisation) } + let!(:log) { create(:sales_log, :completed, owning_organisation: organisation) } + let(:service) { described_class.new(export_type: "labels") } + let(:csv) { CSV.parse(service.prepare_csv(SalesLog.all)) } + + it "calls the form handler to get all questions in order when initialized" do + allow(FormHandler).to receive(:instance).and_return(form_handler_mock) + allow(form_handler_mock).to receive(:ordered_sales_questions_for_all_years).and_return([]) + described_class.new(export_type: "codes") + expect(form_handler_mock).to have_received(:ordered_sales_questions_for_all_years) + end + + it "returns a string" do + result = service.prepare_csv(SalesLog.all) + expect(result).to be_a String + end + + it "returns a csv with headers" do + expect(csv.first.first).to eq "id" + expect(csv.second.first).not_to be log.id.to_s + end + + context "when stubbing :ordered_sales_questions_for_all_years" do + let(:sales_form) do + FormFactory.new(year: 1936, type: "sales") + .with_sections([build(:section, :with_questions, question_ids:, questions:)]) + .build + end + let(:question_ids) { nil } + let(:questions) { nil } + + before do + allow(FormHandler).to receive(:instance).and_return(form_handler_mock) + allow(form_handler_mock).to receive(:form_name_from_start_year) + allow(form_handler_mock).to receive(:get_form).and_return(sales_form) + allow(form_handler_mock).to receive(:ordered_sales_questions_for_all_years).and_return(sales_form.questions) + end + + context "when it returns questions with particular ids" do + let(:question_ids) { %w[type age1 buy1livein exdate] } + + it "includes log attributes related to questions to the headers" do + headers = csv.first + expect(headers).to include(*question_ids.first(3)) + end + + it "removes some log attributes related to questions from the headers and replaces them with their derived values in the correct order" do + headers = csv.first + expect(headers).not_to include "exdate" + expect(headers.last(4)).to eq %w[buy1livein exday exmonth exyear] + end + end + + context "when it returns questions with particular features" do + let(:questions) do + [ + build(:question, id: "attribute_value_check", type: "interruption_screen"), + build(:question, id: "something_or_other_known", type: "radio"), + build(:question, id: "whatchamacallit_asked", type: "radio"), + build(:question, id: "ownershipsch"), + build(:question, id: "checkbox_question", type: "checkbox", answer_options: { "pregyrha" => {}, "pregother" => {} }), + build(:question, id: "type"), + ] + end + + it "does not add questions for checks, whether some other attribute is known or whether something else was asked" do + headers = csv.first + expect(headers).not_to include "attribute_value_check" + expect(headers).not_to include "something_or_other_known" + expect(headers).not_to include "whatchamacallit_asked" + end + + it "does not add the id of checkbox questions, but adds the related attributes of the log in the correct order" do + headers = csv.first + expect(headers.last(4)).to eq %w[ownershipsch pregyrha pregother type] + end + end + end + + it "includes attributes not related to questions to the headers" do + headers = csv.first + expect(headers).to include(*%w[id status created_at updated_at old_id]) + end + + it "returns a csv with the correct number of logs" do + create_list(:sales_log, 15) + log_count = SalesLog.count + expected_row_count_with_headers = log_count + 1 + expect(csv.size).to be expected_row_count_with_headers + end + + context "when exporting with human readable labels" do + it "gives answers to radio questions as their labels" do + national_column_index = csv.first.index("national") + national_value = csv.second[national_column_index] + expect(national_value).to eq "United Kingdom" + relat2_column_index = csv.first.index("relat2") + relat2_value = csv.second[relat2_column_index] + expect(relat2_value).to eq "Partner" + end + + it "gives answers to free input questions as the user input" do + age1_column_index = csv.first.index("age1") + age1_value = csv.second[age1_column_index] + expect(age1_value).to eq 30.to_s + postcode_part1, postcode_part2 = log.postcode_full.split + postcode_part1_column_index = csv.first.index("pcode1") + postcode_part1_value = csv.second[postcode_part1_column_index] + expect(postcode_part1_value).to eq postcode_part1 + postcode_part2_column_index = csv.first.index("pcode2") + postcode_part2_value = csv.second[postcode_part2_column_index] + expect(postcode_part2_value).to eq postcode_part2 + end + + it "exports the code for the local authority under the heading 'la'" do + la_column_index = csv.first.index("la") + la_value = csv.second[la_column_index] + expect(la_value).to eq "E09000003" + end + + it "exports the label for the local authority under the heading 'la_label'" do + la_label_column_index = csv.first.index("la_label") + la_label_value = csv.second[la_label_column_index] + expect(la_label_value).to eq "Barnet" + end + + it "exports the CSV with all values correct" do + expected_content = CSV.read("spec/fixtures/files/sales_logs_csv_export_labels.csv") + values_to_delete = %w[id] + values_to_delete.each do |attribute| + index = csv.first.index(attribute) + csv.second[index] = nil + end + expect(csv).to eq expected_content + end + end + + context "when exporting values as codes" do + let(:service) { described_class.new(export_type: "codes") } + + it "gives answers to radio questions as their codes" do + national_column_index = csv.first.index("national") + national_value = csv.second[national_column_index] + expect(national_value).to eq 18.to_s + relat2_column_index = csv.first.index("relat2") + relat2_value = csv.second[relat2_column_index] + expect(relat2_value).to eq "P" + end + + it "gives answers to free input questions as the user input" do + age1_column_index = csv.first.index("age1") + age1_value = csv.second[age1_column_index] + expect(age1_value).to eq 30.to_s + postcode_part1, postcode_part2 = log.postcode_full.split + postcode_part1_column_index = csv.first.index("pcode1") + postcode_part1_value = csv.second[postcode_part1_column_index] + expect(postcode_part1_value).to eq postcode_part1 + postcode_part2_column_index = csv.first.index("pcode2") + postcode_part2_value = csv.second[postcode_part2_column_index] + expect(postcode_part2_value).to eq postcode_part2 + end + + it "exports the code for the local authority under the heading 'la'" do + la_column_index = csv.first.index("la") + la_value = csv.second[la_column_index] + expect(la_value).to eq "E09000003" + end + + it "exports the label for the local authority under the heading 'la_label'" do + la_label_column_index = csv.first.index("la_label") + la_label_value = csv.second[la_label_column_index] + expect(la_label_value).to eq "Barnet" + end + + it "exports the CSV with all values correct" do + expected_content = CSV.read("spec/fixtures/files/sales_logs_csv_export_codes.csv") + values_to_delete = %w[id] + values_to_delete.each do |attribute| + index = csv.first.index(attribute) + csv.second[index] = nil + end + expect(csv).to eq expected_content + end + end +end diff --git a/spec/services/imports/lettings_logs_import_service_spec.rb b/spec/services/imports/lettings_logs_import_service_spec.rb index ec41ba366..6132890a1 100644 --- a/spec/services/imports/lettings_logs_import_service_spec.rb +++ b/spec/services/imports/lettings_logs_import_service_spec.rb @@ -1173,6 +1173,68 @@ RSpec.describe Imports::LettingsLogsImportService do end end + context "and an error is added to tcharge for in progress log" do + let(:lettings_log_id) { "00d2343e-d5fa-4c89-8400-ec3854b0f2b4" } + let(:lettings_log_file) { open_file(fixture_directory, lettings_log_id) } + let(:lettings_log_xml) { Nokogiri::XML(lettings_log_file) } + + before do + lettings_log_xml.at_xpath("//meta:status").content = "saved" + lettings_log_xml.at_xpath("//xmlns:Q18ai").content = "1" + lettings_log_xml.at_xpath("//xmlns:Q18aii").content = "2" + lettings_log_xml.at_xpath("//xmlns:Q18aiii").content = "3" + lettings_log_xml.at_xpath("//xmlns:Q18aiv").content = "3" + lettings_log_xml.at_xpath("//xmlns:Q18av").content = "9" + end + + it "intercepts the relevant validation error" do + expect(logger).to receive(:warn).with("Log 00d2343e-d5fa-4c89-8400-ec3854b0f2b4: Removing field tcharge from log triggering validation: under_10") + lettings_log_service.send(:create_log, lettings_log_xml) + end + + it "clears out the invalid answers" do + allow(logger).to receive(:warn) + + lettings_log_service.send(:create_log, lettings_log_xml) + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + + expect(lettings_log).not_to be_nil + expect(lettings_log.tcharge).to be_nil + expect(lettings_log.brent).to be_nil + expect(lettings_log.scharge).to be_nil + expect(lettings_log.pscharge).to be_nil + expect(lettings_log.supcharg).to be_nil + end + end + + context "and the earnings is not a whole number" do + let(:lettings_log_id) { "00d2343e-d5fa-4c89-8400-ec3854b0f2b4" } + let(:lettings_log_file) { open_file(fixture_directory, lettings_log_id) } + let(:lettings_log_xml) { Nokogiri::XML(lettings_log_file) } + + before do + lettings_log_xml.at_xpath("//meta:status").content = "submitted" + lettings_log_xml.at_xpath("//xmlns:Q8a").content = "1 Weekly" + lettings_log_xml.at_xpath("//xmlns:Q8Money").content = 100.59 + lettings_log_xml.at_xpath("//xmlns:Q8Refused").content = "" + end + + it "does not error" do + expect { lettings_log_service.send(:create_log, lettings_log_xml) } + .not_to raise_error + end + + it "rounds the earnings value" do + allow(logger).to receive(:warn) + + lettings_log_service.send(:create_log, lettings_log_xml) + lettings_log = LettingsLog.find_by(old_id: lettings_log_id) + + expect(lettings_log).not_to be_nil + expect(lettings_log.earnings).to eq(101) + end + end + context "when setting location fields for 23/24 logs" do let(:lettings_log_id) { "00d2343e-d5fa-4c89-8400-ec3854b0f2b4" } let(:lettings_log_file) { open_file(fixture_directory, lettings_log_id) } diff --git a/spec/services/imports/sales_logs_import_service_spec.rb b/spec/services/imports/sales_logs_import_service_spec.rb index c06b7bf42..eb7051a81 100644 --- a/spec/services/imports/sales_logs_import_service_spec.rb +++ b/spec/services/imports/sales_logs_import_service_spec.rb @@ -528,7 +528,7 @@ RSpec.describe Imports::SalesLogsImportService do end it "intercepts the relevant validation error" do - expect(logger).to receive(:warn).with(/Log shared_ownership_sales_log: Removing equity as the equity is invalid/) + expect(logger).to receive(:warn).with(/Removing equity with error: The maximum initial equity stake is 75%/) expect { sales_log_service.send(:create_log, sales_log_xml) } .not_to raise_error end @@ -552,7 +552,7 @@ RSpec.describe Imports::SalesLogsImportService do end it "intercepts the relevant validation error" do - expect(logger).to receive(:warn).with(/Log shared_ownership_sales_log: Removing equity as the equity is invalid/) + expect(logger).to receive(:warn).with(/Removing equity with error: The minimum initial equity stake for this type of shared ownership sale is 25%/) expect { sales_log_service.send(:create_log, sales_log_xml) } .not_to raise_error end @@ -595,6 +595,33 @@ RSpec.describe Imports::SalesLogsImportService do end end + context "and it has a record with previous postcode in invalid format" do + let(:sales_log_id) { "shared_ownership_sales_log" } + + before do + sales_log_xml.at_xpath("//xmlns:Q7Postcode").content = "L3132AF" + sales_log_xml.at_xpath("//xmlns:Q7ONSLACode").content = "E07000223" + end + + it "intercepts the relevant validation error" do + expect(logger).to receive(:warn).with(/Log shared_ownership_sales_log: Removing previous postcode as the postcode is invalid/) + expect { sales_log_service.send(:create_log, sales_log_xml) } + .not_to raise_error + end + + it "clears out the invalid answers and sets correct prevloc" do + allow(logger).to receive(:warn) + + sales_log_service.send(:create_log, sales_log_xml) + sales_log = SalesLog.find_by(old_id: sales_log_id) + + expect(sales_log).not_to be_nil + expect(sales_log.ppostcode_full).to be_nil + expect(sales_log.ppcodenk).to eq(1) # not known + expect(sales_log.prevloc).to eq("E07000223") # not known + end + end + context "and setup field has validation error in incomplete log" do let(:sales_log_id) { "shared_ownership_sales_log" } @@ -631,7 +658,8 @@ RSpec.describe Imports::SalesLogsImportService do end it "intercepts the relevant validation error" do - expect(logger).to receive(:warn).with(/Removing previous postcode known and previous postcode as the postcode is invalid/) + expect(logger).to receive(:warn).with(/Removing ppcodenk with error: Buyer's last accommodation and discounted ownership postcodes must match, Last settled accommodation and discounted ownership property postcodes must match/) + expect(logger).to receive(:warn).with(/Removing ppostcode_full with error: Buyer's last accommodation and discounted ownership postcodes must match, Last settled accommodation and discounted ownership property postcodes must match/) expect { sales_log_service.send(:create_log, sales_log_xml) } .not_to raise_error end @@ -701,7 +729,7 @@ RSpec.describe Imports::SalesLogsImportService do end it "intercepts the relevant validation error" do - expect(logger).to receive(:warn).with(/Removing exchange date as the exchange date is invalid/) + expect(logger).to receive(:warn).with(/Removing exdate with error: Contract exchange date must be less than 1 year before sale completion date/) expect { sales_log_service.send(:create_log, sales_log_xml) } .not_to raise_error end @@ -726,7 +754,7 @@ RSpec.describe Imports::SalesLogsImportService do end it "intercepts the relevant validation error" do - expect(logger).to receive(:warn).with(/Removing mortgage because it cannot be 0/) + expect(logger).to receive(:warn).with(/Removing mortgage with error: Mortgage amount must be at least £1, Mortgage value cannot be £0 if a mortgage was used for the purchase of this property/) expect { sales_log_service.send(:create_log, sales_log_xml) } .not_to raise_error end @@ -742,7 +770,7 @@ RSpec.describe Imports::SalesLogsImportService do end end - context "and it has an invalid income" do + context "and it has an invalid income 1" do let(:sales_log_id) { "shared_ownership_sales_log" } before do @@ -751,7 +779,58 @@ RSpec.describe Imports::SalesLogsImportService do end it "intercepts the relevant validation error" do - expect(logger).to receive(:warn).with(/Removing income1 as the income1 is invalid/) + expect(logger).to receive(:warn).with(/Removing income1 with error: Income must be £80,000 or lower for properties outside London local authority/) + expect { sales_log_service.send(:create_log, sales_log_xml) } + .not_to raise_error + end + + it "clears out the invalid answers" do + allow(logger).to receive(:warn) + + sales_log_service.send(:create_log, sales_log_xml) + sales_log = SalesLog.find_by(old_id: sales_log_id) + + expect(sales_log).not_to be_nil + expect(sales_log.income1).to be_nil + end + end + + context "and it has an invalid income 2" do + let(:sales_log_id) { "shared_ownership_sales_log" } + + before do + sales_log_xml.at_xpath("//xmlns:Q2Person1Income").content = "0" + sales_log_xml.at_xpath("//xmlns:Q2Person2Income").content = "95000" + sales_log_xml.at_xpath("//xmlns:Q14ONSLACode").content = "E07000223" + end + + it "intercepts the relevant validation error" do + expect(logger).to receive(:warn).with(/Removing income2 with error: Combined income must be £80,000 or lower for properties outside London local authorities, Income must be £80,000 or lower for properties outside London local authority/) + expect { sales_log_service.send(:create_log, sales_log_xml) } + .not_to raise_error + end + + it "clears out the invalid answers" do + allow(logger).to receive(:warn) + + sales_log_service.send(:create_log, sales_log_xml) + sales_log = SalesLog.find_by(old_id: sales_log_id) + + expect(sales_log).not_to be_nil + expect(sales_log.income2).to be_nil + end + end + + context "and it has an invalid income 1 for london" do + let(:sales_log_id) { "shared_ownership_sales_log" } + + before do + sales_log_xml.at_xpath("//xmlns:Q2Person1Income").content = "95000" + sales_log_xml.at_xpath("//xmlns:Q14ONSLACode").content = "E09000012" + end + + it "intercepts the relevant validation error" do + expect(logger).to receive(:warn).with(/Removing income1 with error: Income must be £90,000 or lower for properties within a London local authority/) expect { sales_log_service.send(:create_log, sales_log_xml) } .not_to raise_error end @@ -767,6 +846,81 @@ RSpec.describe Imports::SalesLogsImportService do end end + context "and it has an invalid income 2 for london" do + let(:sales_log_id) { "shared_ownership_sales_log" } + + before do + sales_log_xml.at_xpath("//xmlns:Q2Person2Income").content = "95000" + sales_log_xml.at_xpath("//xmlns:Q14ONSLACode").content = "E09000012" + end + + it "intercepts the relevant validation error" do + expect(logger).to receive(:warn).with(/Removing income2 with error: Combined income must be £90,000 or lower for properties within a London local authority, Income must be £90,000 or lower for properties within a London local authority/) + expect { sales_log_service.send(:create_log, sales_log_xml) } + .not_to raise_error + end + + it "clears out the invalid answers" do + allow(logger).to receive(:warn) + + sales_log_service.send(:create_log, sales_log_xml) + sales_log = SalesLog.find_by(old_id: sales_log_id) + + expect(sales_log).not_to be_nil + expect(sales_log.income2).to be_nil + end + end + + context "and it has an invalid mscharge" do + let(:sales_log_id) { "shared_ownership_sales_log" } + + before do + sales_log_xml.at_xpath("//xmlns:Q29MonthlyCharges").content = "0.1" + end + + it "intercepts the relevant validation error" do + expect(logger).to receive(:warn).with(/Removing mscharge with error: Monthly leasehold charges must be at least £1/) + expect(logger).to receive(:warn).with(/Removing has_mscharge with error: Monthly leasehold charges must be at least £1/) + expect { sales_log_service.send(:create_log, sales_log_xml) } + .not_to raise_error + end + + it "clears out the invalid answers" do + allow(logger).to receive(:warn) + + sales_log_service.send(:create_log, sales_log_xml) + sales_log = SalesLog.find_by(old_id: sales_log_id) + + expect(sales_log).not_to be_nil + expect(sales_log.mscharge).to be_nil + expect(sales_log.has_mscharge).to be_nil + end + end + + context "and it has an invalid frombeds" do + let(:sales_log_id) { "shared_ownership_sales_log" } + + before do + sales_log_xml.at_xpath("//xmlns:Q20Bedrooms").content = "0" + end + + it "intercepts the relevant validation error" do + expect(logger).to receive(:warn).with(/Removing frombeds with error: Number of bedrooms in previous property must be between 1 and 6/) + expect { sales_log_service.send(:create_log, sales_log_xml) } + .not_to raise_error + end + + it "clears out the invalid answers" do + allow(logger).to receive(:warn) + + sales_log_service.send(:create_log, sales_log_xml) + sales_log = SalesLog.find_by(old_id: sales_log_id) + + expect(sales_log).not_to be_nil + expect(sales_log.frombeds).to be_nil + end + end + context "when inferring default answers for completed sales logs" do context "when the armedforcesspouse is not answered" do let(:sales_log_id) { "discounted_ownership_sales_log" } @@ -817,6 +971,26 @@ RSpec.describe Imports::SalesLogsImportService do end end + context "when the savings is given not to the nearest 10" do + let(:sales_log_id) { "discounted_ownership_sales_log" } + + before do + sales_log_xml.at_xpath("//xmlns:Q3Savings").content = "10013" + allow(logger).to receive(:warn).and_return(nil) + end + + it "does not error" do + expect { sales_log_service.send(:create_log, sales_log_xml) }.not_to raise_error + end + + it "sets savings to the nearest 10" do + sales_log_service.send(:create_log, sales_log_xml) + + sales_log = SalesLog.find_by(old_id: sales_log_id) + expect(sales_log&.savings).to be(10_010) + end + end + context "when inferring age known" do let(:sales_log_id) { "discounted_ownership_sales_log" } diff --git a/webpack.config.js b/webpack.config.js index c027f2dd2..d2f1d8993 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -23,7 +23,6 @@ module.exports = { path.resolve(__dirname, 'node_modules/@hotwired/stimulus'), path.resolve(__dirname, 'node_modules/@stimulus/polyfills'), path.resolve(__dirname, 'node_modules/@rails/actioncable'), - path.resolve(__dirname, 'node_modules/chartjs'), path.resolve(__dirname, 'app/frontend') ], use: ['babel-loader'] diff --git a/yarn.lock b/yarn.lock index 1fcfee856..272885f74 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1964,11 +1964,6 @@ character-parser@^2.2.0: dependencies: is-regex "^1.0.3" -chart.js@^3.6.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.9.1.tgz#3abf2c775169c4c71217a107163ac708515924b8" - integrity sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w== - "chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.1, chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" @@ -4272,9 +4267,9 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== nunjucks@^3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/nunjucks/-/nunjucks-3.2.3.tgz#1b33615247290e94e28263b5d855ece765648a31" - integrity sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ== + version "3.2.4" + resolved "https://registry.yarnpkg.com/nunjucks/-/nunjucks-3.2.4.tgz#f0878eef528ce7b0aa35d67cc6898635fd74649e" + integrity sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ== dependencies: a-sync-waterfall "^1.0.0" asap "^2.0.3" @@ -5955,10 +5950,10 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@^5.70.0: - version "5.74.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.74.0.tgz#02a5dac19a17e0bb47093f2be67c695102a55980" - integrity sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA== +webpack@^5.76.0: + version "5.76.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.76.0.tgz#f9fb9fb8c4a7dbdcd0d56a98e56b8a942ee2692c" + integrity sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^0.0.51"