Browse Source

Merge branch 'main' into CLDC-2020-saledate-2-week-validation

# Conflicts:
#	db/schema.rb
pull/1446/head
natdeanlewissoftwire 3 years ago
parent
commit
c9b26bde1e
  1. 2
      app/models/form/lettings/pages/property_let_type.rb
  2. 26
      app/models/form/sales/pages/discounted_sale_value_check.rb
  3. 21
      app/models/form/sales/pages/person_student_not_child_value_check.rb
  4. 11
      app/models/form/sales/pages/previous_bedrooms.rb
  5. 11
      app/models/form/sales/pages/previous_property_type.rb
  6. 11
      app/models/form/sales/pages/previous_tenure.rb
  7. 8
      app/models/form/sales/questions/buyer_previous.rb
  8. 23
      app/models/form/sales/questions/discounted_sale_value_check.rb
  9. 11
      app/models/form/sales/questions/fromprop.rb
  10. 24
      app/models/form/sales/questions/person_student_not_child_value_check.rb
  11. 4
      app/models/form/sales/subsections/discounted_ownership_scheme.rb
  12. 18
      app/models/form/sales/subsections/household_characteristics.rb
  13. 1
      app/models/form/sales/subsections/setup.rb
  14. 31
      app/models/sales_log.rb
  15. 6
      app/models/validations/sales/household_validations.rb
  16. 11
      app/models/validations/sales/sale_information_validations.rb
  17. 20
      app/models/validations/sales/soft_validations.rb
  18. 27
      app/services/imports/sales_logs_import_service.rb
  19. 8
      config/locales/en.yml
  20. 5
      db/migrate/20230316162737_add_discounted_sale_value_check.rb
  21. 5
      db/migrate/20230320084057_add_student_not_child_value_check.rb
  22. 4
      db/schema.rb
  23. 52
      spec/models/form/sales/pages/discounted_sale_value_check_spec.rb
  24. 106
      spec/models/form/sales/pages/person_student_not_child_value_check_spec.rb
  25. 11
      spec/models/form/sales/pages/previous_bedrooms_spec.rb
  26. 11
      spec/models/form/sales/pages/previous_property_type_spec.rb
  27. 11
      spec/models/form/sales/pages/previous_tenure_spec.rb
  28. 8
      spec/models/form/sales/questions/buyer_previous_spec.rb
  29. 57
      spec/models/form/sales/questions/discounted_sale_value_check_spec.rb
  30. 11
      spec/models/form/sales/questions/fromprop_spec.rb
  31. 61
      spec/models/form/sales/questions/person_student_not_child_value_check_spec.rb
  32. 4
      spec/models/form/sales/subsections/discounted_ownership_scheme_spec.rb
  33. 36
      spec/models/form/sales/subsections/household_characteristics_spec.rb
  34. 1
      spec/models/form/sales/subsections/setup_spec.rb
  35. 4
      spec/models/form_spec.rb
  36. 8
      spec/models/sales_log_spec.rb
  37. 11
      spec/models/validations/sales/household_validations_spec.rb
  38. 200
      spec/models/validations/sales/sale_information_validations_spec.rb
  39. 183
      spec/models/validations/sales/soft_validations_spec.rb
  40. 136
      spec/services/imports/sales_logs_import_service_spec.rb

2
app/models/form/lettings/pages/property_let_type.rb

@ -2,7 +2,7 @@ class Form::Lettings::Pages::PropertyLetType < ::Form::Page
def initialize(id, hsh, subsection) def initialize(id, hsh, subsection)
super super
@id = "property_let_type" @id = "property_let_type"
@depends_on = [{ "first_time_property_let_as_social_housing" => 0, "renewal" => 0, "needstype" => 1 }] @depends_on = [{ "first_time_property_let_as_social_housing" => 0, "renewal" => 0 }]
end end
def questions def questions

26
app/models/form/sales/pages/discounted_sale_value_check.rb

@ -0,0 +1,26 @@
class Form::Sales::Pages::DiscountedSaleValueCheck < ::Form::Page
def initialize(id, hsh, subsection, person_index = nil)
super(id, hsh, subsection)
@depends_on = depends_on
@title_text = {
"translation" => "soft_validations.discounted_sale_value.title_text",
"arguments" => [{ "key" => "value_with_discount", "label" => false, "i18n_template" => "value_with_discount" }],
}
@informative_text = {
"translation" => "soft_validations.discounted_sale_value.informative_text",
"arguments" => [{ "key" => "mortgage_deposit_and_grant_total", "label" => false, "i18n_template" => "mortgage_deposit_and_grant_total" }],
}
@person_index = person_index
@depends_on = [
{
"discounted_ownership_value_invalid?" => true,
},
]
end
def questions
@questions ||= [
Form::Sales::Questions::DiscountedSaleValueCheck.new(nil, nil, self),
]
end
end

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

@ -0,0 +1,21 @@
class Form::Sales::Pages::PersonStudentNotChildValueCheck < Form::Sales::Pages::Person
def initialize(id, hsh, subsection, person_index:)
super
@depends_on = [
{
"person_#{person_index}_student_not_child?" => true,
},
]
@person_index = person_index
@title_text = {
"translation" => "soft_validations.student_not_child.title_text",
}
@informative_text = {}
end
def questions
@questions ||= [
Form::Sales::Questions::PersonStudentNotChildValueCheck.new(nil, nil, self, person_index: @person_index),
]
end
end

11
app/models/form/sales/pages/previous_bedrooms.rb

@ -3,9 +3,14 @@ class Form::Sales::Pages::PreviousBedrooms < ::Form::Page
super super
@id = "previous_bedrooms" @id = "previous_bedrooms"
@header = "About the buyers’ previous property" @header = "About the buyers’ previous property"
@depends_on = [{ @depends_on = [
"soctenant" => 1, {
}] "soctenant" => 1,
},
{
"soctenant" => 0,
},
]
end end
def questions def questions

11
app/models/form/sales/pages/previous_property_type.rb

@ -5,9 +5,14 @@ class Form::Sales::Pages::PreviousPropertyType < ::Form::Page
@header = "" @header = ""
@description = "" @description = ""
@subsection = subsection @subsection = subsection
@depends_on = [{ @depends_on = [
"soctenant" => 1, {
}] "soctenant" => 1,
},
{
"soctenant" => 0,
},
]
end end
def questions def questions

11
app/models/form/sales/pages/previous_tenure.rb

@ -5,9 +5,14 @@ class Form::Sales::Pages::PreviousTenure < ::Form::Page
@header = "" @header = ""
@description = "" @description = ""
@subsection = subsection @subsection = subsection
@depends_on = [{ @depends_on = [
"soctenant" => 1, {
}] "soctenant" => 1,
},
{
"soctenant" => 0,
},
]
end end
def questions def questions

8
app/models/form/sales/questions/buyer_previous.rb

@ -12,5 +12,13 @@ class Form::Sales::Questions::BuyerPrevious < ::Form::Question
ANSWER_OPTIONS = { ANSWER_OPTIONS = {
"1" => { "value" => "Yes" }, "1" => { "value" => "Yes" },
"2" => { "value" => "No" }, "2" => { "value" => "No" },
"0" => { "value" => "Don’t know" },
}.freeze }.freeze
def displayed_answer_options(_log, _user = nil)
{
"1" => { "value" => "Yes" },
"2" => { "value" => "No" },
}
end
end end

23
app/models/form/sales/questions/discounted_sale_value_check.rb

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

11
app/models/form/sales/questions/fromprop.rb

@ -17,5 +17,16 @@ class Form::Sales::Questions::Fromprop < ::Form::Question
"3" => { "value" => "House" }, "3" => { "value" => "House" },
"4" => { "value" => "Bungalow" }, "4" => { "value" => "Bungalow" },
"9" => { "value" => "Other" }, "9" => { "value" => "Other" },
"0" => { "value" => "Don’t know" },
}.freeze }.freeze
def displayed_answer_options(_log, _user = nil)
{
"1" => { "value" => "Flat or maisonette" },
"2" => { "value" => "Bedsit" },
"3" => { "value" => "House" },
"4" => { "value" => "Bungalow" },
"9" => { "value" => "Other" },
}
end
end end

24
app/models/form/sales/questions/person_student_not_child_value_check.rb

@ -0,0 +1,24 @@
class Form::Sales::Questions::PersonStudentNotChildValueCheck < ::Form::Question
def initialize(id, hsh, page, person_index:)
super(id, hsh, page)
@id = "student_not_child_value_check"
@check_answer_label = "Student not a child confirmation"
@type = "interruption_screen"
@answer_options = {
"0" => { "value" => "Yes" },
"1" => { "value" => "No" },
}
@hidden_in_check_answers = {
"depends_on" => [
{
"student_not_child_value_check" => 0,
},
{
"student_not_child_value_check" => 1,
},
],
}
@check_answers_card_number = person_index
@header = "Are you sure this person is not a child?"
end
end

4
app/models/form/sales/subsections/discounted_ownership_scheme.rb

@ -14,12 +14,15 @@ class Form::Sales::Subsections::DiscountedOwnershipScheme < ::Form::Subsection
Form::Sales::Pages::AboutPriceNotRtb.new(nil, nil, self), Form::Sales::Pages::AboutPriceNotRtb.new(nil, nil, self),
Form::Sales::Pages::GrantValueCheck.new(nil, nil, self), Form::Sales::Pages::GrantValueCheck.new(nil, nil, self),
Form::Sales::Pages::PurchasePriceOutrightOwnership.new("purchase_price_discounted_ownership", nil, self, ownershipsch: 2), Form::Sales::Pages::PurchasePriceOutrightOwnership.new("purchase_price_discounted_ownership", nil, self, ownershipsch: 2),
Form::Sales::Pages::DiscountedSaleValueCheck.new("discounted_sale_grant_value_check", nil, self),
Form::Sales::Pages::AboutPriceValueCheck.new("about_price_discounted_ownership_value_check", nil, self), Form::Sales::Pages::AboutPriceValueCheck.new("about_price_discounted_ownership_value_check", nil, self),
Form::Sales::Pages::DepositAndMortgageValueCheck.new("discounted_ownership_deposit_and_mortgage_value_check_after_value_and_discount", nil, self), Form::Sales::Pages::DepositAndMortgageValueCheck.new("discounted_ownership_deposit_and_mortgage_value_check_after_value_and_discount", nil, self),
Form::Sales::Pages::Mortgageused.new("mortgage_used_discounted_ownership", nil, self, ownershipsch: 2), Form::Sales::Pages::Mortgageused.new("mortgage_used_discounted_ownership", nil, self, ownershipsch: 2),
Form::Sales::Pages::MortgageValueCheck.new("discounted_ownership_mortgage_used_mortgage_value_check", nil, self), Form::Sales::Pages::MortgageValueCheck.new("discounted_ownership_mortgage_used_mortgage_value_check", nil, self),
Form::Sales::Pages::DiscountedSaleValueCheck.new("discounted_sale_mortgage_used_value_check", nil, self),
Form::Sales::Pages::MortgageAmount.new("mortgage_amount_discounted_ownership", nil, self, ownershipsch: 2), Form::Sales::Pages::MortgageAmount.new("mortgage_amount_discounted_ownership", nil, self, ownershipsch: 2),
Form::Sales::Pages::MortgageValueCheck.new("discounted_ownership_mortgage_amount_mortgage_value_check", nil, self), Form::Sales::Pages::MortgageValueCheck.new("discounted_ownership_mortgage_amount_mortgage_value_check", nil, self),
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::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::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::MortgageLender.new("mortgage_lender_discounted_ownership", nil, self, ownershipsch: 2),
@ -31,6 +34,7 @@ class Form::Sales::Subsections::DiscountedOwnershipScheme < ::Form::Subsection
Form::Sales::Pages::ExtraBorrowingValueCheck.new("extra_borrowing_deposit_value_check", nil, self), Form::Sales::Pages::ExtraBorrowingValueCheck.new("extra_borrowing_deposit_value_check", nil, self),
Form::Sales::Pages::DepositValueCheck.new("discounted_ownership_deposit_value_check", nil, self), Form::Sales::Pages::DepositValueCheck.new("discounted_ownership_deposit_value_check", nil, self),
Form::Sales::Pages::DepositAndMortgageValueCheck.new("discounted_ownership_deposit_and_mortgage_value_check_after_deposit", nil, self), Form::Sales::Pages::DepositAndMortgageValueCheck.new("discounted_ownership_deposit_and_mortgage_value_check_after_deposit", nil, self),
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::LeaseholdCharges.new("leasehold_charges_discounted_ownership", nil, self, ownershipsch: 2),
Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_discounted_ownership_value_check", nil, self), Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_discounted_ownership_value_check", nil, self),
] ]

18
app/models/form/sales/subsections/household_characteristics.rb

@ -27,58 +27,76 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Sales::Pages::Buyer1IncomeValueCheck.new("working_situation_buyer_1_income_value_check", nil, self), Form::Sales::Pages::Buyer1IncomeValueCheck.new("working_situation_buyer_1_income_value_check", nil, self),
Form::Sales::Pages::Buyer1LiveInProperty.new(nil, nil, self), Form::Sales::Pages::Buyer1LiveInProperty.new(nil, nil, self),
Form::Sales::Pages::Buyer2RelationshipToBuyer1.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::Age2.new(nil, nil, self),
Form::Sales::Pages::OldPersonsSharedOwnershipValueCheck.new("age_2_old_persons_shared_ownership_value_check", nil, self), Form::Sales::Pages::OldPersonsSharedOwnershipValueCheck.new("age_2_old_persons_shared_ownership_value_check", nil, self),
Form::Sales::Pages::RetirementValueCheck.new("age_2_buyer_retirement_value_check", nil, self, person_index: 2), Form::Sales::Pages::RetirementValueCheck.new("age_2_buyer_retirement_value_check", nil, self, person_index: 2),
Form::Sales::Pages::PersonStudentNotChildValueCheck.new("buyer_2_age_student_not_child_value_check", nil, self, person_index: 2),
Form::Sales::Pages::GenderIdentity2.new(nil, nil, self), Form::Sales::Pages::GenderIdentity2.new(nil, nil, self),
Form::Sales::Pages::RetirementValueCheck.new("gender_2_buyer_retirement_value_check", nil, self, person_index: 2), Form::Sales::Pages::RetirementValueCheck.new("gender_2_buyer_retirement_value_check", nil, self, person_index: 2),
buyer_2_ethnicity_nationality_pages, buyer_2_ethnicity_nationality_pages,
Form::Sales::Pages::Buyer2WorkingSituation.new(nil, nil, self), Form::Sales::Pages::Buyer2WorkingSituation.new(nil, nil, self),
Form::Sales::Pages::RetirementValueCheck.new("working_situation_2_retirement_value_check_joint_purchase", nil, self, person_index: 2), Form::Sales::Pages::RetirementValueCheck.new("working_situation_2_retirement_value_check_joint_purchase", nil, self, person_index: 2),
Form::Sales::Pages::Buyer2IncomeValueCheck.new("working_situation_buyer_2_income_value_check", nil, self), Form::Sales::Pages::Buyer2IncomeValueCheck.new("working_situation_buyer_2_income_value_check", nil, self),
Form::Sales::Pages::PersonStudentNotChildValueCheck.new("buyer_2_working_situation_student_not_child_value_check", nil, self, person_index: 2),
Form::Sales::Pages::Buyer2LiveInProperty.new(nil, nil, self), Form::Sales::Pages::Buyer2LiveInProperty.new(nil, nil, self),
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", 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::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::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::Sales::Pages::PersonRelationshipToBuyer1.new("person_2_relationship_to_buyer_1", nil, self, person_index: 2),
Form::Sales::Pages::PersonStudentNotChildValueCheck.new("relationship_2_student_not_child_value_check", nil, self, person_index: 2),
Form::Sales::Pages::PersonAge.new("person_2_age", nil, self, person_index: 2), Form::Sales::Pages::PersonAge.new("person_2_age", nil, self, person_index: 2),
Form::Sales::Pages::RetirementValueCheck.new("age_2_retirement_value_check", nil, self, person_index: 2), Form::Sales::Pages::RetirementValueCheck.new("age_2_retirement_value_check", nil, self, person_index: 2),
Form::Sales::Pages::PersonStudentNotChildValueCheck.new("age_2_student_not_child_value_check", nil, self, person_index: 2),
Form::Sales::Pages::PersonGenderIdentity.new("person_2_gender_identity", nil, self, person_index: 2), Form::Sales::Pages::PersonGenderIdentity.new("person_2_gender_identity", nil, self, person_index: 2),
Form::Sales::Pages::RetirementValueCheck.new("gender_2_retirement_value_check", nil, self, person_index: 2), Form::Sales::Pages::RetirementValueCheck.new("gender_2_retirement_value_check", nil, self, person_index: 2),
Form::Sales::Pages::PersonWorkingSituation.new("person_2_working_situation", nil, self, person_index: 2), Form::Sales::Pages::PersonWorkingSituation.new("person_2_working_situation", nil, self, person_index: 2),
Form::Sales::Pages::RetirementValueCheck.new("working_situation_2_retirement_value_check", nil, self, person_index: 2), Form::Sales::Pages::RetirementValueCheck.new("working_situation_2_retirement_value_check", nil, self, person_index: 2),
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::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::Sales::Pages::PersonRelationshipToBuyer1.new("person_3_relationship_to_buyer_1", nil, self, person_index: 3),
Form::Sales::Pages::PersonStudentNotChildValueCheck.new("relationship_3_student_not_child_value_check", nil, self, person_index: 3),
Form::Sales::Pages::PersonAge.new("person_3_age", nil, self, person_index: 3), Form::Sales::Pages::PersonAge.new("person_3_age", nil, self, person_index: 3),
Form::Sales::Pages::RetirementValueCheck.new("age_3_retirement_value_check", nil, self, person_index: 3), Form::Sales::Pages::RetirementValueCheck.new("age_3_retirement_value_check", nil, self, person_index: 3),
Form::Sales::Pages::PersonStudentNotChildValueCheck.new("age_3_student_not_child_value_check", nil, self, person_index: 3),
Form::Sales::Pages::PersonGenderIdentity.new("person_3_gender_identity", nil, self, person_index: 3), Form::Sales::Pages::PersonGenderIdentity.new("person_3_gender_identity", nil, self, person_index: 3),
Form::Sales::Pages::RetirementValueCheck.new("gender_3_retirement_value_check", nil, self, person_index: 3), Form::Sales::Pages::RetirementValueCheck.new("gender_3_retirement_value_check", nil, self, person_index: 3),
Form::Sales::Pages::PersonWorkingSituation.new("person_3_working_situation", nil, self, person_index: 3), Form::Sales::Pages::PersonWorkingSituation.new("person_3_working_situation", nil, self, person_index: 3),
Form::Sales::Pages::RetirementValueCheck.new("working_situation_3_retirement_value_check", nil, self, person_index: 3), Form::Sales::Pages::RetirementValueCheck.new("working_situation_3_retirement_value_check", nil, self, person_index: 3),
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::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::Sales::Pages::PersonRelationshipToBuyer1.new("person_4_relationship_to_buyer_1", nil, self, person_index: 4),
Form::Sales::Pages::PersonStudentNotChildValueCheck.new("relationship_4_student_not_child_value_check", nil, self, person_index: 4),
Form::Sales::Pages::PersonAge.new("person_4_age", nil, self, person_index: 4), Form::Sales::Pages::PersonAge.new("person_4_age", nil, self, person_index: 4),
Form::Sales::Pages::RetirementValueCheck.new("age_4_retirement_value_check", nil, self, person_index: 4), Form::Sales::Pages::RetirementValueCheck.new("age_4_retirement_value_check", nil, self, person_index: 4),
Form::Sales::Pages::PersonStudentNotChildValueCheck.new("age_4_student_not_child_value_check", nil, self, person_index: 4),
Form::Sales::Pages::PersonGenderIdentity.new("person_4_gender_identity", nil, self, person_index: 4), Form::Sales::Pages::PersonGenderIdentity.new("person_4_gender_identity", nil, self, person_index: 4),
Form::Sales::Pages::RetirementValueCheck.new("gender_4_retirement_value_check", nil, self, person_index: 4), Form::Sales::Pages::RetirementValueCheck.new("gender_4_retirement_value_check", nil, self, person_index: 4),
Form::Sales::Pages::PersonWorkingSituation.new("person_4_working_situation", nil, self, person_index: 4), Form::Sales::Pages::PersonWorkingSituation.new("person_4_working_situation", nil, self, person_index: 4),
Form::Sales::Pages::RetirementValueCheck.new("working_situation_4_retirement_value_check", nil, self, person_index: 4), Form::Sales::Pages::RetirementValueCheck.new("working_situation_4_retirement_value_check", nil, self, person_index: 4),
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::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::Sales::Pages::PersonRelationshipToBuyer1.new("person_5_relationship_to_buyer_1", nil, self, person_index: 5),
Form::Sales::Pages::PersonStudentNotChildValueCheck.new("relationship_5_student_not_child_value_check", nil, self, person_index: 5),
Form::Sales::Pages::PersonAge.new("person_5_age", nil, self, person_index: 5), Form::Sales::Pages::PersonAge.new("person_5_age", nil, self, person_index: 5),
Form::Sales::Pages::RetirementValueCheck.new("age_5_retirement_value_check", nil, self, person_index: 5), Form::Sales::Pages::RetirementValueCheck.new("age_5_retirement_value_check", nil, self, person_index: 5),
Form::Sales::Pages::PersonStudentNotChildValueCheck.new("age_5_student_not_child_value_check", nil, self, person_index: 5),
Form::Sales::Pages::PersonGenderIdentity.new("person_5_gender_identity", nil, self, person_index: 5), Form::Sales::Pages::PersonGenderIdentity.new("person_5_gender_identity", nil, self, person_index: 5),
Form::Sales::Pages::RetirementValueCheck.new("gender_5_retirement_value_check", nil, self, person_index: 5), Form::Sales::Pages::RetirementValueCheck.new("gender_5_retirement_value_check", nil, self, person_index: 5),
Form::Sales::Pages::PersonWorkingSituation.new("person_5_working_situation", nil, self, person_index: 5), Form::Sales::Pages::PersonWorkingSituation.new("person_5_working_situation", nil, self, person_index: 5),
Form::Sales::Pages::RetirementValueCheck.new("working_situation_5_retirement_value_check", nil, self, person_index: 5), Form::Sales::Pages::RetirementValueCheck.new("working_situation_5_retirement_value_check", nil, self, person_index: 5),
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::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::Sales::Pages::PersonRelationshipToBuyer1.new("person_6_relationship_to_buyer_1", nil, self, person_index: 6),
Form::Sales::Pages::PersonStudentNotChildValueCheck.new("relationship_6_student_not_child_value_check", nil, self, person_index: 6),
Form::Sales::Pages::PersonAge.new("person_6_age", nil, self, person_index: 6), Form::Sales::Pages::PersonAge.new("person_6_age", nil, self, person_index: 6),
Form::Sales::Pages::RetirementValueCheck.new("age_6_retirement_value_check", nil, self, person_index: 6), Form::Sales::Pages::RetirementValueCheck.new("age_6_retirement_value_check", nil, self, person_index: 6),
Form::Sales::Pages::PersonStudentNotChildValueCheck.new("age_6_student_not_child_value_check", nil, self, person_index: 6),
Form::Sales::Pages::PersonGenderIdentity.new("person_6_gender_identity", nil, self, person_index: 6), Form::Sales::Pages::PersonGenderIdentity.new("person_6_gender_identity", nil, self, person_index: 6),
Form::Sales::Pages::RetirementValueCheck.new("gender_6_retirement_value_check", nil, self, person_index: 6), Form::Sales::Pages::RetirementValueCheck.new("gender_6_retirement_value_check", nil, self, person_index: 6),
Form::Sales::Pages::PersonWorkingSituation.new("person_6_working_situation", nil, self, person_index: 6), Form::Sales::Pages::PersonWorkingSituation.new("person_6_working_situation", nil, self, person_index: 6),
Form::Sales::Pages::RetirementValueCheck.new("working_situation_6_retirement_value_check", nil, self, person_index: 6), Form::Sales::Pages::RetirementValueCheck.new("working_situation_6_retirement_value_check", nil, self, person_index: 6),
Form::Sales::Pages::PersonStudentNotChildValueCheck.new("working_situation_6_student_not_child_value_check", nil, self, person_index: 6),
].flatten.compact ].flatten.compact
end end

1
app/models/form/sales/subsections/setup.rb

@ -18,6 +18,7 @@ class Form::Sales::Subsections::Setup < ::Form::Subsection
Form::Sales::Pages::OutrightOwnershipType.new(nil, nil, self), Form::Sales::Pages::OutrightOwnershipType.new(nil, nil, self),
Form::Sales::Pages::OldPersonsSharedOwnershipValueCheck.new("ownership_type_old_persons_shared_ownership_value_check", nil, self), Form::Sales::Pages::OldPersonsSharedOwnershipValueCheck.new("ownership_type_old_persons_shared_ownership_value_check", nil, self),
Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_type_value_check", nil, self), Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_type_value_check", nil, self),
Form::Sales::Pages::DiscountedSaleValueCheck.new("discounted_sale_type_value_check", nil, self),
Form::Sales::Pages::BuyerCompany.new(nil, nil, self), Form::Sales::Pages::BuyerCompany.new(nil, nil, self),
Form::Sales::Pages::BuyerLive.new(nil, nil, self), Form::Sales::Pages::BuyerLive.new(nil, nil, self),
Form::Sales::Pages::JointPurchase.new(nil, nil, self), Form::Sales::Pages::JointPurchase.new(nil, nil, self),

31
app/models/sales_log.rb

@ -42,7 +42,7 @@ class SalesLog < Log
} }
scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org) } scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org) }
OPTIONAL_FIELDS = %w[saledate_check purchid monthly_charges_value_check old_persons_shared_ownership_value_check].freeze OPTIONAL_FIELDS = %w[saledate_check purchid monthly_charges_value_check old_persons_shared_ownership_value_check mortgagelender othtype discounted_sale_value_check].freeze
RETIREMENT_AGES = { "M" => 65, "F" => 60, "X" => 65 }.freeze RETIREMENT_AGES = { "M" => 65, "F" => 60, "X" => 65 }.freeze
def lettings? def lettings?
@ -78,6 +78,8 @@ class SalesLog < Log
def dynamically_not_required def dynamically_not_required
not_required = [] not_required = []
not_required << "proplen" if proplen_optional? not_required << "proplen" if proplen_optional?
not_required << "mortlen" if mortlen_optional?
not_required << "frombeds" if frombeds_optional?
not_required |= %w[address_line2 county postcode_full] if saledate && saledate.year >= 2023 not_required |= %w[address_line2 county postcode_full] if saledate && saledate.year >= 2023
@ -90,6 +92,18 @@ class SalesLog < Log
collection_start_year < 2023 collection_start_year < 2023
end end
def mortlen_optional?
return false unless collection_start_year
collection_start_year < 2023
end
def frombeds_optional?
return false unless collection_start_year
collection_start_year < 2023
end
def not_started? def not_started?
status == "not_started" status == "not_started"
end end
@ -316,4 +330,19 @@ class SalesLog < Log
def should_process_uprn_change? def should_process_uprn_change?
uprn_changed? && saledate && saledate.year >= 2023 uprn_changed? && saledate && saledate.year >= 2023
end end
def value_with_discount
return if value.blank?
discount_amount = discount ? value * discount / 100 : 0
value - discount_amount
end
def mortgage_deposit_and_grant_total
return if deposit.blank?
grant_amount = grant || 0
mortgage_amount = mortgage || 0
mortgage_amount + deposit + grant_amount
end
end end

6
app/models/validations/sales/household_validations.rb

@ -45,12 +45,6 @@ private
student = person_is_fulltime_student?(economic_status) student = person_is_fulltime_student?(economic_status)
child = person_is_child?(relationship) child = person_is_child?(relationship)
if age_between_16_19 && student && !child
record.errors.add "age#{person_num}", I18n.t("validations.household.age.student_16_19.cannot_be_16_19.student_not_child")
record.errors.add "ecstat#{person_num}", I18n.t("validations.household.ecstat.student_16_19.cannot_be_student.16_19_not_child")
record.errors.add "relat#{person_num}", I18n.t("validations.household.relat.student_16_19.must_be_child")
end
if age_between_16_19 && !student && child if age_between_16_19 && !student && child
record.errors.add "age#{person_num}", I18n.t("validations.household.age.student_16_19.cannot_be_16_19.child_not_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 "ecstat#{person_num}", I18n.t("validations.household.ecstat.student_16_19.must_be_student") record.errors.add "ecstat#{person_num}", I18n.t("validations.household.ecstat.student_16_19.must_be_student")

11
app/models/validations/sales/sale_information_validations.rb

@ -1,4 +1,6 @@
module Validations::Sales::SaleInformationValidations module Validations::Sales::SaleInformationValidations
include CollectionTimeHelper
def validate_practical_completion_date_before_saledate(record) def validate_practical_completion_date_before_saledate(record)
return if record.saledate.blank? || record.hodate.blank? return if record.saledate.blank? || record.hodate.blank?
@ -45,17 +47,14 @@ module Validations::Sales::SaleInformationValidations
end end
def validate_discounted_ownership_value(record) def validate_discounted_ownership_value(record)
return unless record.saledate && collection_start_year(record.saledate) >= 2024
return unless record.value && record.deposit && record.ownershipsch return unless record.value && record.deposit && record.ownershipsch
return unless record.mortgage || record.mortgageused == 2 return unless record.mortgage || record.mortgageused == 2
return unless record.discount || record.grant || record.type == 29 return unless record.discount || record.grant || record.type == 29
discount_amount = record.discount ? record.value * record.discount / 100 : 0 if record.mortgage_deposit_and_grant_total != record.value_with_discount && record.discounted_ownership_sale?
grant_amount = record.grant || 0
mortgage_amount = record.mortgage || 0
value_with_discount = (record.value - discount_amount)
if mortgage_amount + record.deposit + grant_amount != value_with_discount && record.discounted_ownership_sale?
%i[mortgage deposit grant value discount ownershipsch].each do |field| %i[mortgage deposit grant value discount ownershipsch].each do |field|
record.errors.add field, I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: sprintf("%.2f", value_with_discount)) record.errors.add field, I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: sprintf("%.2f", record.value_with_discount))
end end
end end
end end

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

@ -103,6 +103,26 @@ module Validations::Sales::SoftValidations
mscharge > soft_max mscharge > soft_max
end end
(2..6).each do |person_num|
define_method("person_#{person_num}_student_not_child?") do
relat = send("relat#{person_num}")
ecstat = send("ecstat#{person_num}")
age = send("age#{person_num}")
return unless age && ecstat && relat
age.between?(16, 19) && ecstat == 7 && relat != "C"
end
end
def discounted_ownership_value_invalid?
return unless saledate && collection_start_year <= 2023
return unless value && deposit && ownershipsch
return unless mortgage || mortgageused == 2
return unless discount || grant || type == 29
mortgage_deposit_and_grant_total != value_with_discount && discounted_ownership_sale?
end
private private
def sale_range def sale_range

27
app/services/imports/sales_logs_import_service.rb

@ -38,7 +38,7 @@ module Imports
attributes["ownershipsch"] = ownership_from_type(attributes) if attributes["ownershipsch"].blank? # sometimes Ownership is missing, but type is set attributes["ownershipsch"] = ownership_from_type(attributes) if attributes["ownershipsch"].blank? # sometimes Ownership is missing, but type is set
attributes["othtype"] = string_or_nil(xml_doc, "Q38OtherSale") attributes["othtype"] = string_or_nil(xml_doc, "Q38OtherSale")
attributes["jointpur"] = unsafe_string_as_integer(xml_doc, "joint") attributes["jointpur"] = unsafe_string_as_integer(xml_doc, "joint")
attributes["jointmore"] = unsafe_string_as_integer(xml_doc, "JointMore") if attributes["jointpur"] == 1 attributes["jointmore"] = unsafe_string_as_integer(xml_doc, "JointMore") || 3 if attributes["jointpur"] == 1
attributes["beds"] = safe_string_as_integer(xml_doc, "Q11Bedrooms") attributes["beds"] = safe_string_as_integer(xml_doc, "Q11Bedrooms")
attributes["companybuy"] = unsafe_string_as_integer(xml_doc, "company") if attributes["ownershipsch"] == 3 attributes["companybuy"] = unsafe_string_as_integer(xml_doc, "company") if attributes["ownershipsch"] == 3
attributes["hholdcount"] = other_household_members(xml_doc, attributes) attributes["hholdcount"] = other_household_members(xml_doc, attributes)
@ -124,7 +124,7 @@ module Imports
attributes["mortgagelenderother"] = mortgage_lender_other(xml_doc, attributes) attributes["mortgagelenderother"] = mortgage_lender_other(xml_doc, attributes)
attributes["postcode_full"] = parse_postcode(string_or_nil(xml_doc, "Q14Postcode")) attributes["postcode_full"] = parse_postcode(string_or_nil(xml_doc, "Q14Postcode"))
attributes["pcodenk"] = 0 if attributes["postcode_full"].present? # known if given attributes["pcodenk"] = 0 if attributes["postcode_full"].present? # known if given
attributes["soctenant"] = soctenant(attributes) attributes["soctenant"] = 0 if attributes["ownershipsch"] == 1
attributes["ethnic_group2"] = nil # 23/24 variable attributes["ethnic_group2"] = nil # 23/24 variable
attributes["ethnicbuy2"] = nil # 23/24 variable attributes["ethnicbuy2"] = nil # 23/24 variable
attributes["prevshared"] = nil # 23/24 variable attributes["prevshared"] = nil # 23/24 variable
@ -152,6 +152,8 @@ module Imports
attributes["old_persons_shared_ownership_value_check"] = 0 attributes["old_persons_shared_ownership_value_check"] = 0
attributes["income2_value_check"] = 0 attributes["income2_value_check"] = 0
attributes["monthly_charges_value_check"] = 0 attributes["monthly_charges_value_check"] = 0
attributes["student_not_child_value_check"] = 0
attributes["discounted_sale_value_check"] = 0
# Sets the log creator # Sets the log creator
owner_id = meta_field_value(xml_doc, "owner-user-id").strip owner_id = meta_field_value(xml_doc, "owner-user-id").strip
@ -189,6 +191,8 @@ module Imports
sales_log.errors.each do |error| sales_log.errors.each do |error|
@logger.warn("Log #{sales_log.old_id}: Removing field #{error.attribute} from log triggering validation: #{error.type}") @logger.warn("Log #{sales_log.old_id}: Removing field #{error.attribute} from log triggering validation: #{error.type}")
attributes.delete(error.attribute.to_s) attributes.delete(error.attribute.to_s)
attributes.delete("pcodenk") if error.attribute == :postcode_full
attributes.delete("ppcodenk") if error.attribute == :ppostcode_full
end end
@logs_overridden << sales_log.old_id @logs_overridden << sales_log.old_id
save_sales_log(attributes, previous_status) save_sales_log(attributes, previous_status)
@ -253,7 +257,9 @@ module Imports
staircase_bought_value_check staircase_bought_value_check
monthly_charges_value_check monthly_charges_value_check
hodate_check hodate_check
saledate_check] saledate_check
student_not_child_value_check
discounted_sale_value_check]
end end
def check_status_completed(sales_log, previous_status) def check_status_completed(sales_log, previous_status)
@ -375,17 +381,6 @@ module Imports
end end
end end
def soctenant(attributes)
return nil unless attributes["ownershipsch"] == 1
if attributes["frombeds"].blank? && attributes["fromprop"].blank? && attributes["socprevten"].blank?
2
else
1
end
# NO (2) if FROMBEDS, FROMPROP and socprevten are blank, and YES(1) if they are completed
end
def still_serving(xml_doc) def still_serving(xml_doc)
case unsafe_string_as_integer(xml_doc, "LeftArmedF") case unsafe_string_as_integer(xml_doc, "LeftArmedF")
when 4 when 4
@ -514,13 +509,15 @@ module Imports
attributes["hb"] ||= 4 attributes["hb"] ||= 4
attributes["prevown"] ||= 3 attributes["prevown"] ||= 3
attributes["savingsnk"] ||= attributes["savings"].present? ? 0 : 1 attributes["savingsnk"] ||= attributes["savings"].present? ? 0 : 1
attributes["jointmore"] ||= 3 if attributes["jointpur"] == 1
attributes["inc1mort"] ||= 3 attributes["inc1mort"] ||= 3
if [attributes["pregyrha"], attributes["pregla"], attributes["pregghb"], attributes["pregother"]].all?(&:blank?) if [attributes["pregyrha"], attributes["pregla"], attributes["pregghb"], attributes["pregother"]].all?(&:blank?)
attributes["pregblank"] = 1 attributes["pregblank"] = 1
end end
attributes["pcodenk"] ||= 1 attributes["pcodenk"] ||= 1
attributes["prevten"] ||= 0 attributes["prevten"] ||= 0
attributes["extrabor"] ||= 3 if attributes["mortgageused"] == 1
attributes["socprevten"] ||= 10 if attributes["ownershipsch"] == 1
attributes["fromprop"] ||= 0 if attributes["ownershipsch"] == 1
# buyer 1 characteristics # buyer 1 characteristics
attributes["age1_known"] ||= 1 attributes["age1_known"] ||= 1

8
config/locales/en.yml

@ -345,7 +345,6 @@ en:
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" 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: student_16_19:
cannot_be_16_19: cannot_be_16_19:
student_not_child: "Person cannot be aged 16-19 if they are a student but don't have relationship ‘child’"
child_not_student: "Person cannot be aged 16-19 if they have relationship ‘child’ but are not a student" 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’" must_be_16_19: "Person must be aged 16-19 if they are a student and have relationship ‘child’"
partner_under_16: "Cannot be under 16 if the relationship is partner" partner_under_16: "Cannot be under 16 if the relationship is partner"
@ -359,7 +358,6 @@ en:
student_16_19: student_16_19:
cannot_be_student: cannot_be_student:
child_not_16_19: "Person cannot be a student if they are not aged 16-19 but have relationship ‘child’" child_not_16_19: "Person cannot be a student if they are not aged 16-19 but have relationship ‘child’"
16_19_not_child: "Person cannot be a student if they are aged 16-19 but don‘t have relationship ‘child’"
must_be_student: "Person must be a student if they are aged 16-19 and 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_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" retired_female: "Answer cannot be ‘retired’ as the female tenant is under 60"
@ -373,7 +371,6 @@ en:
cannot_be_child: cannot_be_child:
student_not_16_19: "Answer cannot be ‘child’ if the person is a student but not aged 16-19" 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" 16_19_not_student: "Answer cannot be ‘child’ if the person is aged 16-19 but not a student"
must_be_child: "Answer must be ‘child’ if the person is aged 16-19 and a student"
housingneeds_a: 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’" 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_type: housingneeds_type:
@ -527,6 +524,11 @@ en:
staircase_bought_seems_high: "You said %{percentage}% was bought in this staircasing transaction, which seems high. Are you sure?" staircase_bought_seems_high: "You said %{percentage}% was bought in this staircasing transaction, which seems high. Are you sure?"
monthly_charges_over_soft_max: monthly_charges_over_soft_max:
title_text: "The amount of monthly charges is high for this type of property and sale type" title_text: "The amount of monthly charges is high for this type of property and sale type"
student_not_child:
title_text: "You told us this person is a student aged beween 16 and 19"
discounted_sale_value:
title_text: "Mortgage, deposit, and grant total must equal £%{value_with_discount}"
informative_text: "Your given mortgage, deposit and grant total is £%{mortgage_deposit_and_grant_total}"
devise: devise:
two_factor_authentication: two_factor_authentication:

5
db/migrate/20230316162737_add_discounted_sale_value_check.rb

@ -0,0 +1,5 @@
class AddDiscountedSaleValueCheck < ActiveRecord::Migration[7.0]
def change
add_column :sales_logs, :discounted_sale_value_check, :integer
end
end

5
db/migrate/20230320084057_add_student_not_child_value_check.rb

@ -0,0 +1,5 @@
class AddStudentNotChildValueCheck < ActiveRecord::Migration[7.0]
def change
add_column :sales_logs, :student_not_child_value_check, :integer
end
end

4
db/schema.rb

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_03_09_145740) do ActiveRecord::Schema[7.0].define(version: 2023_03_20_084057) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -568,6 +568,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_09_145740) do
t.string "town_or_city" t.string "town_or_city"
t.string "county" t.string "county"
t.integer "nationalbuy2" t.integer "nationalbuy2"
t.integer "student_not_child_value_check"
t.integer "discounted_sale_value_check"
t.index ["bulk_upload_id"], name: "index_sales_logs_on_bulk_upload_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" t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id"
t.index ["old_id"], name: "index_sales_logs_on_old_id", unique: true t.index ["old_id"], name: "index_sales_logs_on_old_id", unique: true

52
spec/models/form/sales/pages/discounted_sale_value_check_spec.rb

@ -0,0 +1,52 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::DiscountedSaleValueCheck, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection, index) }
let(:page_id) { "discounted_sale_value_check" }
let(:page_definition) { nil }
let(:index) { 1 }
let(:subsection) { instance_double(Form::Subsection) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[discounted_sale_value_check])
end
it "has the correct id" do
expect(page.id).to eq("discounted_sale_value_check")
end
it "has the correct header" do
expect(page.header).to be_nil
end
it "has the correct title_text" do
expect(page.title_text).to eq({
"translation" => "soft_validations.discounted_sale_value.title_text",
"arguments" => [{ "key" => "value_with_discount", "label" => false, "i18n_template" => "value_with_discount" }],
})
end
it "has the correct informative_text" do
expect(page.informative_text).to eq({
"translation" => "soft_validations.discounted_sale_value.informative_text",
"arguments" => [{ "key" => "mortgage_deposit_and_grant_total", "label" => false, "i18n_template" => "mortgage_deposit_and_grant_total" }],
})
end
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
it "has correct depends_on" do
expect(page.depends_on).to eq([
{
"discounted_ownership_value_invalid?" => true,
},
])
end
end

106
spec/models/form/sales/pages/person_student_not_child_value_check_spec.rb

@ -0,0 +1,106 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::PersonStudentNotChildValueCheck, 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) }
let(:person_index) { 2 }
let(:page_id) { "person_2_student_not_child_value_check" }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has the correct header" do
expect(page.header).to be_nil
end
it "has the correct description" do
expect(page.description).to be_nil
end
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
it "has correct title_text" do
expect(page.title_text).to eq({
"translation" => "soft_validations.student_not_child.title_text",
})
end
it "has correct informative_text" do
expect(page.informative_text).to eq({})
end
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[student_not_child_value_check])
end
context "with person 2" do
let(:person_index) { 2 }
let(:page_id) { "person_2_student_not_child_value_check" }
it "has the correct id" do
expect(page.id).to eq(page_id)
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "person_2_student_not_child?" => true }])
end
end
context "with person 3" do
let(:person_index) { 3 }
let(:page_id) { "person_3_student_not_child_value_check" }
it "has the correct id" do
expect(page.id).to eq(page_id)
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "person_3_student_not_child?" => true }])
end
end
context "with person 4" do
let(:person_index) { 4 }
let(:page_id) { "person_4_student_not_child_value_check" }
it "has the correct id" do
expect(page.id).to eq(page_id)
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "person_4_student_not_child?" => true }])
end
end
context "with person 5" do
let(:person_index) { 5 }
let(:page_id) { "person_5_student_not_child_value_check" }
it "has the correct id" do
expect(page.id).to eq(page_id)
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "person_5_student_not_child?" => true }])
end
end
context "with person 6" do
let(:person_index) { 6 }
let(:page_id) { "person_6_student_not_child_value_check" }
it "has the correct id" do
expect(page.id).to eq(page_id)
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "person_6_student_not_child?" => true }])
end
end
end

11
spec/models/form/sales/pages/previous_bedrooms_spec.rb

@ -28,8 +28,13 @@ RSpec.describe Form::Sales::Pages::PreviousBedrooms, type: :model do
end end
it "has correct depends_on" do it "has correct depends_on" do
expect(page.depends_on).to eq([{ expect(page.depends_on).to eq([
"soctenant" => 1, {
}]) "soctenant" => 1,
},
{
"soctenant" => 0,
},
])
end end
end end

11
spec/models/form/sales/pages/previous_property_type_spec.rb

@ -28,8 +28,13 @@ RSpec.describe Form::Sales::Pages::PreviousPropertyType, type: :model do
end end
it "has correct depends_on" do it "has correct depends_on" do
expect(page.depends_on).to eq([{ expect(page.depends_on).to eq([
"soctenant" => 1, {
}]) "soctenant" => 1,
},
{
"soctenant" => 0,
},
])
end end
end end

11
spec/models/form/sales/pages/previous_tenure_spec.rb

@ -28,8 +28,13 @@ RSpec.describe Form::Sales::Pages::PreviousTenure, type: :model do
end end
it "has correct depends_on" do it "has correct depends_on" do
expect(page.depends_on).to eq([{ expect(page.depends_on).to eq([
"soctenant" => 1, {
}]) "soctenant" => 1,
},
{
"soctenant" => 0,
},
])
end end
end end

8
spec/models/form/sales/questions/buyer_previous_spec.rb

@ -46,10 +46,18 @@ RSpec.describe Form::Sales::Questions::BuyerPrevious, type: :model do
expect(question.derived?).to be false expect(question.derived?).to be false
end end
it "has the correct displayed_answer_options" do
expect(question.displayed_answer_options(nil)).to eq({
"1" => { "value" => "Yes" },
"2" => { "value" => "No" },
})
end
it "has the correct answer_options" do it "has the correct answer_options" do
expect(question.answer_options).to eq({ expect(question.answer_options).to eq({
"1" => { "value" => "Yes" }, "1" => { "value" => "Yes" },
"2" => { "value" => "No" }, "2" => { "value" => "No" },
"0" => { "value" => "Don’t know" },
}) })
end end

57
spec/models/form/sales/questions/discounted_sale_value_check_spec.rb

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

11
spec/models/form/sales/questions/fromprop_spec.rb

@ -35,6 +35,16 @@ RSpec.describe Form::Sales::Questions::Fromprop, type: :model do
expect(question.hint_text).to eq("") expect(question.hint_text).to eq("")
end end
it "has the correct displayed_answer_options" do
expect(question.displayed_answer_options(nil)).to eq({
"1" => { "value" => "Flat or maisonette" },
"2" => { "value" => "Bedsit" },
"3" => { "value" => "House" },
"4" => { "value" => "Bungalow" },
"9" => { "value" => "Other" },
})
end
it "has the correct answer_options" do it "has the correct answer_options" do
expect(question.answer_options).to eq({ expect(question.answer_options).to eq({
"1" => { "value" => "Flat or maisonette" }, "1" => { "value" => "Flat or maisonette" },
@ -42,6 +52,7 @@ RSpec.describe Form::Sales::Questions::Fromprop, type: :model do
"3" => { "value" => "House" }, "3" => { "value" => "House" },
"4" => { "value" => "Bungalow" }, "4" => { "value" => "Bungalow" },
"9" => { "value" => "Other" }, "9" => { "value" => "Other" },
"0" => { "value" => "Don’t know" },
}) })
end end
end end

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

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

4
spec/models/form/sales/subsections/discounted_ownership_scheme_spec.rb

@ -20,12 +20,15 @@ RSpec.describe Form::Sales::Subsections::DiscountedOwnershipScheme, type: :model
about_price_not_rtb about_price_not_rtb
grant_value_check grant_value_check
purchase_price_discounted_ownership purchase_price_discounted_ownership
discounted_sale_grant_value_check
about_price_discounted_ownership_value_check about_price_discounted_ownership_value_check
discounted_ownership_deposit_and_mortgage_value_check_after_value_and_discount discounted_ownership_deposit_and_mortgage_value_check_after_value_and_discount
mortgage_used_discounted_ownership mortgage_used_discounted_ownership
discounted_ownership_mortgage_used_mortgage_value_check discounted_ownership_mortgage_used_mortgage_value_check
discounted_sale_mortgage_used_value_check
mortgage_amount_discounted_ownership mortgage_amount_discounted_ownership
discounted_ownership_mortgage_amount_mortgage_value_check discounted_ownership_mortgage_amount_mortgage_value_check
discounted_sale_mortgage_value_check
extra_borrowing_mortgage_value_check extra_borrowing_mortgage_value_check
discounted_ownership_deposit_and_mortgage_value_check_after_mortgage discounted_ownership_deposit_and_mortgage_value_check_after_mortgage
mortgage_lender_discounted_ownership mortgage_lender_discounted_ownership
@ -37,6 +40,7 @@ RSpec.describe Form::Sales::Subsections::DiscountedOwnershipScheme, type: :model
extra_borrowing_deposit_value_check extra_borrowing_deposit_value_check
discounted_ownership_deposit_value_check discounted_ownership_deposit_value_check
discounted_ownership_deposit_and_mortgage_value_check_after_deposit discounted_ownership_deposit_and_mortgage_value_check_after_deposit
discounted_sale_deposit_value_check
leasehold_charges_discounted_ownership leasehold_charges_discounted_ownership
monthly_charges_discounted_ownership_value_check monthly_charges_discounted_ownership_value_check
], ],

36
spec/models/form/sales/subsections/household_characteristics_spec.rb

@ -40,57 +40,75 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
working_situation_buyer_1_income_value_check working_situation_buyer_1_income_value_check
buyer_1_live_in_property buyer_1_live_in_property
buyer_2_relationship_to_buyer_1 buyer_2_relationship_to_buyer_1
buyer_2_relationship_student_not_child_value_check
buyer_2_age buyer_2_age
age_2_old_persons_shared_ownership_value_check age_2_old_persons_shared_ownership_value_check
age_2_buyer_retirement_value_check age_2_buyer_retirement_value_check
buyer_2_age_student_not_child_value_check
buyer_2_gender_identity buyer_2_gender_identity
gender_2_buyer_retirement_value_check gender_2_buyer_retirement_value_check
buyer_2_working_situation buyer_2_working_situation
working_situation_2_retirement_value_check_joint_purchase working_situation_2_retirement_value_check_joint_purchase
working_situation_buyer_2_income_value_check working_situation_buyer_2_income_value_check
buyer_2_working_situation_student_not_child_value_check
buyer_2_live_in_property buyer_2_live_in_property
number_of_others_in_property number_of_others_in_property
number_of_others_in_property_joint_purchase number_of_others_in_property_joint_purchase
person_2_known person_2_known
person_2_relationship_to_buyer_1 person_2_relationship_to_buyer_1
relationship_2_student_not_child_value_check
person_2_age person_2_age
age_2_retirement_value_check age_2_retirement_value_check
age_2_student_not_child_value_check
person_2_gender_identity person_2_gender_identity
gender_2_retirement_value_check gender_2_retirement_value_check
person_2_working_situation person_2_working_situation
working_situation_2_retirement_value_check working_situation_2_retirement_value_check
working_situation_2_student_not_child_value_check
person_3_known person_3_known
person_3_relationship_to_buyer_1 person_3_relationship_to_buyer_1
relationship_3_student_not_child_value_check
person_3_age person_3_age
age_3_retirement_value_check age_3_retirement_value_check
age_3_student_not_child_value_check
person_3_gender_identity person_3_gender_identity
gender_3_retirement_value_check gender_3_retirement_value_check
person_3_working_situation person_3_working_situation
working_situation_3_retirement_value_check working_situation_3_retirement_value_check
working_situation_3_student_not_child_value_check
person_4_known person_4_known
person_4_relationship_to_buyer_1 person_4_relationship_to_buyer_1
relationship_4_student_not_child_value_check
person_4_age person_4_age
age_4_retirement_value_check age_4_retirement_value_check
age_4_student_not_child_value_check
person_4_gender_identity person_4_gender_identity
gender_4_retirement_value_check gender_4_retirement_value_check
person_4_working_situation person_4_working_situation
working_situation_4_retirement_value_check working_situation_4_retirement_value_check
working_situation_4_student_not_child_value_check
person_5_known person_5_known
person_5_relationship_to_buyer_1 person_5_relationship_to_buyer_1
relationship_5_student_not_child_value_check
person_5_age person_5_age
age_5_retirement_value_check age_5_retirement_value_check
age_5_student_not_child_value_check
person_5_gender_identity person_5_gender_identity
gender_5_retirement_value_check gender_5_retirement_value_check
person_5_working_situation person_5_working_situation
working_situation_5_retirement_value_check working_situation_5_retirement_value_check
working_situation_5_student_not_child_value_check
person_6_known person_6_known
person_6_relationship_to_buyer_1 person_6_relationship_to_buyer_1
relationship_6_student_not_child_value_check
person_6_age person_6_age
age_6_retirement_value_check age_6_retirement_value_check
age_6_student_not_child_value_check
person_6_gender_identity person_6_gender_identity
gender_6_retirement_value_check gender_6_retirement_value_check
person_6_working_situation person_6_working_situation
working_situation_6_retirement_value_check working_situation_6_retirement_value_check
working_situation_6_student_not_child_value_check
], ],
) )
end end
@ -124,9 +142,11 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
working_situation_buyer_1_income_value_check working_situation_buyer_1_income_value_check
buyer_1_live_in_property buyer_1_live_in_property
buyer_2_relationship_to_buyer_1 buyer_2_relationship_to_buyer_1
buyer_2_relationship_student_not_child_value_check
buyer_2_age buyer_2_age
age_2_old_persons_shared_ownership_value_check age_2_old_persons_shared_ownership_value_check
age_2_buyer_retirement_value_check age_2_buyer_retirement_value_check
buyer_2_age_student_not_child_value_check
buyer_2_gender_identity buyer_2_gender_identity
gender_2_buyer_retirement_value_check gender_2_buyer_retirement_value_check
buyer_2_ethnic_group buyer_2_ethnic_group
@ -139,49 +159,65 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
buyer_2_working_situation buyer_2_working_situation
working_situation_2_retirement_value_check_joint_purchase working_situation_2_retirement_value_check_joint_purchase
working_situation_buyer_2_income_value_check working_situation_buyer_2_income_value_check
buyer_2_working_situation_student_not_child_value_check
buyer_2_live_in_property buyer_2_live_in_property
number_of_others_in_property number_of_others_in_property
number_of_others_in_property_joint_purchase number_of_others_in_property_joint_purchase
person_2_known person_2_known
person_2_relationship_to_buyer_1 person_2_relationship_to_buyer_1
relationship_2_student_not_child_value_check
person_2_age person_2_age
age_2_retirement_value_check age_2_retirement_value_check
age_2_student_not_child_value_check
person_2_gender_identity person_2_gender_identity
gender_2_retirement_value_check gender_2_retirement_value_check
person_2_working_situation person_2_working_situation
working_situation_2_retirement_value_check working_situation_2_retirement_value_check
working_situation_2_student_not_child_value_check
person_3_known person_3_known
person_3_relationship_to_buyer_1 person_3_relationship_to_buyer_1
relationship_3_student_not_child_value_check
person_3_age person_3_age
age_3_retirement_value_check age_3_retirement_value_check
age_3_student_not_child_value_check
person_3_gender_identity person_3_gender_identity
gender_3_retirement_value_check gender_3_retirement_value_check
person_3_working_situation person_3_working_situation
working_situation_3_retirement_value_check working_situation_3_retirement_value_check
working_situation_3_student_not_child_value_check
person_4_known person_4_known
person_4_relationship_to_buyer_1 person_4_relationship_to_buyer_1
relationship_4_student_not_child_value_check
person_4_age person_4_age
age_4_retirement_value_check age_4_retirement_value_check
age_4_student_not_child_value_check
person_4_gender_identity person_4_gender_identity
gender_4_retirement_value_check gender_4_retirement_value_check
person_4_working_situation person_4_working_situation
working_situation_4_retirement_value_check working_situation_4_retirement_value_check
working_situation_4_student_not_child_value_check
person_5_known person_5_known
person_5_relationship_to_buyer_1 person_5_relationship_to_buyer_1
relationship_5_student_not_child_value_check
person_5_age person_5_age
age_5_retirement_value_check age_5_retirement_value_check
age_5_student_not_child_value_check
person_5_gender_identity person_5_gender_identity
gender_5_retirement_value_check gender_5_retirement_value_check
person_5_working_situation person_5_working_situation
working_situation_5_retirement_value_check working_situation_5_retirement_value_check
working_situation_5_student_not_child_value_check
person_6_known person_6_known
person_6_relationship_to_buyer_1 person_6_relationship_to_buyer_1
relationship_6_student_not_child_value_check
person_6_age person_6_age
age_6_retirement_value_check age_6_retirement_value_check
age_6_student_not_child_value_check
person_6_gender_identity person_6_gender_identity
gender_6_retirement_value_check gender_6_retirement_value_check
person_6_working_situation person_6_working_situation
working_situation_6_retirement_value_check working_situation_6_retirement_value_check
working_situation_6_student_not_child_value_check
], ],
) )
end end

1
spec/models/form/sales/subsections/setup_spec.rb

@ -25,6 +25,7 @@ RSpec.describe Form::Sales::Subsections::Setup, type: :model do
outright_ownership_type outright_ownership_type
ownership_type_old_persons_shared_ownership_value_check ownership_type_old_persons_shared_ownership_value_check
monthly_charges_type_value_check monthly_charges_type_value_check
discounted_sale_type_value_check
buyer_company buyer_company
buyer_live buyer_live
joint_purchase joint_purchase

4
spec/models/form_spec.rb

@ -227,9 +227,9 @@ RSpec.describe Form, type: :model do
expect(form.sections[0].class).to eq(Form::Sales::Sections::Setup) expect(form.sections[0].class).to eq(Form::Sales::Sections::Setup)
expect(form.subsections.count).to eq(1) expect(form.subsections.count).to eq(1)
expect(form.subsections.first.id).to eq("setup") expect(form.subsections.first.id).to eq("setup")
expect(form.pages.count).to eq(15) expect(form.pages.count).to eq(16)
expect(form.pages.first.id).to eq("organisation") expect(form.pages.first.id).to eq("organisation")
expect(form.questions.count).to eq(16) expect(form.questions.count).to eq(17)
expect(form.questions.first.id).to eq("owning_organisation_id") expect(form.questions.first.id).to eq("owning_organisation_id")
expect(form.start_date).to eq(Time.zone.parse("2022-04-01")) expect(form.start_date).to eq(Time.zone.parse("2022-04-01"))
expect(form.end_date).to eq(Time.zone.parse("2023-07-07")) expect(form.end_date).to eq(Time.zone.parse("2023-07-07"))

8
spec/models/sales_log_spec.rb

@ -59,7 +59,12 @@ RSpec.describe SalesLog, type: :model do
purchid purchid
monthly_charges_value_check monthly_charges_value_check
old_persons_shared_ownership_value_check old_persons_shared_ownership_value_check
mortgagelender
othtype
discounted_sale_value_check
proplen proplen
mortlen
frombeds
]) ])
end end
end end
@ -73,6 +78,9 @@ RSpec.describe SalesLog, type: :model do
purchid purchid
monthly_charges_value_check monthly_charges_value_check
old_persons_shared_ownership_value_check old_persons_shared_ownership_value_check
mortgagelender
othtype
discounted_sale_value_check
address_line2 address_line2
county county
postcode_full postcode_full

11
spec/models/validations/sales/household_validations_spec.rb

@ -84,17 +84,14 @@ RSpec.describe Validations::Sales::HouseholdValidations do
.to include(match I18n.t("validations.household.age.child_over_20")) .to include(match I18n.t("validations.household.age.child_over_20"))
end end
it "adds errors for a person aged 16-19 who is a student but not a child of the buyer" do it "does not add and error for a person aged 16-19 who is a student but not a child of the buyer" do
record.age2 = 18 record.age2 = 18
record.ecstat2 = "7" record.ecstat2 = "7"
record.relat2 = "P" record.relat2 = "P"
household_validator.validate_household_number_of_other_members(record) household_validator.validate_household_number_of_other_members(record)
expect(record.errors["relat2"]) expect(record.errors["relat2"]).to be_empty
.to include(match I18n.t("validations.household.relat.student_16_19.must_be_child")) expect(record.errors["ecstat2"]).to be_empty
expect(record.errors["age2"]) expect(record.errors["age2"]).to be_empty
.to include(match I18n.t("validations.household.age.student_16_19.cannot_be_16_19.student_not_child"))
expect(record.errors["ecstat2"])
.to include(match I18n.t("validations.household.ecstat.student_16_19.cannot_be_student.16_19_not_child"))
end end
it "adds errors for a person aged 16-19 who is a child of the buyer but not a student" do it "adds errors for a person aged 16-19 who is a child of the buyer but not a student" do

200
spec/models/validations/sales/sale_information_validations_spec.rb

@ -226,112 +226,133 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end end
describe "#validate_discounted_ownership_value" do describe "#validate_discounted_ownership_value" do
context "when grant is routed to" do context "when sale is on or after 24/25 collection window" do
let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, deposit: 5_000, value: 30_000, ownershipsch: 2, type: 8) } context "when grant is routed to" do
let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, deposit: 5_000, value: 30_000, ownershipsch: 2, type: 8, saledate: Time.zone.local(2024, 4, 3)) }
context "and not provided" do context "and not provided" do
before do before do
record.grant = nil record.grant = nil
end end
it "does not add an error" do it "does not add an error" do
sale_information_validator.validate_discounted_ownership_value(record) sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty expect(record.errors).to be_empty
end
end end
end
context "and is provided" do context "and is provided" do
it "adds an error if mortgage, deposit and grant total does not equal market value" do it "adds an error if mortgage, deposit and grant total does not equal market value" do
record.grant = 3_000 record.grant = 3_000
sale_information_validator.validate_discounted_ownership_value(record) sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00")) expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00")) expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00")) expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00")) expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00")) expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
end end
it "does not add an error if mortgage, deposit and grant total equals market value" do it "does not add an error if mortgage, deposit and grant total equals market value" do
record.grant = 15_000 record.grant = 15_000
sale_information_validator.validate_discounted_ownership_value(record) sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty expect(record.errors).to be_empty
end
end end
end end
end
context "when discount is routed to" do context "when discount is routed to" do
let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, deposit: 5_000, value: 30_000, ownershipsch: 2, type: 9) } let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, deposit: 5_000, value: 30_000, ownershipsch: 2, type: 9, saledate: Time.zone.local(2024, 4, 3)) }
context "and not provided" do context "and not provided" do
before do before do
record.discount = nil record.discount = nil
end end
it "does not add an error" do it "does not add an error" do
sale_information_validator.validate_discounted_ownership_value(record) sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty expect(record.errors).to be_empty
end
end
context "and is provided" do
it "adds an error if mortgage and deposit total does not equal market value - discount" do
record.discount = 10
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00"))
expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00"))
expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00"))
expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00"))
expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00"))
end
it "does not add an error if mortgage and deposit total equals market value - discount" do
record.discount = 50
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty
end
end end
end end
context "and is provided" do context "when neither discount nor grant is routed to" do
it "adds an error if mortgage and deposit total does not equal market value - discount" do let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, value: 30_000, ownershipsch: 2, type: 29, saledate: Time.zone.local(2024, 4, 3)) }
record.discount = 10
it "adds an error if mortgage and deposit total does not equal market value" do
record.deposit = 2_000
sale_information_validator.validate_discounted_ownership_value(record) sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00")) expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00")) expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00")) expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00")) expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00")) expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
end end
it "does not add an error if mortgage and deposit total equals market value - discount" do it "does not add an error if mortgage and deposit total equals market value" do
record.discount = 50 record.deposit = 20_000
sale_information_validator.validate_discounted_ownership_value(record) sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty expect(record.errors).to be_empty
end end
end end
end
context "when neither discount nor grant is routed to" do context "when mortgage is routed to" do
let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, value: 30_000, ownershipsch: 2, type: 29) } let(:record) { FactoryBot.build(:sales_log, mortgageused: 1, deposit: 5_000, grant: 3_000, value: 20_000, discount: 10, ownershipsch: 2, saledate: Time.zone.local(2024, 4, 3)) }
it "adds an error if mortgage and deposit total does not equal market value" do context "and not provided" do
record.deposit = 2_000 before do
sale_information_validator.validate_discounted_ownership_value(record) record.mortgage = nil
expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00")) end
expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
end
it "does not add an error if mortgage and deposit total equals market value" do it "does not add an error" do
record.deposit = 20_000 sale_information_validator.validate_discounted_ownership_value(record)
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty
end
end
context "when mortgage is routed to" do expect(record.errors).to be_empty
let(:record) { FactoryBot.build(:sales_log, mortgageused: 1, deposit: 5_000, grant: 3_000, value: 20_000, discount: 10, ownershipsch: 2) } end
context "and not provided" do
before do
record.mortgage = nil
end end
it "does not add an error" do context "and is provided" do
sale_information_validator.validate_discounted_ownership_value(record) it "adds an error if mortgage, grant and deposit total does not equal market value - discount" do
record.mortgage = 10
expect(record.errors).to be_empty sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
end
it "does not add an error if mortgage, grant and deposit total equals market value - discount" do
record.mortgage = 10_000
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty
end
end end
end end
context "and is provided" do context "when mortgage is not routed to" do
it "adds an error if mortgage, grant and deposit total does not equal market value - discount" do let(:record) { FactoryBot.build(:sales_log, mortgageused: 2, deposit: 5_000, grant: 3_000, value: 20_000, discount: 10, ownershipsch: 2, saledate: Time.zone.local(2024, 4, 3)) }
record.mortgage = 10
it "adds an error if grant and deposit total does not equal market value - discount" do
sale_information_validator.validate_discounted_ownership_value(record) sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00")) expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00")) expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
@ -341,34 +362,25 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end end
it "does not add an error if mortgage, grant and deposit total equals market value - discount" do it "does not add an error if mortgage, grant and deposit total equals market value - discount" do
record.mortgage = 10_000 record.grant = 13_000
sale_information_validator.validate_discounted_ownership_value(record) sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty expect(record.errors).to be_empty
end end
end end
end
context "when mortgage is not routed to" do context "when ownership is not discounted" do
let(:record) { FactoryBot.build(:sales_log, mortgageused: 2, deposit: 5_000, grant: 3_000, value: 20_000, discount: 10, ownershipsch: 2) } let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, deposit: 5_000, grant: 3_000, value: 20_000, discount: 10, ownershipsch: 1, saledate: Time.zone.local(2024, 4, 3)) }
it "adds an error if grant and deposit total does not equal market value - discount" do it "does not add an error" do
sale_information_validator.validate_discounted_ownership_value(record) sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
end
it "does not add an error if mortgage, grant and deposit total equals market value - discount" do expect(record.errors).to be_empty
record.grant = 13_000 end
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty
end end
end end
context "when ownership is not discounted" do context "when sale is before 24/25 collection" do
let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, deposit: 5_000, grant: 3_000, value: 20_000, discount: 10, ownershipsch: 1) } let(:record) { FactoryBot.build(:sales_log, mortgageused: 2, deposit: 5_000, grant: 3_000, value: 20_000, discount: 10, ownershipsch: 2, saledate: Time.zone.local(2023, 4, 4)) }
it "does not add an error" do it "does not add an error" do
sale_information_validator.validate_discounted_ownership_value(record) sale_information_validator.validate_discounted_ownership_value(record)

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

@ -730,4 +730,187 @@ RSpec.describe Validations::Sales::SoftValidations do
end end
end end
end end
describe "#person_2_student_not_child?" do
it "returns false if age is not given" do
record.age2 = nil
record.relat2 = "P"
record.ecstat2 = 7
expect(record).not_to be_person_2_student_not_child
end
it "returns false if retaltionship is not given" do
record.age2 = 17
record.relat2 = nil
record.ecstat2 = 7
expect(record).not_to be_person_2_student_not_child
end
it "returns false if economic status is not given" do
record.age2 = 17
record.relat2 = "P"
record.ecstat2 = nil
expect(record).not_to be_person_2_student_not_child
end
it "returns true if it's a student aged 16-19 and not a child" do
record.age2 = 17
record.relat2 = "P"
record.ecstat2 = 7
expect(record).to be_person_2_student_not_child
end
end
describe "#person_3_student_not_child?" do
it "returns false if age is not given" do
record.age3 = nil
record.relat3 = "P"
record.ecstat3 = 7
expect(record).not_to be_person_3_student_not_child
end
it "returns false if retaltionship is not given" do
record.age3 = 17
record.relat3 = nil
record.ecstat3 = 7
expect(record).not_to be_person_3_student_not_child
end
it "returns false if economic status is not given" do
record.age3 = 17
record.relat3 = "P"
record.ecstat3 = nil
expect(record).not_to be_person_3_student_not_child
end
it "returns true if it's a student aged 16-19 and not a child" do
record.age3 = 17
record.relat3 = "P"
record.ecstat3 = 7
expect(record).to be_person_3_student_not_child
end
end
describe "#discounted_ownership_value_invalid?" do
context "when grant is routed to" do
let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, deposit: 5_000, value: 30_000, ownershipsch: 2, type: 8, saledate: Time.zone.local(2023, 4, 3)) }
context "and not provided" do
before do
record.grant = nil
end
it "returns false" do
expect(record).not_to be_discounted_ownership_value_invalid
end
end
context "and is provided" do
it "returns true if mortgage, deposit and grant total does not equal market value" do
record.grant = 3_000
expect(record).to be_discounted_ownership_value_invalid
end
it "returns false if mortgage, deposit and grant total equals market value" do
record.grant = 15_000
expect(record).not_to be_discounted_ownership_value_invalid
end
end
end
context "when discount is routed to" do
let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, deposit: 5_000, value: 30_000, ownershipsch: 2, type: 9, saledate: Time.zone.local(2023, 4, 3)) }
context "and not provided" do
before do
record.discount = nil
end
it "returns false" do
expect(record).not_to be_discounted_ownership_value_invalid
end
end
context "and is provided" do
it "returns true if mortgage and deposit total does not equal market value - discount" do
record.discount = 10
expect(record).to be_discounted_ownership_value_invalid
end
it "returns false if mortgage and deposit total equals market value - discount" do
record.discount = 50
expect(record).not_to be_discounted_ownership_value_invalid
end
end
end
context "when neither discount nor grant is routed to" do
let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, value: 30_000, ownershipsch: 2, type: 29, saledate: Time.zone.local(2023, 4, 3)) }
it "returns true if mortgage and deposit total does not equal market value" do
record.deposit = 2_000
expect(record).to be_discounted_ownership_value_invalid
end
it "returns false if mortgage and deposit total equals market value" do
record.deposit = 20_000
expect(record).not_to be_discounted_ownership_value_invalid
end
end
context "when mortgage is routed to" do
let(:record) { FactoryBot.build(:sales_log, mortgageused: 1, deposit: 5_000, grant: 3_000, value: 20_000, discount: 10, ownershipsch: 2, saledate: Time.zone.local(2023, 4, 3)) }
context "and not provided" do
before do
record.mortgage = nil
end
it "returns false" do
expect(record).not_to be_discounted_ownership_value_invalid
end
end
context "and is provided" do
it "returns true if mortgage, grant and deposit total does not equal market value - discount" do
record.mortgage = 10
expect(record).to be_discounted_ownership_value_invalid
end
it "returns false if mortgage, grant and deposit total equals market value - discount" do
record.mortgage = 10_000
expect(record).not_to be_discounted_ownership_value_invalid
end
end
end
context "when mortgage is not routed to" do
let(:record) { FactoryBot.build(:sales_log, mortgageused: 2, deposit: 5_000, grant: 3_000, value: 20_000, discount: 10, ownershipsch: 2, saledate: Time.zone.local(2023, 4, 3)) }
it "returns true if grant and deposit total does not equal market value - discount" do
expect(record).to be_discounted_ownership_value_invalid
end
it "returns false if mortgage, grant and deposit total equals market value - discount" do
record.grant = 13_000
expect(record).not_to be_discounted_ownership_value_invalid
end
end
context "when ownership is not discounted" do
let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, deposit: 5_000, grant: 3_000, value: 20_000, discount: 10, ownershipsch: 1, saledate: Time.zone.local(2023, 4, 3)) }
it "returns false" do
expect(record).not_to be_discounted_ownership_value_invalid
end
end
end
end end

136
spec/services/imports/sales_logs_import_service_spec.rb

@ -363,20 +363,49 @@ RSpec.describe Imports::SalesLogsImportService do
end end
end end
context "and it has an invalid record with invalid child, student and 16-19 age combination" do context "and the student not child soft validation is triggered (student_not_child_value_check)" do
let(:sales_log_id) { "shared_ownership_sales_log" }
before do
sales_log_xml.at_xpath("//xmlns:P2Rel").content = "P"
sales_log_xml.at_xpath("//xmlns:P2Eco").content = "7"
sales_log_xml.at_xpath("//xmlns:P2Age").content = "16"
end
it "completes the log" do
sales_log_service.send(:create_log, sales_log_xml)
sales_log = SalesLog.find_by(old_id: sales_log_id)
expect(sales_log.status).to eq("completed")
end
end
context "and the value soft validation is triggered (discounted_sale_value_check)" do
let(:sales_log_id) { "discounted_ownership_sales_log" }
before do
sales_log_xml.at_xpath("//xmlns:Q31PurchasePrice").content = "500000"
end
it "completes the log" do
sales_log_service.send(:create_log, sales_log_xml)
sales_log = SalesLog.find_by(old_id: sales_log_id)
expect(sales_log.status).to eq("completed")
expect(sales_log.discounted_sale_value_check).to eq(0)
end
end
context "and it has an invalid record with invalid child age" do
let(:sales_log_id) { "discounted_ownership_sales_log" } let(:sales_log_id) { "discounted_ownership_sales_log" }
before do before do
sales_log_xml.at_xpath("//meta:status").content = "submitted-invalid" sales_log_xml.at_xpath("//meta:status").content = "submitted-invalid"
sales_log_xml.at_xpath("//xmlns:P2Age").content = 16 sales_log_xml.at_xpath("//xmlns:P2Age").content = 17
sales_log_xml.at_xpath("//xmlns:P2Eco").content = 7 sales_log_xml.at_xpath("//xmlns:P2Eco").content = 9
sales_log_xml.at_xpath("//xmlns:P2Rel").content = "X"
end end
it "intercepts the relevant validation error" do it "intercepts the relevant validation error" do
expect(logger).to receive(:warn).with(/Removing field age2 from log triggering validation: Person cannot be aged 16-19 if they are a student but don't have relationship ‘child’/) expect(logger).to receive(:warn).with(/Log discounted_ownership_sales_log: Removing field ecstat2 from log triggering validation: Answer cannot be ‘child under 16’ as you told us the person 2 is older than 16/)
expect(logger).to receive(:warn).with(/Removing field relat2 from log triggering validation: Answer must be ‘child’ if the person is aged 16-19 and a student/) expect(logger).to receive(:warn).with(/Log discounted_ownership_sales_log: Removing field age2 from log triggering validation: Answer cannot be over 16 as person’s 2 working situation is ‘child under 16‘/)
expect(logger).to receive(:warn).with(/Removing field ecstat2 from log triggering validation: Person cannot be a student if they are aged 16-19 but don‘t have relationship ‘child’/)
expect { sales_log_service.send(:create_log, sales_log_xml) } expect { sales_log_service.send(:create_log, sales_log_xml) }
.not_to raise_error .not_to raise_error
end end
@ -389,7 +418,6 @@ RSpec.describe Imports::SalesLogsImportService do
expect(sales_log).not_to be_nil expect(sales_log).not_to be_nil
expect(sales_log.age2).to be_nil expect(sales_log.age2).to be_nil
expect(sales_log.relat2).to be_nil
expect(sales_log.ecstat2).to be_nil expect(sales_log.ecstat2).to be_nil
end end
end end
@ -420,6 +448,32 @@ RSpec.describe Imports::SalesLogsImportService do
end end
end end
context "and it has invalid postcodes" do
let(:sales_log_id) { "shared_ownership_sales_log" }
before do
sales_log_xml.at_xpath("//meta:status").content = "submitted-invalid"
sales_log_xml.at_xpath("//xmlns:Q14Postcode").content = "2AA" # postcode
end
it "intercepts the relevant validation error" do
expect(logger).to receive(:warn).with(/Enter a postcode in the correct format, for example AA1 1AA/)
expect { sales_log_service.send(:create_log, sales_log_xml) }
.not_to raise_error
end
it "clears out the invalid answers" do
allow(logger).to receive(:warn)
sales_log_service.send(:create_log, sales_log_xml)
sales_log = SalesLog.find_by(old_id: sales_log_id)
expect(sales_log).not_to be_nil
expect(sales_log.postcode_full).to be_nil
expect(sales_log.postcode_full).to be_nil
end
end
context "and it has an invalid record with invalid contracts exchange date" do context "and it has an invalid record with invalid contracts exchange date" do
let(:sales_log_id) { "shared_ownership_sales_log" } let(:sales_log_id) { "shared_ownership_sales_log" }
@ -1176,6 +1230,72 @@ RSpec.describe Imports::SalesLogsImportService do
expect(sales_log&.mortgageused).to eq(1) expect(sales_log&.mortgageused).to eq(1)
end end
end end
context "when the extrabor is not answered" do
let(:sales_log_id) { "discounted_ownership_sales_log" }
before do
sales_log_xml.at_xpath("//xmlns:Q35Borrowing").content = ""
allow(logger).to receive(:warn).and_return(nil)
end
it "sets extrabor to don't know" do
sales_log_service.send(:create_log, sales_log_xml)
sales_log = SalesLog.find_by(old_id: sales_log_id)
expect(sales_log&.extrabor).to be(3)
end
end
context "when the fromprop is not answered" do
let(:sales_log_id) { "shared_ownership_sales_log" }
before do
sales_log_xml.at_xpath("//xmlns:Q21PropertyType").content = ""
allow(logger).to receive(:warn).and_return(nil)
end
it "sets fromprop to don't know" do
sales_log_service.send(:create_log, sales_log_xml)
sales_log = SalesLog.find_by(old_id: sales_log_id)
expect(sales_log&.fromprop).to be(0)
end
end
context "when the socprevten is not answered" do
let(:sales_log_id) { "shared_ownership_sales_log" }
before do
sales_log_xml.at_xpath("//xmlns:PrevRentType").content = ""
allow(logger).to receive(:warn).and_return(nil)
end
it "sets socprevten to don't know" do
sales_log_service.send(:create_log, sales_log_xml)
sales_log = SalesLog.find_by(old_id: sales_log_id)
expect(sales_log&.socprevten).to be(10)
end
end
context "when it's a joint tenancy and jointmore is not answered" do
let(:sales_log_id) { "outright_sale_sales_log" }
before do
allow(logger).to receive(:warn).and_return(nil)
end
it "sets jointmore to don't know" do
sales_log_xml.at_xpath("//meta:status").content = "invalid"
sales_log_xml.at_xpath("//xmlns:joint").content = "1 Yes"
sales_log_xml.at_xpath("//xmlns:JointMore").content = ""
sales_log_service.send(:create_log, sales_log_xml)
sales_log = SalesLog.find_by(old_id: sales_log_id)
expect(sales_log&.jointmore).to eq(3)
end
end
end end
end end
end end

Loading…
Cancel
Save