Submit social housing lettings and sales data (CORE)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

525 lines
24 KiB

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 for shared ownership" do
let(:record) { FactoryBot.create(:sales_log, ownershipsch: 1) }
context "when buying in a non london borough" do
before do
record.update!(la: "E08000035")
record.reload
end
it "adds errors if buyer 1 has income over 80,000" do
record.income1 = 85_000
financial_validator.validate_income1(record)
expect(record.errors["income1"]).to include(match I18n.t("validations.financial.income.outside_non_london_income_range"))
expect(record.errors["ownershipsch"]).to include(match I18n.t("validations.financial.income.outside_non_london_income_range"))
expect(record.errors["la"]).to include(match I18n.t("validations.financial.income.outside_non_london_income_range"))
expect(record.errors["postcode_full"]).to include(match I18n.t("validations.financial.income.outside_non_london_income_range"))
end
it "adds errors if buyer 2 has income over 80,000" do
record.income2 = 85_000
financial_validator.validate_income2(record)
expect(record.errors["income2"]).to include(match I18n.t("validations.financial.income.outside_non_london_income_range"))
expect(record.errors["ownershipsch"]).to include(match I18n.t("validations.financial.income.outside_non_london_income_range"))
expect(record.errors["la"]).to include(match I18n.t("validations.financial.income.outside_non_london_income_range"))
expect(record.errors["postcode_full"]).to include(match I18n.t("validations.financial.income.outside_non_london_income_range"))
end
it "does not add errors if buyer 1 has income above 0 and below 80_000" do
record.income1 = 75_000
financial_validator.validate_income1(record)
expect(record.errors).to be_empty
end
it "does not add errors if buyer 2 has income above 0 and below 80_000" do
record.income2 = 75_000
financial_validator.validate_income2(record)
expect(record.errors).to be_empty
end
it "adds errors if buyer 1 has income below 0" do
record.income1 = -500
financial_validator.validate_income1(record)
expect(record.errors["income1"]).to include(match I18n.t("validations.financial.income.outside_non_london_income_range"))
expect(record.errors["ownershipsch"]).to include(match I18n.t("validations.financial.income.outside_non_london_income_range"))
expect(record.errors["la"]).to include(match I18n.t("validations.financial.income.outside_non_london_income_range"))
expect(record.errors["postcode_full"]).to include(match I18n.t("validations.financial.income.outside_non_london_income_range"))
end
it "adds errors if buyer 2 has income below 0" do
record.income2 = -5
financial_validator.validate_income2(record)
expect(record.errors["income2"]).to include(match I18n.t("validations.financial.income.outside_non_london_income_range"))
expect(record.errors["ownershipsch"]).to include(match I18n.t("validations.financial.income.outside_non_london_income_range"))
expect(record.errors["la"]).to include(match I18n.t("validations.financial.income.outside_non_london_income_range"))
expect(record.errors["postcode_full"]).to include(match I18n.t("validations.financial.income.outside_non_london_income_range"))
end
it "adds errors when combined income is over 80_000" do
record.income1 = 45_000
record.income2 = 40_000
financial_validator.validate_combined_income(record)
expect(record.errors["income1"]).to include(match I18n.t("validations.financial.income.combined_over_hard_max_for_outside_london"))
expect(record.errors["income2"]).to include(match I18n.t("validations.financial.income.combined_over_hard_max_for_outside_london"))
end
it "does not add errors when combined income is under 80_000" do
record.income1 = 35_000
record.income2 = 40_000
financial_validator.validate_combined_income(record)
expect(record.errors).to be_empty
end
end
context "when buying in a london borough" do
before do
record.update!(la: "E09000030")
record.reload
end
it "adds errors if buyer 1 has income over 90,000" do
record.income1 = 95_000
financial_validator.validate_income1(record)
expect(record.errors["income1"]).to include(match I18n.t("validations.financial.income.outside_london_income_range"))
expect(record.errors["ownershipsch"]).to include(match I18n.t("validations.financial.income.outside_london_income_range"))
expect(record.errors["la"]).to include(match I18n.t("validations.financial.income.outside_london_income_range"))
expect(record.errors["postcode_full"]).to include(match I18n.t("validations.financial.income.outside_london_income_range"))
end
it "adds errors if buyer 2 has income over 90,000" do
record.income2 = 95_000
financial_validator.validate_income2(record)
expect(record.errors["income2"]).to include(match I18n.t("validations.financial.income.outside_london_income_range"))
expect(record.errors["ownershipsch"]).to include(match I18n.t("validations.financial.income.outside_london_income_range"))
expect(record.errors["la"]).to include(match I18n.t("validations.financial.income.outside_london_income_range"))
expect(record.errors["postcode_full"]).to include(match I18n.t("validations.financial.income.outside_london_income_range"))
end
it "does not add errors if buyer 1 has income above 0 and below 90_000" do
record.income1 = 75_000
financial_validator.validate_income1(record)
expect(record.errors).to be_empty
end
it "does not add errors if buyer 2 has income above 0 and below 90_000" do
record.income2 = 75_000
financial_validator.validate_income2(record)
expect(record.errors).to be_empty
end
it "adds errors if buyer 1 has income below 0" do
record.income1 = -500
financial_validator.validate_income1(record)
expect(record.errors["income1"]).to include(match I18n.t("validations.financial.income.outside_london_income_range"))
expect(record.errors["ownershipsch"]).to include(match I18n.t("validations.financial.income.outside_london_income_range"))
expect(record.errors["la"]).to include(match I18n.t("validations.financial.income.outside_london_income_range"))
expect(record.errors["postcode_full"]).to include(match I18n.t("validations.financial.income.outside_london_income_range"))
end
it "adds errors if buyer 2 has income below 0" do
record.income2 = -2
financial_validator.validate_income2(record)
expect(record.errors["income2"]).to include(match I18n.t("validations.financial.income.outside_london_income_range"))
expect(record.errors["ownershipsch"]).to include(match I18n.t("validations.financial.income.outside_london_income_range"))
expect(record.errors["la"]).to include(match I18n.t("validations.financial.income.outside_london_income_range"))
expect(record.errors["postcode_full"]).to include(match I18n.t("validations.financial.income.outside_london_income_range"))
end
it "adds errors when combined income is over 90_000" do
record.income1 = 55_000
record.income2 = 40_000
financial_validator.validate_combined_income(record)
expect(record.errors["income1"]).to include(match I18n.t("validations.financial.income.combined_over_hard_max_for_london"))
expect(record.errors["income2"]).to include(match I18n.t("validations.financial.income.combined_over_hard_max_for_london"))
end
it "does not add errors when combined income is under 90_000" do
record.income1 = 35_000
record.income2 = 40_000
financial_validator.validate_combined_income(record)
expect(record.errors).to be_empty
end
end
end
describe "#validate_mortgage" do
let(:record) { FactoryBot.create(:sales_log) }
it "adds an error is the mortgage is zero" do
record.mortgageused = 1
record.mortgage = 0
financial_validator.validate_mortgage(record)
expect(record.errors[:mortgage]).to include I18n.t("validations.financial.mortgage")
end
it "does not add an error is the mortgage is positive" do
record.mortgageused = 1
record.mortgage = 234
financial_validator.validate_mortgage(record)
expect(record.errors).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).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).to be_empty
end
it "adds an error to stairowned and not stairbought if the percentage bought is more than the percentage owned for joint purchase" do
record.stairbought = 50
record.stairowned = 40
record.jointpur = 1
financial_validator.validate_percentage_bought_not_greater_than_percentage_owned(record)
expect(record.errors["stairowned"]).to include("Total percentage buyers now own must be more than percentage bought in this transaction")
end
it "adds an error to stairowned and not stairbought if the percentage bought is more than the percentage owned for non joint purchase" do
record.stairbought = 50
record.stairowned = 40
record.jointpur = 2
financial_validator.validate_percentage_bought_not_greater_than_percentage_owned(record)
expect(record.errors["stairowned"]).to include("Total percentage buyer now owns must be more than percentage bought in this transaction")
end
end
describe "#validate_percentage_bought_not_equal_percentage_owned" do
let(:record) { FactoryBot.create(:sales_log) }
context "with 24/25 logs" do
before do
record.saledate = Time.zone.local(2024, 4, 3)
record.save!(validate: false)
end
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_equal_percentage_owned(record)
expect(record.errors).to be_empty
end
it "adds 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_equal_percentage_owned(record)
expect(record.errors["stairowned"]).to include("The percentage bought is 30% and the percentage owned in total is 30%. These figures cannot be the same.")
expect(record.errors["stairbought"]).to include("The percentage bought is 30% and the percentage owned in total is 30%. These figures cannot be the same.")
end
it "does not add 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_equal_percentage_owned(record)
expect(record.errors).to be_empty
end
end
context "with 23/24 logs" do
before do
record.saledate = Time.zone.local(2023, 4, 3)
record.save!(validate: false)
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_equal_percentage_owned(record)
expect(record.errors).to be_empty
end
end
end
describe "#validate_monthly_leasehold_charges" do
let(:record) { FactoryBot.create(:sales_log) }
it "does not add an error if monthly leasehold charges are positive" do
record.mscharge = 2345
financial_validator.validate_monthly_leasehold_charges(record)
expect(record.errors).to be_empty
end
it "adds an error if monthly leasehold charges are zero" do
record.mscharge = 0
financial_validator.validate_monthly_leasehold_charges(record)
expect(record.errors[:mscharge]).to include I18n.t("validations.financial.monthly_leasehold_charges.not_zero")
end
end
describe "#validate_percentage_bought_at_least_threshold" do
let(:record) { FactoryBot.create(:sales_log) }
it "adds an error to stairbought and type if the percentage bought is less than the threshold (which is 1% by default, but higher for some shared ownership types)" do
record.stairbought = 9
[2, 16, 18, 24].each do |type|
record.type = type
financial_validator.validate_percentage_bought_at_least_threshold(record)
expect(record.errors["stairbought"]).to eq(["The minimum increase in equity while staircasing is 10%"])
expect(record.errors["type"]).to eq(["The minimum increase in equity while staircasing is 10% for this shared ownership type"])
record.errors.clear
end
record.stairbought = 0
[28, 30, 31, 32].each do |type|
record.type = type
financial_validator.validate_percentage_bought_at_least_threshold(record)
expect(record.errors["stairbought"]).to eq(["The minimum increase in equity while staircasing is 1%"])
expect(record.errors["type"]).to eq(["The minimum increase in equity while staircasing is 1% for this shared ownership type"])
record.errors.clear
end
end
it "doesn't add an error to stairbought and type if the percentage bought is less than the threshold (which is 1% by default, but higher for some shared ownership types)" do
record.stairbought = 10
[2, 16, 18, 24].each do |type|
record.type = type
financial_validator.validate_percentage_bought_at_least_threshold(record)
expect(record.errors).to be_empty
record.errors.clear
end
record.stairbought = 1
[28, 30, 31, 32].each do |type|
record.type = type
financial_validator.validate_percentage_bought_at_least_threshold(record)
expect(record.errors).to be_empty
record.errors.clear
end
end
end
describe "#validate_child_income" do
let(:record) { FactoryBot.create(:sales_log) }
context "when buyer 2 is not a child" do
before do
record.update!(ecstat2: rand(0..8))
record.reload
end
it "does not add an error if buyer 2 has an income" do
record.ecstat2 = rand(0..8)
record.income2 = 40_000
financial_validator.validate_child_income(record)
expect(record.errors).to be_empty
end
end
context "when buyer 2 is a child" do
it "does not add an error if buyer 2 has no income" do
record.saledate = Time.zone.local(2023, 4, 3)
record.ecstat2 = 9
record.income2 = 0
financial_validator.validate_child_income(record)
expect(record.errors).to be_empty
end
it "adds errors if buyer 2 has an income" do
record.saledate = Time.zone.local(2023, 4, 3)
record.ecstat2 = 9
record.income2 = 40_000
financial_validator.validate_child_income(record)
expect(record.errors["ecstat2"]).to include(match I18n.t("validations.financial.income.child_has_income"))
expect(record.errors["income2"]).to include(match I18n.t("validations.financial.income.child_has_income"))
end
it "does not add an error if the saledate is before the 23/24 collection window" do
record.saledate = Time.zone.local(2022, 4, 3)
record.ecstat2 = 9
record.income2 = 40_000
financial_validator.validate_child_income(record)
expect(record.errors).to be_empty
end
end
end
describe "#validate_equity_in_range_for_year_and_type" do
let(:record) { FactoryBot.create(:sales_log, saledate: now) }
around do |example|
Timecop.freeze(now) do
example.run
end
Timecop.return
end
context "with a log in the 22/23 collection year" do
let(:now) { Time.zone.local(2023, 1, 1) }
it "adds an error for type 2, equity below min with the correct percentage" do
record.type = 2
record.equity = 1
financial_validator.validate_equity_in_range_for_year_and_type(record)
expect(record.errors["equity"]).to include(match I18n.t("validations.financial.equity.under_min", min_equity: 25))
expect(record.errors["type"]).to include(match I18n.t("validations.financial.equity.under_min", min_equity: 25))
end
it "adds an error for type 30, equity below min with the correct percentage" do
record.type = 30
record.equity = 1
financial_validator.validate_equity_in_range_for_year_and_type(record)
expect(record.errors["equity"]).to include(match I18n.t("validations.financial.equity.under_min", min_equity: 10))
expect(record.errors["type"]).to include(match I18n.t("validations.financial.equity.under_min", min_equity: 10))
end
it "does not add an error for equity in range with the correct percentage" do
record.type = 2
record.equity = 50
financial_validator.validate_equity_in_range_for_year_and_type(record)
expect(record.errors).to be_empty
end
it "adds an error for equity above max with the correct percentage" do
record.type = 2
record.equity = 90
financial_validator.validate_equity_in_range_for_year_and_type(record)
expect(record.errors["equity"]).to include(match I18n.t("validations.financial.equity.over_max", max_equity: 75))
expect(record.errors["type"]).to include(match I18n.t("validations.financial.equity.over_max", max_equity: 75))
end
end
context "with a log in 23/24 collection year" do
let(:now) { Time.zone.local(2024, 1, 1) }
it "adds an error for type 2, equity below min with the correct percentage" do
record.type = 2
record.equity = 1
financial_validator.validate_equity_in_range_for_year_and_type(record)
expect(record.errors["equity"]).to include(match I18n.t("validations.financial.equity.under_min", min_equity: 25))
expect(record.errors["type"]).to include(match I18n.t("validations.financial.equity.under_min", min_equity: 25))
end
it "adds an error for type 30, equity below min with the correct percentage" do
record.type = 30
record.equity = 1
financial_validator.validate_equity_in_range_for_year_and_type(record)
expect(record.errors["equity"]).to include(match I18n.t("validations.financial.equity.under_min", min_equity: 10))
expect(record.errors["type"]).to include(match I18n.t("validations.financial.equity.under_min", min_equity: 10))
end
it "does not add an error for equity in range with the correct percentage" do
record.type = 2
record.equity = 50
financial_validator.validate_equity_in_range_for_year_and_type(record)
expect(record.errors).to be_empty
end
it "adds an error for equity above max with the correct percentage" do
record.type = 2
record.equity = 90
financial_validator.validate_equity_in_range_for_year_and_type(record)
expect(record.errors["equity"]).to include(match I18n.t("validations.financial.equity.over_max", max_equity: 75))
expect(record.errors["type"]).to include(match I18n.t("validations.financial.equity.over_max", max_equity: 75))
end
end
end
describe "#validate_equity_less_than_staircase_difference" do
let(:record) { FactoryBot.create(:sales_log, saledate: now) }
around do |example|
Timecop.freeze(now) do
Singleton.__init__(FormHandler)
example.run
end
Timecop.return
Singleton.__init__(FormHandler)
end
context "with a log in the 23/24 collection year" do
let(:now) { Time.zone.local(2023, 4, 1) }
it "does not add an error" do
record.stairbought = 2
record.stairowned = 3
record.equity = 2
financial_validator.validate_equity_less_than_staircase_difference(record)
expect(record.errors).to be_empty
end
end
context "with a log in 24/25 collection year" do
let(:now) { Time.zone.local(2024, 4, 1) }
it "adds errors if equity is more than stairowned - stairbought for joint purchase" do
record.stairbought = 2
record.stairowned = 3
record.equity = 2
record.jointpur = 1
financial_validator.validate_equity_less_than_staircase_difference(record)
expect(record.errors["equity"]).to include("The initial equity stake is 2% and the percentage owned in total minus the percentage bought is 1%. In a staircasing transaction, the equity stake purchased cannot be larger than the percentage the buyers own minus the percentage bought.")
expect(record.errors["stairowned"]).to include("The initial equity stake is 2% and the percentage owned in total minus the percentage bought is 1%. In a staircasing transaction, the equity stake purchased cannot be larger than the percentage the buyers own minus the percentage bought.")
expect(record.errors["stairbought"]).to include("The initial equity stake is 2% and the percentage owned in total minus the percentage bought is 1%. In a staircasing transaction, the equity stake purchased cannot be larger than the percentage the buyers own minus the percentage bought.")
end
it "adds errors if equity is more than stairowned - stairbought for non joint purchase" do
record.stairbought = 2
record.stairowned = 3
record.equity = 2
record.jointpur = 2
financial_validator.validate_equity_less_than_staircase_difference(record)
expect(record.errors["equity"]).to include("The initial equity stake is 2% and the percentage owned in total minus the percentage bought is 1%. In a staircasing transaction, the equity stake purchased cannot be larger than the percentage the buyer owns minus the percentage bought.")
expect(record.errors["stairowned"]).to include("The initial equity stake is 2% and the percentage owned in total minus the percentage bought is 1%. In a staircasing transaction, the equity stake purchased cannot be larger than the percentage the buyer owns minus the percentage bought.")
expect(record.errors["stairbought"]).to include("The initial equity stake is 2% and the percentage owned in total minus the percentage bought is 1%. In a staircasing transaction, the equity stake purchased cannot be larger than the percentage the buyer owns minus the percentage bought.")
end
it "does not add errors if equity is less than stairowned - stairbought" do
record.stairbought = 2
record.stairowned = 10
record.equity = 2
financial_validator.validate_equity_less_than_staircase_difference(record)
expect(record.errors).to be_empty
end
it "does not add errors if equity is equal stairowned - stairbought" do
record.stairbought = 2
record.stairowned = 10
record.equity = 8
financial_validator.validate_equity_less_than_staircase_difference(record)
expect(record.errors).to be_empty
end
it "does not add errors if stairbought is not given" do
record.stairbought = nil
record.stairowned = 10
record.equity = 2
financial_validator.validate_equity_less_than_staircase_difference(record)
expect(record.errors).to be_empty
end
it "does not add errors if stairowned is not given" do
record.stairbought = 2
record.stairowned = nil
record.equity = 2
financial_validator.validate_equity_less_than_staircase_difference(record)
expect(record.errors).to be_empty
end
it "does not add errors if equity is not given" do
record.stairbought = 2
record.stairowned = 10
record.equity = 0
financial_validator.validate_equity_less_than_staircase_difference(record)
expect(record.errors).to be_empty
end
end
end
end