diff --git a/app/controllers/form_controller.rb b/app/controllers/form_controller.rb index 2bf7ce966..635dc12e4 100644 --- a/app/controllers/form_controller.rb +++ b/app/controllers/form_controller.rb @@ -250,7 +250,9 @@ private def check_collection_period return unless @log - redirect_to lettings_log_path(@log) unless @log.collection_period_open_for_editing? + unless @log.collection_period_open_for_editing? + redirect_to @log.lettings? ? lettings_log_path(@log) : sales_log_path(@log) + end end CONFIRMATION_PAGE_IDS = %w[uprn_confirmation uprn_selection].freeze diff --git a/app/models/form/lettings/questions/reason.rb b/app/models/form/lettings/questions/reason.rb index 1a7d0a167..223d68634 100644 --- a/app/models/form/lettings/questions/reason.rb +++ b/app/models/form/lettings/questions/reason.rb @@ -18,10 +18,10 @@ class Form::Lettings::Questions::Reason < ::Form::Question def answer_options if form.start_year_after_2024? { - "50" => { "value" => "End of social housing tenancy - no fault" }, - "51" => { "value" => "End of social housing tenancy - evicted due to anti-social behaviour (ASB)" }, - "52" => { "value" => "End of social housing tenancy - evicted due to rent arrears" }, - "53" => { "value" => "End of social housing tenancy - evicted for any other reason" }, + "50" => { "value" => "End of social or private sector tenancy - no fault" }, + "51" => { "value" => "End of social or private sector tenancy - evicted due to anti-social behaviour (ASB)" }, + "52" => { "value" => "End of social or private sector tenancy - evicted due to rent arrears" }, + "53" => { "value" => "End of social or private sector tenancy - evicted for any other reason" }, "1" => { "value" => "Permanently decanted from another property owned by this landlord" }, "2" => { "value" => "Left home country as a refugee" }, "45" => { "value" => "Discharged from prison" }, diff --git a/app/models/form/lettings/questions/reason_renewal.rb b/app/models/form/lettings/questions/reason_renewal.rb index b3e9fc9dc..7f32b0bba 100644 --- a/app/models/form/lettings/questions/reason_renewal.rb +++ b/app/models/form/lettings/questions/reason_renewal.rb @@ -18,10 +18,10 @@ class Form::Lettings::Questions::ReasonRenewal < ::Form::Question def answer_options if form.start_year_after_2024? { - "50" => { "value" => "End of social housing tenancy - no fault" }, - "51" => { "value" => "End of social housing tenancy - evicted due to anti-social behaviour (ASB)" }, - "52" => { "value" => "End of social housing tenancy - evicted due to rent arrears" }, - "53" => { "value" => "End of social housing tenancy - evicted for any other reason" }, + "50" => { "value" => "End of social or private sector tenancy - no fault" }, + "51" => { "value" => "End of social or private sector tenancy - evicted due to anti-social behaviour (ASB)" }, + "52" => { "value" => "End of social or private sector tenancy - evicted due to rent arrears" }, + "53" => { "value" => "End of social or private sector tenancy - evicted for any other reason" }, "20" => { "value" => "Other" }, "47" => { "value" => "Tenant prefers not to say" }, "divider" => { "value" => true }, diff --git a/app/models/validations/sales/sale_information_validations.rb b/app/models/validations/sales/sale_information_validations.rb index 024272ac3..86295ceb3 100644 --- a/app/models/validations/sales/sale_information_validations.rb +++ b/app/models/validations/sales/sale_information_validations.rb @@ -54,6 +54,20 @@ module Validations::Sales::SaleInformationValidations end end + def validate_outright_sale_value_matches_mortgage_plus_deposit(record) + return unless record.saledate && record.form.start_year_after_2024? + return unless record.outright_sale? + return unless record.mortgage_used? && record.mortgage + return unless record.deposit && record.value + + if over_tolerance?(record.mortgage_and_deposit_total, record.value, 1) + %i[mortgageused mortgage value deposit].each do |field| + record.errors.add field, I18n.t("validations.sale_information.outright_sale_value", mortgage_and_deposit_total: record.field_formatted_as_currency("mortgage_and_deposit_total"), value: record.field_formatted_as_currency("value")) + end + record.errors.add :ownershipsch, :skip_bu_error, message: I18n.t("validations.sale_information.outright_sale_value", mortgage_and_deposit_total: record.field_formatted_as_currency("mortgage_and_deposit_total"), value: record.field_formatted_as_currency("value")) + end + end + def validate_basic_monthly_rent(record) return unless record.mrent && record.ownershipsch && record.type diff --git a/app/views/form/guidance/_finding_location.erb b/app/views/form/guidance/_finding_location.erb index 4107519b3..b50d1802a 100644 --- a/app/views/form/guidance/_finding_location.erb +++ b/app/views/form/guidance/_finding_location.erb @@ -1,4 +1,4 @@ <%= govuk_details(summary_text: "What is a location?") do %> -
A location is a postcode where supported housing is provided under a scheme. A scheme can have multiple locations, and a location can have multiple units at the same postcode.
+A location is a postcode area where supported housing is provided under a scheme. A scheme can have multiple locations, and a location can have multiple units at the same postcode.
<%= govuk_link_to("Read more about schemes and locations", scheme_changes_path) %>
<% end %> diff --git a/app/views/schemes/changes.html.erb b/app/views/schemes/changes.html.erb index 74c23f0f6..9040d7a05 100644 --- a/app/views/schemes/changes.html.erb +++ b/app/views/schemes/changes.html.erb @@ -9,7 +9,7 @@A supported housing scheme (also known as a ‘supported housing service’) provides shared or self-contained housing for a particular client group, for example younger or vulnerable people.
A location is a postcode where supported housing is provided under a scheme. A scheme can have multiple locations, and a location can have multiple units at the same postcode.
+A location is a postcode area where supported housing is provided under a scheme. A scheme can have multiple locations, and a location can have multiple units at the same postcode.
We have restructured the way we group supported housing scheme data.
diff --git a/config/locales/en.yml b/config/locales/en.yml index 6a105b213..d95187a49 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -554,7 +554,7 @@ en: reason: not_internal_transfer: "Answer cannot be ‘permanently decanted from another property owned by this landlord’ as you told us the source of referral for this tenancy was not an internal transfer" renewal_reason_needed: 'The reason for leaving must be "End of assured shorthold tenancy - no fault" or "End of fixed term tenancy - no fault" if the letting is a renewal' - renewal_reason_needed_2024: 'The reason for leaving must be "End of social housing tenancy - no fault", "End of social housing tenancy - evicted due to anti-social behaviour (ASB)", "End of social housing tenancy - evicted due to rent arrears" or "End of social housing tenancy - evicted for any other reason"' + renewal_reason_needed_2024: 'The reason for leaving must be "End of social or private sector tenancy - no fault", "End of social or private sector tenancy - evicted due to anti-social behaviour (ASB)", "End of social or private sector tenancy - evicted due to rent arrears" or "End of social or private sector tenancy - evicted for any other reason"' other_not_settled: "Please give the reason for the tenant leaving their last settled home. This is where they were living before they became homeless, were living in temporary accommodation or sleeping rough" condition_effects: no_choices: "You cannot answer this question as you told us nobody in the household has a physical or mental health condition (or other illness) expected to last 12 months or more" @@ -633,6 +633,7 @@ en: previous_property_type: property_type_bedsit: "A bedsit cannot have more than 1 bedroom" discounted_ownership_value: "The mortgage, deposit, and grant when added together is %{mortgage_deposit_and_grant_total}, and the purchase purchase price times by the discount is %{value_with_discount}. These figures should be the same" + outright_sale_value: "The mortgage and deposit when added together is %{mortgage_and_deposit_total}, and the purchase price is %{value}. These figures should be the same." monthly_rent: higher_than_expected: "Basic monthly rent must be between £0.00 and £9,999.00" grant: diff --git a/spec/models/form/lettings/questions/reason_renewal_spec.rb b/spec/models/form/lettings/questions/reason_renewal_spec.rb index 1f3da549d..6d507d1f6 100644 --- a/spec/models/form/lettings/questions/reason_renewal_spec.rb +++ b/spec/models/form/lettings/questions/reason_renewal_spec.rb @@ -71,10 +71,10 @@ RSpec.describe Form::Lettings::Questions::ReasonRenewal, type: :model do it "has the correct answer_options" do expect(question.answer_options).to eq({ - "50" => { "value" => "End of social housing tenancy - no fault" }, - "51" => { "value" => "End of social housing tenancy - evicted due to anti-social behaviour (ASB)" }, - "52" => { "value" => "End of social housing tenancy - evicted due to rent arrears" }, - "53" => { "value" => "End of social housing tenancy - evicted for any other reason" }, + "50" => { "value" => "End of social or private sector tenancy - no fault" }, + "51" => { "value" => "End of social or private sector tenancy - evicted due to anti-social behaviour (ASB)" }, + "52" => { "value" => "End of social or private sector tenancy - evicted due to rent arrears" }, + "53" => { "value" => "End of social or private sector tenancy - evicted for any other reason" }, "20" => { "value" => "Other" }, "47" => { "value" => "Tenant prefers not to say" }, "divider" => { "value" => true }, diff --git a/spec/models/form/lettings/questions/reason_spec.rb b/spec/models/form/lettings/questions/reason_spec.rb index b1ee8ddf8..ab7d4202d 100644 --- a/spec/models/form/lettings/questions/reason_spec.rb +++ b/spec/models/form/lettings/questions/reason_spec.rb @@ -105,10 +105,10 @@ RSpec.describe Form::Lettings::Questions::Reason, type: :model do it "has the correct answer_options" do expect(question.answer_options).to eq({ - "50" => { "value" => "End of social housing tenancy - no fault" }, - "51" => { "value" => "End of social housing tenancy - evicted due to anti-social behaviour (ASB)" }, - "52" => { "value" => "End of social housing tenancy - evicted due to rent arrears" }, - "53" => { "value" => "End of social housing tenancy - evicted for any other reason" }, + "50" => { "value" => "End of social or private sector tenancy - no fault" }, + "51" => { "value" => "End of social or private sector tenancy - evicted due to anti-social behaviour (ASB)" }, + "52" => { "value" => "End of social or private sector tenancy - evicted due to rent arrears" }, + "53" => { "value" => "End of social or private sector tenancy - evicted for any other reason" }, "1" => { "value" => "Permanently decanted from another property owned by this landlord" }, "2" => { "value" => "Left home country as a refugee" }, "45" => { "value" => "Discharged from prison" }, diff --git a/spec/models/validations/sales/sale_information_validations_spec.rb b/spec/models/validations/sales/sale_information_validations_spec.rb index 4859aca2d..969f2c16f 100644 --- a/spec/models/validations/sales/sale_information_validations_spec.rb +++ b/spec/models/validations/sales/sale_information_validations_spec.rb @@ -505,6 +505,94 @@ RSpec.describe Validations::Sales::SaleInformationValidations do end end + describe "#validate_outright_sale_value_matches_mortgage_plus_deposit" do + context "with a 2024 outright sale log" do + let(:record) { FactoryBot.build(:sales_log, value: 300_000, ownershipsch: 3, saledate: Time.zone.local(2024, 5, 1)) } + + context "when a mortgage is used" do + before do + record.mortgageused = 1 + end + + context "and the mortgage plus deposit match the value" do + before do + record.mortgage = 200_000 + record.deposit = 100_000 + end + + it "does not add errors" do + sale_information_validator.validate_outright_sale_value_matches_mortgage_plus_deposit(record) + expect(record.errors).to be_empty + end + end + + context "and the mortgage plus deposit don't match the value" do + before do + record.mortgage = 100_000 + record.deposit = 100_000 + end + + it "adds errors" do + sale_information_validator.validate_outright_sale_value_matches_mortgage_plus_deposit(record) + expect(record.errors["mortgageused"]).to include("The mortgage and deposit when added together is £200,000.00, and the purchase price is £300,000.00. These figures should be the same.") + expect(record.errors["mortgage"]).to include("The mortgage and deposit when added together is £200,000.00, and the purchase price is £300,000.00. These figures should be the same.") + expect(record.errors["deposit"]).to include("The mortgage and deposit when added together is £200,000.00, and the purchase price is £300,000.00. These figures should be the same.") + expect(record.errors["value"]).to include("The mortgage and deposit when added together is £200,000.00, and the purchase price is £300,000.00. These figures should be the same.") + expect(record.errors["ownershipsch"]).to include("The mortgage and deposit when added together is £200,000.00, and the purchase price is £300,000.00. These figures should be the same.") + end + end + + context "and deposit is not provided" do + before do + record.mortgage = 100_000 + record.deposit = nil + end + + it "does not add errors" do + sale_information_validator.validate_outright_sale_value_matches_mortgage_plus_deposit(record) + expect(record.errors).to be_empty + end + end + + context "and mortgage is not provided" do + before do + record.mortgage = nil + record.deposit = 100_000 + end + + it "does not add errors" do + sale_information_validator.validate_outright_sale_value_matches_mortgage_plus_deposit(record) + expect(record.errors).to be_empty + end + end + end + end + + context "with a 2024 log that is not an outright sale" do + let(:record) { FactoryBot.build(:sales_log, value: 300_000, ownershipsch: 2, saledate: Time.zone.local(2024, 5, 1)) } + + it "does not add errors" do + record.mortgageused = 1 + record.mortgage = 100_000 + record.deposit = 100_000 + sale_information_validator.validate_outright_sale_value_matches_mortgage_plus_deposit(record) + expect(record.errors).to be_empty + end + end + + context "with a 2023 outright sale log" do + let(:record) { FactoryBot.build(:sales_log, value: 300_000, ownershipsch: 3, saledate: Time.zone.local(2023, 5, 1)) } + + it "does not add errors" do + record.mortgageused = 1 + record.mortgage = 100_000 + record.deposit = 100_000 + sale_information_validator.validate_outright_sale_value_matches_mortgage_plus_deposit(record) + expect(record.errors).to be_empty + end + end + end + describe "#validate_basic_monthly_rent" do context "when within permitted bounds" do let(:record) { build(:sales_log, mrent: 9998, ownershipsch: 1, type: 2) } diff --git a/spec/requests/form_controller_spec.rb b/spec/requests/form_controller_spec.rb index ca558ddc4..bc491a78e 100644 --- a/spec/requests/form_controller_spec.rb +++ b/spec/requests/form_controller_spec.rb @@ -358,6 +358,12 @@ RSpec.describe FormController, type: :request do created_by: user, ) end + let!(:sales_log) do + create( + :sales_log, + created_by: user, + ) + end before do allow(user).to receive(:need_two_factor_authentication?).and_return(false) @@ -612,6 +618,104 @@ RSpec.describe FormController, type: :request do expect(page).to have_content("There is a problem") end end + + context "when allow_future_form_use? is enabled" do + before do + allow(FeatureToggle).to receive(:allow_future_form_use?).and_return(true) + end + + context "when the tenancy start date is out of the editable collection year" do + let(:page_id) { "tenancy_start_date" } + let(:params) do + { + id: lettings_log.id, + lettings_log: { + page: page_id, + "startdate(3i)" => 1, + "startdate(2i)" => 1, + "startdate(1i)" => 1, + }, + } + end + + it "redirects to the review page for the log" do + post "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}", params: params + follow_redirect! + follow_redirect! + follow_redirect! + expect(page).to have_content("Review lettings log") + end + end + + context "when the sale date is out of the editable collection year" do + let(:page_id) { "completion_date" } + let(:params) do + { + id: sales_log.id, + sales_log: { + page: page_id, + "saledate(3i)" => 1, + "saledate(2i)" => 1, + "saledate(1i)" => 1, + }, + } + end + + it "redirects to the review page for the log" do + post "/sales-logs/#{sales_log.id}/#{page_id.dasherize}", params: params + follow_redirect! + follow_redirect! + follow_redirect! + expect(page).to have_content("Review sales log") + end + end + end + + context "when allow_future_form_use? is disabled" do + before do + allow(FeatureToggle).to receive(:allow_future_form_use?).and_return(false) + end + + context "when the tenancy start date is out of the editable collection year" do + let(:page_id) { "tenancy_start_date" } + let(:params) do + { + id: lettings_log.id, + lettings_log: { + page: page_id, + "startdate(3i)" => 1, + "startdate(2i)" => 1, + "startdate(1i)" => 1, + }, + } + end + + it "validates the date correctly" do + post "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}", params: params + expect(page).to have_content("There is a problem") + end + end + + context "when the sale date is out of the editable collection year" do + let(:page_id) { "completion_date" } + let(:params) do + { + id: sales_log.id, + sales_log: { + page: page_id, + "saledate(3i)" => 1, + "saledate(2i)" => 1, + "saledate(1i)" => 1, + }, + } + end + + it "validates the date correctly" do + post "/sales-logs/#{sales_log.id}/#{page_id.dasherize}", params: params + expect(page).to have_content("There is a problem") + end + end + end end context "with invalid organisation answers" do diff --git a/spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb index 9549cfe12..d1c2fc761 100644 --- a/spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb @@ -1038,7 +1038,7 @@ RSpec.describe BulkUpload::Lettings::Year2024::RowParser do let(:attributes) { { bulk_upload:, field_98: "1", field_7: "1" } } it "is not permitted" do - expect(parser.errors[:field_98]).to include('The reason for leaving must be "End of social housing tenancy - no fault", "End of social housing tenancy - evicted due to anti-social behaviour (ASB)", "End of social housing tenancy - evicted due to rent arrears" or "End of social housing tenancy - evicted for any other reason"') + expect(parser.errors[:field_98]).to include('The reason for leaving must be "End of social or private sector tenancy - no fault", "End of social or private sector tenancy - evicted due to anti-social behaviour (ASB)", "End of social or private sector tenancy - evicted due to rent arrears" or "End of social or private sector tenancy - evicted for any other reason"') end end end