diff --git a/app/models/derived_variables/lettings_log_variables.rb b/app/models/derived_variables/lettings_log_variables.rb index d0aab14cb..ac17e0219 100644 --- a/app/models/derived_variables/lettings_log_variables.rb +++ b/app/models/derived_variables/lettings_log_variables.rb @@ -91,6 +91,9 @@ module DerivedVariables::LettingsLogVariables self.prevten = 30 if owning_organisation&.provider_type == "LA" end end + if form.start_year_after_2024? && is_bedsit? + self.beds = 1 + end child_under_16_constraints! @@ -179,6 +182,9 @@ private self.wchair = nil self.location_id = nil end + if form.start_year_after_2024? && (unittype_gn_changed? && unittype_gn_was == 2) + self.beds = nil + end end def get_totelder diff --git a/app/models/derived_variables/sales_log_variables.rb b/app/models/derived_variables/sales_log_variables.rb index 12d860492..f662ab3df 100644 --- a/app/models/derived_variables/sales_log_variables.rb +++ b/app/models/derived_variables/sales_log_variables.rb @@ -24,6 +24,10 @@ module DerivedVariables::SalesLogVariables self.hhmemb = number_of_household_members self.hhtype = household_type + if saledate && form.start_year_after_2024? + self.soctenant = soctenant_from_prevten_values + end + self.uprn_known = 0 if address_answered_without_uprn? if uprn_known&.zero? @@ -157,4 +161,14 @@ private def address_answered_without_uprn? [address_line1, town_or_city].all?(&:present?) && uprn.nil? && form.start_date.year >= 2023 end + + def soctenant_from_prevten_values + return unless prevten && shared_ownership_scheme? + + prevten_was_social_housing? ? 1 : 2 + end + + def prevten_was_social_housing? + [1, 2].include?(prevten) || [1, 2].include?(prevtenbuy2) + end end diff --git a/app/models/form/lettings/pages/property_number_of_bedrooms.rb b/app/models/form/lettings/pages/property_number_of_bedrooms.rb index c3a1a2365..8ed9dc37a 100644 --- a/app/models/form/lettings/pages/property_number_of_bedrooms.rb +++ b/app/models/form/lettings/pages/property_number_of_bedrooms.rb @@ -2,7 +2,7 @@ class Form::Lettings::Pages::PropertyNumberOfBedrooms < ::Form::Page def initialize(id, hsh, subsection) super @id = "property_number_of_bedrooms" - @depends_on = [{ "is_general_needs?" => true }] + @depends_on = [{ "is_general_needs?" => true, "is_beds_inferred?" => false }] end def questions diff --git a/app/models/form/lettings/questions/beds.rb b/app/models/form/lettings/questions/beds.rb index 6fa6c7c2b..e75e688bb 100644 --- a/app/models/form/lettings/questions/beds.rb +++ b/app/models/form/lettings/questions/beds.rb @@ -9,8 +9,11 @@ class Form::Lettings::Questions::Beds < ::Form::Question @check_answers_card_number = 0 @max = 12 @min = 1 - @hint_text = "If shared accommodation, enter the number of bedrooms occupied by this household. A bedsit has 1 bedroom." @step = 1 @question_number = 22 end + + def hint_text + form.start_year_after_2024? ? "If shared accommodation, enter the number of bedrooms occupied by this household." : "If shared accommodation, enter the number of bedrooms occupied by this household. A bedsit has 1 bedroom." + end end diff --git a/app/models/form/lettings/questions/gender_identity1.rb b/app/models/form/lettings/questions/gender_identity1.rb index 769dc1850..f7bba8e89 100644 --- a/app/models/form/lettings/questions/gender_identity1.rb +++ b/app/models/form/lettings/questions/gender_identity1.rb @@ -6,7 +6,6 @@ class Form::Lettings::Questions::GenderIdentity1 < ::Form::Question @header = "Which of these best describes the lead tenant’s gender identity?" @type = "radio" @check_answers_card_number = 1 - @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." @answer_options = ANSWER_OPTIONS @question_number = 33 end @@ -18,4 +17,12 @@ class Form::Lettings::Questions::GenderIdentity1 < ::Form::Question "divider" => { "value" => true }, "R" => { "value" => "Tenant prefers not to say" }, }.freeze + + def hint_text + if form.start_year_after_2024? + "This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth." + else + "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." + end + end end diff --git a/app/models/form/lettings/questions/person_gender_identity.rb b/app/models/form/lettings/questions/person_gender_identity.rb index 1416a3414..657438071 100644 --- a/app/models/form/lettings/questions/person_gender_identity.rb +++ b/app/models/form/lettings/questions/person_gender_identity.rb @@ -6,7 +6,6 @@ class Form::Lettings::Questions::PersonGenderIdentity < ::Form::Question @header = "Which of these best describes person #{person_index}’s gender identity?" @type = "radio" @check_answers_card_number = person_index - @hint_text = "" @answer_options = ANSWER_OPTIONS @question_number = 32 + (4 * person_index) end @@ -18,4 +17,12 @@ class Form::Lettings::Questions::PersonGenderIdentity < ::Form::Question "divider" => { "value" => true }, "R" => { "value" => "Person prefers not to say" }, }.freeze + + def hint_text + if form.start_year_after_2024? + "This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth." + else + "" + end + end end diff --git a/app/models/form/lettings/questions/reason.rb b/app/models/form/lettings/questions/reason.rb index 9598780ec..a60cc3b3f 100644 --- a/app/models/form/lettings/questions/reason.rb +++ b/app/models/form/lettings/questions/reason.rb @@ -6,8 +6,7 @@ class Form::Lettings::Questions::Reason < ::Form::Question @header = "What is the tenant’s main reason for the household leaving their last settled home?" @type = "radio" @check_answers_card_number = 0 - @hint_text = "The tenant’s ‘last settled home’ is their last long-standing home. For tenants who were in temporary accommodation or sleeping rough, their last settled home is where they were living previously." - @answer_options = ANSWER_OPTIONS + @hint_text = form.start_year_after_2024? ? "The tenant’s ‘last settled home’ is their last long-standing home. For tenants who were in temporary accommodation, sleeping rough or otherwise homeless, their last settled home is where they were living previously." : "The tenant’s ‘last settled home’ is their last long-standing home. For tenants who were in temporary accommodation or sleeping rough, their last settled home is where they were living previously." @conditional_for = { "reasonother" => [ 20, @@ -16,114 +15,81 @@ class Form::Lettings::Questions::Reason < ::Form::Question @question_number = 77 end - ANSWER_OPTIONS = { - "40" => { - "value" => "End of assured shorthold tenancy (no fault)", - }, - "41" => { - "value" => "End of assured shorthold tenancy (eviction or tenant at fault)", - }, - "42" => { - "value" => "End of fixed term tenancy (no fault)", - }, - "43" => { - "value" => "End of fixed term tenancy (eviction or tenant at fault)", - }, - "1" => { - "value" => "Permanently decanted from another property owned by this landlord", - }, - "46" => { - "value" => "Discharged from long-stay hospital or similar institution", - }, - "45" => { - "value" => "Discharged from prison", - }, - "2" => { - "value" => "Left home country as a refugee", - }, - "4" => { - "value" => "Loss of tied accommodation", - }, - "9" => { - "value" => "Asked to leave by family or friends", - }, - "44" => { - "value" => "Death of household member in last settled accommodation", - }, - "8" => { - "value" => "Relationship breakdown (non-violent) with partner", - }, - "16" => { - "value" => "To move nearer to family, friends or school", - }, - "17" => { - "value" => "To move nearer to work", - }, - "48" => { - "value" => "Domestic abuse - previously joint tenancy with partner", - }, - "49" => { - "value" => "Domestic abuse - other", - }, - "31" => { - "value" => "Hate crime", - }, - "10" => { - "value" => "Racial harassment", - }, - "11" => { - "value" => "Other problems with neighbours", - }, - "35" => { - "value" => "Couldn’t afford fees attached to renewing the tenancy", - }, - "36" => { - "value" => "Couldn’t afford increase in rent", - }, - "38" => { - "value" => "Couldn’t afford rent or mortgage (employment)", - }, - "37" => { - "value" => "Couldn’t afford rent or mortgage (welfare reforms)", - }, - "39" => { - "value" => "Couldn’t afford rent or mortgage (other)", - }, - "34" => { - "value" => "Repossession", - }, - "12" => { - "value" => "Property unsuitable because of overcrowding", - }, - "13" => { - "value" => "Property unsuitable because of ill health or disability", - }, - "14" => { - "value" => "Property unsuitable because of poor condition", - }, - "18" => { - "value" => "To move to accommodation with support", - }, - "19" => { - "value" => "To move to independent accommodation", - }, - "30" => { - "value" => "Under occupation (no incentive)", - }, - "29" => { - "value" => "Under occupation (offered incentive to downsize)", - }, - "20" => { - "value" => "Other", - }, - "47" => { - "value" => "Tenant prefers not to say", - }, - "divider" => { - "value" => true, - }, - "28" => { - "value" => "Don’t know", - }, - }.freeze + def answer_options + if form.start_year_after_2024? + { + "50" => { "value" => "End of social housing tenancy - no fault" }, + "51" => { "value" => "End of social housing tenancy - evicted due to anti-social behaviour (ASB)" }, + "52" => { "value" => "End of social housing tenancy - evicted due to rent arrears" }, + "53" => { "value" => "End of social housing tenancy - evicted for any other reason" }, + "1" => { "value" => "Permanently decanted from another property owned by this landlord" }, + "2" => { "value" => "Left home country as a refugee" }, + "45" => { "value" => "Discharged from prison" }, + "46" => { "value" => "Discharged from long-stay hospital or similar institution" }, + "4" => { "value" => "Loss of tied accommodation" }, + "9" => { "value" => "Asked to leave by family or friends" }, + "8" => { "value" => "Relationship breakdown (non-violent) with partner" }, + "44" => { "value" => "Death of household member in last settled accommodation" }, + "16" => { "value" => "To move nearer to family, friends or school" }, + "17" => { "value" => "To move nearer to work" }, + "48" => { "value" => "Domestic abuse - previously joint tenancy with partner" }, + "49" => { "value" => "Domestic abuse - other" }, + "10" => { "value" => "Racial harassment" }, + "31" => { "value" => "Hate crime" }, + "11" => { "value" => "Other problems with neighbours" }, + "34" => { "value" => "Repossession" }, + "54" => { "value" => "Could no longer afford rent or mortgage" }, + "12" => { "value" => "Property unsuitable because of overcrowding" }, + "13" => { "value" => "Property unsuitable because of ill health or disability" }, + "14" => { "value" => "Property unsuitable because of poor condition" }, + "29" => { "value" => "Under occupation (offered incentive to downsize)" }, + "30" => { "value" => "Under occupation (no incentive)" }, + "18" => { "value" => "To move to accommodation with support" }, + "19" => { "value" => "To move to independent accommodation" }, + "20" => { "value" => "Other" }, + "28" => { "value" => "Don’t know" }, + "divider" => { "value" => true }, + "47" => { "value" => "Tenant prefers not to say" }, + }.freeze + else + { + "40" => { "value" => "End of assured shorthold tenancy (no fault)" }, + "41" => { "value" => "End of assured shorthold tenancy (eviction or tenant at fault)" }, + "42" => { "value" => "End of fixed term tenancy (no fault)" }, + "43" => { "value" => "End of fixed term tenancy (eviction or tenant at fault)" }, + "1" => { "value" => "Permanently decanted from another property owned by this landlord" }, + "46" => { "value" => "Discharged from long-stay hospital or similar institution" }, + "45" => { "value" => "Discharged from prison" }, + "2" => { "value" => "Left home country as a refugee" }, + "4" => { "value" => "Loss of tied accommodation" }, + "9" => { "value" => "Asked to leave by family or friends" }, + "44" => { "value" => "Death of household member in last settled accommodation" }, + "8" => { "value" => "Relationship breakdown (non-violent) with partner" }, + "16" => { "value" => "To move nearer to family, friends or school" }, + "17" => { "value" => "To move nearer to work" }, + "48" => { "value" => "Domestic abuse - previously joint tenancy with partner" }, + "49" => { "value" => "Domestic abuse - other" }, + "31" => { "value" => "Hate crime" }, + "10" => { "value" => "Racial harassment" }, + "11" => { "value" => "Other problems with neighbours" }, + "35" => { "value" => "Couldn’t afford fees attached to renewing the tenancy" }, + "36" => { "value" => "Couldn’t afford increase in rent" }, + "38" => { "value" => "Couldn’t afford rent or mortgage (employment)" }, + "37" => { "value" => "Couldn’t afford rent or mortgage (welfare reforms)" }, + "39" => { "value" => "Couldn’t afford rent or mortgage (other)" }, + "34" => { "value" => "Repossession" }, + "12" => { "value" => "Property unsuitable because of overcrowding" }, + "13" => { "value" => "Property unsuitable because of ill health or disability" }, + "14" => { "value" => "Property unsuitable because of poor condition" }, + "18" => { "value" => "To move to accommodation with support" }, + "19" => { "value" => "To move to independent accommodation" }, + "30" => { "value" => "Under occupation (no incentive)" }, + "29" => { "value" => "Under occupation (offered incentive to downsize)" }, + "20" => { "value" => "Other" }, + "47" => { "value" => "Tenant prefers not to say" }, + "divider" => { "value" => true }, + "28" => { "value" => "Don’t know" }, + }.freeze + end + end end diff --git a/app/models/form/lettings/questions/reason_renewal.rb b/app/models/form/lettings/questions/reason_renewal.rb index 0158eba63..526df044c 100644 --- a/app/models/form/lettings/questions/reason_renewal.rb +++ b/app/models/form/lettings/questions/reason_renewal.rb @@ -7,7 +7,6 @@ class Form::Lettings::Questions::ReasonRenewal < ::Form::Question @type = "radio" @check_answers_card_number = 0 @hint_text = "You told us this letting is a renewal. We have removed some options because of this." - @answer_options = ANSWER_OPTIONS @question_number = 77 @conditional_for = { "reasonother" => [ @@ -16,20 +15,27 @@ class Form::Lettings::Questions::ReasonRenewal < ::Form::Question } end - ANSWER_OPTIONS = { - "40" => { "value" => "End of assured shorthold tenancy (no fault)" }, - "42" => { "value" => "End of fixed term tenancy (no fault)" }, - "20" => { - "value" => "Other", - }, - "47" => { - "value" => "Tenant prefers not to say", - }, - "divider" => { - "value" => true, - }, - "28" => { - "value" => "Don’t know", - }, - }.freeze + def answer_options + if form.start_year_after_2024? + { + "50" => { "value" => "End of social housing tenancy - no fault" }, + "51" => { "value" => "End of social housing tenancy - evicted due to anti-social behaviour (ASB)" }, + "52" => { "value" => "End of social housing tenancy - evicted due to rent arrears" }, + "53" => { "value" => "End of social housing tenancy - evicted for any other reason" }, + "20" => { "value" => "Other" }, + "47" => { "value" => "Tenant prefers not to say" }, + "divider" => { "value" => true }, + "28" => { "value" => "Don’t know" }, + }.freeze + else + { + "40" => { "value" => "End of assured shorthold tenancy (no fault)" }, + "42" => { "value" => "End of fixed term tenancy (no fault)" }, + "20" => { "value" => "Other" }, + "47" => { "value" => "Tenant prefers not to say" }, + "divider" => { "value" => true }, + "28" => { "value" => "Don’t know" }, + }.freeze + end + end end diff --git a/app/models/form/lettings/questions/wheelchair.rb b/app/models/form/lettings/questions/wheelchair.rb index 3ba4f081f..c2aaa3700 100644 --- a/app/models/form/lettings/questions/wheelchair.rb +++ b/app/models/form/lettings/questions/wheelchair.rb @@ -6,7 +6,7 @@ class Form::Lettings::Questions::Wheelchair < ::Form::Question @header = "Is the property built or adapted to wheelchair-user standards?" @type = "radio" @check_answers_card_number = 0 - @hint_text = "" + @hint_text = form.start_year_after_2024? ? "This is whether someone who uses a wheelchair is able to make full use of all of the property’s rooms and facilities, including use of both inside and outside space, and entering and exiting the property." : "" @answer_options = ANSWER_OPTIONS @question_number = 21 end diff --git a/app/models/form/sales/pages/buyer_previous.rb b/app/models/form/sales/pages/buyer_previous.rb index f37c6a847..3a4618d8f 100644 --- a/app/models/form/sales/pages/buyer_previous.rb +++ b/app/models/form/sales/pages/buyer_previous.rb @@ -2,7 +2,7 @@ class Form::Sales::Pages::BuyerPrevious < ::Form::Page def initialize(id, hsh, subsection, joint_purchase:) super(id, hsh, subsection) @joint_purchase = joint_purchase - @depends_on = [{ "joint_purchase?" => joint_purchase }] + @depends_on = [{ "joint_purchase?" => joint_purchase, "soctenant_is_inferred?" => false }] end def questions @@ -10,4 +10,10 @@ class Form::Sales::Pages::BuyerPrevious < ::Form::Page Form::Sales::Questions::BuyerPrevious.new(nil, nil, self, joint_purchase: @joint_purchase), ] end + + def routed_to?(log, _current_user) + return false if log.is_staircase? && log.form.start_year_after_2024? + + super + end end diff --git a/app/models/form/sales/pages/la_nominations.rb b/app/models/form/sales/pages/la_nominations.rb index e756e3071..0ac2cde75 100644 --- a/app/models/form/sales/pages/la_nominations.rb +++ b/app/models/form/sales/pages/la_nominations.rb @@ -9,4 +9,10 @@ class Form::Sales::Pages::LaNominations < ::Form::Page Form::Sales::Questions::LaNominations.new(nil, nil, self), ] end + + def routed_to?(log, _current_user) + return false if log.staircase == 1 && log.form.start_year_after_2024? + + super + end end diff --git a/app/models/form/sales/questions/buyer_live.rb b/app/models/form/sales/questions/buyer_live.rb index ee0a79611..2a3bf7eaf 100644 --- a/app/models/form/sales/questions/buyer_live.rb +++ b/app/models/form/sales/questions/buyer_live.rb @@ -3,7 +3,7 @@ class Form::Sales::Questions::BuyerLive < ::Form::Question super @id = "buylivein" @check_answer_label = "Buyers living in property" - @header = "Will the buyers live in the property?" + @header = form.start_year_after_2024? ? "Will any buyers live in the property?" : "Will the buyers live in the property?" @type = "radio" @answer_options = ANSWER_OPTIONS @question_number = 8 diff --git a/app/models/form/sales/questions/buyer_previous.rb b/app/models/form/sales/questions/buyer_previous.rb index aaea81cfc..cc16ce107 100644 --- a/app/models/form/sales/questions/buyer_previous.rb +++ b/app/models/form/sales/questions/buyer_previous.rb @@ -21,4 +21,8 @@ class Form::Sales::Questions::BuyerPrevious < ::Form::Question "2" => { "value" => "No" }, } end + + def derived? + form.start_year_after_2024? + end end diff --git a/app/models/form/sales/questions/discount.rb b/app/models/form/sales/questions/discount.rb index ee39b8916..4c4595ce1 100644 --- a/app/models/form/sales/questions/discount.rb +++ b/app/models/form/sales/questions/discount.rb @@ -6,7 +6,7 @@ class Form::Sales::Questions::Discount < ::Form::Question @header = "What was the percentage discount?" @type = "numeric" @min = 0 - @max = 100 + @max = form.start_year_after_2024? ? 70 : 100 @step = 1 @width = 5 @suffix = "%" diff --git a/app/models/form/sales/questions/gender_identity1.rb b/app/models/form/sales/questions/gender_identity1.rb index b9b47ee0a..7ee550df1 100644 --- a/app/models/form/sales/questions/gender_identity1.rb +++ b/app/models/form/sales/questions/gender_identity1.rb @@ -5,7 +5,6 @@ class Form::Sales::Questions::GenderIdentity1 < ::Form::Question @check_answer_label = "Buyer 1’s gender identity" @header = "Which of these best describes buyer 1’s gender identity?" @type = "radio" - @hint_text = "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest." @answer_options = ANSWER_OPTIONS @check_answers_card_number = 1 @question_number = 21 @@ -17,4 +16,12 @@ class Form::Sales::Questions::GenderIdentity1 < ::Form::Question "X" => { "value" => "Non-binary" }, "R" => { "value" => "Prefers not to say" }, }.freeze + + def hint_text + if form.start_year_after_2024? + "This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth." + else + "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest." + end + end end diff --git a/app/models/form/sales/questions/gender_identity2.rb b/app/models/form/sales/questions/gender_identity2.rb index 15ef9fda1..87c155acb 100644 --- a/app/models/form/sales/questions/gender_identity2.rb +++ b/app/models/form/sales/questions/gender_identity2.rb @@ -22,4 +22,10 @@ class Form::Sales::Questions::GenderIdentity2 < ::Form::Question "X" => { "value" => "Non-binary" }, "R" => { "value" => "Buyer prefers not to say" }, }.freeze + + def hint_text + return unless form.start_year_after_2024? + + "This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth." + end end diff --git a/app/models/form/sales/questions/person_gender_identity.rb b/app/models/form/sales/questions/person_gender_identity.rb index 94774f5ce..e2da876a4 100644 --- a/app/models/form/sales/questions/person_gender_identity.rb +++ b/app/models/form/sales/questions/person_gender_identity.rb @@ -21,4 +21,10 @@ class Form::Sales::Questions::PersonGenderIdentity < ::Form::Question "X" => { "value" => "Non-binary" }, "R" => { "value" => "Person prefers not to say" }, }.freeze + + def hint_text + return unless form.start_year_after_2024? + + "This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth." + end end diff --git a/app/models/form/sales/questions/property_wheelchair_accessible.rb b/app/models/form/sales/questions/property_wheelchair_accessible.rb index feb017be0..588f350a2 100644 --- a/app/models/form/sales/questions/property_wheelchair_accessible.rb +++ b/app/models/form/sales/questions/property_wheelchair_accessible.rb @@ -7,6 +7,7 @@ class Form::Sales::Questions::PropertyWheelchairAccessible < ::Form::Question @type = "radio" @answer_options = ANSWER_OPTIONS @question_number = 17 + @hint_text = form.start_year_after_2024? ? "This is whether someone who uses a wheelchair is able to make full use of all of the property’s rooms and facilities, including use of both inside and outside space, and entering and exiting the property." : nil end ANSWER_OPTIONS = { diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb index 03405a12d..cc8c7d58b 100644 --- a/app/models/lettings_log.rb +++ b/app/models/lettings_log.rb @@ -356,6 +356,10 @@ class LettingsLog < Log unittype_gn == 2 end + def is_beds_inferred? + form.start_year_after_2024? && is_bedsit? + end + def is_shared_housing? # 4: Shared flat or maisonette # 9: Shared house diff --git a/app/models/sales_log.rb b/app/models/sales_log.rb index 3f47dea80..76c43b09e 100644 --- a/app/models/sales_log.rb +++ b/app/models/sales_log.rb @@ -470,6 +470,10 @@ class SalesLog < Log form.start_date.year >= 2023 && uprn.present? ? "uprn" : nil].compact end + def soctenant_is_inferred? + form.start_year_after_2024? + end + def duplicates SalesLog.where.not(duplicate_set_id: nil).where(duplicate_set_id:).where.not(id:) end @@ -477,4 +481,18 @@ class SalesLog < Log def nationality2_uk_or_prefers_not_to_say? nationality_all_buyer2_group&.zero? || nationality_all_buyer2_group == 826 end + + def is_staircase? + staircase == 1 + end + + def discount_value + return unless discount && value + + value * discount / 100 + end + + def is_not_staircasing? + staircase == 2 || staircase == 3 + end end diff --git a/app/models/validations/property_validations.rb b/app/models/validations/property_validations.rb index 2f2d8d6b8..ba8318282 100644 --- a/app/models/validations/property_validations.rb +++ b/app/models/validations/property_validations.rb @@ -34,7 +34,7 @@ module Validations::PropertyValidations def validate_shared_housing_rooms(record) unless record.unittype_gn.nil? - if record.is_bedsit? && record.beds != 1 && record.beds.present? + if record.is_bedsit? && record.beds != 1 && record.beds.present? && !record.form.start_year_after_2024? record.errors.add :unittype_gn, I18n.t("validations.property.unittype_gn.one_bedroom_bedsit") record.errors.add :beds, I18n.t("validations.property.unittype_gn.one_bedroom_bedsit") end diff --git a/app/models/validations/sales/sale_information_validations.rb b/app/models/validations/sales/sale_information_validations.rb index 5b036580d..0aaa77a93 100644 --- a/app/models/validations/sales/sale_information_validations.rb +++ b/app/models/validations/sales/sale_information_validations.rb @@ -55,4 +55,59 @@ module Validations::Sales::SaleInformationValidations record.errors.add :type, I18n.t("validations.sale_information.monthly_rent.higher_than_expected") end end + + def validate_grant_amount(record) + return unless record.saledate && record.form.start_year_after_2024? + return unless record.grant && (record.type == 8 || record.type == 21) + + unless record.grant.between?(9_000, 16_000) + record.errors.add :grant, I18n.t("validations.sale_information.grant.out_of_range") + end + end + + def validate_stairbought(record) + return unless record.stairbought && record.type + return unless record.saledate && record.form.start_year_after_2024? + + max_stairbought = case record.type + when 30, 16, 28, 31, 32 + 90 + when 2, 18 + 75 + when 24 + 50 + end + + if max_stairbought && record.stairbought > max_stairbought + record.errors.add :stairbought, I18n.t("validations.sale_information.stairbought.over_max", max_stairbought:, type: record.form.get_question("type", record).answer_label(record)) + record.errors.add :type, I18n.t("validations.sale_information.stairbought.over_max", max_stairbought:, type: record.form.get_question("type", record).answer_label(record)) + end + end + + def validate_discount_and_value(record) + return unless record.saledate && record.form.start_year_after_2024? + return unless record.discount && record.value && record.la + + if record.london_property? && record.discount_value > 136_400 + %i[discount value la postcode_full uprn].each do |field| + record.errors.add field, I18n.t("validations.sale_information.value.over_discounted_london_max", discount_value: record.field_formatted_as_currency("discount_value")) + end + elsif record.property_not_in_london? && record.discount_value > 102_400 + %i[discount value la postcode_full uprn].each do |field| + record.errors.add field, I18n.t("validations.sale_information.value.over_discounted_max", discount_value: record.field_formatted_as_currency("discount_value")) + end + end + end + + def validate_non_staircasing_mortgage(record) + return unless record.mortgage && record.value && record.deposit && record.equity + return unless record.is_not_staircasing? + return unless record.saledate && record.form.start_year_after_2024? + + if record.mortgage_and_deposit_total != record.expected_shared_ownership_deposit_value + %i[mortgage value deposit equity].each do |field| + record.errors.add field, I18n.t("validations.sale_information.non_staircasing_mortgage", mortgage_and_deposit_total: record.field_formatted_as_currency("mortgage_and_deposit_total"), expected_shared_ownership_deposit_value: record.field_formatted_as_currency("expected_shared_ownership_deposit_value")) + end + end + end end diff --git a/app/models/validations/sales/soft_validations.rb b/app/models/validations/sales/soft_validations.rb index fce544754..ae9ee66cc 100644 --- a/app/models/validations/sales/soft_validations.rb +++ b/app/models/validations/sales/soft_validations.rb @@ -116,7 +116,8 @@ module Validations::Sales::SoftValidations end def grant_outside_common_range? - return unless grant + return unless grant && type && saledate + return if form.start_year_after_2024? && (type == 21 || type == 8) !grant.between?(9_000, 16_000) end diff --git a/app/services/bulk_upload/lettings/year2024/row_parser.rb b/app/services/bulk_upload/lettings/year2024/row_parser.rb index c87cd0c03..5f05b6e10 100644 --- a/app/services/bulk_upload/lettings/year2024/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2024/row_parser.rb @@ -1036,7 +1036,7 @@ private attributes["unittype_gn"] = field_26 attributes["builtype"] = field_27 attributes["wchair"] = field_28 - attributes["beds"] = field_29 + attributes["beds"] = field_26 == 2 ? 1 : field_29 attributes["joint"] = field_36 attributes["startertenancy"] = field_37 attributes["tenancy"] = field_38 diff --git a/app/services/bulk_upload/sales/year2024/row_parser.rb b/app/services/bulk_upload/sales/year2024/row_parser.rb index 037fb8eef..28e7ef100 100644 --- a/app/services/bulk_upload/sales/year2024/row_parser.rb +++ b/app/services/bulk_upload/sales/year2024/row_parser.rb @@ -717,7 +717,6 @@ private lanomagr: %i[field_97], frombeds: %i[field_98], fromprop: %i[field_99], - soctenant: %i[field_98 field_99 field_100], value: %i[field_101 field_114 field_125], equity: %i[field_102], mortgage: %i[field_104 field_118 field_127], @@ -927,7 +926,7 @@ private attributes["stairbought"] = field_87 attributes["stairowned"] = field_88 attributes["socprevten"] = field_100 - attributes["soctenant"] = [attributes["socprevten"], attributes["frombeds"], attributes["fromprop"]].any?(&:present?) ? 1 : 0 + attributes["soctenant"] = infer_soctenant_from_prevten_and_prevtenbuy2 attributes["mortgageused"] = mortgageused attributes["uprn"] = field_22 @@ -1123,6 +1122,16 @@ private 0 if field_62 == 1 end + def infer_soctenant_from_prevten_and_prevtenbuy2 + return unless shared_ownership? + + if [1, 2].include?(field_61) || [1, 2].include?(field_71.to_i) + 1 + else + 2 + end + end + def block_log_creation! self.block_log_creation = true end diff --git a/app/views/bulk_upload_lettings_logs/forms/prepare_your_file_2024.html.erb b/app/views/bulk_upload_lettings_logs/forms/prepare_your_file_2024.html.erb index 5bdf31536..8bc375450 100644 --- a/app/views/bulk_upload_lettings_logs/forms/prepare_your_file_2024.html.erb +++ b/app/views/bulk_upload_lettings_logs/forms/prepare_your_file_2024.html.erb @@ -13,22 +13,17 @@
Use this template to upload logs for 2024/25:
-There are 7 or 8 rows of content in the templates. These rows are called the ‘headers’. They contain the CORE form questions and guidance about which questions are required and how to format your answers.
+<%= govuk_link_to "Download the lettings bulk upload template (2024 to 2025)", @form.template_path %>
+There are 8 rows of content in the templates. These rows are called the ‘headers’. They contain the CORE form questions and guidance about which questions are required and how to format your answers.
Use this template to upload logs for 2024/25:
-There are 7 or 8 rows of content in the templates. These rows are called the ‘headers’. They contain the CORE form questions and guidance about which questions are required and how to format your answers.
+Use one of these templates to upload logs for 2024/25:
+<%= govuk_link_to "Download the sales bulk upload template (2024 to 2025)", @form.template_path %>: In this template, the questions are in the same order as the 2024/25 paper form and web form.
+There are 8 rows of content in the templates. These rows are called the ‘headers’. They contain the CORE form questions and guidance about which questions are required and how to format your answers.
For each collection year, we publish a bulk upload template and specification.
-The bulk upload templates contain 7 or 8 rows of ‘headers’ with information about how to fill in the template, including:
+ <% if @form.year == 2023 %> +The bulk upload templates contain 7 or 8 rows of ‘headers’ with information about how to fill in the template, including:
+ <% else %> +The bulk upload templates contain 8 rows of ‘headers’ with information about how to fill in the template, including:
+ <% end %>You can paste your data below the headers or copy the headers and insert them above the data in your file. The bulk upload fields start at column B. Leave column A blank.
Make sure that each column of data aligns with the corresponding question in the headers. We recommend ordering your data to match the headers, but you can also reorder the headers to match your data. When processing the file, we check what each column of data represents based on the headers above.
+ <% if @form.year == 2023 %> +For 2023/24 uploads, there are 2 templates to choose from, a new template and a legacy template. They outline suggested ways of ordering your data.
+You must include the headers in your file when you upload, unless your data matches the exact order of the legacy template. If you choose to remove the headers, you must also remove the blank column A.
- <% unless @form.year == 2023 %> -For 2023/24 uploads, there are 2 templates to choose from, a new template and a legacy template. They outline suggested ways of ordering your data.
-You must include the headers in your file when you upload, unless your data matches the exact order of the legacy template. If you choose to remove the headers, you must also remove the blank column A.
+New template: In this template, the questions are in the same order as the 2023/24 paper form and web form. Use this template if your organisation is new to bulk upload or if your housing management system matches the new column ordering.
+<%= govuk_link_to @form.download_text("lettings", "template", "new"), @form.lettings_template_path %>
+<%= govuk_link_to @form.download_text("sales", "template", "new"), @form.sales_template_path %>
-New template: In this template, the questions are in the same order as the <%= @form.year_combo %> paper form and web form. Use this template if your organisation is new to bulk upload or if your housing management system matches the new column ordering.
- <% end %> -<%= govuk_link_to @form.download_text("lettings", "template", "new"), @form.lettings_template_path %>
-<%= govuk_link_to @form.download_text("sales", "template", "new"), @form.sales_template_path %>
- <% if @form.year == 2023 %>Legacy template: In this template, the questions are in the same order as the 2022/23 template, with new questions added on to the end. Use this template if you have not updated your system to match the new template yet.
<%= govuk_link_to @form.download_text("lettings", "template", "legacy"), @form.lettings_legacy_template_path %>
<%= govuk_link_to @form.download_text("sales", "template", "legacy"), @form.sales_legacy_template_path %>
+ <% else %> +<%= govuk_link_to @form.download_text("lettings", "template"), @form.lettings_template_path %>
+<%= govuk_link_to @form.download_text("sales", "template"), @form.sales_template_path %>
<% end %> <% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index b9638ba94..204a20b4e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -615,6 +615,14 @@ en: discounted_ownership_value: "The mortgage, deposit, and grant when added together is %{mortgage_deposit_and_grant_total}, and the purchase purchase price times by the discount is %{value_with_discount}. These figures should be the same" monthly_rent: higher_than_expected: "Basic monthly rent must be between £0.00 and £9,999.00" + grant: + out_of_range: "Loan, grants or subsidies must be between £9,000 and £16,000" + stairbought: + over_max: "The percentage bought in this staircasing transaction cannot be higher than %{max_stairbought}% for %{type} sales." + value: + over_discounted_london_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £136,400 for properties in London." + over_discounted_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £102,400 for properties outside of London." + non_staircasing_mortgage: "The mortgage and deposit added together is %{mortgage_and_deposit_total} and the purchase price times by the equity is %{expected_shared_ownership_deposit_value}. These figures should be the same." merge_request: organisation_part_of_another_merge: "This organisation is part of another merge - select a different one" organisation_not_selected: "Select an organisation from the search list" diff --git a/spec/models/form/lettings/pages/property_number_of_bedrooms_spec.rb b/spec/models/form/lettings/pages/property_number_of_bedrooms_spec.rb new file mode 100644 index 000000000..f6cec39e8 --- /dev/null +++ b/spec/models/form/lettings/pages/property_number_of_bedrooms_spec.rb @@ -0,0 +1,33 @@ +require "rails_helper" + +RSpec.describe Form::Lettings::Pages::PropertyNumberOfBedrooms, 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[beds]) + end + + it "has the correct id" do + expect(page.id).to eq("property_number_of_bedrooms") + end + + it "has the correct header" do + expect(page.header).to be_nil + end + + it "has the correct description" do + expect(page.description).to be_nil + end + + it "has the correct depends_on" do + expect(page.depends_on).to eq([{ "is_general_needs?" => true, "is_beds_inferred?" => false }]) + end +end diff --git a/spec/models/form/lettings/pages/property_wheelchair_accessible_spec.rb b/spec/models/form/lettings/pages/property_wheelchair_accessible_spec.rb index b96a55feb..522a19905 100644 --- a/spec/models/form/lettings/pages/property_wheelchair_accessible_spec.rb +++ b/spec/models/form/lettings/pages/property_wheelchair_accessible_spec.rb @@ -5,6 +5,10 @@ RSpec.describe Form::Lettings::Pages::PropertyWheelchairAccessible, type: :model let(:subsection) { instance_double(Form::Subsection) } + before do + allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: false)) + end + it "has correct subsection" do expect(page.subsection).to be(subsection) end diff --git a/spec/models/form/lettings/questions/beds_spec.rb b/spec/models/form/lettings/questions/beds_spec.rb new file mode 100644 index 000000000..77340f5d5 --- /dev/null +++ b/spec/models/form/lettings/questions/beds_spec.rb @@ -0,0 +1,65 @@ +require "rails_helper" + +RSpec.describe Form::Lettings::Questions::Beds, 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) } + let(:subsection) { instance_double(Form::Subsection) } + let(:form) { instance_double(Form) } + + before do + allow(form).to receive(:start_year_after_2024?).and_return(false) + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(form) + end + + it "has correct page" do + expect(question.page).to eq(page) + end + + it "has the correct id" do + expect(question.id).to eq("beds") + end + + it "has the correct header" do + expect(question.header).to eq("How many bedrooms does the property have?") + end + + it "has the correct check_answer_label" do + expect(question.check_answer_label).to eq("Number of bedrooms") + end + + it "has the correct type" do + expect(question.type).to eq("numeric") + end + + it "is not marked as derived" do + expect(question.derived?).to be false + end + + it "has the correct min" do + expect(question.min).to eq(1) + end + + it "has the correct max" do + expect(question.max).to eq(12) + end + + context "with 2023/24 form" do + it "has the correct hint_text" do + expect(question.hint_text).to eq("If shared accommodation, enter the number of bedrooms occupied by this household. A bedsit has 1 bedroom.") + end + end + + context "with 2024/25 form" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(true) + end + + it "has the correct hint_text" do + expect(question.hint_text).to eq("If shared accommodation, enter the number of bedrooms occupied by this household.") + end + end +end diff --git a/spec/models/form/lettings/questions/gender_identity1_spec.rb b/spec/models/form/lettings/questions/gender_identity1_spec.rb new file mode 100644 index 000000000..596e38941 --- /dev/null +++ b/spec/models/form/lettings/questions/gender_identity1_spec.rb @@ -0,0 +1,69 @@ +require "rails_helper" + +RSpec.describe Form::Lettings::Questions::GenderIdentity1, type: :model do + subject(:question) { described_class.new(nil, question_definition, page) } + + let(:question_definition) { nil } + let(:page) { instance_double(Form::Page) } + let(:subsection) { instance_double(Form::Subsection) } + let(:form) { instance_double(Form) } + + before do + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(form) + end + + it "has the correct id" do + expect(question.id).to eq("sex1") + end + + it "has the correct header" do + expect(question.header).to eq("Which of these best describes the lead tenant’s gender identity?") + end + + it "has the correct check_answer_label" do + expect(question.check_answer_label).to eq("Lead tenant’s gender identity") + end + + it "has the correct check_answers_card_number" do + expect(question.check_answers_card_number).to eq(1) + end + + it "has the correct type" do + expect(question.type).to eq("radio") + end + + it "is not marked as derived" do + expect(question.derived?).to be false + end + + it "has the correct answer_options" do + expect(question.answer_options).to eq({ + "F" => { "value" => "Female" }, + "M" => { "value" => "Male" }, + "X" => { "value" => "Non-binary" }, + "divider" => { "value" => true }, + "R" => { "value" => "Tenant prefers not to say" }, + }) + end + + context "with form year before 2024" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(false) + end + + it "has the correct hint" do + expect(question.hint_text).to eq("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.") + end + end + + context "with form year >= 2024" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(true) + end + + it "has the correct hint" do + expect(question.hint_text).to eq("This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth.") + end + end +end diff --git a/spec/models/form/lettings/questions/person_gender_identity_spec.rb b/spec/models/form/lettings/questions/person_gender_identity_spec.rb index 703ba764a..bff45a624 100644 --- a/spec/models/form/lettings/questions/person_gender_identity_spec.rb +++ b/spec/models/form/lettings/questions/person_gender_identity_spec.rb @@ -6,6 +6,13 @@ RSpec.describe Form::Lettings::Questions::PersonGenderIdentity, type: :model do let(:question_definition) { nil } let(:page) { instance_double(Form::Page) } let(:person_index) { 2 } + let(:subsection) { instance_double(Form::Subsection) } + let(:form) { instance_double(Form) } + + before do + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(form) + end it "has correct page" do expect(question.page).to eq(page) @@ -19,8 +26,24 @@ RSpec.describe Form::Lettings::Questions::PersonGenderIdentity, type: :model do expect(question.derived?).to be false end - it "has the correct hint" do - expect(question.hint_text).to eq("") + context "with form year before 2024" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(false) + end + + it "has the correct hint" do + expect(question.hint_text).to eq("") + end + end + + context "with form year >= 2024" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(true) + end + + it "has the correct hint" do + expect(question.hint_text).to eq("This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth.") + end end context "with person 2" do diff --git a/spec/models/form/lettings/questions/reason_renewal_spec.rb b/spec/models/form/lettings/questions/reason_renewal_spec.rb new file mode 100644 index 000000000..f861fc2af --- /dev/null +++ b/spec/models/form/lettings/questions/reason_renewal_spec.rb @@ -0,0 +1,85 @@ +require "rails_helper" + +RSpec.describe Form::Lettings::Questions::ReasonRenewal, 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) } + let(:subsection) { instance_double(Form::Subsection) } + let(:form) { instance_double(Form) } + + before do + allow(form).to receive(:start_year_after_2024?).and_return(false) + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(form) + end + + it "has correct page" do + expect(question.page).to eq(page) + end + + it "has the correct id" do + expect(question.id).to eq("reason") + end + + it "has the correct header" do + expect(question.header).to eq("What is the tenant’s main reason for the household leaving their last settled home?") + end + + it "has the correct check_answer_label" do + expect(question.check_answer_label).to eq("Reason for leaving last settled home") + end + + it "has the correct type" do + expect(question.type).to eq("radio") + end + + it "has the correct check_answers_card_number" do + expect(question.check_answers_card_number).to eq(0) + end + + it "has the correct hint" do + expect(question.hint_text).to eq("You told us this letting is a renewal. We have removed some options because of this.") + end + + it "has the correct conditional_for" do + expect(question.conditional_for).to eq({ "reasonother" => [20] }) + end + + it "is not marked as derived" do + expect(question).not_to be_derived + end + + context "with 2023/24 form" do + it "has the correct answer_options" do + expect(question.answer_options).to eq({ + "40" => { "value" => "End of assured shorthold tenancy (no fault)" }, + "42" => { "value" => "End of fixed term tenancy (no fault)" }, + "20" => { "value" => "Other" }, + "47" => { "value" => "Tenant prefers not to say" }, + "divider" => { "value" => true }, + "28" => { "value" => "Don’t know" }, + }) + end + end + + context "with 2024/25 form" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(true) + end + + it "has the correct answer_options" do + expect(question.answer_options).to eq({ + "50" => { "value" => "End of social housing tenancy - no fault" }, + "51" => { "value" => "End of social housing tenancy - evicted due to anti-social behaviour (ASB)" }, + "52" => { "value" => "End of social housing tenancy - evicted due to rent arrears" }, + "53" => { "value" => "End of social housing tenancy - evicted for any other reason" }, + "20" => { "value" => "Other" }, + "47" => { "value" => "Tenant prefers not to say" }, + "divider" => { "value" => true }, + "28" => { "value" => "Don’t know" }, + }) + end + end +end diff --git a/spec/models/form/lettings/questions/reason_spec.rb b/spec/models/form/lettings/questions/reason_spec.rb index 24d28fd4b..4ea1a4a35 100644 --- a/spec/models/form/lettings/questions/reason_spec.rb +++ b/spec/models/form/lettings/questions/reason_spec.rb @@ -6,6 +6,14 @@ RSpec.describe Form::Lettings::Questions::Reason, type: :model do let(:question_id) { nil } let(:question_definition) { nil } let(:page) { instance_double(Form::Page) } + let(:subsection) { instance_double(Form::Subsection) } + let(:form) { instance_double(Form) } + + before do + allow(form).to receive(:start_year_after_2024?).and_return(false) + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(form) + end it "has correct page" do expect(question.page).to eq(page) @@ -31,10 +39,6 @@ RSpec.describe Form::Lettings::Questions::Reason, type: :model do expect(question.check_answers_card_number).to eq(0) end - it "has the correct hint" do - expect(question.hint_text).to eq("The tenant’s ‘last settled home’ is their last long-standing home. For tenants who were in temporary accommodation or sleeping rough, their last settled home is where they were living previously.") - end - it "has the correct conditional_for" do expect(question.conditional_for).to eq({ "reasonother" => [20] }) end @@ -43,116 +47,97 @@ RSpec.describe Form::Lettings::Questions::Reason, type: :model do expect(question).not_to be_derived end - it "has the correct answer_options" do - expect(question.answer_options).to eq({ - "40" => { - "value" => "End of assured shorthold tenancy (no fault)", - }, - "41" => { - "value" => "End of assured shorthold tenancy (eviction or tenant at fault)", - }, - "42" => { - "value" => "End of fixed term tenancy (no fault)", - }, - "43" => { - "value" => "End of fixed term tenancy (eviction or tenant at fault)", - }, - "1" => { - "value" => "Permanently decanted from another property owned by this landlord", - }, - "46" => { - "value" => "Discharged from long-stay hospital or similar institution", - }, - "45" => { - "value" => "Discharged from prison", - }, - "2" => { - "value" => "Left home country as a refugee", - }, - "4" => { - "value" => "Loss of tied accommodation", - }, - "9" => { - "value" => "Asked to leave by family or friends", - }, - "44" => { - "value" => "Death of household member in last settled accommodation", - }, - "8" => { - "value" => "Relationship breakdown (non-violent) with partner", - }, - "16" => { - "value" => "To move nearer to family, friends or school", - }, - "17" => { - "value" => "To move nearer to work", - }, - "48" => { - "value" => "Domestic abuse - previously joint tenancy with partner", - }, - "49" => { - "value" => "Domestic abuse - other", - }, - "31" => { - "value" => "Hate crime", - }, - "10" => { - "value" => "Racial harassment", - }, - "11" => { - "value" => "Other problems with neighbours", - }, - "35" => { - "value" => "Couldn’t afford fees attached to renewing the tenancy", - }, - "36" => { - "value" => "Couldn’t afford increase in rent", - }, - "38" => { - "value" => "Couldn’t afford rent or mortgage (employment)", - }, - "37" => { - "value" => "Couldn’t afford rent or mortgage (welfare reforms)", - }, - "39" => { - "value" => "Couldn’t afford rent or mortgage (other)", - }, - "34" => { - "value" => "Repossession", - }, - "12" => { - "value" => "Property unsuitable because of overcrowding", - }, - "13" => { - "value" => "Property unsuitable because of ill health or disability", - }, - "14" => { - "value" => "Property unsuitable because of poor condition", - }, - "18" => { - "value" => "To move to accommodation with support", - }, - "19" => { - "value" => "To move to independent accommodation", - }, - "30" => { - "value" => "Under occupation (no incentive)", - }, - "29" => { - "value" => "Under occupation (offered incentive to downsize)", - }, - "20" => { - "value" => "Other", - }, - "47" => { - "value" => "Tenant prefers not to say", - }, - "divider" => { - "value" => true, - }, - "28" => { - "value" => "Don’t know", - }, - }) + context "with 2023/24 form" do + it "has the correct hint" do + expect(question.hint_text).to eq("The tenant’s ‘last settled home’ is their last long-standing home. For tenants who were in temporary accommodation or sleeping rough, their last settled home is where they were living previously.") + end + + it "has the correct answer_options" do + expect(question.answer_options).to eq({ + "40" => { "value" => "End of assured shorthold tenancy (no fault)" }, + "41" => { "value" => "End of assured shorthold tenancy (eviction or tenant at fault)" }, + "42" => { "value" => "End of fixed term tenancy (no fault)" }, + "43" => { "value" => "End of fixed term tenancy (eviction or tenant at fault)" }, + "1" => { "value" => "Permanently decanted from another property owned by this landlord" }, + "46" => { "value" => "Discharged from long-stay hospital or similar institution" }, + "45" => { "value" => "Discharged from prison" }, + "2" => { "value" => "Left home country as a refugee" }, + "4" => { "value" => "Loss of tied accommodation" }, + "9" => { "value" => "Asked to leave by family or friends" }, + "44" => { "value" => "Death of household member in last settled accommodation" }, + "8" => { "value" => "Relationship breakdown (non-violent) with partner" }, + "16" => { "value" => "To move nearer to family, friends or school" }, + "17" => { "value" => "To move nearer to work" }, + "48" => { "value" => "Domestic abuse - previously joint tenancy with partner" }, + "49" => { "value" => "Domestic abuse - other" }, + "31" => { "value" => "Hate crime" }, + "10" => { "value" => "Racial harassment" }, + "11" => { "value" => "Other problems with neighbours" }, + "35" => { "value" => "Couldn’t afford fees attached to renewing the tenancy" }, + "36" => { "value" => "Couldn’t afford increase in rent" }, + "38" => { "value" => "Couldn’t afford rent or mortgage (employment)" }, + "37" => { "value" => "Couldn’t afford rent or mortgage (welfare reforms)" }, + "39" => { "value" => "Couldn’t afford rent or mortgage (other)" }, + "34" => { "value" => "Repossession" }, + "12" => { "value" => "Property unsuitable because of overcrowding" }, + "13" => { "value" => "Property unsuitable because of ill health or disability" }, + "14" => { "value" => "Property unsuitable because of poor condition" }, + "18" => { "value" => "To move to accommodation with support" }, + "19" => { "value" => "To move to independent accommodation" }, + "30" => { "value" => "Under occupation (no incentive)" }, + "29" => { "value" => "Under occupation (offered incentive to downsize)" }, + "20" => { "value" => "Other" }, + "47" => { "value" => "Tenant prefers not to say" }, + "divider" => { "value" => true }, + "28" => { "value" => "Don’t know" }, + }) + end + end + + context "with 2024/25 form" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(true) + end + + it "has the correct hint" do + expect(question.hint_text).to eq("The tenant’s ‘last settled home’ is their last long-standing home. For tenants who were in temporary accommodation, sleeping rough or otherwise homeless, their last settled home is where they were living previously.") + end + + it "has the correct answer_options" do + expect(question.answer_options).to eq({ + "50" => { "value" => "End of social housing tenancy - no fault" }, + "51" => { "value" => "End of social housing tenancy - evicted due to anti-social behaviour (ASB)" }, + "52" => { "value" => "End of social housing tenancy - evicted due to rent arrears" }, + "53" => { "value" => "End of social housing tenancy - evicted for any other reason" }, + "1" => { "value" => "Permanently decanted from another property owned by this landlord" }, + "2" => { "value" => "Left home country as a refugee" }, + "45" => { "value" => "Discharged from prison" }, + "46" => { "value" => "Discharged from long-stay hospital or similar institution" }, + "4" => { "value" => "Loss of tied accommodation" }, + "9" => { "value" => "Asked to leave by family or friends" }, + "8" => { "value" => "Relationship breakdown (non-violent) with partner" }, + "44" => { "value" => "Death of household member in last settled accommodation" }, + "16" => { "value" => "To move nearer to family, friends or school" }, + "17" => { "value" => "To move nearer to work" }, + "48" => { "value" => "Domestic abuse - previously joint tenancy with partner" }, + "49" => { "value" => "Domestic abuse - other" }, + "10" => { "value" => "Racial harassment" }, + "31" => { "value" => "Hate crime" }, + "11" => { "value" => "Other problems with neighbours" }, + "34" => { "value" => "Repossession" }, + "54" => { "value" => "Could no longer afford rent or mortgage" }, + "12" => { "value" => "Property unsuitable because of overcrowding" }, + "13" => { "value" => "Property unsuitable because of ill health or disability" }, + "14" => { "value" => "Property unsuitable because of poor condition" }, + "29" => { "value" => "Under occupation (offered incentive to downsize)" }, + "30" => { "value" => "Under occupation (no incentive)" }, + "18" => { "value" => "To move to accommodation with support" }, + "19" => { "value" => "To move to independent accommodation" }, + "20" => { "value" => "Other" }, + "28" => { "value" => "Don’t know" }, + "divider" => { "value" => true }, + "47" => { "value" => "Tenant prefers not to say" }, + }) + end end end diff --git a/spec/models/form/lettings/questions/wheelchair_spec.rb b/spec/models/form/lettings/questions/wheelchair_spec.rb index ac0241674..1fa65a68b 100644 --- a/spec/models/form/lettings/questions/wheelchair_spec.rb +++ b/spec/models/form/lettings/questions/wheelchair_spec.rb @@ -4,6 +4,12 @@ RSpec.describe Form::Lettings::Questions::Wheelchair, type: :model do subject(:question) { described_class.new(nil, nil, page) } let(:page) { instance_double(Form::Page) } + let(:subsection) { instance_double(Form::Subsection) } + + before do + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: false)) + end it "has correct page" do expect(question.page).to eq(page) @@ -39,4 +45,14 @@ RSpec.describe Form::Lettings::Questions::Wheelchair, type: :model do it "is not marked as derived" do expect(question.derived?).to be false end + + context "with 2024 form" do + before do + allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: true)) + end + + it "has the correct hint_text" do + expect(question.hint_text).to eq "This is whether someone who uses a wheelchair is able to make full use of all of the property’s rooms and facilities, including use of both inside and outside space, and entering and exiting the property." + end + end end diff --git a/spec/models/form/sales/pages/about_price_rtb_spec.rb b/spec/models/form/sales/pages/about_price_rtb_spec.rb index c73a83b61..81cfbc6fc 100644 --- a/spec/models/form/sales/pages/about_price_rtb_spec.rb +++ b/spec/models/form/sales/pages/about_price_rtb_spec.rb @@ -7,6 +7,10 @@ RSpec.describe Form::Sales::Pages::AboutPriceRtb, type: :model do let(:page_definition) { nil } let(:subsection) { instance_double(Form::Subsection) } + before do + allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: false)) + end + it "has correct subsection" do expect(page.subsection).to eq(subsection) end diff --git a/spec/models/form/sales/pages/buyer_live_spec.rb b/spec/models/form/sales/pages/buyer_live_spec.rb index 685798f59..be75458b3 100644 --- a/spec/models/form/sales/pages/buyer_live_spec.rb +++ b/spec/models/form/sales/pages/buyer_live_spec.rb @@ -6,6 +6,12 @@ RSpec.describe Form::Sales::Pages::BuyerLive, type: :model do let(:page_id) { nil } let(:page_definition) { nil } let(:subsection) { instance_double(Form::Subsection) } + let(:form) { instance_double(Form) } + + before do + allow(form).to receive(:start_year_after_2024?).and_return(false) + allow(subsection).to receive(:form).and_return(form) + end it "has correct subsection" do expect(page.subsection).to eq(subsection) diff --git a/spec/models/form/sales/pages/buyer_previous_spec.rb b/spec/models/form/sales/pages/buyer_previous_spec.rb index e6dda073e..188fac7a4 100644 --- a/spec/models/form/sales/pages/buyer_previous_spec.rb +++ b/spec/models/form/sales/pages/buyer_previous_spec.rb @@ -3,11 +3,21 @@ require "rails_helper" RSpec.describe Form::Sales::Pages::BuyerPrevious, type: :model do subject(:page) { described_class.new(page_id, page_definition, subsection, joint_purchase:) } + let(:log) { create(:sales_log, :completed) } + let(:page_id) { "example" } let(:page_definition) { nil } let(:subsection) { instance_double(Form::Subsection) } + let(:form) { instance_double(Form) } let(:joint_purchase) { false } + before do + allow(subsection).to receive(:depends_on).and_return(nil) + allow(subsection).to receive(:enabled?).and_return(true) + allow(subsection).to receive(:form).and_return(form) + allow(form).to receive(:depends_on_met).and_return(true) + end + it "has correct subsection" do expect(page.subsection).to eq(subsection) end @@ -32,13 +42,60 @@ RSpec.describe Form::Sales::Pages::BuyerPrevious, type: :model do let(:joint_purchase) { true } it "has the correct depends on" do - expect(page.depends_on).to eq([{ "joint_purchase?" => true }]) + expect(page.depends_on).to eq([{ "joint_purchase?" => true, "soctenant_is_inferred?" => false }]) end end context "when sales is not a joint purchase" do it "has the correct depends on" do - expect(page.depends_on).to eq([{ "joint_purchase?" => false }]) + expect(page.depends_on).to eq([{ "joint_purchase?" => false, "soctenant_is_inferred?" => false }]) + end + end + + context "with 23/24 log" do + before do + Timecop.freeze(Time.zone.local(2023, 4, 2)) + Singleton.__init__(FormHandler) + end + + after do + Timecop.return + end + + it "has correct routed to" do + log.staircase = 1 + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "with 24/25 log" do + before do + Timecop.freeze(Time.zone.local(2024, 4, 2)) + Singleton.__init__(FormHandler) + end + + after do + Timecop.return + end + + it "has correct routed to when staircase is yes" do + log.staircase = 1 + expect(page.routed_to?(log, nil)).to eq(false) + end + + it "has correct routed to when staircase is nil" do + log.staircase = nil + expect(page.routed_to?(log, nil)).to eq(true) + end + + it "has correct routed to when staircase is no" do + log.staircase = 2 + expect(page.routed_to?(log, nil)).to eq(true) + end + + it "has correct routed to when staircase is don't know" do + log.staircase = 3 + expect(page.routed_to?(log, nil)).to eq(true) end end end diff --git a/spec/models/form/sales/pages/la_nominations_spec.rb b/spec/models/form/sales/pages/la_nominations_spec.rb index b7b40c2dd..ab41d7f94 100644 --- a/spec/models/form/sales/pages/la_nominations_spec.rb +++ b/spec/models/form/sales/pages/la_nominations_spec.rb @@ -3,10 +3,16 @@ require "rails_helper" RSpec.describe Form::Sales::Pages::LaNominations, type: :model do subject(:page) { described_class.new(page_id, page_definition, subsection) } + let(:log) { create(:sales_log, :completed) } + let(:page_id) { nil } let(:page_definition) { nil } let(:subsection) { instance_double(Form::Subsection) } + before do + allow(subsection).to receive(:depends_on).and_return(nil) + end + it "has correct subsection" do expect(page.subsection).to eq(subsection) end @@ -26,4 +32,51 @@ RSpec.describe Form::Sales::Pages::LaNominations, type: :model do it "has the correct description" do expect(page.description).to be_nil end + + context "with 23/24 log" do + before do + Timecop.freeze(Time.zone.local(2023, 4, 2)) + Singleton.__init__(FormHandler) + end + + after do + Timecop.return + end + + it "has correct routed to" do + log.staircase = 1 + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "with 24/25 log" do + before do + Timecop.freeze(Time.zone.local(2024, 4, 2)) + Singleton.__init__(FormHandler) + end + + after do + Timecop.return + end + + it "has correct routed to when staircase is yes" do + log.staircase = 1 + expect(page.routed_to?(log, nil)).to eq(false) + end + + it "has correct routed to when staircase is nil" do + log.staircase = nil + expect(page.routed_to?(log, nil)).to eq(true) + end + + it "has correct routed to when staircase is no" do + log.staircase = 2 + expect(page.routed_to?(log, nil)).to eq(true) + end + + it "has correct routed to when staircase is don't know" do + log.staircase = 3 + expect(page.routed_to?(log, nil)).to eq(true) + end + end end diff --git a/spec/models/form/sales/pages/property_wheelchair_accessible_spec.rb b/spec/models/form/sales/pages/property_wheelchair_accessible_spec.rb index f83bb7907..a788bc687 100644 --- a/spec/models/form/sales/pages/property_wheelchair_accessible_spec.rb +++ b/spec/models/form/sales/pages/property_wheelchair_accessible_spec.rb @@ -7,6 +7,10 @@ RSpec.describe Form::Sales::Pages::PropertyWheelchairAccessible, type: :model do let(:page_definition) { nil } let(:subsection) { instance_double(Form::Subsection) } + before do + allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: false)) + end + it "has correct subsection" do expect(page.subsection).to eq(subsection) end diff --git a/spec/models/form/sales/questions/buyer_live_spec.rb b/spec/models/form/sales/questions/buyer_live_spec.rb index a946a7905..cea2736e5 100644 --- a/spec/models/form/sales/questions/buyer_live_spec.rb +++ b/spec/models/form/sales/questions/buyer_live_spec.rb @@ -6,6 +6,14 @@ RSpec.describe Form::Sales::Questions::BuyerLive, type: :model do let(:question_id) { nil } let(:question_definition) { nil } let(:page) { instance_double(Form::Page) } + let(:subsection) { instance_double(Form::Subsection) } + let(:form) { instance_double(Form) } + + before do + allow(form).to receive(:start_year_after_2024?).and_return(false) + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(form) + end it "has correct page" do expect(question.page).to eq(page) @@ -15,10 +23,6 @@ RSpec.describe Form::Sales::Questions::BuyerLive, type: :model do expect(question.id).to eq("buylivein") end - it "has the correct header" do - expect(question.header).to eq("Will the buyers live in the property?") - end - it "has the correct check_answer_label" do expect(question.check_answer_label).to eq("Buyers living in property") end @@ -37,4 +41,20 @@ RSpec.describe Form::Sales::Questions::BuyerLive, type: :model do "2" => { "value" => "No" }, }) end + + context "with 2023/24 form" do + it "has the correct header" do + expect(question.header).to eq("Will the buyers live in the property?") + end + end + + context "with 2024/25 form" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(true) + end + + it "has the correct header" do + expect(question.header).to eq("Will any buyers live in the property?") + end + end end diff --git a/spec/models/form/sales/questions/buyer_previous_spec.rb b/spec/models/form/sales/questions/buyer_previous_spec.rb index 06aaca38f..e52a514d3 100644 --- a/spec/models/form/sales/questions/buyer_previous_spec.rb +++ b/spec/models/form/sales/questions/buyer_previous_spec.rb @@ -6,8 +6,15 @@ RSpec.describe Form::Sales::Questions::BuyerPrevious, type: :model do let(:question_id) { nil } let(:question_definition) { nil } let(:page) { instance_double(Form::Page) } + let(:subsection) { instance_double(Form::Subsection) } + let(:form) { instance_double(Form) } let(:joint_purchase) { true } + before do + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(form) + end + it "has correct page" do expect(question.page).to eq(page) end @@ -42,10 +49,6 @@ RSpec.describe Form::Sales::Questions::BuyerPrevious, type: :model do expect(question.type).to eq("radio") end - it "is not marked as derived" do - expect(question.derived?).to be false - end - it "has the correct displayed_answer_options" do expect(question.displayed_answer_options(nil)).to eq({ "1" => { "value" => "Yes" }, @@ -68,4 +71,24 @@ RSpec.describe Form::Sales::Questions::BuyerPrevious, type: :model do it "has the correct hint" do expect(question.hint_text).to eq(nil) end + + context "when form year is before 2024" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(false) + end + + it "is not marked as derived" do + expect(question.derived?).to be false + end + end + + context "when form year is >= 2024" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(true) + end + + it "is marked as derived" do + expect(question.derived?).to be true + end + end end diff --git a/spec/models/form/sales/questions/discount_spec.rb b/spec/models/form/sales/questions/discount_spec.rb index f40b2eded..c3b7d24a1 100644 --- a/spec/models/form/sales/questions/discount_spec.rb +++ b/spec/models/form/sales/questions/discount_spec.rb @@ -6,6 +6,12 @@ RSpec.describe Form::Sales::Questions::Discount, type: :model do let(:question_id) { nil } let(:question_definition) { nil } let(:page) { instance_double(Form::Page) } + let(:subsection) { instance_double(Form::Subsection) } + + before do + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: false)) + end it "has correct page" do expect(question.page).to eq(page) @@ -52,4 +58,14 @@ RSpec.describe Form::Sales::Questions::Discount, type: :model do it "has correct max" do expect(question.max).to eq(100) end + + context "with form start year after 2024" do + before do + allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: true)) + end + + it "has correct max" do + expect(question.max).to eq(70) + end + end end diff --git a/spec/models/form/sales/questions/gender_identity1_spec.rb b/spec/models/form/sales/questions/gender_identity1_spec.rb index 2abe952ed..e7f07577c 100644 --- a/spec/models/form/sales/questions/gender_identity1_spec.rb +++ b/spec/models/form/sales/questions/gender_identity1_spec.rb @@ -6,6 +6,13 @@ RSpec.describe Form::Sales::Questions::GenderIdentity1, type: :model do let(:question_id) { nil } let(:question_definition) { nil } let(:page) { instance_double(Form::Page) } + let(:subsection) { instance_double(Form::Subsection) } + let(:form) { instance_double(Form) } + + before do + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(form) + end it "has correct page" do expect(question.page).to eq(page) @@ -31,10 +38,6 @@ RSpec.describe Form::Sales::Questions::GenderIdentity1, type: :model do expect(question.derived?).to be false end - it "has the correct hint" do - expect(question.hint_text).to eq("Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest.") - end - it "has the correct answer_options" do expect(question.answer_options).to eq({ "F" => { "value" => "Female" }, @@ -47,4 +50,24 @@ RSpec.describe Form::Sales::Questions::GenderIdentity1, type: :model do it "has the correct check_answers_card_number" do expect(question.check_answers_card_number).to eq(1) end + + context "with start year before 2024" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(false) + end + + it "has the correct hint" do + expect(question.hint_text).to eq("Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest.") + end + end + + context "with start year >= 2024" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(true) + end + + it "has the correct hint" do + expect(question.hint_text).to eq("This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth.") + end + end end diff --git a/spec/models/form/sales/questions/gender_identity2_spec.rb b/spec/models/form/sales/questions/gender_identity2_spec.rb index 7c405afc6..28a828155 100644 --- a/spec/models/form/sales/questions/gender_identity2_spec.rb +++ b/spec/models/form/sales/questions/gender_identity2_spec.rb @@ -6,6 +6,13 @@ RSpec.describe Form::Sales::Questions::GenderIdentity2, type: :model do let(:question_id) { nil } let(:question_definition) { nil } let(:page) { instance_double(Form::Page) } + let(:subsection) { instance_double(Form::Subsection) } + let(:form) { instance_double(Form) } + + before do + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(form) + end it "has correct page" do expect(question.page).to eq(page) @@ -49,4 +56,24 @@ RSpec.describe Form::Sales::Questions::GenderIdentity2, type: :model do { "condition" => { "sex2" => "R" }, "value" => "Prefers not to say" }, ]) end + + context "with start year before 2024" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(false) + end + + it "has the correct hint" do + expect(question.hint_text).to be_nil + end + end + + context "with start year >= 2024" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(true) + end + + it "has the correct hint" do + expect(question.hint_text).to eq("This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth.") + end + end end diff --git a/spec/models/form/sales/questions/person_gender_identity_spec.rb b/spec/models/form/sales/questions/person_gender_identity_spec.rb index 0ae959e82..6c0691d25 100644 --- a/spec/models/form/sales/questions/person_gender_identity_spec.rb +++ b/spec/models/form/sales/questions/person_gender_identity_spec.rb @@ -7,6 +7,13 @@ RSpec.describe Form::Sales::Questions::PersonGenderIdentity, type: :model do let(:question_definition) { nil } let(:page) { instance_double(Form::Page) } let(:person_index) { 2 } + let(:subsection) { instance_double(Form::Subsection) } + let(:form) { instance_double(Form) } + + before do + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(form) + end it "has correct page" do expect(question.page).to eq(page) @@ -29,6 +36,26 @@ RSpec.describe Form::Sales::Questions::PersonGenderIdentity, type: :model do }) end + context "when form year is before 2024" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(false) + end + + it "has the correct hint" do + expect(question.hint_text).to be_nil + end + end + + context "when form year is >= 2024" do + before do + allow(form).to receive(:start_year_after_2024?).and_return(true) + end + + it "has the correct hint" do + expect(question.hint_text).to eq("This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth.") + end + end + context "when person 2" do let(:question_id) { "sex2" } let(:person_index) { 2 } diff --git a/spec/models/form/sales/questions/property_wheelchair_accessible_spec.rb b/spec/models/form/sales/questions/property_wheelchair_accessible_spec.rb index c8d7eadaf..bea482aca 100644 --- a/spec/models/form/sales/questions/property_wheelchair_accessible_spec.rb +++ b/spec/models/form/sales/questions/property_wheelchair_accessible_spec.rb @@ -6,6 +6,12 @@ RSpec.describe Form::Sales::Questions::PropertyWheelchairAccessible, type: :mode let(:question_id) { nil } let(:question_definition) { nil } let(:page) { instance_double(Form::Page) } + let(:subsection) { instance_double(Form::Subsection) } + + before do + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: false)) + end it "has correct page" do expect(question.page).to eq(page) @@ -38,4 +44,14 @@ RSpec.describe Form::Sales::Questions::PropertyWheelchairAccessible, type: :mode "3" => { "value" => "Don't know" }, }) end + + context "with 2024 form" do + before do + allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: true)) + end + + it "has the correct hint_text" do + expect(question.hint_text).to eq("This is whether someone who uses a wheelchair is able to make full use of all of the property’s rooms and facilities, including use of both inside and outside space, and entering and exiting the property.") + end + end end diff --git a/spec/models/validations/sales/sale_information_validations_spec.rb b/spec/models/validations/sales/sale_information_validations_spec.rb index 392686ac7..73243e9fa 100644 --- a/spec/models/validations/sales/sale_information_validations_spec.rb +++ b/spec/models/validations/sales/sale_information_validations_spec.rb @@ -467,4 +467,293 @@ RSpec.describe Validations::Sales::SaleInformationValidations do end end end + + describe "#validate_grant_amount" do + context "when within permitted bounds" do + let(:record) { build(:sales_log, grant: 10_000, saledate: Time.zone.local(2024, 4, 5)) } + + it "does not add an error" do + sale_information_validator.validate_grant_amount(record) + + expect(record.errors).not_to be_present + end + end + + context "when over the max" do + let(:record) { build(:sales_log, type: 8, grant: 17_000, saledate: Time.zone.local(2024, 4, 5)) } + + it "adds an error" do + sale_information_validator.validate_grant_amount(record) + + expect(record.errors[:grant]).to include("Loan, grants or subsidies must be between £9,000 and £16,000") + end + end + + context "when under the min" do + let(:record) { build(:sales_log, type: 21, grant: 3, saledate: Time.zone.local(2024, 4, 5)) } + + it "adds an error" do + sale_information_validator.validate_grant_amount(record) + + expect(record.errors[:grant]).to include("Loan, grants or subsidies must be between £9,000 and £16,000") + end + end + + context "when grant is blank" do + let(:record) { build(:sales_log, type: 21, grant: nil, saledate: Time.zone.local(2024, 4, 5)) } + + it "does not add an error" do + sale_information_validator.validate_grant_amount(record) + + expect(record.errors).not_to be_present + end + end + + context "when over the max and type is not RTA of social homebuy" do + let(:record) { build(:sales_log, type: 9, grant: 17_000, saledate: Time.zone.local(2024, 4, 5)) } + + it "does not add an error" do + sale_information_validator.validate_grant_amount(record) + + expect(record.errors).not_to be_present + end + end + + context "when under the min and type is not RTA of social homebuy" do + let(:record) { build(:sales_log, type: 9, grant: 17_000, saledate: Time.zone.local(2024, 4, 5)) } + + it "does not add error" do + sale_information_validator.validate_grant_amount(record) + + expect(record.errors).not_to be_present + end + end + + context "with log before 2024/25 collection" do + let(:record) { build(:sales_log, type: 8, grant: 3, saledate: Time.zone.local(2023, 4, 5)) } + + it "does not add an error" do + sale_information_validator.validate_grant_amount(record) + + expect(record.errors).not_to be_present + end + end + end + + describe "#validate_stairbought" do + let(:now) { Time.zone.local(2024, 4, 4) } + + before do + Timecop.freeze(now) + Singleton.__init__(FormHandler) + end + + after do + Timecop.return + Singleton.__init__(FormHandler) + end + + [ + ["Shared Ownership (new model lease)", 30, 90], + ["Home Ownership for people with Long-Term Disabilities (HOLD)", 16, 90], + ["Rent to Buy — Shared Ownership", 28, 90], + ["Right to Shared Ownership (RtSO)", 31, 90], + ["London Living Rent — Shared Ownership", 32, 90], + ["Shared Ownership (old model lease)", 2, 75], + ["Social HomeBuy — shared ownership purchase", 18, 75], + ["Older Persons Shared Ownership", 24, 50], + ].each do |label, type, max| + context "when ownership type is #{label}" do + let(:record) { build(:sales_log, ownershipsch: 1, type:, saledate: now) } + + it "does not add an error if stairbought is under #{max}%" do + record.stairbought = max - 1 + sale_information_validator.validate_stairbought(record) + + expect(record.errors).to be_empty + end + + it "does not add an error if stairbought is #{max}%" do + record.stairbought = max + sale_information_validator.validate_stairbought(record) + + expect(record.errors).to be_empty + end + + it "does not add an error if stairbought is not given" do + record.stairbought = nil + sale_information_validator.validate_stairbought(record) + + expect(record.errors).to be_empty + end + + it "adds an error if stairbought is over #{max}%" do + record.stairbought = max + 2 + sale_information_validator.validate_stairbought(record) + + expect(record.errors[:stairbought]).to include("The percentage bought in this staircasing transaction cannot be higher than #{max}% for #{label} sales.") + expect(record.errors[:type]).to include("The percentage bought in this staircasing transaction cannot be higher than #{max}% for #{label} sales.") + end + end + end + context "when the collection year is before 2024" do + let(:record) { build(:sales_log, ownershipsch: 1, type: 24, saledate: now, stairbought: 90) } + let(:now) { Time.zone.local(2023, 4, 4) } + + it "does not add an error" do + sale_information_validator.validate_stairbought(record) + + expect(record.errors).to be_empty + end + end + end + + describe "#validate_discount_and_value" do + let(:record) { FactoryBot.build(:sales_log, value: 200_000, discount: 50, ownershipsch: 2, type: 9, saledate: now) } + let(:now) { Time.zone.local(2024, 4, 1) } + + around do |example| + Timecop.freeze(now) do + example.run + end + Timecop.return + end + + context "with a log in the 24/25 collection year" do + context "when in London" do + before do + record.la = "E09000001" + end + + it "adds an error if value * discount is more than 136,400" do + record.discount = 80 + sale_information_validator.validate_discount_and_value(record) + expect(record.errors["value"]).to include("The percentage discount multiplied by the purchase price is £160,000.00. This figure should not be more than £136,400 for properties in London.") + expect(record.errors["discount"]).to include("The percentage discount multiplied by the purchase price is £160,000.00. This figure should not be more than £136,400 for properties in London.") + expect(record.errors["la"]).to include("The percentage discount multiplied by the purchase price is £160,000.00. This figure should not be more than £136,400 for properties in London.") + expect(record.errors["postcode_full"]).to include("The percentage discount multiplied by the purchase price is £160,000.00. This figure should not be more than £136,400 for properties in London.") + expect(record.errors["uprn"]).to include("The percentage discount multiplied by the purchase price is £160,000.00. This figure should not be more than £136,400 for properties in London.") + end + + it "does not add an error value * discount is less than 136,400" do + sale_information_validator.validate_discount_and_value(record) + expect(record.errors["value"]).to be_empty + expect(record.errors["discount"]).to be_empty + expect(record.errors["la"]).to be_empty + expect(record.errors["postcode_full"]).to be_empty + expect(record.errors["uprn"]).to be_empty + end + end + + context "when in outside of London" do + before do + record.la = "E06000015" + end + + it "adds an error if value * discount is more than 136,400" do + record.discount = 52 + sale_information_validator.validate_discount_and_value(record) + expect(record.errors["value"]).to include("The percentage discount multiplied by the purchase price is £104,000.00. This figure should not be more than £102,400 for properties outside of London.") + expect(record.errors["discount"]).to include("The percentage discount multiplied by the purchase price is £104,000.00. This figure should not be more than £102,400 for properties outside of London.") + expect(record.errors["la"]).to include("The percentage discount multiplied by the purchase price is £104,000.00. This figure should not be more than £102,400 for properties outside of London.") + expect(record.errors["postcode_full"]).to include("The percentage discount multiplied by the purchase price is £104,000.00. This figure should not be more than £102,400 for properties outside of London.") + expect(record.errors["uprn"]).to include("The percentage discount multiplied by the purchase price is £104,000.00. This figure should not be more than £102,400 for properties outside of London.") + end + + it "does not add an error value * discount is less than 136,400" do + sale_information_validator.validate_discount_and_value(record) + expect(record.errors["value"]).to be_empty + expect(record.errors["discount"]).to be_empty + expect(record.errors["la"]).to be_empty + expect(record.errors["postcode_full"]).to be_empty + expect(record.errors["uprn"]).to be_empty + end + end + end + + context "when it is a 2023 log" do + let(:record) { FactoryBot.build(:sales_log, value: 200_000, discount: 80, ownershipsch: 2, type: 9, saledate: Time.zone.local(2023, 4, 1), la: "E06000015") } + + it "does not add an error" do + sale_information_validator.validate_discount_and_value(record) + expect(record.errors["value"]).to be_empty + expect(record.errors["discount"]).to be_empty + expect(record.errors["la"]).to be_empty + expect(record.errors["postcode_full"]).to be_empty + expect(record.errors["uprn"]).to be_empty + end + end + end + + describe "#validate_non_staircasing_mortgage" do + let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, deposit: 5_000, value: 30_000, equity: 28, ownershipsch: 1, type: 30, 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 24/25 collection year" do + let(:now) { Time.zone.local(2024, 4, 4) } + + context "when MORTGAGE + DEPOSIT does not equal VALUE * EQUITY/100 " do + context "and it is not a staircase transaction" do + before do + record.staircase = 2 + end + + it "adds an error" do + sale_information_validator.validate_non_staircasing_mortgage(record) + expect(record.errors["mortgage"]).to include("The mortgage and deposit added together is £15,000.00 and the purchase price times by the equity is £8,400.00. These figures should be the same.") + expect(record.errors["value"]).to include("The mortgage and deposit added together is £15,000.00 and the purchase price times by the equity is £8,400.00. These figures should be the same.") + expect(record.errors["deposit"]).to include("The mortgage and deposit added together is £15,000.00 and the purchase price times by the equity is £8,400.00. These figures should be the same.") + expect(record.errors["equity"]).to include("The mortgage and deposit added together is £15,000.00 and the purchase price times by the equity is £8,400.00. These figures should be the same.") + end + end + + context "and it is a staircase transaction" do + before do + record.staircase = 1 + end + + it "does not add an error" do + sale_information_validator.validate_non_staircasing_mortgage(record) + expect(record.errors["mortgage"]).to be_empty + expect(record.errors["value"]).to be_empty + expect(record.errors["deposit"]).to be_empty + expect(record.errors["equity"]).to be_empty + end + end + end + + context "when MORTGAGE + DEPOSIT equals VALUE * EQUITY/100" do + let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, staircase: 2, deposit: 5_000, value: 30_000, equity: 50, ownershipsch: 1, type: 30, saledate: now) } + + it "does not add an error" do + sale_information_validator.validate_non_staircasing_mortgage(record) + expect(record.errors["mortgage"]).to be_empty + expect(record.errors["value"]).to be_empty + expect(record.errors["deposit"]).to be_empty + expect(record.errors["equity"]).to be_empty + end + end + end + + context "when it is a 2023 log" do + let(:now) { Time.zone.local(2023, 4, 1) } + let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, staircase: 2, deposit: 5_000, value: 30_000, equity: 28, ownershipsch: 1, type: 30, saledate: now) } + + it "does not add an error" do + sale_information_validator.validate_non_staircasing_mortgage(record) + expect(record.errors["mortgage"]).to be_empty + expect(record.errors["value"]).to be_empty + expect(record.errors["deposit"]).to be_empty + expect(record.errors["equity"]).to be_empty + 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 8718eee86..e417fbd8e 100644 --- a/spec/models/validations/sales/soft_validations_spec.rb +++ b/spec/models/validations/sales/soft_validations_spec.rb @@ -660,21 +660,51 @@ RSpec.describe Validations::Sales::SoftValidations do describe "#grant_outside_common_range?" do it "returns true if grant is below 9000" do record.grant = 1_000 + record.type = 9 + record.saledate = Time.zone.local(2024, 1, 1) expect(record).to be_grant_outside_common_range end it "returns true if grant is above 16000" do record.grant = 100_000 + record.type = 9 + record.saledate = Time.zone.local(2024, 1, 1) expect(record).to be_grant_outside_common_range end it "returns false if grant is within expected range" do record.grant = 10_000 + record.type = 9 + record.saledate = Time.zone.local(2024, 1, 1) expect(record).not_to be_grant_outside_common_range end + + it "returns false for logs after 2024 with RTA" do + record.grant = 100_000 + record.type = 8 + record.saledate = Time.zone.local(2025, 1, 1) + + expect(record).not_to be_grant_outside_common_range + end + + it "returns false for logs after 2024 with socialBuy" do + record.grant = 100_000 + record.type = 21 + record.saledate = Time.zone.local(2025, 1, 1) + + expect(record).not_to be_grant_outside_common_range + end + + it "returns true for logs after 2024 with other type" do + record.grant = 100_000 + record.type = 9 + record.saledate = Time.zone.local(2025, 1, 1) + + expect(record).to be_grant_outside_common_range + end end describe "#staircase_bought_above_fifty" do diff --git a/spec/requests/bulk_upload_lettings_logs_controller_spec.rb b/spec/requests/bulk_upload_lettings_logs_controller_spec.rb index a64f60531..afc5577c3 100644 --- a/spec/requests/bulk_upload_lettings_logs_controller_spec.rb +++ b/spec/requests/bulk_upload_lettings_logs_controller_spec.rb @@ -49,7 +49,7 @@ RSpec.describe BulkUploadLettingsLogsController, type: :request do it "shows guidance page with correct title" do Timecop.freeze(2022, 1, 1) do - get "/lettings-logs/bulk-upload-logs/guidance?form%5Byear%5D=#{expected_year}", params: {} + get "/lettings-logs/bulk-upload-logs/guidance?form%5Byear%5D=2022", params: {} expect(response.body).to include("How to upload logs in bulk") end @@ -57,11 +57,9 @@ RSpec.describe BulkUploadLettingsLogsController, type: :request do end context "when in crossover period" do - let(:expected_year) { FormHandler.instance.forms["current_sales"].start_date.year } - it "shows guidance page with correct title" do Timecop.freeze(2023, 6, 1) do - get "/lettings-logs/bulk-upload-logs/guidance?form%5Byear%5D=#{expected_year}", params: {} + get "/lettings-logs/bulk-upload-logs/guidance?form%5Byear%5D=2023", params: {} expect(response.body).to include("How to upload logs in bulk") end diff --git a/spec/requests/bulk_upload_sales_logs_controller_spec.rb b/spec/requests/bulk_upload_sales_logs_controller_spec.rb index 299e89aef..99c8de286 100644 --- a/spec/requests/bulk_upload_sales_logs_controller_spec.rb +++ b/spec/requests/bulk_upload_sales_logs_controller_spec.rb @@ -45,11 +45,10 @@ RSpec.describe BulkUploadSalesLogsController, type: :request do describe "GET /sales-logs/bulk-upload-logs/guidance" do context "when not in crossover period" do - let(:expected_year) { FormHandler.instance.forms["current_sales"].start_date.year } it "shows guidance page with correct title" do Timecop.freeze(2022, 1, 1) do - get "/sales-logs/bulk-upload-logs/guidance?form%5Byear%5D=#{expected_year}", params: {} + get "/sales-logs/bulk-upload-logs/guidance?form%5Byear%5D=2022", params: {} expect(response.body).to include("How to upload logs in bulk") end @@ -57,11 +56,9 @@ RSpec.describe BulkUploadSalesLogsController, type: :request do end context "when in crossover period" do - let(:expected_year) { FormHandler.instance.forms["current_sales"].start_date.year } - it "shows guidance page with correct title" do Timecop.freeze(2023, 6, 1) do - get "/sales-logs/bulk-upload-logs/guidance?form%5Byear%5D=#{expected_year}", params: {} + get "/sales-logs/bulk-upload-logs/guidance?form%5Byear%5D=2023", params: {} expect(response.body).to include("How to upload logs in bulk") end diff --git a/spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb index a37110785..58be059fc 100644 --- a/spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb @@ -1816,6 +1816,24 @@ RSpec.describe BulkUpload::Lettings::Year2024::RowParser do end end + describe "#beds" do + context "when property is a bedsit" do + let(:attributes) { setup_section_params.merge({ field_26: 2, field_29: 2 }) } + + it "sets value to 1 even if field_29 contradicts this" do + expect(parser.log.beds).to be(1) + end + end + + context "when property is not a bedsit" do + let(:attributes) { setup_section_params.merge({ field_26: 1, field_29: 2 }) } + + it "sets value to field_29" do + expect(parser.log.beds).to be(2) + end + end + end + describe "#cbl" do context "when field_112 is yes ie 1" do let(:attributes) { { bulk_upload:, field_112: 1 } } diff --git a/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb b/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb index 473967791..93e6fdca1 100644 --- a/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb +++ b/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb @@ -1264,10 +1264,56 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do end describe "#soctenant" do - let(:attributes) { setup_section_params.merge({ field_99: "1" }) } + context "when discounted ownership" do + let(:attributes) { valid_attributes.merge({ field_8: "2" }) } - it "is correctly set" do - expect(parser.log.soctenant).to be(1) + it "is set to nil" do + expect(parser.log.soctenant).to be_nil + end + end + + context "when outright sale" do + let(:attributes) { valid_attributes.merge({ field_8: "3" }) } + + it "is set to nil" do + expect(parser.log.soctenant).to be_nil + end + end + + context "when shared ownership" do + context "when prevten is a social housing type" do + let(:attributes) { valid_attributes.merge({ field_8: "1", field_61: "1" }) } + + it "is set to yes" do + expect(parser.log.soctenant).to be(1) + end + end + + context "when prevten is not a social housing type" do + context "and prevtenbuy2 is a social housing type" do + let(:attributes) { valid_attributes.merge({ field_8: "1", field_61: "3", field_71: "2" }) } + + it "is set to yes" do + expect(parser.log.soctenant).to be(1) + end + end + + context "and prevtenbuy2 is not a social housing type" do + let(:attributes) { valid_attributes.merge({ field_8: "1", field_61: "3", field_71: "4" }) } + + it "is set to no" do + expect(parser.log.soctenant).to be(2) + end + end + + context "and prevtenbuy2 is blank" do + let(:attributes) { valid_attributes.merge({ field_8: "1", field_61: "3", field_71: nil }) } + + it "is set to no" do + expect(parser.log.soctenant).to be(2) + end + end + end end end