From 648e344af60f74084e993fb25cb9680bd06e5420 Mon Sep 17 00:00:00 2001 From: Arthur Campbell <51094020+arfacamble@users.noreply.github.com> Date: Mon, 30 Jan 2023 14:58:38 +0000 Subject: [PATCH] CLDC-1786 add validations for staircasing transaction (#1235) * change percent to % so that suffix is consistent with other similar questions * fix rebase conflicts * add hard validation that percentage owned after a staircasing transaction cannot be less than the percentage bought in that transaction, with associated tests * add compound validation to ensure that logs of type older person shared ownership may not have a stairbought value of greater than 75% \n associated tests included * remove duplicate method and use existing version * fix rebase conflicts * ensure schema correct and test of page number correct * fix form handler test after rebase * fix tests and ensure schema correct after rebase --- .../pages/staircase_bought_value_check.rb | 27 +++++++++ .../form/sales/questions/staircase_bought.rb | 2 +- .../questions/staircase_bought_value_check.rb | 23 ++++++++ .../form/sales/questions/staircase_owned.rb | 2 +- .../subsections/shared_ownership_scheme.rb | 1 + .../sales/financial_validations.rb | 17 ++++++ .../validations/sales/soft_validations.rb | 4 ++ config/locales/en.yml | 4 ++ ...aircase_bought_value_check_to_sales_log.rb | 5 ++ db/schema.rb | 3 +- .../sales/questions/staircase_bought_spec.rb | 2 +- .../sales/questions/staircase_owned_spec.rb | 2 +- .../shared_ownership_scheme_spec.rb | 1 + spec/models/form_handler_spec.rb | 4 +- .../sales/financial_validations_spec.rb | 59 +++++++++++++++++++ .../sales/soft_validations_spec.rb | 22 ++++++- .../validations/shared_validations_spec.rb | 6 +- 17 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 app/models/form/sales/pages/staircase_bought_value_check.rb create mode 100644 app/models/form/sales/questions/staircase_bought_value_check.rb create mode 100644 db/migrate/20230125152916_add_staircase_bought_value_check_to_sales_log.rb diff --git a/app/models/form/sales/pages/staircase_bought_value_check.rb b/app/models/form/sales/pages/staircase_bought_value_check.rb new file mode 100644 index 000000000..b7b8178ff --- /dev/null +++ b/app/models/form/sales/pages/staircase_bought_value_check.rb @@ -0,0 +1,27 @@ +class Form::Sales::Pages::StaircaseBoughtValueCheck < ::Form::Page + def initialize(id, hsh, subsection) + super + @id = "staircase_bought_value_check" + @depends_on = [ + { + "staircase_bought_above_fifty?" => true, + }, + ] + @title_text = { + "translation" => "soft_validations.staircase_bought_seems_high", + "arguments" => [ + { + "key" => "stairbought", + "i18n_template" => "percentage", + }, + ], + } + @informative_text = {} + end + + def questions + @questions ||= [ + Form::Sales::Questions::StaircaseBoughtValueCheck.new(nil, nil, self), + ] + end +end diff --git a/app/models/form/sales/questions/staircase_bought.rb b/app/models/form/sales/questions/staircase_bought.rb index bdb5ac965..385cfb782 100644 --- a/app/models/form/sales/questions/staircase_bought.rb +++ b/app/models/form/sales/questions/staircase_bought.rb @@ -8,6 +8,6 @@ class Form::Sales::Questions::StaircaseBought < ::Form::Question @width = 5 @min = 0 @max = 100 - @suffix = " percent" + @suffix = "%" end end diff --git a/app/models/form/sales/questions/staircase_bought_value_check.rb b/app/models/form/sales/questions/staircase_bought_value_check.rb new file mode 100644 index 000000000..65fe02e66 --- /dev/null +++ b/app/models/form/sales/questions/staircase_bought_value_check.rb @@ -0,0 +1,23 @@ +class Form::Sales::Questions::StaircaseBoughtValueCheck < ::Form::Question + def initialize(id, hsh, page) + super + @id = "staircase_bought_value_check" + @check_answer_label = "Percentage bought confirmation" + @header = "Are you sure this is correct?" + @type = "interruption_screen" + @answer_options = { + "0" => { "value" => "Yes" }, + "1" => { "value" => "No" }, + } + @hidden_in_check_answers = { + "depends_on" => [ + { + "staircase_bought_value_check" => 0, + }, + { + "staircase_bought_value_check" => 1, + }, + ], + } + end +end diff --git a/app/models/form/sales/questions/staircase_owned.rb b/app/models/form/sales/questions/staircase_owned.rb index 6f9e13b14..7e6dab5d6 100644 --- a/app/models/form/sales/questions/staircase_owned.rb +++ b/app/models/form/sales/questions/staircase_owned.rb @@ -8,6 +8,6 @@ class Form::Sales::Questions::StaircaseOwned < ::Form::Question @width = 5 @min = 0 @max = 100 - @suffix = " percent" + @suffix = "%" end end diff --git a/app/models/form/sales/subsections/shared_ownership_scheme.rb b/app/models/form/sales/subsections/shared_ownership_scheme.rb index e7ff1cf6e..0e2e3fdda 100644 --- a/app/models/form/sales/subsections/shared_ownership_scheme.rb +++ b/app/models/form/sales/subsections/shared_ownership_scheme.rb @@ -11,6 +11,7 @@ class Form::Sales::Subsections::SharedOwnershipScheme < ::Form::Subsection Form::Sales::Pages::LivingBeforePurchase.new("living_before_purchase_shared_ownership", nil, self), Form::Sales::Pages::Staircase.new(nil, nil, self), Form::Sales::Pages::AboutStaircase.new(nil, nil, self), + Form::Sales::Pages::StaircaseBoughtValueCheck.new(nil, nil, self), Form::Sales::Pages::Resale.new(nil, nil, self), Form::Sales::Pages::ExchangeDate.new(nil, nil, self), Form::Sales::Pages::HandoverDate.new(nil, nil, self), diff --git a/app/models/validations/sales/financial_validations.rb b/app/models/validations/sales/financial_validations.rb index 233f32b6b..df7dc8df5 100644 --- a/app/models/validations/sales/financial_validations.rb +++ b/app/models/validations/sales/financial_validations.rb @@ -27,4 +27,21 @@ module Validations::Sales::FinancialValidations record.errors.add :cashdis, I18n.t("validations.financial.cash_discount_invalid") end end + + def validate_percentage_bought_not_greater_than_percentage_owned(record) + return unless record.stairbought && record.stairowned + + if record.stairbought > record.stairowned + record.errors.add :stairowned, I18n.t("validations.financial.staircasing.percentage_bought_must_be_greater_than_percentage_owned") + end + end + + def validate_percentage_owned_not_too_much_if_older_person(record) + return unless record.old_persons_shared_ownership? && record.stairowned + + if record.stairowned > 75 + record.errors.add :stairowned, I18n.t("validations.financial.staircasing.older_person_percentage_owned_maximum_75") + record.errors.add :type, I18n.t("validations.financial.staircasing.older_person_percentage_owned_maximum_75") + end + end end diff --git a/app/models/validations/sales/soft_validations.rb b/app/models/validations/sales/soft_validations.rb index 70af22fec..0bffe1234 100644 --- a/app/models/validations/sales/soft_validations.rb +++ b/app/models/validations/sales/soft_validations.rb @@ -13,6 +13,10 @@ module Validations::Sales::SoftValidations income1 < ALLOWED_INCOME_RANGES[ecstat1][:soft_min] end + def staircase_bought_above_fifty? + stairbought && stairbought > 50 + end + def mortgage_over_soft_max? return false unless mortgage && inc1mort && inc2mort return false if income1_used_for_mortgage? && income1.blank? || income2_used_for_mortgage? && income2.blank? diff --git a/config/locales/en.yml b/config/locales/en.yml index 1ec13a304..98f57ae73 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -277,6 +277,10 @@ en: out_of_range: "Household rent and other charges must be between %{min_chcharge} and %{max_chcharge} if paying %{period}" not_provided: "Enter how much rent and other charges the household pays %{period}" cash_discount_invalid: "Cash discount must be £0 - £999,999" + staircasing: + percentage_bought_must_be_greater_than_percentage_owned: "Total percentage buyer now owns must be more than percentage bought in this transaction" + older_person_percentage_owned_maximum_75: "Percentage cannot be above 75% under Older Person's Shared Ownership" + household: reasonpref: not_homeless: "Answer cannot be ‘homeless or about to lose their home’ as the tenant was not homeless immediately prior to this letting" diff --git a/db/migrate/20230125152916_add_staircase_bought_value_check_to_sales_log.rb b/db/migrate/20230125152916_add_staircase_bought_value_check_to_sales_log.rb new file mode 100644 index 000000000..d7e227afa --- /dev/null +++ b/db/migrate/20230125152916_add_staircase_bought_value_check_to_sales_log.rb @@ -0,0 +1,5 @@ +class AddStaircaseBoughtValueCheckToSalesLog < ActiveRecord::Migration[7.0] + def change + add_column :sales_logs, :staircase_bought_value_check, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 940e172fd..dc431d425 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -504,9 +504,10 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_26_145529) do t.integer "retirement_value_check" t.integer "hodate_check" t.integer "extrabor_value_check" + t.integer "grant_value_check" + t.integer "staircase_bought_value_check" t.integer "deposit_and_mortgage_value_check" t.integer "shared_ownership_deposit_value_check" - t.integer "grant_value_check" t.integer "old_persons_shared_ownership_value_check" t.index ["bulk_upload_id"], name: "index_sales_logs_on_bulk_upload_id" t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id" diff --git a/spec/models/form/sales/questions/staircase_bought_spec.rb b/spec/models/form/sales/questions/staircase_bought_spec.rb index 96716dbd3..7b877cc26 100644 --- a/spec/models/form/sales/questions/staircase_bought_spec.rb +++ b/spec/models/form/sales/questions/staircase_bought_spec.rb @@ -44,7 +44,7 @@ RSpec.describe Form::Sales::Questions::StaircaseBought, type: :model do end it "has correct suffix" do - expect(question.suffix).to eq(" percent") + expect(question.suffix).to eq("%") end it "has correct min" do diff --git a/spec/models/form/sales/questions/staircase_owned_spec.rb b/spec/models/form/sales/questions/staircase_owned_spec.rb index 56b563990..cbd577784 100644 --- a/spec/models/form/sales/questions/staircase_owned_spec.rb +++ b/spec/models/form/sales/questions/staircase_owned_spec.rb @@ -44,7 +44,7 @@ RSpec.describe Form::Sales::Questions::StaircaseOwned, type: :model do end it "has correct suffix" do - expect(question.suffix).to eq(" percent") + expect(question.suffix).to eq("%") end it "has correct min" do diff --git a/spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb b/spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb index d131bc605..58a762ccb 100644 --- a/spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb +++ b/spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb @@ -17,6 +17,7 @@ RSpec.describe Form::Sales::Subsections::SharedOwnershipScheme, type: :model do living_before_purchase_shared_ownership staircasing about_staircasing + staircase_bought_value_check resale exchange_contracts handover_date diff --git a/spec/models/form_handler_spec.rb b/spec/models/form_handler_spec.rb index 89f550bee..be88b2b94 100644 --- a/spec/models/form_handler_spec.rb +++ b/spec/models/form_handler_spec.rb @@ -52,14 +52,14 @@ RSpec.describe FormHandler do it "is able to load a current sales form" do form = form_handler.get_form("current_sales") expect(form).to be_a(Form) - expect(form.pages.count).to eq(197) + expect(form.pages.count).to eq(198) expect(form.name).to eq("2022_2023_sales") end it "is able to load a previous sales form" do form = form_handler.get_form("previous_sales") expect(form).to be_a(Form) - expect(form.pages.count).to eq(197) + expect(form.pages.count).to eq(198) expect(form.name).to eq("2021_2022_sales") end end diff --git a/spec/models/validations/sales/financial_validations_spec.rb b/spec/models/validations/sales/financial_validations_spec.rb index 4bc19afbe..88f36943f 100644 --- a/spec/models/validations/sales/financial_validations_spec.rb +++ b/spec/models/validations/sales/financial_validations_spec.rb @@ -99,4 +99,63 @@ RSpec.describe Validations::Sales::FinancialValidations do expect(record.errors["cashdis"]).to be_empty end end + + describe "#validate_percentage_bought_not_greater_than_percentage_owned" do + let(:record) { FactoryBot.create(:sales_log) } + + it "does not add an error if the percentage bought is less than the percentage owned" do + record.stairbought = 20 + record.stairowned = 40 + financial_validator.validate_percentage_bought_not_greater_than_percentage_owned(record) + expect(record.errors["stairbought"]).to be_empty + expect(record.errors["stairowned"]).to be_empty + end + + it "does not add an error if the percentage bought is equal to the percentage owned" do + record.stairbought = 30 + record.stairowned = 30 + financial_validator.validate_percentage_bought_not_greater_than_percentage_owned(record) + expect(record.errors["stairbought"]).to be_empty + expect(record.errors["stairowned"]).to be_empty + end + + it "adds an error to stairowned and not stairbought if the percentage bought is more than the percentage owned" do + record.stairbought = 50 + record.stairowned = 40 + financial_validator.validate_percentage_bought_not_greater_than_percentage_owned(record) + expect(record.errors["stairowned"]).to include(match I18n.t("validations.financial.staircasing.percentage_bought_must_be_greater_than_percentage_owned")) + end + end + + describe "#validate_percentage_owned_not_too_much_if_older_person" do + let(:record) { FactoryBot.create(:sales_log) } + + context "when log type is not older persons shared ownership" do + it "does not add an error when percentage owned after staircasing transaction exceeds 75%" do + record.type = 2 + record.stairowned = 80 + financial_validator.validate_percentage_owned_not_too_much_if_older_person(record) + expect(record.errors["stairowned"]).to be_empty + expect(record.errors["type"]).to be_empty + end + end + + context "when log type is older persons shared ownership" do + it "does not add an error when percentage owned after staircasing transaction is less than 75%" do + record.type = 24 + record.stairowned = 50 + financial_validator.validate_percentage_owned_not_too_much_if_older_person(record) + expect(record.errors["stairowned"]).to be_empty + expect(record.errors["type"]).to be_empty + end + + it "adds an error when percentage owned after staircasing transaction exceeds 75%" do + record.type = 24 + record.stairowned = 90 + financial_validator.validate_percentage_owned_not_too_much_if_older_person(record) + expect(record.errors["stairowned"]).to include(match I18n.t("validations.financial.staircasing.older_person_percentage_owned_maximum_75")) + expect(record.errors["type"]).to include(match I18n.t("validations.financial.staircasing.older_person_percentage_owned_maximum_75")) + end + end + end end diff --git a/spec/models/validations/sales/soft_validations_spec.rb b/spec/models/validations/sales/soft_validations_spec.rb index 234f810bc..dc873bc97 100644 --- a/spec/models/validations/sales/soft_validations_spec.rb +++ b/spec/models/validations/sales/soft_validations_spec.rb @@ -367,7 +367,7 @@ RSpec.describe Validations::Sales::SoftValidations do .to be_deposit_over_soft_max end - it "returns fals if deposit is less than 4/3 of savings" do + it "returns false if deposit is less than 4/3 of savings" do record.deposit = 7_999 record.savings = 6_000 expect(record) @@ -573,4 +573,24 @@ RSpec.describe Validations::Sales::SoftValidations do expect(record).not_to be_grant_outside_common_range end end + + describe "#staircase_bought_above_fifty" do + it "returns false when stairbought is not set" do + record.stairbought = nil + + expect(record).not_to be_staircase_bought_above_fifty + end + + it "returns false when stairbought is below fifty" do + record.stairbought = 40 + + expect(record).not_to be_staircase_bought_above_fifty + end + + it "returns true when stairbought is above fifty" do + record.stairbought = 70 + + expect(record).to be_staircase_bought_above_fifty + end + end end diff --git a/spec/models/validations/shared_validations_spec.rb b/spec/models/validations/shared_validations_spec.rb index abdde5f45..c1d8cb3af 100644 --- a/spec/models/validations/shared_validations_spec.rb +++ b/spec/models/validations/shared_validations_spec.rb @@ -70,11 +70,11 @@ RSpec.describe Validations::SharedValidations do end context "when validating percent" do - it "validates that % suffix is added in the error message" do - sales_record.stairbought = "random" + it "validates that suffixes are added in the error message" do + sales_record.stairbought = 150 shared_validator.validate_numeric_min_max(sales_record) expect(sales_record.errors["stairbought"]) - .to include(match I18n.t("validations.numeric.valid", field: "Percentage bought in this staircasing transaction", min: "0 percent", max: "100 percent")) + .to include(match I18n.t("validations.numeric.valid", field: "Percentage bought in this staircasing transaction", min: "0%", max: "100%")) end end