Browse Source

Sales financial validations (#1079)

* Include shared validations in sales log model and run validate_numeric_min_max method

* Add non london hard max income validation

* Add london income validation

* Remove child vlidation because buyer cannot be a child

* Add income1_value_check column

* Add buyer_1_income_value_check question and page

* Update income1_under_soft_min?

* Add mortgage and value check column

* Add mortgage value check page

* Update mortgage_over_soft_max? in soft validateions

* Remove unused error message
pull/1088/head
kosiakkatrina 2 years ago committed by GitHub
parent
commit
514552934d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 21
      app/models/form/sales/pages/buyer1_income_value_check.rb
  2. 21
      app/models/form/sales/pages/mortgage_value_check.rb
  3. 1
      app/models/form/sales/questions/buyer1_income.rb
  4. 25
      app/models/form/sales/questions/buyer1_income_value_check.rb
  5. 25
      app/models/form/sales/questions/mortgage_value_check.rb
  6. 2
      app/models/form/sales/subsections/income_benefits_and_savings.rb
  7. 51
      app/models/sales_log.rb
  8. 14
      app/models/validations/sales/financial_validations.rb
  9. 23
      app/models/validations/sales/soft_validations.rb
  10. 2
      config/locales/en.yml
  11. 2
      db/migrate/20221212161657_add_details_known1_to_sales_log.rb
  12. 10
      db/migrate/20221213085819_add_mortgage_and_value_checks.rb
  13. 6
      db/schema.rb
  14. 2
      spec/factories/sales_log.rb
  15. 33
      spec/models/form/sales/pages/buyer1_income_value_check_spec.rb
  16. 33
      spec/models/form/sales/pages/mortgage_value_check_spec.rb
  17. 4
      spec/models/form/sales/questions/buyer1_income_spec.rb
  18. 61
      spec/models/form/sales/questions/buyer1_income_value_check_spec.rb
  19. 61
      spec/models/form/sales/questions/mortgage_value_check_spec.rb
  20. 2
      spec/models/form/sales/subsections/income_benefits_and_savings_spec.rb
  21. 4
      spec/models/form_handler_spec.rb
  22. 56
      spec/models/validations/sales/financial_validations_spec.rb
  23. 204
      spec/models/validations/sales/soft_validations_spec.rb

21
app/models/form/sales/pages/buyer1_income_value_check.rb

@ -0,0 +1,21 @@
class Form::Sales::Pages::Buyer1IncomeValueCheck < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "buyer_1_income_value_check"
@header = ""
@description = ""
@subsection = subsection
@depends_on = [
{
"income1_under_soft_min?" => true,
},
]
@informative_text = {}
end
def questions
@questions ||= [
Form::Sales::Questions::Buyer1IncomeValueCheck.new(nil, nil, self),
]
end
end

21
app/models/form/sales/pages/mortgage_value_check.rb

@ -0,0 +1,21 @@
class Form::Sales::Pages::MortgageValueCheck < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "mortgage_value_check"
@header = ""
@description = ""
@subsection = subsection
@depends_on = [
{
"mortgage_over_soft_max?" => true,
},
]
@informative_text = {}
end
def questions
@questions ||= [
Form::Sales::Questions::MortgageValueCheck.new(nil, nil, self),
]
end
end

1
app/models/form/sales/questions/buyer1_income.rb

@ -7,6 +7,7 @@ class Form::Sales::Questions::Buyer1Income < ::Form::Question
@type = "numeric"
@page = page
@min = 0
@max = 999_999
@step = 1
@width = 5
@prefix = "£"

25
app/models/form/sales/questions/buyer1_income_value_check.rb

@ -0,0 +1,25 @@
class Form::Sales::Questions::Buyer1IncomeValueCheck < ::Form::Question
def initialize(id, hsh, page)
super
@id = "income1_value_check"
@check_answer_label = "Income confirmation"
@header = "Are you sure this income is correct?"
@type = "interruption_screen"
@answer_options = {
"0" => { "value" => "Yes" },
"1" => { "value" => "No" },
}
@hidden_in_check_answers = {
"depends_on" => [
{
"income1_value_check" => 0,
},
{
"income1_value_check" => 1,
},
],
}
@check_answers_card_number = 1
@page = page
end
end

25
app/models/form/sales/questions/mortgage_value_check.rb

@ -0,0 +1,25 @@
class Form::Sales::Questions::MortgageValueCheck < ::Form::Question
def initialize(id, hsh, page)
super
@id = "mortgage_value_check"
@check_answer_label = "Mortgage confirmation"
@header = "Are you sure that the mortgage is more than 5 times the income used for the mortgage application?"
@type = "interruption_screen"
@answer_options = {
"0" => { "value" => "Yes" },
"1" => { "value" => "No" },
}
@hidden_in_check_answers = {
"depends_on" => [
{
"mortgage_value_check" => 0,
},
{
"mortgage_value_check" => 1,
},
],
}
@check_answers_card_number = 1
@page = page
end
end

2
app/models/form/sales/subsections/income_benefits_and_savings.rb

@ -10,8 +10,10 @@ class Form::Sales::Subsections::IncomeBenefitsAndSavings < ::Form::Subsection
def pages
@pages ||= [
Form::Sales::Pages::Buyer1Income.new(nil, nil, self),
Form::Sales::Pages::Buyer1IncomeValueCheck.new(nil, nil, self),
Form::Sales::Pages::Buyer1Mortgage.new(nil, nil, self),
Form::Sales::Pages::Buyer2Income.new(nil, nil, self),
Form::Sales::Pages::MortgageValueCheck.new(nil, nil, self),
Form::Sales::Pages::Savings.new(nil, nil, self),
Form::Sales::Pages::PreviousOwnership.new(nil, nil, self),
]

51
app/models/sales_log.rb

@ -1,5 +1,7 @@
class SalesLogValidator < ActiveModel::Validator
include Validations::Sales::HouseholdValidations
include Validations::SharedValidations
include Validations::Sales::FinancialValidations
def validate(record)
validation_methods = public_methods.select { |method| method.starts_with?("validate_") }
@ -9,6 +11,7 @@ end
class SalesLog < Log
include DerivedVariables::SalesLogVariables
include Validations::Sales::SoftValidations
self.inheritance_column = :_type_disabled
@ -60,4 +63,52 @@ class SalesLog < Log
def unresolved
false
end
LONDON_BOROUGHS = %w[
E09000001
E09000033
E09000020
E09000013
E09000032
E09000022
E09000028
E09000030
E09000012
E09000019
E09000007
E09000005
E09000009
E09000018
E09000027
E09000021
E09000024
E09000029
E09000008
E09000006
E09000023
E09000011
E09000004
E09000016
E09000002
E09000026
E09000025
E09000031
E09000014
E09000010
E09000003
E09000015
E09000017
].freeze
def london_property?
la && LONDON_BOROUGHS.include?(la)
end
def income1_used_for_mortgage?
inc1mort == 1
end
def income2_used_for_mortgage?
inc2mort == 1
end
end

14
app/models/validations/sales/financial_validations.rb

@ -0,0 +1,14 @@
module Validations::Sales::FinancialValidations
# Validations methods need to be called 'validate_<page_name>' to run on model save
# or 'validate_' to run on submit as well
def validate_income1(record)
if record.ecstat1 && record.income1 && record.ownershipsch == 1
if record.london_property?
record.errors.add :income1, I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000) if record.income1 > 90_000
elsif record.income1 > 80_000
record.errors.add :income1, I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000)
end
end
end
end

23
app/models/validations/sales/soft_validations.rb

@ -0,0 +1,23 @@
module Validations::Sales::SoftValidations
ALLOWED_INCOME_RANGES = {
1 => OpenStruct.new(soft_min: 5000),
2 => OpenStruct.new(soft_min: 1500),
3 => OpenStruct.new(soft_min: 1000),
5 => OpenStruct.new(soft_min: 2000),
0 => OpenStruct.new(soft_min: 2000),
}.freeze
def income1_under_soft_min?
return false unless ecstat1 && income1 && ALLOWED_INCOME_RANGES[ecstat1]
income1 < ALLOWED_INCOME_RANGES[ecstat1][:soft_min]
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?
income_used_for_mortgage = (income1_used_for_mortgage? ? income1 : 0) + (income2_used_for_mortgage? ? income2 : 0)
mortgage > income_used_for_mortgage * 5
end
end

2
config/locales/en.yml

@ -201,6 +201,8 @@ en:
under_hard_min: "Net income cannot be less than £%{hard_min} per week given the tenant’s working situation"
freq_missing: "Select how often the household receives income"
earnings_missing: "Enter how much income the household has in total"
income1:
over_hard_max: "Income must be lower than £%{hard_max}"
negative_currency: "Enter an amount above 0"
rent:
less_than_shortfall: "Enter an amount that is more than the shortfall in basic rent"

2
db/migrate/20221212161657_add_details_known1_to_sales_log.rb

@ -1,5 +1,7 @@
class AddDetailsKnown1ToSalesLog < ActiveRecord::Migration[7.0]
def change
change_table :sales_logs, bulk: true do |t|
t.column :details_known_1, :integer
end
end
end

10
db/migrate/20221213085819_add_mortgage_and_value_checks.rb

@ -0,0 +1,10 @@
class AddMortgageAndValueChecks < ActiveRecord::Migration[7.0]
def change
change_table :sales_logs, bulk: true do |t|
t.column :income1_value_check, :integer
t.column :mortgage, :integer
t.column :inc2mort, :integer
t.column :mortgage_value_check, :integer
end
end
end

6
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: 2022_12_12_161657) do
ActiveRecord::Schema[7.0].define(version: 2022_12_13_085819) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -404,6 +404,10 @@ ActiveRecord::Schema[7.0].define(version: 2022_12_12_161657) do
t.integer "prevown"
t.string "sex3"
t.integer "details_known_1"
t.integer "income1_value_check"
t.integer "mortgage"
t.integer "inc2mort"
t.integer "mortgage_value_check"
t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id"
t.index ["managing_organisation_id"], name: "index_sales_logs_on_managing_organisation_id"
t.index ["owning_organisation_id"], name: "index_sales_logs_on_owning_organisation_id"

2
spec/factories/sales_log.rb

@ -56,11 +56,13 @@ FactoryBot.define do
inc1mort { 1 }
income2nk { 0 }
income2 { 10_000 }
inc2mort { 1 }
la_known { "1" }
la { "E09000003" }
savingsnk { 1 }
prevown { 1 }
sex3 { "X" }
mortgage { 20_000 }
end
end
end

33
spec/models/form/sales/pages/buyer1_income_value_check_spec.rb

@ -0,0 +1,33 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::Buyer1IncomeValueCheck, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[income1_value_check])
end
it "has the correct id" do
expect(page.id).to eq("buyer_1_income_value_check")
end
it "has the correct header" do
expect(page.header).to eq("")
end
it "has correct depends_on" do
expect(page.depends_on).to eq([
{
"income1_under_soft_min?" => true,
},
])
end
end

33
spec/models/form/sales/pages/mortgage_value_check_spec.rb

@ -0,0 +1,33 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::MortgageValueCheck, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[mortgage_value_check])
end
it "has the correct id" do
expect(page.id).to eq("mortgage_value_check")
end
it "has the correct header" do
expect(page.header).to eq("")
end
it "has correct depends_on" do
expect(page.depends_on).to eq([
{
"mortgage_over_soft_max?" => true,
},
])
end
end

4
spec/models/form/sales/questions/buyer1_income_spec.rb

@ -54,4 +54,8 @@ RSpec.describe Form::Sales::Questions::Buyer1Income, type: :model do
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(1)
end
it "has correct max" do
expect(question.max).to eq(999_999)
end
end

61
spec/models/form/sales/questions/buyer1_income_value_check_spec.rb

@ -0,0 +1,61 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::Buyer1IncomeValueCheck, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("income1_value_check")
end
it "has the correct header" do
expect(question.header).to eq("Are you sure this income is correct?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Income confirmation")
end
it "has the correct type" do
expect(question.type).to eq("interruption_screen")
end
it "is not marked as derived" do
expect(question.derived?).to be false
end
it "has the correct hint" do
expect(question.hint_text).to be_nil
end
it "has a correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(1)
end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"0" => { "value" => "Yes" },
"1" => { "value" => "No" },
})
end
it "has the correct hidden_in_check_answers" do
expect(question.hidden_in_check_answers).to eq({
"depends_on" => [
{
"income1_value_check" => 0,
},
{
"income1_value_check" => 1,
},
],
})
end
end

61
spec/models/form/sales/questions/mortgage_value_check_spec.rb

@ -0,0 +1,61 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::MortgageValueCheck, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("mortgage_value_check")
end
it "has the correct header" do
expect(question.header).to eq("Are you sure that the mortgage is more than 5 times the income used for the mortgage application?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Mortgage confirmation")
end
it "has the correct type" do
expect(question.type).to eq("interruption_screen")
end
it "is not marked as derived" do
expect(question.derived?).to be false
end
it "has the correct hint" do
expect(question.hint_text).to be_nil
end
it "has a correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(1)
end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"0" => { "value" => "Yes" },
"1" => { "value" => "No" },
})
end
it "has the correct hidden_in_check_answers" do
expect(question.hidden_in_check_answers).to eq({
"depends_on" => [
{
"mortgage_value_check" => 0,
},
{
"mortgage_value_check" => 1,
},
],
})
end
end

2
spec/models/form/sales/subsections/income_benefits_and_savings_spec.rb

@ -15,8 +15,10 @@ RSpec.describe Form::Sales::Subsections::IncomeBenefitsAndSavings, type: :model
expect(subsection.pages.map(&:id)).to eq(
%w[
buyer_1_income
buyer_1_income_value_check
buyer_1_mortgage
buyer_2_income
mortgage_value_check
savings
previous_ownership
],

4
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(51)
expect(form.pages.count).to eq(53)
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(51)
expect(form.pages.count).to eq(53)
expect(form.name).to eq("2021_2022_sales")
end
end

56
spec/models/validations/sales/financial_validations_spec.rb

@ -0,0 +1,56 @@
require "rails_helper"
RSpec.describe Validations::Sales::FinancialValidations do
subject(:financial_validator) { validator_class.new }
let(:validator_class) { Class.new { include Validations::Sales::FinancialValidations } }
describe "income validations" do
let(:record) { FactoryBot.create(:sales_log, ownershipsch: 1) }
context "with shared ownership" do
context "and non london borough" do
(0..8).each do |ecstat|
it "adds an error when buyer 1 income is over hard max for ecstat #{ecstat}" do
record.income1 = 85_000
record.ecstat1 = ecstat
financial_validator.validate_income1(record)
expect(record.errors["income1"])
.to include(match I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000))
end
end
it "validates that the income is within the expected range for the tenant’s employment status" do
record.income1 = 75_000
record.ecstat1 = 1
financial_validator.validate_income1(record)
expect(record.errors["income1"]).to be_empty
end
end
context "and a london borough" do
before do
record.update!(la: "E09000030")
record.reload
end
(0..8).each do |ecstat|
it "adds an error when buyer 1 income is over hard max for ecstat #{ecstat}" do
record.income1 = 95_000
record.ecstat1 = ecstat
financial_validator.validate_income1(record)
expect(record.errors["income1"])
.to include(match I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000))
end
end
it "validates that the income is within the expected range for the tenant’s employment status" do
record.income1 = 85_000
record.ecstat1 = 1
financial_validator.validate_income1(record)
expect(record.errors["income1"]).to be_empty
end
end
end
end
end

204
spec/models/validations/sales/soft_validations_spec.rb

@ -0,0 +1,204 @@
require "rails_helper"
RSpec.describe Validations::Sales::SoftValidations do
let(:record) { FactoryBot.create(:sales_log) }
describe "income1 min validations" do
context "when validating soft min" do
it "returns false if no income1 is given" do
record.income1 = nil
expect(record)
.not_to be_income1_under_soft_min
end
it "returns false if no ecstat1 is given" do
record.ecstat1 = nil
expect(record)
.not_to be_income1_under_soft_min
end
[
{
income1: 4500,
ecstat1: 1,
},
{
income1: 1400,
ecstat1: 2,
},
{
income1: 999,
ecstat1: 3,
},
{
income1: 1899,
ecstat1: 5,
},
{
income1: 1888,
ecstat1: 0,
},
].each do |test_case|
it "returns true if income1 is below soft min for ecstat1 #{test_case[:ecstat1]}" do
record.income1 = test_case[:income1]
record.ecstat1 = test_case[:ecstat1]
expect(record)
.to be_income1_under_soft_min
end
end
[
{
income1: 5001,
ecstat1: 1,
},
{
income1: 1600,
ecstat1: 2,
},
{
income1: 1004,
ecstat1: 3,
},
{
income1: 2899,
ecstat1: 4,
},
{
income1: 2899,
ecstat1: 5,
},
{
income1: 5,
ecstat1: 6,
},
{
income1: 10_888,
ecstat1: 0,
},
].each do |test_case|
it "returns false if income1 is over soft min for ecstat1 #{test_case[:ecstat1]}" do
record.income1 = test_case[:income1]
record.ecstat1 = test_case[:ecstat1]
expect(record)
.not_to be_income1_under_soft_min
end
end
end
end
describe "mortgage amount validations" do
context "when validating soft max" do
it "returns false if no mortgage is given" do
record.mortgage = nil
expect(record)
.not_to be_mortgage_over_soft_max
end
it "returns false if no inc1mort is given" do
record.inc1mort = nil
record.mortgage = 20_000
expect(record)
.not_to be_mortgage_over_soft_max
end
it "returns false if no inc2mort is given" do
record.inc1mort = 2
record.inc2mort = nil
record.mortgage = 20_000
expect(record)
.not_to be_mortgage_over_soft_max
end
it "returns false if no income1 is given and inc1mort is yes" do
record.inc1mort = 1
record.inc2mort = 2
record.income1 = nil
record.mortgage = 20_000
expect(record)
.not_to be_mortgage_over_soft_max
end
it "returns false if no income2 is given and inc2mort is yes" do
record.inc1mort = 2
record.inc2mort = 1
record.income2 = nil
record.mortgage = 20_000
expect(record)
.not_to be_mortgage_over_soft_max
end
it "returns true if only income1 is used for morgage and it is less than 1/5 of the morgage" do
record.inc1mort = 1
record.income1 = 10_000
record.mortgage = 51_000
record.inc2mort = 2
expect(record)
.to be_mortgage_over_soft_max
end
it "returns false if only income1 is used for morgage and it is more than 1/5 of the morgage" do
record.inc1mort = 1
record.income1 = 10_000
record.mortgage = 44_000
record.inc2mort = 2
expect(record)
.not_to be_mortgage_over_soft_max
end
it "returns true if only income2 is used for morgage and it is less than 1/5 of the morgage" do
record.inc1mort = 2
record.inc2mort = 1
record.income2 = 10_000
record.mortgage = 51_000
expect(record)
.to be_mortgage_over_soft_max
end
it "returns false if only income2 is used for morgage and it is more than 1/5 of the morgage" do
record.inc1mort = 2
record.inc2mort = 1
record.income2 = 10_000
record.mortgage = 44_000
expect(record)
.not_to be_mortgage_over_soft_max
end
it "returns true if income1 and income2 are used for morgage and their sum is less than 1/5 of the morgage" do
record.inc1mort = 1
record.inc2mort = 1
record.income1 = 10_000
record.income2 = 10_000
record.mortgage = 101_000
expect(record)
.to be_mortgage_over_soft_max
end
it "returns false if income1 and income2 are used for morgage and their sum is more than 1/5 of the morgage" do
record.inc1mort = 1
record.inc2mort = 1
record.income1 = 8_000
record.income2 = 17_000
record.mortgage = 124_000
expect(record)
.not_to be_mortgage_over_soft_max
end
it "returns true if neither of the incomes are used for morgage and the morgage is more than 0" do
record.inc1mort = 2
record.inc2mort = 2
record.mortgage = 124_000
expect(record)
.to be_mortgage_over_soft_max
end
it "returns true if neither of the incomes are used for morgage and the morgage is 0" do
record.inc1mort = 2
record.inc2mort = 2
record.mortgage = 0
expect(record)
.not_to be_mortgage_over_soft_max
end
end
end
end
Loading…
Cancel
Save