From b6ff1b9d29c8243e8c507c8be125c0c983a669a8 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Thu, 4 May 2023 14:52:03 +0100 Subject: [PATCH 01/14] CLDC-2060 Add new organisation address question (#1602) * Add new organisation address fields to merge requests * Add new organisation address page * Add new organisation telephone number placeholder page * close the div --- app/controllers/merge_requests_controller.rb | 7 ++ .../new_organisation_address.html.erb | 30 ++++++- ...new_organisation_telephone_number.html.erb | 5 ++ config/routes.rb | 1 + ...0504111352_add_new_organisation_address.rb | 9 +++ db/schema.rb | 5 +- .../merge_requests_controller_spec.rb | 80 +++++++++++++++++++ 7 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 app/views/merge_requests/new_organisation_telephone_number.html.erb create mode 100644 db/migrate/20230504111352_add_new_organisation_address.rb 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/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/config/routes.rb b/config/routes.rb index 48afc3357..359e7e121 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -140,6 +140,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 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/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 From 89c1fc542bf630530efd4ce487813d9de4353604 Mon Sep 17 00:00:00 2001 From: Arthur Campbell <51094020+arfacamble@users.noreply.github.com> Date: Thu, 4 May 2023 16:45:27 +0100 Subject: [PATCH 02/14] ensure that validation errors about tenancy length are added to needstype (#1599) needstype is a field that contributes to the vcalidation and can therefore trigger the validation relevant test file has also been updated in line with this change --- app/models/validations/tenancy_validations.rb | 1 + .../validations/tenancy_validations_spec.rb | 33 ++++++++++--------- 2 files changed, 18 insertions(+), 16 deletions(-) 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/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 From 745e1e7e4bf31e982cfd06999a6f8b7a2e2ebc09 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Thu, 4 May 2023 16:49:33 +0100 Subject: [PATCH 03/14] CLDC-2248 Improve soft validations (#1584) * Update interruption screen page * Update routing and add flash * Only display routed to affected questions * Add affected_question_ids to pregnancy check * lint * Add skip link and lint * Move affected_question_ids to page, because we reuse questions so they might have different affected_question_ids * typo * Fix button wording * Update action href links * Change how we route back to interruption screen * Update affected_question_ids for lettings * Update sales soft validations * Update title texts * Update styling * Update is_referrer_interruption_screen? check and naming * Add interuption screen helper specs * Add request test for fixing soft validation * Add tests for geting soft validation page * Extract interruption screen banner * Update action_href to be reusable * Extract questions out of check answers summary list * Reuse check_answers_summary_list for interruption screen * refactor string parse * Rename attribute * fix test * Add tests for sales paths * typo * Update validation message: void date, major repairs date * Update validation message: rent_value_check, 2022 * Update validation message: buyer live in * Update validation message: staircase * Update validation message: purchase price * Update validation message: income * Update validation message: savings * Update validation message: extra borrowing * Update validation message: extra borrowing * Update validation message: wheelchair * Update validation message: monthly charge * Update validation message: mortgage * Update validation message: old persons shared ownership * Update validation message: discount * Update validation: min retirement * Wording in tests and time test * Update missing question * Refactor is referrer methods * Update validation message: net income * Update validation message: deposit and savings * Update validation message: mortgage, discount and deposit * Fix test validation messages * Fix the retirement check showing on the card 8, fix tests * test * return the user back to the check_your_answers after fixing a validation from check_your_anwers * pr comments --- ...swers_summary_list_card_component.html.erb | 2 +- ...eck_answers_summary_list_card_component.rb | 4 + app/controllers/form_controller.rb | 40 +- app/helpers/form_page_helper.rb | 5 + app/helpers/interruption_screen_helper.rb | 4 + .../pages/care_home_charges_value_check.rb | 4 + ...pregnant_household_lead_age_value_check.rb | 4 + ...gnant_household_lead_hhmemb_value_check.rb | 4 + ..._in_pregnant_household_lead_value_check.rb | 4 + ...egnant_household_person_age_value_check.rb | 4 + ...n_pregnant_household_person_value_check.rb | 4 + ...range_in_pregnant_household_value_check.rb | 4 + ...lead_tenant_over_retirement_value_check.rb | 6 +- ...ead_tenant_under_retirement_value_check.rb | 22 +- .../lettings/pages/max_rent_value_check.rb | 4 + .../lettings/pages/min_rent_value_check.rb | 4 + .../lettings/pages/net_income_value_check.rb | 26 +- ...pregnant_household_lead_age_value_check.rb | 4 + ...gnant_household_lead_hhmemb_value_check.rb | 4 + ...les_pregnant_household_lead_value_check.rb | 4 + ...egnant_household_person_age_value_check.rb | 4 + ...s_pregnant_household_person_value_check.rb | 4 + ..._females_pregnant_household_value_check.rb | 4 + .../person_over_retirement_value_check.rb | 7 +- .../person_under_retirement_value_check.rb | 27 +- .../property_major_repairs_value_check.rb | 9 +- .../lettings/pages/void_date_value_check.rb | 9 +- .../questions/no_retirement_value_check.rb | 6 +- .../questions/retirement_value_check.rb | 6 +- app/models/form/page.rb | 3 +- app/models/form/question.rb | 4 - .../sales/pages/about_price_value_check.rb | 8 +- .../sales/pages/buyer1_income_value_check.rb | 11 +- .../sales/pages/buyer2_income_value_check.rb | 11 +- .../sales/pages/buyer_live_in_value_check.rb | 9 +- .../pages/deposit_and_mortgage_value_check.rb | 4 + .../form/sales/pages/deposit_value_check.rb | 24 +- .../pages/discounted_sale_value_check.rb | 4 + .../pages/extra_borrowing_value_check.rb | 18 +- .../form/sales/pages/grant_value_check.rb | 19 +- .../form/sales/pages/handover_date_check.rb | 4 + .../sales/pages/household_wheelchair_check.rb | 5 + .../pages/monthly_charges_value_check.rb | 15 +- .../form/sales/pages/mortgage_value_check.rb | 18 + ...ld_persons_shared_ownership_value_check.rb | 11 +- .../pages/percentage_discount_value_check.rb | 9 +- .../person_student_not_child_value_check.rb | 4 + .../sales/pages/retirement_value_check.rb | 24 +- .../form/sales/pages/savings_value_check.rb | 19 +- .../shared_ownership_deposit_value_check.rb | 8 +- .../pages/staircase_bought_value_check.rb | 11 +- .../questions/household_wheelchair_check.rb | 2 +- app/models/sales_log.rb | 14 + .../validations/sales/soft_validations.rb | 4 +- app/models/validations/soft_validations.rb | 10 +- .../form/_check_answers_summary_list.html.erb | 4 +- .../form/_interruption_screen_banner.html.erb | 13 + .../_interruption_screen_question.html.erb | 46 ++- app/views/form/check_answers.html.erb | 2 + app/views/form/page.html.erb | 4 +- app/views/form/review.html.erb | 2 +- config/forms/2022_2023.json | 377 ++++++++---------- config/locales/en.yml | 57 ++- ...nswers_summary_list_card_component_spec.rb | 14 +- spec/features/form/validations_spec.rb | 56 ++- spec/fixtures/forms/2021_2022.json | 40 +- spec/helpers/form_page_helper_spec.rb | 28 ++ .../interruption_screen_helper_spec.rb | 23 +- .../care_home_charges_value_check_spec.rb | 4 + .../pages/max_rent_value_check_spec.rb | 4 + .../pages/min_rent_value_check_spec.rb | 4 + .../pages/net_income_value_check_spec.rb | 4 +- ...erson_under_retirement_value_check_spec.rb | 40 +- spec/models/form/question_spec.rb | 5 - .../pages/about_price_value_check_spec.rb | 8 +- .../pages/buyer1_income_value_check_spec.rb | 4 + .../pages/buyer_live_in_value_check_spec.rb | 8 + .../sales/pages/deposit_value_check_spec.rb | 4 + .../pages/discounted_sale_value_check_spec.rb | 4 + .../sales/pages/handover_date_check_spec.rb | 4 + .../pages/household_wheelchair_check_spec.rb | 4 + .../pages/monthly_charges_value_check_spec.rb | 8 +- .../sales/pages/mortgage_value_check_spec.rb | 4 + ...rsons_shared_ownership_value_check_spec.rb | 8 +- .../percentage_discount_value_check_spec.rb | 9 +- ...rson_student_not_child_value_check_spec.rb | 24 ++ .../pages/retirement_value_check_spec.rb | 144 ++----- .../sales/pages/savings_value_check_spec.rb | 4 + ...ared_ownership_deposit_value_check_spec.rb | 6 +- .../household_wheelchair_check_spec.rb | 2 +- .../validations/soft_validations_spec.rb | 44 +- spec/requests/form_controller_spec.rb | 50 +++ 92 files changed, 994 insertions(+), 578 deletions(-) create mode 100644 app/helpers/form_page_helper.rb create mode 100644 app/views/form/_interruption_screen_banner.html.erb create mode 100644 spec/helpers/form_page_helper_spec.rb 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/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/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/sales_log.rb b/app/models/sales_log.rb index 411977ba4..41b7bd5b1 100644 --- a/app/models/sales_log.rb +++ b/app/models/sales_log.rb @@ -234,6 +234,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/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/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/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 4cf4e2c0e..53565b753 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -532,44 +532,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: @@ -578,11 +588,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/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/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/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/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/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/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 From 4e489b53003ef7fef18c4c741ba38a43a0c2e987 Mon Sep 17 00:00:00 2001 From: Arthur Campbell <51094020+arfacamble@users.noreply.github.com> Date: Thu, 4 May 2023 17:26:42 +0100 Subject: [PATCH 04/14] CLDC-2288 ensure derived answers cleared when renewal changed from yes to no (#1596) * add a validation to prevent an inconsistent combination of values and tests for this validation * show related method in diff * remove comment * extract reusable logic from SalesLogVariables to independent module * update lettings log tests around derivations related to renewal * refactor derivation logic to share functionality with sales logs where possible * remove some tests which are now duplicated above, refactor tests using Jack's wonderful :change suggestion, add in tests about logic deriving vacdays that was not previously covered * minor changes after tech review --- .../lettings_log_variables.rb | 55 +- .../derived_variables/sales_log_variables.rb | 30 +- app/models/derived_variables/shared_logic.rb | 27 + app/models/lettings_log.rb | 19 - config/locales/en.yml | 6 +- spec/models/lettings_log_spec.rb | 574 +++++++----------- 6 files changed, 318 insertions(+), 393 deletions(-) create mode 100644 app/models/derived_variables/shared_logic.rb 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..c30c62b9a 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? @@ -30,7 +32,7 @@ module DerivedVariables::SalesLogVariables self.uprn_known = 0 end - set_encoded_derived_values! + set_encoded_derived_values!(DEPENDENCIES) end private @@ -73,30 +75,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/lettings_log.rb b/app/models/lettings_log.rb index 2d91e2ad9..6fce43f42 100644 --- a/app/models/lettings_log.rb +++ b/app/models/lettings_log.rb @@ -531,24 +531,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? @@ -581,7 +563,6 @@ private reset_invalid_unresolved_log_fields! reset_scheme - reset_derived_questions end def dynamically_not_required diff --git a/config/locales/en.yml b/config/locales/en.yml index 53565b753..0e2500dce 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -539,7 +539,7 @@ en: 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: + 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: @@ -571,10 +571,10 @@ en: hint_text: "This is higher than we would expect." shared_ownership_deposit: title_text: "You told us that the mortgage, deposit and discount add up to %{mortgage_deposit_and_discount_total}" - old_persons_shared_ownership: + 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: + 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: 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 From 99cb26b93471d5de2f8f0cb37e15509a522ee76a Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Thu, 4 May 2023 17:57:41 +0100 Subject: [PATCH 05/14] CLDC-2296 Display soft validation errors on bulk upload (#1594) * Add affected_question_ids to pregnancy check * Update is_referrer_interruption_screen? check and naming * Use interruption_screen_question_ids to set soft validation errors on relevant fields * Add soft validations to sales bulk upload * Add soft validations to lettings logs 23/24 bulk upload * Add errors for optional soft validations * Only add soft validations once * Import helper methods * Update test based on new validation messages * Rebase fix --- .../lettings/year2022/row_parser.rb | 19 +++++++ .../lettings/year2023/row_parser.rb | 20 ++++++++ .../bulk_upload/sales/year2022/row_parser.rb | 19 +++++++ .../lettings/year2022/row_parser_spec.rb | 20 ++++++++ .../lettings/year2023/row_parser_spec.rb | 49 +++++++++++++++++++ .../sales/year2022/row_parser_spec.rb | 20 ++++++++ 6 files changed, 147 insertions(+) diff --git a/app/services/bulk_upload/lettings/year2022/row_parser.rb b/app/services/bulk_upload/lettings/year2022/row_parser.rb index 9d273dab5..80b65c133 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?", @@ -335,6 +336,7 @@ 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 def self.question_for_field(field) QUESTIONS[field] @@ -716,6 +718,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..46ff0ff12 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?", @@ -339,6 +340,8 @@ class BulkUpload::Lettings::Year2023::RowParser 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 @@ -457,6 +460,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 diff --git a/app/services/bulk_upload/sales/year2022/row_parser.rb b/app/services/bulk_upload/sales/year2022/row_parser.rb index b08db8585..e53b42ecb 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] @@ -944,4 +946,21 @@ private errors.add(:field_4, I18n.t("validations.date.outside_collection_window")) 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/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb index dd9bf4a5b..25fc64cf9 100644 --- a/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb @@ -954,6 +954,26 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do 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..f98eed6d0 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| @@ -984,6 +996,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..8c0a4cd45 100644 --- a/spec/services/bulk_upload/sales/year2022/row_parser_spec.rb +++ b/spec/services/bulk_upload/sales/year2022/row_parser_spec.rb @@ -520,5 +520,25 @@ RSpec.describe BulkUpload::Sales::Year2022::RowParser do 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 end end From 6bf582a9a69f9a62f349f85b22e410192501481d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 May 2023 09:43:10 +0100 Subject: [PATCH 06/14] Bump nunjucks from 3.2.3 to 3.2.4 (#1577) Bumps [nunjucks](https://github.com/mozilla/nunjucks) from 3.2.3 to 3.2.4. - [Release notes](https://github.com/mozilla/nunjucks/releases) - [Changelog](https://github.com/mozilla/nunjucks/blob/master/CHANGELOG.md) - [Commits](https://github.com/mozilla/nunjucks/compare/v3.2.3...v3.2.4) --- updated-dependencies: - dependency-name: nunjucks dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1fcfee856..9b7da0c6b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4272,9 +4272,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" From 42aa07cc39f27ba507f6e000db168cc2475bfda7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 May 2023 09:44:10 +0100 Subject: [PATCH 07/14] Bump webpack from 5.74.0 to 5.76.0 (#1414) Bumps [webpack](https://github.com/webpack/webpack) from 5.74.0 to 5.76.0. - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v5.74.0...v5.76.0) --- updated-dependencies: - dependency-name: webpack dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 8b6a0dbe1..707c3c3fe 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,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/yarn.lock b/yarn.lock index 9b7da0c6b..77b4f6c41 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5955,10 +5955,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" From 621289abb70b7effee7b6ad9b23f5110fdf992e1 Mon Sep 17 00:00:00 2001 From: Phil Lee Date: Tue, 9 May 2023 11:12:12 +0100 Subject: [PATCH 08/14] remove chart.js (#1606) # Context - Removing dependencies that are not used # Changes - Remove chart.js which appears to be part of activeadmin and chartkick which seemed to have been removed --- package.json | 1 - webpack.config.js | 1 - yarn.lock | 5 ----- 3 files changed, 7 deletions(-) diff --git a/package.json b/package.json index 707c3c3fe..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", 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 77b4f6c41..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" From 7d4a8f90eb0d44481e3dd273a16d643675c4c70b Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Tue, 9 May 2023 11:19:13 +0100 Subject: [PATCH 09/14] Update import to handle validations (#1605) * Clear all the charges if the error is on tcharge * Round earnings value upon import * Rounds savings to the nearest 10 * Clear income on over_hard_max_for_london validation * Refactor sale import validations to be consistent with lettings * Extract charges attributes into a variable --- .../sales/financial_validations.rb | 4 +- .../imports/lettings_logs_import_service.rb | 19 +++-- .../imports/sales_logs_import_service.rb | 55 ++++++------ .../lettings_logs_import_service_spec.rb | 62 ++++++++++++++ .../imports/sales_logs_import_service_spec.rb | 85 +++++++++++++++++-- 5 files changed, 180 insertions(+), 45 deletions(-) diff --git a/app/models/validations/sales/financial_validations.rb b/app/models/validations/sales/financial_validations.rb index c3b1446c9..8e54758bd 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,7 +18,7 @@ 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") } end diff --git a/app/services/imports/lettings_logs_import_service.rb b/app/services/imports/lettings_logs_import_service.rb index 1bc98925a..b1487a0b7 100644 --- a/app/services/imports/lettings_logs_import_service.rb +++ b/app/services/imports/lettings_logs_import_service.rb @@ -105,7 +105,7 @@ module Imports attributes["hb"] = unsafe_string_as_integer(xml_doc, "Q6Ben") attributes["benefits"] = unsafe_string_as_integer(xml_doc, "Q7Ben") - attributes["earnings"] = safe_string_as_decimal(xml_doc, "Q8Money") + attributes["earnings"] = safe_string_as_decimal(xml_doc, "Q8Money")&.round attributes["net_income_known"] = net_income_known(xml_doc, attributes["earnings"]) attributes["incfreq"] = unsafe_string_as_integer(xml_doc, "Q8a") @@ -284,11 +284,14 @@ module Imports end def rescue_validation_or_raise(lettings_log, attributes, previous_status, exception) + charges_attributes = %w[brent scharge pscharge supcharg tcharge] + # Blank out all invalid fields for in-progress logs if %w[saved submitted-invalid].include?(previous_status) lettings_log.errors.each do |error| @logger.warn("Log #{lettings_log.old_id}: Removing field #{error.attribute} from log triggering validation: #{error.type}") attributes.delete(error.attribute.to_s) + charges_attributes.each { |attribute| attributes.delete(attribute) } if error.attribute == :tcharge end @logs_overridden << lettings_log.old_id return save_lettings_log(attributes, previous_status) @@ -306,16 +309,16 @@ module Imports %i[earnings over_hard_max] => %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| diff --git a/app/services/imports/sales_logs_import_service.rb b/app/services/imports/sales_logs_import_service.rb index 8702a8846..565c16512 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,39 +217,38 @@ 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_london] => %w[income2], + %i[equity over_max] => %w[equity], + %i[equity under_min] => %w[equity], + %i[mortgage cannot_be_0] => %w[mortgage], + } + + 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") - @logs_overridden << sales_log.old_id - attributes.delete("mortgage") - 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(', ')}") @logs_overridden << sales_log.old_id 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..f169dbd78 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 @@ -631,7 +631,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 +702,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 +727,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 +743,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 +752,7 @@ 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 @@ -767,6 +768,56 @@ RSpec.describe Imports::SalesLogsImportService do 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 + + 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 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 "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 +868,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" } From 40cf0c10c2bbe07b6393c3e98b9c0a871a61eca0 Mon Sep 17 00:00:00 2001 From: Arthur Campbell <51094020+arfacamble@users.noreply.github.com> Date: Tue, 9 May 2023 11:50:38 +0100 Subject: [PATCH 10/14] CLDC-1633 build feature csv download of sales logs (#1568) * create a method on the FormHandler that returns the sales form questions for all years in the order that they appear in the form * update csv email job to accomodate sales log export as well as lettings add to tests to reflec the changes made * write tests to cover the desired functionality of the SalesLogCsvService * create the SalesLogCsvService create a necessary method on the log to enable submission method to be included on the csv derive values for the two halves of previous postcode for export * add relevant links in the UI and pipe everything together in controllers amend organisations controller to have flexibility to download logs of either type add necessary methods to sales log controller, raising shared method to logs controller update routing for amendments and additions extract helper method to build urls for downloading logs within an organisation * correct various linter complaints and tech review suggestions * minor amendment to add old_id and reorder early columns * undo my 'clever' refactor that broke things * refactoring of csv service after some tech review and some UI testing in review app * update tests to include a test of a full export and all values in teh csv * correct minor routing error to ensure correct url is shown and tab selected after requesting csv email * update organisations controller requests spec file to cover new functionality and make a minor amendment to authentication scope in the controller after error found in testing * write request tests for the new functionality in the sales log controller, define authorisation in the controller * minor correction after rubocop's kind suggestion' * various corrections from first pass at PO, tech review, linter, etc * refactor :ordered_sales_questions_for_all_years * first pass at implementing flexible code-based form fixtures for testing * second pass * refactor all tests of :ordered_sales_questions_for_all_years to use new factories * some refactoring in the testing of the csv service * use that fact that params is always available in controllers and don't pass it around, inline some methods calls * correct minor bug to ensure that "Return to logs" link returns to the correct index page * remove reminder comments * write further tests on the manipulation of questions into the csv headers, update factories of form constituents to allow the creation of forms with richer questions * fix linter complaints * minor alterations after rebase to account for changes made on other branches * refactor after code review * tweak fixtures after rebase containing alterations to the factory defaults --- app/controllers/lettings_logs_controller.rb | 19 +- app/controllers/logs_controller.rb | 4 + app/controllers/organisations_controller.rb | 36 +++- app/controllers/sales_logs_controller.rb | 21 ++ app/helpers/logs_helper.rb | 39 ++-- app/jobs/email_csv_job.rb | 19 +- .../derived_variables/sales_log_variables.rb | 3 +- app/models/form_handler.rb | 26 ++- app/models/log.rb | 4 + app/services/csv/sales_log_csv_service.rb | 107 ++++++++++ app/views/logs/_log_list.html.erb | 2 +- app/views/logs/csv_confirmation.html.erb | 2 +- app/views/logs/index.html.erb | 4 +- app/views/organisations/logs.html.erb | 4 +- config/routes.rb | 13 +- spec/factories/form.rb | 25 +++ spec/factories/page.rb | 21 ++ spec/factories/question.rb | 6 + spec/factories/sales_log.rb | 3 +- spec/factories/section.rb | 16 ++ spec/factories/subsection.rb | 20 ++ .../files/sales_logs_csv_export_codes.csv | 2 + .../files/sales_logs_csv_export_labels.csv | 2 + spec/jobs/email_csv_job_spec.rb | 39 +++- spec/models/form_handler_spec.rb | 51 +++++ .../requests/organisations_controller_spec.rb | 189 ++++++++++++++++-- spec/requests/sales_logs_controller_spec.rb | 184 ++++++++++++++++- .../csv/sales_log_csv_service_spec.rb | 189 ++++++++++++++++++ 28 files changed, 966 insertions(+), 84 deletions(-) create mode 100644 app/services/csv/sales_log_csv_service.rb create mode 100644 spec/factories/form.rb create mode 100644 spec/factories/page.rb create mode 100644 spec/factories/question.rb create mode 100644 spec/factories/section.rb create mode 100644 spec/factories/subsection.rb create mode 100644 spec/fixtures/files/sales_logs_csv_export_codes.csv create mode 100644 spec/fixtures/files/sales_logs_csv_export_labels.csv create mode 100644 spec/services/csv/sales_log_csv_service_spec.rb 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/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/logs_helper.rb b/app/helpers/logs_helper.rb index aa132afd6..f525f4600 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 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/sales_log_variables.rb b/app/models/derived_variables/sales_log_variables.rb index c30c62b9a..308a62bb8 100644 --- a/app/models/derived_variables/sales_log_variables.rb +++ b/app/models/derived_variables/sales_log_variables.rb @@ -17,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 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/log.rb b/app/models/log.rb index 4ee748eda..807b45c8b 100644 --- a/app/models/log.rb +++ b/app/models/log.rb @@ -136,6 +136,10 @@ class Log < ApplicationRecord format_as_currency(field_value) 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/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?(/((? <%= 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/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/routes.rb b/config/routes.rb index 359e7e121..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" @@ -198,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/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/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/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_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/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/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 From ecdfab1c8880d765e84d12eaf983328d9a13b9fe Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Tue, 9 May 2023 15:46:37 +0100 Subject: [PATCH 11/14] Update more validations upon import (#1609) * Clear invalid format previous postcode * Fix import with income 2 outside of london validation * Remove mscharge if it is under minimum * Clear frombeds if it's out of range --- .../local_authority_validations.rb | 2 +- .../sales/financial_validations.rb | 2 +- .../imports/lettings_logs_import_service.rb | 4 +- .../imports/sales_logs_import_service.rb | 9 ++ .../imports/sales_logs_import_service_spec.rb | 103 ++++++++++++++++++ 5 files changed, 116 insertions(+), 4 deletions(-) diff --git a/app/models/validations/local_authority_validations.rb b/app/models/validations/local_authority_validations.rb index c895880bd..3bdd79166 100644 --- a/app/models/validations/local_authority_validations.rb +++ b/app/models/validations/local_authority_validations.rb @@ -3,7 +3,7 @@ module Validations::LocalAuthorityValidations postcode = record.ppostcode_full if record.previous_postcode_known? && (postcode.blank? || !postcode.match(POSTCODE_REGEXP)) error_message = I18n.t("validations.postcode") - record.errors.add :ppostcode_full, error_message + record.errors.add :ppostcode_full, :wrong_format, message: error_message end end end diff --git a/app/models/validations/sales/financial_validations.rb b/app/models/validations/sales/financial_validations.rb index 8e54758bd..02fceeadc 100644 --- a/app/models/validations/sales/financial_validations.rb +++ b/app/models/validations/sales/financial_validations.rb @@ -20,7 +20,7 @@ module Validations::Sales::FinancialValidations if record.london_property? && record.income2 > 90_000 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/services/imports/lettings_logs_import_service.rb b/app/services/imports/lettings_logs_import_service.rb index b1487a0b7..75d7f1737 100644 --- a/app/services/imports/lettings_logs_import_service.rb +++ b/app/services/imports/lettings_logs_import_service.rb @@ -467,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 565c16512..6e2c11bf3 100644 --- a/app/services/imports/sales_logs_import_service.rb +++ b/app/services/imports/sales_logs_import_service.rb @@ -225,10 +225,13 @@ module Imports %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)| @@ -249,6 +252,12 @@ module Imports attributes.delete("postcode_full") attributes["pcodenk"] = attributes["la"].present? ? 1 : nil save_sales_log(attributes, previous_status) + 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("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(', ')}") @logs_overridden << sales_log.old_id diff --git a/spec/services/imports/sales_logs_import_service_spec.rb b/spec/services/imports/sales_logs_import_service_spec.rb index f169dbd78..eb7051a81 100644 --- a/spec/services/imports/sales_logs_import_service_spec.rb +++ b/spec/services/imports/sales_logs_import_service_spec.rb @@ -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" } @@ -768,6 +795,32 @@ RSpec.describe Imports::SalesLogsImportService do 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" } @@ -818,6 +871,56 @@ RSpec.describe Imports::SalesLogsImportService do 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" } From 1053607ed84b5ea79f7358bba40f2adf62be3885 Mon Sep 17 00:00:00 2001 From: Phil Lee Date: Wed, 10 May 2023 10:00:39 +0100 Subject: [PATCH 12/14] CLDC-2287 Bulk upload setup errors (#1585) # Context - https://digital.dclg.gov.uk/jira/browse/CLDC-2287 - bulk upload errors on setup fields are not being categorised as setup errors # Changes - ensure errors on setup field are categorised as `setup` errors - ordering of validations tweaked, so `validate_nulls` is further down the execution order. this is so any existing validation run first and `validate_nulls` only adds errors if there aren't any existing errors on a field - removed old 16/60% tests are no longer required due to introduction of `how to fix` journey --- .../lettings/year2022/row_parser.rb | 79 ++++++++++---- .../lettings/year2023/row_parser.rb | 100 +++++++++++++----- .../bulk_upload/sales/year2022/row_parser.rb | 6 +- .../bulk_upload/lettings/validator_spec.rb | 2 +- .../lettings/year2022/row_parser_spec.rb | 68 ++++++------ .../lettings/year2023/row_parser_spec.rb | 57 +++++----- .../sales/year2022/row_parser_spec.rb | 9 +- 7 files changed, 200 insertions(+), 121 deletions(-) diff --git a/app/services/bulk_upload/lettings/year2022/row_parser.rb b/app/services/bulk_upload/lettings/year2022/row_parser.rb index 80b65c133..62cf9e872 100644 --- a/app/services/bulk_upload/lettings/year2022/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2022/row_parser.rb @@ -280,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 @@ -293,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 @@ -335,9 +365,10 @@ 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] end @@ -424,7 +455,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 @@ -481,13 +516,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 @@ -499,19 +534,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 @@ -538,7 +573,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 @@ -567,7 +602,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 @@ -645,9 +680,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 @@ -704,7 +739,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 diff --git a/app/services/bulk_upload/lettings/year2023/row_parser.rb b/app/services/bulk_upload/lettings/year2023/row_parser.rb index 46ff0ff12..9dc5b60de 100644 --- a/app/services/bulk_upload/lettings/year2023/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2023/row_parser.rb @@ -280,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 @@ -293,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 @@ -336,7 +380,7 @@ 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 @@ -428,7 +472,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 @@ -493,7 +541,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 @@ -576,9 +624,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 @@ -599,7 +647,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 @@ -620,19 +668,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 @@ -644,19 +692,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 @@ -710,7 +758,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 e53b42ecb..4383898fb 100644 --- a/app/services/bulk_upload/sales/year2022/row_parser.rb +++ b/app/services/bulk_upload/sales/year2022/row_parser.rb @@ -941,9 +941,9 @@ 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 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 25fc64cf9..99adb049a 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 @@ -955,6 +949,16 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do 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 "soft validations" do context "when soft validation is triggered" do let(:attributes) { setup_section_params.merge({ field_12: 22, field_35: 5 }) } 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 f98eed6d0..ef9935b44 100644 --- a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb @@ -501,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 @@ -510,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 @@ -547,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 @@ -581,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 @@ -717,8 +717,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 @@ -729,8 +727,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 @@ -749,8 +745,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 @@ -762,19 +756,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 @@ -784,9 +775,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 @@ -887,16 +876,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 @@ -905,8 +890,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 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 8c0a4cd45..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,16 @@ 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 From 14faee14bf6796fdbc008fd54b13005ecd5d0d21 Mon Sep 17 00:00:00 2001 From: Jack <113976590+bibblobcode@users.noreply.github.com> Date: Wed, 10 May 2023 11:10:13 +0100 Subject: [PATCH 13/14] Check new telephone number when there's an error (#1608) --- app/views/merge_requests/confirm_telephone_number.html.erb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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" %> From 4fa895809bbdc4668c3b3d968790dd052bbf6e43 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Thu, 11 May 2023 10:59:02 +0100 Subject: [PATCH 14/14] CLDC-2331 Add disabled access needs missing error (#1611) * Validate no housing needs answers for 2022 * Validate no housing needs answers for 2023 --- .../bulk_upload/lettings/year2022/row_parser.rb | 11 +++++++++++ .../bulk_upload/lettings/year2023/row_parser.rb | 11 +++++++++++ .../lettings/year2022/row_parser_spec.rb | 14 ++++++++++++++ .../lettings/year2023/row_parser_spec.rb | 14 ++++++++++++++ 4 files changed, 50 insertions(+) diff --git a/app/services/bulk_upload/lettings/year2022/row_parser.rb b/app/services/bulk_upload/lettings/year2022/row_parser.rb index 62cf9e872..953487cd7 100644 --- a/app/services/bulk_upload/lettings/year2022/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2022/row_parser.rb @@ -340,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 @@ -648,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")) diff --git a/app/services/bulk_upload/lettings/year2023/row_parser.rb b/app/services/bulk_upload/lettings/year2023/row_parser.rb index 9dc5b60de..85abd180d 100644 --- a/app/services/bulk_upload/lettings/year2023/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2023/row_parser.rb @@ -356,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 @@ -584,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")) 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 99adb049a..0587c0f51 100644 --- a/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb @@ -959,6 +959,20 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do 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 }) } 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 ef9935b44..d881b972b 100644 --- a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb @@ -675,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 } }