Browse Source

CLDC-4325: Check tests will run on go live day (#3250)

* CLDC-4325: Set date to 2026

* CLDC-4325: Fix sale information % tests

I'm not sure why I turned off strict post 2026, it's wanted in the same scenarios as before as shown by the failing tests

* CLDC-4325: Fix check answers spec

* CLDC-4325: Fix old rake task tests

* CLDC-4325: Fix sales soft validations

* CLDC-4325: Fix collection resources controller spec

* CLDC-4325: Only add noms and org to CSV row if prp

in most cases this was done by default but some tests construct an invalid log

* CLDC-4325: Fix lettings household validations spec

* CLDC-4221: Add 2026 collection deadlines

* CLDC-4325: Fix remaining model specs

* CLDC-4325: Fix form controller spec

* CLDC-4325: Fix hhmemb spec

* CLDC-4325: Fix duplicate logs controller spec

also introduce a new helper for a dynamic way of setting year

* CLDC-4325: Fix sales BU validator spec

* CLDC-4325: Fix check answers summary component

* CLDC-4325: Remove 2024 rake

* CLDC-4325: Use or later in all touched tests

* CLDC-4325: Fix guidance helper spec

* CLDC-4325: Run all tests now and in 2026

* CLDC-4325: Turn off 2026 override

* CLDC-4325: Add a corresponding return to timecop missing one

* CLDC-4325: Fix sales log spec in 2025

need to use let rather than let! so the timecop can run first

* CLDC-4325: Fix lettings log derived spec in 2025

timecop required for the start dates to work

* CLDC-4325: Turn the 2026 override on again

* fixup! CLDC-4325: Fix check answers summary component

* CLDC-4325: Delete recalculate_reasonpref_dontknow rake

only needed for 2024 logs

* fixup! CLDC-4325: Fix lettings household validations spec

Co-authored-by: Oscar Richardson <116292912+oscar-richardson-softwire@users.noreply.github.com>

* fixup! CLDC-4325: Fix sales log spec in 2025

* fixup! CLDC-4325: Fix sales log spec in 2025

* CLDC-4325: Make start controller spec not year specific

* CLDC-4325: Run tests in current day

* CLDC-4325: Run tests on 31st march

* CLDC-4325: Run tests on 2nd april

* CLDC-4325: Properly run tests on current day

* fixup! CLDC-4325: Fix sales soft validations

* fixup! CLDC-4325: Make start controller spec not year specific

* CLDC-4325: Run tests on 31st march

* CLDC-4325: Run tests on 1st april

* CLDC-4325: Run tests on 2nd april

* CLDC-4325: Revert time travel

---------

Co-authored-by: Oscar Richardson <116292912+oscar-richardson-softwire@users.noreply.github.com>
pull/3278/head
Samuel Young 3 months ago committed by GitHub
parent
commit
6a655e7210
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      app/helpers/bulk_upload/lettings_log_to_csv.rb
  2. 6
      app/helpers/collection_time_helper.rb
  3. 2
      app/models/validations/sales/sale_information_validations.rb
  4. 12
      lib/tasks/recalculate_invalid_reasonpref_dontknow.rake
  5. 207
      lib/tasks/update_manual_address_entry_selected_prexisting_logs.rake
  6. 8
      spec/components/check_answers_summary_list_card_component_spec.rb
  7. 6
      spec/features/form/check_answers_page_sales_logs_spec.rb
  8. 2
      spec/helpers/guidance_helper_spec.rb
  9. 77
      spec/lib/tasks/recalculate_invalid_reasonpref_dontknow_spec.rb
  10. 218
      spec/lib/tasks/update_manual_address_entry_selected_prexisting_logs_spec.rb
  11. 42
      spec/models/bulk_upload_spec.rb
  12. 11
      spec/models/form/lettings/questions/hhmemb_spec.rb
  13. 67
      spec/models/lettings_log_derived_fields_spec.rb
  14. 10
      spec/models/organisation_name_change_spec.rb
  15. 88
      spec/models/sales_log_spec.rb
  16. 177
      spec/models/validations/household_validations_spec.rb
  17. 113
      spec/models/validations/property_validations_spec.rb
  18. 260
      spec/models/validations/sales/sale_information_validations_spec.rb
  19. 33
      spec/models/validations/sales/soft_validations_spec.rb
  20. 18
      spec/requests/collection_resources_controller_spec.rb
  21. 1111
      spec/requests/duplicate_logs_controller_spec.rb
  22. 2
      spec/requests/form_controller_spec.rb
  23. 76
      spec/requests/start_controller_spec.rb
  24. 7
      spec/services/bulk_upload/sales/validator_spec.rb

4
app/helpers/bulk_upload/lettings_log_to_csv.rb

@ -240,8 +240,8 @@ class BulkUpload::LettingsLogToCsv
accessible_register, # 130
log.owning_organisation.la? ? log.referral_register : nil,
log.owning_organisation.prp? ? log.referral_register : nil,
log.referral_noms,
log.referral_org,
log.owning_organisation.prp? ? log.referral_noms : nil,
log.owning_organisation.prp? ? log.referral_org : nil,
net_income_known,
log.incfreq,
log.earnings,

6
app/helpers/collection_time_helper.rb

@ -94,4 +94,10 @@ module CollectionTimeHelper
available_dates = (start_date..end_date).to_a - [date.to_date]
available_dates.empty? ? nil : available_dates.sample
end
# useful for writing future tests that will also test the current time if it can or a future year if needed.
# stops tests being frozen on a specific year.
def collection_start_date_for_year_or_later(year)
collection_start_date_for_year([current_collection_start_year, year].max)
end
end

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

@ -82,7 +82,7 @@ module Validations::Sales::SaleInformationValidations
tolerance = record.value_with_discount_tolerance
if over_tolerance?(record.mortgage_deposit_and_grant_total, record.value_with_discount, tolerance, strict: !record.discount.nil? || record.form.start_year_2026_or_later?) && record.discounted_ownership_sale?
if over_tolerance?(record.mortgage_deposit_and_grant_total, record.value_with_discount, tolerance, strict: !record.discount.nil?) && record.discounted_ownership_sale?
deposit_and_grant_sentence = record.grant.present? ? ", cash deposit (#{record.field_formatted_as_currency('deposit')}), and grant (#{record.field_formatted_as_currency('grant')})" : " and cash deposit (#{record.field_formatted_as_currency('deposit')})"
discount_sentence = record.discount.present? ? " (#{record.field_formatted_as_currency('value')}) subtracted by the sum of the full purchase price (#{record.field_formatted_as_currency('value')}) multiplied by the percentage discount (#{record.discount}%)" : ""
%i[mortgageused mortgage value deposit discount grant].each do |field|

12
lib/tasks/recalculate_invalid_reasonpref_dontknow.rake

@ -1,12 +0,0 @@
desc "Bulk update logs with invalid rp_dontknow values"
task recalculate_invalid_rpdontknow: :environment do
validation_trigger_condition = "rp_dontknow = 1 AND (rp_homeless = 1 OR rp_insan_unsat = 1 OR rp_medwel = 1 OR rp_hardship = 1)"
LettingsLog.filter_by_year(2024).where(validation_trigger_condition).find_each do |log|
log.rp_dontknow = 0
unless log.save
Rails.logger.info "Could not save changes to lettings log #{log.id}"
end
end
end

207
lib/tasks/update_manual_address_entry_selected_prexisting_logs.rake

@ -1,207 +0,0 @@
namespace :bulk_update do
desc "Update logs with specific criteria and set manual_address_entry_selected to true"
task update_manual_address_entry_selected: :environment do
updated_lettings_logs_count = 0
lettings_postcode_fixed_count = 0
lettings_postcode_fixed_status_changed_count = 0
lettings_postcode_not_fixed_status_changed_count = 0
lettings_postcode_fixed_status_changed_ids = []
lettings_postcode_not_fixed_status_changed_ids = []
lettings_updated_without_issue = 0
updated_sales_logs_count = 0
sales_postcode_fixed_count = 0
sales_postcode_fixed_status_changed_count = 0
sales_postcode_not_fixed_status_changed_count = 0
sales_postcode_fixed_status_changed_ids = []
sales_postcode_not_fixed_status_changed_ids = []
sales_updated_without_issue = 0
lettings_logs = LettingsLog.filter_by_year(2024)
.where(status: %w[in_progress completed])
.where(needstype: 1, manual_address_entry_selected: false, uprn: nil)
.where("(address_line1 IS NOT NULL AND address_line1 != '') OR (address_line2 IS NOT NULL AND address_line2 != '') OR (town_or_city IS NOT NULL AND town_or_city != '') OR (county IS NOT NULL AND county != '') OR (postcode_full IS NOT NULL AND postcode_full != '')")
lettings_logs.find_each do |log|
status_pre_change = log.status
log.manual_address_entry_selected = true
if log.save
updated_lettings_logs_count += 1
Rails.logger.info "manual_address_entry_selected updated for lettings log #{log.id}"
else
Rails.logger.info "Could not save manual_address_entry_selected changes to lettings log #{log.id} : #{log.errors.full_messages.join(', ')}"
end
postcode_fixed = false
if log.postcode_full.nil? && log.address_line1 == log.address_line1_input
log.postcode_full = log.postcode_full_input
if log.save
lettings_postcode_fixed_count += 1
Rails.logger.info "postcode_full updated by address_line1_input for lettings log #{log.id}"
postcode_fixed = true
else
Rails.logger.info "Could not save postcode_full changes to lettings log #{log.id} : #{log.errors.full_messages.join(', ')}"
end
end
if log.postcode_full.nil? && log.creation_method == "bulk upload" && log.address_line1 == log.address_line1_as_entered
log.postcode_full = log.postcode_full_as_entered
if log.save
lettings_postcode_fixed_count += 1
Rails.logger.info "postcode_full updated by address_line1_as_entered for lettings log #{log.id}"
postcode_fixed = true
else
Rails.logger.info "Could not save postcode_full changes to lettings log #{log.id} : #{log.errors.full_messages.join(', ')}"
end
end
status_post_change = log.status
if status_pre_change != status_post_change
if postcode_fixed
lettings_postcode_fixed_status_changed_count += 1
lettings_postcode_fixed_status_changed_ids << log.id
else
lettings_postcode_not_fixed_status_changed_count += 1
lettings_postcode_not_fixed_status_changed_ids << log.id
end
else
lettings_updated_without_issue += 1
end
end
sales_logs = SalesLog.filter_by_year(2024)
.where(status: %w[in_progress completed])
.where(manual_address_entry_selected: false, uprn: nil)
.where("(address_line1 IS NOT NULL AND address_line1 != '') OR (address_line2 IS NOT NULL AND address_line2 != '') OR (town_or_city IS NOT NULL AND town_or_city != '') OR (county IS NOT NULL AND county != '') OR (postcode_full IS NOT NULL AND postcode_full != '')")
sales_logs.find_each do |log|
status_pre_change = log.status
log.manual_address_entry_selected = true
if log.save
updated_sales_logs_count += 1
Rails.logger.info "manual_address_entry_selected updated for sales log #{log.id}"
else
Rails.logger.info "Could not save manual_address_entry_selected changes to sales log #{log.id} : #{log.errors.full_messages.join(', ')}"
end
postcode_fixed = false
if log.postcode_full.nil? && log.address_line1 == log.address_line1_input
log.postcode_full = log.postcode_full_input
if log.save
sales_postcode_fixed_count += 1
Rails.logger.info "postcode_full updated by address_line1_input for sales log #{log.id}"
postcode_fixed = true
else
Rails.logger.info "Could not save postcode_full changes to sales log #{log.id} : #{log.errors.full_messages.join(', ')}"
end
end
if log.postcode_full.nil? && log.creation_method == "bulk upload" && log.address_line1 == log.address_line1_as_entered
log.postcode_full = log.postcode_full_as_entered
if log.save
sales_postcode_fixed_count += 1
Rails.logger.info "postcode_full updated by address_line1_as_entered for sales log #{log.id}"
postcode_fixed = true
else
Rails.logger.info "Could not save postcode_full changes to sales log #{log.id} : #{log.errors.full_messages.join(', ')}"
end
end
status_post_change = log.status
if status_pre_change != status_post_change
if postcode_fixed
sales_postcode_fixed_status_changed_count += 1
sales_postcode_fixed_status_changed_ids << log.id
else
sales_postcode_not_fixed_status_changed_count += 1
sales_postcode_not_fixed_status_changed_ids << log.id
end
else
sales_updated_without_issue += 1
end
end
puts "#{updated_lettings_logs_count} lettings logs were updated."
puts "#{lettings_updated_without_issue} lettings logs were updated without issue."
puts "#{lettings_postcode_fixed_count} lettings logs where postcode fix was applied."
puts "#{lettings_postcode_fixed_status_changed_count} lettings logs with postcode fix and status changed."
puts "#{lettings_postcode_not_fixed_status_changed_count} lettings logs without postcode fix and status changed."
puts "IDs of lettings logs with postcode fix and status changed: [#{lettings_postcode_fixed_status_changed_ids.join(', ')}]"
puts "IDs of lettings logs without postcode fix and status changed: [#{lettings_postcode_not_fixed_status_changed_ids.join(', ')}]"
lettings_postcode_fixed_org_counts = LettingsLog.where(id: lettings_postcode_fixed_status_changed_ids).group(:owning_organisation_id).count
lettings_postcode_fixed_org_counts.each do |org_id, count|
puts "Org #{org_id}: #{count} logs with postcode fix and status changed."
end
lettings_postcode_not_fixed_org_counts = LettingsLog.where(id: lettings_postcode_not_fixed_status_changed_ids).group(:owning_organisation_id).count
lettings_postcode_not_fixed_org_counts.each do |org_id, count|
puts "Org #{org_id}: #{count} logs without postcode fix and status changed."
end
puts "#{updated_sales_logs_count} sales logs were updated."
puts "#{sales_updated_without_issue} sales logs were updated without issue."
puts "#{sales_postcode_fixed_count} sales logs where postcode fix was applied."
puts "#{sales_postcode_fixed_status_changed_count} sales logs with postcode fix and status changed."
puts "#{sales_postcode_not_fixed_status_changed_count} sales logs without postcode fix and status changed."
puts "IDs of sales logs with postcode fix and status changed: [#{sales_postcode_fixed_status_changed_ids.join(', ')}]"
puts "IDs of sales logs without postcode fix and status changed: [#{sales_postcode_not_fixed_status_changed_ids.join(', ')}]"
sales_postcode_fixed_org_counts = SalesLog.where(id: sales_postcode_fixed_status_changed_ids).group(:owning_organisation_id).count
sales_postcode_fixed_org_counts.each do |org_id, count|
puts "Org #{org_id}: #{count} logs with postcode fix and status changed."
end
sales_postcode_not_fixed_org_counts = SalesLog.where(id: sales_postcode_not_fixed_status_changed_ids).group(:owning_organisation_id).count
sales_postcode_not_fixed_org_counts.each do |org_id, count|
puts "Org #{org_id}: #{count} logs without postcode fix and status changed."
end
end
desc "Find logs to fix and update postcode_full if conditions are met"
task update_postcode_full_preexisting_manual_entry_logs: :environment do
updated_count = 0
fixed_count = 0
not_updated_count = 0
not_updated_ids = []
updated_but_not_fixed_ids = []
logs_to_fix = LettingsLog.filter_by_year(2024).where(manual_address_entry_selected: true, uprn: nil, status: "in_progress", postcode_full: nil, updated_at: Time.zone.parse("2025-03-19 16:00:00")..Time.zone.parse("2025-03-19 17:00:00"))
logs_to_fix.find_each do |log|
previous_version = log.versions[-2]
previous_status = previous_version&.reify&.status
if log.address_line1 == log.address_line1_input
log.postcode_full = log.postcode_full_input
elsif log.creation_method == "bulk upload" && log.address_line1 == log.address_line1_as_entered
log.postcode_full = log.postcode_full_as_entered
end
if log.postcode_full.present?
if log.save
Rails.logger.info "Updated postcode_full for lettings log #{log.id}"
updated_count += 1
if log.status == previous_status
fixed_count += 1
else
updated_but_not_fixed_ids << log.id
end
else
Rails.logger.info "Could not save changes to lettings log #{log.id}: #{log.errors.full_messages.join(', ')}"
not_updated_count += 1
not_updated_ids << log.id
end
else
not_updated_count += 1
not_updated_ids << log.id
end
end
puts "#{updated_count} logs updated."
puts "#{fixed_count} logs fixed."
puts "#{not_updated_count} logs not updated."
puts "IDs of logs not updated: [#{not_updated_ids.join(', ')}]"
puts "IDs of logs updated but not fixed: [#{updated_but_not_fixed_ids.join(', ')}]"
end
end

8
spec/components/check_answers_summary_list_card_component_spec.rb

@ -6,7 +6,7 @@ RSpec.describe CheckAnswersSummaryListCardComponent, type: :component do
let(:rendered) { render_inline(component) }
let(:user) { create(:user) }
let(:log) { create(:lettings_log, :completed, sex1: "F", age2: 99, retirement_value_check: 1) }
let(:log) { create(:lettings_log, :completed) }
let(:subsection_id) { "household_characteristics" }
let(:subsection) { log.form.get_subsection(subsection_id) }
let(:questions) { subsection.applicable_questions(log) }
@ -21,8 +21,8 @@ RSpec.describe CheckAnswersSummaryListCardComponent, type: :component do
end
it "has the correct answer label for a question" do
sex1_question = questions.find { |q| q.id == "sex1" }
expect(component.get_answer_label(sex1_question)).to eq("Female")
question = questions.find { |q| q.id == "ecstat1" }
expect(component.get_answer_label(question)).to eq("Other")
end
context "when log was created via a bulk upload and has an unanswered question" do
@ -46,7 +46,7 @@ RSpec.describe CheckAnswersSummaryListCardComponent, type: :component do
let(:log) { create(:lettings_log, :in_progress) }
it "displays normal copy with muted colour" do
expect(rendered).to have_link(log.form.get_question("sex1", log).check_answer_prompt, href: "/lettings-logs/#{log.id}/lead-tenant-gender-identity?referrer=check_answers_new_answer", class: "govuk-link govuk-link--no-visited-state")
expect(rendered).to have_link(log.form.get_question("age1", log).check_answer_prompt, href: "/lettings-logs/#{log.id}/lead-tenant-age?referrer=check_answers_new_answer", class: "govuk-link govuk-link--no-visited-state")
end
end

6
spec/features/form/check_answers_page_sales_logs_spec.rb

@ -3,6 +3,8 @@ require_relative "helpers"
RSpec.describe "Sales Log Check Answers Page" do
include Helpers
include CollectionTimeHelper
let(:user) { FactoryBot.create(:user) }
let(:subsection) { "household-characteristics" }
let(:conditional_subsection) { "conditional-question" }
@ -13,7 +15,7 @@ RSpec.describe "Sales Log Check Answers Page" do
:completed,
assigned_to: user,
jointpur: 1,
hholdcount: 4,
hholdcount: current_collection_start_year >= 2026 ? 6 : 4,
)
end
@ -23,7 +25,7 @@ RSpec.describe "Sales Log Check Answers Page" do
:completed,
assigned_to: user,
jointpur: 2,
hholdcount: 4,
hholdcount: current_collection_start_year >= 2026 ? 6 : 4,
)
end

2
spec/helpers/guidance_helper_spec.rb

@ -14,7 +14,7 @@ RSpec.describe GuidanceHelper do
let(:log) { create(:sales_log, :shared_ownership_setup_complete, mortgageused: 1, staircase: 2) }
it "returns a link to the question with correct question number in brackets" do
expect(question_link("mortgage", log, log.assigned_to)).to eq("(<a class=\"govuk-link\" href=\"/sales-logs/#{log.id}/mortgage-amount-shared-ownership\">Q83</a>)")
expect(question_link("mortgage", log, log.assigned_to)).to match(/\(<a class="govuk-link" href="\/sales-logs\/#{log.id}\/mortgage-amount-shared-ownership">Q\d+<\/a>\)/)
end
end
end

77
spec/lib/tasks/recalculate_invalid_reasonpref_dontknow_spec.rb

@ -1,77 +0,0 @@
require "rails_helper"
require "rake"
RSpec.describe "recalculate_invalid_reasonpref_dontknow" do
include CollectionTimeHelper
subject(:task) { Rake::Task["recalculate_invalid_rpdontknow"] }
before do
Rake.application.rake_require("tasks/recalculate_invalid_reasonpref_dontknow")
Rake::Task.define_task(:environment)
task.reenable
Timecop.freeze(previous_collection_end_date)
end
after do
Timecop.return
end
let(:invalid_logs) { create_list(:lettings_log, 5, :completed, :ignore_validation_errors, reasonpref: 1, rp_dontknow: 1, rp_homeless: 1, rp_insan_unsat: rand(2), rp_medwel: rand(2), rp_hardship: rand(2), updated_at: Time.zone.local(2024, 4, 2, 12, 0, 0), startdate: Time.zone.local(2024, rand(4..12), rand(1..30))) }
let(:pre_2024_invalid_logs) do
create_list(:lettings_log, 5, :completed, reasonpref: 1, rp_dontknow: 1, rp_homeless: 1, rp_insan_unsat: rand(2), rp_medwel: rand(2), rp_hardship: rand(2)).each do |log|
log.startdate = Time.zone.local(rand(2021..2023), 4, 1)
log.save!(validate: false)
end
end
let(:valid_logs) { create_list(:lettings_log, 3, :completed, :ignore_validation_errors, reasonpref: 1, rp_dontknow: 0, rp_homeless: 1, rp_insan_unsat: 1, rp_medwel: rand(2), rp_hardship: rand(2), updated_at: Time.zone.local(2024, 4, 2, 12, 0, 0), startdate: Time.zone.local(2024, rand(4..12), rand(1..30))) }
it "updates the logs from 2024/25 with invalid rp_dontknow values" do
invalid_logs.each do |log|
expect(log.reasonpref).to eq(1)
expect(log.rp_dontknow).to eq(1)
expect(log.rp_homeless).to eq(1)
end
task.invoke
invalid_logs.each do |log|
log.reload
expect(log.reasonpref).to eq(1)
expect(log.rp_dontknow).to eq(0)
expect(log.rp_homeless).to eq(1)
expect(log.updated_at).not_to eq(Time.zone.local(2024, 4, 2, 12, 0, 0))
end
end
it "does not update the logs pre 2024 with invalid rp_dontknow values" do
pre_2024_invalid_logs.each do |log|
expect(log.reasonpref).to eq(1)
expect(log.rp_dontknow).to eq(1)
expect(log.rp_homeless).to eq(1)
end
task.invoke
pre_2024_invalid_logs.each do |log|
log.reload
expect(log.reasonpref).to eq(1)
expect(log.rp_dontknow).to eq(1)
expect(log.rp_homeless).to eq(1)
end
end
it "does not update the logs with valid rp_dontknow values" do
valid_logs.each do |log|
expect(log.reasonpref).to eq(1)
expect(log.rp_dontknow).to eq(0)
expect(log.rp_homeless).to eq(1)
expect(log.rp_insan_unsat).to eq(1)
end
task.invoke
valid_logs.each do |log|
log.reload
expect(log.reasonpref).to eq(1)
expect(log.rp_dontknow).to eq(0)
expect(log.rp_homeless).to eq(1)
expect(log.rp_insan_unsat).to eq(1)
expect(log.updated_at).to eq(Time.zone.local(2024, 4, 2, 12, 0, 0))
end
end
end

218
spec/lib/tasks/update_manual_address_entry_selected_prexisting_logs_spec.rb

@ -1,218 +0,0 @@
require "rails_helper"
require "rake"
RSpec.describe "update_manual_address_entry_selected_preexisting_logs_spec", type: :task do
include CollectionTimeHelper
before do
Rake.application.rake_require("tasks/update_manual_address_entry_selected_prexisting_logs")
Rake::Task.define_task(:environment)
task.reenable
Timecop.freeze(previous_collection_end_date)
end
after do
Timecop.return
end
describe "bulk_update:update_manual_address_entry_selected" do
let(:task) { Rake::Task["bulk_update:update_manual_address_entry_selected"] }
let(:lettings_log_uprn_entered) do
build(:lettings_log, :completed, startdate: Time.zone.local(2024, 6, 1), needstype: 1, manual_address_entry_selected: false)
end
let(:lettings_log_uprn_found) do
build(:lettings_log, :completed, startdate: Time.zone.local(2024, 9, 1), needstype: 1, manual_address_entry_selected: false, address_line1_input: "1 Test Street", postcode_full_input: "SW1 1AA")
end
let(:lettings_log_address_fields_not_entered) do
build(:lettings_log, :inprogress_without_address_fields, startdate: Time.zone.local(2024, 9, 1), needstype: 1)
end
let(:lettings_log_address_manually_entered) do
build(:lettings_log, :completed_without_uprn, startdate: Time.zone.local(2024, 12, 1), needstype: 1)
end
let(:sales_log_uprn_entered) do
build(:sales_log, :completed, saledate: Time.zone.local(2024, 12, 1), manual_address_entry_selected: false)
end
let(:sales_log_uprn_found) do
build(:sales_log, :completed, saledate: Time.zone.local(2024, 7, 1), manual_address_entry_selected: false, address_line1_input: "1 Test Street", postcode_full_input: "SW1 1AA")
end
let(:sales_log_address_fields_not_entered) do
build(:sales_log, :inprogress_without_address_fields, saledate: Time.zone.local(2024, 12, 30))
end
let(:sales_log_address_manually_entered) do
build(:sales_log, :completed_without_uprn, saledate: Time.zone.local(2024, 12, 30))
end
context "when running the task" do
context "when logs do not meet the criteria" do
before do
lettings_log_uprn_found.save!(validate: false)
lettings_log_uprn_entered.save!(validate: false)
lettings_log_address_fields_not_entered.save!(validate: false)
sales_log_uprn_found.save!(validate: false)
sales_log_uprn_entered.save!(validate: false)
sales_log_address_fields_not_entered.save!(validate: false)
end
it "does not update logs with a UPRN entered" do
task.invoke
lettings_log_uprn_entered.reload
sales_log_uprn_entered.reload
expect(lettings_log_uprn_entered.manual_address_entry_selected).to be false
expect(lettings_log_uprn_entered.uprn).to eq("10033558653")
expect(sales_log_uprn_entered.manual_address_entry_selected).to be false
expect(sales_log_uprn_entered.uprn).to eq("10033558653")
end
it "does not update logs with a UPRN found" do
task.invoke
lettings_log_uprn_found.reload
sales_log_uprn_found.reload
expect(lettings_log_uprn_found.manual_address_entry_selected).to be false
expect(lettings_log_uprn_found.uprn).to eq("10033558653")
expect(sales_log_uprn_found.manual_address_entry_selected).to be false
expect(sales_log_uprn_found.uprn).to eq("10033558653")
end
it "does not update logs with no UPRN or address fields entered" do
task.invoke
lettings_log_address_fields_not_entered.reload
sales_log_address_fields_not_entered.reload
expect(lettings_log_address_fields_not_entered.manual_address_entry_selected).to be false
expect(sales_log_address_fields_not_entered.manual_address_entry_selected).to be false
end
end
context "when logs do meet the criteria" do
before do
lettings_log_address_manually_entered.manual_address_entry_selected = false
lettings_log_address_manually_entered.save!(validate: false)
sales_log_address_manually_entered.manual_address_entry_selected = false
sales_log_address_manually_entered.save!(validate: false)
end
it "updates logs with an address manually entered" do
expect(lettings_log_address_manually_entered.manual_address_entry_selected).to be false
expect(lettings_log_address_manually_entered.address_line1).to eq("1 Test Street")
expect(lettings_log_address_manually_entered.address_line2).to eq("Testville")
expect(lettings_log_address_manually_entered.town_or_city).to eq("Testford")
expect(lettings_log_address_manually_entered.postcode_full).to eq("SW1 1AA")
expect(sales_log_address_manually_entered.manual_address_entry_selected).to be false
expect(sales_log_address_manually_entered.address_line1).to eq("1 Test Street")
expect(sales_log_address_manually_entered.address_line2).to eq("Testville")
expect(sales_log_address_manually_entered.town_or_city).to eq("Testford")
expect(sales_log_address_manually_entered.postcode_full).to eq("SW1 1AA")
task.invoke
lettings_log_address_manually_entered.reload
sales_log_address_manually_entered.reload
expect(lettings_log_address_manually_entered.manual_address_entry_selected).to be true
expect(lettings_log_address_manually_entered.address_line1).to eq("1 Test Street")
expect(lettings_log_address_manually_entered.address_line2).to eq("Testville")
expect(lettings_log_address_manually_entered.town_or_city).to eq("Testford")
expect(lettings_log_address_manually_entered.postcode_full).to eq("SW1 1AA")
expect(sales_log_address_manually_entered.manual_address_entry_selected).to be true
expect(sales_log_address_manually_entered.address_line1).to eq("1 Test Street")
expect(sales_log_address_manually_entered.address_line2).to eq("Testville")
expect(sales_log_address_manually_entered.town_or_city).to eq("Testford")
expect(sales_log_address_manually_entered.postcode_full).to eq("SW1 1AA")
end
end
end
end
describe "bulk_update:update_postcode_full_preexisting_manual_entry_logs" do
let(:task) { Rake::Task["bulk_update:update_postcode_full_preexisting_manual_entry_logs"] }
let(:lettings_log_to_fix) do
build(:lettings_log, :inprogress_without_address_fields, startdate: Time.zone.local(2024, 6, 1), updated_at: Time.zone.parse("2025-03-19 16:30:00"))
end
let(:bu_lettings_log_to_fix) do
build(:lettings_log, :inprogress_without_address_fields, startdate: Time.zone.local(2024, 6, 1), creation_method: "bulk upload", updated_at: Time.zone.parse("2025-03-19 16:30:00"))
end
let(:lettings_log_not_to_fix) do
build(:lettings_log, :inprogress_without_address_fields, startdate: Time.zone.local(2024, 6, 1), updated_at: Time.zone.parse("2025-03-19 15:30:00"))
end
before do
lettings_log_to_fix.manual_address_entry_selected = true
lettings_log_to_fix.address_line1 = "1 Test Street"
lettings_log_to_fix.address_line2 = "Testville"
lettings_log_to_fix.town_or_city = "Testford"
lettings_log_to_fix.postcode_full = nil
lettings_log_to_fix.address_line1_input = "1 Test Street"
lettings_log_to_fix.postcode_full_input = "SW1 2BB"
lettings_log_to_fix.save!(validate: false)
bu_lettings_log_to_fix.manual_address_entry_selected = true
bu_lettings_log_to_fix.address_line1 = "1 Test Street"
bu_lettings_log_to_fix.address_line2 = "Testville"
bu_lettings_log_to_fix.town_or_city = "Testford"
bu_lettings_log_to_fix.postcode_full = nil
bu_lettings_log_to_fix.address_line1_as_entered = "1 Test Street"
bu_lettings_log_to_fix.postcode_full_as_entered = "SW1 2BB"
bu_lettings_log_to_fix.save!(validate: false)
lettings_log_not_to_fix.postcode_full = nil
lettings_log_not_to_fix.save!(validate: false)
end
context "when running the task" do
it "updates logs that meet the criteria" do
expect(lettings_log_to_fix.postcode_full).to be_nil
expect(lettings_log_to_fix.address_line1).to eq("1 Test Street")
expect(lettings_log_to_fix.address_line2).to eq("Testville")
expect(lettings_log_to_fix.town_or_city).to eq("Testford")
expect(lettings_log_to_fix.address_line1_input).to eq("1 Test Street")
expect(lettings_log_to_fix.postcode_full_input).to eq("SW1 2BB")
expect(bu_lettings_log_to_fix.postcode_full).to be_nil
expect(bu_lettings_log_to_fix.address_line1_input).to be_nil
expect(bu_lettings_log_to_fix.address_line1).to eq("1 Test Street")
expect(bu_lettings_log_to_fix.address_line2).to eq("Testville")
expect(bu_lettings_log_to_fix.town_or_city).to eq("Testford")
expect(bu_lettings_log_to_fix.address_line1_as_entered).to eq("1 Test Street")
expect(bu_lettings_log_to_fix.postcode_full_as_entered).to eq("SW1 2BB")
task.invoke
lettings_log_to_fix.reload
bu_lettings_log_to_fix.reload
expect(lettings_log_to_fix.postcode_full).to eq(lettings_log_to_fix.postcode_full_input)
expect(lettings_log_to_fix.postcode_full).to eq("SW1 2BB")
expect(bu_lettings_log_to_fix.postcode_full).to eq(bu_lettings_log_to_fix.postcode_full_as_entered)
expect(bu_lettings_log_to_fix.postcode_full).to eq("SW1 2BB")
end
it "does not update logs that do not meet the criteria" do
task.invoke
lettings_log_not_to_fix.reload
expect(lettings_log_not_to_fix.postcode_full).to be_nil
end
end
end
end

42
spec/models/bulk_upload_spec.rb

@ -22,19 +22,45 @@ RSpec.describe BulkUpload, type: :model do
end
describe "value check clearing" do
context "with a lettings log bulk upload" do
let(:log) { build(:lettings_log, startdate: current_collection_start_date, bulk_upload:) }
context "when 2025", metadata: { year: 25 } do
let(:startdate) { collection_start_date_for_year(2025) }
let(:saledate) { collection_start_date_for_year(2025) }
it "has the correct number of value checks to be set as confirmed" do
expect(bulk_upload.fields_to_confirm(log)).to match_array %w[rent_value_check void_date_value_check major_repairs_date_value_check pregnancy_value_check retirement_value_check referral_value_check net_income_value_check scharge_value_check pscharge_value_check supcharg_value_check multiple_partners_value_check partner_under_16_value_check reasonother_value_check]
context "with a lettings log bulk upload" do
let(:log) { build(:lettings_log, startdate:, bulk_upload:) }
it "has the correct number of value checks to be set as confirmed" do
expect(bulk_upload.fields_to_confirm(log)).to match_array %w[rent_value_check void_date_value_check major_repairs_date_value_check pregnancy_value_check retirement_value_check referral_value_check net_income_value_check scharge_value_check pscharge_value_check supcharg_value_check multiple_partners_value_check partner_under_16_value_check reasonother_value_check]
end
end
context "with a sales log bulk upload" do
let(:log) { build(:sales_log, saledate:, bulk_upload:) }
it "has the correct number of value checks to be set as confirmed" do
expect(bulk_upload.fields_to_confirm(log)).to match_array %w[value_value_check monthly_charges_value_check percentage_discount_value_check income1_value_check income2_value_check combined_income_value_check retirement_value_check old_persons_shared_ownership_value_check buyer_livein_value_check wheel_value_check mortgage_value_check savings_value_check deposit_value_check staircase_bought_value_check stairowned_value_check hodate_check shared_ownership_deposit_value_check extrabor_value_check grant_value_check discounted_sale_value_check deposit_and_mortgage_value_check multiple_partners_value_check partner_under_16_value_check]
end
end
end
context "with a sales log bulk upload" do
let(:log) { build(:sales_log, saledate: current_collection_start_date, bulk_upload:) }
context "when 2026 or later", metadata: { year: 26 } do
let(:startdate) { collection_start_date_for_year_or_later(2026) }
let(:saledate) { collection_start_date_for_year_or_later(2026) }
it "has the correct number of value checks to be set as confirmed" do
expect(bulk_upload.fields_to_confirm(log)).to match_array %w[value_value_check monthly_charges_value_check percentage_discount_value_check income1_value_check income2_value_check combined_income_value_check retirement_value_check old_persons_shared_ownership_value_check buyer_livein_value_check wheel_value_check mortgage_value_check savings_value_check deposit_value_check staircase_bought_value_check stairowned_value_check hodate_check shared_ownership_deposit_value_check extrabor_value_check grant_value_check discounted_sale_value_check deposit_and_mortgage_value_check multiple_partners_value_check partner_under_16_value_check]
context "with a lettings log bulk upload" do
let(:log) { build(:lettings_log, startdate:, bulk_upload:) }
it "has the correct number of value checks to be set as confirmed" do
expect(bulk_upload.fields_to_confirm(log)).to match_array %w[rent_value_check void_date_value_check major_repairs_date_value_check pregnancy_value_check retirement_value_check net_income_value_check scharge_value_check pscharge_value_check supcharg_value_check reasonother_value_check tenancyother_value_check working_situation_illness_check]
end
end
context "with a sales log bulk upload" do
let(:log) { build(:sales_log, saledate:, bulk_upload:) }
it "has the correct number of value checks to be set as confirmed" do
expect(bulk_upload.fields_to_confirm(log)).to match_array %w[value_value_check monthly_charges_value_check percentage_discount_value_check income1_value_check income2_value_check combined_income_value_check retirement_value_check old_persons_shared_ownership_value_check buyer_livein_value_check wheel_value_check mortgage_value_check savings_value_check deposit_value_check staircase_bought_value_check stairowned_value_check hodate_check shared_ownership_deposit_value_check extrabor_value_check grant_value_check discounted_sale_value_check deposit_and_mortgage_value_check multiple_partners_value_check partner_under_16_value_check]
end
end
end
end

11
spec/models/form/lettings/questions/hhmemb_spec.rb

@ -8,8 +8,9 @@ RSpec.describe Form::Lettings::Questions::Hhmemb, type: :model do
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
let(:subsection) { instance_double(Form::Subsection) }
let(:start_year_2026_or_later?) { false }
let(:form) { instance_double(Form, start_date: current_collection_start_date, start_year_2026_or_later?: start_year_2026_or_later?) }
let(:start_year_2026_or_later?) { true }
let(:start_date) { current_collection_start_date }
let(:form) { instance_double(Form, start_date:, start_year_2026_or_later?: start_year_2026_or_later?) }
before do
allow(page).to receive(:subsection).and_return(subsection)
@ -37,6 +38,9 @@ RSpec.describe Form::Lettings::Questions::Hhmemb, type: :model do
end
context "when in 2025", { year: 25 } do
let(:start_year_2026_or_later?) { false }
let(:start_date) { collection_start_date_for_year(2025) }
it "does not have check answers card title" do
expect(question.check_answers_card_title).to be_nil
end
@ -48,13 +52,14 @@ RSpec.describe Form::Lettings::Questions::Hhmemb, type: :model do
context "when in 2026", { year: 26 } do
let(:start_year_2026_or_later?) { true }
let(:start_date) { collection_start_date_for_year(2026) }
it "has correct check answers card title" do
expect(question.check_answers_card_title).to eq("Household")
end
it "has the correct question number" do
expect(question.question_number).to eq(30)
expect(question.question_number).to eq(29)
end
end
end

67
spec/models/lettings_log_derived_fields_spec.rb

@ -1068,26 +1068,61 @@ RSpec.describe LettingsLog, type: :model do
describe "variables dependent on whether a letting is a renewal" do
let(:organisation) { create(:organisation) }
let(:user) { create(:user, organisation:) }
let(:startdate) { Time.zone.today }
let(:persisted_renewal_lettings_log) { create(:lettings_log, :setup_completed, startdate:, renewal: 1, assigned_to: user) }
it "derives waityear offered referral first_time_property_let_as_social_housing rsnvac when renewal" do
log.renewal = 1
expect { log.set_derived_fields! }
.to change(log, :waityear).to(2)
.and change(log, :offered).to(0)
.and change(log, :referral).to(1)
.and change(log, :first_time_property_let_as_social_housing).to(0)
.and change(log, :rsnvac).to(14)
before do
Timecop.travel(startdate)
Singleton.__init__(FormHandler)
end
after do
Timecop.return
end
context "when 2025", metadata: { year: 25 } do
let(:startdate) { collection_start_date_for_year(2025) }
it "derives waityear offered referral first_time_property_let_as_social_housing rsnvac when renewal" do
log.renewal = 1
expect { log.set_derived_fields! }
.to change(log, :waityear).to(2)
.and change(log, :offered).to(0)
.and change(log, :referral).to(1)
.and change(log, :first_time_property_let_as_social_housing).to(0)
.and change(log, :rsnvac).to(14)
end
it "clears waityear offered referral first_time_property_let_as_social_housing rsnvac when not a renewal" do
expect { persisted_renewal_lettings_log.update!(renewal: 0) }
.to change(persisted_renewal_lettings_log, :waityear).from(2).to(nil)
.and change(persisted_renewal_lettings_log, :offered).from(0).to(nil)
.and change(persisted_renewal_lettings_log, :referral).from(1).to(nil)
.and change(persisted_renewal_lettings_log, :first_time_property_let_as_social_housing).from(0).to(nil)
.and change(persisted_renewal_lettings_log, :rsnvac).from(14).to(nil)
end
end
it "clears waityear offered referral first_time_property_let_as_social_housing rsnvac when not a renewal" do
expect { persisted_renewal_lettings_log.update!(renewal: 0) }
.to change(persisted_renewal_lettings_log, :waityear).from(2).to(nil)
.and change(persisted_renewal_lettings_log, :offered).from(0).to(nil)
.and change(persisted_renewal_lettings_log, :referral).from(1).to(nil)
.and change(persisted_renewal_lettings_log, :first_time_property_let_as_social_housing).from(0).to(nil)
.and change(persisted_renewal_lettings_log, :rsnvac).from(14).to(nil)
context "when 2026 or later", metadata: { year: 26 } do
let(:startdate) { collection_start_date_for_year_or_later(2026) }
it "derives waityear offered referral first_time_property_let_as_social_housing rsnvac when renewal" do
log.renewal = 1
expect { log.set_derived_fields! }
.to change(log, :waityear).to(2)
.and change(log, :offered).to(0)
.and change(log, :referral_register).to(1)
.and change(log, :first_time_property_let_as_social_housing).to(0)
.and change(log, :rsnvac).to(14)
end
it "clears waityear offered referral first_time_property_let_as_social_housing rsnvac when not a renewal" do
expect { persisted_renewal_lettings_log.update!(renewal: 0) }
.to change(persisted_renewal_lettings_log, :waityear).from(2).to(nil)
.and change(persisted_renewal_lettings_log, :offered).from(0).to(nil)
.and change(persisted_renewal_lettings_log, :referral_register).from(1).to(nil)
.and change(persisted_renewal_lettings_log, :first_time_property_let_as_social_housing).from(0).to(nil)
.and change(persisted_renewal_lettings_log, :rsnvac).from(14).to(nil)
end
end
describe "deriving voiddate from startdate" do

10
spec/models/organisation_name_change_spec.rb

@ -30,13 +30,13 @@ RSpec.describe OrganisationNameChange, type: :model do
it "is invalid if name is the same as the current name on the change date" do
create(:organisation_name_change, organisation:, name: "New Name", startdate: 1.day.ago)
name_change = build(:organisation_name_change, organisation:, name: "New Name", startdate: Time.zone.now)
name_change = build(:organisation_name_change, organisation:, name: "New Name", startdate: Time.zone.today)
expect(name_change).not_to be_valid
expect(name_change.errors[:name]).to include(I18n.t("validations.organisation.name_changes.name.must_be_different"))
end
it "is invalid if startdate is after the organisation's merge date" do
organisation.update!(merge_date: Time.zone.now)
organisation.update!(merge_date: Time.zone.today)
name_change = build(:organisation_name_change, organisation:, immediate_change: false, startdate: Time.zone.tomorrow)
expect(name_change).not_to be_valid
expect(name_change.errors[:startdate]).to include(I18n.t("validations.organisation.name_changes.startdate.must_be_before_merge_date", merge_date: organisation.merge_date.to_formatted_s(:govuk_date)))
@ -54,12 +54,12 @@ RSpec.describe OrganisationNameChange, type: :model do
it "returns changes before a specific date" do
name_change = create(:organisation_name_change, organisation:, startdate: 1.day.ago)
expect(described_class.before_date(Time.zone.now)).to include(name_change)
expect(described_class.before_date(Time.zone.today)).to include(name_change)
end
it "returns changes after a specific date" do
name_change = create(:organisation_name_change, organisation:, startdate: 2.days.from_now)
expect(described_class.after_date(Time.zone.now)).to include(name_change)
expect(described_class.after_date(Time.zone.today)).to include(name_change)
end
end
@ -84,7 +84,7 @@ RSpec.describe OrganisationNameChange, type: :model do
describe "#includes_date?" do
it "returns true if the date is within the change period" do
name_change = create(:organisation_name_change, organisation:, startdate: 1.day.ago)
expect(name_change.includes_date?(Time.zone.now)).to be true
expect(name_change.includes_date?(Time.zone.today)).to be true
end
it "returns false if the date is outside the change period" do

88
spec/models/sales_log_spec.rb

@ -684,12 +684,13 @@ RSpec.describe SalesLog, type: :model do
end
context "when deriving household variables" do
let!(:sales_log) do
let(:sales_log) do
create(
:sales_log,
:completed,
saledate:,
jointpur: 1,
hholdcount: 4,
hholdcount:,
details_known_3: 1,
details_known_4: 1,
details_known_5: 1,
@ -711,30 +712,75 @@ RSpec.describe SalesLog, type: :model do
)
end
it "correctly derives and saves hhmemb" do
record_from_db = described_class.find(sales_log.id)
expect(record_from_db["hhmemb"]).to eq(6)
before do
Timecop.travel(saledate)
Singleton.__init__(FormHandler)
end
it "correctly derives and saves hhmemb if it's a joint purchase" do
sales_log.update!(jointpur: 2, jointmore: 2)
record_from_db = described_class.find(sales_log.id)
expect(record_from_db["hhmemb"]).to eq(5)
after do
Timecop.return
end
it "correctly derives and saves totchild" do
record_from_db = described_class.find(sales_log.id)
expect(record_from_db["totchild"]).to eq(2)
end
context "when 2025", metadata: { year: 25 } do
let(:saledate) { collection_start_date_for_year(2025) }
let(:hholdcount) { 4 }
it "correctly derives and saves hhmemb if it's a joint purchase" do
record_from_db = described_class.find(sales_log.id)
expect(record_from_db["hhmemb"]).to eq(6)
end
it "correctly derives and saves hhmemb if it's not a joint purchase" do
sales_log.update!(jointpur: 2)
record_from_db = described_class.find(sales_log.id)
expect(record_from_db["hhmemb"]).to eq(5)
end
it "correctly derives and saves totchild" do
record_from_db = described_class.find(sales_log.id)
expect(record_from_db["totchild"]).to eq(2)
end
it "correctly derives and saves totadult" do
record_from_db = described_class.find(sales_log.id)
expect(record_from_db["totadult"]).to eq(4)
end
it "correctly derives and saves totadult" do
record_from_db = described_class.find(sales_log.id)
expect(record_from_db["totadult"]).to eq(4)
it "correctly derives and saves hhtype" do
record_from_db = described_class.find(sales_log.id)
expect(record_from_db["hhtype"]).to eq(9)
end
end
it "correctly derives and saves hhtype" do
record_from_db = described_class.find(sales_log.id)
expect(record_from_db["hhtype"]).to eq(9)
context "when 2026 or later", metadata: { year: 26 } do
let(:saledate) { collection_start_date_for_year_or_later(2026) }
let(:hholdcount) { 6 }
it "correctly derives and saves hhmemb if it's a joint purchase" do
record_from_db = described_class.find(sales_log.id)
expect(record_from_db["hhmemb"]).to eq(6)
end
it "correctly derives and saves hhmemb if it's not a joint purchase" do
sales_log.update!(jointpur: 2)
record_from_db = described_class.find(sales_log.id)
expect(record_from_db["hhmemb"]).to eq(6)
end
it "correctly derives and saves totchild" do
record_from_db = described_class.find(sales_log.id)
expect(record_from_db["totchild"]).to eq(2)
end
it "correctly derives and saves totadult" do
record_from_db = described_class.find(sales_log.id)
expect(record_from_db["totadult"]).to eq(4)
end
it "correctly derives and saves hhtype" do
record_from_db = described_class.find(sales_log.id)
expect(record_from_db["hhtype"]).to eq(9)
end
end
end
@ -806,10 +852,10 @@ RSpec.describe SalesLog, type: :model do
end
describe "expected_shared_ownership_deposit_value" do
let!(:completed_sales_log) { create(:sales_log, :completed, ownershipsch: 1, type: 2, value: 1000, equity: 50, staircase: 1) }
let(:completed_sales_log) { create(:sales_log, :completed, ownershipsch: 1, type: 2, value: 15_000, equity: 50, staircase: 1) }
it "is set to completed for a completed sales log" do
expect(completed_sales_log.expected_shared_ownership_deposit_value).to eq(500)
expect(completed_sales_log.expected_shared_ownership_deposit_value).to eq(7500)
end
end

177
spec/models/validations/household_validations_spec.rb

@ -6,7 +6,7 @@ RSpec.describe Validations::HouseholdValidations do
subject(:household_validator) { validator_class.new }
let(:validator_class) { Class.new { include Validations::HouseholdValidations } }
let(:startdate) { Time.zone.now }
let(:startdate) { current_collection_start_date }
let(:record) { FactoryBot.build(:lettings_log, :setup_completed, startdate:, assigned_to: create(:user)) }
describe "reasonable preference validations" do
@ -117,52 +117,57 @@ RSpec.describe Validations::HouseholdValidations do
end
end
context "when referral is not internal transfer" do
it "can be permanently decanted from another property owned by this landlord" do
record.reason = 1
record.referral_type = 1
record.referral = 2
household_validator.validate_reason_for_leaving_last_settled_home(record)
expect(record.errors["reason"])
.to be_empty
expect(record.errors["referral"])
.to be_empty
expect(record.errors["referral_type"])
.to be_empty
end
end
context "when 2025", metadata: { year: 25 } do
# post 2025 we don't ask referral and referral_type anymore
let(:startdate) { collection_start_date_for_year(2025) }
context "when referral is internal transfer" do
it "can be permanently decanted from another property owned by this landlord" do
record.reason = 1
record.referral_type = 3
record.referral = 1
household_validator.validate_reason_for_leaving_last_settled_home(record)
expect(record.errors["reason"])
.to be_empty
expect(record.errors["referral"])
.to be_empty
expect(record.errors["referral_type"])
.to be_empty
context "when referral is not internal transfer" do
it "can be permanently decanted from another property owned by this landlord" do
record.reason = 1
record.referral_type = 1
record.referral = 2
household_validator.validate_reason_for_leaving_last_settled_home(record)
expect(record.errors["reason"])
.to be_empty
expect(record.errors["referral"])
.to be_empty
expect(record.errors["referral_type"])
.to be_empty
end
end
it "cannot have a PRP as landlord and Housing situation before this letting cannot be LA general needs" do
record.owning_organisation.provider_type = "PRP"
record.prevten = 30
record.referral_type = 3
record.referral = 1
household_validator.validate_referral(record)
expect(record.errors["referral"])
.to include(match(I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer")))
expect(record.errors["prevten"])
.to include(match(I18n.t("validations.lettings.household.prevten.la_general_needs.internal_transfer")))
context "when referral is internal transfer" do
it "can be permanently decanted from another property owned by this landlord" do
record.reason = 1
record.referral_type = 3
record.referral = 1
household_validator.validate_reason_for_leaving_last_settled_home(record)
expect(record.errors["reason"])
.to be_empty
expect(record.errors["referral"])
.to be_empty
expect(record.errors["referral_type"])
.to be_empty
end
record.prevten = 31
household_validator.validate_referral(record)
expect(record.errors["referral"])
.to include(match(I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer")))
expect(record.errors["prevten"])
.to include(match(I18n.t("validations.lettings.household.prevten.la_general_needs.internal_transfer")))
it "cannot have a PRP as landlord and Housing situation before this letting cannot be LA general needs" do
record.owning_organisation.provider_type = "PRP"
record.prevten = 30
record.referral_type = 3
record.referral = 1
household_validator.validate_referral(record)
expect(record.errors["referral"])
.to include(match(I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer")))
expect(record.errors["prevten"])
.to include(match(I18n.t("validations.lettings.household.prevten.la_general_needs.internal_transfer")))
record.prevten = 31
household_validator.validate_referral(record)
expect(record.errors["referral"])
.to include(match(I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer")))
expect(record.errors["prevten"])
.to include(match(I18n.t("validations.lettings.household.prevten.la_general_needs.internal_transfer")))
end
end
end
end
@ -445,54 +450,58 @@ RSpec.describe Validations::HouseholdValidations do
end
describe "referral validations" do
context "when homelessness is assessed" do
it "can be internal transfer" do
record.homeless = 11
record.referral_type = 3
record.referral = 1
household_validator.validate_referral(record)
expect(record.errors["referral"]).to be_empty
expect(record.errors["referral_type"]).to be_empty
expect(record.errors["homeless"]).to be_empty
end
context "when start year is 2025", metadata: { year: 25 } do
let(:startdate) { collection_start_date_for_year(2025) }
it "can be non internal transfer" do
record.owning_organisation.provider_type = "PRP"
record.homeless = 0
record.referral_type = 2
record.referral = 3
household_validator.validate_referral(record)
expect(record.errors["referral"]).to be_empty
expect(record.errors["referral_type"]).to be_empty
expect(record.errors["homeless"]).to be_empty
end
end
context "when homelessness is assessed" do
it "can be internal transfer" do
record.homeless = 11
record.referral_type = 3
record.referral = 1
household_validator.validate_referral(record)
expect(record.errors["referral"]).to be_empty
expect(record.errors["referral_type"]).to be_empty
expect(record.errors["homeless"]).to be_empty
end
context "when homelessness is other" do
it "cannot be internal transfer" do
record.referral_type = 3
record.referral = 1
record.homeless = 7
household_validator.validate_referral(record)
expect(record.errors["referral"]).to be_empty
expect(record.errors["referral_type"]).to be_empty
expect(record.errors["homeless"]).to be_empty
it "can be non internal transfer" do
record.owning_organisation.provider_type = "PRP"
record.homeless = 0
record.referral_type = 2
record.referral = 3
household_validator.validate_referral(record)
expect(record.errors["referral"]).to be_empty
expect(record.errors["referral_type"]).to be_empty
expect(record.errors["homeless"]).to be_empty
end
end
it "can be non internal transfer" do
record.owning_organisation.provider_type = "PRP"
record.referral_type = 2
record.referral = 3
record.homeless = 1
household_validator.validate_referral(record)
expect(record.errors["referral"]).to be_empty
expect(record.errors["referral_type"]).to be_empty
expect(record.errors["homeless"]).to be_empty
context "when homelessness is other" do
it "cannot be internal transfer" do
record.referral_type = 3
record.referral = 1
record.homeless = 7
household_validator.validate_referral(record)
expect(record.errors["referral"]).to be_empty
expect(record.errors["referral_type"]).to be_empty
expect(record.errors["homeless"]).to be_empty
end
it "can be non internal transfer" do
record.owning_organisation.provider_type = "PRP"
record.referral_type = 2
record.referral = 3
record.homeless = 1
household_validator.validate_referral(record)
expect(record.errors["referral"]).to be_empty
expect(record.errors["referral_type"]).to be_empty
expect(record.errors["homeless"]).to be_empty
end
end
end
context "when start year is 2026" do
let(:startdate) { collection_start_date_for_year(2026) }
context "when start year is 2026 or later", metadata: { year: 26 } do
let(:startdate) { collection_start_date_for_year_or_later(2026) }
[
{

113
spec/models/validations/property_validations_spec.rb

@ -1,6 +1,8 @@
require "rails_helper"
RSpec.describe Validations::PropertyValidations do
include CollectionTimeHelper
subject(:property_validator) { property_validator_class.new }
let(:property_validator_class) { Class.new { include Validations::PropertyValidations } }
@ -316,42 +318,65 @@ RSpec.describe Validations::PropertyValidations do
end
end
context "and the local authority is active for supported housing log" do
let(:location) { create(:location, location_code: la_ecode_active) }
let(:log) { build(:lettings_log, :completed, needstype: 2, location:) }
context "when 2025", metadata: { year: 25 } do
let(:startdate) { collection_start_date_for_year(2025) }
it "does not add an error" do
property_validator.validate_la_is_active(log)
expect(log.errors["scheme_id"]).to be_empty
expect(log.errors["location_id"]).to be_empty
expect(log.errors["startdate"]).to be_empty
expect(log.errors["la"]).to be_empty
expect(log.errors["postcode_full"]).to be_empty
expect(log.errors["uprn"]).to be_empty
expect(log.errors["uprn_selection"]).to be_empty
context "and the local authority is active for supported housing log" do
let(:location) { create(:location, location_code: la_ecode_active) }
let(:log) { build(:lettings_log, :completed, startdate:, needstype: 2, location:) }
it "does not add an error" do
property_validator.validate_la_is_active(log)
expect(log.errors["scheme_id"]).to be_empty
expect(log.errors["location_id"]).to be_empty
expect(log.errors["startdate"]).to be_empty
expect(log.errors["la"]).to be_empty
expect(log.errors["postcode_full"]).to be_empty
expect(log.errors["uprn"]).to be_empty
expect(log.errors["uprn_selection"]).to be_empty
end
end
end
context "and the local authority is inactive for supported housing log" do
let(:location) { create(:location, location_code: la_ecode_inactive) }
let(:log) { build(:lettings_log, :completed, needstype: 2, location:) }
context "and the local authority is inactive for supported housing log" do
let(:location) { create(:location, location_code: la_ecode_inactive) }
let(:log) { build(:lettings_log, :completed, startdate:, needstype: 2, location:) }
context "and the inactive local authority is not linked to an active one" do
it "adds an error" do
property_validator.validate_la_is_active(log)
expect(log.errors["scheme_id"]).to include(I18n.t("validations.lettings.property.scheme_id.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["location_id"]).to include(I18n.t("validations.lettings.property.location_id.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["startdate"]).to include(I18n.t("validations.lettings.property.startdate.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["la"]).to include(I18n.t("validations.lettings.property.la.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["postcode_full"]).to include(I18n.t("validations.lettings.property.postcode_full.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["uprn"]).to include(I18n.t("validations.lettings.property.uprn.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["uprn_selection"]).to include(I18n.t("validations.lettings.property.uprn_selection.la_not_valid_for_date", la: local_authority_inactive.name))
end
end
context "and the inactive local authority is not linked to an active one" do
it "adds an error" do
property_validator.validate_la_is_active(log)
expect(log.errors["scheme_id"]).to include(I18n.t("validations.lettings.property.scheme_id.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["location_id"]).to include(I18n.t("validations.lettings.property.location_id.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["startdate"]).to include(I18n.t("validations.lettings.property.startdate.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["la"]).to include(I18n.t("validations.lettings.property.la.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["postcode_full"]).to include(I18n.t("validations.lettings.property.postcode_full.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["uprn"]).to include(I18n.t("validations.lettings.property.uprn.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["uprn_selection"]).to include(I18n.t("validations.lettings.property.uprn_selection.la_not_valid_for_date", la: local_authority_inactive.name))
context "and the inactive local authority is linked to an active one" do
it "does not add an error" do
LocalAuthorityLink.create!(local_authority: local_authority_inactive, linked_local_authority: local_authority_active)
property_validator.validate_la_is_active(log)
expect(log.errors["scheme_id"]).to be_empty
expect(log.errors["location_id"]).to be_empty
expect(log.errors["startdate"]).to be_empty
expect(log.errors["la"]).to be_empty
expect(log.errors["postcode_full"]).to be_empty
expect(log.errors["uprn"]).to be_empty
expect(log.errors["uprn_selection"]).to be_empty
end
end
end
end
context "when 2026 or later", metadata: { year: 26 } do
let(:startdate) { collection_start_date_for_year_or_later(2026) }
context "and the local authority is active for supported housing log" do
let(:log) { build(:lettings_log, :completed, startdate:, la: la_ecode_active, needstype: 2) }
context "and the inactive local authority is linked to an active one" do
it "does not add an error" do
LocalAuthorityLink.create!(local_authority: local_authority_inactive, linked_local_authority: local_authority_active)
property_validator.validate_la_is_active(log)
expect(log.errors["scheme_id"]).to be_empty
expect(log.errors["location_id"]).to be_empty
@ -362,6 +387,38 @@ RSpec.describe Validations::PropertyValidations do
expect(log.errors["uprn_selection"]).to be_empty
end
end
context "and the local authority is inactive for supported housing log" do
let(:log) { build(:lettings_log, :completed, startdate:, la: la_ecode_inactive, needstype: 2) }
context "and the inactive local authority is not linked to an active one" do
it "adds an error" do
property_validator.validate_la_is_active(log)
expect(log.errors["scheme_id"]).to include(I18n.t("validations.lettings.property.scheme_id.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["location_id"]).to include(I18n.t("validations.lettings.property.location_id.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["startdate"]).to include(I18n.t("validations.lettings.property.startdate.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["la"]).to include(I18n.t("validations.lettings.property.la.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["postcode_full"]).to include(I18n.t("validations.lettings.property.postcode_full.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["uprn"]).to include(I18n.t("validations.lettings.property.uprn.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["uprn_selection"]).to include(I18n.t("validations.lettings.property.uprn_selection.la_not_valid_for_date", la: local_authority_inactive.name))
end
end
context "and the inactive local authority is linked to an active one" do
# the link code was only ever used if the LA was drawn from the location
it "adds an error" do
LocalAuthorityLink.create!(local_authority: local_authority_inactive, linked_local_authority: local_authority_active)
property_validator.validate_la_is_active(log)
expect(log.errors["scheme_id"]).to include(I18n.t("validations.lettings.property.scheme_id.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["location_id"]).to include(I18n.t("validations.lettings.property.location_id.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["startdate"]).to include(I18n.t("validations.lettings.property.startdate.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["la"]).to include(I18n.t("validations.lettings.property.la.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["postcode_full"]).to include(I18n.t("validations.lettings.property.postcode_full.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["uprn"]).to include(I18n.t("validations.lettings.property.uprn.la_not_valid_for_date", la: local_authority_inactive.name))
expect(log.errors["uprn_selection"]).to include(I18n.t("validations.lettings.property.uprn_selection.la_not_valid_for_date", la: local_authority_inactive.name))
end
end
end
end
end

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

@ -6,6 +6,7 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
subject(:sale_information_validator) { validator_class.new }
let(:validator_class) { Class.new { include Validations::Sales::SaleInformationValidations } }
let(:saledate) { current_collection_start_date }
describe "#validate_practical_completion_date" do
context "when hodate blank" do
@ -59,8 +60,10 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end
context "when hodate 3 or more years before saledate" do
let(:record) { build(:sales_log, hodate: saledate - 3.years, saledate:) }
context "and form year is 2024 or earlier" do
let(:record) { build(:sales_log, hodate: previous_collection_start_date - 3.years, saledate: previous_collection_start_date) }
let(:saledate) { collection_start_date_for_year(2024) }
it "does add an error" do
sale_information_validator.validate_practical_completion_date(record)
@ -71,7 +74,7 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end
context "and form year is 2025 or later" do
let(:record) { build(:sales_log, hodate: current_collection_start_date - 3.years, saledate: current_collection_start_date) }
let(:saledate) { collection_start_date_for_year_or_later(2025) }
it "does not add an error" do
sale_information_validator.validate_practical_completion_date(record)
@ -376,10 +379,8 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end
describe "#validate_discounted_ownership_value" do
let(:record) { FactoryBot.build(:sales_log, :saledate_today, 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, :saledate_today, deposit: nil, ownershipsch: 2, type: 8) }
let(:record) { FactoryBot.build(:sales_log, saledate:, deposit: nil, ownershipsch: 2, type: 8) }
context "and not provided" do
before do
@ -453,7 +454,7 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end
context "when discount is routed to" do
let(:record) { FactoryBot.build(:sales_log, :saledate_today, grant: nil, ownershipsch: 2, type: 9) }
let(:record) { FactoryBot.build(:sales_log, saledate:, grant: nil, ownershipsch: 2, type: 9) }
context "and not provided" do
it "returns false" do
@ -475,72 +476,159 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end
context "and is provided" do
it "does not add errors if mortgage and deposit total equals market value - discount" do
record.value = 30_000
record.mortgage = 10_000
record.deposit = 5_000
record.discount = 50
context "and is 2025" do
let(:saledate) { collection_start_date_for_year(2025) }
sale_information_validator.validate_discounted_ownership_value(record)
it "does not add errors if mortgage and deposit total equals market value - discount" do
record.value = 30_000
record.mortgage = 10_000
record.deposit = 5_000
record.discount = 50
expect(record.errors["mortgageused"]).to be_empty
expect(record.errors["mortgage"]).to be_empty
expect(record.errors["value"]).to be_empty
expect(record.errors["deposit"]).to be_empty
expect(record.errors["ownershipsch"]).to be_empty
expect(record.errors["discount"]).to be_empty
expect(record.errors["grant"]).to be_empty
end
sale_information_validator.validate_discounted_ownership_value(record)
it "does not add errors if mortgage and deposit total is within a 0.05% x market value tolerance of market value - discount" do
record.value = 123_000
record.mortgage = 66_112
record.deposit = 0
record.discount = 46.3
expect(record.errors["mortgageused"]).to be_empty
expect(record.errors["mortgage"]).to be_empty
expect(record.errors["value"]).to be_empty
expect(record.errors["deposit"]).to be_empty
expect(record.errors["ownershipsch"]).to be_empty
expect(record.errors["discount"]).to be_empty
expect(record.errors["grant"]).to be_empty
end
sale_information_validator.validate_discounted_ownership_value(record)
it "does not add errors if mortgage and deposit total is within a 0.05% x market value tolerance of market value - discount" do
record.value = 123_000
record.mortgage = 66_112
record.deposit = 0
record.discount = 46.3
expect(record.errors["mortgageused"]).to be_empty
expect(record.errors["mortgage"]).to be_empty
expect(record.errors["value"]).to be_empty
expect(record.errors["deposit"]).to be_empty
expect(record.errors["ownershipsch"]).to be_empty
expect(record.errors["discount"]).to be_empty
expect(record.errors["grant"]).to be_empty
end
sale_information_validator.validate_discounted_ownership_value(record)
it "adds errors if mortgage and deposit total is not within a 0.05% x market value tolerance of market value - discount" do
record.value = 123_000
record.mortgage = 66_113
record.deposit = 0
record.discount = 46.3
expect(record.errors["mortgageused"]).to be_empty
expect(record.errors["mortgage"]).to be_empty
expect(record.errors["value"]).to be_empty
expect(record.errors["deposit"]).to be_empty
expect(record.errors["ownershipsch"]).to be_empty
expect(record.errors["discount"]).to be_empty
expect(record.errors["grant"]).to be_empty
end
sale_information_validator.validate_discounted_ownership_value(record)
it "adds errors if mortgage and deposit total is not within a 0.05% x market value tolerance of market value - discount" do
record.value = 123_000
record.mortgage = 66_113
record.deposit = 0
record.discount = 46.3
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors["mortgageused"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (46.3%) is £66,051.00.</br></br>These two amounts should be the same.")
expect(record.errors["mortgage"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (46.3%) is £66,051.00.</br></br>These two amounts should be the same.")
expect(record.errors["value"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (46.3%) is £66,051.00.</br></br>These two amounts should be the same.")
expect(record.errors["deposit"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (46.3%) is £66,051.00.</br></br>These two amounts should be the same.")
expect(record.errors["ownershipsch"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (46.3%) is £66,051.00.</br></br>These two amounts should be the same.")
expect(record.errors["discount"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (46.3%) is £66,051.00.</br></br>These two amounts should be the same.")
expect(record.errors["grant"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (46.3%) is £66,051.00.</br></br>These two amounts should be the same.")
end
expect(record.errors["mortgageused"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (46.3%) is £66,051.00.</br></br>These two amounts should be the same.")
expect(record.errors["mortgage"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (46.3%) is £66,051.00.</br></br>These two amounts should be the same.")
expect(record.errors["value"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (46.3%) is £66,051.00.</br></br>These two amounts should be the same.")
expect(record.errors["deposit"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (46.3%) is £66,051.00.</br></br>These two amounts should be the same.")
expect(record.errors["ownershipsch"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (46.3%) is £66,051.00.</br></br>These two amounts should be the same.")
expect(record.errors["discount"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (46.3%) is £66,051.00.</br></br>These two amounts should be the same.")
expect(record.errors["grant"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (46.3%) is £66,051.00.</br></br>These two amounts should be the same.")
it "does not add errors if mortgage and deposit total is exactly 0.05% x market value away from market value - discount" do
record.value = 120_000
record.mortgage = 64_500
record.deposit = 0
record.discount = 46.3
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors["mortgageused"]).to be_empty
expect(record.errors["mortgage"]).to be_empty
expect(record.errors["value"]).to be_empty
expect(record.errors["deposit"]).to be_empty
expect(record.errors["ownershipsch"]).to be_empty
expect(record.errors["discount"]).to be_empty
expect(record.errors["grant"]).to be_empty
end
it "does not add errors if mortgageused is don't know" do
record.mortgageused = 3
record.value = 30_000
record.deposit = 5_000
record.discount = 50
expect(record.errors["mortgageused"]).to be_empty
expect(record.errors["mortgage"]).to be_empty
expect(record.errors["value"]).to be_empty
expect(record.errors["deposit"]).to be_empty
expect(record.errors["ownershipsch"]).to be_empty
expect(record.errors["discount"]).to be_empty
expect(record.errors["grant"]).to be_empty
end
end
it "does not add errors if mortgage and deposit total is exactly 0.05% x market value away from market value - discount" do
record.value = 120_000
record.mortgage = 64_500
record.deposit = 0
record.discount = 46.3
context "and is 2026" do
let(:saledate) { collection_start_date_for_year(2026) }
sale_information_validator.validate_discounted_ownership_value(record)
it "does not add errors if mortgage and deposit total equals market value - discount" do
record.value = 30_000
record.mortgage = 10_000
record.deposit = 5_000
record.discount = 50
expect(record.errors["mortgageused"]).to be_empty
expect(record.errors["mortgage"]).to be_empty
expect(record.errors["value"]).to be_empty
expect(record.errors["deposit"]).to be_empty
expect(record.errors["ownershipsch"]).to be_empty
expect(record.errors["discount"]).to be_empty
expect(record.errors["grant"]).to be_empty
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors["mortgageused"]).to be_empty
expect(record.errors["mortgage"]).to be_empty
expect(record.errors["value"]).to be_empty
expect(record.errors["deposit"]).to be_empty
expect(record.errors["ownershipsch"]).to be_empty
expect(record.errors["discount"]).to be_empty
expect(record.errors["grant"]).to be_empty
end
it "does not add errors if mortgage and deposit total is within a 0.1% tolerance" do
record.value = 30_000
record.mortgage = 10_000
record.deposit = 5_000
record.discount = 50.1
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors["mortgageused"]).to be_empty
expect(record.errors["mortgage"]).to be_empty
expect(record.errors["value"]).to be_empty
expect(record.errors["deposit"]).to be_empty
expect(record.errors["ownershipsch"]).to be_empty
expect(record.errors["discount"]).to be_empty
expect(record.errors["grant"]).to be_empty
end
it "adds errors if mortgage and deposit total is not within a 0.1% tolerance" do
record.value = 123_000
record.mortgage = 66_113
record.deposit = 0
record.discount = 50.2
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors["mortgageused"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (50.2%) is £61,254.00.</br></br>These two amounts should be the same.")
expect(record.errors["mortgage"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (50.2%) is £61,254.00.</br></br>These two amounts should be the same.")
expect(record.errors["value"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (50.2%) is £61,254.00.</br></br>These two amounts should be the same.")
expect(record.errors["deposit"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (50.2%) is £61,254.00.</br></br>These two amounts should be the same.")
expect(record.errors["ownershipsch"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (50.2%) is £61,254.00.</br></br>These two amounts should be the same.")
expect(record.errors["discount"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (50.2%) is £61,254.00.</br></br>These two amounts should be the same.")
expect(record.errors["grant"]).to include("The mortgage (£66,113.00) and cash deposit (£0.00) added together is £66,113.00.</br></br>The full purchase price (£123,000.00) subtracted by the sum of the full purchase price (£123,000.00) multiplied by the percentage discount (50.2%) is £61,254.00.</br></br>These two amounts should be the same.")
end
it "does not add errors if mortgageused is don't know" do
record.mortgageused = 3
record.value = 30_000
record.deposit = 5_000
record.discount = 50
expect(record.errors["mortgageused"]).to be_empty
expect(record.errors["mortgage"]).to be_empty
expect(record.errors["value"]).to be_empty
expect(record.errors["deposit"]).to be_empty
expect(record.errors["ownershipsch"]).to be_empty
expect(record.errors["discount"]).to be_empty
expect(record.errors["grant"]).to be_empty
end
end
end
end
@ -663,58 +751,6 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
expect(record.errors["grant"]).to be_empty
end
end
context "with year 2026", :aggregate_failures do
let(:saledate) { Time.zone.local(2026, 4, 1) }
context "when mortgage and deposit is exact" do
let(:record) { FactoryBot.build(:sales_log, saledate:, mortgage: 85_000, deposit: 5_000, value: 100_000, discount: 10, ownershipsch: 2, type: 9) }
it "does not add an error" do
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors["mortgage"]).to be_empty
expect(record.errors["value"]).to be_empty
expect(record.errors["deposit"]).to be_empty
expect(record.errors["discount"]).to be_empty
end
end
context "when mortgage and deposit is within 0.1% discount tolerance" do
let(:record) { FactoryBot.build(:sales_log, saledate:, mortgage: 85_000, deposit: 5_000, value: 100_000, discount: 10.1, ownershipsch: 2, type: 9) }
it "does not add an error" do
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors["mortgage"]).to be_empty
expect(record.errors["value"]).to be_empty
expect(record.errors["deposit"]).to be_empty
expect(record.errors["discount"]).to be_empty
end
end
context "when mortgage and deposit is outside 0.1% discount tolerance" do
let(:record) { FactoryBot.build(:sales_log, saledate:, mortgage: 85_000, deposit: 5_000, value: 100_000, discount: 10.2, ownershipsch: 2, type: 9) }
it "adds an error" do
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors["mortgage"]).not_to be_empty
expect(record.errors["value"]).not_to be_empty
expect(record.errors["deposit"]).not_to be_empty
expect(record.errors["discount"]).not_to be_empty
end
end
end
context "when mortgageused is don't know" do
let(:record) { FactoryBot.build(:sales_log, :saledate_today, mortgageused: 3, deposit: 10_000, value: 100_000, discount: 10, ownershipsch: 2, type: 9) }
it "does not add an error" do
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors["mortgage"]).to be_empty
expect(record.errors["value"]).to be_empty
expect(record.errors["deposit"]).to be_empty
expect(record.errors["discount"]).to be_empty
end
end
end
describe "#validate_outright_sale_value_matches_mortgage_plus_deposit" do

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

@ -3,7 +3,8 @@ require "rails_helper"
RSpec.describe Validations::Sales::SoftValidations do
include CollectionTimeHelper
let(:record) { build(:sales_log) }
let(:saledate) { current_collection_start_date }
let(:record) { build(:sales_log, saledate:) }
describe "income validations" do
context "when validating soft range based on ecstat" do
@ -396,17 +397,33 @@ RSpec.describe Validations::Sales::SoftValidations do
expect(record).not_to be_mortgage_plus_deposit_less_than_discounted_value
end
it "returns true if the deposit and mortgage add up to less than the discounted value" do
record.value = 500_000
record.discount = 10
record.mortgage = 200_000
record.deposit = 200_000
expect(record).to be_mortgage_plus_deposit_less_than_discounted_value
context "and 2025", metadata: { year: 25 } do
let(:saledate) { collection_start_date_for_year(2025) }
it "returns true if the deposit and mortgage add up to less than the discounted value" do
record.value = 500_000
record.discount = 10
record.mortgage = 200_000
record.deposit = 200_000
expect(record).to be_mortgage_plus_deposit_less_than_discounted_value
end
end
context "and 2026 or later", metadata: { year: 26 } do
let(:saledate) { collection_start_date_for_year_or_later(2026) }
it "returns false if the deposit and mortgage add up to less than the discounted value" do
record.value = 500_000
record.discount = 10
record.mortgage = 200_000
record.deposit = 200_000
expect(record).not_to be_mortgage_plus_deposit_less_than_discounted_value
end
end
end
context "when validating extra borrowing" do
let(:record) { build(:sales_log, saledate: previous_collection_start_date) }
let(:saledate) { collection_start_date_for_year_or_later(2024) }
it "returns false for logs from 2024 onwards" do
record.extrabor = 2

18
spec/requests/collection_resources_controller_spec.rb

@ -49,12 +49,16 @@ RSpec.describe CollectionResourcesController, type: :request do
let(:user) { create(:user, :support) }
before do
allow(Time.zone).to receive(:today).and_return(Time.zone.local(2025, 1, 8))
Timecop.travel(Time.zone.local(current_collection_start_year, 1, 8))
allow(user).to receive(:need_two_factor_authentication?).and_return(false)
allow(storage_service).to receive(:file_exists?).and_return(true)
sign_in user
end
after do
Timecop.return
end
it "displays collection resources" do
get collection_resources_path
@ -122,7 +126,7 @@ RSpec.describe CollectionResourcesController, type: :request do
context "when the collection year has not started yet" do
before do
Timecop.freeze(Time.zone.local(2025, 3, 1))
Timecop.travel(Time.zone.local(current_collection_start_year, 3, 1))
get collection_resources_path
end
@ -131,8 +135,8 @@ RSpec.describe CollectionResourcesController, type: :request do
end
it "displays next year banner" do
expect(page).to have_content("The 2025 to 2026 collection resources are not yet available to users.")
expect(page).to have_link("Release the 2025 to 2026 collection resources to users", href: confirm_mandatory_collection_resources_release_path(year: 2025))
expect(page).to have_content("The #{next_collection_start_year} to #{next_collection_end_year} collection resources are not yet available to users.")
expect(page).to have_link("Release the #{next_collection_start_year} to #{next_collection_end_year} collection resources to users", href: confirm_mandatory_collection_resources_release_path(year: next_collection_start_year))
end
end
@ -176,7 +180,7 @@ RSpec.describe CollectionResourcesController, type: :request do
context "when the collection year has not started yet" do
before do
Timecop.freeze(Time.zone.local(2025, 3, 1))
Timecop.travel(Time.zone.local(current_collection_start_year, 3, 1))
get collection_resources_path
end
@ -185,8 +189,8 @@ RSpec.describe CollectionResourcesController, type: :request do
end
it "displays next year banner" do
expect(page).to have_content("The 2025 to 2026 collection resources are not yet available to users.")
expect(page).to have_content("Once you have uploaded all the required 2025 to 2026 collection resources, you will be able to release them to users.")
expect(page).to have_content("The #{next_collection_start_year} to #{next_collection_end_year} collection resources are not yet available to users.")
expect(page).to have_content("Once you have uploaded all the required #{next_collection_start_year} to #{next_collection_end_year} collection resources, you will be able to release them to users.")
end
end
end

1111
spec/requests/duplicate_logs_controller_spec.rb

File diff suppressed because it is too large Load Diff

2
spec/requests/form_controller_spec.rb

@ -1194,7 +1194,7 @@ RSpec.describe FormController, type: :request do
it "displays a success banner" do
follow_redirect!
expect(response.body).to include("You have successfully updated Q31: lead tenant’s age")
expect(response.body).to match(/You have successfully updated Q\d+: lead tenant’s age/)
end
end

76
spec/requests/start_controller_spec.rb

@ -1,6 +1,8 @@
require "rails_helper"
RSpec.describe StartController, type: :request do
include CollectionTimeHelper
let(:user) { create(:user) }
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:notify_client) { instance_double(Notifications::Client) }
@ -322,41 +324,67 @@ RSpec.describe StartController, type: :request do
end
end
context "and 2024 collection window is open for editing" do
context "and previous collection window is open for editing" do
before do
create(:collection_resource, :additional, year: 2024, log_type: "sales", display_name: "sales additional resource (2024 to 2025)")
allow(Time).to receive(:now).and_return(Time.zone.local(2025, 4, 1))
create(:collection_resource, :additional, year: previous_collection_start_year, log_type: "sales", display_name: "sales additional resource (#{previous_collection_start_year} to #{previous_collection_end_year})")
Timecop.freeze(current_collection_start_date)
end
after do
Timecop.return
end
it "displays correct resources for 2024/25 and 2025/26 collection years" do
it "displays correct resources for previous and current collection years" do
current_collection_start_year_short = current_collection_start_year - 2000
current_collection_end_year_short = current_collection_end_year - 2000
previous_collection_start_year_short = previous_collection_start_year - 2000
previous_collection_end_year_short = previous_collection_end_year - 2000
current_collection_range_slash = "#{current_collection_start_year_short}/#{current_collection_end_year_short}"
previous_collection_range_slash = "#{previous_collection_start_year_short}/#{previous_collection_end_year_short}"
current_collection_range_to = "#{current_collection_start_year} to #{current_collection_end_year}"
previous_collection_range_to = "#{previous_collection_start_year} to #{previous_collection_end_year}"
get root_path
expect(page).to have_content("Lettings 25/26")
expect(page).to have_content("Lettings 24/25")
expect(page).to have_content("Lettings 2025 to 2026")
expect(page).to have_content("Lettings 2024 to 2025")
expect(page).to have_content("Sales 25/26")
expect(page).to have_content("Sales 24/25")
expect(page).to have_content("Sales 2025 to 2026")
expect(page).to have_content("Sales 2024 to 2025")
expect(page).to have_content("Download the sales additional resource (2024 to 2025)")
expect(page).to have_content("Lettings #{current_collection_range_slash}")
expect(page).to have_content("Lettings #{previous_collection_range_slash}")
expect(page).to have_content("Lettings #{current_collection_range_to}")
expect(page).to have_content("Lettings #{previous_collection_range_to}")
expect(page).to have_content("Sales #{current_collection_range_slash}")
expect(page).to have_content("Sales #{previous_collection_range_slash}")
expect(page).to have_content("Sales #{current_collection_range_to}")
expect(page).to have_content("Sales #{previous_collection_range_to}")
expect(page).to have_content("Download the sales additional resource (#{previous_collection_range_to})")
end
end
context "and 2024 collection window is closed for editing" do
context "and previous collection window is closed for editing" do
before do
allow(Time).to receive(:now).and_return(Time.zone.local(2025, 12, 1))
Timecop.freeze(current_collection_start_date + 6.months)
end
it "displays correct resources" do
after do
Timecop.return
end
it "displays correct resources for current collection year only" do
current_collection_start_year_short = current_collection_start_year - 2000
current_collection_end_year_short = current_collection_end_year - 2000
previous_collection_start_year_short = previous_collection_start_year - 2000
previous_collection_end_year_short = previous_collection_end_year - 2000
current_collection_range_slash = "#{current_collection_start_year_short}/#{current_collection_end_year_short}"
previous_collection_range_slash = "#{previous_collection_start_year_short}/#{previous_collection_end_year_short}"
current_collection_range_to = "#{current_collection_start_year} to #{current_collection_end_year}"
previous_collection_range_to = "#{previous_collection_start_year} to #{previous_collection_end_year}"
get root_path
expect(page).to have_content("Lettings 25/26")
expect(page).not_to have_content("Lettings 24/25")
expect(page).to have_content("Lettings 2025 to 2026")
expect(page).not_to have_content("Lettings 2024 to 2025")
expect(page).to have_content("Sales 25/26")
expect(page).not_to have_content("Sales 24/25")
expect(page).to have_content("Sales 2025 to 2026")
expect(page).not_to have_content("Sales 2024 to 2025")
expect(page).to have_content("Lettings #{current_collection_range_slash}")
expect(page).not_to have_content("Lettings #{previous_collection_range_slash}")
expect(page).to have_content("Lettings #{current_collection_range_to}")
expect(page).not_to have_content("Lettings #{previous_collection_range_to}")
expect(page).to have_content("Sales #{current_collection_range_slash}")
expect(page).not_to have_content("Sales #{previous_collection_range_slash}")
expect(page).to have_content("Sales #{current_collection_range_to}")
expect(page).not_to have_content("Sales #{previous_collection_range_to}")
end
end

7
spec/services/bulk_upload/sales/validator_spec.rb

@ -37,10 +37,15 @@ RSpec.describe BulkUpload::Sales::Validator do
context "when file has too many columns" do
before do
file.write((%w[a] * (Object.const_get("BulkUpload::Sales::Year#{year}::CsvParser::MAX_COLUMNS") + 1)).join(","))
Timecop.travel(collection_start_date_for_year_or_later(2025))
file.write((%w[a] * (Object.const_get("BulkUpload::Sales::Year#{year}::CsvParser::FIELDS") + 1)).join(","))
file.rewind
end
after do
Timecop.return
end
it "is not valid" do
expect(validator).not_to be_valid
end

Loading…
Cancel
Save