From 910f13d50757674211b4d4f7edd91de3e09b4053 Mon Sep 17 00:00:00 2001 From: baarkerlounger <5101747+baarkerlounger@users.noreply.github.com> Date: Fri, 25 Mar 2022 10:47:16 +0000 Subject: [PATCH] Household member validations (#420) * Add missing ecstat validation * Add compound validation errors to all involved fields * Update ecstat values * Update values in validations and tests * Update rent rant values * Add some helper methods for readability --- .../validations/household_validations.rb | 45 ++++++- app/models/validations/soft_validations.rb | 18 +-- config/forms/2021_2022.json | 127 +++++++++--------- config/locales/en.yml | 11 ++ spec/factories/case_log.rb | 2 +- spec/fixtures/complete_case_log.json | 16 +-- spec/fixtures/exports/case_logs.xml | 2 +- .../validations/household_validations_spec.rb | 62 ++++++++- 8 files changed, 186 insertions(+), 97 deletions(-) diff --git a/app/models/validations/household_validations.rb b/app/models/validations/household_validations.rb index 830bc3549..c79890fc0 100644 --- a/app/models/validations/household_validations.rb +++ b/app/models/validations/household_validations.rb @@ -131,11 +131,17 @@ private economic_status = record.public_send("ecstat#{person_num}") return unless age && economic_status - if age > 70 && economic_status != 4 + if age > 70 && !tenant_is_retired?(economic_status) record.errors.add "ecstat#{person_num}", I18n.t("validations.household.ecstat.retired_over_70", person_num:) + record.errors.add "age#{person_num}", I18n.t("validations.household.age.retired_over_70", person_num:) end - if age < 16 && economic_status != 8 + if age < 16 && !tenant_is_economic_child?(economic_status) record.errors.add "ecstat#{person_num}", I18n.t("validations.household.ecstat.child_under_16", person_num:) + record.errors.add "age#{person_num}", I18n.t("validations.household.age.child_under_16", person_num:) + end + if tenant_is_economic_child?(economic_status) && age > 16 + record.errors.add "ecstat#{person_num}", I18n.t("validations.household.ecstat.child_over_16", person_num:) + record.errors.add "age#{person_num}", I18n.t("validations.household.age.child_over_16", person_num:) end end @@ -144,8 +150,9 @@ private relationship = record.public_send("relat#{person_num}") return unless age && relationship - if age < 16 && relationship != 1 + if age < 16 && !tenant_is_child?(relationship) record.errors.add "relat#{person_num}", I18n.t("validations.household.relat.child_under_16", person_num:) + record.errors.add "age#{person_num}", I18n.t("validations.household.age.child_under_16_relat", person_num:) end end @@ -155,8 +162,10 @@ private relationship = record.public_send("relat#{person_num}") return unless age && economic_status && relationship - if age >= 16 && age <= 19 && relationship == 1 && (economic_status != 6 && economic_status != 10) + if age >= 16 && age <= 19 && tenant_is_child?(relationship) && (!tenant_is_fulltime_student?(economic_status) && !tenant_economic_status_refused?(economic_status)) record.errors.add "ecstat#{person_num}", I18n.t("validations.household.ecstat.student_16_19", person_num:) + record.errors.add "age#{person_num}", I18n.t("validations.household.age.student_16_19", person_num:) + record.errors.add "relat#{person_num}", I18n.t("validations.household.relat.student_16_19", person_num:) end end @@ -166,11 +175,15 @@ private economic_status = record.public_send("ecstat#{person_num}") return unless age && economic_status && gender - if gender == "M" && economic_status == 4 && age < 65 + if gender == "M" && tenant_is_retired?(economic_status) && age < 65 record.errors.add "age#{person_num}", I18n.t("validations.household.age.retired_male") + record.errors.add "sex#{person_num}", I18n.t("validations.household.gender.retired_male") + record.errors.add "ecstat#{person_num}", I18n.t("validations.household.ecstat.retired_male") end - if gender == "F" && economic_status == 4 && age < 60 + if gender == "F" && tenant_is_retired?(economic_status) && age < 60 record.errors.add "age#{person_num}", I18n.t("validations.household.age.retired_female") + record.errors.add "sex#{person_num}", I18n.t("validations.household.gender.retired_female") + record.errors.add "ecstat#{person_num}", I18n.t("validations.household.ecstat.retired_female") end end @@ -180,4 +193,24 @@ private record.errors.add :base, I18n.t("validations.household.relat.one_partner") end end + + def tenant_is_retired?(economic_status) + economic_status == 5 + end + + def tenant_is_economic_child?(economic_status) + economic_status == 9 + end + + def tenant_is_fulltime_student?(economic_status) + economic_status == 7 + end + + def tenant_economic_status_refused?(economic_status) + economic_status == 10 + end + + def tenant_is_child?(relationship) + relationship == 1 + end end diff --git a/app/models/validations/soft_validations.rb b/app/models/validations/soft_validations.rb index dcf5404ba..ec217ca77 100644 --- a/app/models/validations/soft_validations.rb +++ b/app/models/validations/soft_validations.rb @@ -1,15 +1,15 @@ module Validations::SoftValidations ALLOWED_INCOME_RANGES = { 1 => OpenStruct.new(soft_min: 143, soft_max: 730, hard_min: 90, hard_max: 1230), - 0 => OpenStruct.new(soft_min: 67, soft_max: 620, hard_min: 50, hard_max: 950), - 2 => OpenStruct.new(soft_min: 80, soft_max: 480, hard_min: 40, hard_max: 990), - 3 => OpenStruct.new(soft_min: 50, soft_max: 370, hard_min: 10, hard_max: 450), - 4 => OpenStruct.new(soft_min: 50, soft_max: 380, hard_min: 10, hard_max: 690), - 5 => OpenStruct.new(soft_min: 53, soft_max: 540, hard_min: 10, hard_max: 890), - 6 => OpenStruct.new(soft_min: 47, soft_max: 460, hard_min: 10, hard_max: 1300), - 7 => OpenStruct.new(soft_min: 54, soft_max: 460, hard_min: 10, hard_max: 820), - 8 => OpenStruct.new(soft_min: 50, soft_max: 450, hard_min: 10, hard_max: 750), - 9 => OpenStruct.new(soft_min: 50, soft_max: 580, hard_min: 10, hard_max: 1040), + 2 => OpenStruct.new(soft_min: 67, soft_max: 620, hard_min: 50, hard_max: 950), + 3 => OpenStruct.new(soft_min: 80, soft_max: 480, hard_min: 40, hard_max: 990), + 4 => OpenStruct.new(soft_min: 50, soft_max: 370, hard_min: 10, hard_max: 450), + 5 => OpenStruct.new(soft_min: 50, soft_max: 380, hard_min: 10, hard_max: 690), + 6 => OpenStruct.new(soft_min: 53, soft_max: 540, hard_min: 10, hard_max: 890), + 7 => OpenStruct.new(soft_min: 47, soft_max: 460, hard_min: 10, hard_max: 1300), + 8 => OpenStruct.new(soft_min: 54, soft_max: 460, hard_min: 10, hard_max: 820), + 9 => OpenStruct.new(soft_min: 50, soft_max: 450, hard_min: 10, hard_max: 750), + 0 => OpenStruct.new(soft_min: 50, soft_max: 580, hard_min: 10, hard_max: 1040), 10 => OpenStruct.new(soft_min: 47, soft_max: 730, hard_min: 10, hard_max: 1300), }.freeze diff --git a/config/forms/2021_2022.json b/config/forms/2021_2022.json index 4216c416d..9e734871a 100644 --- a/config/forms/2021_2022.json +++ b/config/forms/2021_2022.json @@ -1537,13 +1537,13 @@ "hint_text": "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.", "type": "radio", "answer_options": { - "0": { + "2": { "value": "Part-time – Less than 30 hours" }, "1": { "value": "Full-time – 30 hours or more" }, - "2": { + "7": { "value": "Full-time student" }, "3": { @@ -1552,19 +1552,16 @@ "4": { "value": "Jobseeker" }, - "5": { + "6": { "value": "Not seeking work" }, - "6": { + "8": { "value": "Unable to work because of long term sick or disability" }, - "7": { + "5": { "value": "Retired" }, - "8": { - "value": "Child under 16" - }, - "9": { + "0": { "value": "Other" }, "divider": { @@ -1743,13 +1740,13 @@ "hint_text": "", "type": "radio", "answer_options": { - "0": { + "2": { "value": "Part-time – Less than 30 hours" }, "1": { "value": "Full-time – 30 hours or more" }, - "2": { + "7": { "value": "Full-time student" }, "3": { @@ -1758,26 +1755,26 @@ "4": { "value": "Jobseeker" }, - "5": { + "6": { "value": "Not seeking work" }, - "6": { + "8": { "value": "Unable to work because of long term sick or disability" }, - "7": { + "5": { "value": "Retired" }, - "8": { + "9": { "value": "Child under 16" }, - "9": { + "0": { "value": "Other" }, "divider": { "value": true }, "10": { - "value": "Person prefers not to say" + "value": "Tenant prefers not to say" } } } @@ -1951,13 +1948,13 @@ "hint_text": "", "type": "radio", "answer_options": { - "0": { + "2": { "value": "Part-time – Less than 30 hours" }, "1": { "value": "Full-time – 30 hours or more" }, - "2": { + "7": { "value": "Full-time student" }, "3": { @@ -1966,26 +1963,26 @@ "4": { "value": "Jobseeker" }, - "5": { + "6": { "value": "Not seeking work" }, - "6": { + "8": { "value": "Unable to work because of long term sick or disability" }, - "7": { + "5": { "value": "Retired" }, - "8": { + "9": { "value": "Child under 16" }, - "9": { + "0": { "value": "Other" }, "divider": { "value": true }, "10": { - "value": "Person prefers not to say" + "value": "Tenant prefers not to say" } } } @@ -2156,13 +2153,13 @@ "hint_text": "", "type": "radio", "answer_options": { - "0": { + "2": { "value": "Part-time – Less than 30 hours" }, "1": { "value": "Full-time – 30 hours or more" }, - "2": { + "7": { "value": "Full-time student" }, "3": { @@ -2171,26 +2168,26 @@ "4": { "value": "Jobseeker" }, - "5": { + "6": { "value": "Not seeking work" }, - "6": { + "8": { "value": "Unable to work because of long term sick or disability" }, - "7": { + "5": { "value": "Retired" }, - "8": { + "9": { "value": "Child under 16" }, - "9": { + "0": { "value": "Other" }, "divider": { "value": true }, "10": { - "value": "Person prefers not to say" + "value": "Tenant prefers not to say" } } } @@ -2358,13 +2355,13 @@ "hint_text": "", "type": "radio", "answer_options": { - "0": { + "2": { "value": "Part-time – Less than 30 hours" }, "1": { "value": "Full-time – 30 hours or more" }, - "2": { + "7": { "value": "Full-time student" }, "3": { @@ -2373,26 +2370,26 @@ "4": { "value": "Jobseeker" }, - "5": { + "6": { "value": "Not seeking work" }, - "6": { + "8": { "value": "Unable to work because of long term sick or disability" }, - "7": { + "5": { "value": "Retired" }, - "8": { + "9": { "value": "Child under 16" }, - "9": { + "0": { "value": "Other" }, "divider": { "value": true }, "10": { - "value": "Person prefers not to say" + "value": "Tenant prefers not to say" } } } @@ -2557,13 +2554,13 @@ "hint_text": "", "type": "radio", "answer_options": { - "0": { + "2": { "value": "Part-time – Less than 30 hours" }, "1": { "value": "Full-time – 30 hours or more" }, - "2": { + "7": { "value": "Full-time student" }, "3": { @@ -2572,26 +2569,26 @@ "4": { "value": "Jobseeker" }, - "5": { + "6": { "value": "Not seeking work" }, - "6": { + "8": { "value": "Unable to work because of long term sick or disability" }, - "7": { + "5": { "value": "Retired" }, - "8": { + "9": { "value": "Child under 16" }, - "9": { + "0": { "value": "Other" }, "divider": { "value": true }, "10": { - "value": "Person prefers not to say" + "value": "Tenant prefers not to say" } } } @@ -2753,13 +2750,13 @@ "hint_text": "", "type": "radio", "answer_options": { - "0": { + "2": { "value": "Part-time – Less than 30 hours" }, "1": { "value": "Full-time – 30 hours or more" }, - "2": { + "7": { "value": "Full-time student" }, "3": { @@ -2768,26 +2765,26 @@ "4": { "value": "Jobseeker" }, - "5": { + "6": { "value": "Not seeking work" }, - "6": { + "8": { "value": "Unable to work because of long term sick or disability" }, - "7": { + "5": { "value": "Retired" }, - "8": { + "9": { "value": "Child under 16" }, - "9": { + "0": { "value": "Other" }, "divider": { "value": true }, "10": { - "value": "Person prefers not to say" + "value": "Tenant prefers not to say" } } } @@ -2946,13 +2943,13 @@ "hint_text": "", "type": "radio", "answer_options": { - "0": { + "2": { "value": "Part-time – Less than 30 hours" }, "1": { "value": "Full-time – 30 hours or more" }, - "2": { + "7": { "value": "Full-time student" }, "3": { @@ -2961,26 +2958,26 @@ "4": { "value": "Jobseeker" }, - "5": { + "6": { "value": "Not seeking work" }, - "6": { + "8": { "value": "Unable to work because of long term sick or disability" }, - "7": { + "5": { "value": "Retired" }, - "8": { + "9": { "value": "Child under 16" }, - "9": { + "0": { "value": "Other" }, "divider": { "value": true }, "10": { - "value": "Person prefers not to say" + "value": "Tenant prefers not to say" } } } diff --git a/config/locales/en.yml b/config/locales/en.yml index 0e43522a7..3c213a1f1 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -152,15 +152,24 @@ en: age: retired_male: "Male tenant who is retired must be 65 or over" retired_female: "Female tenant who is retired must be 60 or over" + retired_over_70: 'Answer cannot be over 70 as as tenant %{person_num} has economic status that is not "Retired"' + child_under_16_relat: 'Answer cannot be under 16 as tenant %{person_num} is not a child of the lead tenant' + child_under_16: 'Answer cannot be under 16 as tenant %{person_num} has economic status that is not "Child under 16"' + child_over_16: 'Answer cannot be over 16 as tenant %{person_num} has economic status "Child under 16"' + student_16_19: 'Answer cannot be between 16 and 19 as tenant %{person_num} is a child of the lead tenant but is not a full time student' lead: over_20: "Lead tenant must be under 20 as you told us that their housing situation immediately before this letting was a children's home or foster care" ecstat: retired_over_70: "Tenant %{person_num} must be retired if over 70" child_under_16: "Tenant %{person_num} economic status must be Child under 16 if their age is under 16" + child_over_16: 'Answer cannot be "Child under 16" as tenant %{person_num} is older than 16' student_16_19: "If age is between 16 and 19 - tenant %{person_num} must be a full time student or prefer not to say." + retired_male: "Male tenant who is under 65 cannot be retired" + retired_female: "Female tenant who is under 60 cannot be retired" relat: child_under_16: "Tenant %{person_num}’s relationship to tenant 1 must be Child if their age is under 16" one_partner: "Number of partners cannot be greater than 1" + student_16_19: 'Answer cannot be "Child" as tenant %{person_num} is between 16 and 19 but is not a full time student' housingneeds_a: one_or_two_choices: "Only one box must be ticked or 'other disabilities' plus one of mobility disabilities" prevten: @@ -185,6 +194,8 @@ en: not_homeless: "Answer cannot be ‘no’ as you already told us the tenant was homeless or about to lose their home" previous_la_known: "Enter a local authority" gender: + retired_male: 'Answer cannot be "Male" as tenant is under 65 and retired' + retired_female: 'Answer cannot be "Female" as tenant is under 60 and retired' male_refuge: "Answer cannot be male as you told us their housing situation immediately before this letting was a refuge" reason: not_internal_transfer: "Answer cannot be permanently decanted from another property owned by this landlord as you told us the source of referral for this tenancy was not an internal transfer" diff --git a/spec/factories/case_log.rb b/spec/factories/case_log.rb index f23f3c10c..c900cc84e 100644 --- a/spec/factories/case_log.rb +++ b/spec/factories/case_log.rb @@ -46,7 +46,7 @@ FactoryBot.define do relat2 { 0 } age2 { 32 } sex2 { "M" } - ecstat2 { 5 } + ecstat2 { 6 } homeless { 1 } underoccupation_benefitcap { 0 } leftreg { 1 } diff --git a/spec/fixtures/complete_case_log.json b/spec/fixtures/complete_case_log.json index 3b6ff4637..b81719cb9 100644 --- a/spec/fixtures/complete_case_log.json +++ b/spec/fixtures/complete_case_log.json @@ -14,31 +14,31 @@ "relat2": 0, "age2": 32, "sex2": "M", - "ecstat2": 5, + "ecstat2": 6, "relat3": 1, "age3": 12, "sex3": "M", - "ecstat3": 8, + "ecstat3": 9, "relat4": 1, "age4": 12, "sex4": "F", - "ecstat4": 8, + "ecstat4": 9, "relat5": 1, "age5": 10, "sex5": "X", - "ecstat5": 8, + "ecstat5": 9, "relat6": 1, "age6": 5, "sex6": "R", - "ecstat6": 8, + "ecstat6": 9, "relat7": 1, "age7": 5, "sex7": "R", - "ecstat7": 8, + "ecstat7": 9, "relat8": 1, "age8": 2, "sex8": "R", - "ecstat8": 8, + "ecstat8": 9, "homeless": 2, "reason": 1, "underoccupation_benefitcap": 0, @@ -66,7 +66,7 @@ "property_void_date": "10/10/2020", "vday": 10, "vmonth": 10, - "vyear": 2020, + "vyear": 2020, "majorrepairs": 1, "mrcdate": "11/11/2020", "mrcday": 11, diff --git a/spec/fixtures/exports/case_logs.xml b/spec/fixtures/exports/case_logs.xml index d3eaa7d42..649947215 100644 --- a/spec/fixtures/exports/case_logs.xml +++ b/spec/fixtures/exports/case_logs.xml @@ -15,7 +15,7 @@ 2 32 M - 5 + 6 diff --git a/spec/models/validations/household_validations_spec.rb b/spec/models/validations/household_validations_spec.rb index 9819c3d49..605197739 100644 --- a/spec/models/validations/household_validations_spec.rb +++ b/spec/models/validations/household_validations_spec.rb @@ -296,6 +296,8 @@ RSpec.describe Validations::HouseholdValidations do household_validator.validate_household_number_of_other_members(record) expect(record.errors["relat2"]) .to include(match I18n.t("validations.household.relat.child_under_16", person_num: 2)) + expect(record.errors["age2"]) + .to include(match I18n.t("validations.household.age.child_under_16_relat", person_num: 2)) end it "expects that person is a child of the tenant" do @@ -303,6 +305,7 @@ RSpec.describe Validations::HouseholdValidations do record.relat2 = 1 household_validator.validate_household_number_of_other_members(record) expect(record.errors["relat2"]).to be_empty + expect(record.errors["age2"]).to be_empty end it "validates that person's economic status must be Child" do @@ -311,13 +314,27 @@ RSpec.describe Validations::HouseholdValidations do household_validator.validate_household_number_of_other_members(record) expect(record.errors["ecstat2"]) .to include(match I18n.t("validations.household.ecstat.child_under_16", person_num: 2)) + expect(record.errors["age2"]) + .to include(match I18n.t("validations.household.age.child_under_16", person_num: 2)) end it "expects that person's economic status is Child" do record.age2 = 14 - record.ecstat2 = 8 + record.ecstat2 = 9 household_validator.validate_household_number_of_other_members(record) expect(record.errors["ecstat2"]).to be_empty + expect(record.errors["age2"]).to be_empty + end + + it "validates that a person with economic status 'child' must be under 16" do + record.age2 = 21 + record.relat2 = 1 + record.ecstat2 = 9 + household_validator.validate_household_number_of_other_members(record) + expect(record.errors["ecstat2"]) + .to include(match I18n.t("validations.household.ecstat.child_over_16", person_num: 2)) + expect(record.errors["age2"]) + .to include(match I18n.t("validations.household.age.child_over_16", person_num: 2)) end end @@ -329,14 +346,20 @@ RSpec.describe Validations::HouseholdValidations do household_validator.validate_household_number_of_other_members(record) expect(record.errors["ecstat2"]) .to include(match I18n.t("validations.household.ecstat.student_16_19", person_num: 2)) + expect(record.errors["age2"]) + .to include(match I18n.t("validations.household.age.student_16_19", person_num: 2)) + expect(record.errors["relat2"]) + .to include(match I18n.t("validations.household.relat.student_16_19", person_num: 2)) end it "expects that person can be a full time student" do record.age2 = 17 record.relat2 = 1 - record.ecstat2 = 6 + record.ecstat2 = 7 household_validator.validate_household_number_of_other_members(record) expect(record.errors["ecstat2"]).to be_empty + expect(record.errors["age2"]).to be_empty + expect(record.errors["relat2"]).to be_empty end it "expects that person can refuse to share their work status" do @@ -345,6 +368,8 @@ RSpec.describe Validations::HouseholdValidations do record.ecstat2 = 10 household_validator.validate_household_number_of_other_members(record) expect(record.errors["ecstat2"]).to be_empty + expect(record.errors["age2"]).to be_empty + expect(record.errors["relat2"]).to be_empty end end @@ -355,13 +380,24 @@ RSpec.describe Validations::HouseholdValidations do household_validator.validate_household_number_of_other_members(record) expect(record.errors["ecstat2"]) .to include(match I18n.t("validations.household.ecstat.retired_over_70", person_num: 2)) + expect(record.errors["age2"]) + .to include(match I18n.t("validations.household.age.retired_over_70", person_num: 2)) end - it "expects that person is retired" do + it "expects that person under 70 does not need to be retired" do record.age2 = 50 record.ecstat2 = 1 household_validator.validate_household_number_of_other_members(record) expect(record.errors["ecstat2"]).to be_empty + expect(record.errors["age2"]).to be_empty + end + + it "expects that person over 70 is retired" do + record.age2 = 71 + record.ecstat2 = 5 + household_validator.validate_household_number_of_other_members(record) + expect(record.errors["ecstat2"]).to be_empty + expect(record.errors["age2"]).to be_empty end end @@ -369,19 +405,25 @@ RSpec.describe Validations::HouseholdValidations do it "validates that person must be over 65" do record.age2 = 64 record.sex2 = "M" - record.ecstat2 = 4 + record.ecstat2 = 5 household_validator.validate_household_number_of_other_members(record) expect(record.errors["age2"]) .to include(match I18n.t("validations.household.age.retired_male")) + expect(record.errors["sex2"]) + .to include(match I18n.t("validations.household.gender.retired_male")) + expect(record.errors["ecstat2"]) + .to include(match I18n.t("validations.household.ecstat.retired_male")) end it "expects that person is over 65" do record.age2 = 66 record.sex2 = "M" - record.ecstat2 = 4 + record.ecstat2 = 5 household_validator.validate_household_number_of_other_members(record) household_validator.validate_household_number_of_other_members(record) expect(record.errors["ecstat2"]).to be_empty + expect(record.errors["sex2"]).to be_empty + expect(record.errors["age2"]).to be_empty end it "validates that the number of other household members cannot be less than 0" do @@ -409,19 +451,25 @@ RSpec.describe Validations::HouseholdValidations do it "validates that person must be over 60" do record.age2 = 59 record.sex2 = "F" - record.ecstat2 = 4 + record.ecstat2 = 5 household_validator.validate_household_number_of_other_members(record) expect(record.errors["age2"]) .to include(match I18n.t("validations.household.age.retired_female")) + expect(record.errors["sex2"]) + .to include(match I18n.t("validations.household.gender.retired_female")) + expect(record.errors["ecstat2"]) + .to include(match I18n.t("validations.household.ecstat.retired_female")) end it "expects that person is over 60" do record.age2 = 61 record.sex2 = "F" - record.ecstat2 = 4 + record.ecstat2 = 5 household_validator.validate_household_number_of_other_members(record) household_validator.validate_household_number_of_other_members(record) expect(record.errors["ecstat2"]).to be_empty + expect(record.errors["sex2"]).to be_empty + expect(record.errors["age2"]).to be_empty end end end