From 0b4ff6542916c671b20e4fcca65c9296b61d5ccc Mon Sep 17 00:00:00 2001 From: Manny Dinssa <44172848+Dinssa@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:59:15 +0000 Subject: [PATCH 01/34] CLDC-3689: Extract lettings household validations (#2741) * Extract household validations * Use question ids a primary key * Fix lint * Refactor, remove one level of keys * Fix merge --- .../validations/household_validations.rb | 89 +++++++-------- config/locales/en.yml | 50 --------- .../validations/lettings/household.en.yml | 93 +++++++++++++++ .../validations/household_validations_spec.rb | 106 +++++++++--------- 4 files changed, 190 insertions(+), 148 deletions(-) diff --git a/app/models/validations/household_validations.rb b/app/models/validations/household_validations.rb index 703e9f394..cf0ae009e 100644 --- a/app/models/validations/household_validations.rb +++ b/app/models/validations/household_validations.rb @@ -5,7 +5,7 @@ module Validations::HouseholdValidations # or 'validate_' to run on submit as well def validate_reasonable_preference(record) if !record.given_reasonable_preference? && [record.rp_homeless, record.rp_insan_unsat, record.rp_medwel, record.rp_hardship, record.rp_dontknow].any? { |a| a == 1 } - record.errors.add :reasonable_preference_reason, I18n.t("validations.household.reasonable_preference_reason.reason_not_required") + record.errors.add :reasonable_preference_reason, I18n.t("validations.lettings.household.reasonable_preference_reason.reason_not_required") end end @@ -25,29 +25,29 @@ module Validations::HouseholdValidations def validate_reason_for_leaving_last_settled_home(record) if record.reason == 32 && record.underoccupation_benefitcap != 4 - record.errors.add :underoccupation_benefitcap, I18n.t("validations.household.underoccupation_benefitcap.dont_know_required") - record.errors.add :reason, I18n.t("validations.household.underoccupation_benefitcap.dont_know_required") + record.errors.add :underoccupation_benefitcap, I18n.t("validations.lettings.household.underoccupation_benefitcap.leaving_last_settled_home.dont_know_required") + record.errors.add :reason, I18n.t("validations.lettings.household.reason.leaving_last_settled_home.dont_know_required") end validate_other_field(record, 20, :reason, :reasonother) if record.is_reason_permanently_decanted? && record.referral.present? && !record.is_internal_transfer? - record.errors.add :referral, I18n.t("validations.household.referral.reason_permanently_decanted") - record.errors.add :reason, I18n.t("validations.household.reason.not_internal_transfer") + record.errors.add :referral, I18n.t("validations.lettings.household.referral.leaving_last_settled_home.reason_permanently_decanted") + record.errors.add :reason, I18n.t("validations.lettings.household.reason.leaving_last_settled_home.not_internal_transfer") end return unless record.form.start_year_2024_or_later? if record.reason == 20 && PHRASES_INDICATING_HOMELESSNESS_REGEX.match?(record.reasonother) - record.errors.add :reason, I18n.t("validations.household.reason.other_not_settled") + record.errors.add :reason, I18n.t("validations.lettings.household.reason.leaving_last_settled_home.other_not_settled") end end def validate_armed_forces(record) if (record.armed_forces_no? || record.armed_forces_refused?) && record.reservist.present? - record.errors.add :reservist, I18n.t("validations.household.reservist.injury_not_required") + record.errors.add :reservist, I18n.t("validations.lettings.household.reservist.injury_not_required") end if !record.armed_forces_regular? && record.leftreg.present? - record.errors.add :leftreg, I18n.t("validations.household.leftreg.question_not_required") + record.errors.add :leftreg, I18n.t("validations.lettings.household.leftreg.question_not_required") end end @@ -66,12 +66,12 @@ module Validations::HouseholdValidations return unless record.age1 && record.ecstat1 && !record.form.start_year_2024_or_later? if record.age1 < 16 && !economic_status_is_child_other_or_refused?(record.ecstat1) - record.errors.add "ecstat1", I18n.t("validations.household.ecstat.child_under_16", person_num: 1) - record.errors.add "age1", I18n.t("validations.household.age.child_under_16_ecstat", person_num: 1) + record.errors.add "ecstat1", I18n.t("validations.lettings.household.ecstat.child_under_16", person_num: 1) + record.errors.add "age1", I18n.t("validations.lettings.household.age.child_under_16_ecstat", person_num: 1) end if tenant_is_economic_child?(record.ecstat1) && record.age1 > 16 - record.errors.add "ecstat1", I18n.t("validations.household.ecstat.child_over_16", person_num: 1) - record.errors.add "age1", I18n.t("validations.household.age.child_over_16", person_num: 1) + record.errors.add "ecstat1", I18n.t("validations.lettings.household.ecstat.child_over_16", person_num: 1) + record.errors.add "age1", I18n.t("validations.lettings.household.age.child_over_16", person_num: 1) end end @@ -82,12 +82,13 @@ module Validations::HouseholdValidations next unless age && economic_status if age < 16 && !economic_status_is_child_other_or_refused?(economic_status) && !record.form.start_year_2024_or_later? - record.errors.add "ecstat#{person_num}", I18n.t("validations.household.ecstat.child_under_16", person_num:) - record.errors.add "age#{person_num}", I18n.t("validations.household.age.child_under_16_ecstat", person_num:) + record.errors.add "ecstat#{person_num}", I18n.t("validations.lettings.household.ecstat.child_under_16", person_num:) + record.errors.add "age#{person_num}", I18n.t("validations.lettings.household.age.child_under_16_ecstat", person_num:) + end if tenant_is_economic_child?(economic_status) && age > 16 - record.errors.add "ecstat#{person_num}", I18n.t("validations.household.ecstat.child_over_16", person_num:) - record.errors.add "age#{person_num}", I18n.t("validations.household.age.child_over_16", person_num:) + record.errors.add "ecstat#{person_num}", I18n.t("validations.lettings.household.ecstat.child_over_16", person_num:) + record.errors.add "age#{person_num}", I18n.t("validations.lettings.household.age.child_over_16", person_num:) end end end @@ -101,8 +102,8 @@ module Validations::HouseholdValidations next unless age && relationship if age < 16 && !relationship_is_child_other_or_refused?(relationship) - record.errors.add "relat#{person_num}", I18n.t("validations.household.relat.child_under_16_lettings", person_num:) - record.errors.add "age#{person_num}", I18n.t("validations.household.age.child_under_16_relat_lettings", person_num:) + record.errors.add "relat#{person_num}", I18n.t("validations.lettings.household.relat.child_under_16", person_num:) + record.errors.add "age#{person_num}", I18n.t("validations.lettings.household.age.child_under_16_relat", person_num:) end end end @@ -122,34 +123,34 @@ module Validations::HouseholdValidations child = tenant_is_child?(relationship) if age_between_16_19 && !(student || economic_status_refused) && child - record.errors.add "ecstat#{person_num}", I18n.t("validations.household.ecstat.student_16_19.must_be_student") - record.errors.add "age#{person_num}", I18n.t("validations.household.age.student_16_19.cannot_be_16_19.child_not_student") - record.errors.add "relat#{person_num}", I18n.t("validations.household.relat.student_16_19.cannot_be_child.16_19_not_student") + record.errors.add "ecstat#{person_num}", I18n.t("validations.lettings.household.ecstat.student_16_19.must_be_student") + record.errors.add "age#{person_num}", I18n.t("validations.lettings.household.age.student_16_19.cannot_be_16_19.child_not_student") + record.errors.add "relat#{person_num}", I18n.t("validations.lettings.household.relat.student_16_19.cannot_be_child.16_19_not_student") end next unless !age_between_16_19 && student && child - record.errors.add "age#{person_num}", I18n.t("validations.household.age.student_16_19.must_be_16_19") - record.errors.add "ecstat#{person_num}", I18n.t("validations.household.ecstat.student_16_19.cannot_be_student.child_not_16_19") - record.errors.add "relat#{person_num}", I18n.t("validations.household.relat.student_16_19.cannot_be_child.student_not_16_19") + record.errors.add "age#{person_num}", I18n.t("validations.lettings.household.age.student_16_19.must_be_16_19") + record.errors.add "ecstat#{person_num}", I18n.t("validations.lettings.household.ecstat.student_16_19.cannot_be_student.child_not_16_19") + record.errors.add "relat#{person_num}", I18n.t("validations.lettings.household.relat.student_16_19.cannot_be_child.student_not_16_19") end end def validate_condition_effects(record) all_options = [record.illness_type_1, record.illness_type_2, record.illness_type_3, record.illness_type_4, record.illness_type_5, record.illness_type_6, record.illness_type_7, record.illness_type_8, record.illness_type_9, record.illness_type_10] if all_options.count(1) >= 1 && household_no_illness?(record) - record.errors.add :condition_effects, I18n.t("validations.household.condition_effects.no_choices") + record.errors.add :condition_effects, I18n.t("validations.lettings.household.condition_effects.no_choices") end end def validate_previous_housing_situation(record) if record.is_relet_to_temp_tenant? && !record.previous_tenancy_was_temporary? - record.errors.add :prevten, :non_temp_accommodation, message: I18n.t("validations.household.prevten.non_temp_accommodation") + record.errors.add :prevten, :non_temp_accommodation, message: I18n.t("validations.lettings.household.prevten.non_temp_accommodation") end if record.age1.present? && record.age1 > 25 && record.previous_tenancy_was_foster_care? - record.errors.add :prevten, :over_25_foster_care, message: I18n.t("validations.household.prevten.over_25_foster_care") - record.errors.add :age1, I18n.t("validations.household.age.lead.over_25") + record.errors.add :prevten, :over_25_foster_care, message: I18n.t("validations.lettings.household.prevten.over_25_foster_care") + record.errors.add :age1, I18n.t("validations.lettings.household.age.lead.over_25") end # 3 Private Sector Tenancy @@ -168,8 +169,8 @@ module Validations::HouseholdValidations # 29 Prison / Approved Probation Hostel if record.is_internal_transfer? && [3, 4, 7, 10, 13, 14, 19, 23, 24, 25, 26, 27, 28, 29].include?(record.prevten) label = record.form.get_question("prevten", record).present? ? record.form.get_question("prevten", record).label_from_value(record.prevten) : "" - record.errors.add :prevten, :internal_transfer_non_social_housing, message: I18n.t("validations.household.prevten.internal_transfer", prevten: label) - record.errors.add :referral, :internal_transfer_non_social_housing, message: I18n.t("validations.household.referral.prevten_invalid", prevten: label) + record.errors.add :prevten, :internal_transfer_non_social_housing, message: I18n.t("validations.lettings.household.prevten.internal_transfer", prevten: label) + record.errors.add :referral, :internal_transfer_non_social_housing, message: I18n.t("validations.lettings.household.referral.prevten_invalid", prevten: label) end end @@ -177,14 +178,14 @@ module Validations::HouseholdValidations return unless record.owning_organisation if record.is_internal_transfer? && record.owning_organisation.provider_type == "PRP" && record.is_prevten_la_general_needs? - record.errors.add :prevten, :internal_transfer_fixed_or_lifetime, message: I18n.t("validations.household.prevten.la_general_needs.internal_transfer") - record.errors.add :referral, :internal_transfer_fixed_or_lifetime, message: I18n.t("validations.household.referral.la_general_needs.internal_transfer") + record.errors.add :prevten, :internal_transfer_fixed_or_lifetime, message: I18n.t("validations.lettings.household.prevten.la_general_needs.internal_transfer") + record.errors.add :referral, :internal_transfer_fixed_or_lifetime, message: I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer") end end def validate_prevloc(record) if record.previous_la_known? && record.prevloc.blank? - record.errors.add :prevloc, I18n.t("validations.household.previous_la_known") + record.errors.add :prevloc, I18n.t("validations.lettings.household.prevloc.previous_la_known") end end @@ -192,8 +193,8 @@ module Validations::HouseholdValidations return unless record.layear && record.renewal if record.is_renewal? && record.layear == 1 - record.errors.add :layear, :renewal_just_moved, message: I18n.t("validations.household.renewal_just_moved_to_area.layear") - record.errors.add :renewal, I18n.t("validations.household.renewal_just_moved_to_area.renewal") + record.errors.add :layear, :renewal_just_moved, message: I18n.t("validations.lettings.household.layear.renewal_just_moved_to_area") + record.errors.add :renewal, I18n.t("validations.lettings.household.renewal.renewal_just_moved_to_area") end end @@ -201,20 +202,20 @@ module Validations::HouseholdValidations return unless record.layear && record.la && record.prevloc && record.collection_start_year if record.la == record.prevloc && record.layear == 1 && record.collection_start_year >= 2023 - record.errors.add :layear, :renewal_just_moved, message: I18n.t("validations.household.same_la_just_moved_to_area.layear") - record.errors.add :la, :renewal_just_moved, message: I18n.t("validations.household.same_la_just_moved_to_area.current_la") - record.errors.add :postcode_full, :renewal_just_moved, message: I18n.t("validations.household.same_la_just_moved_to_area.current_la") - record.errors.add :uprn, :renewal_just_moved, message: I18n.t("validations.household.same_la_just_moved_to_area.current_la") - record.errors.add :ppostcode_full, :renewal_just_moved, message: I18n.t("validations.household.same_la_just_moved_to_area.previous_la") - record.errors.add :prevloc, :renewal_just_moved, message: I18n.t("validations.household.same_la_just_moved_to_area.previous_la") + record.errors.add :layear, :renewal_just_moved, message: I18n.t("validations.lettings.household.layear.same_la_just_moved_to_area") + record.errors.add :la, :renewal_just_moved, message: I18n.t("validations.lettings.household.la.same_la_just_moved_to_area") + record.errors.add :postcode_full, :renewal_just_moved, message: I18n.t("validations.lettings.household.postcode_full.same_la_just_moved_to_area") + record.errors.add :uprn, :renewal_just_moved, message: I18n.t("validations.lettings.household.uprn.same_la_just_moved_to_area") + record.errors.add :ppostcode_full, :renewal_just_moved, message: I18n.t("validations.lettings.household.ppostcode_full.same_la_just_moved_to_area") + record.errors.add :prevloc, :renewal_just_moved, message: I18n.t("validations.lettings.household.prevloc.same_la_just_moved_to_area") end end def validate_combination_of_housing_needs_responses(record) if record.has_housingneeds? && record.housingneeds_type_not_listed? && record.no_or_unknown_other_housing_needs? - record.errors.add :housingneeds, I18n.t("validations.household.housingneeds.invalid") - record.errors.add :housingneeds_type, I18n.t("validations.household.housingneeds.invalid") - record.errors.add :housingneeds_other, I18n.t("validations.household.housingneeds.invalid") + record.errors.add :housingneeds, I18n.t("validations.lettings.household.housingneeds.invalid") + record.errors.add :housingneeds_type, I18n.t("validations.lettings.household.housingneeds_type.invalid") + record.errors.add :housingneeds_other, I18n.t("validations.lettings.household.housingneeds_other.invalid") end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 1c8f49dfb..45f0d2317 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -294,74 +294,35 @@ en: shared_ownership_deposit: "The %{mortgage_deposit_and_discount_error_fields} added together is %{mortgage_deposit_and_discount_total}. The value times the equity percentage is %{value_times_equity}. These figures should be the same." household: - reasonable_preference_reason: - reason_required: "Enter a reason if you've answered 'yes' to reasonable preference." - reason_not_required: "Do not enter a reason if you've answered 'no' to reasonable preference." - underoccupation_benefitcap: - dont_know_required: "Answer must be ‘don’t know’ as you told us you don’t know the tenant’s main reason for leaving." reservist: injury_required: "Tell us whether the person was seriously injured or ill as a result of serving in the UK armed forces." - injury_not_required: "You cannot answer this question as you told us the person has not served in the UK armed forces or prefers not to say." leftreg: question_required: "Tell us whether the person is still serving in the UK armed forces as you told us they’re a current or former regular." - question_not_required: "You cannot answer whether the person is still serving in the UK armed forces as you told us they’re not a current or former regular." age: retired_male: "A male tenant who is retired must be 65 or over." retired_female: "A female tenant who is retired must be 60 or over." retired_over_70: "Answer cannot be over 70 as person %{person_num} has economic status that is not ‘retired’." - child_under_16_relat_lettings: "Answer cannot be under 16 as person %{person_num}'s relationship to the lead tenant is ‘partner’." - child_under_16_ecstat: "Answer cannot be under 16 as person %{person_num}’s working situation is not ‘child under 16’, ‘other’ or ‘prefers not to say’." - child_over_16: "Answer cannot be over 16 as person’s %{person_num} working situation is ‘child under 16‘." not_student_16_19: "Answer cannot be between 16 and 19 as person %{person_num} is a child of the lead tenant but is not a full-time student." - student_16_19: - cannot_be_16_19: - child_not_student: "Person cannot be aged 16-19 if they have relationship ‘child’ but are not a student." - must_be_16_19: "Person must be aged 16-19 if they are a student and have relationship ‘child’." - lead: - over_25: "The lead tenant must be under 26 as you told us their housing situation immediately before this letting was a children’s home or foster care." student_not_child: cannot_be_16_19: "Person cannot be aged 16-19 if they are a student but not a child." ecstat: retired_over_70: "Person %{person_num} must be retired if over 70." - child_under_16: "Person %{person_num}’s working situation must be ‘child under 16’, ‘other’ or ‘prefers not to say’ as you told us they’re under 16." - child_over_16: "Answer cannot be ‘child under 16’ as you told us the person %{person_num} is older than 16." not_student_16_19: "Person’s %{person_num} working situation must be full-time student or prefers not to say as you told us they’re between 16 and 19." - student_16_19: - cannot_be_student: - child_not_16_19: "Person cannot be a student if they are not aged 16-19 but have relationship ‘child’." - must_be_student: "Person must be a student if they are aged 16-19 and have relationship ‘child’." retired_male: "Answer cannot be ‘retired’ as the male tenant is under 65." retired_female: "Answer cannot be ‘retired’ as the female tenant is under 60." not_child_16_19: cannot_be_student: "Person cannot be a student if they are aged 16-19 but are not a child." relat: - child_under_16_lettings: "Answer cannot be ‘partner’ as you told us person %{person_num}'s age is under 16." not_student_16_19: "Answer cannot be ‘child’ as you told us the person %{person_num} is between 16 and 19 and is not a full-time student." - student_16_19: - cannot_be_child: - student_not_16_19: "Answer cannot be ‘child’ if the person is a student but not aged 16-19." - 16_19_not_student: "Answer cannot be ‘child’ if the person is aged 16-19 but not a student." child_over_19: "Answer cannot be child as you told us person %{person_num} is over 19." housingneeds_a: one_or_two_choices: "You can only select one option or ‘other disabled access needs’ plus ‘wheelchair-accessible housing’, ‘wheelchair access to essential rooms’ or ‘level access housing’." - housingneeds: - invalid: "If somebody in the household has disabled access needs, they must have the access needs listed, or other access needs." - prevten: - non_temp_accommodation: "Answer cannot be non-temporary accommodation as this is a re-let to a tenant who occupied the same property as temporary accommodation." - over_25_foster_care: "Answer cannot be a children’s home or foster care as the lead tenant is 26 or older." - internal_transfer: "Answer cannot be %{prevten} as this tenancy is an internal transfer." - la_general_needs: - internal_transfer: "Answer cannot be a fixed-term or lifetime local authority general needs tenancy as it’s an internal transfer and a private registered provider is on the tenancy agreement." referral: secure_tenancy: "Answer must be internal transfer as this is a secure tenancy." rsnvac_non_temp: "Answer cannot be this source of referral as this is a re-let to tenant who occupied the same property as temporary accommodation." cannot_be_secure_tenancy: "Answer cannot be secure tenancy as this is not an internal transfer." assessed_homeless: "Answer cannot be internal transfer as the tenant was assessed as homeless." other_homeless: "Answer cannot be internal transfer as the tenant was considered homeless by their landlord." - prevten_invalid: "Answer cannot be internal transfer as the household situation immediately before this letting was %{prevten}." - reason_permanently_decanted: "Answer must be internal transfer as the tenant was permanently decanted from another property owned by this landlord." - la_general_needs: - internal_transfer: "Answer cannot be internal transfer as it’s the same landlord on the tenancy agreement and the household had either a fixed-term or lifetime local authority general needs tenancy immediately before this letting." homeless: assessed: internal_transfer: "Answer cannot be 'assessed as homeless' as this tenancy is an internal transfer." @@ -369,20 +330,9 @@ en: internal_transfer: "Answer cannot be 'other homelessness' as this tenancy was an internal transfer." reasonpref: not_homeless: "Answer cannot be ‘no’ as the tenant was homeless or about to lose their home." - previous_la_known: "Enter name of local authority." - renewal_just_moved_to_area: - layear: 'The household cannot have just moved to the local authority area if this letting is a renewal' - renewal: 'This letting cannot be a renewal if the household has just moved to the local authority area' - same_la_just_moved_to_area: - layear: 'You told us this tenant previously lived in this local authority. Check your answers are correct.' - current_la: 'You told us the tenant had just moved into the local authority, but this location is in the same local authority. Check your answers are correct' - previous_la: 'The local authority of the previous property should not be the same as the current local authority, as you told us they had just moved to the local authority area. Check your answers are correct.' gender: retired_male: "Answer cannot be ‘male’ as tenant is under 65 and retired." retired_female: "Answer cannot be ‘female’ as tenant is under 60 and retired." - reason: - not_internal_transfer: "Answer cannot be ‘permanently decanted from another property owned by this landlord’ as you told us the source of referral for this tenancy was not an internal transfer." - other_not_settled: "Please give the reason for the tenant leaving their last settled home. This is where they were living before they became homeless, were living in temporary accommodation or sleeping rough." condition_effects: no_choices: "You cannot answer this question as you told us nobody in the household has a physical or mental health condition (or other illness) expected to last 12 months or more." postcode: diff --git a/config/locales/validations/lettings/household.en.yml b/config/locales/validations/lettings/household.en.yml index b9573f4d2..6c38b5d24 100644 --- a/config/locales/validations/lettings/household.en.yml +++ b/config/locales/validations/lettings/household.en.yml @@ -2,6 +2,99 @@ en: validations: lettings: household: + reasonable_preference_reason: + reason_not_required: "Do not enter a reason if you've answered 'no' to reasonable preference." + + underoccupation_benefitcap: + leaving_last_settled_home: + dont_know_required: "Answer must be ‘don’t know’ as you told us you don’t know the tenant’s main reason for leaving." + + reservist: + injury_not_required: "You cannot answer this question as you told us the person has not served in the UK armed forces or prefers not to say." + + leftreg: + question_required: "Tell us whether the person is still serving in the UK armed forces as you told us they’re a current or former regular." + question_not_required: "You cannot answer whether the person is still serving in the UK armed forces as you told us they’re not a current or former regular." + + housingneeds: + invalid: "If somebody in the household has disabled access needs, they must have the access needs listed, or other access needs." + + housingneeds_other: + invalid: "If somebody in the household has disabled access needs, they must have the access needs listed, or other access needs." + + housingneeds_type: + invalid: "If somebody in the household has disabled access needs, they must have the access needs listed, or other access needs." + + reason: + leaving_last_settled_home: + dont_know_required: "Answer must be ‘don’t know’ as you told us you don’t know the tenant’s main reason for leaving." + not_internal_transfer: "Answer cannot be ‘permanently decanted from another property owned by this landlord’ as you told us the source of referral for this tenancy was not an internal transfer." + other_not_settled: "Please give the reason for the tenant leaving their last settled home. This is where they were living before they became homeless, were living in temporary accommodation or sleeping rough." + + condition_effects: + no_choices: "You cannot answer this question as you told us nobody in the household has a physical or mental health condition (or other illness) expected to last 12 months or more." + + prevloc: + previous_la_known: "Enter name of local authority." + same_la_just_moved_to_area: "The local authority of the previous property should not be the same as the current local authority, as you told us they had just moved to the local authority area. Check your answers are correct." + + renewal: + renewal_just_moved_to_area: "This letting cannot be a renewal if the household has just moved to the local authority area" + + layear: + renewal_just_moved_to_area: "The household cannot have just moved to the local authority area if this letting is a renewal" + same_la_just_moved_to_area: "You told us this tenant previously lived in this local authority. Check your answers are correct." + + la: + same_la_just_moved_to_area: "You told us the tenant had just moved into the local authority, but this location is in the same local authority. Check your answers are correct" + + postcode_full: + same_la_just_moved_to_area: "You told us the tenant had just moved into the local authority, but this location is in the same local authority. Check your answers are correct" + + ppostcode_full: + same_la_just_moved_to_area: "The local authority of the previous property should not be the same as the current local authority, as you told us they had just moved to the local authority area. Check your answers are correct." + + uprn: + same_la_just_moved_to_area: "You told us the tenant had just moved into the local authority, but this location is in the same local authority. Check your answers are correct" + + age: + child_under_16_relat: "Answer cannot be under 16 as person %{person_num}'s relationship to the lead tenant is ‘partner’." + child_under_16_ecstat: "Answer cannot be under 16 as person %{person_num}’s working situation is not ‘child under 16’, ‘other’ or ‘prefers not to say’." + child_over_16: "Answer cannot be over 16 as person’s %{person_num} working situation is ‘child under 16‘." + student_16_19: + cannot_be_16_19: + child_not_student: "Person cannot be aged 16-19 if they have relationship ‘child’ but are not a student." + must_be_16_19: "Person must be aged 16-19 if they are a student and have relationship ‘child’." + lead: + over_25: "The lead tenant must be under 26 as you told us their housing situation immediately before this letting was a children’s home or foster care." + + ecstat: + child_under_16: "Person %{person_num}’s working situation must be ‘child under 16’, ‘other’ or ‘prefers not to say’ as you told us they’re under 16." + child_over_16: "Answer cannot be ‘child under 16’ as you told us the person %{person_num} is older than 16." + student_16_19: + cannot_be_student: + child_not_16_19: "Person cannot be a student if they are not aged 16-19 but have relationship ‘child’." + must_be_student: "Person must be a student if they are aged 16-19 and have relationship ‘child’." + relat: one_partner: "Number of partners cannot be greater than 1." + child_under_16: "Answer cannot be ‘partner’ as you told us person %{person_num}'s age is under 16." + student_16_19: + cannot_be_child: + student_not_16_19: "Answer cannot be ‘child’ if the person is a student but not aged 16-19." + 16_19_not_student: "Answer cannot be ‘child’ if the person is aged 16-19 but not a student." + + prevten: + non_temp_accommodation: "Answer cannot be non-temporary accommodation as this is a re-let to a tenant who occupied the same property as temporary accommodation." + over_25_foster_care: "Answer cannot be a children’s home or foster care as the lead tenant is 26 or older." + internal_transfer: "Answer cannot be %{prevten} as this tenancy is an internal transfer." + la_general_needs: + internal_transfer: "Answer cannot be a fixed-term or lifetime local authority general needs tenancy as it’s an internal transfer and a private registered provider is on the tenancy agreement." + + referral: + prevten_invalid: "Answer cannot be internal transfer as the household situation immediately before this letting was %{prevten}." + leaving_last_settled_home: + reason_permanently_decanted: "Answer must be internal transfer as the tenant was permanently decanted from another property owned by this landlord." + la_general_needs: + internal_transfer: "Answer cannot be internal transfer as it’s the same landlord on the tenancy agreement and the household had either a fixed-term or lifetime local authority general needs tenancy immediately before this letting." diff --git a/spec/models/validations/household_validations_spec.rb b/spec/models/validations/household_validations_spec.rb index ea472f618..0893c89cf 100644 --- a/spec/models/validations/household_validations_spec.rb +++ b/spec/models/validations/household_validations_spec.rb @@ -21,7 +21,7 @@ RSpec.describe Validations::HouseholdValidations do record.rp_medwel = 1 household_validator.validate_reasonable_preference(record) expect(record.errors["reasonable_preference_reason"]) - .to include(match I18n.t("validations.household.reasonable_preference_reason.reason_not_required")) + .to include(match I18n.t("validations.lettings.household.reasonable_preference_reason.reason_not_required")) end end end @@ -69,7 +69,7 @@ RSpec.describe Validations::HouseholdValidations do record.reason = 20 record.reasonother = "Temp accommodation" household_validator.validate_reason_for_leaving_last_settled_home(record) - expect(record.errors["reason"]).to include(I18n.t("validations.household.reason.other_not_settled")) + expect(record.errors["reason"]).to include(I18n.t("validations.lettings.household.reason.leaving_last_settled_home.other_not_settled")) end it "allows reasons that don't exactly match a phrase indicating homelessness" do @@ -83,7 +83,7 @@ RSpec.describe Validations::HouseholdValidations do record.reason = 20 record.reasonother = " 0homelessness ! " household_validator.validate_reason_for_leaving_last_settled_home(record) - expect(record.errors["reason"]).to include(I18n.t("validations.household.reason.other_not_settled")) + expect(record.errors["reason"]).to include(I18n.t("validations.lettings.household.reason.leaving_last_settled_home.other_not_settled")) end end end @@ -107,16 +107,14 @@ RSpec.describe Validations::HouseholdValidations do end context "when reason is don't know" do - let(:expected_error) { I18n.t("validations.household.underoccupation_benefitcap.dont_know_required") } - it "validates that under occupation benefit cap is also not known" do record.reason = 32 record.underoccupation_benefitcap = 1 household_validator.validate_reason_for_leaving_last_settled_home(record) expect(record.errors["underoccupation_benefitcap"]) - .to include(match(expected_error)) + .to include(match(I18n.t("validations.lettings.household.underoccupation_benefitcap.leaving_last_settled_home.dont_know_required"))) expect(record.errors["reason"]) - .to include(match(expected_error)) + .to include(match(I18n.t("validations.lettings.household.reason.leaving_last_settled_home.dont_know_required"))) end it "expects that under occupation benefit cap is also not known" do @@ -134,9 +132,9 @@ RSpec.describe Validations::HouseholdValidations do record.referral = 2 household_validator.validate_reason_for_leaving_last_settled_home(record) expect(record.errors["reason"]) - .to include(match(I18n.t("validations.household.reason.not_internal_transfer"))) + .to include(match(I18n.t("validations.lettings.household.reason.leaving_last_settled_home.not_internal_transfer"))) expect(record.errors["referral"]) - .to include(match(I18n.t("validations.household.referral.reason_permanently_decanted"))) + .to include(match(I18n.t("validations.lettings.household.referral.leaving_last_settled_home.reason_permanently_decanted"))) end end @@ -157,16 +155,16 @@ RSpec.describe Validations::HouseholdValidations do record.referral = 1 household_validator.validate_referral(record) expect(record.errors["referral"]) - .to include(match(I18n.t("validations.household.referral.la_general_needs.internal_transfer"))) + .to include(match(I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer"))) expect(record.errors["prevten"]) - .to include(match(I18n.t("validations.household.prevten.la_general_needs.internal_transfer"))) + .to include(match(I18n.t("validations.lettings.household.prevten.la_general_needs.internal_transfer"))) record.prevten = 31 household_validator.validate_referral(record) expect(record.errors["referral"]) - .to include(match(I18n.t("validations.household.referral.la_general_needs.internal_transfer"))) + .to include(match(I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer"))) expect(record.errors["prevten"]) - .to include(match(I18n.t("validations.household.prevten.la_general_needs.internal_transfer"))) + .to include(match(I18n.t("validations.lettings.household.prevten.la_general_needs.internal_transfer"))) end end end @@ -178,7 +176,7 @@ RSpec.describe Validations::HouseholdValidations do record.reservist = 1 household_validator.validate_armed_forces(record) expect(record.errors["reservist"]) - .to include(match I18n.t("validations.household.reservist.injury_not_required")) + .to include(match I18n.t("validations.lettings.household.reservist.injury_not_required")) end end @@ -188,7 +186,7 @@ RSpec.describe Validations::HouseholdValidations do record.reservist = 1 household_validator.validate_armed_forces(record) expect(record.errors["reservist"]) - .to include(match I18n.t("validations.household.reservist.injury_not_required")) + .to include(match I18n.t("validations.lettings.household.reservist.injury_not_required")) end end @@ -225,7 +223,7 @@ RSpec.describe Validations::HouseholdValidations do record.leftreg = 0 household_validator.validate_armed_forces(record) expect(record.errors["leftreg"]) - .to include(match I18n.t("validations.household.leftreg.question_not_required")) + .to include(match I18n.t("validations.lettings.household.leftreg.question_not_required")) end it "expects that they served in the armed forces" do @@ -300,9 +298,9 @@ RSpec.describe Validations::HouseholdValidations do record.relat2 = "P" household_validator.validate_person_age_matches_relationship(record) expect(record.errors["relat2"]) - .to include(match I18n.t("validations.household.relat.child_under_16_lettings", person_num: 2)) + .to include(match I18n.t("validations.lettings.household.relat.child_under_16", person_num: 2)) expect(record.errors["age2"]) - .to include(match I18n.t("validations.household.age.child_under_16_relat_lettings", person_num: 2)) + .to include(match I18n.t("validations.lettings.household.age.child_under_16_relat", person_num: 2)) end it "expects that person is a child of the tenant" do @@ -346,9 +344,9 @@ RSpec.describe Validations::HouseholdValidations do record.ecstat2 = 1 household_validator.validate_person_age_matches_economic_status(record) expect(record.errors["ecstat2"]) - .to include(match I18n.t("validations.household.ecstat.child_under_16", person_num: 2)) + .to include(match I18n.t("validations.lettings.household.ecstat.child_under_16", person_num: 2)) expect(record.errors["age2"]) - .to include(match I18n.t("validations.household.age.child_under_16_ecstat", person_num: 2)) + .to include(match I18n.t("validations.lettings.household.age.child_under_16_ecstat", person_num: 2)) end it "expects that person's economic status is Child" do @@ -365,9 +363,9 @@ RSpec.describe Validations::HouseholdValidations do record.ecstat2 = 9 household_validator.validate_person_age_matches_economic_status(record) expect(record.errors["ecstat2"]) - .to include(match I18n.t("validations.household.ecstat.child_over_16", person_num: 2)) + .to include(match I18n.t("validations.lettings.household.ecstat.child_over_16", person_num: 2)) expect(record.errors["age2"]) - .to include(match I18n.t("validations.household.age.child_over_16", person_num: 2)) + .to include(match I18n.t("validations.lettings.household.age.child_over_16", person_num: 2)) end end end @@ -380,9 +378,9 @@ RSpec.describe Validations::HouseholdValidations do record.ecstat2 = 1 household_validator.validate_person_age_matches_economic_status(record) expect(record.errors["ecstat2"]) - .not_to include(match I18n.t("validations.household.ecstat.child_under_16", person_num: 2)) + .not_to include(match I18n.t("validations.lettings.household.ecstat.child_under_16", person_num: 2)) expect(record.errors["age2"]) - .not_to include(match I18n.t("validations.household.age.child_under_16_ecstat", person_num: 2)) + .not_to include(match I18n.t("validations.lettings.household.age.child_under_16_ecstat", person_num: 2)) end end end @@ -398,11 +396,11 @@ RSpec.describe Validations::HouseholdValidations do record.ecstat2 = 1 household_validator.validate_person_age_and_relationship_matches_economic_status(record) expect(record.errors["ecstat2"]) - .to include(match I18n.t("validations.household.ecstat.student_16_19.must_be_student", person_num: 2)) + .to include(match I18n.t("validations.lettings.household.ecstat.student_16_19.must_be_student", person_num: 2)) expect(record.errors["age2"]) - .to include(match I18n.t("validations.household.age.student_16_19.cannot_be_16_19.child_not_student", person_num: 2)) + .to include(match I18n.t("validations.lettings.household.age.student_16_19.cannot_be_16_19.child_not_student", person_num: 2)) expect(record.errors["relat2"]) - .to include(match I18n.t("validations.household.relat.student_16_19.cannot_be_child.16_19_not_student", person_num: 2)) + .to include(match I18n.t("validations.lettings.household.relat.student_16_19.cannot_be_child.16_19_not_student", person_num: 2)) end it "expects that person can be a full time student" do @@ -452,11 +450,11 @@ RSpec.describe Validations::HouseholdValidations do record.relat2 = "C" household_validator.validate_person_age_and_relationship_matches_economic_status(record) expect(record.errors["relat2"]) - .to include(match I18n.t("validations.household.relat.student_16_19.cannot_be_child.student_not_16_19")) + .to include(match I18n.t("validations.lettings.household.relat.student_16_19.cannot_be_child.student_not_16_19")) expect(record.errors["age2"]) - .to include(match I18n.t("validations.household.age.student_16_19.must_be_16_19")) + .to include(match I18n.t("validations.lettings.household.age.student_16_19.must_be_16_19")) expect(record.errors["ecstat2"]) - .to include(match I18n.t("validations.household.ecstat.student_16_19.cannot_be_student.child_not_16_19")) + .to include(match I18n.t("validations.lettings.household.ecstat.student_16_19.cannot_be_student.child_not_16_19")) end end @@ -516,7 +514,7 @@ RSpec.describe Validations::HouseholdValidations do record.illness_type_1 = 1 household_validator.validate_condition_effects(record) expect(record.errors["condition_effects"]) - .to include(match I18n.t("validations.household.condition_effects.no_choices")) + .to include(match I18n.t("validations.lettings.household.condition_effects.no_choices")) end it "validates hearing can't be selected if answer to anyone in household with health condition is not yes" do @@ -524,7 +522,7 @@ RSpec.describe Validations::HouseholdValidations do record.illness_type_2 = 1 household_validator.validate_condition_effects(record) expect(record.errors["condition_effects"]) - .to include(match I18n.t("validations.household.condition_effects.no_choices")) + .to include(match I18n.t("validations.lettings.household.condition_effects.no_choices")) end it "validates mobility can't be selected if answer to anyone in household with health condition is not yes" do @@ -532,7 +530,7 @@ RSpec.describe Validations::HouseholdValidations do record.illness_type_3 = 1 household_validator.validate_condition_effects(record) expect(record.errors["condition_effects"]) - .to include(match I18n.t("validations.household.condition_effects.no_choices")) + .to include(match I18n.t("validations.lettings.household.condition_effects.no_choices")) end it "validates dexterity can't be selected if answer to anyone in household with health condition is not yes" do @@ -540,7 +538,7 @@ RSpec.describe Validations::HouseholdValidations do record.illness_type_4 = 1 household_validator.validate_condition_effects(record) expect(record.errors["condition_effects"]) - .to include(match I18n.t("validations.household.condition_effects.no_choices")) + .to include(match I18n.t("validations.lettings.household.condition_effects.no_choices")) end it "validates learning or understanding or concentrating can't be selected if answer to anyone in household with health condition is not yes" do @@ -548,7 +546,7 @@ RSpec.describe Validations::HouseholdValidations do record.illness_type_5 = 1 household_validator.validate_condition_effects(record) expect(record.errors["condition_effects"]) - .to include(match I18n.t("validations.household.condition_effects.no_choices")) + .to include(match I18n.t("validations.lettings.household.condition_effects.no_choices")) end it "validates memory can't be selected if answer to anyone in household with health condition is not yes" do @@ -556,7 +554,7 @@ RSpec.describe Validations::HouseholdValidations do record.illness_type_6 = 1 household_validator.validate_condition_effects(record) expect(record.errors["condition_effects"]) - .to include(match I18n.t("validations.household.condition_effects.no_choices")) + .to include(match I18n.t("validations.lettings.household.condition_effects.no_choices")) end it "validates mental health can't be selected if answer to anyone in household with health condition is not yes" do @@ -564,7 +562,7 @@ RSpec.describe Validations::HouseholdValidations do record.illness_type_7 = 1 household_validator.validate_condition_effects(record) expect(record.errors["condition_effects"]) - .to include(match I18n.t("validations.household.condition_effects.no_choices")) + .to include(match I18n.t("validations.lettings.household.condition_effects.no_choices")) end it "validates stamina or breathing or fatigue can't be selected if answer to anyone in household with health condition is not yes" do @@ -572,7 +570,7 @@ RSpec.describe Validations::HouseholdValidations do record.illness_type_8 = 1 household_validator.validate_condition_effects(record) expect(record.errors["condition_effects"]) - .to include(match I18n.t("validations.household.condition_effects.no_choices")) + .to include(match I18n.t("validations.lettings.household.condition_effects.no_choices")) end it "validates socially or behaviourally can't be selected if answer to anyone in household with health condition is not yes" do @@ -580,7 +578,7 @@ RSpec.describe Validations::HouseholdValidations do record.illness_type_9 = 1 household_validator.validate_condition_effects(record) expect(record.errors["condition_effects"]) - .to include(match I18n.t("validations.household.condition_effects.no_choices")) + .to include(match I18n.t("validations.lettings.household.condition_effects.no_choices")) end it "validates other can't be selected if answer to anyone in household with health condition is not yes" do @@ -588,7 +586,7 @@ RSpec.describe Validations::HouseholdValidations do record.illness_type_10 = 1 household_validator.validate_condition_effects(record) expect(record.errors["condition_effects"]) - .to include(match I18n.t("validations.household.condition_effects.no_choices")) + .to include(match I18n.t("validations.lettings.household.condition_effects.no_choices")) end it "expects that an illness can be selected if answer to anyone in household with health condition is yes " do @@ -647,7 +645,7 @@ RSpec.describe Validations::HouseholdValidations do record.previous_la_known = 1 household_validator.validate_prevloc(record) expect(record.errors["prevloc"]) - .to include(match I18n.t("validations.household.previous_la_known")) + .to include(match I18n.t("validations.lettings.household.prevloc.previous_la_known")) end end @@ -657,9 +655,9 @@ RSpec.describe Validations::HouseholdValidations do record.renewal = 1 household_validator.validate_layear(record) expect(record.errors["layear"]) - .to include(match I18n.t("validations.household.renewal_just_moved_to_area.layear")) + .to include(match I18n.t("validations.lettings.household.layear.renewal_just_moved_to_area")) expect(record.errors["renewal"]) - .to include(match I18n.t("validations.household.renewal_just_moved_to_area.renewal")) + .to include(match I18n.t("validations.lettings.household.renewal.renewal_just_moved_to_area")) end context "when validating layear and prevloc" do @@ -670,17 +668,17 @@ RSpec.describe Validations::HouseholdValidations do record.startdate = Time.zone.now household_validator.validate_layear_and_prevloc(record) expect(record.errors["layear"]) - .to include(match I18n.t("validations.household.same_la_just_moved_to_area.layear")) + .to include(match I18n.t("validations.lettings.household.layear.same_la_just_moved_to_area")) expect(record.errors["prevloc"]) - .to include(match I18n.t("validations.household.same_la_just_moved_to_area.previous_la")) + .to include(match I18n.t("validations.lettings.household.prevloc.same_la_just_moved_to_area")) expect(record.errors["ppostcode_full"]) - .to include(match I18n.t("validations.household.same_la_just_moved_to_area.previous_la")) + .to include(match I18n.t("validations.lettings.household.ppostcode_full.same_la_just_moved_to_area")) expect(record.errors["la"]) - .to include(match I18n.t("validations.household.same_la_just_moved_to_area.current_la")) + .to include(match I18n.t("validations.lettings.household.la.same_la_just_moved_to_area")) expect(record.errors["postcode_full"]) - .to include(match I18n.t("validations.household.same_la_just_moved_to_area.current_la")) + .to include(match I18n.t("validations.lettings.household.postcode_full.same_la_just_moved_to_area")) expect(record.errors["uprn"]) - .to include(match I18n.t("validations.household.same_la_just_moved_to_area.current_la")) + .to include(match I18n.t("validations.lettings.household.uprn.same_la_just_moved_to_area")) end end end @@ -693,7 +691,7 @@ RSpec.describe Validations::HouseholdValidations do record.prevten = 4 household_validator.validate_previous_housing_situation(record) expect(record.errors["prevten"]) - .to include(match I18n.t("validations.household.prevten.non_temp_accommodation")) + .to include(match I18n.t("validations.lettings.household.prevten.non_temp_accommodation")) end end @@ -703,9 +701,9 @@ RSpec.describe Validations::HouseholdValidations do record.age1 = 26 household_validator.validate_previous_housing_situation(record) expect(record.errors["prevten"]) - .to include(match I18n.t("validations.household.prevten.over_25_foster_care")) + .to include(match I18n.t("validations.lettings.household.prevten.over_25_foster_care")) expect(record.errors["age1"]) - .to include(match I18n.t("validations.household.age.lead.over_25")) + .to include(match I18n.t("validations.lettings.household.age.lead.over_25")) end end @@ -740,9 +738,9 @@ RSpec.describe Validations::HouseholdValidations do record.prevten = prevten[:code] household_validator.validate_previous_housing_situation(record) expect(record.errors["prevten"]) - .to include(match I18n.t("validations.household.prevten.internal_transfer", prevten: prevten[:label])) + .to include(match I18n.t("validations.lettings.household.prevten.internal_transfer", prevten: prevten[:label])) expect(record.errors["referral"]) - .to include(match I18n.t("validations.household.referral.prevten_invalid", prevten: "")) + .to include(match I18n.t("validations.lettings.household.referral.prevten_invalid", prevten: "")) end end end From 0083485b4278dea30e66a052a9f92685ed945f74 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:10:27 +0000 Subject: [PATCH 02/34] CLDC-3690 Extract lettings property validations copy (#2742) * Extract lettings property validations copy * Update uprn question * Remove redundant validations (can't trigger them because on inferrance) --- app/models/form/lettings/questions/uprn.rb | 2 +- .../validations/property_validations.rb | 39 ++---- config/locales/en.yml | 14 --- .../lettings/property_information.en.yml | 17 ++- .../form/lettings/questions/uprn_spec.rb | 2 +- spec/models/lettings_log_spec.rb | 12 +- .../validations/property_validations_spec.rb | 112 ++---------------- 7 files changed, 41 insertions(+), 157 deletions(-) diff --git a/app/models/form/lettings/questions/uprn.rb b/app/models/form/lettings/questions/uprn.rb index 53df6bcf2..e6741d25b 100644 --- a/app/models/form/lettings/questions/uprn.rb +++ b/app/models/form/lettings/questions/uprn.rb @@ -15,7 +15,7 @@ class Form::Lettings::Questions::Uprn < ::Form::Question end def unanswered_error_message - I18n.t("validations.property.uprn.invalid") + I18n.t("validations.lettings.property.uprn.invalid") end def get_extra_check_answer_value(log) diff --git a/app/models/validations/property_validations.rb b/app/models/validations/property_validations.rb index 1864adc5c..09e6d1436 100644 --- a/app/models/validations/property_validations.rb +++ b/app/models/validations/property_validations.rb @@ -4,49 +4,30 @@ module Validations::PropertyValidations REFERRAL_INVALID_TMP = [8, 10, 12, 13, 14, 15].freeze def validate_rsnvac(record) - if !record.first_time_property_let_as_social_housing? && record.has_first_let_vacancy_reason? - record.errors.add :rsnvac, I18n.t("validations.property.rsnvac.first_let_not_social") - end - - if record.first_time_property_let_as_social_housing? && record.rsnvac.present? && !record.has_first_let_vacancy_reason? - record.errors.add :rsnvac, I18n.t("validations.property.rsnvac.first_let_social") - end - if record.is_relet_to_temp_tenant? && !record.previous_tenancy_was_temporary? - record.errors.add :rsnvac, I18n.t("validations.property.rsnvac.non_temp_accommodation") + record.errors.add :rsnvac, I18n.t("validations.lettings.property.rsnvac.non_temp_accommodation") end if record.is_relet_to_temp_tenant? && REFERRAL_INVALID_TMP.include?(record.referral) - record.errors.add :rsnvac, I18n.t("validations.property.rsnvac.referral_invalid") - record.errors.add :referral, :referral_invalid, message: I18n.t("validations.household.referral.rsnvac_non_temp") + record.errors.add :rsnvac, I18n.t("validations.lettings.property.rsnvac.referral_invalid") + record.errors.add :referral, :referral_invalid, message: I18n.t("validations.lettings.property.referral.rsnvac_non_temp") end if record.renewal.present? && record.renewal.zero? && record.rsnvac == 14 - record.errors.add :rsnvac, I18n.t("validations.property.rsnvac.not_a_renewal") - end - end - - def validate_unitletas(record) - if record.first_time_property_let_as_social_housing? && record.unitletas.present? - record.errors.add :unitletas, I18n.t("validations.property.rsnvac.previous_let_social") + record.errors.add :rsnvac, I18n.t("validations.lettings.property.rsnvac.not_a_renewal") end end def validate_shared_housing_rooms(record) return unless record.unittype_gn - if record.is_bedsit? && record.beds != 1 && record.beds.present? && !record.form.start_year_2024_or_later? - 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 - if record.hhmemb == 1 && record.is_shared_housing? && !record.beds.to_i.between?(1, 3) && record.beds.present? - record.errors.add :unittype_gn, I18n.t("validations.property.unittype_gn.one_three_bedroom_single_tenant_shared") - record.errors.add :beds, :one_three_bedroom_single_tenant_shared, message: I18n.t("validations.property.unittype_gn.one_three_bedroom_single_tenant_shared") + record.errors.add :unittype_gn, I18n.t("validations.lettings.property.unittype_gn.one_three_bedroom_single_tenant_shared") + record.errors.add :beds, :one_three_bedroom_single_tenant_shared, message: I18n.t("validations.lettings.property.beds.one_three_bedroom_single_tenant_shared") elsif record.is_shared_housing? && record.beds.present? && !record.beds.to_i.between?(1, 7) - record.errors.add :unittype_gn, I18n.t("validations.property.unittype_gn.one_seven_bedroom_shared") - record.errors.add :beds, I18n.t("validations.property.unittype_gn.one_seven_bedroom_shared") + record.errors.add :unittype_gn, I18n.t("validations.lettings.property.unittype_gn.one_seven_bedroom_shared") + record.errors.add :beds, I18n.t("validations.lettings.property.beds.one_seven_bedroom_shared") end end @@ -55,13 +36,13 @@ module Validations::PropertyValidations return if record.uprn.match?(/^[0-9]{1,12}$/) - record.errors.add :uprn, I18n.t("validations.property.uprn.invalid") + record.errors.add :uprn, I18n.t("validations.lettings.property.uprn.invalid") end def validate_property_postcode(record) postcode = record.postcode_full if record.postcode_known? && (postcode.blank? || !postcode.match(POSTCODE_REGEXP)) - error_message = I18n.t("validations.lettings.property_information.postcode_full.invalid") + error_message = I18n.t("validations.lettings.property.postcode_full.invalid") record.errors.add :postcode_full, :wrong_format, message: error_message end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 45f0d2317..f8bb8255b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -263,24 +263,11 @@ en: owning_organisation: data_sharing_agreement_not_signed: "The organisation must accept the Data Sharing Agreement before it can be selected as the owning organisation." property: - uprn: - invalid: "UPRN must be 12 digits or less." uprn_known: invalid: "You must answer UPRN known?" la: la_invalid_for_org: "%{org_name} does not operate in %{la_name}." postcode_invalid_for_org: "Enter a postcode in an area covered by %{org_name}." - rsnvac: - first_let_not_social: "Enter a reason for vacancy that is not 'first let' if unit has been previously let as social housing." - first_let_social: "Reason for vacancy must be first let if unit has been previously let as social housing." - previous_let_social: "Property cannot have a previous let type if being let as social housing for the first time." - non_temp_accommodation: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as this accommodation is not temporary." - referral_invalid: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as a different source of referral for this letting." - not_a_renewal: "Reason for vacancy cannot be 'Renewal of fixed-term tenancy' if letting is not a renewal." - unittype_gn: - one_bedroom_bedsit: "A bedsit can only have one bedroom." - one_seven_bedroom_shared: "A shared house must have 1 to 7 bedrooms." - one_three_bedroom_single_tenant_shared: "A shared house with fewer than two tenants must have 1 to 3 bedrooms." financial: charges: @@ -319,7 +306,6 @@ en: one_or_two_choices: "You can only select one option or ‘other disabled access needs’ plus ‘wheelchair-accessible housing’, ‘wheelchair access to essential rooms’ or ‘level access housing’." referral: secure_tenancy: "Answer must be internal transfer as this is a secure tenancy." - rsnvac_non_temp: "Answer cannot be this source of referral as this is a re-let to tenant who occupied the same property as temporary accommodation." cannot_be_secure_tenancy: "Answer cannot be secure tenancy as this is not an internal transfer." assessed_homeless: "Answer cannot be internal transfer as the tenant was assessed as homeless." other_homeless: "Answer cannot be internal transfer as the tenant was considered homeless by their landlord." diff --git a/config/locales/validations/lettings/property_information.en.yml b/config/locales/validations/lettings/property_information.en.yml index ed42acb4c..6530c9488 100644 --- a/config/locales/validations/lettings/property_information.en.yml +++ b/config/locales/validations/lettings/property_information.en.yml @@ -1,6 +1,21 @@ en: validations: lettings: - property_information: + property: postcode_full: invalid: "Enter a postcode in the correct format, for example AA1 1AA." + rsnvac: + non_temp_accommodation: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as this accommodation is not temporary." + referral_invalid: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as a different source of referral for this letting." + not_a_renewal: "Reason for vacancy cannot be 'Renewal of fixed-term tenancy' if letting is not a renewal." + referral: + rsnvac_non_temp: "Answer cannot be this source of referral as this is a re-let to tenant who occupied the same property as temporary accommodation." + unittype_gn: + one_three_bedroom_single_tenant_shared: "A shared house with fewer than two tenants must have 1 to 3 bedrooms." + one_seven_bedroom_shared: "A shared house must have 1 to 7 bedrooms." + beds: + one_three_bedroom_single_tenant_shared: "A shared house with fewer than two tenants must have 1 to 3 bedrooms." + one_seven_bedroom_shared: "A shared house must have 1 to 7 bedrooms." + uprn: + invalid: "UPRN must be 12 digits or less." + diff --git a/spec/models/form/lettings/questions/uprn_spec.rb b/spec/models/form/lettings/questions/uprn_spec.rb index 21f3e078b..8bb932b35 100644 --- a/spec/models/form/lettings/questions/uprn_spec.rb +++ b/spec/models/form/lettings/questions/uprn_spec.rb @@ -28,7 +28,7 @@ RSpec.describe Form::Lettings::Questions::Uprn, type: :model do end it "has the correct unanswered_error_message" do - expect(question.unanswered_error_message).to eq("UPRN must be 12 digits or less.") + expect(question.unanswered_error_message).to eq(I18n.t("validations.lettings.property.uprn.invalid")) end describe "get_extra_check_answer_value" do diff --git a/spec/models/lettings_log_spec.rb b/spec/models/lettings_log_spec.rb index e28f0f2c5..c47de8cf6 100644 --- a/spec/models/lettings_log_spec.rb +++ b/spec/models/lettings_log_spec.rb @@ -194,10 +194,6 @@ RSpec.describe LettingsLog do expect(validator).to receive(:validate_tshortfall) end - it "validates let type" do - expect(validator).to receive(:validate_unitletas) - end - it "validates reason for vacancy" do expect(validator).to receive(:validate_rsnvac) end @@ -1010,8 +1006,8 @@ RSpec.describe LettingsLog do end it "does not impact other validations" do - expect { lettings_log.update!(startdate: Time.zone.yesterday, first_time_property_let_as_social_housing: 0, rsnvac: 16) } - .to raise_error(ActiveRecord::RecordInvalid, /Enter a reason for vacancy that is not 'first let' if unit has been previously let as social housing/) + expect { lettings_log.update!(startdate: Time.zone.yesterday, referral: 8, rsnvac: 9) } + .to raise_error(ActiveRecord::RecordInvalid, /#{I18n.t("validations.lettings.property.rsnvac.referral_invalid")}/) end end @@ -1046,8 +1042,8 @@ RSpec.describe LettingsLog do end it "does not impact other validations" do - expect { lettings_log.update!(location:, scheme:, first_time_property_let_as_social_housing: 0, rsnvac: 16) } - .to raise_error(ActiveRecord::RecordInvalid, /Enter a reason for vacancy that is not 'first let' if unit has been previously let as social housing/) + expect { lettings_log.update!(startdate: Time.zone.yesterday, referral: 8, rsnvac: 9) } + .to raise_error(ActiveRecord::RecordInvalid, /#{I18n.t("validations.lettings.property.rsnvac.referral_invalid")}/) end end end diff --git a/spec/models/validations/property_validations_spec.rb b/spec/models/validations/property_validations_spec.rb index 8cf484f91..f478ae466 100644 --- a/spec/models/validations/property_validations_spec.rb +++ b/spec/models/validations/property_validations_spec.rb @@ -47,47 +47,10 @@ RSpec.describe Validations::PropertyValidations do expect(log.errors).to be_empty end end - - context "and the log is from before 24/25" do - it "adds an error" do - allow(log.form).to receive(:start_year_2024_or_later?).and_return false - - property_validator.validate_shared_housing_rooms(log) - - expect(log.errors["unittype_gn"]).to include(I18n.t("validations.property.unittype_gn.one_bedroom_bedsit")) - expect(log.errors["beds"]).to include(I18n.t("validations.property.unittype_gn.one_bedroom_bedsit")) - end - end - end - - context "when a bedsit has less than 1 bedroom" do - before do - log.beds = 0 - log.unittype_gn = 2 - end - - context "and the log is for 24/25 or later" do - it "does not add an error" do - property_validator.validate_shared_housing_rooms(log) - - expect(log.errors).to be_empty - end - end - - context "and the log is from before 24/25" do - it "adds an error" do - allow(log.form).to receive(:start_year_2024_or_later?).and_return false - - property_validator.validate_shared_housing_rooms(log) - - expect(log.errors["unittype_gn"]).to include(I18n.t("validations.property.unittype_gn.one_bedroom_bedsit")) - expect(log.errors["beds"]).to include(I18n.t("validations.property.unittype_gn.one_bedroom_bedsit")) - end - end end context "when shared housing has more than 7 bedrooms" do - let(:expected_error) { I18n.t("validations.property.unittype_gn.one_seven_bedroom_shared") } + let(:expected_error) { I18n.t("validations.lettings.property.unittype_gn.one_seven_bedroom_shared") } it "adds an error if the number of bedrooms is not between 1 and 7" do log.beds = 8 @@ -95,12 +58,12 @@ RSpec.describe Validations::PropertyValidations do log.hhmemb = 3 property_validator.validate_shared_housing_rooms(log) expect(log.errors["unittype_gn"]).to include(match(expected_error)) - expect(log.errors["beds"]).to include(I18n.t("validations.property.unittype_gn.one_seven_bedroom_shared")) + expect(log.errors["beds"]).to include(I18n.t("validations.lettings.property.unittype_gn.one_seven_bedroom_shared")) end end context "when shared housing has less than 1 bedrooms" do - let(:expected_error) { I18n.t("validations.property.unittype_gn.one_seven_bedroom_shared") } + let(:expected_error) { I18n.t("validations.lettings.property.unittype_gn.one_seven_bedroom_shared") } it "adds an error if the number of bedrooms is not between 1 and 7" do log.beds = 0 @@ -108,12 +71,12 @@ RSpec.describe Validations::PropertyValidations do log.hhmemb = 3 property_validator.validate_shared_housing_rooms(log) expect(log.errors["unittype_gn"]).to include(match(expected_error)) - expect(log.errors["beds"]).to include(I18n.t("validations.property.unittype_gn.one_seven_bedroom_shared")) + expect(log.errors["beds"]).to include(I18n.t("validations.lettings.property.unittype_gn.one_seven_bedroom_shared")) end end context "when there are too many bedrooms for the number of household members and unit type" do - let(:expected_error) { I18n.t("validations.property.unittype_gn.one_three_bedroom_single_tenant_shared") } + let(:expected_error) { I18n.t("validations.lettings.property.unittype_gn.one_three_bedroom_single_tenant_shared") } it "adds an error" do log.beds = 4 @@ -121,54 +84,13 @@ RSpec.describe Validations::PropertyValidations do log.hhmemb = 1 property_validator.validate_shared_housing_rooms(log) expect(log.errors["unittype_gn"]).to include(match(expected_error)) - expect(log.errors["beds"]).to include(I18n.t("validations.property.unittype_gn.one_three_bedroom_single_tenant_shared")) - end - end - end - - describe "#validate_unitletas" do - context "when the property has not been let before" do - it "validates that no previous let type is provided" do - log.first_time_property_let_as_social_housing = 1 - log.unitletas = 0 - property_validator.validate_unitletas(log) - expect(log.errors["unitletas"]) - .to include(match I18n.t("validations.property.rsnvac.previous_let_social")) - log.unitletas = 1 - property_validator.validate_unitletas(log) - expect(log.errors["unitletas"]) - .to include(match I18n.t("validations.property.rsnvac.previous_let_social")) - log.unitletas = 2 - property_validator.validate_unitletas(log) - expect(log.errors["unitletas"]) - .to include(match I18n.t("validations.property.rsnvac.previous_let_social")) - log.unitletas = 3 - property_validator.validate_unitletas(log) - expect(log.errors["unitletas"]) - .to include(match I18n.t("validations.property.rsnvac.previous_let_social")) - end - end - - context "when the property has been let previously" do - it "expects to have a previous let type" do - log.first_time_property_let_as_social_housing = 0 - log.unitletas = 0 - property_validator.validate_unitletas(log) - expect(log.errors["unitletas"]).to be_empty + expect(log.errors["beds"]).to include(I18n.t("validations.lettings.property.unittype_gn.one_three_bedroom_single_tenant_shared")) end end end describe "validate_rsnvac" do context "when the property has not been let before" do - it "validates that it has a first let reason for vacancy" do - log.first_time_property_let_as_social_housing = 1 - log.rsnvac = 6 - property_validator.validate_rsnvac(log) - expect(log.errors["rsnvac"]) - .to include(match I18n.t("validations.property.rsnvac.first_let_social")) - end - it "expects to have a first let reason for vacancy" do log.first_time_property_let_as_social_housing = 1 log.rsnvac = 15 @@ -184,22 +106,6 @@ RSpec.describe Validations::PropertyValidations do end context "when the property has been let as social housing before" do - it "validates that the reason for vacancy is not a first let as social housing reason" do - log.first_time_property_let_as_social_housing = 0 - log.rsnvac = 15 - property_validator.validate_rsnvac(log) - expect(log.errors["rsnvac"]) - .to include(match I18n.t("validations.property.rsnvac.first_let_not_social")) - log.rsnvac = 16 - property_validator.validate_rsnvac(log) - expect(log.errors["rsnvac"]) - .to include(match I18n.t("validations.property.rsnvac.first_let_not_social")) - log.rsnvac = 17 - property_validator.validate_rsnvac(log) - expect(log.errors["rsnvac"]) - .to include(match I18n.t("validations.property.rsnvac.first_let_not_social")) - end - it "expects the reason for vacancy to be a first let as social housing reason" do log.first_time_property_let_as_social_housing = 1 log.rsnvac = 15 @@ -220,7 +126,7 @@ RSpec.describe Validations::PropertyValidations do log.rsnvac = 14 property_validator.validate_rsnvac(log) expect(log.errors["rsnvac"]) - .to include(match I18n.t("validations.property.rsnvac.not_a_renewal")) + .to include(match I18n.t("validations.lettings.property.rsnvac.not_a_renewal")) end end end @@ -237,7 +143,7 @@ RSpec.describe Validations::PropertyValidations do log.prevten = prevten property_validator.validate_rsnvac(log) expect(log.errors["rsnvac"]) - .to include(match I18n.t("validations.property.rsnvac.non_temp_accommodation")) + .to include(match I18n.t("validations.lettings.property.rsnvac.non_temp_accommodation")) end end @@ -247,7 +153,7 @@ RSpec.describe Validations::PropertyValidations do log.referral = src property_validator.validate_rsnvac(log) expect(log.errors["rsnvac"]) - .to include(match I18n.t("validations.property.rsnvac.referral_invalid")) + .to include(match I18n.t("validations.lettings.property.rsnvac.referral_invalid")) end end end From 8dab730c54d285a82c37e87f3bff97ce88bab7aa Mon Sep 17 00:00:00 2001 From: Rachael Booth Date: Wed, 13 Nov 2024 17:20:34 +0000 Subject: [PATCH 03/34] CLDC-3704: Allow equity/stairowned/stairbought to have 1 decimal place (#2746) * CLDC-3666: Pull sales financial validations into translations file * CLDC-3704: Allow equity/stairowned/stairbought to have 1 decimal place * Fix lint for migration * Fix tests * Fix sale information validations tests * Fix lint * Fix form spec * Don't expect unexpected cashdis error --- app/models/form/sales/questions/equity.rb | 2 +- .../form/sales/questions/staircase_bought.rb | 2 +- .../form/sales/questions/staircase_owned.rb | 2 +- ...llow_decimal_stairbought_and_stairowned.rb | 15 ++++++ db/schema.rb | 6 +-- spec/models/form_spec.rb | 4 +- .../sales/financial_validations_spec.rb | 16 +++---- .../sale_information_validations_spec.rb | 46 +++++++++---------- .../sales/year2024/row_parser_spec.rb | 6 +-- 9 files changed, 56 insertions(+), 43 deletions(-) create mode 100644 db/migrate/20241031102744_allow_decimal_stairbought_and_stairowned.rb diff --git a/app/models/form/sales/questions/equity.rb b/app/models/form/sales/questions/equity.rb index dd1a14227..4aae785b8 100644 --- a/app/models/form/sales/questions/equity.rb +++ b/app/models/form/sales/questions/equity.rb @@ -6,7 +6,7 @@ class Form::Sales::Questions::Equity < ::Form::Question @type = "numeric" @min = 0 @max = 100 - @step = 1 + @step = 0.1 @width = 5 @suffix = "%" @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] diff --git a/app/models/form/sales/questions/staircase_bought.rb b/app/models/form/sales/questions/staircase_bought.rb index 8a3e88347..a9a3d6f23 100644 --- a/app/models/form/sales/questions/staircase_bought.rb +++ b/app/models/form/sales/questions/staircase_bought.rb @@ -7,7 +7,7 @@ class Form::Sales::Questions::StaircaseBought < ::Form::Question @width = 5 @min = 0 @max = 100 - @step = 1 + @step = 0.1 @suffix = "%" @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @top_guidance_partial = "financial_calculations_shared_ownership" diff --git a/app/models/form/sales/questions/staircase_owned.rb b/app/models/form/sales/questions/staircase_owned.rb index dc8daaf20..e0ec8865c 100644 --- a/app/models/form/sales/questions/staircase_owned.rb +++ b/app/models/form/sales/questions/staircase_owned.rb @@ -7,7 +7,7 @@ class Form::Sales::Questions::StaircaseOwned < ::Form::Question @width = 5 @min = 0 @max = 100 - @step = 1 + @step = 0.1 @suffix = "%" @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] end diff --git a/db/migrate/20241031102744_allow_decimal_stairbought_and_stairowned.rb b/db/migrate/20241031102744_allow_decimal_stairbought_and_stairowned.rb new file mode 100644 index 000000000..44154d630 --- /dev/null +++ b/db/migrate/20241031102744_allow_decimal_stairbought_and_stairowned.rb @@ -0,0 +1,15 @@ +class AllowDecimalStairboughtAndStairowned < ActiveRecord::Migration[7.0] + def up + change_table :sales_logs, bulk: true do |t| + t.change :stairbought, :decimal + t.change :stairowned, :decimal + end + end + + def down + change_table :sales_logs, bulk: true do |t| + t.change :stairbought, :integer + t.change :stairowned, :integer + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 84bf048af..ef635628c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2024_10_11_112158) do +ActiveRecord::Schema[7.0].define(version: 2024_10_31_102744) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -627,8 +627,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_10_11_112158) do t.integer "deposit_value_check" t.integer "frombeds" t.integer "staircase" - t.integer "stairbought" - t.integer "stairowned" + t.decimal "stairbought" + t.decimal "stairowned" t.decimal "mrent", precision: 10, scale: 2 t.datetime "exdate" t.integer "exday" diff --git a/spec/models/form_spec.rb b/spec/models/form_spec.rb index bb03cef59..d3dc7e21f 100644 --- a/spec/models/form_spec.rb +++ b/spec/models/form_spec.rb @@ -282,10 +282,10 @@ RSpec.describe Form, type: :model do context "and attribute Y is changed such that it is no longer routed to" do it "the value of this attribute is cleared" do - expect(log.stairbought).to be 25 + expect(log.stairbought).to eq 25 log.staircase = 2 log.form.reset_not_routed_questions_and_invalid_answers(log) - expect(log.stairbought).to be nil + expect(log.stairbought).to be_nil end end end diff --git a/spec/models/validations/sales/financial_validations_spec.rb b/spec/models/validations/sales/financial_validations_spec.rb index 7b7749003..cb55615c0 100644 --- a/spec/models/validations/sales/financial_validations_spec.rb +++ b/spec/models/validations/sales/financial_validations_spec.rb @@ -436,25 +436,25 @@ RSpec.describe Validations::Sales::FinancialValidations do let(:saledate) { Time.zone.local(2024, 4, 1) } it "adds errors if equity is more than stairowned - stairbought for joint purchase" do - record.stairbought = 2 + record.stairbought = 2.5 record.stairowned = 3 record.equity = 2 record.jointpur = 1 financial_validator.validate_equity_less_than_staircase_difference(record) - expect(record.errors["equity"]).to include(I18n.t("validations.sales.financial.equity.equity_over_stairowned_minus_stairbought.joint_purchase", equity: 2, staircase_difference: 1)) - expect(record.errors["stairowned"]).to include(I18n.t("validations.sales.financial.stairowned.equity_over_stairowned_minus_stairbought.joint_purchase", equity: 2, staircase_difference: 1)) - expect(record.errors["stairbought"]).to include(I18n.t("validations.sales.financial.stairbought.equity_over_stairowned_minus_stairbought.joint_purchase", equity: 2, staircase_difference: 1)) + expect(record.errors["equity"]).to include(I18n.t("validations.sales.financial.equity.equity_over_stairowned_minus_stairbought.joint_purchase", equity: 2, staircase_difference: 0.5)) + expect(record.errors["stairowned"]).to include(I18n.t("validations.sales.financial.stairowned.equity_over_stairowned_minus_stairbought.joint_purchase", equity: 2, staircase_difference: 0.5)) + expect(record.errors["stairbought"]).to include(I18n.t("validations.sales.financial.stairbought.equity_over_stairowned_minus_stairbought.joint_purchase", equity: 2, staircase_difference: 0.5)) end it "adds errors if equity is more than stairowned - stairbought for non joint purchase" do record.stairbought = 2 record.stairowned = 3 - record.equity = 2 + record.equity = 2.5 record.jointpur = 2 financial_validator.validate_equity_less_than_staircase_difference(record) - expect(record.errors["equity"]).to include(I18n.t("validations.sales.financial.equity.equity_over_stairowned_minus_stairbought.not_joint_purchase", equity: 2, staircase_difference: 1)) - expect(record.errors["stairowned"]).to include(I18n.t("validations.sales.financial.stairowned.equity_over_stairowned_minus_stairbought.not_joint_purchase", equity: 2, staircase_difference: 1)) - expect(record.errors["stairbought"]).to include(I18n.t("validations.sales.financial.stairbought.equity_over_stairowned_minus_stairbought.not_joint_purchase", equity: 2, staircase_difference: 1)) + expect(record.errors["equity"]).to include(I18n.t("validations.sales.financial.equity.equity_over_stairowned_minus_stairbought.not_joint_purchase", equity: 2.5, staircase_difference: 1.0)) + expect(record.errors["stairowned"]).to include(I18n.t("validations.sales.financial.stairowned.equity_over_stairowned_minus_stairbought.not_joint_purchase", equity: 2.5, staircase_difference: 1.0)) + expect(record.errors["stairbought"]).to include(I18n.t("validations.sales.financial.stairbought.equity_over_stairowned_minus_stairbought.not_joint_purchase", equity: 2.5, staircase_difference: 1.0)) end it "does not add errors if equity is less than stairowned - stairbought" do diff --git a/spec/models/validations/sales/sale_information_validations_spec.rb b/spec/models/validations/sales/sale_information_validations_spec.rb index 1f20163d3..36dadc345 100644 --- a/spec/models/validations/sales/sale_information_validations_spec.rb +++ b/spec/models/validations/sales/sale_information_validations_spec.rb @@ -1087,12 +1087,11 @@ RSpec.describe Validations::Sales::SaleInformationValidations do it "adds an error" do sale_information_validator.validate_staircasing_mortgage(record) - expect(record.errors["mortgage"]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.

The full purchase price (£30,000.00) multiplied by the percentage bought is £8,400.00.

These two amounts should be the same.") - expect(record.errors["value"]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.

The full purchase price (£30,000.00) multiplied by the percentage bought is £8,400.00.

These two amounts should be the same.") - expect(record.errors["deposit"]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.

The full purchase price (£30,000.00) multiplied by the percentage bought is £8,400.00.

These two amounts should be the same.") - expect(record.errors["stairbought"]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.

The full purchase price (£30,000.00) multiplied by the percentage bought is £8,400.00.

These two amounts should be the same.") - expect(record.errors["type"]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.

The full purchase price (£30,000.00) multiplied by the percentage bought is £8,400.00.

These two amounts should be the same.") - expect(record.errors["cashdis"]).not_to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.

The full purchase price (£30,000.00) multiplied by the percentage bought is £8,400.00.

These two amounts should be the same.") + expect(record.errors["mortgage"]).to include(I18n.t("validations.sales.sale_information.mortgage.staircasing_mortgage.mortgage_used", mortgage: "£10,000.00", deposit: "£5,000.00", mortgage_and_deposit_total: "£15,000.00", value: "£30,000.00", stairbought_part_of_value: "£8,400.00")) + expect(record.errors["value"]).to include(I18n.t("validations.sales.sale_information.value.staircasing_mortgage.mortgage_used", mortgage: "£10,000.00", deposit: "£5,000.00", mortgage_and_deposit_total: "£15,000.00", value: "£30,000.00", stairbought_part_of_value: "£8,400.00")) + expect(record.errors["deposit"]).to include(I18n.t("validations.sales.sale_information.deposit.staircasing_mortgage.mortgage_used", mortgage: "£10,000.00", deposit: "£5,000.00", mortgage_and_deposit_total: "£15,000.00", value: "£30,000.00", stairbought_part_of_value: "£8,400.00")) + expect(record.errors["stairbought"]).to include(I18n.t("validations.sales.sale_information.stairbought.staircasing_mortgage.mortgage_used", mortgage: "£10,000.00", deposit: "£5,000.00", mortgage_and_deposit_total: "£15,000.00", value: "£30,000.00", stairbought_part_of_value: "£8,400.00")) + expect(record.errors["type"]).to include(I18n.t("validations.sales.sale_information.type.staircasing_mortgage.mortgage_used", mortgage: "£10,000.00", deposit: "£5,000.00", mortgage_and_deposit_total: "£15,000.00", value: "£30,000.00", stairbought_part_of_value: "£8,400.00")) end context "and it is a social homebuy" do @@ -1103,12 +1102,12 @@ RSpec.describe Validations::Sales::SaleInformationValidations do it "adds an error" do sale_information_validator.validate_staircasing_mortgage(record) - expect(record.errors["mortgage"]).to include("The mortgage amount (£10,000.00), cash deposit (£5,000.00), and cash discount (£200.00) added together is £15,200.00.

The full purchase price (£30,000.00) multiplied by the percentage bought (28%) is £8,400.00.

These two amounts should be the same.") - expect(record.errors["value"]).to include("The mortgage amount (£10,000.00), cash deposit (£5,000.00), and cash discount (£200.00) added together is £15,200.00.

The full purchase price (£30,000.00) multiplied by the percentage bought (28%) is £8,400.00.

These two amounts should be the same.") - expect(record.errors["deposit"]).to include("The mortgage amount (£10,000.00), cash deposit (£5,000.00), and cash discount (£200.00) added together is £15,200.00.

The full purchase price (£30,000.00) multiplied by the percentage bought (28%) is £8,400.00.

These two amounts should be the same.") - expect(record.errors["stairbought"]).to include("The mortgage amount (£10,000.00), cash deposit (£5,000.00), and cash discount (£200.00) added together is £15,200.00.

The full purchase price (£30,000.00) multiplied by the percentage bought (28%) is £8,400.00.

These two amounts should be the same.") - expect(record.errors["cashdis"]).to include("The mortgage amount (£10,000.00), cash deposit (£5,000.00), and cash discount (£200.00) added together is £15,200.00.

The full purchase price (£30,000.00) multiplied by the percentage bought (28%) is £8,400.00.

These two amounts should be the same.") - expect(record.errors["type"]).to include("The mortgage amount (£10,000.00), cash deposit (£5,000.00), and cash discount (£200.00) added together is £15,200.00.

The full purchase price (£30,000.00) multiplied by the percentage bought (28%) is £8,400.00.

These two amounts should be the same.") + expect(record.errors["mortgage"]).to include(I18n.t("validations.sales.sale_information.mortgage.staircasing_mortgage.mortgage_used_socialhomebuy", mortgage: "£10,000.00", deposit: "£5,000.00", cashdis: "£200.00", mortgage_deposit_and_discount_total: "£15,200.00", value: "£30,000.00", stairbought_part_of_value: "£8,400.00", stairbought: "28.0%")) + expect(record.errors["value"]).to include(I18n.t("validations.sales.sale_information.value.staircasing_mortgage.mortgage_used_socialhomebuy", mortgage: "£10,000.00", deposit: "£5,000.00", cashdis: "£200.00", mortgage_deposit_and_discount_total: "£15,200.00", value: "£30,000.00", stairbought_part_of_value: "£8,400.00", stairbought: "28.0%")) + expect(record.errors["deposit"]).to include(I18n.t("validations.sales.sale_information.deposit.staircasing_mortgage.mortgage_used_socialhomebuy", mortgage: "£10,000.00", deposit: "£5,000.00", cashdis: "£200.00", mortgage_deposit_and_discount_total: "£15,200.00", value: "£30,000.00", stairbought_part_of_value: "£8,400.00", stairbought: "28.0%")) + expect(record.errors["stairbought"]).to include(I18n.t("validations.sales.sale_information.stairbought.staircasing_mortgage.mortgage_used_socialhomebuy", mortgage: "£10,000.00", deposit: "£5,000.00", cashdis: "£200.00", mortgage_deposit_and_discount_total: "£15,200.00", value: "£30,000.00", stairbought_part_of_value: "£8,400.00", stairbought: "28.0%")) + expect(record.errors["cashdis"]).to include(I18n.t("validations.sales.sale_information.cashdis.staircasing_mortgage.mortgage_used_socialhomebuy", mortgage: "£10,000.00", deposit: "£5,000.00", cashdis: "£200.00", mortgage_deposit_and_discount_total: "£15,200.00", value: "£30,000.00", stairbought_part_of_value: "£8,400.00", stairbought: "28.0%")) + expect(record.errors["type"]).to include(I18n.t("validations.sales.sale_information.type.staircasing_mortgage.mortgage_used_socialhomebuy", mortgage: "£10,000.00", deposit: "£5,000.00", cashdis: "£200.00", mortgage_deposit_and_discount_total: "£15,200.00", value: "£30,000.00", stairbought_part_of_value: "£8,400.00", stairbought: "28.0%")) end end @@ -1206,12 +1205,11 @@ RSpec.describe Validations::Sales::SaleInformationValidations do it "adds an error" do sale_information_validator.validate_staircasing_mortgage(record) - expect(record.errors["mortgageused"]).to include("The cash deposit is £5,000.00.

The full purchase price (£30,000.00) multiplied by the percentage bought is £8,400.00.

These two amounts should be the same.") - expect(record.errors["value"]).to include("The cash deposit is £5,000.00.

The full purchase price (£30,000.00) multiplied by the percentage bought is £8,400.00.

These two amounts should be the same.") - expect(record.errors["deposit"]).to include("The cash deposit is £5,000.00.

The full purchase price (£30,000.00) multiplied by the percentage bought is £8,400.00.

These two amounts should be the same.") - expect(record.errors["stairbought"]).to include("The cash deposit is £5,000.00.

The full purchase price (£30,000.00) multiplied by the percentage bought is £8,400.00.

These two amounts should be the same.") - expect(record.errors["type"]).to include("The cash deposit is £5,000.00.

The full purchase price (£30,000.00) multiplied by the percentage bought is £8,400.00.

These two amounts should be the same.") - expect(record.errors["cashdis"]).not_to include("The cash deposit is £5,000.00.

The full purchase price (£30,000.00) multiplied by the percentage bought is £8,400.00.

These two amounts should be the same.") + expect(record.errors["mortgageused"]).to include(I18n.t("validations.sales.sale_information.mortgageused.staircasing_mortgage.mortgage_not_used", deposit: "£5,000.00", value: "£30,000.00", stairbought_part_of_value: "£8,400.00")) + expect(record.errors["value"]).to include(I18n.t("validations.sales.sale_information.value.staircasing_mortgage.mortgage_not_used", deposit: "£5,000.00", value: "£30,000.00", stairbought_part_of_value: "£8,400.00")) + expect(record.errors["deposit"]).to include(I18n.t("validations.sales.sale_information.deposit.staircasing_mortgage.mortgage_not_used", deposit: "£5,000.00", value: "£30,000.00", stairbought_part_of_value: "£8,400.00")) + expect(record.errors["stairbought"]).to include(I18n.t("validations.sales.sale_information.stairbought.staircasing_mortgage.mortgage_not_used", deposit: "£5,000.00", value: "£30,000.00", stairbought_part_of_value: "£8,400.00")) + expect(record.errors["type"]).to include(I18n.t("validations.sales.sale_information.type.staircasing_mortgage.mortgage_not_used", deposit: "£5,000.00", value: "£30,000.00", stairbought_part_of_value: "£8,400.00")) end context "and it is a social homebuy" do @@ -1222,12 +1220,12 @@ RSpec.describe Validations::Sales::SaleInformationValidations do it "adds an error" do sale_information_validator.validate_staircasing_mortgage(record) - expect(record.errors["mortgageused"]).to include("The cash deposit (£5,000.00) and cash discount (£200.00) added together is £5,200.00.

The full purchase price (£30,000.00) multiplied by the percentage bought (28%) is £8,400.00.

These two amounts should be the same.") - expect(record.errors["value"]).to include("The cash deposit (£5,000.00) and cash discount (£200.00) added together is £5,200.00.

The full purchase price (£30,000.00) multiplied by the percentage bought (28%) is £8,400.00.

These two amounts should be the same.") - expect(record.errors["deposit"]).to include("The cash deposit (£5,000.00) and cash discount (£200.00) added together is £5,200.00.

The full purchase price (£30,000.00) multiplied by the percentage bought (28%) is £8,400.00.

These two amounts should be the same.") - expect(record.errors["stairbought"]).to include("The cash deposit (£5,000.00) and cash discount (£200.00) added together is £5,200.00.

The full purchase price (£30,000.00) multiplied by the percentage bought (28%) is £8,400.00.

These two amounts should be the same.") - expect(record.errors["cashdis"]).to include("The cash deposit (£5,000.00) and cash discount (£200.00) added together is £5,200.00.

The full purchase price (£30,000.00) multiplied by the percentage bought (28%) is £8,400.00.

These two amounts should be the same.") - expect(record.errors["type"]).to include("The cash deposit (£5,000.00) and cash discount (£200.00) added together is £5,200.00.

The full purchase price (£30,000.00) multiplied by the percentage bought (28%) is £8,400.00.

These two amounts should be the same.") + expect(record.errors["mortgageused"]).to include(I18n.t("validations.sales.sale_information.mortgageused.staircasing_mortgage.mortgage_not_used_socialhomebuy", deposit: "£5,000.00", cashdis: "£200.00", deposit_and_discount_total: "£5,200.00", value: "£30,000.00", stairbought: "28.0%", stairbought_part_of_value: "£8,400.00")) + expect(record.errors["value"]).to include(I18n.t("validations.sales.sale_information.value.staircasing_mortgage.mortgage_not_used_socialhomebuy", deposit: "£5,000.00", cashdis: "£200.00", deposit_and_discount_total: "£5,200.00", value: "£30,000.00", stairbought: "28.0%", stairbought_part_of_value: "£8,400.00")) + expect(record.errors["deposit"]).to include(I18n.t("validations.sales.sale_information.deposit.staircasing_mortgage.mortgage_not_used_socialhomebuy", deposit: "£5,000.00", cashdis: "£200.00", deposit_and_discount_total: "£5,200.00", value: "£30,000.00", stairbought: "28.0%", stairbought_part_of_value: "£8,400.00")) + expect(record.errors["stairbought"]).to include(I18n.t("validations.sales.sale_information.stairbought.staircasing_mortgage.mortgage_not_used_socialhomebuy", deposit: "£5,000.00", cashdis: "£200.00", deposit_and_discount_total: "£5,200.00", value: "£30,000.00", stairbought: "28.0%", stairbought_part_of_value: "£8,400.00")) + expect(record.errors["cashdis"]).to include(I18n.t("validations.sales.sale_information.cashdis.staircasing_mortgage.mortgage_not_used_socialhomebuy", deposit: "£5,000.00", cashdis: "£200.00", deposit_and_discount_total: "£5,200.00", value: "£30,000.00", stairbought: "28.0%", stairbought_part_of_value: "£8,400.00")) + expect(record.errors["type"]).to include(I18n.t("validations.sales.sale_information.type.staircasing_mortgage.mortgage_not_used_socialhomebuy", deposit: "£5,000.00", cashdis: "£200.00", deposit_and_discount_total: "£5,200.00", value: "£30,000.00", stairbought: "28.0%", stairbought_part_of_value: "£8,400.00")) end end 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 4844ce490..e4e2eb6f5 100644 --- a/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb +++ b/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb @@ -1393,9 +1393,9 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do it "does not add errors and sets mortgage used to 3" do parser.valid? - expect(parser.log.mortgageused).to be(3) - expect(parser.log.stairowned).to be(100) - expect(parser.log.deposit).to be(nil) + expect(parser.log.mortgageused).to eq(3) + expect(parser.log.stairowned).to eq(100) + expect(parser.log.deposit).to be_nil expect(parser.errors[:field_103]).to be_empty expect(parser.errors[:field_109]).to be_empty end From 10de48540857822940cf74fed95351e692fd252a Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Thu, 14 Nov 2024 08:16:31 +0000 Subject: [PATCH 04/34] CLDC-3698 Rename household needs to other household information for sales (#2772) * Rename household needs occurences * Rename files --- app/models/form/sales/sections/household.rb | 2 +- .../{household_needs.rb => other_household_information.rb} | 4 ++-- ...sehold_needs.en.yml => other_household_information.en.yml} | 2 +- ...sehold_needs.en.yml => other_household_information.en.yml} | 2 +- ...sehold_needs.en.yml => other_household_information.en.yml} | 2 +- spec/features/form/check_answers_page_sales_logs_spec.rb | 2 +- spec/models/form/sales/sections/household_spec.rb | 2 +- ...hold_needs_spec.rb => other_household_information_spec.rb} | 4 ++-- 8 files changed, 10 insertions(+), 10 deletions(-) rename app/models/form/sales/subsections/{household_needs.rb => other_household_information.rb} (85%) rename config/locales/forms/2023/sales/{household_needs.en.yml => other_household_information.en.yml} (97%) rename config/locales/forms/2024/sales/{household_needs.en.yml => other_household_information.en.yml} (97%) rename config/locales/forms/2025/sales/{household_needs.en.yml => other_household_information.en.yml} (97%) rename spec/models/form/sales/subsections/{household_needs_spec.rb => other_household_information_spec.rb} (86%) diff --git a/app/models/form/sales/sections/household.rb b/app/models/form/sales/sections/household.rb index e8bcc1451..674f38197 100644 --- a/app/models/form/sales/sections/household.rb +++ b/app/models/form/sales/sections/household.rb @@ -7,7 +7,7 @@ class Form::Sales::Sections::Household < ::Form::Section @subsections = [ Form::Sales::Subsections::HouseholdCharacteristics.new(nil, nil, self), Form::Sales::Subsections::HouseholdSituation.new(nil, nil, self), - Form::Sales::Subsections::HouseholdNeeds.new(nil, nil, self), + Form::Sales::Subsections::OtherHouseholdInformation.new(nil, nil, self), ] end end diff --git a/app/models/form/sales/subsections/household_needs.rb b/app/models/form/sales/subsections/other_household_information.rb similarity index 85% rename from app/models/form/sales/subsections/household_needs.rb rename to app/models/form/sales/subsections/other_household_information.rb index a419cff95..3acc9ffd5 100644 --- a/app/models/form/sales/subsections/household_needs.rb +++ b/app/models/form/sales/subsections/other_household_information.rb @@ -1,7 +1,7 @@ -class Form::Sales::Subsections::HouseholdNeeds < ::Form::Subsection +class Form::Sales::Subsections::OtherHouseholdInformation < ::Form::Subsection def initialize(id, hsh, section) super - @id = "household_needs" + @id = "other_household_information" @label = "Other household information" @depends_on = [{ "setup_completed?" => true }] end diff --git a/config/locales/forms/2023/sales/household_needs.en.yml b/config/locales/forms/2023/sales/other_household_information.en.yml similarity index 97% rename from config/locales/forms/2023/sales/household_needs.en.yml rename to config/locales/forms/2023/sales/other_household_information.en.yml index f4a13e47a..6d96ca46e 100644 --- a/config/locales/forms/2023/sales/household_needs.en.yml +++ b/config/locales/forms/2023/sales/other_household_information.en.yml @@ -2,7 +2,7 @@ en: forms: 2023: sales: - household_needs: + other_household_information: hhregres: page_header: "" check_answer_label: "Have any of the buyers ever served as a regular in the UK armed forces?" diff --git a/config/locales/forms/2024/sales/household_needs.en.yml b/config/locales/forms/2024/sales/other_household_information.en.yml similarity index 97% rename from config/locales/forms/2024/sales/household_needs.en.yml rename to config/locales/forms/2024/sales/other_household_information.en.yml index 290543a34..8363c5108 100644 --- a/config/locales/forms/2024/sales/household_needs.en.yml +++ b/config/locales/forms/2024/sales/other_household_information.en.yml @@ -2,7 +2,7 @@ en: forms: 2024: sales: - household_needs: + other_household_information: hhregres: page_header: "" check_answer_label: "Have any of the buyers ever served as a regular in the UK armed forces?" diff --git a/config/locales/forms/2025/sales/household_needs.en.yml b/config/locales/forms/2025/sales/other_household_information.en.yml similarity index 97% rename from config/locales/forms/2025/sales/household_needs.en.yml rename to config/locales/forms/2025/sales/other_household_information.en.yml index 1908ba6ca..ddae70ffa 100644 --- a/config/locales/forms/2025/sales/household_needs.en.yml +++ b/config/locales/forms/2025/sales/other_household_information.en.yml @@ -2,7 +2,7 @@ en: forms: 2025: sales: - household_needs: + other_household_information: hhregres: page_header: "" check_answer_label: "Have any of the buyers ever served as a regular in the UK armed forces?" diff --git a/spec/features/form/check_answers_page_sales_logs_spec.rb b/spec/features/form/check_answers_page_sales_logs_spec.rb index 85d3d39b5..26ece07d3 100644 --- a/spec/features/form/check_answers_page_sales_logs_spec.rb +++ b/spec/features/form/check_answers_page_sales_logs_spec.rb @@ -35,7 +35,7 @@ RSpec.describe "Sales Log Check Answers Page" do let(:last_question_for_subsection) { "propcode" } it "does not group questions into summary cards if the questions in the subsection don't have a check_answers_card_number attribute" do - visit("/sales-logs/#{completed_sales_log_joint_purchase.id}/household-needs/check-answers") + visit("/sales-logs/#{completed_sales_log_joint_purchase.id}/other-household-information/check-answers") assert_selector ".govuk-summary-card__title", count: 0 end diff --git a/spec/models/form/sales/sections/household_spec.rb b/spec/models/form/sales/sections/household_spec.rb index cff166510..d747d171d 100644 --- a/spec/models/form/sales/sections/household_spec.rb +++ b/spec/models/form/sales/sections/household_spec.rb @@ -16,7 +16,7 @@ RSpec.describe Form::Sales::Sections::Household, type: :model do %w[ household_characteristics household_situation - household_needs + other_household_information ], ) end diff --git a/spec/models/form/sales/subsections/household_needs_spec.rb b/spec/models/form/sales/subsections/other_household_information_spec.rb similarity index 86% rename from spec/models/form/sales/subsections/household_needs_spec.rb rename to spec/models/form/sales/subsections/other_household_information_spec.rb index cd4ff67fa..461ad2cc9 100644 --- a/spec/models/form/sales/subsections/household_needs_spec.rb +++ b/spec/models/form/sales/subsections/other_household_information_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Sales::Subsections::HouseholdNeeds, type: :model do +RSpec.describe Form::Sales::Subsections::OtherHouseholdInformation, type: :model do subject(:household_characteristics) { described_class.new(subsection_id, subsection_definition, section) } let(:subsection_id) { nil } @@ -27,7 +27,7 @@ RSpec.describe Form::Sales::Subsections::HouseholdNeeds, type: :model do end it "has the correct id" do - expect(household_characteristics.id).to eq("household_needs") + expect(household_characteristics.id).to eq("other_household_information") end it "has the correct label" do From b87c107f25703e4befeaf23422f362d8c9b27ff3 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Thu, 14 Nov 2024 08:55:41 +0000 Subject: [PATCH 05/34] Route only to buyer questions for staircase transactions (#2765) --- .../form/sales/pages/buyer1_ethnic_group.rb | 18 +-- .../sales/pages/buyer1_live_in_property.rb | 37 +++--- .../form/sales/pages/buyer1_nationality.rb | 18 +-- .../sales/pages/buyer1_working_situation.rb | 10 ++ .../form/sales/pages/buyer2_ethnic_group.rb | 21 +-- .../sales/pages/buyer2_live_in_property.rb | 39 +++--- .../form/sales/pages/buyer2_nationality.rb | 21 +-- .../sales/pages/buyer2_working_situation.rb | 21 +-- .../pages/number_of_others_in_property.rb | 21 +-- .../sales/pages/buyer1_ethnic_group_spec.rb | 71 +++++++++- .../pages/buyer1_live_in_property_spec.rb | 106 +++++++++++---- .../sales/pages/buyer1_nationality_spec.rb | 75 +++++++++-- .../pages/buyer1_working_situation_spec.rb | 71 +++++++++- .../sales/pages/buyer2_ethnic_group_spec.rb | 88 +++++++++++-- .../pages/buyer2_live_in_property_spec.rb | 108 ++++++++++++---- .../sales/pages/buyer2_nationality_spec.rb | 96 ++++++++++---- .../pages/buyer2_working_situation_spec.rb | 88 +++++++++++-- .../number_of_others_in_property_spec.rb | 121 ++++++++++++++---- 18 files changed, 802 insertions(+), 228 deletions(-) diff --git a/app/models/form/sales/pages/buyer1_ethnic_group.rb b/app/models/form/sales/pages/buyer1_ethnic_group.rb index afd5a69f3..a545c95a8 100644 --- a/app/models/form/sales/pages/buyer1_ethnic_group.rb +++ b/app/models/form/sales/pages/buyer1_ethnic_group.rb @@ -2,14 +2,6 @@ class Form::Sales::Pages::Buyer1EthnicGroup < ::Form::Page def initialize(id, hsh, subsection) super @id = "buyer_1_ethnic_group" - @depends_on = [ - { - "buyer_has_seen_privacy_notice?" => true, - }, - { - "buyer_not_interviewed?" => true, - }, - ] end def questions @@ -17,4 +9,14 @@ class Form::Sales::Pages::Buyer1EthnicGroup < ::Form::Page Form::Sales::Questions::Buyer1EthnicGroup.new(nil, nil, self), ] end + + def routed_to?(log, _current_user) + super && page_routed_to?(log) + end + + def page_routed_to?(log) + return false if log.form.start_year_2025_or_later? && log.is_staircase? + + log.buyer_has_seen_privacy_notice? || log.buyer_not_interviewed? + end end diff --git a/app/models/form/sales/pages/buyer1_live_in_property.rb b/app/models/form/sales/pages/buyer1_live_in_property.rb index 8ee63d26c..eede7b282 100644 --- a/app/models/form/sales/pages/buyer1_live_in_property.rb +++ b/app/models/form/sales/pages/buyer1_live_in_property.rb @@ -2,26 +2,6 @@ class Form::Sales::Pages::Buyer1LiveInProperty < ::Form::Page def initialize(id, hsh, subsection) super @id = "buyer_1_live_in_property" - @depends_on = [ - { - "buyer_has_seen_privacy_notice?" => true, - "outright_sale?" => false, - }, - { - "buyer_not_interviewed?" => true, - "outright_sale?" => false, - }, - { - "buyer_has_seen_privacy_notice?" => true, - "joint_purchase?" => true, - "buyers_will_live_in?" => true, - }, - { - "buyer_not_interviewed?" => true, - "joint_purchase?" => true, - "buyers_will_live_in?" => true, - }, - ] end def questions @@ -29,4 +9,21 @@ class Form::Sales::Pages::Buyer1LiveInProperty < ::Form::Page Form::Sales::Questions::Buyer1LiveInProperty.new(nil, nil, self), ] end + + def routed_to?(log, _current_user) + super && page_routed_to?(log) + end + + def page_routed_to?(log) + return false if log.form.start_year_2025_or_later? && log.is_staircase? + + privacy_notice_seen_or_not_interviewed = log.buyer_has_seen_privacy_notice? || log.buyer_not_interviewed? + not_outright_sale = !log.outright_sale? + joint_purchase_and_live_in = log.joint_purchase? && log.buyers_will_live_in? + + return true if privacy_notice_seen_or_not_interviewed && not_outright_sale + return true if privacy_notice_seen_or_not_interviewed && joint_purchase_and_live_in + + false + end end diff --git a/app/models/form/sales/pages/buyer1_nationality.rb b/app/models/form/sales/pages/buyer1_nationality.rb index 791ecafac..7c87421ca 100644 --- a/app/models/form/sales/pages/buyer1_nationality.rb +++ b/app/models/form/sales/pages/buyer1_nationality.rb @@ -2,14 +2,6 @@ class Form::Sales::Pages::Buyer1Nationality < ::Form::Page def initialize(id, hsh, subsection) super @id = "buyer_1_nationality" - @depends_on = [ - { - "buyer_has_seen_privacy_notice?" => true, - }, - { - "buyer_not_interviewed?" => true, - }, - ] end def questions @@ -22,4 +14,14 @@ class Form::Sales::Pages::Buyer1Nationality < ::Form::Page [Form::Sales::Questions::Buyer1Nationality.new(nil, nil, self)] end end + + def routed_to?(log, _current_user) + super && page_routed_to?(log) + end + + def page_routed_to?(log) + return false if log.form.start_year_2025_or_later? && log.is_staircase? + + log.buyer_has_seen_privacy_notice? || log.buyer_not_interviewed? + end end diff --git a/app/models/form/sales/pages/buyer1_working_situation.rb b/app/models/form/sales/pages/buyer1_working_situation.rb index d9f33f8e9..911bc2a70 100644 --- a/app/models/form/sales/pages/buyer1_working_situation.rb +++ b/app/models/form/sales/pages/buyer1_working_situation.rb @@ -17,4 +17,14 @@ class Form::Sales::Pages::Buyer1WorkingSituation < ::Form::Page Form::Sales::Questions::Buyer1WorkingSituation.new(nil, nil, self), ] end + + def routed_to?(log, _current_user) + super && page_routed_to?(log) + end + + def page_routed_to?(log) + return false if log.form.start_year_2025_or_later? && log.is_staircase? + + log.buyer_has_seen_privacy_notice? || log.buyer_not_interviewed? + end end diff --git a/app/models/form/sales/pages/buyer2_ethnic_group.rb b/app/models/form/sales/pages/buyer2_ethnic_group.rb index 196063985..8c1dc59e3 100644 --- a/app/models/form/sales/pages/buyer2_ethnic_group.rb +++ b/app/models/form/sales/pages/buyer2_ethnic_group.rb @@ -2,16 +2,6 @@ class Form::Sales::Pages::Buyer2EthnicGroup < ::Form::Page def initialize(id, hsh, subsection) super @id = "buyer_2_ethnic_group" - @depends_on = [ - { - "joint_purchase?" => true, - "buyer_has_seen_privacy_notice?" => true, - }, - { - "joint_purchase?" => true, - "buyer_not_interviewed?" => true, - }, - ] end def questions @@ -19,4 +9,15 @@ class Form::Sales::Pages::Buyer2EthnicGroup < ::Form::Page Form::Sales::Questions::Buyer2EthnicGroup.new(nil, nil, self), ] end + + def routed_to?(log, _current_user) + super && page_routed_to?(log) + end + + def page_routed_to?(log) + return false unless log.joint_purchase? + return false if log.form.start_year_2025_or_later? && log.is_staircase? + + log.buyer_has_seen_privacy_notice? || log.buyer_not_interviewed? + end end diff --git a/app/models/form/sales/pages/buyer2_live_in_property.rb b/app/models/form/sales/pages/buyer2_live_in_property.rb index a16638c0d..b38f0ec3b 100644 --- a/app/models/form/sales/pages/buyer2_live_in_property.rb +++ b/app/models/form/sales/pages/buyer2_live_in_property.rb @@ -2,28 +2,6 @@ class Form::Sales::Pages::Buyer2LiveInProperty < ::Form::Page def initialize(id, hsh, subsection) super @id = "buyer_2_live_in_property" - @depends_on = [ - { - "buyer_has_seen_privacy_notice?" => true, - "outright_sale?" => false, - "joint_purchase?" => true, - }, - { - "buyer_not_interviewed?" => true, - "outright_sale?" => false, - "joint_purchase?" => true, - }, - { - "buyer_has_seen_privacy_notice?" => true, - "joint_purchase?" => true, - "buyers_will_live_in?" => true, - }, - { - "buyer_not_interviewed?" => true, - "joint_purchase?" => true, - "buyers_will_live_in?" => true, - }, - ] end def questions @@ -31,4 +9,21 @@ class Form::Sales::Pages::Buyer2LiveInProperty < ::Form::Page Form::Sales::Questions::Buyer2LiveInProperty.new(nil, nil, self), ] end + + def routed_to?(log, _current_user) + super && page_routed_to?(log) + end + + def page_routed_to?(log) + return false if log.form.start_year_2025_or_later? && log.is_staircase? + return false unless log.joint_purchase? + + privacy_notice_seen_or_not_interviewed = log.buyer_has_seen_privacy_notice? || log.buyer_not_interviewed? + not_outright_sale = !log.outright_sale? + + return true if privacy_notice_seen_or_not_interviewed && not_outright_sale + return true if privacy_notice_seen_or_not_interviewed && log.buyers_will_live_in? + + false + end end diff --git a/app/models/form/sales/pages/buyer2_nationality.rb b/app/models/form/sales/pages/buyer2_nationality.rb index 30790fd63..8cbf32980 100644 --- a/app/models/form/sales/pages/buyer2_nationality.rb +++ b/app/models/form/sales/pages/buyer2_nationality.rb @@ -2,16 +2,6 @@ class Form::Sales::Pages::Buyer2Nationality < ::Form::Page def initialize(id, hsh, subsection) super @id = "buyer_2_nationality" - @depends_on = [ - { - "joint_purchase?" => true, - "buyer_has_seen_privacy_notice?" => true, - }, - { - "joint_purchase?" => true, - "buyer_not_interviewed?" => true, - }, - ] end def questions @@ -24,4 +14,15 @@ class Form::Sales::Pages::Buyer2Nationality < ::Form::Page [Form::Sales::Questions::Buyer2Nationality.new(nil, nil, self)] end end + + def routed_to?(log, _current_user) + super && page_routed_to?(log) + end + + def page_routed_to?(log) + return false unless log.joint_purchase? + return false if log.form.start_year_2025_or_later? && log.is_staircase? + + log.buyer_has_seen_privacy_notice? || log.buyer_not_interviewed? + end end diff --git a/app/models/form/sales/pages/buyer2_working_situation.rb b/app/models/form/sales/pages/buyer2_working_situation.rb index 4e73c09cf..264d720f2 100644 --- a/app/models/form/sales/pages/buyer2_working_situation.rb +++ b/app/models/form/sales/pages/buyer2_working_situation.rb @@ -3,16 +3,6 @@ class Form::Sales::Pages::Buyer2WorkingSituation < ::Form::Page super @id = "buyer_2_working_situation" @copy_key = "sales.household_characteristics.ecstat2.buyer" - @depends_on = [ - { - "joint_purchase?" => true, - "buyer_has_seen_privacy_notice?" => true, - }, - { - "joint_purchase?" => true, - "buyer_not_interviewed?" => true, - }, - ] end def questions @@ -20,4 +10,15 @@ class Form::Sales::Pages::Buyer2WorkingSituation < ::Form::Page Form::Sales::Questions::Buyer2WorkingSituation.new(nil, nil, self), ] end + + def routed_to?(log, _current_user) + super && page_routed_to?(log) + end + + def page_routed_to?(log) + return false unless log.joint_purchase? + return false if log.form.start_year_2025_or_later? && log.is_staircase? + + log.buyer_has_seen_privacy_notice? || log.buyer_not_interviewed? + end end diff --git a/app/models/form/sales/pages/number_of_others_in_property.rb b/app/models/form/sales/pages/number_of_others_in_property.rb index 23d670f93..1bf454b9c 100644 --- a/app/models/form/sales/pages/number_of_others_in_property.rb +++ b/app/models/form/sales/pages/number_of_others_in_property.rb @@ -1,16 +1,6 @@ class Form::Sales::Pages::NumberOfOthersInProperty < ::Form::Page def initialize(id, hsh, subsection, joint_purchase:) super(id, hsh, subsection) - @depends_on = [ - { - "buyer_has_seen_privacy_notice?" => true, - "joint_purchase?" => joint_purchase, - }, - { - "buyer_not_interviewed?" => true, - "joint_purchase?" => joint_purchase, - }, - ] @joint_purchase = joint_purchase end @@ -19,4 +9,15 @@ class Form::Sales::Pages::NumberOfOthersInProperty < ::Form::Page Form::Sales::Questions::NumberOfOthersInProperty.new(nil, nil, self, joint_purchase: @joint_purchase), ] end + + def routed_to?(log, _current_user) + super && page_routed_to?(log) + end + + def page_routed_to?(log) + return false unless log.joint_purchase? == @joint_purchase + return false if log.form.start_year_2025_or_later? && log.is_staircase? + + log.buyer_has_seen_privacy_notice? || log.buyer_not_interviewed? + end end diff --git a/spec/models/form/sales/pages/buyer1_ethnic_group_spec.rb b/spec/models/form/sales/pages/buyer1_ethnic_group_spec.rb index 9a94fc0fd..c1cbb1dff 100644 --- a/spec/models/form/sales/pages/buyer1_ethnic_group_spec.rb +++ b/spec/models/form/sales/pages/buyer1_ethnic_group_spec.rb @@ -5,7 +5,8 @@ RSpec.describe Form::Sales::Pages::Buyer1EthnicGroup, type: :model do let(:page_id) { nil } let(:page_definition) { nil } - let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_2024_or_later?: false)) } + let(:form) { Form.new(nil, 2023, [], "sales") } + let(:subsection) { instance_double(Form::Subsection, form:, depends_on: nil) } it "has correct subsection" do expect(page.subsection).to eq(subsection) @@ -23,7 +24,71 @@ RSpec.describe Form::Sales::Pages::Buyer1EthnicGroup, type: :model do expect(page.description).to be_nil end - it "has correct depends_on" do - expect(page.depends_on).to eq([{ "buyer_has_seen_privacy_notice?" => true }, { "buyer_not_interviewed?" => true }]) + context "with year 2024" do + let(:form) { Form.new(nil, 2024, [], "sales") } + + context "when routing" do + before do + allow(log).to receive(:form).and_return(form) + end + + context "when buyer has seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when buyer has seen privacy notice and buyer not interviewed" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 1, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "and buyer has not seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 0, staircase: 2) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + + context "and buyer has not seen privacy notice and buyer not interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 1, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + end + end + + context "with year 2025" do + let(:form) { Form.new(nil, 2025, [], "sales") } + + before do + allow(log).to receive(:form).and_return(form) + end + + context "when routing" do + context "and staircase is not 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "and staircase is 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 1) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + end end end diff --git a/spec/models/form/sales/pages/buyer1_live_in_property_spec.rb b/spec/models/form/sales/pages/buyer1_live_in_property_spec.rb index edc53bd79..76cb612eb 100644 --- a/spec/models/form/sales/pages/buyer1_live_in_property_spec.rb +++ b/spec/models/form/sales/pages/buyer1_live_in_property_spec.rb @@ -5,7 +5,8 @@ RSpec.describe Form::Sales::Pages::Buyer1LiveInProperty, type: :model do let(:page_id) { nil } let(:page_definition) { nil } - let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_2024_or_later?: false)) } + let(:form) { Form.new(nil, 2023, [], "sales") } + let(:subsection) { instance_double(Form::Subsection, form:, enabled?: true, depends_on: nil) } it "has correct subsection" do expect(page.subsection).to eq(subsection) @@ -23,26 +24,87 @@ RSpec.describe Form::Sales::Pages::Buyer1LiveInProperty, type: :model do expect(page.description).to be_nil end - it "has correct depends_on" do - expect(page.depends_on).to eq([ - { - "buyer_has_seen_privacy_notice?" => true, - "outright_sale?" => false, - }, - { - "buyer_not_interviewed?" => true, - "outright_sale?" => false, - }, - { - "buyer_has_seen_privacy_notice?" => true, - "joint_purchase?" => true, - "buyers_will_live_in?" => true, - }, - { - "buyer_not_interviewed?" => true, - "joint_purchase?" => true, - "buyers_will_live_in?" => true, - }, - ]) + context "with year 2024" do + let(:form) { Form.new(nil, 2024, [], "sales") } + + context "when routing" do + before do + allow(log).to receive(:form).and_return(form) + end + + context "when privacy notice seen and not outright sale" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 2, noint: nil, staircase: 2, ownershipsch: 1, buylivein: nil) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when buyer not interviewed and not outright sale" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 2, noint: 1, staircase: 2, ownershipsch: 1, buylivein: nil) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when privacy notice seen, joint purchase and live in" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 2, ownershipsch: 1, buylivein: 1) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when buyer not interviewed, joint purchase and live in" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 1, staircase: 2, ownershipsch: 1, buylivein: 1) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when buyer has seen privacy notice and buyer interviewed and it's outright sale" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 2, noint: 0, staircase: 2, ownershipsch: 3, buylivein: nil) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + + context "and buyer has not seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 2, noint: 0, staircase: 2, ownershipsch: 1, buylivein: nil) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + end + end + + context "with year 2025" do + let(:form) { Form.new(nil, 2025, [], "sales") } + + before do + allow(log).to receive(:form).and_return(form) + end + + context "when routing" do + context "and staircase is not 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 2, noint: 1, staircase: 2, ownershipsch: 1, buylivein: nil) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "and staircase is 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 2, noint: 1, ownershipsch: 1, buylivein: nil, staircase: 1) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + end end end diff --git a/spec/models/form/sales/pages/buyer1_nationality_spec.rb b/spec/models/form/sales/pages/buyer1_nationality_spec.rb index a84a44009..ad09ee526 100644 --- a/spec/models/form/sales/pages/buyer1_nationality_spec.rb +++ b/spec/models/form/sales/pages/buyer1_nationality_spec.rb @@ -3,12 +3,11 @@ require "rails_helper" RSpec.describe Form::Sales::Pages::Buyer1Nationality, type: :model do subject(:page) { described_class.new(nil, nil, subsection) } - let(:subsection) { instance_double(Form::Subsection) } - let(:form) { instance_double(Form, start_date: Time.zone.local(2023, 4, 1)) } + let(:subsection) { instance_double(Form::Subsection, depends_on: nil) } + let(:form) { Form.new(nil, 2023, [], "sales") } before do allow(subsection).to receive(:form).and_return(form) - allow(form).to receive(:start_year_2024_or_later?).and_return(false) end it "has correct subsection" do @@ -27,17 +26,75 @@ RSpec.describe Form::Sales::Pages::Buyer1Nationality, type: :model do expect(page.description).to be_nil end - it "has correct depends_on" do - expect(page.depends_on).to eq [{ "buyer_has_seen_privacy_notice?" => true }, { "buyer_not_interviewed?" => true }] + context "with year 2024" do + let(:form) { Form.new(nil, 2024, [], "sales") } + + it "has correct questions" do + expect(page.questions.map(&:id)).to eq %w[nationality_all_group nationality_all] + end + + context "when routing" do + before do + allow(log).to receive(:form).and_return(form) + end + + context "when buyer has seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when buyer has seen privacy notice and buyer not interviewed" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 1, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "and buyer has not seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 0, staircase: 2) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + + context "and buyer has not seen privacy notice and buyer not interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 1, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + end end - context "with year 2024" do + context "with year 2025" do + let(:form) { Form.new(nil, 2025, [], "sales") } + before do - allow(form).to receive(:start_year_2024_or_later?).and_return(true) + allow(log).to receive(:form).and_return(form) end - it "has correct questions" do - expect(page.questions.map(&:id)).to eq %w[nationality_all_group nationality_all] + context "when routing" do + context "and staircase is not 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "and staircase is 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 1) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end end end end diff --git a/spec/models/form/sales/pages/buyer1_working_situation_spec.rb b/spec/models/form/sales/pages/buyer1_working_situation_spec.rb index 182da0a82..47a997623 100644 --- a/spec/models/form/sales/pages/buyer1_working_situation_spec.rb +++ b/spec/models/form/sales/pages/buyer1_working_situation_spec.rb @@ -5,7 +5,8 @@ RSpec.describe Form::Sales::Pages::Buyer1WorkingSituation, type: :model do let(:page_id) { nil } let(:page_definition) { nil } - let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_2024_or_later?: true, start_year_2025_or_later?: false)) } + let(:form) { Form.new(nil, 2023, [], "sales") } + let(:subsection) { instance_double(Form::Subsection, form:, enabled?: true) } it "has correct subsection" do expect(page.subsection).to eq(subsection) @@ -18,4 +19,72 @@ RSpec.describe Form::Sales::Pages::Buyer1WorkingSituation, type: :model do it "has the correct id" do expect(page.id).to eq("buyer_1_working_situation") end + + context "with year 2024" do + let(:form) { Form.new(nil, 2024, [], "sales") } + + context "when routing" do + before do + allow(log).to receive(:form).and_return(form) + end + + context "when buyer has seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when buyer has seen privacy notice and buyer not interviewed" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 1, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "and buyer has not seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 0, staircase: 2) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + + context "and buyer has not seen privacy notice and buyer not interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 1, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + end + end + + context "with year 2025" do + let(:form) { Form.new(nil, 2025, [], "sales") } + + before do + allow(log).to receive(:form).and_return(form) + end + + context "when routing" do + context "and staircase is not 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "and staircase is 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 1) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + end + end end diff --git a/spec/models/form/sales/pages/buyer2_ethnic_group_spec.rb b/spec/models/form/sales/pages/buyer2_ethnic_group_spec.rb index d129508ee..d12138682 100644 --- a/spec/models/form/sales/pages/buyer2_ethnic_group_spec.rb +++ b/spec/models/form/sales/pages/buyer2_ethnic_group_spec.rb @@ -5,7 +5,8 @@ RSpec.describe Form::Sales::Pages::Buyer2EthnicGroup, type: :model do let(:page_id) { nil } let(:page_definition) { nil } - let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1))) } + let(:form) { Form.new(nil, 2023, [], "sales") } + let(:subsection) { instance_double(Form::Subsection, form:, depends_on: nil) } it "has correct subsection" do expect(page.subsection).to eq(subsection) @@ -23,16 +24,79 @@ RSpec.describe Form::Sales::Pages::Buyer2EthnicGroup, type: :model do expect(page.description).to be_nil end - it "has correct depends_on" do - expect(page.depends_on).to eq([ - { - "joint_purchase?" => true, - "buyer_has_seen_privacy_notice?" => true, - }, - { - "joint_purchase?" => true, - "buyer_not_interviewed?" => true, - }, - ]) + context "with year 2024" do + let(:form) { Form.new(nil, 2024, [], "sales") } + + context "when routing" do + before do + allow(log).to receive(:form).and_return(form) + end + + context "when buyer has seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when buyer has seen privacy notice and buyer not interviewed" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 1, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "and buyer has not seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 0, staircase: 2) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + + context "and buyer has not seen privacy notice and buyer not interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 1, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when it's not a joint purchase" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 2, noint: 1, staircase: 2) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + end + end + + context "with year 2025" do + let(:form) { Form.new(nil, 2025, [], "sales") } + + before do + allow(log).to receive(:form).and_return(form) + end + + context "when routing" do + context "and staircase is not 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "and staircase is 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 1) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + end end end diff --git a/spec/models/form/sales/pages/buyer2_live_in_property_spec.rb b/spec/models/form/sales/pages/buyer2_live_in_property_spec.rb index b2eb2386b..73a56be00 100644 --- a/spec/models/form/sales/pages/buyer2_live_in_property_spec.rb +++ b/spec/models/form/sales/pages/buyer2_live_in_property_spec.rb @@ -5,7 +5,8 @@ RSpec.describe Form::Sales::Pages::Buyer2LiveInProperty, type: :model do let(:page_id) { nil } let(:page_definition) { nil } - let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1))) } + let(:form) { Form.new(nil, 2023, [], "sales") } + let(:subsection) { instance_double(Form::Subsection, form:, depends_on: nil, enabled?: true) } it "has correct subsection" do expect(page.subsection).to eq(subsection) @@ -23,28 +24,87 @@ RSpec.describe Form::Sales::Pages::Buyer2LiveInProperty, type: :model do expect(page.description).to be_nil end - it "has correct depends_on" do - expect(page.depends_on).to eq([ - { - "buyer_has_seen_privacy_notice?" => true, - "outright_sale?" => false, - "joint_purchase?" => true, - }, - { - "buyer_not_interviewed?" => true, - "outright_sale?" => false, - "joint_purchase?" => true, - }, - { - "buyer_has_seen_privacy_notice?" => true, - "joint_purchase?" => true, - "buyers_will_live_in?" => true, - }, - { - "buyer_not_interviewed?" => true, - "joint_purchase?" => true, - "buyers_will_live_in?" => true, - }, - ]) + context "with year 2024" do + let(:form) { Form.new(nil, 2024, [], "sales") } + + context "when routing" do + before do + allow(log).to receive(:form).and_return(form) + end + + context "when privacy notice seen and not outright sale" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: nil, staircase: 2, ownershipsch: 1, buylivein: nil) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when buyer not interviewed and not outright sale" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 1, staircase: 2, ownershipsch: 1, buylivein: nil) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when privacy notice seen, joint purchase and live in" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 2, ownershipsch: 1, buylivein: 1) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when buyer not interviewed, joint purchase and live in" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 1, staircase: 2, ownershipsch: 1, buylivein: 1) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when buyer has seen privacy notice and buyer interviewed and it's outright sale" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 2, ownershipsch: 3, buylivein: nil) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + + context "and buyer has not seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 0, staircase: 2, ownershipsch: 1, buylivein: nil) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + end + end + + context "with year 2025" do + let(:form) { Form.new(nil, 2025, [], "sales") } + + before do + allow(log).to receive(:form).and_return(form) + end + + context "when routing" do + context "and staircase is not 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 1, staircase: 2, ownershipsch: 1, buylivein: nil) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "and staircase is 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 1, ownershipsch: 1, buylivein: nil, staircase: 1) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + end end end diff --git a/spec/models/form/sales/pages/buyer2_nationality_spec.rb b/spec/models/form/sales/pages/buyer2_nationality_spec.rb index e01215d21..fa9270ec7 100644 --- a/spec/models/form/sales/pages/buyer2_nationality_spec.rb +++ b/spec/models/form/sales/pages/buyer2_nationality_spec.rb @@ -3,13 +3,8 @@ require "rails_helper" RSpec.describe Form::Sales::Pages::Buyer2Nationality, type: :model do subject(:page) { described_class.new(nil, nil, subsection) } - let(:subsection) { instance_double(Form::Subsection) } - let(:form) { instance_double(Form, start_date: Time.zone.local(2023, 4, 1)) } - - before do - allow(subsection).to receive(:form).and_return(form) - allow(form).to receive(:start_year_2024_or_later?).and_return(false) - end + let(:form) { Form.new(nil, 2023, [], "sales") } + let(:subsection) { instance_double(Form::Subsection, form:, enabled?: true, depends_on: nil) } it "has correct subsection" do expect(page.subsection).to be subsection @@ -27,26 +22,83 @@ RSpec.describe Form::Sales::Pages::Buyer2Nationality, type: :model do expect(page.description).to be_nil end - it "has correct depends_on" do - expect(page.depends_on).to eq [ - { - "joint_purchase?" => true, - "buyer_has_seen_privacy_notice?" => true, - }, - { - "joint_purchase?" => true, - "buyer_not_interviewed?" => true, - }, - ] + context "with year 2024" do + let(:form) { Form.new(nil, 2024, [], "sales") } + + it "has correct questions" do + expect(page.questions.map(&:id)).to eq %w[nationality_all_buyer2_group nationality_all_buyer2] + end + + context "when routing" do + before do + allow(log).to receive(:form).and_return(form) + end + + context "when buyer has seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when buyer has seen privacy notice and buyer not interviewed" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 1, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "and buyer has not seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 0, staircase: 2) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + + context "and buyer has not seen privacy notice and buyer not interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 1, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when it's not a joint purchase" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 2, noint: 1, staircase: 2) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + end end - context "with year 2024" do + context "with year 2025" do + let(:form) { Form.new(nil, 2025, [], "sales") } + before do - allow(form).to receive(:start_year_2024_or_later?).and_return(true) + allow(log).to receive(:form).and_return(form) end - it "has correct questions" do - expect(page.questions.map(&:id)).to eq %w[nationality_all_buyer2_group nationality_all_buyer2] + context "when routing" do + context "and staircase is not 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "and staircase is 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 1) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end end end end diff --git a/spec/models/form/sales/pages/buyer2_working_situation_spec.rb b/spec/models/form/sales/pages/buyer2_working_situation_spec.rb index 6c9915903..f0022006e 100644 --- a/spec/models/form/sales/pages/buyer2_working_situation_spec.rb +++ b/spec/models/form/sales/pages/buyer2_working_situation_spec.rb @@ -5,7 +5,8 @@ RSpec.describe Form::Sales::Pages::Buyer2WorkingSituation, type: :model do let(:page_id) { nil } let(:page_definition) { nil } - let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_2025_or_later?: false)) } + let(:form) { Form.new(nil, 2023, [], "sales") } + let(:subsection) { instance_double(Form::Subsection, form:, depends_on: nil) } it "has correct subsection" do expect(page.subsection).to eq(subsection) @@ -23,16 +24,79 @@ RSpec.describe Form::Sales::Pages::Buyer2WorkingSituation, type: :model do expect(page.description).to be_nil end - it "has correct depends_on" do - expect(page.depends_on).to eq([ - { - "joint_purchase?" => true, - "buyer_has_seen_privacy_notice?" => true, - }, - { - "joint_purchase?" => true, - "buyer_not_interviewed?" => true, - }, - ]) + context "with year 2024" do + let(:form) { Form.new(nil, 2024, [], "sales") } + + context "when routing" do + before do + allow(log).to receive(:form).and_return(form) + end + + context "when buyer has seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when buyer has seen privacy notice and buyer not interviewed" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 1, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "and buyer has not seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 0, staircase: 2) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + + context "and buyer has not seen privacy notice and buyer not interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 1, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when it's not a joint purchase" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 2, noint: 1, staircase: 2) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + end + end + + context "with year 2025" do + let(:form) { Form.new(nil, 2025, [], "sales") } + + before do + allow(log).to receive(:form).and_return(form) + end + + context "when routing" do + context "and staircase is not 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "and staircase is 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 1) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + end end end diff --git a/spec/models/form/sales/pages/number_of_others_in_property_spec.rb b/spec/models/form/sales/pages/number_of_others_in_property_spec.rb index 859aff8e0..b4e5f8ea9 100644 --- a/spec/models/form/sales/pages/number_of_others_in_property_spec.rb +++ b/spec/models/form/sales/pages/number_of_others_in_property_spec.rb @@ -6,7 +6,8 @@ RSpec.describe Form::Sales::Pages::NumberOfOthersInProperty, type: :model do let(:page_id) { "number_of_others_in_property" } let(:page_definition) { nil } let(:joint_purchase) { false } - let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1))) } + let(:form) { Form.new(nil, 2024, [], "sales") } + let(:subsection) { instance_double(Form::Subsection, form:, enabled?: true, depends_on: nil) } it "has correct subsection" do expect(page.subsection).to eq(subsection) @@ -24,19 +25,6 @@ RSpec.describe Form::Sales::Pages::NumberOfOthersInProperty, type: :model do expect(page.description).to be_nil end - it "has the correct depends_on" do - expect(page.depends_on).to eq([ - { - "buyer_has_seen_privacy_notice?" => true, - "joint_purchase?" => joint_purchase, - }, - { - "buyer_not_interviewed?" => true, - "joint_purchase?" => joint_purchase, - }, - ]) - end - context "with joint purchase" do let(:page_id) { "number_of_others_in_property_joint_purchase" } let(:joint_purchase) { true } @@ -45,17 +33,100 @@ RSpec.describe Form::Sales::Pages::NumberOfOthersInProperty, type: :model do expect(page.id).to eq("number_of_others_in_property_joint_purchase") end - it "has the correct depends_on" do - expect(page.depends_on).to eq([ - { - "buyer_has_seen_privacy_notice?" => true, - "joint_purchase?" => joint_purchase, - }, - { - "buyer_not_interviewed?" => true, - "joint_purchase?" => joint_purchase, - }, - ]) + context "when routing" do + before do + allow(log).to receive(:form).and_return(form) + end + + context "with 2024 logs" do + context "with joint purchase" do + context "when buyer has seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "when buyer has seen privacy notice and buyer not interviewed" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 1) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "and buyer has not seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 0) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + + context "and buyer has not seen privacy notice and buyer not interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 1, noint: 1) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + end + + context "with non joint purchase" do + context "when buyer has seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 2, noint: 0) } + + it "routes to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + + context "when buyer has seen privacy notice and buyer not interviewed" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 2, noint: 1) } + + it "routes to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + + context "and buyer has not seen privacy notice and buyer interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 2, noint: 0) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + + context "and buyer has not seen privacy notice and buyer not interviewed" do + let(:log) { build(:sales_log, privacynotice: nil, jointpur: 2, noint: 1) } + + it "routes to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + end + end + + context "with 2025 logs" do + let(:form) { Form.new(nil, 2025, [], "sales") } + + context "and staircase is not 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 2) } + + it "routes to the page" do + expect(page.routed_to?(log, nil)).to eq(true) + end + end + + context "and staircase is 1" do + let(:log) { build(:sales_log, privacynotice: 1, jointpur: 1, noint: 0, staircase: 1) } + + it "does not route to the page" do + expect(page).not_to be_routed_to(log, nil) + end + end + end end end end From 16393cf4ab77f6ff88069c33afd5d3f72f1e82eb Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Thu, 14 Nov 2024 08:57:06 +0000 Subject: [PATCH 06/34] Trigger income error on uprn_selection (#2771) --- app/models/validations/sales/financial_validations.rb | 4 ++-- config/locales/validations/sales/financial.en.yml | 4 ++++ .../validations/sales/financial_validations_spec.rb | 8 ++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/models/validations/sales/financial_validations.rb b/app/models/validations/sales/financial_validations.rb index 1d2ef3d6f..a2cb2170f 100644 --- a/app/models/validations/sales/financial_validations.rb +++ b/app/models/validations/sales/financial_validations.rb @@ -5,7 +5,7 @@ module Validations::Sales::FinancialValidations def validate_income1(record) return unless record.income1 && record.la && record.shared_ownership_scheme? - relevant_fields = %i[income1 ownershipsch uprn la postcode_full] + relevant_fields = %i[income1 ownershipsch uprn la postcode_full uprn_selection] if record.london_property? && !record.income1.between?(0, 90_000) relevant_fields.each { |field| record.errors.add field, :outside_london_income_range, message: I18n.t("validations.sales.financial.#{field}.outside_london_income_range") } elsif record.property_not_in_london? && !record.income1.between?(0, 80_000) @@ -16,7 +16,7 @@ module Validations::Sales::FinancialValidations def validate_income2(record) return unless record.income2 && record.la && record.shared_ownership_scheme? - relevant_fields = %i[income2 ownershipsch uprn la postcode_full] + relevant_fields = %i[income2 ownershipsch uprn la postcode_full uprn_selection] if record.london_property? && !record.income2.between?(0, 90_000) relevant_fields.each { |field| record.errors.add field, :outside_london_income_range, message: I18n.t("validations.sales.financial.#{field}.outside_london_income_range") } elsif record.property_not_in_london? && !record.income2.between?(0, 80_000) diff --git a/config/locales/validations/sales/financial.en.yml b/config/locales/validations/sales/financial.en.yml index efd257ea5..55d563ee3 100644 --- a/config/locales/validations/sales/financial.en.yml +++ b/config/locales/validations/sales/financial.en.yml @@ -79,3 +79,7 @@ en: not_joint_purchase: "The initial equity stake is %{equity}% and the percentage owned in total minus the percentage bought is %{staircase_difference}%. In a staircasing transaction, the equity stake purchased cannot be larger than the percentage the buyer owns minus the percentage bought." percentage_bought_must_be_at_least_threshold: "The minimum increase in equity while staircasing is %{threshold}%." percentage_bought_equal_percentage_owned: "The percentage bought is %{stairbought}% and the percentage owned in total is %{stairowned}%. These figures cannot be the same." + + uprn_selection: + outside_london_income_range: "Income must be between £0 and £90,000 for properties within a London local authority." + outside_non_london_income_range: "Income must be between £0 and £80,000 for properties in a non-London local authority." diff --git a/spec/models/validations/sales/financial_validations_spec.rb b/spec/models/validations/sales/financial_validations_spec.rb index cb55615c0..f579ae27e 100644 --- a/spec/models/validations/sales/financial_validations_spec.rb +++ b/spec/models/validations/sales/financial_validations_spec.rb @@ -20,6 +20,7 @@ RSpec.describe Validations::Sales::FinancialValidations do expect(record.errors["ownershipsch"]).to include(match I18n.t("validations.sales.financial.ownershipsch.outside_non_london_income_range")) expect(record.errors["la"]).to include(match I18n.t("validations.sales.financial.la.outside_non_london_income_range")) expect(record.errors["postcode_full"]).to include(match I18n.t("validations.sales.financial.postcode_full.outside_non_london_income_range")) + expect(record.errors["uprn_selection"]).to include(match I18n.t("validations.sales.financial.uprn_selection.outside_non_london_income_range")) end it "adds errors if buyer 2 has income over 80,000" do @@ -29,6 +30,7 @@ RSpec.describe Validations::Sales::FinancialValidations do expect(record.errors["ownershipsch"]).to include(match I18n.t("validations.sales.financial.ownershipsch.outside_non_london_income_range")) expect(record.errors["la"]).to include(match I18n.t("validations.sales.financial.la.outside_non_london_income_range")) expect(record.errors["postcode_full"]).to include(match I18n.t("validations.sales.financial.postcode_full.outside_non_london_income_range")) + expect(record.errors["uprn_selection"]).to include(match I18n.t("validations.sales.financial.uprn_selection.outside_non_london_income_range")) end it "does not add errors if buyer 1 has income above 0 and below 80_000" do @@ -50,6 +52,7 @@ RSpec.describe Validations::Sales::FinancialValidations do expect(record.errors["ownershipsch"]).to include(match I18n.t("validations.sales.financial.ownershipsch.outside_non_london_income_range")) expect(record.errors["la"]).to include(match I18n.t("validations.sales.financial.la.outside_non_london_income_range")) expect(record.errors["postcode_full"]).to include(match I18n.t("validations.sales.financial.postcode_full.outside_non_london_income_range")) + expect(record.errors["uprn_selection"]).to include(match I18n.t("validations.sales.financial.uprn_selection.outside_non_london_income_range")) end it "adds errors if buyer 2 has income below 0" do @@ -59,6 +62,7 @@ RSpec.describe Validations::Sales::FinancialValidations do expect(record.errors["ownershipsch"]).to include(match I18n.t("validations.sales.financial.ownershipsch.outside_non_london_income_range")) expect(record.errors["la"]).to include(match I18n.t("validations.sales.financial.la.outside_non_london_income_range")) expect(record.errors["postcode_full"]).to include(match I18n.t("validations.sales.financial.postcode_full.outside_non_london_income_range")) + expect(record.errors["uprn_selection"]).to include(match I18n.t("validations.sales.financial.uprn_selection.outside_non_london_income_range")) end it "adds errors when combined income is over 80_000" do @@ -89,6 +93,7 @@ RSpec.describe Validations::Sales::FinancialValidations do expect(record.errors["ownershipsch"]).to include(match I18n.t("validations.sales.financial.ownershipsch.outside_london_income_range")) expect(record.errors["la"]).to include(match I18n.t("validations.sales.financial.la.outside_london_income_range")) expect(record.errors["postcode_full"]).to include(match I18n.t("validations.sales.financial.postcode_full.outside_london_income_range")) + expect(record.errors["uprn_selection"]).to include(match I18n.t("validations.sales.financial.uprn_selection.outside_london_income_range")) end it "adds errors if buyer 2 has income over 90,000" do @@ -98,6 +103,7 @@ RSpec.describe Validations::Sales::FinancialValidations do expect(record.errors["ownershipsch"]).to include(match I18n.t("validations.sales.financial.ownershipsch.outside_london_income_range")) expect(record.errors["la"]).to include(match I18n.t("validations.sales.financial.la.outside_london_income_range")) expect(record.errors["postcode_full"]).to include(match I18n.t("validations.sales.financial.postcode_full.outside_london_income_range")) + expect(record.errors["uprn_selection"]).to include(match I18n.t("validations.sales.financial.uprn_selection.outside_london_income_range")) end it "does not add errors if buyer 1 has income above 0 and below 90_000" do @@ -119,6 +125,7 @@ RSpec.describe Validations::Sales::FinancialValidations do expect(record.errors["ownershipsch"]).to include(match I18n.t("validations.sales.financial.ownershipsch.outside_london_income_range")) expect(record.errors["la"]).to include(match I18n.t("validations.sales.financial.la.outside_london_income_range")) expect(record.errors["postcode_full"]).to include(match I18n.t("validations.sales.financial.postcode_full.outside_london_income_range")) + expect(record.errors["uprn_selection"]).to include(match I18n.t("validations.sales.financial.uprn_selection.outside_london_income_range")) end it "adds errors if buyer 2 has income below 0" do @@ -128,6 +135,7 @@ RSpec.describe Validations::Sales::FinancialValidations do expect(record.errors["ownershipsch"]).to include(match I18n.t("validations.sales.financial.ownershipsch.outside_london_income_range")) expect(record.errors["la"]).to include(match I18n.t("validations.sales.financial.la.outside_london_income_range")) expect(record.errors["postcode_full"]).to include(match I18n.t("validations.sales.financial.postcode_full.outside_london_income_range")) + expect(record.errors["uprn_selection"]).to include(match I18n.t("validations.sales.financial.uprn_selection.outside_london_income_range")) end it "adds errors when combined income is over 90_000" do From c8a36fd3a6edaf0164bf6678cdc5d0b623186daa Mon Sep 17 00:00:00 2001 From: Manny Dinssa <44172848+Dinssa@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:07:34 +0000 Subject: [PATCH 07/34] Move sales staircasing question (#2777) * Change order * Update test --- app/models/form/sales/subsections/setup.rb | 2 +- spec/models/form/sales/subsections/setup_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/form/sales/subsections/setup.rb b/app/models/form/sales/subsections/setup.rb index 0c667ebda..be6c20400 100644 --- a/app/models/form/sales/subsections/setup.rb +++ b/app/models/form/sales/subsections/setup.rb @@ -13,8 +13,8 @@ class Form::Sales::Subsections::Setup < ::Form::Subsection Form::Sales::Pages::CreatedBy.new(nil, nil, self), Form::Sales::Pages::PurchaserCode.new(nil, nil, self), Form::Sales::Pages::OwnershipScheme.new(nil, nil, self), - (Form::Sales::Pages::Staircase.new(nil, nil, self) if form.start_year_2025_or_later?), Form::Sales::Pages::SharedOwnershipType.new(nil, nil, self), + (Form::Sales::Pages::Staircase.new(nil, nil, self) if form.start_year_2025_or_later?), Form::Sales::Pages::DiscountedOwnershipType.new(nil, nil, self), (Form::Sales::Pages::OutrightOwnershipType.new(nil, nil, self) unless form.start_year_2025_or_later?), (Form::Sales::Pages::BuyerCompany.new(nil, nil, self) unless form.start_year_2025_or_later?), diff --git a/spec/models/form/sales/subsections/setup_spec.rb b/spec/models/form/sales/subsections/setup_spec.rb index ad01ccd41..f93e7ef5e 100644 --- a/spec/models/form/sales/subsections/setup_spec.rb +++ b/spec/models/form/sales/subsections/setup_spec.rb @@ -93,8 +93,8 @@ RSpec.describe Form::Sales::Subsections::Setup, type: :model do assigned_to purchaser_code ownership_scheme - staircasing shared_ownership_type + staircasing discounted_ownership_type joint_purchase number_joint_buyers From de1d28c27e2b1a065f24a1275f054aecdf8a252d Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:46:52 +0000 Subject: [PATCH 08/34] Increase soft max for old persons shared ownership savings (#2778) --- .../validations/sales/soft_validations.rb | 4 ++- .../sales/soft_validations_spec.rb | 27 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/app/models/validations/sales/soft_validations.rb b/app/models/validations/sales/soft_validations.rb index fe05ec13c..5095e5ff9 100644 --- a/app/models/validations/sales/soft_validations.rb +++ b/app/models/validations/sales/soft_validations.rb @@ -58,7 +58,9 @@ module Validations::Sales::SoftValidations end def savings_over_soft_max? - savings && savings > 100_000 + soft_max = form.start_year_2025_or_later? && type == 24 ? 200_000 : 100_000 + + savings && savings > soft_max end def deposit_over_soft_max? diff --git a/spec/models/validations/sales/soft_validations_spec.rb b/spec/models/validations/sales/soft_validations_spec.rb index c8e8618fc..99c615250 100644 --- a/spec/models/validations/sales/soft_validations_spec.rb +++ b/spec/models/validations/sales/soft_validations_spec.rb @@ -371,6 +371,33 @@ RSpec.describe Validations::Sales::SoftValidations do expect(record) .not_to be_mortgage_over_soft_max end + + context "with log for 2025 of after" do + before do + record.saledate = Time.zone.local(2025, 5, 1) + end + + it "allows savings over 100_000 for old persons shared ownership" do + record.savings = 100_001 + record.type = 24 + expect(record) + .not_to be_savings_over_soft_max + end + + it "does not allows savings over 200_000 for old persons shared ownership" do + record.savings = 200_001 + record.type = 24 + expect(record) + .to be_savings_over_soft_max + end + + it "does not allows savings over 100_000 for other type" do + record.savings = 100_001 + record.type = 8 + expect(record) + .to be_savings_over_soft_max + end + end end end From 4f3526c447889a1c9d6c4f67d4b7ee69ff8ef3c6 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Mon, 18 Nov 2024 15:39:24 +0000 Subject: [PATCH 09/34] Update hardcoded postgres version in docker (#2787) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 46bd31b5f..74faebfd8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ RUN apk add --update --no-cache tzdata && \ # build-base: compilation tools for bundle # yarn: node package manager # postgresql-dev: postgres driver and libraries -RUN apk add --no-cache build-base=0.5-r3 busybox=1.36.1-r7 nodejs-current=20.8.1-r0 yarn=1.22.19-r0 postgresql13-dev=13.16-r0 git=2.40.3-r0 bash=5.2.15-r5 +RUN apk add --no-cache build-base=0.5-r3 busybox=1.36.1-r7 nodejs-current=20.8.1-r0 yarn=1.22.19-r0 postgresql13-dev=13.17-r0 git=2.40.3-r0 bash=5.2.15-r5 # Bundler version should be the same version as what the Gemfile.lock was bundled with RUN gem install bundler:2.3.14 --no-document From aeae0c1806c9ed4a80a1b3d17243028a533fe120 Mon Sep 17 00:00:00 2001 From: Rachael Booth Date: Mon, 18 Nov 2024 16:18:29 +0000 Subject: [PATCH 10/34] CLDC-3766: Ensure copy for sales Q78 is found correctly for 2024 logs (#2790) * CLDC-3766: Ensure copy for Q78 is found correctly * Fix tests --- app/models/form/sales/questions/staircase.rb | 2 +- app/models/form/sales/subsections/shared_ownership_scheme.rb | 1 + app/models/form/subsection.rb | 4 ++++ spec/models/form/sales/pages/staircase_spec.rb | 4 ++-- spec/models/form/sales/questions/staircase_spec.rb | 4 ++-- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/models/form/sales/questions/staircase.rb b/app/models/form/sales/questions/staircase.rb index 6c98397ff..d61456a8e 100644 --- a/app/models/form/sales/questions/staircase.rb +++ b/app/models/form/sales/questions/staircase.rb @@ -2,7 +2,7 @@ class Form::Sales::Questions::Staircase < ::Form::Question def initialize(id, hsh, page) super @id = "staircase" - @copy_key = "sales.#{page.subsection.id}.staircasing" + @copy_key = "sales.#{page.subsection.copy_key}.staircasing" @type = "radio" @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] end diff --git a/app/models/form/sales/subsections/shared_ownership_scheme.rb b/app/models/form/sales/subsections/shared_ownership_scheme.rb index 455755e31..f5d52153e 100644 --- a/app/models/form/sales/subsections/shared_ownership_scheme.rb +++ b/app/models/form/sales/subsections/shared_ownership_scheme.rb @@ -4,6 +4,7 @@ class Form::Sales::Subsections::SharedOwnershipScheme < ::Form::Subsection @id = "shared_ownership_scheme" @label = "Shared ownership scheme" @depends_on = [{ "ownershipsch" => 1, "setup_completed?" => true }] + @copy_key = "sale_information" end def pages diff --git a/app/models/form/subsection.rb b/app/models/form/subsection.rb index 25a473e98..3b74e29a5 100644 --- a/app/models/form/subsection.rb +++ b/app/models/form/subsection.rb @@ -14,6 +14,10 @@ class Form::Subsection delegate :form, to: :section + def copy_key + @copy_key ||= @id + end + def questions @questions ||= pages.flat_map(&:questions) end diff --git a/spec/models/form/sales/pages/staircase_spec.rb b/spec/models/form/sales/pages/staircase_spec.rb index 3c26181c2..6ce969ec2 100644 --- a/spec/models/form/sales/pages/staircase_spec.rb +++ b/spec/models/form/sales/pages/staircase_spec.rb @@ -8,7 +8,7 @@ RSpec.describe Form::Sales::Pages::Staircase, type: :model do let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1))) } context "when start year is 2024" do - let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1)), id: "sale_information") } + let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1)), id: "shared_ownership_scheme", copy_key: "sale_information") } before do allow(subsection.form).to receive(:start_year_2025_or_later?).and_return(false) @@ -32,7 +32,7 @@ RSpec.describe Form::Sales::Pages::Staircase, type: :model do end context "when start year is >= 2025" do - let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1)), id: "setup") } + let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1)), id: "setup", copy_key: "setup") } before do allow(subsection.form).to receive(:start_year_2025_or_later?).and_return(true) diff --git a/spec/models/form/sales/questions/staircase_spec.rb b/spec/models/form/sales/questions/staircase_spec.rb index 29ba34d31..c9a9f44d8 100644 --- a/spec/models/form/sales/questions/staircase_spec.rb +++ b/spec/models/form/sales/questions/staircase_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Form::Sales::Questions::Staircase, type: :model do let(:question_definition) { nil } context "when start year is 2024" do - let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2024, 4, 1)), id: "sale_information")) } + let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2024, 4, 1)), id: "shared_ownership_scheme", copy_key: "sale_information")) } before do allow(page.subsection.form).to receive(:start_year_2025_or_later?).and_return(false) @@ -43,7 +43,7 @@ RSpec.describe Form::Sales::Questions::Staircase, type: :model do end context "when start year is 2025" do - let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1)), id: "setup")) } + let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1)), id: "setup", copy_key: "setup")) } before do allow(page.subsection.form).to receive(:start_year_2025_or_later?).and_return(true) From f0603c35dda68f1e9b444de3c49038df3799dd95 Mon Sep 17 00:00:00 2001 From: Rachael Booth Date: Mon, 18 Nov 2024 17:33:37 +0000 Subject: [PATCH 11/34] CLDC-3705: Rework referrers and back links for scheme creation (#2775) * CLDC-3705: Rework referrers and back links for scheme creation * Fix typo on primary client group page * Only direct via secondary client group page when newly required * Update hardcoded postgres version in docker --------- Co-authored-by: Kat <54268893+kosiakkatrina@users.noreply.github.com> --- app/controllers/schemes_controller.rb | 12 +++--- app/helpers/schemes_helper.rb | 10 ++--- app/views/schemes/confirm_secondary.html.erb | 6 +-- app/views/schemes/details.html.erb | 2 +- app/views/schemes/edit_name.html.erb | 2 +- .../schemes/primary_client_group.html.erb | 8 ++-- .../schemes/secondary_client_group.html.erb | 6 +-- app/views/schemes/support.html.erb | 6 +-- spec/features/schemes_spec.rb | 27 +++++------- spec/requests/schemes_controller_spec.rb | 42 +++++++++++++++++-- 10 files changed, 71 insertions(+), 50 deletions(-) diff --git a/app/controllers/schemes_controller.rb b/app/controllers/schemes_controller.rb index 1468bc013..be5d3f7ef 100644 --- a/app/controllers/schemes_controller.rb +++ b/app/controllers/schemes_controller.rb @@ -145,8 +145,8 @@ class SchemesController < ApplicationController if @scheme.errors.empty? && @scheme.update(scheme_params) @scheme.update!(secondary_client_group: nil) if @scheme.has_other_client_group == "No" if scheme_params[:confirmed] == "true" || @scheme.confirmed? - if check_answers && confirm_secondary_page?(page) - redirect_to scheme_secondary_client_group_path(@scheme, check_answers: "true") + if check_answers && should_direct_via_secondary_client_group_page?(page) + redirect_to scheme_secondary_client_group_path(@scheme, referrer: "check-answers") else @scheme.locations.update!(confirmed: true) flash[:notice] = if scheme_previously_confirmed @@ -157,8 +157,8 @@ class SchemesController < ApplicationController redirect_to scheme_path(@scheme) end elsif check_answers - if confirm_secondary_page?(page) - redirect_to scheme_secondary_client_group_path(@scheme, check_answers: "true") + if should_direct_via_secondary_client_group_page?(page) + redirect_to scheme_secondary_client_group_path(@scheme, referrer: "check-answers") else redirect_to scheme_check_answers_path(@scheme) end @@ -249,8 +249,8 @@ private end end - def confirm_secondary_page?(page) - page == "confirm-secondary" && @scheme.has_other_client_group == "Yes" + def should_direct_via_secondary_client_group_page?(page) + page == "confirm-secondary" && @scheme.has_other_client_group == "Yes" && @scheme.secondary_client_group.nil? end def current_template(page) diff --git a/app/helpers/schemes_helper.rb b/app/helpers/schemes_helper.rb index 7efb9fffd..bcd40b082 100644 --- a/app/helpers/schemes_helper.rb +++ b/app/helpers/schemes_helper.rb @@ -64,15 +64,15 @@ module SchemesHelper def change_answer_link(scheme, question_id, user) case question_id when "service_name", "sensitive", "scheme_type", "registered_under_care_act", "owning_organisation_id", "arrangement_type" - user.support? || !scheme.confirmed? ? scheme_details_path(scheme, check_answers: true) : scheme_edit_name_path(scheme) + user.support? || !scheme.confirmed? ? scheme_details_path(scheme, referrer: "check-answers") : scheme_edit_name_path(scheme) when "primary_client_group" - scheme_primary_client_group_path(scheme, check_answers: true) + scheme_primary_client_group_path(scheme, referrer: "check-answers") when "has_other_client_group" - scheme_confirm_secondary_client_group_path(scheme, check_answers: true) + scheme_confirm_secondary_client_group_path(scheme, referrer: "check-answers") when "secondary_client_group" - scheme_secondary_client_group_path(scheme, check_answers: true) + scheme_secondary_client_group_path(scheme, referrer: "check-answers") when "support_type", "intended_stay" - scheme_support_path(scheme, check_answers: true) + scheme_support_path(scheme, referrer: "check-answers") end end diff --git a/app/views/schemes/confirm_secondary.html.erb b/app/views/schemes/confirm_secondary.html.erb index 7e7cfeebf..9b715c264 100644 --- a/app/views/schemes/confirm_secondary.html.erb +++ b/app/views/schemes/confirm_secondary.html.erb @@ -1,9 +1,7 @@ <% content_for :title, "Does this scheme provide for another client group?" %> <% content_for :before_content do %> - <%= govuk_back_link( - href: request.query_parameters["check_answers"] ? "check-answers" : "primary-client-group", - ) %> + <%= govuk_back_link(href: :back) %> <% end %> <%= render partial: "organisations/headings", locals: { main: "Does this scheme provide for another client group?", sub: @scheme.service_name } %> @@ -22,7 +20,7 @@ legend: nil %> <%= f.hidden_field :page, value: "confirm-secondary" %> - <% if request.query_parameters["check_answers"] == "true" %> + <% if params[:referrer] == "check-answers" %> <%= f.hidden_field :check_answers, value: "true" %> <%= f.govuk_submit "Save changes" %> <% else %> diff --git a/app/views/schemes/details.html.erb b/app/views/schemes/details.html.erb index 71f924782..cb29a56dc 100644 --- a/app/views/schemes/details.html.erb +++ b/app/views/schemes/details.html.erb @@ -75,7 +75,7 @@ legend: { text: "Who provides the support services used by this scheme?", size: "m" } %> <%= f.hidden_field :page, value: "details" %> - <% if request.query_parameters["check_answers"] %> + <% if params[:referrer] == "check-answers" %> <%= f.hidden_field :check_answers, value: "true" %> <%= f.govuk_submit "Save changes" %> <% else %> diff --git a/app/views/schemes/edit_name.html.erb b/app/views/schemes/edit_name.html.erb index 030635b79..2cb0f791a 100644 --- a/app/views/schemes/edit_name.html.erb +++ b/app/views/schemes/edit_name.html.erb @@ -39,7 +39,7 @@ <%= f.hidden_field :page, value: "edit-name" %> - <% if request.query_parameters["check_answers"] %> + <% if params[:referrer] == "check-answers" %> <%= f.hidden_field :check_answers, value: "true" %> <% end %> diff --git a/app/views/schemes/primary_client_group.html.erb b/app/views/schemes/primary_client_group.html.erb index 85db05b55..55dc504b4 100644 --- a/app/views/schemes/primary_client_group.html.erb +++ b/app/views/schemes/primary_client_group.html.erb @@ -1,9 +1,9 @@ <% content_for :title, "What client group is this scheme intended for?" %> -<% if request.referer&.include?("new") || request.referer&.include?("details") %> +<% if request.referer&.include?("new") %> <% back_button_path = scheme_details_path(@scheme) %> -<% elsif request.query_parameters["check_answers"] %> - <% back_button_path = scheme_check_answers_path(@scheme) %> +<% else %> + <% back_button_path = :back %> <% end %> <% content_for :before_content do %> @@ -30,7 +30,7 @@ <%= f.hidden_field :check_answers, value: "true" %> <% end %> - <% if request.query_parameters["check_answers"] == "true" %> + <% if params[:referrer] == "check-answers" %> <%= f.hidden_field :check_answers, value: "true" %> <%= f.govuk_submit "Save changes" %> <% else %> diff --git a/app/views/schemes/secondary_client_group.html.erb b/app/views/schemes/secondary_client_group.html.erb index 1e70fd92b..cd50283e3 100644 --- a/app/views/schemes/secondary_client_group.html.erb +++ b/app/views/schemes/secondary_client_group.html.erb @@ -1,9 +1,7 @@ <% content_for :title, "What is the other client group?" %> <% content_for :before_content do %> - <%= govuk_back_link( - href: request.query_parameters["check_answers"] ? "check-answers" : "confirm-secondary-client-group", - ) %> + <%= govuk_back_link(href: :back) %> <% end %> <%= form_for(@scheme, method: :patch) do |f| %> @@ -22,7 +20,7 @@ legend: nil %> <%= f.hidden_field :page, value: "secondary-client-group" %> - <% if request.query_parameters["check_answers"] == "true" %> + <% if params[:referrer] == "check-answers" %> <%= f.hidden_field :check_answers, value: "true" %> <%= f.govuk_submit "Save changes" %> <% else %> diff --git a/app/views/schemes/support.html.erb b/app/views/schemes/support.html.erb index a004a3da9..a5d30ed7f 100644 --- a/app/views/schemes/support.html.erb +++ b/app/views/schemes/support.html.erb @@ -1,9 +1,7 @@ <% content_for :title, "What support does this scheme provide?" %> <% content_for :before_content do %> - <%= govuk_back_link( - href: request.query_parameters["check_answers"] ? "check-answers" : "secondary-client-group", - ) %> + <%= govuk_back_link(href: :back) %> <% end %> <%= form_for(@scheme, method: :patch) do |f| %> @@ -38,7 +36,7 @@ <%= f.hidden_field :page, value: "support" %> - <% if request.query_parameters["check_answers"] == "true" %> + <% if params[:referrer] == "check-answers" %> <%= f.hidden_field :check_answers, value: "true" %> <%= f.govuk_submit "Save changes" %> <% else %> diff --git a/spec/features/schemes_spec.rb b/spec/features/schemes_spec.rb index 0706a0d7e..33ab00b34 100644 --- a/spec/features/schemes_spec.rb +++ b/spec/features/schemes_spec.rb @@ -644,8 +644,8 @@ RSpec.describe "Schemes scheme Features" do end it "allows changing details questions" do - click_link("Change", href: "/schemes/#{scheme.id}/details?check_answers=true", match: :first) - expect(page).to have_current_path("/schemes/#{scheme.id}/details?check_answers=true") + click_link("Change", href: "/schemes/#{scheme.id}/details?referrer=check-answers", match: :first) + expect(page).to have_current_path("/schemes/#{scheme.id}/details?referrer=check-answers") fill_in "Scheme name", with: "Example" click_button "Save changes" @@ -655,14 +655,14 @@ RSpec.describe "Schemes scheme Features" do end it "lets me select the support answers after navigating back" do - click_link("Change", href: "/schemes/#{scheme.id}/details?check_answers=true", match: :first) + click_link("Change", href: "/schemes/#{scheme.id}/details?referrer=check-answers", match: :first) click_link "Back" expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers") expect(page).to have_content "Check your changes before creating this scheme" end it "indicates if the scheme is not complete" do - click_link("Change", href: "/schemes/#{scheme.id}/confirm-secondary-client-group?check_answers=true", match: :first) + click_link("Change", href: "/schemes/#{scheme.id}/confirm-secondary-client-group?referrer=check-answers", match: :first) choose "Yes" click_button "Save changes" visit("/schemes/#{scheme.id}/check-answers") @@ -710,8 +710,8 @@ RSpec.describe "Schemes scheme Features" do end it "allows changing details questions" do - click_link("Change", href: "/schemes/#{scheme.id}/details?check_answers=true", match: :first) - expect(page).to have_current_path("/schemes/#{scheme.id}/details?check_answers=true") + click_link("Change", href: "/schemes/#{scheme.id}/details?referrer=check-answers", match: :first) + expect(page).to have_current_path("/schemes/#{scheme.id}/details?referrer=check-answers") fill_in "Scheme name", with: "Example" click_button "Save changes" @@ -721,21 +721,14 @@ RSpec.describe "Schemes scheme Features" do end it "lets me select the support answers after navigating back" do - click_link("Change", href: "/schemes/#{scheme.id}/details?check_answers=true", match: :first) + click_link("Change", href: "/schemes/#{scheme.id}/details?referrer=check-answers", match: :first) click_link "Back" expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers") expect(page).to have_content "Check your changes before creating this scheme" end - it "keeps the provider answer when switching between other provider options" do - click_link("Change", href: "/schemes/#{scheme.id}/confirm-secondary-client-group?check_answers=true", match: :first) - choose "Yes" - click_button "Save changes" - expect(find_field("Offenders and people at risk of offending")).to be_checked - end - it "does not display the answer if it's changed to the same support provider" do - click_link("Change", href: "/schemes/#{scheme.id}/details?check_answers=true", match: :first) + click_link("Change", href: "/schemes/#{scheme.id}/details?referrer=check-answers", match: :first) choose "The same organisation that owns the housing stock" click_button "Save changes" expect(page).not_to have_content("Organisation providing support") @@ -787,11 +780,11 @@ RSpec.describe "Schemes scheme Features" do context "when I click to change scheme name" do before do - click_link("Change", href: "/schemes/#{scheme.id}/details?check_answers=true", match: :first) + click_link("Change", href: "/schemes/#{scheme.id}/details?referrer=check-answers", match: :first) end it "shows available fields to edit" do - expect(page).to have_current_path("/schemes/#{scheme.id}/details?check_answers=true") + expect(page).to have_current_path("/schemes/#{scheme.id}/details?referrer=check-answers") expect(page).to have_content "Scheme details" end diff --git a/spec/requests/schemes_controller_spec.rb b/spec/requests/schemes_controller_spec.rb index d93bc873d..19ede5cc4 100644 --- a/spec/requests/schemes_controller_spec.rb +++ b/spec/requests/schemes_controller_spec.rb @@ -1405,10 +1405,11 @@ RSpec.describe SchemesController, type: :request do expect(scheme_to_update.reload.has_other_client_group).to eq("Yes") end - context "when updating from check answers page with the answer YES" do + context "when updating from check answers page with the answer YES and no secondary client group set" do + let(:scheme_to_update) { create(:scheme, owning_organisation: user.organisation, confirmed: nil, secondary_client_group: nil) } let(:params) { { scheme: { has_other_client_group: "Yes", page: "confirm-secondary", check_answers: "true" } } } - it "renders check answers page after successful update" do + it "renders secondary client group page after successful update" do follow_redirect! expect(response).to have_http_status(:ok) expect(page).to have_content("What is the other client group?") @@ -1420,6 +1421,22 @@ RSpec.describe SchemesController, type: :request do end end + context "when updating from check answers page with the answer YES and a secondary client group set" do + let(:scheme_to_update) { create(:scheme, owning_organisation: user.organisation, confirmed: nil, secondary_client_group: "F") } + let(:params) { { scheme: { has_other_client_group: "Yes", page: "confirm-secondary", check_answers: "true" } } } + + it "renders check answers page after successful update" do + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Check your changes before creating this scheme") + end + + it "updates a scheme with valid params" do + follow_redirect! + expect(scheme_to_update.reload.has_other_client_group).to eq("Yes") + end + end + context "when updating from check answers page with the answer NO" do let(:params) { { scheme: { has_other_client_group: "No", page: "confirm-secondary", check_answers: "true" } } } @@ -1716,10 +1733,11 @@ RSpec.describe SchemesController, type: :request do expect(scheme_to_update.reload.has_other_client_group).to eq("Yes") end - context "when updating from check answers page with the answer YES" do + context "when updating from check answers page with the answer YES and no existing secondary client group set" do + let(:scheme_to_update) { create(:scheme, owning_organisation: user.organisation, confirmed: nil, secondary_client_group: nil) } let(:params) { { scheme: { has_other_client_group: "Yes", page: "confirm-secondary", check_answers: "true" } } } - it "renders check answers page after successful update" do + it "renders secondary client group page after successful update" do follow_redirect! expect(response).to have_http_status(:ok) expect(page).to have_content("What is the other client group?") @@ -1731,6 +1749,22 @@ RSpec.describe SchemesController, type: :request do end end + context "when updating from check answers page with the answer YES and an existing secondary client group set" do + let(:scheme_to_update) { create(:scheme, owning_organisation: user.organisation, confirmed: nil, secondary_client_group: "F") } + let(:params) { { scheme: { has_other_client_group: "Yes", page: "confirm-secondary", check_answers: "true" } } } + + it "renders check answers page after successful update" do + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("Check your changes before creating this scheme") + end + + it "updates a scheme with valid params" do + follow_redirect! + expect(scheme_to_update.reload.has_other_client_group).to eq("Yes") + end + end + context "when updating from check answers page with the answer NO" do let(:params) { { scheme: { has_other_client_group: "No", page: "confirm-secondary", check_answers: "true" } } } From 6fb1384364d589f6d8e8dcc181f7827a48333907 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Tue, 19 Nov 2024 09:37:15 +0000 Subject: [PATCH 12/34] Remove mortgage lender questions for 2025 discounted sales (#2784) --- .../discounted_ownership_scheme.rb | 14 ++++-- .../discounted_ownership_scheme_spec.rb | 44 ++++++++++++++++++- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/app/models/form/sales/subsections/discounted_ownership_scheme.rb b/app/models/form/sales/subsections/discounted_ownership_scheme.rb index 8131df4a7..63ce4af47 100644 --- a/app/models/form/sales/subsections/discounted_ownership_scheme.rb +++ b/app/models/form/sales/subsections/discounted_ownership_scheme.rb @@ -28,8 +28,7 @@ class Form::Sales::Subsections::DiscountedOwnershipScheme < ::Form::Subsection Form::Sales::Pages::DiscountedSaleValueCheck.new("discounted_sale_mortgage_value_check", nil, self), Form::Sales::Pages::ExtraBorrowingValueCheck.new("extra_borrowing_mortgage_value_check", nil, self), Form::Sales::Pages::DepositAndMortgageValueCheck.new("discounted_ownership_deposit_and_mortgage_value_check_after_mortgage", nil, self), - Form::Sales::Pages::MortgageLender.new("mortgage_lender_discounted_ownership", nil, self, ownershipsch: 2), - Form::Sales::Pages::MortgageLenderOther.new("mortgage_lender_other_discounted_ownership", nil, self, ownershipsch: 2), + mortgage_lender_questions, Form::Sales::Pages::MortgageLength.new("mortgage_length_discounted_ownership", nil, self, ownershipsch: 2), Form::Sales::Pages::ExtraBorrowing.new("extra_borrowing_discounted_ownership", nil, self, ownershipsch: 2), Form::Sales::Pages::ExtraBorrowingValueCheck.new("extra_borrowing_value_check", nil, self), @@ -41,10 +40,19 @@ class Form::Sales::Subsections::DiscountedOwnershipScheme < ::Form::Subsection Form::Sales::Pages::DiscountedSaleValueCheck.new("discounted_sale_deposit_value_check", nil, self), Form::Sales::Pages::LeaseholdCharges.new("leasehold_charges_discounted_ownership", nil, self, ownershipsch: 2), Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_discounted_ownership_value_check", nil, self), - ] + ].flatten.compact end def displayed_in_tasklist?(log) log.ownershipsch.nil? || log.ownershipsch == 2 end + + def mortgage_lender_questions + unless form.start_year_2025_or_later? + [ + Form::Sales::Pages::MortgageLender.new("mortgage_lender_discounted_ownership", nil, self, ownershipsch: 2), + Form::Sales::Pages::MortgageLenderOther.new("mortgage_lender_other_discounted_ownership", nil, self, ownershipsch: 2), + ] + end + end end diff --git a/spec/models/form/sales/subsections/discounted_ownership_scheme_spec.rb b/spec/models/form/sales/subsections/discounted_ownership_scheme_spec.rb index bfd5313fb..a573103a3 100644 --- a/spec/models/form/sales/subsections/discounted_ownership_scheme_spec.rb +++ b/spec/models/form/sales/subsections/discounted_ownership_scheme_spec.rb @@ -5,7 +5,7 @@ RSpec.describe Form::Sales::Subsections::DiscountedOwnershipScheme, type: :model let(:subsection_id) { nil } let(:subsection_definition) { nil } - let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1)) } + let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1), start_year_2025_or_later?: false) } let(:section) { instance_double(Form::Sales::Sections::SaleInformation, form:) } it "has correct section" do @@ -83,4 +83,46 @@ RSpec.describe Form::Sales::Subsections::DiscountedOwnershipScheme, type: :model expect(discounted_ownership_scheme.displayed_in_tasklist?(log)).to eq(false) end end + + context "with form on or after 2025" do + let(:form) { instance_double(Form, start_date: Time.zone.local(2025, 4, 1), start_year_2025_or_later?: true) } + + it "has correct pages" do + expect(discounted_ownership_scheme.pages.map(&:id)).to eq( + %w[ + living_before_purchase_discounted_ownership_joint_purchase + living_before_purchase_discounted_ownership + purchase_price + discount + extra_borrowing_price_value_check + percentage_discount_value_check + grant + grant_value_check + purchase_price_discounted_ownership + discounted_sale_grant_value_check + about_price_discounted_ownership_value_check + discounted_ownership_deposit_and_mortgage_value_check_after_value_and_discount + mortgage_used_discounted_ownership + discounted_ownership_mortgage_used_mortgage_value_check + discounted_sale_mortgage_used_value_check + mortgage_amount_discounted_ownership + discounted_ownership_mortgage_amount_mortgage_value_check + discounted_sale_mortgage_value_check + extra_borrowing_mortgage_value_check + discounted_ownership_deposit_and_mortgage_value_check_after_mortgage + mortgage_length_discounted_ownership + extra_borrowing_discounted_ownership + extra_borrowing_value_check + deposit_discounted_ownership + extra_borrowing_deposit_value_check + discounted_ownership_deposit_joint_purchase_value_check + discounted_ownership_deposit_value_check + discounted_ownership_deposit_and_mortgage_value_check_after_deposit + discounted_sale_deposit_value_check + leasehold_charges_discounted_ownership + monthly_charges_discounted_ownership_value_check + ], + ) + end + end end From 045249a9ed459fdab2375470a2a4e37697109d8e Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Tue, 19 Nov 2024 11:04:01 +0000 Subject: [PATCH 13/34] Update scheme name wrapping (#2786) --- app/frontend/styles/_table-group.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/frontend/styles/_table-group.scss b/app/frontend/styles/_table-group.scss index 6f488d6cb..a38f749dd 100644 --- a/app/frontend/styles/_table-group.scss +++ b/app/frontend/styles/_table-group.scss @@ -24,7 +24,8 @@ } .scheme-name-cell { - word-break: break-all; + overflow-wrap: break-word; + word-break: break-word; } .app-table-group:focus { From 2f221e412cf03c6247cdb3f2f64486e00bdfb53a Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Tue, 19 Nov 2024 11:06:37 +0000 Subject: [PATCH 14/34] Count unique attributes (#2791) --- app/helpers/form_page_error_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/form_page_error_helper.rb b/app/helpers/form_page_error_helper.rb index c2c13e0c6..2c90bfd2a 100644 --- a/app/helpers/form_page_error_helper.rb +++ b/app/helpers/form_page_error_helper.rb @@ -14,6 +14,6 @@ module FormPageErrorHelper end def all_questions_affected_by_errors(log) - log.errors.map(&:attribute) - [:base] + (log.errors.map(&:attribute) - [:base]).uniq end end From 05e000276dd113e9566c55e4b332ae5165919122 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Tue, 19 Nov 2024 11:09:00 +0000 Subject: [PATCH 15/34] Update scheme_params order (#2773) --- app/controllers/schemes_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/schemes_controller.rb b/app/controllers/schemes_controller.rb index be5d3f7ef..3df38a237 100644 --- a/app/controllers/schemes_controller.rb +++ b/app/controllers/schemes_controller.rb @@ -293,9 +293,9 @@ private def scheme_params required_params = params.require(:scheme).permit(:service_name, :sensitive, - :owning_organisation_id, :scheme_type, :registered_under_care_act, + :owning_organisation_id, :id, :has_other_client_group, :primary_client_group, From 4dc323059fd2e297a15e4f986c3faa04ba2b11ae Mon Sep 17 00:00:00 2001 From: Manny Dinssa <44172848+Dinssa@users.noreply.github.com> Date: Tue, 19 Nov 2024 13:59:54 +0000 Subject: [PATCH 16/34] CLDC-3738: Sales - Update household characteristics Q29 and Q38 (25/26) (#2774) * Create 2025 versions of these questions * Remove blank line * Update copy and use 2025 version of question * Add tests * Update depends on * Update question number * Update test * Remove depends_on companybuy which has been removed * Fix lint --- .../buyer2_relationship_to_buyer1_yes_no.rb | 14 ++ .../person_relationship_to_buyer_1_yes_no.rb | 15 ++ .../buyer2_relationship_to_buyer1.rb | 1 - .../buyer2_relationship_to_buyer1_yes_no.rb | 23 +++ .../person_relationship_to_buyer_1_yes_no.rb | 29 +++ .../subsections/household_characteristics.rb | 23 ++- .../sales/household_characteristics.en.yml | 24 +-- ...yer2_relationship_to_buyer1_yes_no_spec.rb | 29 +++ ...rson_relationship_to_buyer1_yes_no_spec.rb | 87 ++++++++ ...yer2_relationship_to_buyer1_yes_no_spec.rb | 43 ++++ ...rson_relationship_to_buyer1_yes_no_spec.rb | 110 +++++++++++ .../household_characteristics_spec.rb | 185 +++++++++++++++--- 12 files changed, 541 insertions(+), 42 deletions(-) create mode 100644 app/models/form/sales/pages/buyer2_relationship_to_buyer1_yes_no.rb create mode 100644 app/models/form/sales/pages/person_relationship_to_buyer_1_yes_no.rb create mode 100644 app/models/form/sales/questions/buyer2_relationship_to_buyer1_yes_no.rb create mode 100644 app/models/form/sales/questions/person_relationship_to_buyer_1_yes_no.rb create mode 100644 spec/models/form/sales/pages/buyer2_relationship_to_buyer1_yes_no_spec.rb create mode 100644 spec/models/form/sales/pages/person_relationship_to_buyer1_yes_no_spec.rb create mode 100644 spec/models/form/sales/questions/buyer2_relationship_to_buyer1_yes_no_spec.rb create mode 100644 spec/models/form/sales/questions/person_relationship_to_buyer1_yes_no_spec.rb diff --git a/app/models/form/sales/pages/buyer2_relationship_to_buyer1_yes_no.rb b/app/models/form/sales/pages/buyer2_relationship_to_buyer1_yes_no.rb new file mode 100644 index 000000000..570dbd1bb --- /dev/null +++ b/app/models/form/sales/pages/buyer2_relationship_to_buyer1_yes_no.rb @@ -0,0 +1,14 @@ +class Form::Sales::Pages::Buyer2RelationshipToBuyer1YesNo < ::Form::Page + def initialize(id, hsh, subsection) + super + @id = "buyer_2_relationship_to_buyer_1" + @copy_key = "sales.household_characteristics.relat2.buyer" + @depends_on = [{ "joint_purchase?" => true }] + end + + def questions + @questions ||= [ + Form::Sales::Questions::Buyer2RelationshipToBuyer1YesNo.new(nil, nil, self), + ] + end +end diff --git a/app/models/form/sales/pages/person_relationship_to_buyer_1_yes_no.rb b/app/models/form/sales/pages/person_relationship_to_buyer_1_yes_no.rb new file mode 100644 index 000000000..01234b219 --- /dev/null +++ b/app/models/form/sales/pages/person_relationship_to_buyer_1_yes_no.rb @@ -0,0 +1,15 @@ +class Form::Sales::Pages::PersonRelationshipToBuyer1YesNo < ::Form::Sales::Pages::Person + def initialize(id, hsh, subsection, person_index:) + super + @copy_key = "sales.household_characteristics.relat2.person" if person_index == 2 + @depends_on = [ + { "details_known_#{person_index}" => 1 }, + ] + end + + def questions + @questions ||= [ + Form::Sales::Questions::PersonRelationshipToBuyer1YesNo.new("relat#{@person_index}", nil, self, person_index: @person_index), + ] + end +end diff --git a/app/models/form/sales/questions/buyer2_relationship_to_buyer1.rb b/app/models/form/sales/questions/buyer2_relationship_to_buyer1.rb index 9520ace67..fd65f14a2 100644 --- a/app/models/form/sales/questions/buyer2_relationship_to_buyer1.rb +++ b/app/models/form/sales/questions/buyer2_relationship_to_buyer1.rb @@ -17,7 +17,6 @@ class Form::Sales::Questions::Buyer2RelationshipToBuyer1 < ::Form::Question def answer_options if form.start_year_2024_or_later? - { "P" => { "value" => "Partner" }, "C" => { "value" => "Child" }, diff --git a/app/models/form/sales/questions/buyer2_relationship_to_buyer1_yes_no.rb b/app/models/form/sales/questions/buyer2_relationship_to_buyer1_yes_no.rb new file mode 100644 index 000000000..43fe672e1 --- /dev/null +++ b/app/models/form/sales/questions/buyer2_relationship_to_buyer1_yes_no.rb @@ -0,0 +1,23 @@ +class Form::Sales::Questions::Buyer2RelationshipToBuyer1YesNo < ::Form::Question + def initialize(id, hsh, page) + super + @id = "relat2" + @copy_key = "sales.household_characteristics.relat2.buyer" + @type = "radio" + @answer_options = { + "P" => { "value" => "Yes" }, + "X" => { "value" => "No" }, + "R" => { "value" => "Buyer prefers not to say" }, + } + @inferred_check_answers_value = [{ + "condition" => { + "relat2" => "R", + }, + "value" => "Prefers not to say", + }] + @check_answers_card_number = 2 + @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] + end + + QUESTION_NUMBER_FROM_YEAR = { 2025 => 29 }.freeze +end diff --git a/app/models/form/sales/questions/person_relationship_to_buyer_1_yes_no.rb b/app/models/form/sales/questions/person_relationship_to_buyer_1_yes_no.rb new file mode 100644 index 000000000..60dddfd45 --- /dev/null +++ b/app/models/form/sales/questions/person_relationship_to_buyer_1_yes_no.rb @@ -0,0 +1,29 @@ +class Form::Sales::Questions::PersonRelationshipToBuyer1YesNo < ::Form::Question + def initialize(id, hsh, page, person_index:) + super(id, hsh, page) + @type = "radio" + @copy_key = "sales.household_characteristics.relat2.person" if person_index == 2 + @answer_options = { + "P" => { "value" => "Yes" }, + "X" => { "value" => "No" }, + "R" => { "value" => "Person prefers not to say" }, + } + @inferred_check_answers_value = [{ + "condition" => { + id => "R", + }, + "value" => "Prefers not to say", + }] + @check_answers_card_number = person_index + @person_index = person_index + @question_number = question_number + end + + def question_number + base_question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] + + base_question_number + (4 * @person_index) + end + + QUESTION_NUMBER_FROM_YEAR = { 2025 => 30 }.freeze +end diff --git a/app/models/form/sales/subsections/household_characteristics.rb b/app/models/form/sales/subsections/household_characteristics.rb index f1f01ba62..e839b1979 100644 --- a/app/models/form/sales/subsections/household_characteristics.rb +++ b/app/models/form/sales/subsections/household_characteristics.rb @@ -3,7 +3,14 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection super @id = "household_characteristics" @label = "Household characteristics" - @depends_on = [{ "setup_completed?" => true, "company_buyer?" => false }] + end + + def depends_on + if form.start_year_2025_or_later? + [{ "setup_completed?" => true }] + else + [{ "setup_completed?" => true, "company_buyer?" => false }] + end end def pages @@ -31,7 +38,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection Form::Sales::Pages::Buyer1IncomeMinValueCheck.new("working_situation_buyer_1_income_min_value_check", nil, self), Form::Sales::Pages::Buyer1LiveInProperty.new(nil, nil, self), Form::Sales::Pages::BuyerLiveInValueCheck.new("buyer_1_live_in_property_value_check", nil, self, person_index: 1), - Form::Sales::Pages::Buyer2RelationshipToBuyer1.new(nil, nil, self), + (form.start_year_2025_or_later? ? Form::Sales::Pages::Buyer2RelationshipToBuyer1YesNo.new(nil, nil, self) : Form::Sales::Pages::Buyer2RelationshipToBuyer1.new(nil, nil, self)), Form::Sales::Pages::PersonStudentNotChildValueCheck.new("buyer_2_relationship_student_not_child_value_check", nil, self, person_index: 2), Form::Sales::Pages::Age2.new(nil, nil, self), Form::Sales::Pages::OldPersonsSharedOwnershipValueCheck.new("age_2_old_persons_shared_ownership_joint_purchase_value_check", nil, self, joint_purchase: true), @@ -51,7 +58,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection Form::Sales::Pages::NumberOfOthersInProperty.new("number_of_others_in_property", nil, self, joint_purchase: false), Form::Sales::Pages::NumberOfOthersInProperty.new("number_of_others_in_property_joint_purchase", nil, self, joint_purchase: true), Form::Sales::Pages::PersonKnown.new("person_2_known", nil, self, person_index: 2), - Form::Sales::Pages::PersonRelationshipToBuyer1.new("person_2_relationship_to_buyer_1", nil, self, person_index: 2), + (form.start_year_2025_or_later? ? Form::Sales::Pages::PersonRelationshipToBuyer1YesNo.new("person_2_relationship_to_buyer_1", nil, self, person_index: 2) : Form::Sales::Pages::PersonRelationshipToBuyer1.new("person_2_relationship_to_buyer_1", nil, self, person_index: 2)), (Form::Sales::Pages::PartnerUnder16ValueCheck.new("relationship_2_partner_under_16_value_check", nil, self, person_index: 2) if form.start_year_2024_or_later?), (Form::Sales::Pages::MultiplePartnersValueCheck.new("relationship_2_multiple_partners_value_check", nil, self, person_index: 2) if form.start_year_2024_or_later?), Form::Sales::Pages::PersonStudentNotChildValueCheck.new("relationship_2_student_not_child_value_check", nil, self, person_index: 2), @@ -66,7 +73,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection (Form::Sales::Pages::NotRetiredValueCheck.new("working_situation_2_not_retired_value_check", nil, self, person_index: 2) if form.start_year_2024_or_later?), Form::Sales::Pages::PersonStudentNotChildValueCheck.new("working_situation_2_student_not_child_value_check", nil, self, person_index: 2), Form::Sales::Pages::PersonKnown.new("person_3_known", nil, self, person_index: 3), - Form::Sales::Pages::PersonRelationshipToBuyer1.new("person_3_relationship_to_buyer_1", nil, self, person_index: 3), + (form.start_year_2025_or_later? ? Form::Sales::Pages::PersonRelationshipToBuyer1YesNo.new("person_3_relationship_to_buyer_1", nil, self, person_index: 3) : Form::Sales::Pages::PersonRelationshipToBuyer1.new("person_3_relationship_to_buyer_1", nil, self, person_index: 3)), (Form::Sales::Pages::PartnerUnder16ValueCheck.new("relationship_3_partner_under_16_value_check", nil, self, person_index: 3) if form.start_year_2024_or_later?), (Form::Sales::Pages::MultiplePartnersValueCheck.new("relationship_3_multiple_partners_value_check", nil, self, person_index: 3) if form.start_year_2024_or_later?), Form::Sales::Pages::PersonStudentNotChildValueCheck.new("relationship_3_student_not_child_value_check", nil, self, person_index: 3), @@ -81,7 +88,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection (Form::Sales::Pages::NotRetiredValueCheck.new("working_situation_3_not_retired_value_check", nil, self, person_index: 3) if form.start_year_2024_or_later?), Form::Sales::Pages::PersonStudentNotChildValueCheck.new("working_situation_3_student_not_child_value_check", nil, self, person_index: 3), Form::Sales::Pages::PersonKnown.new("person_4_known", nil, self, person_index: 4), - Form::Sales::Pages::PersonRelationshipToBuyer1.new("person_4_relationship_to_buyer_1", nil, self, person_index: 4), + (form.start_year_2025_or_later? ? Form::Sales::Pages::PersonRelationshipToBuyer1YesNo.new("person_4_relationship_to_buyer_1", nil, self, person_index: 4) : Form::Sales::Pages::PersonRelationshipToBuyer1.new("person_4_relationship_to_buyer_1", nil, self, person_index: 4)), (Form::Sales::Pages::PartnerUnder16ValueCheck.new("relationship_4_partner_under_16_value_check", nil, self, person_index: 4) if form.start_year_2024_or_later?), (Form::Sales::Pages::MultiplePartnersValueCheck.new("relationship_4_multiple_partners_value_check", nil, self, person_index: 4) if form.start_year_2024_or_later?), Form::Sales::Pages::PersonStudentNotChildValueCheck.new("relationship_4_student_not_child_value_check", nil, self, person_index: 4), @@ -96,7 +103,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection (Form::Sales::Pages::NotRetiredValueCheck.new("working_situation_4_not_retired_value_check", nil, self, person_index: 4) if form.start_year_2024_or_later?), Form::Sales::Pages::PersonStudentNotChildValueCheck.new("working_situation_4_student_not_child_value_check", nil, self, person_index: 4), Form::Sales::Pages::PersonKnown.new("person_5_known", nil, self, person_index: 5), - Form::Sales::Pages::PersonRelationshipToBuyer1.new("person_5_relationship_to_buyer_1", nil, self, person_index: 5), + (form.start_year_2025_or_later? ? Form::Sales::Pages::PersonRelationshipToBuyer1YesNo.new("person_5_relationship_to_buyer_1", nil, self, person_index: 5) : Form::Sales::Pages::PersonRelationshipToBuyer1.new("person_5_relationship_to_buyer_1", nil, self, person_index: 5)), (Form::Sales::Pages::PartnerUnder16ValueCheck.new("relationship_5_partner_under_16_value_check", nil, self, person_index: 5) if form.start_year_2024_or_later?), (Form::Sales::Pages::MultiplePartnersValueCheck.new("relationship_5_multiple_partners_value_check", nil, self, person_index: 5) if form.start_year_2024_or_later?), Form::Sales::Pages::PersonStudentNotChildValueCheck.new("relationship_5_student_not_child_value_check", nil, self, person_index: 5), @@ -111,7 +118,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection (Form::Sales::Pages::NotRetiredValueCheck.new("working_situation_5_not_retired_value_check", nil, self, person_index: 5) if form.start_year_2024_or_later?), Form::Sales::Pages::PersonStudentNotChildValueCheck.new("working_situation_5_student_not_child_value_check", nil, self, person_index: 5), Form::Sales::Pages::PersonKnown.new("person_6_known", nil, self, person_index: 6), - Form::Sales::Pages::PersonRelationshipToBuyer1.new("person_6_relationship_to_buyer_1", nil, self, person_index: 6), + (form.start_year_2025_or_later? ? Form::Sales::Pages::PersonRelationshipToBuyer1YesNo.new("person_6_relationship_to_buyer_1", nil, self, person_index: 6) : Form::Sales::Pages::PersonRelationshipToBuyer1.new("person_6_relationship_to_buyer_1", nil, self, person_index: 6)), (Form::Sales::Pages::PartnerUnder16ValueCheck.new("relationship_6_partner_under_16_value_check", nil, self, person_index: 6) if form.start_year_2024_or_later?), (Form::Sales::Pages::MultiplePartnersValueCheck.new("relationship_6_multiple_partners_value_check", nil, self, person_index: 6) if form.start_year_2024_or_later?), Form::Sales::Pages::PersonStudentNotChildValueCheck.new("relationship_6_student_not_child_value_check", nil, self, person_index: 6), @@ -143,6 +150,8 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection end def displayed_in_tasklist?(log) + return true if form.start_year_2025_or_later? + !log.company_buyer? end end diff --git a/config/locales/forms/2025/sales/household_characteristics.en.yml b/config/locales/forms/2025/sales/household_characteristics.en.yml index b92cba2de..3f9f503be 100644 --- a/config/locales/forms/2025/sales/household_characteristics.en.yml +++ b/config/locales/forms/2025/sales/household_characteristics.en.yml @@ -80,14 +80,14 @@ en: relat2: buyer: page_header: "" - check_answer_label: "Buyer 2's relationship to buyer 1" + check_answer_label: "Buyer 2 is the partner of buyer 1" hint_text: "" - question_text: "What is buyer 2's relationship to buyer 1?" + question_text: "Is buyer 2 the partner of buyer 1?" person: page_header: "" - check_answer_label: "Person 2’s relationship to Buyer 1" + check_answer_label: "Person 2 is the partner of buyer 1" hint_text: "" - question_text: "What is Person 2’s relationship to Buyer 1?" + question_text: "Is person 2 the partner of buyer 1?" age2: buyer: @@ -212,9 +212,9 @@ en: relat3: page_header: "" - check_answer_label: "Person 3’s relationship to Buyer 1" + check_answer_label: "Person 3 is the partner of buyer 1" hint_text: "" - question_text: "What is Person 3’s relationship to Buyer 1?" + question_text: "Is person 3 the partner of buyer 1?" age3: page_header: "" @@ -247,9 +247,9 @@ en: relat4: page_header: "" - check_answer_label: "Person 4’s relationship to Buyer 1" + check_answer_label: "Person 4 is the partner of buyer 1" hint_text: "" - question_text: "What is Person 4’s relationship to Buyer 1?" + question_text: "Is person 4 the partner of buyer 1?" age4: page_header: "" @@ -282,9 +282,9 @@ en: relat5: page_header: "" - check_answer_label: "Person 5’s relationship to Buyer 1" + check_answer_label: "Person 5 is the partner of buyer 1" hint_text: "" - question_text: "What is Person 5’s relationship to Buyer 1?" + question_text: "Is person 5 the partner of buyer 1?" age5: page_header: "" @@ -317,9 +317,9 @@ en: relat6: page_header: "" - check_answer_label: "Person 6’s relationship to Buyer 1" + check_answer_label: "Person 6 is the partner of buyer 1" hint_text: "" - question_text: "What is Person 6’s relationship to Buyer 1?" + question_text: "Is person 6 the partner of buyer 1?" age6: page_header: "" diff --git a/spec/models/form/sales/pages/buyer2_relationship_to_buyer1_yes_no_spec.rb b/spec/models/form/sales/pages/buyer2_relationship_to_buyer1_yes_no_spec.rb new file mode 100644 index 000000000..67ecb9ae3 --- /dev/null +++ b/spec/models/form/sales/pages/buyer2_relationship_to_buyer1_yes_no_spec.rb @@ -0,0 +1,29 @@ +require "rails_helper" + +RSpec.describe Form::Sales::Pages::Buyer2RelationshipToBuyer1YesNo, 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, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1))) } + + 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[relat2]) + end + + it "has the correct id" do + expect(page.id).to eq("buyer_2_relationship_to_buyer_1") + end + + it "has the correct description" do + expect(page.description).to be_nil + end + + it "has correct depends_on" do + expect(page.depends_on).to eq([{ "joint_purchase?" => true }]) + end +end diff --git a/spec/models/form/sales/pages/person_relationship_to_buyer1_yes_no_spec.rb b/spec/models/form/sales/pages/person_relationship_to_buyer1_yes_no_spec.rb new file mode 100644 index 000000000..a8215a0ba --- /dev/null +++ b/spec/models/form/sales/pages/person_relationship_to_buyer1_yes_no_spec.rb @@ -0,0 +1,87 @@ +require "rails_helper" + +RSpec.describe Form::Sales::Pages::PersonRelationshipToBuyer1YesNo, type: :model do + subject(:page) { described_class.new(page_id, page_definition, subsection, person_index:) } + + let(:page_definition) { nil } + let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1))) } + let(:person_index) { 1 } + + let(:page_id) { "person_1_relationship_to_buyer_1" } + + it "has correct subsection" do + expect(page.subsection).to eq(subsection) + end + + it "has the correct description" do + expect(page.description).to be_nil + end + + context "with person 2" do + let(:person_index) { 2 } + let(:page_id) { "person_2_relationship_to_buyer_1" } + + it "has correct questions" do + expect(page.questions.map(&:id)).to eq(%w[relat2]) + end + + it "has the correct id" do + expect(page.id).to eq("person_2_relationship_to_buyer_1") + end + + it "has correct depends_on" do + expect(page.depends_on).to eq([{ "details_known_2" => 1 }]) + end + end + + context "with person 3" do + let(:person_index) { 3 } + let(:page_id) { "person_3_relationship_to_buyer_1" } + + it "has correct questions" do + expect(page.questions.map(&:id)).to eq(%w[relat3]) + end + + it "has the correct id" do + expect(page.id).to eq("person_3_relationship_to_buyer_1") + end + + it "has correct depends_on" do + expect(page.depends_on).to eq([{ "details_known_3" => 1 }]) + end + end + + context "with person 4" do + let(:person_index) { 4 } + let(:page_id) { "person_4_relationship_to_buyer_1" } + + it "has correct questions" do + expect(page.questions.map(&:id)).to eq(%w[relat4]) + end + + it "has the correct id" do + expect(page.id).to eq("person_4_relationship_to_buyer_1") + end + + it "has correct depends_on" do + expect(page.depends_on).to eq([{ "details_known_4" => 1 }]) + end + end + + context "with person 5" do + let(:person_index) { 5 } + let(:page_id) { "person_5_relationship_to_buyer_1" } + + it "has correct questions" do + expect(page.questions.map(&:id)).to eq(%w[relat5]) + end + + it "has the correct id" do + expect(page.id).to eq("person_5_relationship_to_buyer_1") + end + + it "has correct depends_on" do + expect(page.depends_on).to eq([{ "details_known_5" => 1 }]) + end + end +end diff --git a/spec/models/form/sales/questions/buyer2_relationship_to_buyer1_yes_no_spec.rb b/spec/models/form/sales/questions/buyer2_relationship_to_buyer1_yes_no_spec.rb new file mode 100644 index 000000000..5e94d7e7a --- /dev/null +++ b/spec/models/form/sales/questions/buyer2_relationship_to_buyer1_yes_no_spec.rb @@ -0,0 +1,43 @@ +require "rails_helper" + +RSpec.describe Form::Sales::Questions::Buyer2RelationshipToBuyer1YesNo, 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, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1)))) } + + it "has correct page" do + expect(question.page).to eq(page) + end + + it "has the correct id" do + expect(question.id).to eq("relat2") + end + + it "has the correct type" do + expect(question.type).to eq("radio") + end + + it "is not marked as derived" do + expect(question.derived?(nil)).to be false + end + + it "has the correct answer_options" do + expect(question.answer_options).to eq({ + "P" => { "value" => "Yes" }, + "X" => { "value" => "No" }, + "R" => { "value" => "Buyer prefers not to say" }, + }) + end + + it "has the correct check_answers_card_number" do + expect(question.check_answers_card_number).to eq(2) + end + + it "has the correct inferred_check_answers_value" do + expect(question.inferred_check_answers_value).to eq([ + { "condition" => { "relat2" => "R" }, "value" => "Prefers not to say" }, + ]) + end +end diff --git a/spec/models/form/sales/questions/person_relationship_to_buyer1_yes_no_spec.rb b/spec/models/form/sales/questions/person_relationship_to_buyer1_yes_no_spec.rb new file mode 100644 index 000000000..cdfedc41e --- /dev/null +++ b/spec/models/form/sales/questions/person_relationship_to_buyer1_yes_no_spec.rb @@ -0,0 +1,110 @@ +require "rails_helper" + +RSpec.describe Form::Sales::Questions::PersonRelationshipToBuyer1YesNo, type: :model do + subject(:question) { described_class.new(question_id, question_definition, page, person_index:) } + + let(:question_id) { "relat2" } + let(:question_definition) { nil } + let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1)))) } + let(:person_index) { 2 } + + it "has correct page" do + expect(question.page).to eq(page) + end + + it "has the correct type" do + expect(question.type).to eq("radio") + end + + it "is not marked as derived" do + expect(question.derived?(nil)).to be false + end + + it "has expected check answers card number" do + expect(question.check_answers_card_number).to eq(2) + end + + it "has the correct answer_options" do + expect(question.answer_options).to eq({ + "P" => { "value" => "Yes" }, + "X" => { "value" => "No" }, + "R" => { "value" => "Person prefers not to say" }, + }) + end + + context "when person 2" do + let(:question_id) { "relat2" } + let(:person_index) { 2 } + + it "has the correct id" do + expect(question.id).to eq("relat2") + end + + it "has expected check answers card number" do + expect(question.check_answers_card_number).to eq(2) + end + + it "has the correct inferred_check_answers_value" do + expect(question.inferred_check_answers_value).to eq([ + { "condition" => { "relat2" => "R" }, "value" => "Prefers not to say" }, + ]) + end + end + + context "when person 3" do + let(:question_id) { "relat3" } + let(:person_index) { 3 } + + it "has the correct id" do + expect(question.id).to eq("relat3") + end + + it "has expected check answers card number" do + expect(question.check_answers_card_number).to eq(3) + end + + it "has the correct inferred_check_answers_value" do + expect(question.inferred_check_answers_value).to eq([ + { "condition" => { "relat3" => "R" }, "value" => "Prefers not to say" }, + ]) + end + end + + context "when person 4" do + let(:question_id) { "relat4" } + let(:person_index) { 4 } + + it "has the correct id" do + expect(question.id).to eq("relat4") + end + + it "has expected check answers card number" do + expect(question.check_answers_card_number).to eq(4) + end + + it "has the correct inferred_check_answers_value" do + expect(question.inferred_check_answers_value).to eq([ + { "condition" => { "relat4" => "R" }, "value" => "Prefers not to say" }, + ]) + end + end + + context "when person 5" do + let(:question_id) { "relat5" } + let(:person_index) { 5 } + + it "has the correct id" do + expect(question.id).to eq("relat5") + end + + it "has expected check answers card number" do + expect(question.check_answers_card_number).to eq(5) + end + + it "has the correct inferred_check_answers_value" do + expect(question.inferred_check_answers_value).to eq([ + { "condition" => { "relat5" => "R" }, "value" => "Prefers not to say" }, + ]) + end + end +end diff --git a/spec/models/form/sales/subsections/household_characteristics_spec.rb b/spec/models/form/sales/subsections/household_characteristics_spec.rb index 3eb5042d8..3cd856eb3 100644 --- a/spec/models/form/sales/subsections/household_characteristics_spec.rb +++ b/spec/models/form/sales/subsections/household_characteristics_spec.rb @@ -16,10 +16,19 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model expect(household_characteristics.section).to eq(section) end + it "has the correct id" do + expect(household_characteristics.id).to eq("household_characteristics") + end + + it "has the correct label" do + expect(household_characteristics.label).to eq("Household characteristics") + end + context "with 2022/23 form" do before do allow(form).to receive(:start_date).and_return(Time.zone.local(2022, 4, 1)) allow(form).to receive(:start_year_2024_or_later?).and_return(false) + allow(form).to receive(:start_year_2025_or_later?).and_return(false) end it "has correct pages" do @@ -121,6 +130,7 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model before do allow(form).to receive(:start_date).and_return(Time.zone.local(2023, 4, 1)) allow(form).to receive(:start_year_2024_or_later?).and_return(false) + allow(form).to receive(:start_year_2025_or_later?).and_return(false) end it "has correct pages" do @@ -229,6 +239,27 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model before do allow(form).to receive(:start_date).and_return(Time.zone.local(2024, 4, 1)) allow(form).to receive(:start_year_2024_or_later?).and_return(true) + allow(form).to receive(:start_year_2025_or_later?).and_return(false) + end + + it "has correct depends on" do + expect(household_characteristics.depends_on).to eq([{ "setup_completed?" => true, "company_buyer?" => false }]) + end + + context "when the sale is to a company buyer" do + let(:log) { FactoryBot.build(:sales_log, ownershipsch: 3, companybuy: 1) } + + it "is not displayed in tasklist" do + expect(household_characteristics.displayed_in_tasklist?(log)).to eq(false) + end + end + + context "when the sale is not to a company buyer" do + let(:log) { FactoryBot.build(:sales_log, ownershipsch: 3, companybuy: 2) } + + it "is displayed in tasklist" do + expect(household_characteristics.displayed_in_tasklist?(log)).to eq(true) + end end it "has correct pages" do @@ -358,31 +389,141 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model end end - it "has the correct id" do - expect(household_characteristics.id).to eq("household_characteristics") - end - - it "has the correct label" do - expect(household_characteristics.label).to eq("Household characteristics") - end - - it "has correct depends on" do - expect(household_characteristics.depends_on).to eq([{ "setup_completed?" => true, "company_buyer?" => false }]) - end - - context "when the sale is to a company buyer" do - let(:log) { FactoryBot.build(:sales_log, ownershipsch: 3, companybuy: 1) } - - it "is not displayed in tasklist" do - expect(household_characteristics.displayed_in_tasklist?(log)).to eq(false) + context "with 2025/26 form" do + before do + allow(form).to receive(:start_date).and_return(Time.zone.local(2025, 4, 1)) + allow(form).to receive(:start_year_2024_or_later?).and_return(true) + allow(form).to receive(:start_year_2025_or_later?).and_return(true) end - end - context "when the sale is not to a company buyer" do - let(:log) { FactoryBot.build(:sales_log, ownershipsch: 3, companybuy: 2) } + it "has correct depends on" do + expect(household_characteristics.depends_on).to eq([{ "setup_completed?" => true }]) + end - it "is displayed in tasklist" do - expect(household_characteristics.displayed_in_tasklist?(log)).to eq(true) + it "has correct pages" do + expect(household_characteristics.pages.map(&:id)).to eq( + %w[ + buyer_1_age + age_1_retirement_value_check + age_1_not_retired_value_check + age_1_old_persons_shared_ownership_joint_purchase_value_check + age_1_old_persons_shared_ownership_value_check + buyer_1_gender_identity + buyer_1_ethnic_group + buyer_1_ethnic_background_black + buyer_1_ethnic_background_asian + buyer_1_ethnic_background_arab + buyer_1_ethnic_background_mixed + buyer_1_ethnic_background_white + buyer_1_nationality + buyer_1_working_situation + working_situation_1_retirement_value_check + working_situation_1_not_retired_value_check + working_situation_buyer_1_income_min_value_check + buyer_1_live_in_property + buyer_1_live_in_property_value_check + buyer_2_relationship_to_buyer_1 + buyer_2_relationship_student_not_child_value_check + buyer_2_age + age_2_old_persons_shared_ownership_joint_purchase_value_check + age_2_old_persons_shared_ownership_value_check + age_2_buyer_retirement_value_check + age_2_buyer_not_retired_value_check + buyer_2_age_student_not_child_value_check + buyer_2_gender_identity + buyer_2_ethnic_group + buyer_2_ethnic_background_black + buyer_2_ethnic_background_asian + buyer_2_ethnic_background_arab + buyer_2_ethnic_background_mixed + buyer_2_ethnic_background_white + buyer_2_nationality + buyer_2_working_situation + working_situation_2_retirement_value_check_joint_purchase + working_situation_2_not_retired_value_check_joint_purchase + working_situation_buyer_2_income_min_value_check + buyer_2_working_situation_student_not_child_value_check + buyer_2_live_in_property + buyer_2_live_in_property_value_check + number_of_others_in_property + number_of_others_in_property_joint_purchase + person_2_known + person_2_relationship_to_buyer_1 + relationship_2_partner_under_16_value_check + relationship_2_multiple_partners_value_check + relationship_2_student_not_child_value_check + person_2_age + age_2_retirement_value_check + age_2_not_retired_value_check + age_2_student_not_child_value_check + age_2_partner_under_16_value_check + person_2_gender_identity + person_2_working_situation + working_situation_2_retirement_value_check + working_situation_2_not_retired_value_check + working_situation_2_student_not_child_value_check + person_3_known + person_3_relationship_to_buyer_1 + relationship_3_partner_under_16_value_check + relationship_3_multiple_partners_value_check + relationship_3_student_not_child_value_check + person_3_age + age_3_retirement_value_check + age_3_not_retired_value_check + age_3_student_not_child_value_check + age_3_partner_under_16_value_check + person_3_gender_identity + person_3_working_situation + working_situation_3_retirement_value_check + working_situation_3_not_retired_value_check + working_situation_3_student_not_child_value_check + person_4_known + person_4_relationship_to_buyer_1 + relationship_4_partner_under_16_value_check + relationship_4_multiple_partners_value_check + relationship_4_student_not_child_value_check + person_4_age + age_4_retirement_value_check + age_4_not_retired_value_check + age_4_student_not_child_value_check + age_4_partner_under_16_value_check + person_4_gender_identity + person_4_working_situation + working_situation_4_retirement_value_check + working_situation_4_not_retired_value_check + working_situation_4_student_not_child_value_check + person_5_known + person_5_relationship_to_buyer_1 + relationship_5_partner_under_16_value_check + relationship_5_multiple_partners_value_check + relationship_5_student_not_child_value_check + person_5_age + age_5_retirement_value_check + age_5_not_retired_value_check + age_5_student_not_child_value_check + age_5_partner_under_16_value_check + person_5_gender_identity + person_5_working_situation + working_situation_5_retirement_value_check + working_situation_5_not_retired_value_check + working_situation_5_student_not_child_value_check + person_6_known + person_6_relationship_to_buyer_1 + relationship_6_partner_under_16_value_check + relationship_6_multiple_partners_value_check + relationship_6_student_not_child_value_check + person_6_age + age_6_retirement_value_check + age_6_not_retired_value_check + age_6_student_not_child_value_check + age_6_partner_under_16_value_check + person_6_gender_identity + person_6_working_situation + working_situation_6_retirement_value_check + working_situation_6_not_retired_value_check + working_situation_6_student_not_child_value_check + ], + ) end end end From 4015b2020719ada3b6776c2be276488b3d719144 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 08:49:56 +0000 Subject: [PATCH 17/34] Bump cross-spawn from 7.0.3 to 7.0.6 (#2794) Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6. - [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md) - [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6) --- updated-dependencies: - dependency-name: cross-spawn dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 65de3d8d2..3e9afa0d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2244,9 +2244,9 @@ cosmiconfig@^9.0.0: parse-json "^5.2.0" cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" From a3ce7e9f95fa8dd1daf04278c432a53c71f8d477 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Wed, 20 Nov 2024 11:08:48 +0000 Subject: [PATCH 18/34] Update validation message (#2783) --- app/models/validations/sales/financial_validations.rb | 5 +++-- config/locales/validations/sales/financial.en.yml | 4 ++-- .../validations/sales/financial_validations_spec.rb | 10 ++++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/models/validations/sales/financial_validations.rb b/app/models/validations/sales/financial_validations.rb index a2cb2170f..91d4c31b7 100644 --- a/app/models/validations/sales/financial_validations.rb +++ b/app/models/validations/sales/financial_validations.rb @@ -73,8 +73,9 @@ module Validations::Sales::FinancialValidations end if threshold && record.stairbought < threshold - record.errors.add :stairbought, I18n.t("validations.sales.financial.stairbought.percentage_bought_must_be_at_least_threshold", threshold:) - record.errors.add :type, I18n.t("validations.sales.financial.type.percentage_bought_must_be_at_least_threshold", threshold:) + shared_ownership_type = record.form.get_question("type", record).label_from_value(record.type).downcase + record.errors.add :stairbought, I18n.t("validations.sales.financial.stairbought.percentage_bought_must_be_at_least_threshold", threshold:, shared_ownership_type:) + record.errors.add :type, I18n.t("validations.sales.financial.type.percentage_bought_must_be_at_least_threshold", threshold:, shared_ownership_type:) end end diff --git a/config/locales/validations/sales/financial.en.yml b/config/locales/validations/sales/financial.en.yml index 55d563ee3..a4fe65464 100644 --- a/config/locales/validations/sales/financial.en.yml +++ b/config/locales/validations/sales/financial.en.yml @@ -55,7 +55,7 @@ en: type: equity_under_min: "The minimum initial equity stake for this type of shared ownership sale is %{min_equity}%." equity_over_max: "The maximum initial equity stake is %{max_equity}%." - percentage_bought_must_be_at_least_threshold: "The minimum increase in equity while staircasing is %{threshold}% for this shared ownership type." + percentage_bought_must_be_at_least_threshold: "The minimum percentage share that can be bought in a staircasing transaction for %{shared_ownership_type} is %{threshold}%. Please change your answer or check that you have selected the correct shared ownership type." equity: equity_under_min: "The minimum initial equity stake for this type of shared ownership sale is %{min_equity}%." @@ -77,7 +77,7 @@ en: equity_over_stairowned_minus_stairbought: joint_purchase: "The initial equity stake is %{equity}% and the percentage owned in total minus the percentage bought is %{staircase_difference}%. In a staircasing transaction, the equity stake purchased cannot be larger than the percentage the buyers own minus the percentage bought." not_joint_purchase: "The initial equity stake is %{equity}% and the percentage owned in total minus the percentage bought is %{staircase_difference}%. In a staircasing transaction, the equity stake purchased cannot be larger than the percentage the buyer owns minus the percentage bought." - percentage_bought_must_be_at_least_threshold: "The minimum increase in equity while staircasing is %{threshold}%." + percentage_bought_must_be_at_least_threshold: "The minimum percentage share that can be bought in a staircasing transaction for %{shared_ownership_type} is %{threshold}%. Please change your answer or check that you have selected the correct shared ownership type." percentage_bought_equal_percentage_owned: "The percentage bought is %{stairbought}% and the percentage owned in total is %{stairowned}%. These figures cannot be the same." uprn_selection: diff --git a/spec/models/validations/sales/financial_validations_spec.rb b/spec/models/validations/sales/financial_validations_spec.rb index f579ae27e..f49179063 100644 --- a/spec/models/validations/sales/financial_validations_spec.rb +++ b/spec/models/validations/sales/financial_validations_spec.rb @@ -275,18 +275,20 @@ RSpec.describe Validations::Sales::FinancialValidations do record.stairbought = 9 [2, 16, 18, 24].each do |type| record.type = type + shared_ownership_type = record.form.get_question("type", record).label_from_value(record.type).downcase financial_validator.validate_percentage_bought_at_least_threshold(record) - expect(record.errors["stairbought"]).to eq([I18n.t("validations.sales.financial.stairbought.percentage_bought_must_be_at_least_threshold", threshold: 10)]) - expect(record.errors["type"]).to eq([I18n.t("validations.sales.financial.type.percentage_bought_must_be_at_least_threshold", threshold: 10)]) + expect(record.errors["stairbought"]).to eq([I18n.t("validations.sales.financial.stairbought.percentage_bought_must_be_at_least_threshold", threshold: 10, shared_ownership_type:)]) + expect(record.errors["type"]).to eq([I18n.t("validations.sales.financial.type.percentage_bought_must_be_at_least_threshold", threshold: 10, shared_ownership_type:)]) record.errors.clear end record.stairbought = 0 [28, 30, 31, 32].each do |type| record.type = type + shared_ownership_type = record.form.get_question("type", record).label_from_value(record.type).downcase financial_validator.validate_percentage_bought_at_least_threshold(record) - expect(record.errors["stairbought"]).to eq([I18n.t("validations.sales.financial.stairbought.percentage_bought_must_be_at_least_threshold", threshold: 1)]) - expect(record.errors["type"]).to eq([I18n.t("validations.sales.financial.type.percentage_bought_must_be_at_least_threshold", threshold: 1)]) + expect(record.errors["stairbought"]).to eq([I18n.t("validations.sales.financial.stairbought.percentage_bought_must_be_at_least_threshold", threshold: 1, shared_ownership_type:)]) + expect(record.errors["type"]).to eq([I18n.t("validations.sales.financial.type.percentage_bought_must_be_at_least_threshold", threshold: 1, shared_ownership_type:)]) record.errors.clear end end From bc1fe83750b9b6bf480b65bf30908af666e77a69 Mon Sep 17 00:00:00 2001 From: Manny Dinssa <44172848+Dinssa@users.noreply.github.com> Date: Wed, 20 Nov 2024 12:50:14 +0000 Subject: [PATCH 19/34] CLDC-3757: Sales - Change values in validation check for Q84 (25/26) (#2781) * Update 2025 version of validation check * Update validation for 2025 * Put two if statements into one * Different approach to comparing dates * Alternative method to compare dates * Update tests --- .../form/sales/pages/handover_date_check.rb | 12 ++- .../sales/sale_information_validations.rb | 5 +- .../validations/sales/soft_validations.rb | 6 ++ .../validations/sales/sale_information.en.yml | 2 + .../sales/pages/handover_date_check_spec.rb | 82 ++++++++++++------- .../sale_information_validations_spec.rb | 5 +- 6 files changed, 77 insertions(+), 35 deletions(-) diff --git a/app/models/form/sales/pages/handover_date_check.rb b/app/models/form/sales/pages/handover_date_check.rb index 690c3dde7..8ae41316c 100644 --- a/app/models/form/sales/pages/handover_date_check.rb +++ b/app/models/form/sales/pages/handover_date_check.rb @@ -3,8 +3,6 @@ class Form::Sales::Pages::HandoverDateCheck < ::Form::Page super @id = "handover_date_check" @copy_key = "sales.soft_validations.hodate_check" - @depends_on = [{ "saledate_check" => nil, "hodate_3_years_or_more_saledate?" => true }, - { "saledate_check" => 1, "hodate_3_years_or_more_saledate?" => true }] @informative_text = {} @title_text = { "translation" => "forms.#{form.start_date.year}.#{@copy_key}.title_text", @@ -12,6 +10,16 @@ class Form::Sales::Pages::HandoverDateCheck < ::Form::Page } end + def depends_on + if form.start_year_2025_or_later? + [{ "saledate_check" => nil, "hodate_5_years_or_more_saledate?" => true }, + { "saledate_check" => 1, "hodate_5_years_or_more_saledate?" => true }] + else + [{ "saledate_check" => nil, "hodate_3_years_or_more_saledate?" => true }, + { "saledate_check" => 1, "hodate_3_years_or_more_saledate?" => true }] + end + end + def questions @questions ||= [ Form::Sales::Questions::HandoverDateCheck.new(nil, nil, self), diff --git a/app/models/validations/sales/sale_information_validations.rb b/app/models/validations/sales/sale_information_validations.rb index 3c5e3f2b9..fa095a5e2 100644 --- a/app/models/validations/sales/sale_information_validations.rb +++ b/app/models/validations/sales/sale_information_validations.rb @@ -12,7 +12,10 @@ module Validations::Sales::SaleInformationValidations record.errors.add :saledate, I18n.t("validations.sales.sale_information.saledate.must_be_after_hodate") end - if record.saledate - record.hodate >= 3.years && record.form.start_year_2024_or_later? + if (record.saledate - 5.years) >= record.hodate && record.form.start_year_2025_or_later? + record.errors.add :hodate, I18n.t("validations.sales.sale_information.hodate.must_be_less_than_5_years_from_saledate") + record.errors.add :saledate, I18n.t("validations.sales.sale_information.saledate.must_be_less_than_5_years_from_hodate") + elsif (record.saledate - 3.years) >= record.hodate && record.startdate.year <= 2024 record.errors.add :hodate, I18n.t("validations.sales.sale_information.hodate.must_be_less_than_3_years_from_saledate") record.errors.add :saledate, I18n.t("validations.sales.sale_information.saledate.must_be_less_than_3_years_from_hodate") end diff --git a/app/models/validations/sales/soft_validations.rb b/app/models/validations/sales/soft_validations.rb index 5095e5ff9..d53391dd1 100644 --- a/app/models/validations/sales/soft_validations.rb +++ b/app/models/validations/sales/soft_validations.rb @@ -110,6 +110,12 @@ module Validations::Sales::SoftValidations saledate - hodate >= 3.years end + def hodate_5_years_or_more_saledate? + return unless hodate && saledate + + saledate - hodate >= 5.years + end + def purchase_price_higher_or_lower_text value < sale_range.soft_min ? "lower" : "higher" end diff --git a/config/locales/validations/sales/sale_information.en.yml b/config/locales/validations/sales/sale_information.en.yml index 20aca17d2..8fb7d02d4 100644 --- a/config/locales/validations/sales/sale_information.en.yml +++ b/config/locales/validations/sales/sale_information.en.yml @@ -8,9 +8,11 @@ en: hodate: must_be_before_saledate: "Practical completion or handover date must be before sale completion date." must_be_less_than_3_years_from_saledate: "Practical completion or handover date must be less than 3 years before sale completion date." + must_be_less_than_5_years_from_saledate: "Practical completion or handover date must be less than 5 years before sale completion date." saledate: must_be_after_hodate: "Sale completion date must be after practical completion or handover date." must_be_less_than_3_years_from_hodate: "Sale completion date must be less than 3 years after practical completion or handover date." + must_be_less_than_5_years_from_hodate: "Sale completion date must be less than 5 years after practical completion or handover date." must_be_after_exdate: "Sale completion date must be after contract exchange date." must_be_less_than_1_year_from_exdate: "Sale completion date must be less than 1 year after contract exchange date." mortgage_used_year: "You must answer either ‘yes’ or ‘no’ to the question ‘was a mortgage used’ for the selected year." diff --git a/spec/models/form/sales/pages/handover_date_check_spec.rb b/spec/models/form/sales/pages/handover_date_check_spec.rb index ae465da16..e6cc3acf5 100644 --- a/spec/models/form/sales/pages/handover_date_check_spec.rb +++ b/spec/models/form/sales/pages/handover_date_check_spec.rb @@ -5,44 +5,66 @@ RSpec.describe Form::Sales::Pages::HandoverDateCheck, type: :model do let(:page_id) { "" } let(:page_definition) { nil } - let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1)) } - let(:subsection) { instance_double(Form::Subsection, form:) } - it "has correct subsection" do - expect(page.subsection).to eq(subsection) - end + context "when form start year is <= 2024" do + let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1), start_year_2025_or_later?: false) } + let(:subsection) { instance_double(Form::Subsection, form:) } - it "has correct questions" do - expect(page.questions.map(&:id)).to eq(%w[hodate_check]) - end + it "has correct subsection" do + expect(page.subsection).to eq(subsection) + end - it "has the correct id" do - expect(page.id).to eq("handover_date_check") - end + it "has correct questions" do + expect(page.questions.map(&:id)).to eq(%w[hodate_check]) + end - it "has the correct title_text" do - expect(page.title_text).to eq({ - "translation" => "forms.2024.sales.soft_validations.hodate_check.title_text", - "arguments" => [], - }) - end + it "has the correct id" do + expect(page.id).to eq("handover_date_check") + end - it "has the correct informative_text" do - expect(page.informative_text).to eq({}) - end + it "has the correct title_text" do + expect(page.title_text).to eq({ + "translation" => "forms.2024.sales.soft_validations.hodate_check.title_text", + "arguments" => [], + }) + end - it "has correct depends_on" do - expect(page.depends_on).to eq([ - { "hodate_3_years_or_more_saledate?" => true, "saledate_check" => nil }, - { "hodate_3_years_or_more_saledate?" => true, "saledate_check" => 1 }, - ]) - end + it "has the correct informative_text" do + expect(page.informative_text).to eq({}) + end + + it "has correct depends_on" do + expect(page.depends_on).to eq([ + { "hodate_3_years_or_more_saledate?" => true, "saledate_check" => nil }, + { "hodate_3_years_or_more_saledate?" => true, "saledate_check" => 1 }, + ]) + end + + it "is interruption screen page" do + expect(page.interruption_screen?).to eq(true) + end - it "is interruption screen page" do - expect(page.interruption_screen?).to eq(true) + it "is has correct interruption_screen_question_ids" do + expect(page.interruption_screen_question_ids).to eq(%w[hodate saledate]) + end end - it "is has correct interruption_screen_question_ids" do - expect(page.interruption_screen_question_ids).to eq(%w[hodate saledate]) + context "when form start year is 2025" do + let(:form) { instance_double(Form, start_date: Time.zone.local(2025, 4, 1), start_year_2025_or_later?: true) } + let(:subsection) { instance_double(Form::Subsection, form:) } + + it "has the correct title_text" do + expect(page.title_text).to eq({ + "translation" => "forms.2025.sales.soft_validations.hodate_check.title_text", + "arguments" => [], + }) + end + + it "has correct depends_on" do + expect(page.depends_on).to eq([ + { "hodate_5_years_or_more_saledate?" => true, "saledate_check" => nil }, + { "hodate_5_years_or_more_saledate?" => true, "saledate_check" => 1 }, + ]) + 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 36dadc345..5cc0cdf07 100644 --- a/spec/models/validations/sales/sale_information_validations_spec.rb +++ b/spec/models/validations/sales/sale_information_validations_spec.rb @@ -60,10 +60,11 @@ RSpec.describe Validations::Sales::SaleInformationValidations do context "and form year is 2023 or earlier" do let(:record) { build(:sales_log, hodate: Date.new(2020, 12, 1), saledate: Date.new(2023, 12, 1)) } - it "does not add an error" do + it "does add an error" do sale_information_validator.validate_practical_completion_date(record) - expect(record.errors).not_to be_present + expect(record.errors[:hodate]).to be_present + expect(record.errors[:saledate]).to be_present end end From 502d038e9e0e93dc72d9ab5a2791d7f89f869bfe Mon Sep 17 00:00:00 2001 From: Manny Dinssa <44172848+Dinssa@users.noreply.github.com> Date: Wed, 20 Nov 2024 14:24:37 +0000 Subject: [PATCH 20/34] CLDC-3752: Sales - Hide questions if a staircasing transaction (25/26) (#2780) * Do not ask questions from these sections when it's a staircase transaction * Hide section/subsections from task list * Add tests * Fix lint * Move logic to section model --- .../sales/subsections/household_situation.rb | 15 ++++- .../income_benefits_and_savings.rb | 15 ++++- .../other_household_information.rb | 15 ++++- app/models/form/section.rb | 4 ++ app/views/logs/_tasklist.html.erb | 5 +- .../subsections/household_situation_spec.rb | 22 +++---- .../income_benefits_and_savings_spec.rb | 29 ++++++--- .../other_household_information_spec.rb | 62 +++++++++++-------- 8 files changed, 116 insertions(+), 51 deletions(-) diff --git a/app/models/form/sales/subsections/household_situation.rb b/app/models/form/sales/subsections/household_situation.rb index 2e496908b..d5b8ab7e2 100644 --- a/app/models/form/sales/subsections/household_situation.rb +++ b/app/models/form/sales/subsections/household_situation.rb @@ -3,7 +3,14 @@ class Form::Sales::Subsections::HouseholdSituation < ::Form::Subsection super @id = "household_situation" @label = "Household situation" - @depends_on = [{ "setup_completed?" => true }] + end + + def depends_on + if form.start_year_2025_or_later? + [{ "setup_completed?" => true, "is_staircase?" => false }] + else + [{ "setup_completed?" => true }] + end end def pages @@ -16,4 +23,10 @@ class Form::Sales::Subsections::HouseholdSituation < ::Form::Subsection Form::Sales::Pages::Buyer2PreviousHousingSituation.new(nil, nil, self), ].flatten.compact end + + def displayed_in_tasklist?(log) + return true unless form.start_year_2025_or_later? + + log.staircase != 1 + end end diff --git a/app/models/form/sales/subsections/income_benefits_and_savings.rb b/app/models/form/sales/subsections/income_benefits_and_savings.rb index 19b6e7e03..9244267b9 100644 --- a/app/models/form/sales/subsections/income_benefits_and_savings.rb +++ b/app/models/form/sales/subsections/income_benefits_and_savings.rb @@ -3,7 +3,14 @@ class Form::Sales::Subsections::IncomeBenefitsAndSavings < ::Form::Subsection super @id = "income_benefits_and_savings" @label = "Income, benefits and savings" - @depends_on = [{ "setup_completed?" => true }] + end + + def depends_on + if form.start_year_2025_or_later? + [{ "setup_completed?" => true, "is_staircase?" => false }] + else + [{ "setup_completed?" => true }] + end end def pages @@ -36,6 +43,12 @@ class Form::Sales::Subsections::IncomeBenefitsAndSavings < ::Form::Subsection ].compact end + def displayed_in_tasklist?(log) + return true unless form.start_year_2025_or_later? + + log.staircase != 1 + end + private def previous_shared_page diff --git a/app/models/form/sales/subsections/other_household_information.rb b/app/models/form/sales/subsections/other_household_information.rb index 3acc9ffd5..e30afddc1 100644 --- a/app/models/form/sales/subsections/other_household_information.rb +++ b/app/models/form/sales/subsections/other_household_information.rb @@ -3,7 +3,14 @@ class Form::Sales::Subsections::OtherHouseholdInformation < ::Form::Subsection super @id = "other_household_information" @label = "Other household information" - @depends_on = [{ "setup_completed?" => true }] + end + + def depends_on + if form.start_year_2025_or_later? + [{ "setup_completed?" => true, "is_staircase?" => false }] + else + [{ "setup_completed?" => true }] + end end def pages @@ -17,4 +24,10 @@ class Form::Sales::Subsections::OtherHouseholdInformation < ::Form::Subsection Form::Sales::Pages::HouseholdWheelchairCheck.new("wheelchair_check", nil, self), ] end + + def displayed_in_tasklist?(log) + return true unless form.start_year_2025_or_later? + + log.staircase != 1 + end end diff --git a/app/models/form/section.rb b/app/models/form/section.rb index 97a128f74..a836bd45a 100644 --- a/app/models/form/section.rb +++ b/app/models/form/section.rb @@ -10,4 +10,8 @@ class Form::Section @subsections = hsh["subsections"].map { |s_id, s| Form::Subsection.new(s_id, s, self) } end end + + def displayed_in_tasklist?(log) + subsections.any? { |subsection| subsection.displayed_in_tasklist?(log) } + end end diff --git a/app/views/logs/_tasklist.html.erb b/app/views/logs/_tasklist.html.erb index df8a3afad..e2f977a70 100644 --- a/app/views/logs/_tasklist.html.erb +++ b/app/views/logs/_tasklist.html.erb @@ -1,5 +1,6 @@
    - <% @log.form.sections.map do |section| %> + <% @log.form.sections.each do |section| %> + <% next unless section.displayed_in_tasklist?(@log) %>
  1. <%= section.label %> @@ -8,7 +9,7 @@

    <%= section.description.html_safe %>

    <% end %>
      - <% section.subsections.map do |subsection| %> + <% section.subsections.each do |subsection| %> <% if subsection.displayed_in_tasklist?(@log) && (subsection.applicable_questions(@log).count > 0 || !subsection.enabled?(@log)) %> <% subsection_status = subsection.status(@log) %>
    • diff --git a/spec/models/form/sales/subsections/household_situation_spec.rb b/spec/models/form/sales/subsections/household_situation_spec.rb index 903960a8d..821847d9e 100644 --- a/spec/models/form/sales/subsections/household_situation_spec.rb +++ b/spec/models/form/sales/subsections/household_situation_spec.rb @@ -25,6 +25,14 @@ RSpec.describe Form::Sales::Subsections::HouseholdSituation, type: :model do ], ) end + + it "has the correct id" do + expect(household_characteristics.id).to eq("household_situation") + end + + it "has the correct label" do + expect(household_characteristics.label).to eq("Household situation") + end end context "when the start year is 2025" do @@ -41,17 +49,9 @@ RSpec.describe Form::Sales::Subsections::HouseholdSituation, type: :model do ], ) end - end - it "has the correct id" do - expect(household_characteristics.id).to eq("household_situation") - end - - it "has the correct label" do - expect(household_characteristics.label).to eq("Household situation") - end - - it "has correct depends on" do - expect(household_characteristics.depends_on).to eq([{ "setup_completed?" => true }]) + it "has correct depends on" do + expect(household_characteristics.depends_on).to eq([{ "setup_completed?" => true, "is_staircase?" => false }]) + end end end diff --git a/spec/models/form/sales/subsections/income_benefits_and_savings_spec.rb b/spec/models/form/sales/subsections/income_benefits_and_savings_spec.rb index 22a84f2a9..557155758 100644 --- a/spec/models/form/sales/subsections/income_benefits_and_savings_spec.rb +++ b/spec/models/form/sales/subsections/income_benefits_and_savings_spec.rb @@ -11,8 +11,16 @@ RSpec.describe Form::Sales::Subsections::IncomeBenefitsAndSavings, type: :model expect(subsection.section).to eq(section) end + it "has the correct id" do + expect(subsection.id).to eq("income_benefits_and_savings") + end + + it "has the correct label" do + expect(subsection.label).to eq("Income, benefits and savings") + end + describe "pages" do - let(:section) { instance_double(Form::Sales::Sections::Household, form: instance_double(Form, start_date:)) } + let(:section) { instance_double(Form::Sales::Sections::Household, form: instance_double(Form, start_date:, start_year_2025_or_later?: false)) } context "when 2022" do let(:start_date) { Time.utc(2022, 2, 8) } @@ -83,18 +91,19 @@ RSpec.describe Form::Sales::Subsections::IncomeBenefitsAndSavings, type: :model ], ) end - end - end - it "has the correct id" do - expect(subsection.id).to eq("income_benefits_and_savings") + it "has correct depends on" do + expect(subsection.depends_on).to eq([{ "setup_completed?" => true }]) + end + end end - it "has the correct label" do - expect(subsection.label).to eq("Income, benefits and savings") - end + context "when 2025" do + let(:start_date) { Time.utc(2025, 2, 8) } + let(:section) { instance_double(Form::Sales::Sections::Household, form: instance_double(Form, start_date:, start_year_2025_or_later?: true)) } - it "has correct depends on" do - expect(subsection.depends_on).to eq([{ "setup_completed?" => true }]) + it "has correct depends on" do + expect(subsection.depends_on).to eq([{ "setup_completed?" => true, "is_staircase?" => false }]) + end end end diff --git a/spec/models/form/sales/subsections/other_household_information_spec.rb b/spec/models/form/sales/subsections/other_household_information_spec.rb index 461ad2cc9..836540b5b 100644 --- a/spec/models/form/sales/subsections/other_household_information_spec.rb +++ b/spec/models/form/sales/subsections/other_household_information_spec.rb @@ -5,36 +5,48 @@ RSpec.describe Form::Sales::Subsections::OtherHouseholdInformation, type: :model let(:subsection_id) { nil } let(:subsection_definition) { nil } - let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1)) } - let(:section) { instance_double(Form::Sales::Sections::Household, form:) } - it "has correct section" do - expect(household_characteristics.section).to eq(section) - end + context "when 2024" do + let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1), start_year_2025_or_later?: false) } + let(:section) { instance_double(Form::Sales::Sections::Household, form:) } - it "has correct pages" do - expect(household_characteristics.pages.map(&:id)).to eq( - %w[ - armed_forces - buyer_still_serving - armed_forces_spouse - household_disability - disability_wheelchair_check - household_wheelchair - wheelchair_check - ], - ) - end + it "has correct section" do + expect(household_characteristics.section).to eq(section) + end - it "has the correct id" do - expect(household_characteristics.id).to eq("other_household_information") - end + it "has correct pages" do + expect(household_characteristics.pages.map(&:id)).to eq( + %w[ + armed_forces + buyer_still_serving + armed_forces_spouse + household_disability + disability_wheelchair_check + household_wheelchair + wheelchair_check + ], + ) + end - it "has the correct label" do - expect(household_characteristics.label).to eq("Other household information") + it "has the correct id" do + expect(household_characteristics.id).to eq("other_household_information") + end + + it "has the correct label" do + expect(household_characteristics.label).to eq("Other household information") + end + + it "has correct depends on" do + expect(household_characteristics.depends_on).to eq([{ "setup_completed?" => true }]) + end end - it "has correct depends on" do - expect(household_characteristics.depends_on).to eq([{ "setup_completed?" => true }]) + context "when 2025" do + let(:form) { instance_double(Form, start_date: Time.zone.local(2025, 4, 1), start_year_2025_or_later?: true) } + let(:section) { instance_double(Form::Sales::Sections::Household, form:) } + + it "has correct depends on" do + expect(household_characteristics.depends_on).to eq([{ "setup_completed?" => true, "is_staircase?" => false }]) + end end end From 671232daa91f74a8702fdc1e4c5e09093219e710 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Wed, 20 Nov 2024 14:42:19 +0000 Subject: [PATCH 21/34] CLDC-3734 Add LA in England validation for 2025 (#2763) * Add LA in England validation for 2025 * Validate supported housing logs * Add error message to the date * Update error messages * lint * Fix error messages for date * Update no address found soft validation * Update validation messages --- .../form/lettings/pages/no_address_found.rb | 5 +- .../validations/property_validations.rb | 27 +++++++ .../validations/sales/property_validations.rb | 16 ++++ .../2024/lettings/soft_validations.en.yml | 8 ++ .../lettings/property_information.en.yml | 17 ++++- .../sales/property_information.en.yml | 11 +++ .../lettings/pages/no_address_found_spec.rb | 7 +- .../validations/property_validations_spec.rb | 74 +++++++++++++++++++ .../sales/property_validations_spec.rb | 56 ++++++++++++++ 9 files changed, 215 insertions(+), 6 deletions(-) diff --git a/app/models/form/lettings/pages/no_address_found.rb b/app/models/form/lettings/pages/no_address_found.rb index b1a78caf6..631d6f58e 100644 --- a/app/models/form/lettings/pages/no_address_found.rb +++ b/app/models/form/lettings/pages/no_address_found.rb @@ -3,12 +3,13 @@ class Form::Lettings::Pages::NoAddressFound < ::Form::Page super @id = "no_address_found" @type = "interruption_screen" + @copy_key = "lettings.soft_validations.no_address_found" @title_text = { - "translation" => "soft_validations.no_address_found.title_text", + "translation" => "forms.#{form.start_date.year}.#{@copy_key}.title_text", "arguments" => [], } @informative_text = { - "translation" => "soft_validations.no_address_found.informative_text", + "translation" => "forms.#{form.start_date.year}.#{@copy_key}.informative_text", "arguments" => [], } @depends_on = [ diff --git a/app/models/validations/property_validations.rb b/app/models/validations/property_validations.rb index 09e6d1436..e9eba8184 100644 --- a/app/models/validations/property_validations.rb +++ b/app/models/validations/property_validations.rb @@ -46,4 +46,31 @@ module Validations::PropertyValidations record.errors.add :postcode_full, :wrong_format, message: error_message end end + + def validate_la_in_england(record) + return unless record.form.start_year_2025_or_later? + + if record.is_general_needs? + return unless record.la + return if record.la.in?(LocalAuthority.england.pluck(:code)) + + record.errors.add :la, I18n.t("validations.lettings.property.la.not_in_england") + record.errors.add :postcode_full, I18n.t("validations.lettings.property.postcode_full.not_in_england") + record.errors.add :uprn, I18n.t("validations.lettings.property.uprn.not_in_england") + record.errors.add :uprn_confirmation, I18n.t("validations.lettings.property.uprn_confirmation.not_in_england") + record.errors.add :uprn_selection, I18n.t("validations.lettings.property.uprn_selection.not_in_england") + if record.uprn.present? + record.errors.add :startdate, I18n.t("validations.lettings.property.startdate.address_not_in_england") + else + record.errors.add :startdate, I18n.t("validations.lettings.property.startdate.postcode_not_in_england") + end + elsif record.is_supported_housing? + return unless record.location + return if record.location.location_code.in?(LocalAuthority.england.pluck(:code)) + + record.errors.add :location_id, I18n.t("validations.lettings.property.location_id.not_in_england") + record.errors.add :scheme_id, I18n.t("validations.lettings.property.scheme_id.not_in_england") + record.errors.add :startdate, I18n.t("validations.lettings.property.startdate.location_not_in_england") + end + end end diff --git a/app/models/validations/sales/property_validations.rb b/app/models/validations/sales/property_validations.rb index 5cf70ed8d..2238a634a 100644 --- a/app/models/validations/sales/property_validations.rb +++ b/app/models/validations/sales/property_validations.rb @@ -36,4 +36,20 @@ module Validations::Sales::PropertyValidations record.errors.add :postcode_full, :wrong_format, message: error_message end end + + def validate_la_in_england(record) + return unless record.form.start_year_2025_or_later? && record.la.present? + return if record.la.in?(LocalAuthority.england.pluck(:code)) + + record.errors.add :la, I18n.t("validations.sales.property_information.la.not_in_england") + record.errors.add :postcode_full, I18n.t("validations.sales.property_information.postcode_full.not_in_england") + record.errors.add :uprn, I18n.t("validations.sales.property_information.uprn.not_in_england") + record.errors.add :uprn_confirmation, I18n.t("validations.sales.property_information.uprn_confirmation.not_in_england") + record.errors.add :uprn_selection, I18n.t("validations.sales.property_information.uprn_selection.not_in_england") + if record.uprn.present? + record.errors.add :saledate, I18n.t("validations.sales.property_information.saledate.address_not_in_england") + else + record.errors.add :saledate, I18n.t("validations.sales.property_information.saledate.postcode_not_in_england") + end + end end diff --git a/config/locales/forms/2024/lettings/soft_validations.en.yml b/config/locales/forms/2024/lettings/soft_validations.en.yml index ada093d39..b4a76af04 100644 --- a/config/locales/forms/2024/lettings/soft_validations.en.yml +++ b/config/locales/forms/2024/lettings/soft_validations.en.yml @@ -130,3 +130,11 @@ en: question_text: "Are you sure the property has been vacant for this long?" title_text: "You told us the property has been vacant for 2 years." informative_text: "This is longer than we would expect." + + no_address_found: + page_header: "" + check_answer_label: "No address found" + hint_text: "" + question_text: "We could not find an address that matches your search. You can search again or continue to enter the address manually." + title_text: "No address found" + informative_text: "We could not find an address that matches your search. You can search again or continue to enter the address manually." diff --git a/config/locales/validations/lettings/property_information.en.yml b/config/locales/validations/lettings/property_information.en.yml index 6530c9488..091e89664 100644 --- a/config/locales/validations/lettings/property_information.en.yml +++ b/config/locales/validations/lettings/property_information.en.yml @@ -4,6 +4,7 @@ en: property: postcode_full: invalid: "Enter a postcode in the correct format, for example AA1 1AA." + not_in_england: "It looks like you have an entered a postcode outside of England. Only create logs for lettings in England." rsnvac: non_temp_accommodation: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as this accommodation is not temporary." referral_invalid: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as a different source of referral for this letting." @@ -18,4 +19,18 @@ en: one_seven_bedroom_shared: "A shared house must have 1 to 7 bedrooms." uprn: invalid: "UPRN must be 12 digits or less." - + not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England." + uprn_confirmation: + not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England." + uprn_selection: + not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England." + la: + not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England." + scheme_id: + not_in_england: "This scheme’s only location is outside of England. Only create logs for lettings in England." + location_id: + not_in_england: "It looks like you have selected a location outside of England. Only create logs for lettings in England." + startdate: + postcode_not_in_england: "It looks like you have an entered a postcode outside of England. Only create logs for lettings in England." + address_not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England." + location_not_in_england: "It looks like you have selected a location outside of England. Only create logs for lettings in England." diff --git a/config/locales/validations/sales/property_information.en.yml b/config/locales/validations/sales/property_information.en.yml index a91e47849..9014f1d78 100644 --- a/config/locales/validations/sales/property_information.en.yml +++ b/config/locales/validations/sales/property_information.en.yml @@ -7,6 +7,7 @@ en: joint_purchase: "Buyers’ last accommodation and discounted ownership postcodes must match." not_joint_purchase: "Buyer’s last accommodation and discounted ownership postcodes must match." invalid: "Enter a postcode in the correct format, for example AA1 1AA." + not_in_england: "It looks like you have entered a postcode outside of England - only submit Lettings forms for Lettings that occur in England" ppostcode_full: postcode_must_match_previous: joint_purchase: "Buyers’ last accommodation and discounted ownership postcodes must match." @@ -20,9 +21,19 @@ en: joint_purchase: "Buyers’ last accommodation and discounted ownership postcodes must match." not_joint_purchase: "Buyer’s last accommodation and discounted ownership postcodes must match." invalid: "UPRN must be 12 digits or less." + not_in_england: "It looks like you have an entered a postcode outside of England. Only create logs for lettings in England." beds: bedsits_have_max_one_bedroom: "Number of bedrooms must be 1 if the property is a bedsit." proptype: bedsits_have_max_one_bedroom: "Answer cannot be 'Bedsit' if the property has 2 or more bedrooms." uprn_known: invalid: "You must answer UPRN known?" + la: + not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England." + uprn_confirmation: + not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England." + uprn_selection: + not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England." + saledate: + postcode_not_in_england: "It looks like you have an entered a postcode outside of England. Only create logs for lettings in England." + address_not_in_england: "It looks like you have entered an address outside of England. Only create logs for lettings in England." diff --git a/spec/models/form/lettings/pages/no_address_found_spec.rb b/spec/models/form/lettings/pages/no_address_found_spec.rb index ad8c04e04..1825d4d30 100644 --- a/spec/models/form/lettings/pages/no_address_found_spec.rb +++ b/spec/models/form/lettings/pages/no_address_found_spec.rb @@ -5,7 +5,8 @@ RSpec.describe Form::Lettings::Pages::NoAddressFound, type: :model do let(:page_id) { nil } let(:page_definition) { nil } - let(:subsection) { instance_double(Form::Subsection) } + let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1)) } + let(:subsection) { instance_double(Form::Subsection, form:) } let(:log) { create(:lettings_log) } it "has correct subsection" do @@ -37,11 +38,11 @@ RSpec.describe Form::Lettings::Pages::NoAddressFound, type: :model do end it "has the correct title_text" do - expect(page.title_text).to eq({ "arguments" => [], "translation" => "soft_validations.no_address_found.title_text" }) + expect(page.title_text).to eq({ "arguments" => [], "translation" => "forms.2024.lettings.soft_validations.no_address_found.title_text" }) end it "has the correct informative_text" do - expect(page.informative_text).to eq({ "arguments" => [], "translation" => "soft_validations.no_address_found.informative_text" }) + expect(page.informative_text).to eq({ "arguments" => [], "translation" => "forms.2024.lettings.soft_validations.no_address_found.informative_text" }) end it "has the correct interruption_screen_question_ids" do diff --git a/spec/models/validations/property_validations_spec.rb b/spec/models/validations/property_validations_spec.rb index f478ae466..142cb2491 100644 --- a/spec/models/validations/property_validations_spec.rb +++ b/spec/models/validations/property_validations_spec.rb @@ -204,4 +204,78 @@ RSpec.describe Validations::PropertyValidations do end end end + + describe "#validate_la_in_england" do + context "with a log on or after 2025" do + before do + allow(log.form).to receive(:start_year_2025_or_later?).and_return true + end + + context "and the local authority is not in England for general needs log" do + let(:log) { build(:lettings_log, la: "S12000019", needstype: 1) } + + it "adds an error" do + property_validator.validate_la_in_england(log) + expect(log.errors["la"]).to include(I18n.t("validations.lettings.property.la.not_in_england")) + expect(log.errors["postcode_full"]).to include(I18n.t("validations.lettings.property.postcode_full.not_in_england")) + expect(log.errors["uprn"]).to include(I18n.t("validations.lettings.property.uprn.not_in_england")) + expect(log.errors["uprn_confirmation"]).to include(I18n.t("validations.lettings.property.uprn_confirmation.not_in_england")) + expect(log.errors["uprn_selection"]).to include(I18n.t("validations.lettings.property.uprn_selection.not_in_england")) + expect(log.errors["startdate"]).to include(I18n.t("validations.lettings.property.startdate.postcode_not_in_england")) + expect(log.errors["scheme_id"]).to be_empty + expect(log.errors["location_id"]).to be_empty + end + end + + context "and the local authority is not in England for supported housing log" do + let(:location) { create(:location, location_code: "S12000019") } + let(:log) { build(:lettings_log, la: "S12000019", needstype: 2, location:) } + + it "adds an error" do + property_validator.validate_la_in_england(log) + expect(log.errors["scheme_id"]).to include(I18n.t("validations.lettings.property.scheme_id.not_in_england")) + expect(log.errors["location_id"]).to include(I18n.t("validations.lettings.property.location_id.not_in_england")) + expect(log.errors["startdate"]).to include(I18n.t("validations.lettings.property.startdate.location_not_in_england")) + expect(log.errors["la"]).to be_empty + expect(log.errors["postcode_full"]).to be_empty + expect(log.errors["uprn"]).to be_empty + expect(log.errors["uprn_confirmation"]).to be_empty + expect(log.errors["uprn_selection"]).to be_empty + end + end + + context "and the local authority is in England" do + let(:log) { build(:lettings_log, la: "E06000002") } + + it "does not add an error" do + property_validator.validate_la_in_england(log) + expect(log.errors["la"]).to be_empty + expect(log.errors["postcode_full"]).to be_empty + expect(log.errors["uprn"]).to be_empty + expect(log.errors["uprn_confirmation"]).to be_empty + expect(log.errors["uprn_selection"]).to be_empty + expect(log.errors["startdate"]).to be_empty + end + end + end + + context "with a log before 2025" do + before do + allow(log.form).to receive(:start_year_2025_or_later?).and_return false + end + + context "and the local authority is not in England" do + let(:log) { build(:lettings_log, la: "S12000019") } + + it "does not add an error" do + property_validator.validate_la_in_england(log) + expect(log.errors["la"]).to be_empty + expect(log.errors["postcode_full"]).to be_empty + expect(log.errors["uprn"]).to be_empty + expect(log.errors["uprn_confirmation"]).to be_empty + expect(log.errors["uprn_selection"]).to be_empty + end + end + end + end end diff --git a/spec/models/validations/sales/property_validations_spec.rb b/spec/models/validations/sales/property_validations_spec.rb index df0396845..c5af3ae78 100644 --- a/spec/models/validations/sales/property_validations_spec.rb +++ b/spec/models/validations/sales/property_validations_spec.rb @@ -142,4 +142,60 @@ RSpec.describe Validations::Sales::PropertyValidations do end end end + + describe "#validate_la_in_england" do + context "with a log on or after 2025" do + before do + allow(log.form).to receive(:start_year_2025_or_later?).and_return true + end + + context "and the local authority is not in England" do + let(:log) { build(:sales_log, la: "S12000019") } + + it "adds an error" do + property_validator.validate_la_in_england(log) + expect(log.errors["la"]).to include(I18n.t("validations.sales.property_information.la.not_in_england")) + expect(log.errors["postcode_full"]).to include(I18n.t("validations.sales.property_information.postcode_full.not_in_england")) + expect(log.errors["uprn"]).to include(I18n.t("validations.sales.property_information.uprn.not_in_england")) + expect(log.errors["uprn_confirmation"]).to include(I18n.t("validations.sales.property_information.uprn_confirmation.not_in_england")) + expect(log.errors["uprn_selection"]).to include(I18n.t("validations.sales.property_information.uprn_selection.not_in_england")) + expect(log.errors["saledate"]).to include(I18n.t("validations.sales.property_information.saledate.postcode_not_in_england")) + end + end + + context "and the local authority is in England" do + let(:log) { build(:sales_log, la: "E06000002") } + + it "does not add an error" do + property_validator.validate_la_in_england(log) + expect(log.errors["la"]).to be_empty + expect(log.errors["postcode_full"]).to be_empty + expect(log.errors["uprn"]).to be_empty + expect(log.errors["uprn_confirmation"]).to be_empty + expect(log.errors["uprn_selection"]).to be_empty + expect(log.errors["saledate"]).to be_empty + end + end + end + + context "with a log before 2025" do + before do + allow(log.form).to receive(:start_year_2025_or_later?).and_return false + end + + context "and the local authority is not in England" do + let(:log) { build(:sales_log, la: "S12000019") } + + it "does not add an error" do + property_validator.validate_la_in_england(log) + expect(log.errors["la"]).to be_empty + expect(log.errors["postcode_full"]).to be_empty + expect(log.errors["uprn"]).to be_empty + expect(log.errors["uprn_confirmation"]).to be_empty + expect(log.errors["uprn_selection"]).to be_empty + expect(log.errors["saledate"]).to be_empty + end + end + end + end end From 9628b4e915b71b0883101b40d5e617ccfc79a794 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Fri, 22 Nov 2024 08:37:01 +0000 Subject: [PATCH 22/34] Update hint text (#2796) --- config/locales/forms/2024/lettings/setup.en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/forms/2024/lettings/setup.en.yml b/config/locales/forms/2024/lettings/setup.en.yml index 5a18fe719..68bc95364 100644 --- a/config/locales/forms/2024/lettings/setup.en.yml +++ b/config/locales/forms/2024/lettings/setup.en.yml @@ -30,7 +30,7 @@ en: scheme_id: page_header: "Scheme" check_answer_label: "Scheme name" - hint_text: "Enter postcode or scheme name.

      A supported housing scheme provides shared or self-contained housing for a particular client group, for example younger or vulnerable people." + hint_text: "Enter postcode, scheme name, or scheme code (for example, S123).

      A supported housing scheme provides shared or self-contained housing for a particular client group, for example younger or vulnerable people." question_text: "What scheme is this log for?" location_id: From 6b61a68df739871d1304a446fe386d5a1e62a1b5 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Fri, 22 Nov 2024 08:41:32 +0000 Subject: [PATCH 23/34] CLDC-3700 Update csv downloads (#2785) * Save a csv download record * Allow downloading CSVs * Make links expire * Send correct download link * Fix world * Make csv downloads work locally * Set expiration time on the record * lint * Update download urls * Update expiration time * Update expired link content * Add a page to view the download * lint * lint again --- app/controllers/csv_downloads_controller.rb | 27 ++++ app/jobs/email_csv_job.rb | 13 +- app/jobs/scheme_email_csv_job.rb | 13 +- app/models/csv_download.rb | 10 ++ app/policies/csv_download_policy.rb | 16 +++ app/services/csv/downloader.rb | 50 +++++++ app/views/csv_downloads/show.html.erb | 10 ++ .../errors/download_link_expired.html.erb | 8 ++ config/routes.rb | 7 + .../20241118104046_add_csv_download_table.rb | 12 ++ db/schema.rb | 14 +- spec/factories/csv_download.rb | 9 ++ spec/jobs/email_csv_job_spec.rb | 25 +++- spec/jobs/scheme_email_csv_job_spec.rb | 36 ++++- .../requests/csv_downloads_controller_spec.rb | 136 ++++++++++++++++++ 15 files changed, 371 insertions(+), 15 deletions(-) create mode 100644 app/controllers/csv_downloads_controller.rb create mode 100644 app/models/csv_download.rb create mode 100644 app/policies/csv_download_policy.rb create mode 100644 app/services/csv/downloader.rb create mode 100644 app/views/csv_downloads/show.html.erb create mode 100644 app/views/errors/download_link_expired.html.erb create mode 100644 db/migrate/20241118104046_add_csv_download_table.rb create mode 100644 spec/factories/csv_download.rb create mode 100644 spec/requests/csv_downloads_controller_spec.rb diff --git a/app/controllers/csv_downloads_controller.rb b/app/controllers/csv_downloads_controller.rb new file mode 100644 index 000000000..25f70026f --- /dev/null +++ b/app/controllers/csv_downloads_controller.rb @@ -0,0 +1,27 @@ +class CsvDownloadsController < ApplicationController + before_action :authenticate_user! + + def show + @csv_download = CsvDownload.find(params[:id]) + authorize @csv_download + + return render "errors/download_link_expired" if @csv_download.expired? + end + + def download + csv_download = CsvDownload.find(params[:id]) + authorize csv_download + + return render "errors/download_link_expired" if csv_download.expired? + + downloader = Csv::Downloader.new(csv_download:) + + if Rails.env.development? + downloader.call + send_file downloader.path, filename: csv_download.filename, type: "text/csv" + else + presigned_url = downloader.presigned_url + redirect_to presigned_url, allow_other_host: true + end + end +end diff --git a/app/jobs/email_csv_job.rb b/app/jobs/email_csv_job.rb index 58f2d50b8..dd0f2917c 100644 --- a/app/jobs/email_csv_job.rb +++ b/app/jobs/email_csv_job.rb @@ -1,9 +1,10 @@ class EmailCsvJob < ApplicationJob + include Rails.application.routes.url_helpers queue_as :default BYTE_ORDER_MARK = "\uFEFF".freeze # Required to ensure Excel always reads CSV as UTF-8 - EXPIRATION_TIME = 24.hours.to_i + EXPIRATION_TIME = 48.hours.to_i def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil, codes_only_export = false, log_type = "lettings", year = nil) # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params export_type = codes_only_export ? "codes" : "labels" @@ -20,10 +21,16 @@ class EmailCsvJob < ApplicationJob filename = "#{[log_type, 'logs', organisation&.name, Time.zone.now].compact.join('-')}.csv" - storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"]) + storage_service = if FeatureToggle.upload_enabled? + Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"]) + else + Storage::LocalDiskService.new + end + storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string) + csv_download = CsvDownload.create!(user:, organisation: user.organisation, filename:, download_type: log_type, expiration_time: EXPIRATION_TIME) - url = storage_service.get_presigned_url(filename, EXPIRATION_TIME) + url = csv_download_url(csv_download.id, host: ENV["APP_HOST"]) CsvDownloadMailer.new.send_csv_download_mail(user, url, EXPIRATION_TIME) end diff --git a/app/jobs/scheme_email_csv_job.rb b/app/jobs/scheme_email_csv_job.rb index 44d016a90..803d3dce3 100644 --- a/app/jobs/scheme_email_csv_job.rb +++ b/app/jobs/scheme_email_csv_job.rb @@ -1,9 +1,10 @@ class SchemeEmailCsvJob < ApplicationJob + include Rails.application.routes.url_helpers queue_as :default BYTE_ORDER_MARK = "\uFEFF".freeze # Required to ensure Excel always reads CSV as UTF-8 - EXPIRATION_TIME = 24.hours.to_i + EXPIRATION_TIME = 48.hours.to_i def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil, download_type = "combined") # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params unfiltered_schemes = if organisation.present? && user.support? @@ -23,10 +24,16 @@ class SchemeEmailCsvJob < ApplicationJob filename = "#{['schemes-and-locations', organisation&.name, Time.zone.now].compact.join('-')}.csv" end - storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"]) + storage_service = if FeatureToggle.upload_enabled? + Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"]) + else + Storage::LocalDiskService.new + end + storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string) + csv_download = CsvDownload.create!(user:, organisation: user.organisation, filename:, download_type:, expiration_time: EXPIRATION_TIME) - url = storage_service.get_presigned_url(filename, EXPIRATION_TIME) + url = csv_download_url(csv_download.id, host: ENV["APP_HOST"]) CsvDownloadMailer.new.send_csv_download_mail(user, url, EXPIRATION_TIME) end diff --git a/app/models/csv_download.rb b/app/models/csv_download.rb new file mode 100644 index 000000000..4064c62f3 --- /dev/null +++ b/app/models/csv_download.rb @@ -0,0 +1,10 @@ +class CsvDownload < ApplicationRecord + enum download_type: { lettings: "lettings", sales: "sales", schemes: "schemes", locations: "locations", combined: "combined" } + + belongs_to :user + belongs_to :organisation + + def expired? + created_at < expiration_time.seconds.ago + end +end diff --git a/app/policies/csv_download_policy.rb b/app/policies/csv_download_policy.rb new file mode 100644 index 000000000..04471ccd0 --- /dev/null +++ b/app/policies/csv_download_policy.rb @@ -0,0 +1,16 @@ +class CsvDownloadPolicy + attr_reader :current_user, :csv_download + + def initialize(current_user, csv_download) + @current_user = current_user + @csv_download = csv_download + end + + def show? + @current_user == @csv_download.user || @current_user.support? || @current_user.organisation == @csv_download.organisation + end + + def download? + @current_user == @csv_download.user || @current_user.support? || @current_user.organisation == @csv_download.organisation + end +end diff --git a/app/services/csv/downloader.rb b/app/services/csv/downloader.rb new file mode 100644 index 000000000..24545bc41 --- /dev/null +++ b/app/services/csv/downloader.rb @@ -0,0 +1,50 @@ +class Csv::Downloader + attr_reader :csv_download + + delegate :path, to: :file + + def initialize(csv_download:) + @csv_download = csv_download + end + + def call + download + end + + def delete_local_file! + file.unlink + end + + def presigned_url + s3_storage_service.get_presigned_url(csv_download.filename, 60, response_content_disposition: "attachment; filename=#{csv_download.filename}") + end + +private + + def download + io = storage_service.get_file_io(csv_download.filename) + file.write(io.read) + io.close + file.close + end + + def file + @file ||= Tempfile.new + end + + def storage_service + @storage_service ||= if FeatureToggle.upload_enabled? + s3_storage_service + else + local_disk_storage_service + end + end + + def s3_storage_service + Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"]) + end + + def local_disk_storage_service + Storage::LocalDiskService.new + end +end diff --git a/app/views/csv_downloads/show.html.erb b/app/views/csv_downloads/show.html.erb new file mode 100644 index 000000000..18f8a67fe --- /dev/null +++ b/app/views/csv_downloads/show.html.erb @@ -0,0 +1,10 @@ +<% title = "Downlaod CSV file" %> +<% content_for :title, title %> + +
      +
      +

      You are about to download a CSV file

      +

      Filename: <%= @csv_download.filename %>

      + <%= govuk_button_link_to "Download CSV", download_csv_download_path(@csv_download) %> +
      +
      diff --git a/app/views/errors/download_link_expired.html.erb b/app/views/errors/download_link_expired.html.erb new file mode 100644 index 000000000..7477ee6b3 --- /dev/null +++ b/app/views/errors/download_link_expired.html.erb @@ -0,0 +1,8 @@ +<% content_for :title, "This link has expired" %> + +
      +
      +

      This link has expired.

      +

      Download the logs again to get a new link.

      +
      +
      diff --git a/config/routes.rb b/config/routes.rb index 6ac7b3f34..1c7af8c59 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -382,6 +382,13 @@ Rails.application.routes.draw do end end + resources :csv_downloads, path: "csv-downloads" do + member do + get "/", to: "csv_downloads#show", as: "show" + get "download", to: "csv_downloads#download" + end + end + scope via: :all do match "/404", to: "errors#not_found" match "/429", to: "errors#too_many_requests", status: 429 diff --git a/db/migrate/20241118104046_add_csv_download_table.rb b/db/migrate/20241118104046_add_csv_download_table.rb new file mode 100644 index 000000000..9b4f73f0b --- /dev/null +++ b/db/migrate/20241118104046_add_csv_download_table.rb @@ -0,0 +1,12 @@ +class AddCsvDownloadTable < ActiveRecord::Migration[7.0] + def change + create_table :csv_downloads do |t| + t.column :download_type, :string + t.column :filename, :string + t.column :expiration_time, :integer + t.timestamps + t.references :user + t.references :organisation + end + end +end diff --git a/db/schema.rb b/db/schema.rb index ef635628c..5b6ddacdb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2024_10_31_102744) do +ActiveRecord::Schema[7.0].define(version: 2024_11_18_104046) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -64,6 +64,18 @@ ActiveRecord::Schema[7.0].define(version: 2024_10_31_102744) do t.datetime "discarded_at" end + create_table "csv_downloads", force: :cascade do |t| + t.string "download_type" + t.string "filename" + t.integer "expiration_time" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.bigint "user_id" + t.bigint "organisation_id" + t.index ["organisation_id"], name: "index_csv_downloads_on_organisation_id" + t.index ["user_id"], name: "index_csv_downloads_on_user_id" + end + create_table "csv_variable_definitions", force: :cascade do |t| t.string "variable", null: false t.string "definition", null: false diff --git a/spec/factories/csv_download.rb b/spec/factories/csv_download.rb new file mode 100644 index 000000000..415f69de6 --- /dev/null +++ b/spec/factories/csv_download.rb @@ -0,0 +1,9 @@ +FactoryBot.define do + factory :csv_download do + download_type { "lettings" } + user { create(:user) } + organisation { user.organisation } + filename { "lettings.csv" } + expiration_time { 24.hours.to_i } + end +end diff --git a/spec/jobs/email_csv_job_spec.rb b/spec/jobs/email_csv_job_spec.rb index 8e8a027ea..59cf7bf7d 100644 --- a/spec/jobs/email_csv_job_spec.rb +++ b/spec/jobs/email_csv_job_spec.rb @@ -3,8 +3,6 @@ require "rails_helper" describe EmailCsvJob do include Helpers - test_url = :test_url - let(:job) { described_class.new } let(:user) { FactoryBot.create(:user) } let(:storage_service) { instance_double(Storage::S3Service) } @@ -22,7 +20,6 @@ describe EmailCsvJob do before do allow(Storage::S3Service).to receive(:new).and_return(storage_service) allow(storage_service).to receive(:write_file) - allow(storage_service).to receive(:get_presigned_url).and_return(test_url) allow(Csv::SalesLogCsvService).to receive(:new).and_return(sales_log_csv_service) allow(sales_log_csv_service).to receive(:prepare_csv).and_return("") @@ -67,6 +64,16 @@ describe EmailCsvJob do expect(lettings_log_csv_service).to receive(:prepare_csv).with(lettings_logs) job.perform(user, nil, {}, nil, nil, codes_only_export) end + + it "creates a CsvDownload record" do + job.perform(user, nil, {}, nil, nil, codes_only_export, "lettings") + expect(CsvDownload.count).to eq(1) + expect(CsvDownload.first.user).to eq(user) + expect(CsvDownload.first.organisation).to eq(user.organisation) + expect(CsvDownload.first.filename).to match(/lettings-logs-.*\.csv/) + expect(CsvDownload.first.download_type).to eq("lettings") + expect(CsvDownload.first.expiration_time).to eq(172_800) + end end context "when exporting sales logs" do @@ -102,10 +109,20 @@ describe EmailCsvJob do expect(sales_log_csv_service).to receive(:prepare_csv).with(sales_logs) job.perform(user, nil, {}, nil, nil, codes_only_export, "sales") end + + it "creates a CsvDownload record" do + job.perform(user, nil, {}, nil, nil, codes_only_export, "sales") + expect(CsvDownload.count).to eq(1) + expect(CsvDownload.first.user).to eq(user) + expect(CsvDownload.first.organisation).to eq(user.organisation) + expect(CsvDownload.first.filename).to match(/sales-logs-.*\.csv/) + expect(CsvDownload.first.download_type).to eq("sales") + expect(CsvDownload.first.expiration_time).to eq(172_800) + end end it "sends an E-mail with the presigned URL and duration" do - expect(mailer).to receive(:send_csv_download_mail).with(user, test_url, instance_of(Integer)) + expect(mailer).to receive(:send_csv_download_mail).with(user, /csv-downloads/, instance_of(Integer)) job.perform(user) end end diff --git a/spec/jobs/scheme_email_csv_job_spec.rb b/spec/jobs/scheme_email_csv_job_spec.rb index 5ddaa91a6..efb75b698 100644 --- a/spec/jobs/scheme_email_csv_job_spec.rb +++ b/spec/jobs/scheme_email_csv_job_spec.rb @@ -3,10 +3,8 @@ require "rails_helper" describe SchemeEmailCsvJob do include Helpers - test_url = :test_url - let(:job) { described_class.new } - let(:storage_service) { instance_double(Storage::S3Service, write_file: nil, get_presigned_url: test_url) } + let(:storage_service) { instance_double(Storage::S3Service, write_file: nil) } let(:mailer) { instance_double(CsvDownloadMailer, send_csv_download_mail: nil) } let(:user) { FactoryBot.create(:user) } @@ -53,6 +51,16 @@ describe SchemeEmailCsvJob do job.perform(user, nil, {}, nil, nil, download_type) end end + + it "creates a CsvDownload record" do + job.perform(user, nil, {}, nil, nil, download_type) + expect(CsvDownload.count).to eq(1) + expect(CsvDownload.first.user).to eq(user) + expect(CsvDownload.first.organisation).to eq(user.organisation) + expect(CsvDownload.first.filename).to match(/schemes-.*\.csv/) + expect(CsvDownload.first.download_type).to eq("schemes") + expect(CsvDownload.first.expiration_time).to eq(172_800) + end end context "when download type locations" do @@ -62,6 +70,16 @@ describe SchemeEmailCsvJob do expect(storage_service).to receive(:write_file).with(/locations-.*\.csv/, anything) job.perform(user, nil, {}, nil, nil, download_type) end + + it "creates a CsvDownload record" do + job.perform(user, nil, {}, nil, nil, download_type) + expect(CsvDownload.count).to eq(1) + expect(CsvDownload.first.user).to eq(user) + expect(CsvDownload.first.organisation).to eq(user.organisation) + expect(CsvDownload.first.filename).to match(/locations-.*\.csv/) + expect(CsvDownload.first.download_type).to eq("locations") + expect(CsvDownload.first.expiration_time).to eq(172_800) + end end context "when download type combined" do @@ -71,6 +89,16 @@ describe SchemeEmailCsvJob do expect(storage_service).to receive(:write_file).with(/schemes-and-locations.*\.csv/, anything) job.perform(user, nil, {}, nil, nil, download_type) end + + it "creates a CsvDownload record" do + job.perform(user, nil, {}, nil, nil, download_type) + expect(CsvDownload.count).to eq(1) + expect(CsvDownload.first.user).to eq(user) + expect(CsvDownload.first.organisation).to eq(user.organisation) + expect(CsvDownload.first.filename).to match(/schemes-and-locations-.*\.csv/) + expect(CsvDownload.first.download_type).to eq("combined") + expect(CsvDownload.first.expiration_time).to eq(172_800) + end end it "includes the organisation name in the filename when one is provided" do @@ -117,7 +145,7 @@ describe SchemeEmailCsvJob do end it "sends an E-mail with the presigned URL and duration" do - expect(mailer).to receive(:send_csv_download_mail).with(user, test_url, instance_of(Integer)) + expect(mailer).to receive(:send_csv_download_mail).with(user, /csv-downloads/, instance_of(Integer)) job.perform(user) end end diff --git a/spec/requests/csv_downloads_controller_spec.rb b/spec/requests/csv_downloads_controller_spec.rb new file mode 100644 index 000000000..982077a12 --- /dev/null +++ b/spec/requests/csv_downloads_controller_spec.rb @@ -0,0 +1,136 @@ +require "rails_helper" + +RSpec.describe CsvDownloadsController, type: :request do + describe "GET #show" do + let(:page) { Capybara::Node::Simple.new(response.body) } + let(:csv_user) { create(:user) } + let(:csv_download) { create(:csv_download, user: csv_user, organisation: csv_user.organisation) } + let(:get_file_io) do + io = StringIO.new + io.write("hello") + io.rewind + io + end + let(:mock_storage_service) { instance_double(Storage::S3Service, get_file_io:, get_presigned_url: "https://example.com") } + + before do + allow(Storage::S3Service).to receive(:new).and_return(mock_storage_service) + end + + context "when user is not signed in" do + it "redirects to sign in page" do + get "/csv-downloads/#{csv_download.id}" + expect(response).to redirect_to("/account/sign-in") + end + end + + context "when user is signed in" do + before do + sign_in user + end + + context "and the user is from a different organisation" do + let(:user) { create(:user) } + + before do + get "/csv-downloads/#{csv_download.id}" + end + + it "returns page not found" do + expect(response).to have_http_status(:unauthorized) + end + end + + context "and is the user who generated the csv" do + let(:user) { csv_user } + + before do + get "/csv-downloads/#{csv_download.id}" + end + + it "allows downloading the csv" do + expect(response).to have_http_status(:ok) + expect(page).to have_link("Download CSV", href: "/csv-downloads/#{csv_download.id}/download") + end + end + + context "and is the user is from the same organisation" do + let(:user) { create(:user, organisation: csv_user.organisation) } + + before do + get "/csv-downloads/#{csv_download.id}" + end + + it "allows downloading the csv" do + expect(response).to have_http_status(:ok) + expect(page).to have_link("Download CSV", href: "/csv-downloads/#{csv_download.id}/download") + end + end + end + end + + describe "GET #download" do + let(:csv_user) { create(:user) } + let(:csv_download) { create(:csv_download, user: csv_user, organisation: csv_user.organisation) } + let(:get_file_io) do + io = StringIO.new + io.write("hello") + io.rewind + io + end + let(:mock_storage_service) { instance_double(Storage::S3Service, get_file_io:, get_presigned_url: "https://example.com") } + + before do + allow(Storage::S3Service).to receive(:new).and_return(mock_storage_service) + end + + context "when user is not signed in" do + it "redirects to sign in page" do + get "/csv-downloads/#{csv_download.id}/download" + expect(response).to redirect_to("/account/sign-in") + end + end + + context "when user is signed in" do + before do + sign_in user + end + + context "and the user is from a different organisation" do + let(:user) { create(:user) } + + before do + get "/csv-downloads/#{csv_download.id}/download" + end + + it "returns page not found" do + expect(response).to have_http_status(:unauthorized) + end + end + + context "and is the user who generated the csv" do + let(:user) { csv_user } + + before do + get "/csv-downloads/#{csv_download.id}/download" + end + + it "allows downloading the csv" do + expect(response).to have_http_status(:found) + end + end + + context "and is the user is from the same organisation" do + let(:user) { create(:user, organisation: csv_user.organisation) } + + before do + get "/csv-downloads/#{csv_download.id}/download" + end + + it "allows downloading the csv" do + expect(response).to have_http_status(:found) + end + end + end + end +end From 8c092a02e2854560c9defa212db394064d4315ae Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Fri, 22 Nov 2024 08:42:35 +0000 Subject: [PATCH 24/34] Update paper form display name (#2797) --- .../mandatory_collection_resources_service.rb | 2 +- spec/factories/collection_resource.rb | 2 +- spec/helpers/collection_resources_helper_spec.rb | 14 +++++++------- .../mandatory_collection_resources_service_spec.rb | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/services/mandatory_collection_resources_service.rb b/app/services/mandatory_collection_resources_service.rb index 397e4b5d0..197e521d4 100644 --- a/app/services/mandatory_collection_resources_service.rb +++ b/app/services/mandatory_collection_resources_service.rb @@ -46,7 +46,7 @@ class MandatoryCollectionResourcesService year_range = "#{year} to #{year + 1}" case resource when "paper_form" - "#{log_type} log for tenants (#{year_range})" + "#{log_type} paper form (#{year_range})" when "bulk_upload_template" "#{log_type} bulk upload template (#{year_range})" when "bulk_upload_specification" diff --git a/spec/factories/collection_resource.rb b/spec/factories/collection_resource.rb index 0282e33e3..fb1895806 100644 --- a/spec/factories/collection_resource.rb +++ b/spec/factories/collection_resource.rb @@ -1,7 +1,7 @@ FactoryBot.define do factory :collection_resource, class: "CollectionResource" do resource_type { "paper_form" } - display_name { "lettings log for tenants (2021 to 2022)" } + display_name { "lettings paper form (2021 to 2022)" } short_display_name { "Paper Form" } year { 2024 } log_type { "lettings" } diff --git a/spec/helpers/collection_resources_helper_spec.rb b/spec/helpers/collection_resources_helper_spec.rb index 9fc77a4c9..05c164fc1 100644 --- a/spec/helpers/collection_resources_helper_spec.rb +++ b/spec/helpers/collection_resources_helper_spec.rb @@ -94,9 +94,9 @@ RSpec.describe CollectionResourcesHelper do context "and next year resources were manually released" do before do - create(:collection_resource, year: 2025, resource_type: "paper_form", display_name: "lettings log for tenants (2025 to 2026)", download_filename: "file.pdf", mandatory: true, released_to_user: true) + create(:collection_resource, year: 2025, resource_type: "paper_form", display_name: "lettings paper form (2025 to 2026)", download_filename: "file.pdf", mandatory: true, released_to_user: true) create(:collection_resource, year: 2025, resource_type: "bulk_upload_template", display_name: "bulk upload template (2025 to 2026)", download_filename: "file.xlsx", mandatory: true, released_to_user: true) - create(:collection_resource, year: 2025, resource_type: "bulk_upload_specification", display_name: "sales log for tenants (2025 to 2026)", download_filename: "file.xlsx", mandatory: true, released_to_user: true) + create(:collection_resource, year: 2025, resource_type: "bulk_upload_specification", display_name: "sales paper form (2025 to 2026)", download_filename: "file.xlsx", mandatory: true, released_to_user: true) end it "reutrns current and next years" do @@ -121,7 +121,7 @@ RSpec.describe CollectionResourcesHelper do describe "#document_list_component_items" do let(:resources) do [ - build(:collection_resource, year: 2023, resource_type: "paper_form", display_name: "lettings log for tenants (2023 to 2024)", download_filename: "2023_24_lettings_paper_form.pdf"), + build(:collection_resource, year: 2023, resource_type: "paper_form", display_name: "lettings paper form (2023 to 2024)", download_filename: "2023_24_lettings_paper_form.pdf"), build(:collection_resource, year: 2023, resource_type: "bulk_upload_template", display_name: "bulk upload template (2023 to 2024)", download_filename: "2023_24_lettings_bulk_upload_template.xlsx"), ] end @@ -134,7 +134,7 @@ RSpec.describe CollectionResourcesHelper do it "returns component items" do expect(document_list_component_items(resources)).to eq([ { - name: "Download the lettings log for tenants (2023 to 2024)", + name: "Download the lettings paper form (2023 to 2024)", href: "/collection-resources/lettings/2023/paper_form/download", metadata: "PDF, 286 KB", }, @@ -150,7 +150,7 @@ RSpec.describe CollectionResourcesHelper do describe "#document_list_edit_component_items" do let(:resources) do [ - build(:collection_resource, year: 2023, resource_type: "paper_form", display_name: "lettings log for tenants (2023 to 2024)", download_filename: "2023_24_lettings_paper_form.pdf"), + build(:collection_resource, year: 2023, resource_type: "paper_form", display_name: "lettings paper form (2023 to 2024)", download_filename: "2023_24_lettings_paper_form.pdf"), build(:collection_resource, year: 2023, resource_type: "bulk_upload_template", display_name: "bulk upload template (2023 to 2024)", download_filename: "2023_24_lettings_bulk_upload_template.xlsx"), ] end @@ -199,9 +199,9 @@ RSpec.describe CollectionResourcesHelper do context "and the resources have been manually released" do before do - create(:collection_resource, year: 2025, resource_type: "paper_form", display_name: "lettings log for tenants (2025 to 2026)", download_filename: "file.pdf", mandatory: true, released_to_user: true) + create(:collection_resource, year: 2025, resource_type: "paper_form", display_name: "lettings paper form (2025 to 2026)", download_filename: "file.pdf", mandatory: true, released_to_user: true) create(:collection_resource, year: 2025, resource_type: "bulk_upload_template", display_name: "bulk upload template (2025 to 2026)", download_filename: "file.xlsx", mandatory: true, released_to_user: true) - create(:collection_resource, year: 2025, resource_type: "bulk_upload_specification", display_name: "sales log for tenants (2025 to 2026)", download_filename: "file.xlsx", mandatory: true, released_to_user: true) + create(:collection_resource, year: 2025, resource_type: "bulk_upload_specification", display_name: "sales paper form (2025 to 2026)", download_filename: "file.xlsx", mandatory: true, released_to_user: true) end it "returns false" do diff --git a/spec/services/mandatory_collection_resources_service_spec.rb b/spec/services/mandatory_collection_resources_service_spec.rb index 423370e96..6b67e1d13 100644 --- a/spec/services/mandatory_collection_resources_service_spec.rb +++ b/spec/services/mandatory_collection_resources_service_spec.rb @@ -17,7 +17,7 @@ describe MandatoryCollectionResourcesService do it "returns a CollectionResource object with the correct attributes" do resource = service.generate_resource("lettings", 2024, "paper_form") expect(resource.resource_type).to eq("paper_form") - expect(resource.display_name).to eq("lettings log for tenants (2024 to 2025)") + expect(resource.display_name).to eq("lettings paper form (2024 to 2025)") expect(resource.short_display_name).to eq("Paper form") expect(resource.year).to eq(2024) expect(resource.log_type).to eq("lettings") From ba8816590318942fe9261ee4bbd77a7647740b8d Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Fri, 22 Nov 2024 08:53:49 +0000 Subject: [PATCH 25/34] CLDC-3695 Update docs (#2795) * Update readme * lint * typo * Update app api doc * lint --- docs/Gemfile.lock | 1 + docs/app_api.md | 2 +- docs/exports.md | 16 ++-- docs/form/builder.md | 182 ++++++++++++++++++---------------------- docs/form/definition.md | 35 +++----- docs/form/index.md | 12 +-- docs/form/page.md | 67 +++++++-------- docs/form/question.md | 86 +++++++++---------- docs/form/section.md | 30 ++++--- docs/form/subsection.md | 32 ++++--- docs/setup.md | 14 +++- 11 files changed, 219 insertions(+), 258 deletions(-) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index f8962318e..d102b14ef 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -255,6 +255,7 @@ GEM PLATFORMS arm64-darwin-21 + arm64-darwin-23 x86_64-darwin-22 x86_64-linux diff --git a/docs/app_api.md b/docs/app_api.md index e1987a897..627be0ad7 100644 --- a/docs/app_api.md +++ b/docs/app_api.md @@ -12,4 +12,4 @@ In order to use the app as an API, you will need to configure requests to the AP - `Content-Type = application/json` - `Action = application/json` N.B. If you use `*/*` instead, the request won't be recognised as an API request` -Currently only the logs controller is configured to accept and authenticate API requests, when the above API environment variables are set. +Currently, only the Logs Controller is configured to accept and authenticate API requests, provided that the specified API environment variables are set. Please note that the API has not been actively maintained for an extended period and may not function as expected. Additionally, the required environment variables are not configured on any of the environments deployed on AWS, rendering API requests to those environments non-functional. diff --git a/docs/exports.md b/docs/exports.md index f16efe893..4b971e29a 100644 --- a/docs/exports.md +++ b/docs/exports.md @@ -6,16 +6,14 @@ nav_order: 7 All data collected by the application needs to be exported to the Consolidated Data Store (CDS) which is a data warehouse based on MS SQL running in the DAP (Data Analytics Platform). -This is done via XML exports saved in an S3 bucket located in the DAP VPC using dedicated credentials shared out of band. The data mapping for this export can be found in `app/services/exports/lettings_log_export_service.rb` +This is done via XML exports saved in an S3 bucket. +We currently export lettings logs, users and organisations. +The data mapping for these exports can be found in: + +- Lettings logs `app/services/exports/lettings_log_export_service.rb` +- Organisations `app/services/exports/organisation_export_service.rb` +- Users `app/services/exports/user_export_service.rb` Initially the application database field names and field types were chosen to match the existing CDS data as closely as possible to minimise the amount of transformation needed. This has led to a less than optimal data model though and increasingly we should look to transform at the mapping layer where beneficial for our application. We have a cron job triggering the export service daily at 5am. - -The S3 bucket is located in the DAP VPC rather than the application VPC as DAP runs in an AWS account directly so access to the S3 bucket can be restricted to only the IPs used by the application. This is not possible the other way around as [Gov PaaS does not support restricting S3 access by IP](https://github.com/alphagov/paas-roadmap/issues/107). - -## Other options previously considered - -- CDC replication using a managed service such as [AWS DMS](https://aws.amazon.com/dms/) - - Would require VPC peering which [Gov PaaS does not currently support](https://github.com/alphagov/paas-roadmap/issues/105) - - Would require CDS to make changes to their ingestion model diff --git a/docs/form/builder.md b/docs/form/builder.md index 155abaed6..5d21b1c43 100644 --- a/docs/form/builder.md +++ b/docs/form/builder.md @@ -9,13 +9,11 @@ nav_order: 1 The setup this log section is treated slightly differently from the rest of the form. It is more accurately viewed as providing metadata about the form than as being part of the form itself. It also needs to know far more about the application specific context than other parts of the form such as who the current user is, what organisation they’re part of and what role they have etc. -As a result it’s not modelled as part of the config but rather as code. It still uses the same [Form Runner](/form/runner) components though. - -## Features the Form Config supports +## Features the Form supports - Defining sections, subsections, pages and questions that fit the GOV.UK task list pattern -- Auto-generated routes – URLs are automatically created from dasherized page names +- Auto-generated routes – URLs are automatically created from dasherized page names (ids) - Data persistence requires a database field to exist which matches the name/id for each question (and answer option for checkbox questions) @@ -39,63 +37,84 @@ As a result it’s not modelled as part of the config but rather as code. It sti - For complex HTML guidance partials can be referenced -## JSON Config - -The form for this is driven by a JSON file in `/config/forms/{start_year}_{end_year}.json` - -The JSON should follow the structure: - -```jsonc -{ - "form_type": "lettings" / "sales", - "start_year": Integer, // i.e. 2020 - "end_year": Integer, // i.e. 2021 - "sections": { - "[snake_case_section_name_string]": { - "label": String, - "description": String, - "subsections": { - "[snake_case_subsection_name_string]": { - "label": String, - "pages": { - "[snake_case_page_name_string]": { - "header": String, - "description": String, - "questions": { - "[snake_case_question_name_string]": { - "header": String, - "hint_text": String, - "check_answer_label": String, - "type": "text" / "numeric" / "radio" / "checkbox" / "date", - "min": Integer, // numeric only - "max": Integer, // numeric only - "step": Integer, // numeric only - "width": 2 / 3 / 4 / 5 / 10 / 20, // text and numeric only - "prefix": String, // numeric only - "suffix": String, //numeric only - "answer_options": { // checkbox and radio only - "0": String, - "1": String - }, - "conditional_for": { - "[snake_case_question_to_enable_1_name_string]": ["condition-that-enables"], - "[snake_case_question_to_enable_2_name_string]": ["condition-that-enables"] - }, - "inferred_answers": { "field_that_gets_inferred_from_current_field": { "is_that_field_inferred": true } }, - "inferred_check_answers_value": [{ - "condition": { "field_name_for_inferred_check_answers_condition": "field_value_for_inferred_check_answers_condition" }, - "value": "Inferred value that gets displayed if condition is met" - }] - } - }, - "depends_on": [{ "question_key": "answer_value_required_for_this_page_to_be_shown" }] - } - } - } - } - } - } -} +## Form definition + +The Form should follow the structure: + +``` +SECTIONS = [ + Form::Sales::Sections::Section +].freeze + +Form.new(nil, start_year, SECTIONS, form_type - "lettings" / "sales") + +class Form::Sales::Sections::Section < ::Form::Section + def initialize(id, hsh, form) + super + @id = [snake_case_section_name_string] + @label = [String] + @description = [String] + @subsections = [Form::Sales::Subsections::Subsection.new(nil, nil, self)] + end +end + +class Form::Sales::Subsections::Subsection < ::Form::Subsection + def initialize(id, hsh, section) + super + @id = [snake_case_subsection_name_string] + @label = [String] + @depends_on = [{ "question_key/method_key": "answer_value_required_for_this_subsection_to_be_shown" }] + end + + def pages + @pages ||= [Form::Sales::Pages::Page.new(nil, nil, self),] + end +end + +class Form::Sales::Pages::Page < ::Form::Page + def initialize(id, hsh, subsection) + super + @id = [snake_case_page_name_string] + @header = [String,] + @depends_on = [{ "question_key": "answer_value_required_for_this_page_to_be_shown" }] + end + + def questions + @questions ||= [ + Form::Sales::Questions::Question.new(nil, nil, self), + ] + end +end + +class Form::Sales::Questions::Question < ::Form::Question + def initialize(id, hsh, page) + super + @id = [snake_case_question_name_string] + @hint_text = [String,] + @check_answer_label = [String,] + @type = ["text" / "numeric" / "radio" / "checkbox" / "date",] + @min = [Integer, // numeric only] + @max = [Integer, // numeric only] + @step = [Integer, // numeric only] + @width = [2 / 3 / 4 / 5 / 10 / 20, // text and numeric only] + @prefix = [String, // numeric only] + @suffix = [String, //numeric only] + @answer_options = { // checkbox and radio only + "0": String, + "1": String + }, + @conditional_for = { + "[snake_case_question_to_enable_1_name_string]": ["condition-that-enables"], + "[snake_case_question_to_enable_2_name_string]": ["condition-that-enables"] + }, + @inferred_answers = { "field_that_gets_inferred_from_current_field": { "is_that_field_inferred": true } }, + @inferred_check_answers_value = [{ + "condition": { "field_name_for_inferred_check_answers_condition": "field_value_for_inferred_check_answers_condition" }, + "value": "Inferred value that gets displayed if condition is met" + }] + @question_number = Integer + end +end ``` Assumptions made by the format: @@ -127,47 +146,8 @@ Assumptions made by the format: Form navigation works by stepping sequentially through every page defined in the JSON form definition for the given subsection. For every page it checks if it has "depends_on" conditions. If it does, it evaluates them to determine whether that page should be show or not. -In this way we can build up whole branches by having: - -```jsonc -"page_1": { "questions": { "question_1: "answer_options": ["A", "B"] } }, -"page_2": { "questions": { "question_2: "answer_options": ["C", "D"] }, "depends_on": [{ "question_1": "A" }] }, -"page_3": { "questions": { "question_3: "answer_options": ["E", "F"] }, "depends_on": [{ "question_1": "A" }] }, -"page_4": { "questions": { "question_4: "answer_options": ["G", "H"] }, "depends_on": [{ "question_1": "B" }] }, -``` - -## JSON form validation against Schema - -To validate the form JSON against the schema you can run: - -```bash -rake form_definition:validate["config/forms/2021_2022.json"] -``` - -Note: you may have to escape square brackets in zsh: - -```bash -rake form_definition:validate\["config/forms/2021_2022.json"\] -``` - -This will validate the given form definition against the schema in `config/forms/schema/generic.json`. - -You can also run: - -```bash -rake form_definition:validate_all -``` - -This will validate all forms in directories `["config/forms", "spec/fixtures/forms"]` +We can also define custom `routed_to?` methods on pages for more complex routing logic. ## Form models and definition For information about the form model and related models (section, subsection, page, question) and how these relate to each other see [form definition](/form/definition). - -## Improvements that could be made - -- JSON schema definition could be expanded such that we can better automatically validate that a given config is valid and internally consistent - -- Generators could parse a given valid JSON form and generate the required database migrations to ensure all the expected fields exist and are of a compatible type - -- The parsed form could be visualised using something like GraphViz to help manually verify the coded config meets requirements diff --git a/docs/form/definition.md b/docs/form/definition.md index 3d27bb30e..8eb646284 100644 --- a/docs/form/definition.md +++ b/docs/form/definition.md @@ -6,26 +6,15 @@ nav_order: 3 # Form definition -The current system is built around a form definition written in JSON. At the top level every form will expect to have the following attributes: +The current system is built around a form definition constructed from various Form subclasses. At the top level every form will expect to have the following attributes: - Form type: this is to define whether the form is a lettings form or a sales form. The questions will differ between the types. - Start date: the start of the collection window for the form, this will usually be in April. -- End date: the end date of the collection window for the form, this will usually be in July, a year after the start date. +- Submission deadline: the official end date of the collection window for the form, this will usually be in July, a year after the start date. +- New logs end date: the end date for creating any new logs for this form +- Edit end date: the end date for editing any existing logs for this form - Sections: the sections in the form, this block is where the bulk of the form definition will be. -An example of this might look like the following: - -```json -{ - "form_type": "lettings", - "start_date": "2021-04-01T00:00:00.000+01:00", - "end_date": "2022-07-01T00:00:00.000+01:00", - "sections": { - ... - } -} -``` - Note that the end date of one form will overlap the start date of another to allow for late submissions. This means that every year there will be a period of time in which two forms are running simultaneously. A form is split up is as follows: @@ -39,24 +28,24 @@ Rails uses the model, view, controller (MVC) pattern which we follow. ## Form model -There is no need to manually initialise a form object as this is handled by the FormHandler class at boot time. If a new form needs to be added then a JSON file containing the form definition should be added to `config/forms` where the FormHandler will be able to locate it and instantiate it. +There is no need to manually initialise a form object as this is handled by the FormHandler class at boot time. A form has the following attributes: - `name`: The name of the form -- `setup_sections`: The setup section (this is not defined in the JSON, for more information see this) -- `form_definition`: The parsed form JSON -- `form_sections`: The sections found within the form definition JSON +- `setup_sections`: The setup section +- `form_sections`: The sections passed to form on init - `type`: The type of form (this is used to indicate if the form is for a sale or a letting) -- `sections`: The combination of the setup section with those found in the JSON definition +- `sections`: The combination of the setup section with form sections - `subsections`: The subsections of the form (these live under the sections) - `pages`: The pages of the form (these live under the subsections) - `questions`: The questions of the form (these live under the pages) - `start_date`: The start date of the form, in ISO 8601 format -- `end_date`: The end date of the form, in ISO 8601 format +- `submission_deadline`: The official end date of the form, in ISO 8601 format +- `new_logs_end_date`: The new logs end date of the form, in ISO 8601 format +- `edit_end_date`: The edit end date of the form, in ISO 8601 format -Each form has an `end_date` which for JSON forms is defined in the form definition JSON file and for code defined forms it is set to 1st July, 1 year after the start year. -Logs with a form that has `end_date` in the past can no longer be edited through the UI. +Logs with a form that has `edit_end_date` in the past can no longer be edited through the UI. ## Form views diff --git a/docs/form/index.md b/docs/form/index.md index 664b136c7..ed21e3b10 100644 --- a/docs/form/index.md +++ b/docs/form/index.md @@ -13,18 +13,12 @@ A paper form is produced for guidance and to help data providers collect the dat Data is accepted for a collection window for up to 3 months after it’s finished to allow for late data submission. This means that between April and July 2 versions of the form run simultaneously. -Other considerations that went into our design are being able to re-use as much of this solution for other data collections, and possibly having the ability to generate the form and/or form changes from a user interface. +Other initial considerations that went into our design are being able to re-use as much of this solution for other data collections, and possibly having the ability to generate the form and/or form changes from a user interface. -We haven’t used micro-services, preferring to deploy a single application but we have modelled the form itself as configuration in the form of a JSON structure that acts as a sort of DSL/form builder for the form. +Each form has historically been defined as a JSON configuration, but has since been replaced with subsection, page and question classes that contruct a form in code due to increased complexity. -The idea is to decouple the code that creates the required routes, controller methods, views etc to display the form from the actual wording of questions or order of pages such that it becomes possible to make changes to the form with little or no code changes. - -This should also mean that in the future it could be possible to create an interface that can construct the JSON config, which would open up the ability to make form changes to a wider audience. Doing this fully would require generating and running the necessary migrations for data storage, generating the required ActiveRecord methods to validate the data server side, and generating/updating API endpoints and documentation. All of this is likely to be beyond the scope of initial MVP but could be looked at in the future. - -Since initially the JSON config will not create database migrations or ActiveRecord model validations, it will instead assume that these have been correctly created for the config provided. The reasoning for this is the following assumptions: +To allow for easier content changes, the copy for questions has been extracted into translation files. The reasoning for this is the following assumptions: - The form will be tweaked regularly (amending questions wording, changing the order of questions or the page a question is displayed on) - The actual data collected will change very infrequently. Time series continuity is very important to ADD (Analysis and Data Directorate) so the actual data collected should stay largely consistent i.e. in general we can change the question wording in ways that makes the intent clearer or easier to understand, but not in ways that would make the data provider give a different answer. - -A form parser class will parse this config into ruby objects/methods that can be used as an API by the rest of the application, such that we could change the underlying config if needed (for example swap JSON for YAML or for DataBase objects) without needing to change the rest of the application. We’ll call this the Form Runner part of the application. diff --git a/docs/form/page.md b/docs/form/page.md index 47de24d98..7e0607a70 100644 --- a/docs/form/page.md +++ b/docs/form/page.md @@ -10,43 +10,44 @@ Pages sit below the [`Subsection`](subsection) level of a form definition. An example page might look something like this: -```json -"property_postcode": { - "header": "", - "description": "", - "questions": { - ... - }, - "depends_on": [ - { - "needstype": 1 +``` +class Form::Sales::Pages::PropertyPostcode < ::Form::Page + def initialize(id, hsh, subsection) + super + @id = property_postcode + @depends_on = [{ "needstype" => 1 }] + @title_text = { + "translation": "translation1", + "arguments": [ + { + "key": "some_general_field", + "label": true, + "i18n_template": "template1" + } + ] } - ], - "title_text": { - "translation": "translation1", - "arguments": [ - { - "key": "some_general_field", - "label": true, - "i18n_template": "template1" - } - ] - }, - "informative_text": { - "translation": "translation2", - "arguments": [ - { - "key": "some_currency_method", - "label": false, - "i18n_template": "template2", - "currency": true, - } + @informative_text": { + "translation": "translation2", + "arguments": [ + { + "key": "some_currency_method", + "label": false, + "i18n_template": "template2", + "currency": true, + } + ] + } + end + + def questions + @questions ||= [ + Form::Sales::Questions::Question.new(nil, nil, self), ] - }, -} + end +end ``` -In the above example the the subsection has the id `property_postcode`. This id is used for the url of the web page, but the underscore is replaced with a hash, so the url for this page would be `[environment-url]/logs/[log-id]/property-postcode` e.g. on staging this url might look like the following: `https://dluhc-core-staging.london.cloudapps.digital/logs/1234/property-postcode`. +In the above example the the subsection has the id `property_postcode`. This id is used for the url of the web page, but the underscore is replaced with a dash, so the url for this page would be `[environment-url]/logs/[log-id]/property-postcode` e.g. on staging this url might look like the following: `https://staging.submit-social-housing-data.communities.gov.uk/logs/1234/property-postcode`. The header is optional but if provided is used for the heading displayed on the page. diff --git a/docs/form/question.md b/docs/form/question.md index dd3254e72..7112596cf 100644 --- a/docs/form/question.md +++ b/docs/form/question.md @@ -10,25 +10,25 @@ Questions are under the page level of the form definition. An example question might look something like this: -```json -"postcode_known": { - "check_answer_label": "Do you know the property postcode?", - "header": "Do you know the property’s postcode?", - "hint_text": "", - "type": "radio", - "answer_options": { - "1": { - "value": "Yes" +``` +class Form::Sales::Questions::PostcodeKnown < ::Form::Question + def initialize(id, hsh, page) + super + @id = postcode_known + @hint_text = "" + @header = "Do you know the property postcode?" + @check_answer_label = "Do you know the property postcode?" + @type = "radio" + @answer_options = { + "1" => { "value" => "Yes" }, + "0" => { "value" => "No" } }, - "0": { - "value": "No" - } - }, - "conditional_for": { - "postcode_full": [1] - }, - "hidden_in_check_answers": true -} + @conditional_for = { + "postcode_full" => [1] + }, + @hidden_in_check_answers = true + end +end ``` In the above example the the question has the id `postcode_known`. @@ -45,15 +45,11 @@ The `conditional_for` contains the value needed to be selected by the data input the `hidden_in_check_answers` is used to hide a value from displaying on the check answers page. You only need to provide this if you want to set it to true in order to hide the value for some reason e.g. it's one of two questions appearing on a page and the other question is displayed on the check answers page. It's also worth noting that you can declare this as a with a `depends_on` which can be useful for conditionally displaying values on the check answers page. For example: -```json -"hidden_in_check_answers": { - "depends_on": [ - { - "age6_known": 0 - }, - { - "age6_known": 1 - } +``` +@hidden_in_check_answers = { + "depends_on" => [ + { "age6_known" => 0 }, + { "age6_known" => 1 } ] } ``` @@ -62,25 +58,25 @@ Would mean the question the above is attached to would be hidden in the check an The answer the data inputter provides to some questions allows us to infer the values of other questions we might have asked in the form, allowing us to save the data inputters some time. An example of how this might look is as follows: -```json -"postcode_full": { - "check_answer_label": "Postcode", - "header": "What is the property’s postcode?", - "hint_text": "", - "type": "text", - "width": 5, - "inferred_answers": { - "la": { - "is_la_inferred": true +``` +class Form::Sales::Questions::PostcodeFull < ::Form::Question + def initialize(id, hsh, page) + super + @id = postcode_full + @hint_text = "" + @header = "What is the property’s postcode?"" + @check_answer_label = "Postcode"" + @type = "text" + @width = 5 + @inferred_answers = { + "la" => { "is_la_inferred" => true } } - }, - "inferred_check_answers_value": [{ - "condition": { - "postcode_known": 0 - }, - "value": "Not known" - }] -} + @inferred_check_answers_value => [{ + "condition" => { "postcode_known" => 0 }, + "value": "Not known" + }] + end +end ``` In the above example the width is an optional attribute and can be provided for text type questions to determine the width of the text box on the page when when the question is displayed to a user (this allows you to match the width of the text box on the page to that of the design for a question). diff --git a/docs/form/section.md b/docs/form/section.md index 514842355..e4443af3b 100644 --- a/docs/form/section.md +++ b/docs/form/section.md @@ -10,24 +10,22 @@ Sections sit at the top level of a form definition. An example section might look something like this: -```json -"sections": { - "tenancy_and_property": { - "label": "Property and tenancy information", - "subsections": { - "property_information": { - ... - }, - "tenancy_information": { - ... - } - } - }, - ... -} +``` +class Form::Sales::Sections::TenancyAndProperty < ::Form::Section + def initialize(id, hsh, form) + super + @id = "tenancy_and_property" + @label = "Property and tenancy information" + @description = "" + @subsections = [ + Form::Sales::Subsections::PropertyInformation.new(nil, nil, self), + Form::Sales::Subsections::TenancyInformation.new(nil, nil, self) + ] + end +end ``` -In the above example the section id would be `tenancy_and_property` and its subsections would be `property_information` and `tenancy_information`. +In the above example the section id would be `tenancy_and_property` and its subsections would be `PropertyInformation` and `TenancyInformation`. The label contains the text that users will see for that section in the task list page of a lettings log. diff --git a/docs/form/subsection.md b/docs/form/subsection.md index aa81c0259..5e659836a 100644 --- a/docs/form/subsection.md +++ b/docs/form/subsection.md @@ -10,29 +10,25 @@ Subsections sit below the [`Section`](section) level of a form definition. An example subsection might look something like this: -```json -"property_information": { - "label": "Property information", - "depends_on": [ - { - "setup": "completed" - } - ], - "pages": { - "property_postcode": { - ... - }, - "property_local_authority": { - ... - } - } -} +``` +class Form::Sales::Subsections::PropertyInformation < ::Form::Subsection + def initialize(id, hsh, section) + super + @id = property_information + @depends_on = [{ "setup": "completed" }] + @label = "Property information" + end + + def pages + @pages ||= [Form::Sales::Pages::PropertyPostcode.new(nil, nil, self),Form::Sales::Pages::PropertyLocalAuthority.new(nil, nil, self)] + end +end ``` In the above example the the subsection has the id `property_information`. The `depends_on` contains the set of conditions that must be met for the section to be accessible to a data provider, in this example subsection depends on the completion of the setup section/subsection (note that this is a common condition as the answers provided to questions in the setup subsection often have an impact on what questions are asked of the data provider in later subsections of the form). The label contains the text that users will see for that subsection in the task list page of a lettings log. -The pages of the subsection in the example would be `property_postcode` and `property_local_authority`. +The pages of the subsection in the example would be `PropertyPostcode` and `PropertyLocalAuthority`. Subsections can contain one or more [pages](page). diff --git a/docs/setup.md b/docs/setup.md index d14fa58d9..4400a7ae2 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -79,15 +79,23 @@ We recommend using [nvm](https://github.com/nvm-sh/nvm) to manage NodeJS version macOS (using nvm): ```bash - nvm install 16 - nvm use 16 + nvm install 20 + nvm use 20 + brew install yarn + ``` + + or you could run it without specifying the version and it should use the version from .nvmrc + + ```bash + nvm install + nvm use brew install yarn ``` Linux (Debian): ```bash - curl -sL https://deb.nodesource.com/setup_16.x | sudo bash - + curl -sL https://deb.nodesource.com/setup_20.x | sudo bash - sudo apt -y install nodejs mkdir -p ~/.npm-packages npm config set prefix ~/.npm-packages From 542565ed85c466cad98b6cb8114270406afe115b Mon Sep 17 00:00:00 2001 From: Rachael Booth Date: Fri, 22 Nov 2024 10:31:05 +0000 Subject: [PATCH 26/34] Use subsection copy key (#2792) * CLDC-3766: Ensure copy for Q78 is found correctly * Fix tests * Use subsection copy key in defaults and remove explit keys that are now defaults * Replace other uses of subsection.id interpolation in copy keys * Test copy key when specified on subsection * Update tests * Update privacy notice page tests * More test fixes --- app/models/form/page.rb | 2 +- app/models/form/question.rb | 2 +- .../form/sales/pages/buyer_interview.rb | 2 +- app/models/form/sales/pages/deposit.rb | 1 - .../form/sales/pages/deposit_discount.rb | 1 - app/models/form/sales/pages/discount.rb | 1 - app/models/form/sales/pages/equity.rb | 1 - .../form/sales/pages/extra_borrowing.rb | 1 - app/models/form/sales/pages/grant.rb | 1 - app/models/form/sales/pages/monthly_rent.rb | 1 - .../form/sales/pages/mortgage_amount.rb | 1 - .../form/sales/pages/mortgage_lender.rb | 1 - .../form/sales/pages/mortgage_lender_other.rb | 1 - .../form/sales/pages/mortgage_length.rb | 1 - app/models/form/sales/pages/mortgageused.rb | 1 - .../form/sales/pages/previous_bedrooms.rb | 1 - .../sales/pages/previous_property_type.rb | 1 - .../form/sales/pages/previous_tenure.rb | 1 - app/models/form/sales/pages/privacy_notice.rb | 2 +- app/models/form/sales/pages/resale.rb | 1 - app/models/form/sales/pages/staircase.rb | 2 +- .../sales/pages/value_shared_ownership.rb | 1 - .../form/sales/questions/buyer_interview.rb | 2 +- .../form/sales/questions/deposit_amount.rb | 1 - .../form/sales/questions/deposit_discount.rb | 1 - app/models/form/sales/questions/discount.rb | 1 - app/models/form/sales/questions/equity.rb | 1 - .../form/sales/questions/extra_borrowing.rb | 1 - app/models/form/sales/questions/fromprop.rb | 1 - app/models/form/sales/questions/grant.rb | 1 - .../form/sales/questions/monthly_rent.rb | 1 - .../form/sales/questions/mortgage_amount.rb | 1 - .../form/sales/questions/mortgage_lender.rb | 1 - .../sales/questions/mortgage_lender_other.rb | 1 - .../form/sales/questions/mortgage_length.rb | 1 - .../form/sales/questions/mortgageused.rb | 1 - .../form/sales/questions/previous_bedrooms.rb | 1 - .../form/sales/questions/previous_tenure.rb | 1 - .../form/sales/questions/privacy_notice.rb | 2 +- app/models/form/sales/questions/resale.rb | 1 - app/models/form/sales/questions/value.rb | 1 - .../discounted_ownership_scheme.rb | 1 + .../form/sales/subsections/outright_sale.rb | 1 + .../form/lettings/questions/homeless_spec.rb | 4 -- spec/models/form/page_spec.rb | 4 +- spec/models/form/question_spec.rb | 4 +- .../form/sales/pages/buyer_interview_spec.rb | 42 +++++-------------- .../form/sales/pages/privacy_notice_spec.rb | 34 ++++----------- .../sales/questions/buyer_interview_spec.rb | 37 ++++------------ .../sales/questions/privacy_notice_spec.rb | 4 +- .../sales/questions/uprn_confirmation_spec.rb | 4 -- .../discounted_ownership_scheme_spec.rb | 4 ++ .../sales/subsections/outright_sale_spec.rb | 4 ++ .../shared_ownership_scheme_spec.rb | 4 ++ spec/models/form/subsection_spec.rb | 4 ++ 55 files changed, 57 insertions(+), 142 deletions(-) diff --git a/app/models/form/page.rb b/app/models/form/page.rb index 7a5c4bf87..c1c09c362 100644 --- a/app/models/form/page.rb +++ b/app/models/form/page.rb @@ -25,7 +25,7 @@ class Form::Page delegate :form, to: :subsection def copy_key - @copy_key ||= "#{form.type}.#{subsection.id}.#{questions[0].id}" + @copy_key ||= "#{form.type}.#{subsection.copy_key}.#{questions[0].id}" end def header diff --git a/app/models/form/question.rb b/app/models/form/question.rb index 9409350f6..05eec89dd 100644 --- a/app/models/form/question.rb +++ b/app/models/form/question.rb @@ -51,7 +51,7 @@ class Form::Question delegate :form, to: :subsection def copy_key - @copy_key ||= "#{form.type}.#{subsection.id}.#{id}" + @copy_key ||= "#{form.type}.#{subsection.copy_key}.#{id}" end def check_answer_label diff --git a/app/models/form/sales/pages/buyer_interview.rb b/app/models/form/sales/pages/buyer_interview.rb index c6a43690b..4398e434a 100644 --- a/app/models/form/sales/pages/buyer_interview.rb +++ b/app/models/form/sales/pages/buyer_interview.rb @@ -2,7 +2,7 @@ class Form::Sales::Pages::BuyerInterview < ::Form::Page def initialize(id, hsh, subsection, joint_purchase:) super(id, hsh, subsection) @joint_purchase = joint_purchase - @copy_key = "sales.#{subsection.id}.noint.#{joint_purchase ? 'joint_purchase' : 'not_joint_purchase'}" + @copy_key = "sales.#{subsection.copy_key}.noint.#{joint_purchase ? 'joint_purchase' : 'not_joint_purchase'}" end def questions diff --git a/app/models/form/sales/pages/deposit.rb b/app/models/form/sales/pages/deposit.rb index 4870a3c35..b204227b2 100644 --- a/app/models/form/sales/pages/deposit.rb +++ b/app/models/form/sales/pages/deposit.rb @@ -3,7 +3,6 @@ class Form::Sales::Pages::Deposit < ::Form::Page super(id, hsh, subsection) @ownershipsch = ownershipsch @optional = optional - @copy_key = "sales.sale_information.deposit" end def questions diff --git a/app/models/form/sales/pages/deposit_discount.rb b/app/models/form/sales/pages/deposit_discount.rb index 84fcbb45f..3fae9c0f8 100644 --- a/app/models/form/sales/pages/deposit_discount.rb +++ b/app/models/form/sales/pages/deposit_discount.rb @@ -2,7 +2,6 @@ class Form::Sales::Pages::DepositDiscount < ::Form::Page def initialize(id, hsh, subsection, optional:) super(id, hsh, subsection) @optional = optional - @copy_key = "sales.sale_information.cashdis" end def questions diff --git a/app/models/form/sales/pages/discount.rb b/app/models/form/sales/pages/discount.rb index 2d632985e..38d675a77 100644 --- a/app/models/form/sales/pages/discount.rb +++ b/app/models/form/sales/pages/discount.rb @@ -2,7 +2,6 @@ class Form::Sales::Pages::Discount < ::Form::Page def initialize(id, hsh, subsection) super @id = "discount" - @copy_key = "sales.sale_information.discount" @depends_on = [{ "right_to_buy?" => true, }] diff --git a/app/models/form/sales/pages/equity.rb b/app/models/form/sales/pages/equity.rb index 9bf3050a0..46eec40a3 100644 --- a/app/models/form/sales/pages/equity.rb +++ b/app/models/form/sales/pages/equity.rb @@ -2,7 +2,6 @@ class Form::Sales::Pages::Equity < ::Form::Page def initialize(id, hsh, subsection) super @id = "equity" - @copy_key = "sales.sale_information.equity" end def questions diff --git a/app/models/form/sales/pages/extra_borrowing.rb b/app/models/form/sales/pages/extra_borrowing.rb index c6ddb705a..d86db67b5 100644 --- a/app/models/form/sales/pages/extra_borrowing.rb +++ b/app/models/form/sales/pages/extra_borrowing.rb @@ -2,7 +2,6 @@ class Form::Sales::Pages::ExtraBorrowing < ::Form::Page def initialize(id, hsh, subsection, ownershipsch:) super(id, hsh, subsection) @ownershipsch = ownershipsch - @copy_key = "sales.sale_information.extrabor" @description = "" @subsection = subsection @depends_on = [{ diff --git a/app/models/form/sales/pages/grant.rb b/app/models/form/sales/pages/grant.rb index 2f96701c5..1d11fba82 100644 --- a/app/models/form/sales/pages/grant.rb +++ b/app/models/form/sales/pages/grant.rb @@ -2,7 +2,6 @@ class Form::Sales::Pages::Grant < ::Form::Page def initialize(id, hsh, subsection) super @id = "grant" - @copy_key = "sales.sale_information.grant" @depends_on = [{ "right_to_buy?" => false, "rent_to_buy_full_ownership?" => false, diff --git a/app/models/form/sales/pages/monthly_rent.rb b/app/models/form/sales/pages/monthly_rent.rb index 943e47cff..29f0d895f 100644 --- a/app/models/form/sales/pages/monthly_rent.rb +++ b/app/models/form/sales/pages/monthly_rent.rb @@ -2,7 +2,6 @@ class Form::Sales::Pages::MonthlyRent < ::Form::Page def initialize(id, hsh, subsection) super @id = "monthly_rent" - @copy_key = "sales.sale_information.mrent" end def questions diff --git a/app/models/form/sales/pages/mortgage_amount.rb b/app/models/form/sales/pages/mortgage_amount.rb index 41fba167c..e6a722853 100644 --- a/app/models/form/sales/pages/mortgage_amount.rb +++ b/app/models/form/sales/pages/mortgage_amount.rb @@ -2,7 +2,6 @@ class Form::Sales::Pages::MortgageAmount < ::Form::Page def initialize(id, hsh, subsection, ownershipsch:) super(id, hsh, subsection) @ownershipsch = ownershipsch - @copy_key = "sales.sale_information.mortgage" @depends_on = [{ "mortgage_used?" => true }] end diff --git a/app/models/form/sales/pages/mortgage_lender.rb b/app/models/form/sales/pages/mortgage_lender.rb index 87646a514..6db3c01df 100644 --- a/app/models/form/sales/pages/mortgage_lender.rb +++ b/app/models/form/sales/pages/mortgage_lender.rb @@ -2,7 +2,6 @@ class Form::Sales::Pages::MortgageLender < ::Form::Page def initialize(id, hsh, subsection, ownershipsch:) super(id, hsh, subsection) @ownershipsch = ownershipsch - @copy_key = "sales.sale_information.mortgagelender" @description = "" @subsection = subsection @depends_on = [{ diff --git a/app/models/form/sales/pages/mortgage_lender_other.rb b/app/models/form/sales/pages/mortgage_lender_other.rb index 903d6518f..f71089377 100644 --- a/app/models/form/sales/pages/mortgage_lender_other.rb +++ b/app/models/form/sales/pages/mortgage_lender_other.rb @@ -2,7 +2,6 @@ class Form::Sales::Pages::MortgageLenderOther < ::Form::Page def initialize(id, hsh, subsection, ownershipsch:) super(id, hsh, subsection) @ownershipsch = ownershipsch - @copy_key = "sales.sale_information.mortgagelenderother" @description = "" @subsection = subsection @depends_on = [{ diff --git a/app/models/form/sales/pages/mortgage_length.rb b/app/models/form/sales/pages/mortgage_length.rb index 76c46694a..dbc01a695 100644 --- a/app/models/form/sales/pages/mortgage_length.rb +++ b/app/models/form/sales/pages/mortgage_length.rb @@ -2,7 +2,6 @@ class Form::Sales::Pages::MortgageLength < ::Form::Page def initialize(id, hsh, subsection, ownershipsch:) super(id, hsh, subsection) @ownershipsch = ownershipsch - @copy_key = "sales.sale_information.mortlen" @depends_on = [{ "mortgageused" => 1, }] diff --git a/app/models/form/sales/pages/mortgageused.rb b/app/models/form/sales/pages/mortgageused.rb index ab48b0c2d..f9d8eae2e 100644 --- a/app/models/form/sales/pages/mortgageused.rb +++ b/app/models/form/sales/pages/mortgageused.rb @@ -1,7 +1,6 @@ class Form::Sales::Pages::Mortgageused < ::Form::Page def initialize(id, hsh, subsection, ownershipsch:) super(id, hsh, subsection) - @copy_key = "sales.sale_information.mortgageused" @ownershipsch = ownershipsch end diff --git a/app/models/form/sales/pages/previous_bedrooms.rb b/app/models/form/sales/pages/previous_bedrooms.rb index 26b3ef050..214632d49 100644 --- a/app/models/form/sales/pages/previous_bedrooms.rb +++ b/app/models/form/sales/pages/previous_bedrooms.rb @@ -2,7 +2,6 @@ class Form::Sales::Pages::PreviousBedrooms < ::Form::Page def initialize(id, hsh, subsection) super @id = "previous_bedrooms" - @copy_key = "sales.sale_information.frombeds" @depends_on = [ { "soctenant" => 1, diff --git a/app/models/form/sales/pages/previous_property_type.rb b/app/models/form/sales/pages/previous_property_type.rb index c5dd4f66a..26669d774 100644 --- a/app/models/form/sales/pages/previous_property_type.rb +++ b/app/models/form/sales/pages/previous_property_type.rb @@ -2,7 +2,6 @@ class Form::Sales::Pages::PreviousPropertyType < ::Form::Page def initialize(id, hsh, subsection) super @id = "previous_property_type" - @copy_key = "sales.sale_information.fromprop" @description = "" @subsection = subsection @depends_on = [ diff --git a/app/models/form/sales/pages/previous_tenure.rb b/app/models/form/sales/pages/previous_tenure.rb index c35b6bd67..0f4a4b250 100644 --- a/app/models/form/sales/pages/previous_tenure.rb +++ b/app/models/form/sales/pages/previous_tenure.rb @@ -2,7 +2,6 @@ class Form::Sales::Pages::PreviousTenure < ::Form::Page def initialize(id, hsh, subsection) super @id = "shared_ownership_previous_tenure" - @copy_key = "sales.sale_information.socprevten" @header = "" @description = "" @subsection = subsection diff --git a/app/models/form/sales/pages/privacy_notice.rb b/app/models/form/sales/pages/privacy_notice.rb index 40c441d3e..c99ee3397 100644 --- a/app/models/form/sales/pages/privacy_notice.rb +++ b/app/models/form/sales/pages/privacy_notice.rb @@ -1,7 +1,7 @@ class Form::Sales::Pages::PrivacyNotice < ::Form::Page def initialize(id, hsh, subsection, joint_purchase:) super(id, hsh, subsection) - @copy_key = "sales.#{subsection.id}.privacynotice.#{joint_purchase ? 'joint_purchase' : 'not_joint_purchase'}" + @copy_key = "sales.#{subsection.copy_key}.privacynotice.#{joint_purchase ? 'joint_purchase' : 'not_joint_purchase'}" @joint_purchase = joint_purchase end diff --git a/app/models/form/sales/pages/resale.rb b/app/models/form/sales/pages/resale.rb index 6f4cd24e2..ffdbbc046 100644 --- a/app/models/form/sales/pages/resale.rb +++ b/app/models/form/sales/pages/resale.rb @@ -2,7 +2,6 @@ class Form::Sales::Pages::Resale < ::Form::Page def initialize(id, hsh, subsection) super @id = "resale" - @copy_key = "sales.sale_information.resale" @depends_on = [ { "staircase" => 2, diff --git a/app/models/form/sales/pages/staircase.rb b/app/models/form/sales/pages/staircase.rb index 1413abadc..c96da3f3f 100644 --- a/app/models/form/sales/pages/staircase.rb +++ b/app/models/form/sales/pages/staircase.rb @@ -3,7 +3,7 @@ class Form::Sales::Pages::Staircase < ::Form::Page super @id = "staircasing" @depends_on = [{ "ownershipsch" => 1 }] - @copy_key = "sales.#{subsection.id}.staircasing" + @copy_key = "sales.#{subsection.copy_key}.staircasing" end def questions diff --git a/app/models/form/sales/pages/value_shared_ownership.rb b/app/models/form/sales/pages/value_shared_ownership.rb index c2212c787..200563053 100644 --- a/app/models/form/sales/pages/value_shared_ownership.rb +++ b/app/models/form/sales/pages/value_shared_ownership.rb @@ -2,7 +2,6 @@ class Form::Sales::Pages::ValueSharedOwnership < ::Form::Page def initialize(id, hsh, subsection) super @id = "value_shared_ownership" - @copy_key = "sales.sale_information.value" end def questions diff --git a/app/models/form/sales/questions/buyer_interview.rb b/app/models/form/sales/questions/buyer_interview.rb index b49b57807..50c290904 100644 --- a/app/models/form/sales/questions/buyer_interview.rb +++ b/app/models/form/sales/questions/buyer_interview.rb @@ -2,7 +2,7 @@ class Form::Sales::Questions::BuyerInterview < ::Form::Question def initialize(id, hsh, page, joint_purchase:) super(id, hsh, page) @id = "noint" - @copy_key = "sales.#{subsection.id}.noint.#{joint_purchase ? 'joint_purchase' : 'not_joint_purchase'}" + @copy_key = "sales.#{subsection.copy_key}.noint.#{joint_purchase ? 'joint_purchase' : 'not_joint_purchase'}" @type = "radio" @answer_options = ANSWER_OPTIONS @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] diff --git a/app/models/form/sales/questions/deposit_amount.rb b/app/models/form/sales/questions/deposit_amount.rb index 41586cd94..6f2b98ce8 100644 --- a/app/models/form/sales/questions/deposit_amount.rb +++ b/app/models/form/sales/questions/deposit_amount.rb @@ -2,7 +2,6 @@ class Form::Sales::Questions::DepositAmount < ::Form::Question def initialize(id, hsh, subsection, ownershipsch:, optional:) super(id, hsh, subsection) @id = "deposit" - @copy_key = "sales.sale_information.deposit" @type = "numeric" @min = 0 @max = 999_999 diff --git a/app/models/form/sales/questions/deposit_discount.rb b/app/models/form/sales/questions/deposit_discount.rb index 289e3962c..bfc5f425d 100644 --- a/app/models/form/sales/questions/deposit_discount.rb +++ b/app/models/form/sales/questions/deposit_discount.rb @@ -2,7 +2,6 @@ class Form::Sales::Questions::DepositDiscount < ::Form::Question def initialize(id, hsh, page) super @id = "cashdis" - @copy_key = "sales.sale_information.cashdis" @type = "numeric" @min = 0 @max = 999_999 diff --git a/app/models/form/sales/questions/discount.rb b/app/models/form/sales/questions/discount.rb index 3807a8cfc..5dcf1f125 100644 --- a/app/models/form/sales/questions/discount.rb +++ b/app/models/form/sales/questions/discount.rb @@ -3,7 +3,6 @@ class Form::Sales::Questions::Discount < ::Form::Question super @id = "discount" @type = "numeric" - @copy_key = "sales.sale_information.discount" @min = 0 @max = form.start_year_2024_or_later? ? 70 : 100 @step = 0.1 diff --git a/app/models/form/sales/questions/equity.rb b/app/models/form/sales/questions/equity.rb index 4aae785b8..7a2a4ce5b 100644 --- a/app/models/form/sales/questions/equity.rb +++ b/app/models/form/sales/questions/equity.rb @@ -2,7 +2,6 @@ class Form::Sales::Questions::Equity < ::Form::Question def initialize(id, hsh, page) super @id = "equity" - @copy_key = "sales.sale_information.equity" @type = "numeric" @min = 0 @max = 100 diff --git a/app/models/form/sales/questions/extra_borrowing.rb b/app/models/form/sales/questions/extra_borrowing.rb index e3cd0ff7e..2ad13ef5d 100644 --- a/app/models/form/sales/questions/extra_borrowing.rb +++ b/app/models/form/sales/questions/extra_borrowing.rb @@ -2,7 +2,6 @@ class Form::Sales::Questions::ExtraBorrowing < ::Form::Question def initialize(id, hsh, subsection, ownershipsch:) super(id, hsh, subsection) @id = "extrabor" - @copy_key = "sales.sale_information.extrabor" @type = "radio" @answer_options = ANSWER_OPTIONS @page = page diff --git a/app/models/form/sales/questions/fromprop.rb b/app/models/form/sales/questions/fromprop.rb index 1a3393b7a..dec591cd0 100644 --- a/app/models/form/sales/questions/fromprop.rb +++ b/app/models/form/sales/questions/fromprop.rb @@ -2,7 +2,6 @@ class Form::Sales::Questions::Fromprop < ::Form::Question def initialize(id, hsh, page) super @id = "fromprop" - @copy_key = "sales.sale_information.fromprop" @type = "radio" @page = page @answer_options = ANSWER_OPTIONS diff --git a/app/models/form/sales/questions/grant.rb b/app/models/form/sales/questions/grant.rb index 17361fe9c..f069fedd2 100644 --- a/app/models/form/sales/questions/grant.rb +++ b/app/models/form/sales/questions/grant.rb @@ -2,7 +2,6 @@ class Form::Sales::Questions::Grant < ::Form::Question def initialize(id, hsh, page) super @id = "grant" - @copy_key = "sales.sale_information.grant" @type = "numeric" @min = 0 @max = 999_999 diff --git a/app/models/form/sales/questions/monthly_rent.rb b/app/models/form/sales/questions/monthly_rent.rb index 7e64d8571..8e9ecfaef 100644 --- a/app/models/form/sales/questions/monthly_rent.rb +++ b/app/models/form/sales/questions/monthly_rent.rb @@ -2,7 +2,6 @@ class Form::Sales::Questions::MonthlyRent < ::Form::Question def initialize(id, hsh, page) super @id = "mrent" - @copy_key = "sales.sale_information.mrent" @type = "numeric" @min = 0 @step = 0.01 diff --git a/app/models/form/sales/questions/mortgage_amount.rb b/app/models/form/sales/questions/mortgage_amount.rb index a6ffcf26a..e0677ee18 100644 --- a/app/models/form/sales/questions/mortgage_amount.rb +++ b/app/models/form/sales/questions/mortgage_amount.rb @@ -2,7 +2,6 @@ class Form::Sales::Questions::MortgageAmount < ::Form::Question def initialize(id, hsh, subsection, ownershipsch:) super(id, hsh, subsection) @id = "mortgage" - @copy_key = "sales.sale_information.mortgage" @type = "numeric" @min = 1 @step = 1 diff --git a/app/models/form/sales/questions/mortgage_lender.rb b/app/models/form/sales/questions/mortgage_lender.rb index a4aa55f17..96bf9e5b3 100644 --- a/app/models/form/sales/questions/mortgage_lender.rb +++ b/app/models/form/sales/questions/mortgage_lender.rb @@ -2,7 +2,6 @@ class Form::Sales::Questions::MortgageLender < ::Form::Question def initialize(id, hsh, subsection, ownershipsch:) super(id, hsh, subsection) @id = "mortgagelender" - @copy_key = "sales.sale_information.mortgagelender" @type = "select" @page = page @bottom_guidance_partial = "mortgage_lender" diff --git a/app/models/form/sales/questions/mortgage_lender_other.rb b/app/models/form/sales/questions/mortgage_lender_other.rb index 49876efb0..8cd5de8fb 100644 --- a/app/models/form/sales/questions/mortgage_lender_other.rb +++ b/app/models/form/sales/questions/mortgage_lender_other.rb @@ -2,7 +2,6 @@ class Form::Sales::Questions::MortgageLenderOther < ::Form::Question def initialize(id, hsh, subsection, ownershipsch:) super(id, hsh, subsection) @id = "mortgagelenderother" - @copy_key = "sales.sale_information.mortgagelenderother" @type = "text" @page = page @ownershipsch = ownershipsch diff --git a/app/models/form/sales/questions/mortgage_length.rb b/app/models/form/sales/questions/mortgage_length.rb index 877818b98..5d94fc832 100644 --- a/app/models/form/sales/questions/mortgage_length.rb +++ b/app/models/form/sales/questions/mortgage_length.rb @@ -2,7 +2,6 @@ class Form::Sales::Questions::MortgageLength < ::Form::Question def initialize(id, hsh, subsection, ownershipsch:) super(id, hsh, subsection) @id = "mortlen" - @copy_key = "sales.sale_information.mortlen" @type = "numeric" @min = 0 @max = 60 diff --git a/app/models/form/sales/questions/mortgageused.rb b/app/models/form/sales/questions/mortgageused.rb index e4a101072..3c3c42840 100644 --- a/app/models/form/sales/questions/mortgageused.rb +++ b/app/models/form/sales/questions/mortgageused.rb @@ -2,7 +2,6 @@ class Form::Sales::Questions::Mortgageused < ::Form::Question def initialize(id, hsh, subsection, ownershipsch:) super(id, hsh, subsection) @id = "mortgageused" - @copy_key = "sales.sale_information.mortgageused" @type = "radio" @answer_options = ANSWER_OPTIONS @ownershipsch = ownershipsch diff --git a/app/models/form/sales/questions/previous_bedrooms.rb b/app/models/form/sales/questions/previous_bedrooms.rb index d29da208a..dd243137d 100644 --- a/app/models/form/sales/questions/previous_bedrooms.rb +++ b/app/models/form/sales/questions/previous_bedrooms.rb @@ -2,7 +2,6 @@ class Form::Sales::Questions::PreviousBedrooms < ::Form::Question def initialize(id, hsh, page) super @id = "frombeds" - @copy_key = "sales.sale_information.frombeds" @type = "numeric" @width = 5 @min = 1 diff --git a/app/models/form/sales/questions/previous_tenure.rb b/app/models/form/sales/questions/previous_tenure.rb index 55b103f0d..794d449b5 100644 --- a/app/models/form/sales/questions/previous_tenure.rb +++ b/app/models/form/sales/questions/previous_tenure.rb @@ -2,7 +2,6 @@ class Form::Sales::Questions::PreviousTenure < ::Form::Question def initialize(id, hsh, page) super @id = "socprevten" - @copy_key = "sales.sale_information.socprevten" @type = "radio" @page = page @answer_options = ANSWER_OPTIONS diff --git a/app/models/form/sales/questions/privacy_notice.rb b/app/models/form/sales/questions/privacy_notice.rb index 5e73e7a3a..ace2c9ec1 100644 --- a/app/models/form/sales/questions/privacy_notice.rb +++ b/app/models/form/sales/questions/privacy_notice.rb @@ -2,7 +2,7 @@ class Form::Sales::Questions::PrivacyNotice < ::Form::Question def initialize(id, hsh, page, joint_purchase:) super(id, hsh, page) @id = "privacynotice" - @copy_key = "sales.#{subsection.id}.privacynotice.#{joint_purchase ? 'joint_purchase' : 'not_joint_purchase'}" + @copy_key = "sales.#{subsection.copy_key}.privacynotice.#{joint_purchase ? 'joint_purchase' : 'not_joint_purchase'}" @type = "checkbox" @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @joint_purchase = joint_purchase diff --git a/app/models/form/sales/questions/resale.rb b/app/models/form/sales/questions/resale.rb index 0026adb48..2417960b4 100644 --- a/app/models/form/sales/questions/resale.rb +++ b/app/models/form/sales/questions/resale.rb @@ -2,7 +2,6 @@ class Form::Sales::Questions::Resale < ::Form::Question def initialize(id, hsh, page) super @id = "resale" - @copy_key = "sales.sale_information.resale" @type = "radio" @answer_options = ANSWER_OPTIONS @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] diff --git a/app/models/form/sales/questions/value.rb b/app/models/form/sales/questions/value.rb index 257344fa2..1d258899d 100644 --- a/app/models/form/sales/questions/value.rb +++ b/app/models/form/sales/questions/value.rb @@ -2,7 +2,6 @@ class Form::Sales::Questions::Value < ::Form::Question def initialize(id, hsh, page) super @id = "value" - @copy_key = "sales.sale_information.value" @type = "numeric" @min = 0 @step = 1 diff --git a/app/models/form/sales/subsections/discounted_ownership_scheme.rb b/app/models/form/sales/subsections/discounted_ownership_scheme.rb index 63ce4af47..c74dcd262 100644 --- a/app/models/form/sales/subsections/discounted_ownership_scheme.rb +++ b/app/models/form/sales/subsections/discounted_ownership_scheme.rb @@ -4,6 +4,7 @@ class Form::Sales::Subsections::DiscountedOwnershipScheme < ::Form::Subsection @id = "discounted_ownership_scheme" @label = "Discounted ownership scheme" @depends_on = [{ "ownershipsch" => 2, "setup_completed?" => true }] + @copy_key = "sale_information" end def pages diff --git a/app/models/form/sales/subsections/outright_sale.rb b/app/models/form/sales/subsections/outright_sale.rb index af63c8179..afa0f4a69 100644 --- a/app/models/form/sales/subsections/outright_sale.rb +++ b/app/models/form/sales/subsections/outright_sale.rb @@ -4,6 +4,7 @@ class Form::Sales::Subsections::OutrightSale < ::Form::Subsection @id = "outright_sale" @label = "Outright sale" @depends_on = [{ "ownershipsch" => 3, "setup_completed?" => true }] + @copy_key = "sale_information" end def pages diff --git a/spec/models/form/lettings/questions/homeless_spec.rb b/spec/models/form/lettings/questions/homeless_spec.rb index f18a8ece7..7f2c1b054 100644 --- a/spec/models/form/lettings/questions/homeless_spec.rb +++ b/spec/models/form/lettings/questions/homeless_spec.rb @@ -28,10 +28,6 @@ RSpec.describe Form::Lettings::Questions::Homeless, type: :model do }) end - it "has no hint text" do - expect(question.hint_text).to be_empty - end - it "has the correct check_answers_card_number" do expect(question.check_answers_card_number).to eq(0) end diff --git a/spec/models/form/page_spec.rb b/spec/models/form/page_spec.rb index 9315fb237..3ec929fc4 100644 --- a/spec/models/form/page_spec.rb +++ b/spec/models/form/page_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Form::Page, type: :model do let(:enabled) { true } let(:depends_on_met) { true } let(:form) { instance_double(Form, depends_on_met:, type: "form-type", start_date: Time.utc(2024, 12, 25)) } - let(:subsection) { instance_double(Form::Subsection, depends_on:, enabled?: enabled, form:, id: "subsection-id") } + let(:subsection) { instance_double(Form::Subsection, depends_on:, enabled?: enabled, form:, id: "subsection-id", copy_key: "subsection-copy-key") } let(:page_id) { "net_income" } let(:questions) { [["earnings", { "conditional_for" => { "age1": nil }, "type" => "radio" }], %w[incfreq]] } let(:page_definition) do @@ -25,7 +25,7 @@ RSpec.describe Form::Page, type: :model do end it "sets copy_key in the default style" do - expect(page.copy_key).to eq("#{form.type}.#{subsection.id}.#{questions[0][0]}") + expect(page.copy_key).to eq("#{form.type}.#{subsection.copy_key}.#{questions[0][0]}") end context "when header is not provided" do diff --git a/spec/models/form/question_spec.rb b/spec/models/form/question_spec.rb index 651d167bd..8b9ab7a85 100644 --- a/spec/models/form/question_spec.rb +++ b/spec/models/form/question_spec.rb @@ -15,7 +15,7 @@ RSpec.describe Form::Question, type: :model do let(:inferred_check_answers_value) { [{ "condition" => { "postcode_known" => 0 }, "value" => "Weekly" }] } let(:form) { instance_double(Form, depends_on_met:, conditional_question_conditions:, type: "form-type", start_date: Time.utc(2024, 12, 25)) } - let(:subsection) { instance_double(Form::Subsection, form:, id: "subsection-id") } + let(:subsection) { instance_double(Form::Subsection, form:, id: "subsection-id", copy_key: "subsection-copy-key") } let(:page) { instance_double(Form::Page, subsection:, routed_to?: true, questions: form_questions) } let(:question_id) { "earnings" } let(:question_definition) do @@ -39,7 +39,7 @@ RSpec.describe Form::Question, type: :model do end it "sets copy_key in the default style" do - expect(question.copy_key).to eq("#{form.type}.#{subsection.id}.#{question_id}") + expect(question.copy_key).to eq("#{form.type}.#{subsection.copy_key}.#{question_id}") end context "when copy is not provided" do diff --git a/spec/models/form/sales/pages/buyer_interview_spec.rb b/spec/models/form/sales/pages/buyer_interview_spec.rb index f7c6bbb70..ea707a0fe 100644 --- a/spec/models/form/sales/pages/buyer_interview_spec.rb +++ b/spec/models/form/sales/pages/buyer_interview_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Form::Sales::Pages::BuyerInterview, type: :model do let(:page_id) { "buyer_interview" } let(:page_definition) { nil } let(:form) { instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_2024_or_later?: false) } - let(:subsection) { instance_double(Form::Subsection, form:, id: "setup") } + let(:subsection) { instance_double(Form::Subsection, form:, id: "setup", copy_key: "subsection_copy_key") } it "has correct subsection" do expect(page.subsection).to eq(subsection) @@ -24,43 +24,23 @@ RSpec.describe Form::Sales::Pages::BuyerInterview, type: :model do expect(page.description).to be_nil end - context "when form is before 2024" do - let(:subsection) { instance_double(Form::Subsection, form:, id: "household_characteristics") } + context "when there are joint buyers" do + subject(:page) { described_class.new(page_id, page_definition, subsection, joint_purchase: true) } - context "when there are joint buyers" do - subject(:page) { described_class.new(page_id, page_definition, subsection, joint_purchase: true) } + let(:subsection) { instance_double(Form::Subsection, form:, copy_key: "subsection_copy_key") } - it "has the expected copy_key" do - expect(page.copy_key).to eq("sales.household_characteristics.noint.joint_purchase") - end - end - - context "when there is a single buyer" do - subject(:page) { described_class.new(page_id, page_definition, subsection, joint_purchase: false) } - - it "has the expected copy_key" do - expect(page.copy_key).to eq("sales.household_characteristics.noint.not_joint_purchase") - end + it "has the expected copy_key" do + expect(page.copy_key).to eq("sales.subsection_copy_key.noint.joint_purchase") end end - context "when form is after 2024" do - let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1), start_year_2024_or_later?: true) } - - context "when there are joint buyers" do - subject(:page) { described_class.new(page_id, page_definition, subsection, joint_purchase: true) } - - it "has the expected copy_key" do - expect(page.copy_key).to eq("sales.setup.noint.joint_purchase") - end - end + context "when there is a single buyer" do + subject(:page) { described_class.new(page_id, page_definition, subsection, joint_purchase: false) } - context "when there is a single buyer" do - subject(:page) { described_class.new(page_id, page_definition, subsection, joint_purchase: false) } + let(:subsection) { instance_double(Form::Subsection, form:, copy_key: "subsection_copy_key") } - it "has the expected copy_key" do - expect(page.copy_key).to eq("sales.setup.noint.not_joint_purchase") - end + it "has the expected copy_key" do + expect(page.copy_key).to eq("sales.subsection_copy_key.noint.not_joint_purchase") end end end diff --git a/spec/models/form/sales/pages/privacy_notice_spec.rb b/spec/models/form/sales/pages/privacy_notice_spec.rb index 80be7ae66..8b21e7b9e 100644 --- a/spec/models/form/sales/pages/privacy_notice_spec.rb +++ b/spec/models/form/sales/pages/privacy_notice_spec.rb @@ -5,7 +5,7 @@ RSpec.describe Form::Sales::Pages::PrivacyNotice, type: :model do let(:page_id) { "privacy_notice" } let(:page_definition) { nil } - let(:subsection) { instance_double(Form::Subsection, id: "setup") } + let(:subsection) { instance_double(Form::Subsection, id: "setup", copy_key: "setup") } let(:form) { instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_2024_or_later?: false) } before do @@ -31,20 +31,10 @@ RSpec.describe Form::Sales::Pages::PrivacyNotice, type: :model do context "when there are joint buyers" do subject(:page) { described_class.new(page_id, page_definition, subsection, joint_purchase: true) } - context "when the form start year is before 2024" do - let(:subsection) { instance_double(Form::Subsection, id: "household_characteristics") } + let(:subsection) { instance_double(Form::Subsection, id: "subsection_id", copy_key: "subsection_copy_key") } - it "has the expected copy_key" do - expect(page.copy_key).to eq("sales.household_characteristics.privacynotice.joint_purchase") - end - end - - context "when the form start year is after 2024" do - let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1), start_year_2024_or_later?: true) } - - it "has the expected copy_key" do - expect(page.copy_key).to eq("sales.setup.privacynotice.joint_purchase") - end + it "has the expected copy_key" do + expect(page.copy_key).to eq("sales.subsection_copy_key.privacynotice.joint_purchase") end it "has correct depends_on" do @@ -55,20 +45,10 @@ RSpec.describe Form::Sales::Pages::PrivacyNotice, type: :model do context "when there is a single buyer" do subject(:page) { described_class.new(page_id, page_definition, subsection, joint_purchase: false) } - context "when the form start year is before 2024" do - let(:subsection) { instance_double(Form::Subsection, id: "household_characteristics") } - - it "has the expected copy_key" do - expect(page.copy_key).to eq("sales.household_characteristics.privacynotice.not_joint_purchase") - end - end - - context "when the form start year is after 2024" do - let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1), start_year_2024_or_later?: true) } + let(:subsection) { instance_double(Form::Subsection, id: "subsection_id", copy_key: "subsection_copy_key") } - it "has the expected copy_key" do - expect(page.copy_key).to eq("sales.setup.privacynotice.not_joint_purchase") - end + it "has the expected copy_key" do + expect(page.copy_key).to eq("sales.subsection_copy_key.privacynotice.not_joint_purchase") end it "has correct depends_on" do diff --git a/spec/models/form/sales/questions/buyer_interview_spec.rb b/spec/models/form/sales/questions/buyer_interview_spec.rb index 0db43407f..e812f6146 100644 --- a/spec/models/form/sales/questions/buyer_interview_spec.rb +++ b/spec/models/form/sales/questions/buyer_interview_spec.rb @@ -6,7 +6,8 @@ RSpec.describe Form::Sales::Questions::BuyerInterview, type: :model do let(:question_id) { nil } let(:question_definition) { nil } let(:form) { instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_2024_or_later?: true) } - let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form:, id: "setup")) } + let(:subsection) { instance_double(Form::Subsection, form:, copy_key: "setup") } + let(:page) { instance_double(Form::Page, subsection:) } it "has correct page" do expect(question.page).to eq(page) @@ -34,42 +35,20 @@ RSpec.describe Form::Sales::Questions::BuyerInterview, type: :model do context "when there are joint buyers" do subject(:question) { described_class.new(question_id, question_definition, page, joint_purchase: true) } - context "when the form start year is before 2024" do - let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form:, id: "household_characteristics")) } - let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 3, 1), start_year_2024_or_later?: false) } + let(:subsection) { instance_double(Form::Subsection, form:, copy_key: "subsection_copy_key") } - it "has the expected copy_key" do - expect(question.copy_key).to eq("sales.household_characteristics.noint.joint_purchase") - end - end - - context "when the form start year is after 2024" do - let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1), start_year_2024_or_later?: true) } - - it "has the expected copy_key" do - expect(question.copy_key).to eq("sales.setup.noint.joint_purchase") - end + it "has the expected copy_key" do + expect(question.copy_key).to eq("sales.subsection_copy_key.noint.joint_purchase") end end context "when there is a single buyer" do subject(:question) { described_class.new(question_id, question_definition, page, joint_purchase: false) } - context "when the form start year is before 2024" do - let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form:, id: "household_characteristics")) } - let(:form) { instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_2024_or_later?: false) } - - it "has the expected copy_key" do - expect(question.copy_key).to eq("sales.household_characteristics.noint.not_joint_purchase") - end - end - - context "when the form start year is after 2024" do - let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1), start_year_2024_or_later?: true) } + let(:subsection) { instance_double(Form::Subsection, form:, copy_key: "subsection_copy_key") } - it "has the expected copy_key" do - expect(question.copy_key).to eq("sales.setup.noint.not_joint_purchase") - end + it "has the expected copy_key" do + expect(question.copy_key).to eq("sales.subsection_copy_key.noint.not_joint_purchase") end end end diff --git a/spec/models/form/sales/questions/privacy_notice_spec.rb b/spec/models/form/sales/questions/privacy_notice_spec.rb index 5f764c7af..f279c6bf9 100644 --- a/spec/models/form/sales/questions/privacy_notice_spec.rb +++ b/spec/models/form/sales/questions/privacy_notice_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Form::Sales::Questions::PrivacyNotice, type: :model do let(:question_id) { nil } let(:question_definition) { nil } let(:page) { instance_double(Form::Page) } - let(:subsection) { instance_double(Form::Subsection, id: "setup") } + let(:subsection) { instance_double(Form::Subsection, id: "setup", copy_key: "setup") } let(:form) { instance_double(Form, start_date: Time.zone.local(2023, 4, 1)) } before do @@ -32,7 +32,7 @@ RSpec.describe Form::Sales::Questions::PrivacyNotice, type: :model do end context "when the form year is before 2024" do - let(:subsection) { instance_double(Form::Subsection, id: "household_characteristics") } + let(:subsection) { instance_double(Form::Subsection, id: "household_characteristics", copy_key: "household_characteristics") } before do allow(form).to receive(:start_year_2024_or_later?).and_return(false) diff --git a/spec/models/form/sales/questions/uprn_confirmation_spec.rb b/spec/models/form/sales/questions/uprn_confirmation_spec.rb index 0e54155b6..c6f7fd723 100644 --- a/spec/models/form/sales/questions/uprn_confirmation_spec.rb +++ b/spec/models/form/sales/questions/uprn_confirmation_spec.rb @@ -25,10 +25,6 @@ RSpec.describe Form::Sales::Questions::UprnConfirmation, type: :model do expect(question.derived?(nil)).to be false end - it "has the correct unanswered_error_message" do - expect(question.unanswered_error_message).to eq("You must answer #{format_ending(I18n.t('forms.2023.sales.property_information.uprn_confirmed.check_answer_label'))}") - end - describe "notification_banner" do context "when address is not present" do it "returns nil" do diff --git a/spec/models/form/sales/subsections/discounted_ownership_scheme_spec.rb b/spec/models/form/sales/subsections/discounted_ownership_scheme_spec.rb index a573103a3..3fa57571b 100644 --- a/spec/models/form/sales/subsections/discounted_ownership_scheme_spec.rb +++ b/spec/models/form/sales/subsections/discounted_ownership_scheme_spec.rb @@ -56,6 +56,10 @@ RSpec.describe Form::Sales::Subsections::DiscountedOwnershipScheme, type: :model expect(discounted_ownership_scheme.id).to eq("discounted_ownership_scheme") end + it "has the correct copy key" do + expect(discounted_ownership_scheme.copy_key).to eq("sale_information") + end + it "has the correct label" do expect(discounted_ownership_scheme.label).to eq("Discounted ownership scheme") end diff --git a/spec/models/form/sales/subsections/outright_sale_spec.rb b/spec/models/form/sales/subsections/outright_sale_spec.rb index 52941d0a9..b91553939 100644 --- a/spec/models/form/sales/subsections/outright_sale_spec.rb +++ b/spec/models/form/sales/subsections/outright_sale_spec.rb @@ -109,6 +109,10 @@ RSpec.describe Form::Sales::Subsections::OutrightSale, type: :model do expect(outright_sale.id).to eq("outright_sale") end + it "has the correct copy key" do + expect(outright_sale.copy_key).to eq("sale_information") + end + it "has the correct label" do expect(outright_sale.label).to eq("Outright sale") end diff --git a/spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb b/spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb index 4c546d58c..51e2ee60d 100644 --- a/spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb +++ b/spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb @@ -65,6 +65,10 @@ RSpec.describe Form::Sales::Subsections::SharedOwnershipScheme, type: :model do expect(shared_ownership_scheme.id).to eq("shared_ownership_scheme") end + it "has the correct copy key" do + expect(shared_ownership_scheme.copy_key).to eq("sale_information") + end + it "has the correct label" do expect(shared_ownership_scheme.label).to eq("Shared ownership scheme") end diff --git a/spec/models/form/subsection_spec.rb b/spec/models/form/subsection_spec.rb index 8a4b9d3d7..0215e51dc 100644 --- a/spec/models/form/subsection_spec.rb +++ b/spec/models/form/subsection_spec.rb @@ -19,6 +19,10 @@ RSpec.describe Form::Subsection, type: :model do expect(subsection.id).to eq(subsection_id) end + it "has a copy_key defaulting to the id" do + expect(subsection.copy_key).to eq(subsection_id) + end + it "has a label" do expect(subsection.label).to eq("Household characteristics") end From 5d36a13e7c74c08fbd1b437d14b4774f4a08653a Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Fri, 22 Nov 2024 12:39:32 +0000 Subject: [PATCH 27/34] Ignore irrelevant empty headers (#2800) --- .../lettings/year2024/csv_parser.rb | 2 +- .../bulk_upload/sales/year2024/csv_parser.rb | 2 +- .../lettings/year2024/csv_parser_spec.rb | 23 +++++++++++++ .../sales/year2024/csv_parser_spec.rb | 32 +++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/app/services/bulk_upload/lettings/year2024/csv_parser.rb b/app/services/bulk_upload/lettings/year2024/csv_parser.rb index 22caeab02..08e12353b 100644 --- a/app/services/bulk_upload/lettings/year2024/csv_parser.rb +++ b/app/services/bulk_upload/lettings/year2024/csv_parser.rb @@ -15,7 +15,7 @@ class BulkUpload::Lettings::Year2024::CsvParser def row_offset if with_headers? - rows.find_index { |row| row[0].match(/field number/i) } + 1 + rows.find_index { |row| row[0].present? && row[0].match(/field number/i) } + 1 else 0 end diff --git a/app/services/bulk_upload/sales/year2024/csv_parser.rb b/app/services/bulk_upload/sales/year2024/csv_parser.rb index 4a3cb7ac9..b20c5b3d3 100644 --- a/app/services/bulk_upload/sales/year2024/csv_parser.rb +++ b/app/services/bulk_upload/sales/year2024/csv_parser.rb @@ -15,7 +15,7 @@ class BulkUpload::Sales::Year2024::CsvParser def row_offset if with_headers? - rows.find_index { |row| row[0].match(/field number/i) } + 1 + rows.find_index { |row| row[0].present? && row[0].match(/field number/i) } + 1 else 0 end diff --git a/spec/services/bulk_upload/lettings/year2024/csv_parser_spec.rb b/spec/services/bulk_upload/lettings/year2024/csv_parser_spec.rb index d0e5b3692..b0fcaf8b6 100644 --- a/spec/services/bulk_upload/lettings/year2024/csv_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2024/csv_parser_spec.rb @@ -30,6 +30,29 @@ RSpec.describe BulkUpload::Lettings::Year2024::CsvParser do end end + context "when some csv headers are empty (and we don't care about them)" do + before do + file.write("Question\n") + file.write("Additional info\n") + file.write("Values\n") + file.write("\n") + file.write("Type of letting the question applies to\n") + file.write("Duplicate check field?\n") + file.write(BulkUpload::LettingsLogToCsv.new(log:).default_2024_field_numbers_row) + file.write(BulkUpload::LettingsLogToCsv.new(log:).to_2024_csv_row) + file.rewind + end + + it "returns correct offsets" do + expect(service.row_offset).to eq(7) + expect(service.col_offset).to eq(1) + end + + it "parses csv correctly" do + expect(service.row_parsers[0].field_13).to eql(log.tenancycode) + end + end + context "when parsing csv with headers with extra rows" do before do file.write("Section\n") diff --git a/spec/services/bulk_upload/sales/year2024/csv_parser_spec.rb b/spec/services/bulk_upload/sales/year2024/csv_parser_spec.rb index 9440b7e8c..5f9f003d0 100644 --- a/spec/services/bulk_upload/sales/year2024/csv_parser_spec.rb +++ b/spec/services/bulk_upload/sales/year2024/csv_parser_spec.rb @@ -39,6 +39,38 @@ RSpec.describe BulkUpload::Sales::Year2024::CsvParser do end end + context "when some csv headers are empty (and we don't care about them)" do + before do + file.write("Question\n") + file.write("Additional info\n") + file.write("Values\n") + file.write("\n") + file.write("Type of letting the question applies to\n") + file.write("Duplicate check field?\n") + file.write(BulkUpload::SalesLogToCsv.new(log:).default_2024_field_numbers_row) + file.write(BulkUpload::SalesLogToCsv.new(log:).to_2024_csv_row) + file.write("\n") + file.rewind + end + + it "returns correct offsets" do + expect(service.row_offset).to eq(7) + expect(service.col_offset).to eq(1) + end + + it "parses csv correctly" do + expect(service.row_parsers[0].field_22).to eql(log.uprn) + end + + it "counts the number of valid field numbers correctly" do + expect(service).to be_correct_field_count + end + + it "does not parse the last empty row" do + expect(service.row_parsers.count).to eq(1) + end + end + context "when parsing csv with headers in arbitrary order" do let(:seed) { rand } From 6c10172c78151bcbf2c9fecd3c7117a3d970955c Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Fri, 22 Nov 2024 14:11:43 +0000 Subject: [PATCH 28/34] CLDC-3755 Add 2025 shared ownership subsection (#2782) * Add shared ownership initial purchase subsection * Display correct subsection based on year * Update some content * Update LivingBeforePurchase routing * Update routing and check for page existence in guidance * Add management fee questions * Only check the subsections relevant to the form * Update content * Update financial calculations text --- .../form/sales/pages/estate_management_fee.rb | 13 +++ .../sales/pages/living_before_purchase.rb | 12 ++- .../sales/questions/has_management_fee.rb | 24 +++++ .../form/sales/questions/management_fee.rb | 12 +++ .../form/sales/sections/sale_information.rb | 10 +- .../shared_ownership_initial_purchase.rb | 48 ++++++++++ .../subsections/shared_ownership_scheme.rb | 2 +- ...ial_calculations_shared_ownership.html.erb | 6 +- .../forms/2025/sales/sale_information.en.yml | 21 +++- ...0241114154215_add_management_fee_fields.rb | 8 ++ db/schema.rb | 2 + spec/features/accessibility_spec.rb | 5 +- .../pages/living_before_purchase_spec.rb | 72 +++++++++++--- .../sales/sections/sale_information_spec.rb | 28 ++++-- .../shared_ownership_initial_purchase_spec.rb | 95 +++++++++++++++++++ 15 files changed, 326 insertions(+), 32 deletions(-) create mode 100644 app/models/form/sales/pages/estate_management_fee.rb create mode 100644 app/models/form/sales/questions/has_management_fee.rb create mode 100644 app/models/form/sales/questions/management_fee.rb create mode 100644 app/models/form/sales/subsections/shared_ownership_initial_purchase.rb create mode 100644 db/migrate/20241114154215_add_management_fee_fields.rb create mode 100644 spec/models/form/sales/subsections/shared_ownership_initial_purchase_spec.rb diff --git a/app/models/form/sales/pages/estate_management_fee.rb b/app/models/form/sales/pages/estate_management_fee.rb new file mode 100644 index 000000000..5be478f80 --- /dev/null +++ b/app/models/form/sales/pages/estate_management_fee.rb @@ -0,0 +1,13 @@ +class Form::Sales::Pages::EstateManagementFee < ::Form::Page + def initialize(id, hsh, subsection) + super + @copy_key = "sales.sale_information.management_fee" + end + + def questions + @questions ||= [ + Form::Sales::Questions::HasManagementFee.new(nil, nil, self), + Form::Sales::Questions::ManagementFee.new(nil, nil, self), + ] + end +end diff --git a/app/models/form/sales/pages/living_before_purchase.rb b/app/models/form/sales/pages/living_before_purchase.rb index 3bb5510ce..b8797537b 100644 --- a/app/models/form/sales/pages/living_before_purchase.rb +++ b/app/models/form/sales/pages/living_before_purchase.rb @@ -19,11 +19,17 @@ class Form::Sales::Pages::LivingBeforePurchase < ::Form::Page end end - def depends_on + def routed_to?(log, _user) + super && page_routed_to?(log) + end + + def page_routed_to?(log) + return false if form.start_year_2025_or_later? && log.resale != 2 + if @joint_purchase - [{ "joint_purchase?" => true }] + log.joint_purchase? else - [{ "not_joint_purchase?" => true }, { "jointpur" => nil }] + log.not_joint_purchase? || log.jointpur.nil? end end end diff --git a/app/models/form/sales/questions/has_management_fee.rb b/app/models/form/sales/questions/has_management_fee.rb new file mode 100644 index 000000000..20a71ff5e --- /dev/null +++ b/app/models/form/sales/questions/has_management_fee.rb @@ -0,0 +1,24 @@ +class Form::Sales::Questions::HasManagementFee < ::Form::Question + def initialize(id, hsh, subsection) + super + @id = "has_management_fee" + @copy_key = "sales.sale_information.management_fee.has_management_fee" + @type = "radio" + @answer_options = ANSWER_OPTIONS + @conditional_for = { + "management_fee" => [1], + } + @hidden_in_check_answers = { + "depends_on" => [ + { + "has_management_fee" => 1, + }, + ], + } + end + + ANSWER_OPTIONS = { + "1" => { "value" => "Yes" }, + "0" => { "value" => "No" }, + }.freeze +end diff --git a/app/models/form/sales/questions/management_fee.rb b/app/models/form/sales/questions/management_fee.rb new file mode 100644 index 000000000..213b9e3df --- /dev/null +++ b/app/models/form/sales/questions/management_fee.rb @@ -0,0 +1,12 @@ +class Form::Sales::Questions::ManagementFee < ::Form::Question + def initialize(id, hsh, subsection) + super + @id = "management_fee" + @copy_key = "sales.sale_information.management_fee.management_fee" + @type = "numeric" + @min = 1 + @step = 0.01 + @width = 5 + @prefix = "£" + end +end diff --git a/app/models/form/sales/sections/sale_information.rb b/app/models/form/sales/sections/sale_information.rb index b57eb70a6..22dbbef5a 100644 --- a/app/models/form/sales/sections/sale_information.rb +++ b/app/models/form/sales/sections/sale_information.rb @@ -5,9 +5,17 @@ class Form::Sales::Sections::SaleInformation < ::Form::Section @label = "Sale information" @description = "" @subsections = [ - Form::Sales::Subsections::SharedOwnershipScheme.new(nil, nil, self), + shared_ownership_scheme_subsection, Form::Sales::Subsections::DiscountedOwnershipScheme.new(nil, nil, self), Form::Sales::Subsections::OutrightSale.new(nil, nil, self), ] || [] end + + def shared_ownership_scheme_subsection + if form.start_year_2025_or_later? + Form::Sales::Subsections::SharedOwnershipInitialPurchase.new(nil, nil, self) + else + Form::Sales::Subsections::SharedOwnershipScheme.new(nil, nil, self) + end + end end diff --git a/app/models/form/sales/subsections/shared_ownership_initial_purchase.rb b/app/models/form/sales/subsections/shared_ownership_initial_purchase.rb new file mode 100644 index 000000000..5dfb322a2 --- /dev/null +++ b/app/models/form/sales/subsections/shared_ownership_initial_purchase.rb @@ -0,0 +1,48 @@ +class Form::Sales::Subsections::SharedOwnershipInitialPurchase < ::Form::Subsection + def initialize(id, hsh, section) + super + @id = "shared_ownership_initial_purchase" + @label = "Shared ownership - initial purchase" + @depends_on = [{ "ownershipsch" => 1, "setup_completed?" => true, "staircase" => 2 }] + end + + def pages + @pages ||= [ + Form::Sales::Pages::Resale.new(nil, nil, self), + Form::Sales::Pages::LivingBeforePurchase.new("living_before_purchase_shared_ownership_joint_purchase", nil, self, ownershipsch: 1, joint_purchase: true), + Form::Sales::Pages::LivingBeforePurchase.new("living_before_purchase_shared_ownership", nil, self, ownershipsch: 1, joint_purchase: false), + Form::Sales::Pages::HandoverDate.new(nil, nil, self), + Form::Sales::Pages::HandoverDateCheck.new(nil, nil, self), + Form::Sales::Pages::BuyerPrevious.new("buyer_previous_joint_purchase", nil, self, joint_purchase: true), + Form::Sales::Pages::BuyerPrevious.new("buyer_previous_not_joint_purchase", nil, self, joint_purchase: false), + Form::Sales::Pages::PreviousBedrooms.new(nil, nil, self), + Form::Sales::Pages::PreviousPropertyType.new(nil, nil, self), + Form::Sales::Pages::PreviousTenure.new(nil, nil, self), + Form::Sales::Pages::ValueSharedOwnership.new(nil, nil, self), + Form::Sales::Pages::AboutPriceValueCheck.new("about_price_shared_ownership_value_check", nil, self), + Form::Sales::Pages::Equity.new(nil, nil, self), + Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_equity_value_check", nil, self), + Form::Sales::Pages::Mortgageused.new("mortgage_used_shared_ownership", nil, self, ownershipsch: 1), + Form::Sales::Pages::MortgageValueCheck.new("mortgage_used_mortgage_value_check", nil, self), + Form::Sales::Pages::MortgageAmount.new("mortgage_amount_shared_ownership", nil, self, ownershipsch: 1), + Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_mortgage_amount_value_check", nil, self), + Form::Sales::Pages::MortgageValueCheck.new("mortgage_amount_mortgage_value_check", nil, self), + Form::Sales::Pages::MortgageLength.new("mortgage_length_shared_ownership", nil, self, ownershipsch: 1), + Form::Sales::Pages::Deposit.new("deposit_shared_ownership", nil, self, ownershipsch: 1, optional: false), + Form::Sales::Pages::Deposit.new("deposit_shared_ownership_optional", nil, self, ownershipsch: 1, optional: true), + Form::Sales::Pages::DepositValueCheck.new("deposit_joint_purchase_value_check", nil, self, joint_purchase: true), + Form::Sales::Pages::DepositValueCheck.new("deposit_value_check", nil, self, joint_purchase: false), + Form::Sales::Pages::DepositDiscount.new("deposit_discount", nil, self, optional: false), + Form::Sales::Pages::DepositDiscount.new("deposit_discount_optional", nil, self, optional: true), + Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_deposit_value_check", nil, self), + Form::Sales::Pages::MonthlyRent.new(nil, nil, self), + Form::Sales::Pages::LeaseholdCharges.new("leasehold_charges_shared_ownership", nil, self, ownershipsch: 1), + Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_shared_ownership_value_check", nil, self), + Form::Sales::Pages::EstateManagementFee.new("estate_management_fee", nil, self), + ].compact + end + + def displayed_in_tasklist?(log) + log.staircase == 2 && (log.ownershipsch.nil? || log.ownershipsch == 1) + end +end diff --git a/app/models/form/sales/subsections/shared_ownership_scheme.rb b/app/models/form/sales/subsections/shared_ownership_scheme.rb index f5d52153e..20a088eae 100644 --- a/app/models/form/sales/subsections/shared_ownership_scheme.rb +++ b/app/models/form/sales/subsections/shared_ownership_scheme.rb @@ -11,7 +11,7 @@ class Form::Sales::Subsections::SharedOwnershipScheme < ::Form::Subsection @pages ||= [ Form::Sales::Pages::LivingBeforePurchase.new("living_before_purchase_shared_ownership_joint_purchase", nil, self, ownershipsch: 1, joint_purchase: true), Form::Sales::Pages::LivingBeforePurchase.new("living_before_purchase_shared_ownership", nil, self, ownershipsch: 1, joint_purchase: false), - (Form::Sales::Pages::Staircase.new(nil, nil, self) unless form.start_year_2025_or_later?), + Form::Sales::Pages::Staircase.new(nil, nil, self), Form::Sales::Pages::AboutStaircase.new("about_staircasing_joint_purchase", nil, self, joint_purchase: true), Form::Sales::Pages::AboutStaircase.new("about_staircasing_not_joint_purchase", nil, self, joint_purchase: false), Form::Sales::Pages::StaircaseBoughtValueCheck.new(nil, nil, self), diff --git a/app/views/form/guidance/_financial_calculations_shared_ownership.html.erb b/app/views/form/guidance/_financial_calculations_shared_ownership.html.erb index 0741e6afa..2dd2f343e 100644 --- a/app/views/form/guidance/_financial_calculations_shared_ownership.html.erb +++ b/app/views/form/guidance/_financial_calculations_shared_ownership.html.erb @@ -20,11 +20,11 @@ <% end %> must equal the purchase price <%= question_link("value", log, current_user) %> - <% stairbought_page = log.form.get_question("stairbought", log).page %> - <% if stairbought_page.routed_to?(log, current_user) %> + <% stairbought_page = log.form.get_question("stairbought", log)&.page %> + <% if stairbought_page&.routed_to?(log, current_user) %> multiplied by the percentage bought <%= question_link("stairbought", log, current_user) %> <% else %> - multiplied by the percentage equity stake <%= question_link("equity", log, current_user) %> + multiplied by the percentage equity share <%= question_link("equity", log, current_user) %> <% end %>

      <% end %> diff --git a/config/locales/forms/2025/sales/sale_information.en.yml b/config/locales/forms/2025/sales/sale_information.en.yml index 33826e58b..9a273d1c3 100644 --- a/config/locales/forms/2025/sales/sale_information.en.yml +++ b/config/locales/forms/2025/sales/sale_information.en.yml @@ -107,9 +107,9 @@ en: equity: page_header: "About the price of the property" - check_answer_label: "Initial percentage equity stake" - hint_text: "Enter the amount of initial equity held by the purchaser (for example, 25% or 50%)" - question_text: "What was the initial percentage equity stake purchased?" + check_answer_label: "Initial percentage equity share" + hint_text: "Enter the amount of initial equity share held by the purchaser (for example, 25% or 50%)" + question_text: "What was the initial percentage share purchased?" mortgageused: page_header: "Mortgage Amount" @@ -168,9 +168,9 @@ en: leaseholdcharges: page_header: "" has_mscharge: - check_answer_label: "Does the property have any monthly leasehold charges?" + check_answer_label: "Does the property have any service charges?" hint_text: "For example, service and management charges" - question_text: "Does the property have any monthly leasehold charges?" + question_text: "Does the property have any service charges?" mscharge: check_answer_label: "Monthly leasehold charges" hint_text: "" @@ -199,3 +199,14 @@ en: check_answer_label: "Amount of any loan, grant or subsidy" hint_text: "For all schemes except Right to Buy (RTB), Preserved Right to Buy (PRTB), Voluntary Right to Buy (VRTB) and Rent to Buy" question_text: "What was the amount of any loan, grant, discount or subsidy given?" + + management_fee: + page_header: "" + has_management_fee: + check_answer_label: "Does the property have an estate management fee?" + hint_text: "Estate management fees are typically used for the maintenance of communal gardens, payments, private roads, car parks and/or play areas within new build estates." + question_text: "Does the property have an estate management fee?" + management_fee: + check_answer_label: "Monthly estate management fee" + hint_text: "" + question_text: "Enter the total monthly management fee" diff --git a/db/migrate/20241114154215_add_management_fee_fields.rb b/db/migrate/20241114154215_add_management_fee_fields.rb new file mode 100644 index 000000000..f8455d259 --- /dev/null +++ b/db/migrate/20241114154215_add_management_fee_fields.rb @@ -0,0 +1,8 @@ +class AddManagementFeeFields < ActiveRecord::Migration[7.0] + def change + change_table :sales_logs, bulk: true do |t| + t.column :has_management_fee, :integer + t.column :management_fee, :decimal, precision: 10, scale: 2 + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 5b6ddacdb..9aa744dc2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -758,6 +758,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_11_18_104046) do t.integer "partner_under_16_value_check" t.integer "multiple_partners_value_check" t.bigint "created_by_id" + t.integer "has_management_fee" + t.decimal "management_fee", precision: 10, scale: 2 t.index ["assigned_to_id"], name: "index_sales_logs_on_assigned_to_id" t.index ["bulk_upload_id"], name: "index_sales_logs_on_bulk_upload_id" t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id" diff --git a/spec/features/accessibility_spec.rb b/spec/features/accessibility_spec.rb index 15cd87ebb..889fcdf26 100644 --- a/spec/features/accessibility_spec.rb +++ b/spec/features/accessibility_spec.rb @@ -138,15 +138,18 @@ RSpec.describe "Accessibility", js: true do routes = find_routes("sales-log", sales_log, bulk_upload) - routes.reject { |path| + routes = routes.reject { |path| path.include?("/edit") || path.include?("/new") || path.include?("*page") || path.include?("/sales-logs/bulk-upload-logs/#{bulk_upload.id}") || path.include?("bulk-upload-soft-validations-check") || path.include?("filters/update") || path == "/sales-logs/bulk-upload-resume/#{bulk_upload.id}" || path == "/sales-logs/bulk-upload-logs" || + path.include?("/check-answers") || other_form_page_ids.any? { |page_id| path.include?(page_id.dasherize) } || sales_log_pages.any? { |page| path.include?(page.id.dasherize) && !page.routed_to?(sales_log, user) } }.uniq + + routes + sales_log.form.subsections.map(&:id).map { |id| "/sales-logs/#{sales_log.id}/#{id.dasherize}/check-answers" } end before do diff --git a/spec/models/form/sales/pages/living_before_purchase_spec.rb b/spec/models/form/sales/pages/living_before_purchase_spec.rb index 26026471b..b597f90e9 100644 --- a/spec/models/form/sales/pages/living_before_purchase_spec.rb +++ b/spec/models/form/sales/pages/living_before_purchase_spec.rb @@ -5,17 +5,19 @@ RSpec.describe Form::Sales::Pages::LivingBeforePurchase, type: :model do let(:page_id) { nil } let(:page_definition) { nil } - let(:subsection) { instance_double(Form::Subsection) } + let(:start_year) { 2022 } + let(:form) { Form.new(nil, start_year, [], "sales") } + let(:subsection) { instance_double(Form::Subsection, depends_on: nil, form:) } it "has correct subsection" do expect(page.subsection).to eq(subsection) end describe "questions" do - let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date:)) } + let(:subsection) { instance_double(Form::Subsection, form:, depends_on: nil) } context "when 2022" do - let(:start_date) { Time.utc(2022, 2, 8) } + let(:start_year) { 2022 } it "has correct questions" do expect(page.questions.map(&:id)).to eq(%w[proplen]) @@ -23,7 +25,7 @@ RSpec.describe Form::Sales::Pages::LivingBeforePurchase, type: :model do end context "when 2023" do - let(:start_date) { Time.utc(2023, 2, 8) } + let(:start_year) { 2023 } it "has correct questions" do expect(page.questions.map(&:id)).to eq(%w[proplen_asked proplen]) @@ -39,15 +41,63 @@ RSpec.describe Form::Sales::Pages::LivingBeforePurchase, type: :model do expect(page.description).to be_nil end - it "has correct depends_on" do - expect(page.depends_on).to eq([{ "not_joint_purchase?" => true }, { "jointpur" => nil }]) - end + context "when routing" do + context "with form before 2025" do + let(:start_year) { 2024 } + + context "with joint purchase" do + subject(:page) { described_class.new(page_id, page_definition, subsection, ownershipsch: 1, joint_purchase: true) } + + it "routes to the page when joint purchase is true" do + log = build(:sales_log, jointpur: 1) + expect(page.routed_to?(log, nil)).to eq(true) + end + + it "does not route to the page when joint purchase is false" do + log = build(:sales_log, jointpur: 2) + expect(page.routed_to?(log, nil)).to eq(false) + end + + it "does not route to the page when joint purchase is missing" do + log = build(:sales_log, jointpur: nil) + expect(page.routed_to?(log, nil)).to eq(false) + end + end + + context "with non joint purchase" do + subject(:page) { described_class.new(page_id, page_definition, subsection, ownershipsch: 1, joint_purchase: false) } + + it "routes to the page when joint purchase is false" do + log = build(:sales_log, jointpur: 2) + expect(page.routed_to?(log, nil)).to eq(true) + end - context "with joint purchase" do - subject(:page) { described_class.new(page_id, page_definition, subsection, ownershipsch: 1, joint_purchase: true) } + it "does not route to the page when joint purchase is true" do + log = build(:sales_log, jointpur: 1) + expect(page.routed_to?(log, nil)).to eq(false) + end - it "has correct depends_on" do - expect(page.depends_on).to eq([{ "joint_purchase?" => true }]) + it "routes to the page when joint purchase is missing" do + log = build(:sales_log, jointpur: nil) + expect(page.routed_to?(log, nil)).to eq(true) + end + end + end + + context "with form on or after 2025" do + subject(:page) { described_class.new(page_id, page_definition, subsection, ownershipsch: 1, joint_purchase: true) } + + let(:start_year) { 2025 } + + it "routes to the page when resale is 2" do + log = build(:sales_log, jointpur: 1, resale: 2) + expect(page.routed_to?(log, nil)).to eq(true) + end + + it "does not route to the page when resale is not 2" do + log = build(:sales_log, jointpur: 1, resale: nil) + expect(page.routed_to?(log, nil)).to eq(false) + end end end end diff --git a/spec/models/form/sales/sections/sale_information_spec.rb b/spec/models/form/sales/sections/sale_information_spec.rb index 0b2ab4144..8167c92a3 100644 --- a/spec/models/form/sales/sections/sale_information_spec.rb +++ b/spec/models/form/sales/sections/sale_information_spec.rb @@ -5,18 +5,32 @@ RSpec.describe Form::Sales::Sections::SaleInformation, type: :model do let(:section_id) { nil } let(:section_definition) { nil } - let(:form) { instance_double(Form) } + let(:form) { instance_double(Form, start_year_2025_or_later?: false) } it "has correct form" do expect(sale_information.form).to eq(form) end - it "has correct subsections" do - expect(sale_information.subsections.map(&:id)).to eq(%w[ - shared_ownership_scheme - discounted_ownership_scheme - outright_sale - ]) + context "when form is before 2025" do + it "has correct subsections" do + expect(sale_information.subsections.map(&:id)).to eq(%w[ + shared_ownership_scheme + discounted_ownership_scheme + outright_sale + ]) + end + end + + context "when form is 2025 or later" do + let(:form) { instance_double(Form, start_year_2025_or_later?: true) } + + it "has correct subsections" do + expect(sale_information.subsections.map(&:id)).to eq(%w[ + shared_ownership_initial_purchase + discounted_ownership_scheme + outright_sale + ]) + end end it "has the correct id" do diff --git a/spec/models/form/sales/subsections/shared_ownership_initial_purchase_spec.rb b/spec/models/form/sales/subsections/shared_ownership_initial_purchase_spec.rb new file mode 100644 index 000000000..3b2d72b01 --- /dev/null +++ b/spec/models/form/sales/subsections/shared_ownership_initial_purchase_spec.rb @@ -0,0 +1,95 @@ +require "rails_helper" + +RSpec.describe Form::Sales::Subsections::SharedOwnershipInitialPurchase, type: :model do + subject(:shared_ownership_initial_purchase) { described_class.new(subsection_id, subsection_definition, section) } + + let(:subsection_id) { nil } + let(:subsection_definition) { nil } + let(:section) { instance_double(Form::Sales::Sections::SaleInformation) } + + before do + allow(section).to receive(:form).and_return(instance_double(Form, start_date: Time.zone.local(2025, 4, 1))) + end + + it "has correct section" do + expect(shared_ownership_initial_purchase.section).to eq(section) + end + + it "has correct pages" do + expect(shared_ownership_initial_purchase.pages.map(&:id)).to eq( + %w[ + resale + living_before_purchase_shared_ownership_joint_purchase + living_before_purchase_shared_ownership + handover_date + handover_date_check + buyer_previous_joint_purchase + buyer_previous_not_joint_purchase + previous_bedrooms + previous_property_type + shared_ownership_previous_tenure + value_shared_ownership + about_price_shared_ownership_value_check + equity + shared_ownership_equity_value_check + mortgage_used_shared_ownership + mortgage_used_mortgage_value_check + mortgage_amount_shared_ownership + shared_ownership_mortgage_amount_value_check + mortgage_amount_mortgage_value_check + mortgage_length_shared_ownership + deposit_shared_ownership + deposit_shared_ownership_optional + deposit_joint_purchase_value_check + deposit_value_check + deposit_discount + deposit_discount_optional + shared_ownership_deposit_value_check + monthly_rent + leasehold_charges_shared_ownership + monthly_charges_shared_ownership_value_check + estate_management_fee + ], + ) + end + + it "has the correct id" do + expect(shared_ownership_initial_purchase.id).to eq("shared_ownership_initial_purchase") + end + + it "has the correct label" do + expect(shared_ownership_initial_purchase.label).to eq("Shared ownership - initial purchase") + end + + it "has the correct depends_on" do + expect(shared_ownership_initial_purchase.depends_on).to eq([ + { + "ownershipsch" => 1, "setup_completed?" => true, "staircase" => 2 + }, + ]) + end + + context "when it is a shared ownership scheme and not staircase" do + let(:log) { FactoryBot.build(:sales_log, ownershipsch: 1, staircase: 2) } + + it "is displayed in tasklist" do + expect(shared_ownership_initial_purchase.displayed_in_tasklist?(log)).to eq(true) + end + end + + context "when it is not a shared ownership scheme" do + let(:log) { FactoryBot.build(:sales_log, ownershipsch: 2, staircase: 2) } + + it "is displayed in tasklist" do + expect(shared_ownership_initial_purchase.displayed_in_tasklist?(log)).to eq(false) + end + end + + context "when it is staircase" do + let(:log) { FactoryBot.build(:sales_log, ownershipsch: 1, staircase: 1) } + + it "is displayed in tasklist" do + expect(shared_ownership_initial_purchase.displayed_in_tasklist?(log)).to eq(false) + end + end +end From fcd4744694d12118077b060a4c5bce6a023d122e Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Fri, 22 Nov 2024 15:59:30 +0000 Subject: [PATCH 29/34] Add create test log button (#2804) * Add create test log button * Add setup only button * enable test logs on review apps * Add factorybot to all environments * Add faker --- Gemfile | 4 ++-- .../create_log_actions_component.html.erb | 5 +++++ app/components/create_log_actions_component.rb | 8 ++++++++ app/controllers/lettings_logs_controller.rb | 14 ++++++++++++++ app/controllers/sales_logs_controller.rb | 14 ++++++++++++++ app/services/feature_toggle.rb | 4 ++++ config/routes.rb | 5 +++++ 7 files changed, 52 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index baaddab2e..e9af29d55 100644 --- a/Gemfile +++ b/Gemfile @@ -62,6 +62,8 @@ gem "possessive" # Strip whitespace from active record attributes gem "auto_strip_attributes" # Use sidekiq for background processing +gem "factory_bot_rails" +gem "faker" gem "method_source", "~> 1.1" gem "rails_admin", "~> 3.1" gem "ruby-openai" @@ -75,8 +77,6 @@ group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem "byebug", platforms: %i[mri mingw x64_mingw] gem "dotenv-rails" - gem "factory_bot_rails" - gem "faker" gem "pry-byebug" gem "parallel_tests" diff --git a/app/components/create_log_actions_component.html.erb b/app/components/create_log_actions_component.html.erb index 53e2bb57b..a600f3290 100644 --- a/app/components/create_log_actions_component.html.erb +++ b/app/components/create_log_actions_component.html.erb @@ -7,5 +7,10 @@ <% if user.support? %> <%= govuk_button_link_to view_uploads_button_copy, view_uploads_button_href, secondary: true %> <% end %> + + <% if FeatureToggle.create_test_logs_enabled? %> + <%= govuk_button_link_to "Create test log", create_test_log_href, secondary: true %> + <%= govuk_button_link_to "Create test log (setup only)", create_setup_test_log_href, secondary: true %> + <% end %> <% end %> diff --git a/app/components/create_log_actions_component.rb b/app/components/create_log_actions_component.rb index 4395c48a9..896bfe97e 100644 --- a/app/components/create_log_actions_component.rb +++ b/app/components/create_log_actions_component.rb @@ -34,6 +34,14 @@ class CreateLogActionsComponent < ViewComponent::Base send("bulk_upload_#{log_type}_log_path", id: "start") end + def create_test_log_href + send("create_test_#{log_type}_log_path") + end + + def create_setup_test_log_href + send("create_setup_test_#{log_type}_log_path") + end + def view_uploads_button_copy "View #{log_type} bulk uploads" end diff --git a/app/controllers/lettings_logs_controller.rb b/app/controllers/lettings_logs_controller.rb index cc3c731d5..af3a6c32f 100644 --- a/app/controllers/lettings_logs_controller.rb +++ b/app/controllers/lettings_logs_controller.rb @@ -149,6 +149,20 @@ class LettingsLogsController < LogsController end end + def create_test_log + return render_not_found unless FeatureToggle.create_test_logs_enabled? + + log = FactoryBot.create(:lettings_log, :completed, assigned_to: current_user, ppostcode_full: "SW1A 1AA") + redirect_to lettings_log_path(log) + end + + def create_setup_test_log + return render_not_found unless FeatureToggle.create_test_logs_enabled? + + log = FactoryBot.create(:lettings_log, :setup_completed, assigned_to: current_user) + redirect_to lettings_log_path(log) + end + private def session_filters diff --git a/app/controllers/sales_logs_controller.rb b/app/controllers/sales_logs_controller.rb index af9879896..8799fe528 100644 --- a/app/controllers/sales_logs_controller.rb +++ b/app/controllers/sales_logs_controller.rb @@ -119,6 +119,20 @@ class SalesLogsController < LogsController end end + def create_test_log + return render_not_found unless FeatureToggle.create_test_logs_enabled? + + log = FactoryBot.create(:sales_log, :completed, assigned_to: current_user) + redirect_to sales_log_path(log) + end + + def create_setup_test_log + return render_not_found unless FeatureToggle.create_test_logs_enabled? + + log = FactoryBot.create(:sales_log, :shared_ownership_setup_complete, assigned_to: current_user) + redirect_to sales_log_path(log) + end + private def session_filters diff --git a/app/services/feature_toggle.rb b/app/services/feature_toggle.rb index 1b67b8b37..065c3b54e 100644 --- a/app/services/feature_toggle.rb +++ b/app/services/feature_toggle.rb @@ -46,4 +46,8 @@ class FeatureToggle def self.managing_resources_enabled? !Rails.env.production? end + + def self.create_test_logs_enabled? + Rails.env.development? || Rails.env.review? + end end diff --git a/config/routes.rb b/config/routes.rb index 1c7af8c59..55d58b41b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -389,6 +389,11 @@ Rails.application.routes.draw do end end + get "create-test-lettings-log", to: "lettings_logs#create_test_log" + get "create-test-sales-log", to: "sales_logs#create_test_log" + get "create-setup-test-lettings-log", to: "lettings_logs#create_setup_test_log" + get "create-setup-test-sales-log", to: "sales_logs#create_setup_test_log" + scope via: :all do match "/404", to: "errors#not_found" match "/429", to: "errors#too_many_requests", status: 429 From e89d41dc0cfa263f619886820a82a9997c4351fd Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Mon, 25 Nov 2024 09:27:01 +0000 Subject: [PATCH 30/34] CLDC-3090 Update DPO seed (#2799) * Update dpo seed * lint --- db/seeds.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/db/seeds.rb b/db/seeds.rb index b58f7e0a8..9654b2b78 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -15,6 +15,8 @@ def create_data_protection_confirmation(user) signed_at: Time.zone.local(2019, 1, 1), data_protection_officer_email: user.email, data_protection_officer_name: user.name, + organisation_name: user.organisation.name, + organisation_address: user.organisation.address_row, ) end From f7cf3a164148e289c45b2679f9b7c76674096ecb Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Mon, 25 Nov 2024 09:27:27 +0000 Subject: [PATCH 31/34] CLDC-2890 Clear LA if it's no longer valid (#2789) * Clear LA if it's no longer valid * lint * lint --- .../derived_variables/lettings_log_variables.rb | 5 +++++ .../derived_variables/sales_log_variables.rb | 5 +++++ spec/models/lettings_log_spec.rb | 15 +++++++++++++++ spec/models/sales_log_spec.rb | 17 +++++++++++++++++ 4 files changed, 42 insertions(+) diff --git a/app/models/derived_variables/lettings_log_variables.rb b/app/models/derived_variables/lettings_log_variables.rb index 9219392f7..4692a0e6b 100644 --- a/app/models/derived_variables/lettings_log_variables.rb +++ b/app/models/derived_variables/lettings_log_variables.rb @@ -124,6 +124,11 @@ module DerivedVariables::LettingsLogVariables self.nationality_all = nationality_all_group if nationality_uk_or_prefers_not_to_say? + if startdate_changed? && !LocalAuthority.active(startdate).where(code: la).exists? + self.la = nil + self.is_la_inferred = false + end + reset_address_fields! if is_supported_housing? end diff --git a/app/models/derived_variables/sales_log_variables.rb b/app/models/derived_variables/sales_log_variables.rb index a1462454c..0c13d4fdf 100644 --- a/app/models/derived_variables/sales_log_variables.rb +++ b/app/models/derived_variables/sales_log_variables.rb @@ -75,6 +75,11 @@ module DerivedVariables::SalesLogVariables self.nationality_all = nationality_all_group if nationality_uk_or_prefers_not_to_say? self.nationality_all_buyer2 = nationality_all_buyer2_group if nationality2_uk_or_prefers_not_to_say? + if saledate_changed? && !LocalAuthority.active(saledate).where(code: la).exists? + self.la = nil + self.is_la_inferred = false + end + set_encoded_derived_values!(DEPENDENCIES) end diff --git a/spec/models/lettings_log_spec.rb b/spec/models/lettings_log_spec.rb index c47de8cf6..5702e65c8 100644 --- a/spec/models/lettings_log_spec.rb +++ b/spec/models/lettings_log_spec.rb @@ -809,6 +809,21 @@ RSpec.describe LettingsLog do expect { lettings_log.update!(nationality_all_group: nil, declaration: 1) }.not_to change(lettings_log, :nationality_all) end end + + context "when form year changes and LA is no longer active" do + before do + LocalAuthority.find_by(code: "E08000003").update!(end_date: Time.zone.today) + end + + it "removes the LA" do + lettings_log.update!(startdate: Time.zone.yesterday, la: "E08000003") + expect(lettings_log.reload.la).to eq("E08000003") + + lettings_log.update!(startdate: Time.zone.tomorrow) + expect(lettings_log.reload.la).to eq(nil) + expect(lettings_log.reload.is_la_inferred).to eq(false) + end + end end describe "optional fields" do diff --git a/spec/models/sales_log_spec.rb b/spec/models/sales_log_spec.rb index ae9b00d4c..f3dea90f9 100644 --- a/spec/models/sales_log_spec.rb +++ b/spec/models/sales_log_spec.rb @@ -978,5 +978,22 @@ RSpec.describe SalesLog, type: :model do end end end + + context "when form year changes and LA is no longer active" do + let!(:sales_log) { create(:sales_log) } + + before do + LocalAuthority.find_by(code: "E08000003").update!(end_date: Time.zone.today) + end + + it "removes the LA" do + sales_log.update!(saledate: Time.zone.yesterday, la: "E08000003") + expect(sales_log.reload.la).to eq("E08000003") + + sales_log.update!(saledate: Time.zone.tomorrow) + expect(sales_log.reload.la).to eq(nil) + expect(sales_log.reload.is_la_inferred).to eq(false) + end + end end # rubocop:enable RSpec/MessageChain From d3cd29e3241bafe8065f926cb31226eb50a90324 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:59:38 +0000 Subject: [PATCH 32/34] Remove wrong copy key (#2810) --- app/models/form/lettings/subsections/household_needs.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/form/lettings/subsections/household_needs.rb b/app/models/form/lettings/subsections/household_needs.rb index 3bfbbb336..2f6900f4f 100644 --- a/app/models/form/lettings/subsections/household_needs.rb +++ b/app/models/form/lettings/subsections/household_needs.rb @@ -2,7 +2,6 @@ class Form::Lettings::Subsections::HouseholdNeeds < ::Form::Subsection def initialize(id, hsh, section) super @id = "household_needs" - @copy_key = "lettings.household_needs.housingneeds_type" @label = "Household needs" @depends_on = [{ "non_location_setup_questions_completed?" => true }] end From b0500a297d6aa054a97f385237e9f42f3a381115 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:08:39 +0000 Subject: [PATCH 33/34] CLDC-3728 Add more merge request validations (#2814) * Add more_than_year_from_today merge date validation * Add a part of another merge validation * Disable merging if merge day is in the future --- app/controllers/merge_requests_controller.rb | 1 + app/helpers/merge_requests_helper.rb | 4 +++ app/models/merge_request_organisation.rb | 7 ++++ .../_notification_banners.html.erb | 8 +++++ app/views/merge_requests/show.html.erb | 2 +- config/locales/en.yml | 4 +++ .../merge_requests_controller_spec.rb | 34 +++++++++++++++++++ 7 files changed, 59 insertions(+), 1 deletion(-) diff --git a/app/controllers/merge_requests_controller.rb b/app/controllers/merge_requests_controller.rb index a21d42bbb..a6e2c08e5 100644 --- a/app/controllers/merge_requests_controller.rb +++ b/app/controllers/merge_requests_controller.rb @@ -143,6 +143,7 @@ private if [day, month, year].none?(&:blank?) && Date.valid_date?(year.to_i, month.to_i, day.to_i) merge_request_params["merge_date"] = Time.zone.local(year.to_i, month.to_i, day.to_i) + @merge_request.errors.add(:merge_date, :more_than_year_from_today) if Time.zone.local(year.to_i, month.to_i, day.to_i) - 1.year > Time.zone.today else @merge_request.errors.add(:merge_date, :invalid) end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 6283ef42e..28c693935 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -276,4 +276,8 @@ module MergeRequestsHelper def any_organisations_share_logs?(organisations, type) organisations.any? { |organisation| organisation.send("#{type}_logs").filter_by_managing_organisation(organisations.where.not(id: organisation.id)).exists? } end + + def begin_merge_disabled?(merge_request) + merge_request.status != "ready_to_merge" || merge_request.merge_date.future? + end end diff --git a/app/models/merge_request_organisation.rb b/app/models/merge_request_organisation.rb index 6dda8b35e..5bfbe14d7 100644 --- a/app/models/merge_request_organisation.rb +++ b/app/models/merge_request_organisation.rb @@ -29,5 +29,12 @@ private if merging_organisation_id.blank? || !Organisation.where(id: merging_organisation_id).exists? merge_request.errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_not_selected")) end + + existing_merges = MergeRequestOrganisation.with_merging_organisation(merging_organisation) + if existing_merges.count.positive? + existing_merge_request = existing_merges.first.merge_request + errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_merge")) + merge_request.errors.add(:merging_organisation, I18n.t("validations.merge_request.organisation_part_of_another_incomplete_merge", organisation: merging_organisation.name, absorbing_organisation: existing_merge_request.absorbing_organisation&.name, merge_date: existing_merge_request.merge_date&.to_fs(:govuk_date))) + end end end diff --git a/app/views/merge_requests/_notification_banners.html.erb b/app/views/merge_requests/_notification_banners.html.erb index 38c05dbcd..9e6a085ca 100644 --- a/app/views/merge_requests/_notification_banners.html.erb +++ b/app/views/merge_requests/_notification_banners.html.erb @@ -19,3 +19,11 @@ No changes have been made. Try beginning the merge again. <% end %> <% end %> + +<% if @merge_request.merge_date&.future? %> + <%= govuk_notification_banner(title_text: "Important") do %> +

      + This merge is happening in the future. Wait until the merge date to begin this merge. +

      + <% end %> +<% end %> diff --git a/app/views/merge_requests/show.html.erb b/app/views/merge_requests/show.html.erb index 0fbde7621..040cd7704 100644 --- a/app/views/merge_requests/show.html.erb +++ b/app/views/merge_requests/show.html.erb @@ -12,7 +12,7 @@

    <% unless @merge_request.status == "request_merged" || @merge_request.status == "processing" %>
    - <%= govuk_button_link_to "Begin merge", merge_start_confirmation_merge_request_path(@merge_request), disabled: @merge_request.status != "ready_to_merge" %> + <%= govuk_button_link_to "Begin merge", merge_start_confirmation_merge_request_path(@merge_request), disabled: begin_merge_disabled?(@merge_request) %> <%= govuk_button_link_to "Delete merge request", delete_confirmation_merge_request_path(@merge_request), warning: true %>
    <% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index f8bb8255b..698618717 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -183,8 +183,11 @@ en: merge_date: blank: "Enter a merge date." invalid: "Enter a valid merge date." + more_than_year_from_today: "The merge date must not be later than a year from today’s date." existing_absorbing_organisation: blank: "You must answer absorbing organisation already active?" + merging_organisation_id: + part_of_another_merge: "Another merge request records %{organisation} as merging into %{absorbing_organisation} on %{merge_date}. Select another organisation or remove this organisation from the other merge request." notification: attributes: title: @@ -370,6 +373,7 @@ en: during_deactivated_period: "The location is already deactivated during this date, please enter a different date." merge_request: organisation_part_of_another_merge: "This organisation is part of another merge - select a different one." + organisation_part_of_another_incomplete_merge: "Another merge request records %{organisation} as merging into %{absorbing_organisation} on %{merge_date}. Select another organisation or remove this organisation from the other merge request." organisation_not_selected: "Select an organisation from the search list." soft_validations: diff --git a/spec/requests/merge_requests_controller_spec.rb b/spec/requests/merge_requests_controller_spec.rb index dc1dd817d..a73db8067 100644 --- a/spec/requests/merge_requests_controller_spec.rb +++ b/spec/requests/merge_requests_controller_spec.rb @@ -84,6 +84,22 @@ RSpec.describe MergeRequestsController, type: :request do end end + context "when the user updates merge request with organisation that is already part of another merge" do + let(:another_organisation) { create(:organisation) } + let(:other_merge_request) { create(:merge_request, merge_date: Time.zone.local(2022, 5, 4)) } + let(:params) { { merge_request: { merging_organisation: another_organisation.id, new_merging_org_ids: [] } } } + + before do + MergeRequestOrganisation.create!(merge_request_id: other_merge_request.id, merging_organisation_id: another_organisation.id) + patch "/merge-request/#{merge_request.id}/merging-organisations", headers:, params: + end + + it "displays the page with an error message" do + expect(response).to have_http_status(:unprocessable_entity) + expect(page).to have_content("Another merge request records #{another_organisation.name} as merging into #{other_merge_request.absorbing_organisation&.name} on 4 May 2022. Select another organisation or remove this organisation from the other merge request.") + end + end + context "when the user selects an organisation that is a part of another merge" do let(:another_organisation) { create(:organisation) } let(:params) { { merge_request: { merging_organisation: another_organisation.id, new_merging_org_ids: [] } } } @@ -396,6 +412,24 @@ RSpec.describe MergeRequestsController, type: :request do }.from(nil).to(Time.zone.local(2022, 4, 10)) end end + + context "when merge date set to a date more than 1 year in the future" do + let(:merge_request) { MergeRequest.create!(requesting_organisation: organisation) } + let(:params) do + { merge_request: { page: "merge_date", "merge_date(3i)": (Time.zone.now.day + 1).to_s, "merge_date(2i)": Time.zone.now.month.to_s, "merge_date(1i)": (Time.zone.now.year + 1).to_s } } + end + + let(:request) do + patch "/merge-request/#{merge_request.id}", headers:, params: + end + + it "displays the page with an error message" do + request + + expect(response).to have_http_status(:unprocessable_entity) + expect(page).to have_content("The merge date must not be later than a year from today’s date.") + end + end end describe "from merging_organisations page" do From eb995e75edf7eb2f857f7e7c62589ed34542eae5 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:08:57 +0000 Subject: [PATCH 34/34] Remove outdated copy (#2815) --- app/controllers/schemes_controller.rb | 2 +- spec/features/schemes_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/schemes_controller.rb b/app/controllers/schemes_controller.rb index 3df38a237..7f246b1e2 100644 --- a/app/controllers/schemes_controller.rb +++ b/app/controllers/schemes_controller.rb @@ -152,7 +152,7 @@ class SchemesController < ApplicationController flash[:notice] = if scheme_previously_confirmed "#{@scheme.service_name} has been updated." else - "#{@scheme.service_name} has been created. It does not require helpdesk approval." + "#{@scheme.service_name} has been created." end redirect_to scheme_path(@scheme) end diff --git a/spec/features/schemes_spec.rb b/spec/features/schemes_spec.rb index 33ab00b34..7c7d9f3fb 100644 --- a/spec/features/schemes_spec.rb +++ b/spec/features/schemes_spec.rb @@ -677,7 +677,7 @@ RSpec.describe "Schemes scheme Features" do end it "adds scheme to the list of schemes" do - expect(page).to have_content "#{scheme.service_name} has been created. It does not require helpdesk approval." + expect(page).to have_content "#{scheme.service_name} has been created." click_link "Schemes" expect(page).to have_content "Supported housing schemes" expect(page).to have_content scheme.id_to_display