diff --git a/app/models/form/sales/pages/about_deposit_with_discount.rb b/app/models/form/sales/pages/about_deposit_with_discount.rb index 1c684b0bc..5d1e41a95 100644 --- a/app/models/form/sales/pages/about_deposit_with_discount.rb +++ b/app/models/form/sales/pages/about_deposit_with_discount.rb @@ -1,15 +1,22 @@ class Form::Sales::Pages::AboutDepositWithDiscount < ::Form::Page - def initialize(id, hsh, subsection) - super - @id = "about_deposit_with_discount" + def initialize(id, hsh, subsection, optional:) + super(id, hsh, subsection) @header = "About the deposit" - @depends_on = [{ "is_type_discount?" => true }] + @optional = optional end def questions @questions ||= [ - Form::Sales::Questions::DepositAmount.new(nil, nil, self, ownershipsch: 1), + Form::Sales::Questions::DepositAmount.new(nil, nil, self, ownershipsch: 1, optional: @optional), Form::Sales::Questions::DepositDiscount.new(nil, nil, self), ] end + + def depends_on + if form.start_year_after_2024? + [{ "is_type_discount?" => true, "stairowned_100?" => @optional }] + else + [{ "is_type_discount?" => true }] + end + end end diff --git a/app/models/form/sales/pages/about_deposit_without_discount.rb b/app/models/form/sales/pages/about_deposit_without_discount.rb index 1114b8f84..fdd74cf31 100644 --- a/app/models/form/sales/pages/about_deposit_without_discount.rb +++ b/app/models/form/sales/pages/about_deposit_without_discount.rb @@ -1,16 +1,26 @@ class Form::Sales::Pages::AboutDepositWithoutDiscount < ::Form::Page - def initialize(id, hsh, subsection, ownershipsch:) + def initialize(id, hsh, subsection, ownershipsch:, optional:) super(id, hsh, subsection) @header = "About the deposit" - @depends_on = [{ "is_type_discount?" => false, "ownershipsch" => 1 }, - { "ownershipsch" => 2 }, - { "ownershipsch" => 3, "mortgageused" => 1 }] @ownershipsch = ownershipsch + @optional = optional end def questions @questions ||= [ - Form::Sales::Questions::DepositAmount.new(nil, nil, self, ownershipsch: @ownershipsch), + Form::Sales::Questions::DepositAmount.new(nil, nil, self, ownershipsch: @ownershipsch, optional: @optional), ] end + + def depends_on + if form.start_year_after_2024? + [{ "is_type_discount?" => false, "ownershipsch" => 1, "stairowned_100?" => @optional }, + { "ownershipsch" => 2 }, + { "ownershipsch" => 3, "mortgageused" => 1 }] + else + [{ "is_type_discount?" => false, "ownershipsch" => 1 }, + { "ownershipsch" => 2 }, + { "ownershipsch" => 3, "mortgageused" => 1 }] + end + end end diff --git a/app/models/form/sales/questions/deposit_amount.rb b/app/models/form/sales/questions/deposit_amount.rb index 784bb56a1..689299e56 100644 --- a/app/models/form/sales/questions/deposit_amount.rb +++ b/app/models/form/sales/questions/deposit_amount.rb @@ -1,5 +1,5 @@ class Form::Sales::Questions::DepositAmount < ::Form::Question - def initialize(id, hsh, subsection, ownershipsch:) + def initialize(id, hsh, subsection, ownershipsch:, optional:) super(id, hsh, subsection) @id = "deposit" @check_answer_label = "Cash deposit" @@ -10,10 +10,10 @@ class Form::Sales::Questions::DepositAmount < ::Form::Question @step = 1 @width = 5 @prefix = "£" - @hint_text = "Enter the total cash sum paid by the buyer towards the property that was not funded by the mortgage" @derived = true @ownershipsch = ownershipsch @question_number = question_number + @optional = optional end def selected_answer_option_is_derived?(_log) @@ -30,4 +30,12 @@ class Form::Sales::Questions::DepositAmount < ::Form::Question 116 end end + + def hint_text + if @optional + "Enter the total cash sum paid by the buyer towards the property that was not funded by the mortgage. As this is a fully staircased sale this question is optional. If you do not have the information available click save and continue" + else + "Enter the total cash sum paid by the buyer towards the property that was not funded by the mortgage" + end + end end diff --git a/app/models/form/sales/subsections/discounted_ownership_scheme.rb b/app/models/form/sales/subsections/discounted_ownership_scheme.rb index 52ee9e95c..200565ab6 100644 --- a/app/models/form/sales/subsections/discounted_ownership_scheme.rb +++ b/app/models/form/sales/subsections/discounted_ownership_scheme.rb @@ -31,7 +31,7 @@ class Form::Sales::Subsections::DiscountedOwnershipScheme < ::Form::Subsection Form::Sales::Pages::MortgageLength.new("mortgage_length_discounted_ownership", nil, self, ownershipsch: 2), Form::Sales::Pages::ExtraBorrowing.new("extra_borrowing_discounted_ownership", nil, self, ownershipsch: 2), Form::Sales::Pages::ExtraBorrowingValueCheck.new("extra_borrowing_value_check", nil, self), - Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_discounted_ownership", nil, self, ownershipsch: 2), + Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_discounted_ownership", nil, self, ownershipsch: 2, optional: false), 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::DepositAndMortgageValueCheck.new("discounted_ownership_deposit_and_mortgage_value_check_after_deposit", nil, self), diff --git a/app/models/form/sales/subsections/outright_sale.rb b/app/models/form/sales/subsections/outright_sale.rb index 245cbcb10..39275d7b2 100644 --- a/app/models/form/sales/subsections/outright_sale.rb +++ b/app/models/form/sales/subsections/outright_sale.rb @@ -18,7 +18,7 @@ class Form::Sales::Subsections::OutrightSale < ::Form::Subsection (Form::Sales::Pages::MortgageLenderOther.new("mortgage_lender_other_outright_sale", nil, self, ownershipsch: 3) unless form.start_year_after_2024?), Form::Sales::Pages::MortgageLength.new("mortgage_length_outright_sale", nil, self, ownershipsch: 3), Form::Sales::Pages::ExtraBorrowing.new("extra_borrowing_outright_sale", nil, self, ownershipsch: 3), - Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_outright_sale", nil, self, ownershipsch: 3), + Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_outright_sale", nil, self, ownershipsch: 3, optional: false), Form::Sales::Pages::DepositValueCheck.new("outright_sale_deposit_value_check", nil, self), leasehold_charge_pages, Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_outright_sale_value_check", nil, self), diff --git a/app/models/form/sales/subsections/shared_ownership_scheme.rb b/app/models/form/sales/subsections/shared_ownership_scheme.rb index 66a711dcd..51abe3656 100644 --- a/app/models/form/sales/subsections/shared_ownership_scheme.rb +++ b/app/models/form/sales/subsections/shared_ownership_scheme.rb @@ -37,14 +37,16 @@ class Form::Sales::Subsections::SharedOwnershipScheme < ::Form::Subsection Form::Sales::Pages::MortgageLenderOther.new("mortgage_lender_other_shared_ownership", nil, self, ownershipsch: 1), Form::Sales::Pages::MortgageLength.new("mortgage_length_shared_ownership", nil, self, ownershipsch: 1), Form::Sales::Pages::ExtraBorrowing.new("extra_borrowing_shared_ownership", nil, self, ownershipsch: 1), - Form::Sales::Pages::AboutDepositWithDiscount.new(nil, nil, self), - Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_shared_ownership", nil, self, ownershipsch: 1), + Form::Sales::Pages::AboutDepositWithDiscount.new("about_deposit_with_discount", nil, self, optional: false), + (Form::Sales::Pages::AboutDepositWithDiscount.new("about_deposit_with_discount_optional", nil, self, optional: true) if form.start_year_after_2024?), + Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_shared_ownership", nil, self, ownershipsch: 1, optional: false), + (Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_shared_ownership_optional", nil, self, ownershipsch: 1, optional: true) if form.start_year_after_2024?), Form::Sales::Pages::DepositValueCheck.new("deposit_value_check", nil, self), Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_deposit_value_check", nil, self), Form::Sales::Pages::MonthlyRent.new(nil, nil, self), Form::Sales::Pages::LeaseholdCharges.new("leasehold_charges_shared_ownership", nil, self, ownershipsch: 1), Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_shared_ownership_value_check", nil, self), - ] + ].compact end def displayed_in_tasklist?(log) diff --git a/app/models/sales_log.rb b/app/models/sales_log.rb index 76c43b09e..13d16a786 100644 --- a/app/models/sales_log.rb +++ b/app/models/sales_log.rb @@ -121,6 +121,7 @@ class SalesLog < Log not_required << "proplen" if proplen_optional? not_required << "mortlen" if mortlen_optional? not_required << "frombeds" if frombeds_optional? + not_required << "deposit" if form.start_year_after_2024? && stairowned_100? not_required |= %w[address_line2 county postcode_full] if saledate && collection_start_year_for_date(saledate) >= 2023 @@ -495,4 +496,8 @@ class SalesLog < Log def is_not_staircasing? staircase == 2 || staircase == 3 end + + def stairowned_100? + stairowned == 100 + end end diff --git a/app/models/validations/sales/sale_information_validations.rb b/app/models/validations/sales/sale_information_validations.rb index 0aaa77a93..c8a4e46dc 100644 --- a/app/models/validations/sales/sale_information_validations.rb +++ b/app/models/validations/sales/sale_information_validations.rb @@ -110,4 +110,14 @@ module Validations::Sales::SaleInformationValidations end end end + + def validate_mortgage_used_and_stairbought(record) + return unless record.stairowned && record.mortgageused + return unless record.saledate && record.form.start_year_after_2024? + + if !record.stairowned_100? && record.mortgageused == 3 + record.errors.add :stairowned, I18n.t("validations.sale_information.stairowned.mortgageused_dont_know") + record.errors.add :mortgageused, I18n.t("validations.sale_information.stairowned.mortgageused_dont_know") + end + end end diff --git a/app/services/bulk_upload/sales/year2024/row_parser.rb b/app/services/bulk_upload/sales/year2024/row_parser.rb index 28e7ef100..c7a5349d0 100644 --- a/app/services/bulk_upload/sales/year2024/row_parser.rb +++ b/app/services/bulk_upload/sales/year2024/row_parser.rb @@ -341,6 +341,14 @@ class BulkUpload::Sales::Year2024::RowParser }, on: :before_log + validates :field_103, + inclusion: { + in: [1, 2], + if: proc { field_88 != 100 }, + question: QUESTIONS[:field_103], + }, + on: :before_log + validates :field_9, presence: { message: I18n.t("validations.not_answered", question: "type of shared ownership sale"), diff --git a/config/locales/en.yml b/config/locales/en.yml index 204a20b4e..37239d234 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -623,6 +623,8 @@ en: over_discounted_london_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £136,400 for properties in London." over_discounted_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £102,400 for properties outside of London." non_staircasing_mortgage: "The mortgage and deposit added together is %{mortgage_and_deposit_total} and the purchase price times by the equity is %{expected_shared_ownership_deposit_value}. These figures should be the same." + stairowned: + mortgageused_dont_know: "The percentage owned has to be 100% if the mortgage used is 'Don’t know'" merge_request: organisation_part_of_another_merge: "This organisation is part of another merge - select a different one" organisation_not_selected: "Select an organisation from the search list" diff --git a/spec/models/form/sales/pages/about_deposit_with_discount_spec.rb b/spec/models/form/sales/pages/about_deposit_with_discount_spec.rb index cc33a9641..89db397f1 100644 --- a/spec/models/form/sales/pages/about_deposit_with_discount_spec.rb +++ b/spec/models/form/sales/pages/about_deposit_with_discount_spec.rb @@ -1,12 +1,16 @@ require "rails_helper" RSpec.describe Form::Sales::Pages::AboutDepositWithDiscount, type: :model do - subject(:page) { described_class.new(page_id, page_definition, subsection) } + subject(:page) { described_class.new(page_id, page_definition, subsection, optional: false) } - let(:page_id) { nil } + let(:page_id) { "about_deposit_with_discount" } let(:page_definition) { nil } let(:subsection) { instance_double(Form::Subsection) } + before do + allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: false)) + end + it "has correct subsection" do expect(page.subsection).to eq(subsection) end @@ -32,4 +36,36 @@ RSpec.describe Form::Sales::Pages::AboutDepositWithDiscount, type: :model do [{ "is_type_discount?" => true }], ) end + + context "when optional" do + subject(:page) { described_class.new(page_id, page_definition, subsection, optional: true) } + + it "has correct depends_on" do + expect(page.depends_on).to eq( + [{ "is_type_discount?" => true }], + ) + end + end + + context "when it's a 2024 form" do + before do + allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: true)) + end + + it "has correct depends_on" do + expect(page.depends_on).to eq( + [{ "is_type_discount?" => true, "stairowned_100?" => false }], + ) + end + + context "and optional" do + subject(:page) { described_class.new(page_id, page_definition, subsection, optional: true) } + + it "has correct depends_on" do + expect(page.depends_on).to eq( + [{ "is_type_discount?" => true, "stairowned_100?" => true }], + ) + end + end + end end diff --git a/spec/models/form/sales/pages/about_deposit_without_discount_spec.rb b/spec/models/form/sales/pages/about_deposit_without_discount_spec.rb index 1a0b5ef4d..1a4e420c6 100644 --- a/spec/models/form/sales/pages/about_deposit_without_discount_spec.rb +++ b/spec/models/form/sales/pages/about_deposit_without_discount_spec.rb @@ -1,12 +1,16 @@ require "rails_helper" RSpec.describe Form::Sales::Pages::AboutDepositWithoutDiscount, type: :model do - subject(:page) { described_class.new(page_id, page_definition, subsection, ownershipsch: 1) } + subject(:page) { described_class.new(page_id, page_definition, subsection, ownershipsch: 1, optional: false) } let(:page_id) { nil } let(:page_definition) { nil } let(:subsection) { instance_double(Form::Subsection) } + before do + allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: false)) + end + it "has correct subsection" do expect(page.subsection).to eq(subsection) end @@ -34,4 +38,42 @@ RSpec.describe Form::Sales::Pages::AboutDepositWithoutDiscount, type: :model do { "ownershipsch" => 3, "mortgageused" => 1 }], ) end + + context "when optional is true" do + subject(:page) { described_class.new(page_id, page_definition, subsection, ownershipsch: 1, optional: true) } + + it "has correct depends_on" do + expect(page.depends_on).to eq( + [{ "is_type_discount?" => false, "ownershipsch" => 1 }, + { "ownershipsch" => 2 }, + { "ownershipsch" => 3, "mortgageused" => 1 }], + ) + end + end + + context "when it's a 2024 form" do + before do + allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: true)) + end + + it "has correct depends_on" do + expect(page.depends_on).to eq( + [{ "is_type_discount?" => false, "ownershipsch" => 1, "stairowned_100?" => false }, + { "ownershipsch" => 2 }, + { "ownershipsch" => 3, "mortgageused" => 1 }], + ) + end + + context "and optional is true" do + subject(:page) { described_class.new(page_id, page_definition, subsection, ownershipsch: 1, optional: true) } + + it "has correct depends_on" do + expect(page.depends_on).to eq( + [{ "is_type_discount?" => false, "ownershipsch" => 1, "stairowned_100?" => true }, + { "ownershipsch" => 2 }, + { "ownershipsch" => 3, "mortgageused" => 1 }], + ) + end + end + end end diff --git a/spec/models/form/sales/questions/deposit_amount_spec.rb b/spec/models/form/sales/questions/deposit_amount_spec.rb index e0a77b7fc..80429b5af 100644 --- a/spec/models/form/sales/questions/deposit_amount_spec.rb +++ b/spec/models/form/sales/questions/deposit_amount_spec.rb @@ -1,7 +1,7 @@ require "rails_helper" RSpec.describe Form::Sales::Questions::DepositAmount, type: :model do - subject(:question) { described_class.new(question_id, question_definition, page, ownershipsch: 1) } + subject(:question) { described_class.new(question_id, question_definition, page, ownershipsch: 1, optional: false) } let(:question_id) { nil } let(:question_definition) { nil } @@ -50,4 +50,12 @@ RSpec.describe Form::Sales::Questions::DepositAmount, type: :model do it "has correct max" do expect(question.max).to eq(999_999) end + + context "when optional iis true" do + subject(:question) { described_class.new(question_id, question_definition, page, ownershipsch: 1, optional: true) } + + it "has a correct hint_text" do + expect(question.hint_text).to eq("Enter the total cash sum paid by the buyer towards the property that was not funded by the mortgage. As this is a fully staircased sale this question is optional. If you do not have the information available click save and continue") + end + end end diff --git a/spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb b/spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb index 192f7cbda..3746768d0 100644 --- a/spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb +++ b/spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb @@ -7,6 +7,10 @@ RSpec.describe Form::Sales::Subsections::SharedOwnershipScheme, type: :model do let(:subsection_definition) { nil } let(:section) { instance_double(Form::Sales::Sections::SaleInformation) } + before do + allow(section).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: false)) + end + it "has correct section" do expect(shared_ownership_scheme.section).to eq(section) end diff --git a/spec/models/validations/sales/sale_information_validations_spec.rb b/spec/models/validations/sales/sale_information_validations_spec.rb index 73243e9fa..3d323112e 100644 --- a/spec/models/validations/sales/sale_information_validations_spec.rb +++ b/spec/models/validations/sales/sale_information_validations_spec.rb @@ -756,4 +756,48 @@ RSpec.describe Validations::Sales::SaleInformationValidations do end end end + + describe "#validate_mortgage_used_and_stairbought" do + let(:now) { Time.zone.local(2024, 4, 4) } + + before do + Timecop.freeze(now) + Singleton.__init__(FormHandler) + end + + after do + Timecop.return + Singleton.__init__(FormHandler) + end + + context "when mortgageused don't know" do + let(:record) { build(:sales_log, ownershipsch: 1, type: 9, saledate: now, mortgageused: 3) } + + it "does not add an error if stairowned 100" do + record.stairowned = 100 + sale_information_validator.validate_mortgage_used_and_stairbought(record) + + expect(record.errors).to be_empty + end + + it "adds an error if stairowned is not 100" do + record.stairowned = 90 + sale_information_validator.validate_mortgage_used_and_stairbought(record) + + expect(record.errors[:stairowned]).to include("The percentage owned has to be 100% if the mortgage used is 'Don’t know'") + expect(record.errors[:mortgageused]).to include("The percentage owned has to be 100% if the mortgage used is 'Don’t know'") + end + end + + context "when the collection year is before 2024" do + let(:record) { build(:sales_log, ownershipsch: 1, type: 9, saledate: now, mortgageused: 3, stairowned: 90) } + let(:now) { Time.zone.local(2023, 4, 4) } + + it "does not add an error" do + sale_information_validator.validate_mortgage_used_and_stairbought(record) + + expect(record.errors).to be_empty + end + end + end end diff --git a/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb b/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb index 93e6fdca1..d93453a22 100644 --- a/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb +++ b/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb @@ -975,6 +975,36 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do end end + describe "#field_103" do # shared ownership mortgageused + context "when invalid value" do + let(:attributes) { setup_section_params.merge(field_103: "4") } + + it "returns correct errors" do + expect(parser.errors[:field_103]).to include("Enter a valid value for Was a mortgage used for the purchase of this property? - Shared ownership") + end + end + + context "when value is 3 and stairowned is not 100" do + let(:attributes) { setup_section_params.merge(field_103: "3", field_86: "1", field_87: "50", field_88: "99", field_109: nil) } + + it "returns correct errors" do + expect(parser.errors[:field_103]).to include("Enter a valid value for Was a mortgage used for the purchase of this property? - Shared ownership") + end + end + + context "when value is 3 and stairowned is 100" do + let(:attributes) { setup_section_params.merge(field_103: "3", field_86: "1", field_87: "50", field_88: "100", field_109: nil) } + + it "does not add errors and sets mortgage used to 3" do + expect(parser.log.mortgageused).to be(3) + expect(parser.log.stairowned).to be(100) + expect(parser.log.deposit).to be(nil) + expect(parser.errors[:field_103]).to be_empty + expect(parser.errors[:field_109]).to be_empty + end + end + end + describe "soft validations" do context "when soft validation is triggered" do let(:attributes) { valid_attributes.merge({ field_31: 22, field_35: 5 }) }