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 accessible_register, # 130
log.owning_organisation.la? ? log.referral_register : nil, log.owning_organisation.la? ? log.referral_register : nil,
log.owning_organisation.prp? ? log.referral_register : nil, log.owning_organisation.prp? ? log.referral_register : nil,
log.referral_noms, log.owning_organisation.prp? ? log.referral_noms : nil,
log.referral_org, log.owning_organisation.prp? ? log.referral_org : nil,
net_income_known, net_income_known,
log.incfreq, log.incfreq,
log.earnings, 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 = (start_date..end_date).to_a - [date.to_date]
available_dates.empty? ? nil : available_dates.sample available_dates.empty? ? nil : available_dates.sample
end 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 end

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

@ -82,7 +82,7 @@ module Validations::Sales::SaleInformationValidations
tolerance = record.value_with_discount_tolerance 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')})" 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}%)" : "" 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| %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(:rendered) { render_inline(component) }
let(:user) { create(:user) } 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_id) { "household_characteristics" }
let(:subsection) { log.form.get_subsection(subsection_id) } let(:subsection) { log.form.get_subsection(subsection_id) }
let(:questions) { subsection.applicable_questions(log) } let(:questions) { subsection.applicable_questions(log) }
@ -21,8 +21,8 @@ RSpec.describe CheckAnswersSummaryListCardComponent, type: :component do
end end
it "has the correct answer label for a question" do it "has the correct answer label for a question" do
sex1_question = questions.find { |q| q.id == "sex1" } question = questions.find { |q| q.id == "ecstat1" }
expect(component.get_answer_label(sex1_question)).to eq("Female") expect(component.get_answer_label(question)).to eq("Other")
end end
context "when log was created via a bulk upload and has an unanswered question" do 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) } let(:log) { create(:lettings_log, :in_progress) }
it "displays normal copy with muted colour" do 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
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 RSpec.describe "Sales Log Check Answers Page" do
include Helpers include Helpers
include CollectionTimeHelper
let(:user) { FactoryBot.create(:user) } let(:user) { FactoryBot.create(:user) }
let(:subsection) { "household-characteristics" } let(:subsection) { "household-characteristics" }
let(:conditional_subsection) { "conditional-question" } let(:conditional_subsection) { "conditional-question" }
@ -13,7 +15,7 @@ RSpec.describe "Sales Log Check Answers Page" do
:completed, :completed,
assigned_to: user, assigned_to: user,
jointpur: 1, jointpur: 1,
hholdcount: 4, hholdcount: current_collection_start_year >= 2026 ? 6 : 4,
) )
end end
@ -23,7 +25,7 @@ RSpec.describe "Sales Log Check Answers Page" do
:completed, :completed,
assigned_to: user, assigned_to: user,
jointpur: 2, jointpur: 2,
hholdcount: 4, hholdcount: current_collection_start_year >= 2026 ? 6 : 4,
) )
end 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) } 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 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 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 end
describe "value check clearing" do describe "value check clearing" do
context "with a lettings log bulk upload" do context "when 2025", metadata: { year: 25 } do
let(:log) { build(:lettings_log, startdate: current_collection_start_date, bulk_upload:) } 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 context "with a lettings log bulk upload" 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] 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
end end
context "with a sales log bulk upload" do context "when 2026 or later", metadata: { year: 26 } do
let(:log) { build(:sales_log, saledate: current_collection_start_date, bulk_upload:) } 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 context "with a lettings log bulk upload" 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] 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 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(:question_definition) { nil }
let(:page) { instance_double(Form::Page) } let(:page) { instance_double(Form::Page) }
let(:subsection) { instance_double(Form::Subsection) } let(:subsection) { instance_double(Form::Subsection) }
let(:start_year_2026_or_later?) { false } let(:start_year_2026_or_later?) { true }
let(:form) { instance_double(Form, start_date: current_collection_start_date, start_year_2026_or_later?: start_year_2026_or_later?) } 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 before do
allow(page).to receive(:subsection).and_return(subsection) allow(page).to receive(:subsection).and_return(subsection)
@ -37,6 +38,9 @@ RSpec.describe Form::Lettings::Questions::Hhmemb, type: :model do
end end
context "when in 2025", { year: 25 } do 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 it "does not have check answers card title" do
expect(question.check_answers_card_title).to be_nil expect(question.check_answers_card_title).to be_nil
end end
@ -48,13 +52,14 @@ RSpec.describe Form::Lettings::Questions::Hhmemb, type: :model do
context "when in 2026", { year: 26 } do context "when in 2026", { year: 26 } do
let(:start_year_2026_or_later?) { true } let(:start_year_2026_or_later?) { true }
let(:start_date) { collection_start_date_for_year(2026) }
it "has correct check answers card title" do it "has correct check answers card title" do
expect(question.check_answers_card_title).to eq("Household") expect(question.check_answers_card_title).to eq("Household")
end end
it "has the correct question number" do it "has the correct question number" do
expect(question.question_number).to eq(30) expect(question.question_number).to eq(29)
end end
end 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 describe "variables dependent on whether a letting is a renewal" do
let(:organisation) { create(:organisation) } let(:organisation) { create(:organisation) }
let(:user) { create(:user, 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) } 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 before do
log.renewal = 1 Timecop.travel(startdate)
expect { log.set_derived_fields! } Singleton.__init__(FormHandler)
.to change(log, :waityear).to(2) end
.and change(log, :offered).to(0)
.and change(log, :referral).to(1) after do
.and change(log, :first_time_property_let_as_social_housing).to(0) Timecop.return
.and change(log, :rsnvac).to(14) 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 end
it "clears waityear offered referral first_time_property_let_as_social_housing rsnvac when not a renewal" do context "when 2026 or later", metadata: { year: 26 } do
expect { persisted_renewal_lettings_log.update!(renewal: 0) } let(:startdate) { collection_start_date_for_year_or_later(2026) }
.to change(persisted_renewal_lettings_log, :waityear).from(2).to(nil)
.and change(persisted_renewal_lettings_log, :offered).from(0).to(nil) it "derives waityear offered referral first_time_property_let_as_social_housing rsnvac when renewal" do
.and change(persisted_renewal_lettings_log, :referral).from(1).to(nil) log.renewal = 1
.and change(persisted_renewal_lettings_log, :first_time_property_let_as_social_housing).from(0).to(nil) expect { log.set_derived_fields! }
.and change(persisted_renewal_lettings_log, :rsnvac).from(14).to(nil) .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 end
describe "deriving voiddate from startdate" do 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 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) 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).not_to be_valid
expect(name_change.errors[:name]).to include(I18n.t("validations.organisation.name_changes.name.must_be_different")) expect(name_change.errors[:name]).to include(I18n.t("validations.organisation.name_changes.name.must_be_different"))
end end
it "is invalid if startdate is after the organisation's merge date" do 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) name_change = build(:organisation_name_change, organisation:, immediate_change: false, startdate: Time.zone.tomorrow)
expect(name_change).not_to be_valid 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))) 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 it "returns changes before a specific date" do
name_change = create(:organisation_name_change, organisation:, startdate: 1.day.ago) 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 end
it "returns changes after a specific date" do it "returns changes after a specific date" do
name_change = create(:organisation_name_change, organisation:, startdate: 2.days.from_now) 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
end end
@ -84,7 +84,7 @@ RSpec.describe OrganisationNameChange, type: :model do
describe "#includes_date?" do describe "#includes_date?" do
it "returns true if the date is within the change period" do it "returns true if the date is within the change period" do
name_change = create(:organisation_name_change, organisation:, startdate: 1.day.ago) 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 end
it "returns false if the date is outside the change period" do 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 end
context "when deriving household variables" do context "when deriving household variables" do
let!(:sales_log) do let(:sales_log) do
create( create(
:sales_log, :sales_log,
:completed, :completed,
saledate:,
jointpur: 1, jointpur: 1,
hholdcount: 4, hholdcount:,
details_known_3: 1, details_known_3: 1,
details_known_4: 1, details_known_4: 1,
details_known_5: 1, details_known_5: 1,
@ -711,30 +712,75 @@ RSpec.describe SalesLog, type: :model do
) )
end end
it "correctly derives and saves hhmemb" do before do
record_from_db = described_class.find(sales_log.id) Timecop.travel(saledate)
expect(record_from_db["hhmemb"]).to eq(6) Singleton.__init__(FormHandler)
end end
it "correctly derives and saves hhmemb if it's a joint purchase" do after do
sales_log.update!(jointpur: 2, jointmore: 2) Timecop.return
record_from_db = described_class.find(sales_log.id)
expect(record_from_db["hhmemb"]).to eq(5)
end end
it "correctly derives and saves totchild" do context "when 2025", metadata: { year: 25 } do
record_from_db = described_class.find(sales_log.id) let(:saledate) { collection_start_date_for_year(2025) }
expect(record_from_db["totchild"]).to eq(2) let(:hholdcount) { 4 }
end
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 it "correctly derives and saves hhtype" do
record_from_db = described_class.find(sales_log.id) record_from_db = described_class.find(sales_log.id)
expect(record_from_db["totadult"]).to eq(4) expect(record_from_db["hhtype"]).to eq(9)
end
end end
it "correctly derives and saves hhtype" do context "when 2026 or later", metadata: { year: 26 } do
record_from_db = described_class.find(sales_log.id) let(:saledate) { collection_start_date_for_year_or_later(2026) }
expect(record_from_db["hhtype"]).to eq(9) 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
end end
@ -806,10 +852,10 @@ RSpec.describe SalesLog, type: :model do
end end
describe "expected_shared_ownership_deposit_value" do 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 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
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 } subject(:household_validator) { validator_class.new }
let(:validator_class) { Class.new { include Validations::HouseholdValidations } } 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)) } let(:record) { FactoryBot.build(:lettings_log, :setup_completed, startdate:, assigned_to: create(:user)) }
describe "reasonable preference validations" do describe "reasonable preference validations" do
@ -117,52 +117,57 @@ RSpec.describe Validations::HouseholdValidations do
end end
end end
context "when referral is not internal transfer" do context "when 2025", metadata: { year: 25 } do
it "can be permanently decanted from another property owned by this landlord" do # post 2025 we don't ask referral and referral_type anymore
record.reason = 1 let(:startdate) { collection_start_date_for_year(2025) }
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 referral is internal transfer" do context "when referral is not internal transfer" do
it "can be permanently decanted from another property owned by this landlord" do it "can be permanently decanted from another property owned by this landlord" do
record.reason = 1 record.reason = 1
record.referral_type = 3 record.referral_type = 1
record.referral = 1 record.referral = 2
household_validator.validate_reason_for_leaving_last_settled_home(record) household_validator.validate_reason_for_leaving_last_settled_home(record)
expect(record.errors["reason"]) expect(record.errors["reason"])
.to be_empty .to be_empty
expect(record.errors["referral"]) expect(record.errors["referral"])
.to be_empty .to be_empty
expect(record.errors["referral_type"]) expect(record.errors["referral_type"])
.to be_empty .to be_empty
end
end end
it "cannot have a PRP as landlord and Housing situation before this letting cannot be LA general needs" do context "when referral is internal transfer" do
record.owning_organisation.provider_type = "PRP" it "can be permanently decanted from another property owned by this landlord" do
record.prevten = 30 record.reason = 1
record.referral_type = 3 record.referral_type = 3
record.referral = 1 record.referral = 1
household_validator.validate_referral(record) household_validator.validate_reason_for_leaving_last_settled_home(record)
expect(record.errors["referral"]) expect(record.errors["reason"])
.to include(match(I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer"))) .to be_empty
expect(record.errors["prevten"]) expect(record.errors["referral"])
.to include(match(I18n.t("validations.lettings.household.prevten.la_general_needs.internal_transfer"))) .to be_empty
expect(record.errors["referral_type"])
.to be_empty
end
record.prevten = 31 it "cannot have a PRP as landlord and Housing situation before this letting cannot be LA general needs" do
household_validator.validate_referral(record) record.owning_organisation.provider_type = "PRP"
expect(record.errors["referral"]) record.prevten = 30
.to include(match(I18n.t("validations.lettings.household.referral.la_general_needs.internal_transfer"))) record.referral_type = 3
expect(record.errors["prevten"]) record.referral = 1
.to include(match(I18n.t("validations.lettings.household.prevten.la_general_needs.internal_transfer"))) 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 end
end end
@ -445,54 +450,58 @@ RSpec.describe Validations::HouseholdValidations do
end end
describe "referral validations" do describe "referral validations" do
context "when homelessness is assessed" do context "when start year is 2025", metadata: { year: 25 } do
it "can be internal transfer" do let(:startdate) { collection_start_date_for_year(2025) }
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
it "can be non internal transfer" do context "when homelessness is assessed" do
record.owning_organisation.provider_type = "PRP" it "can be internal transfer" do
record.homeless = 0 record.homeless = 11
record.referral_type = 2 record.referral_type = 3
record.referral = 3 record.referral = 1
household_validator.validate_referral(record) household_validator.validate_referral(record)
expect(record.errors["referral"]).to be_empty expect(record.errors["referral"]).to be_empty
expect(record.errors["referral_type"]).to be_empty expect(record.errors["referral_type"]).to be_empty
expect(record.errors["homeless"]).to be_empty expect(record.errors["homeless"]).to be_empty
end end
end
context "when homelessness is other" do it "can be non internal transfer" do
it "cannot be internal transfer" do record.owning_organisation.provider_type = "PRP"
record.referral_type = 3 record.homeless = 0
record.referral = 1 record.referral_type = 2
record.homeless = 7 record.referral = 3
household_validator.validate_referral(record) household_validator.validate_referral(record)
expect(record.errors["referral"]).to be_empty expect(record.errors["referral"]).to be_empty
expect(record.errors["referral_type"]).to be_empty expect(record.errors["referral_type"]).to be_empty
expect(record.errors["homeless"]).to be_empty expect(record.errors["homeless"]).to be_empty
end
end end
it "can be non internal transfer" do context "when homelessness is other" do
record.owning_organisation.provider_type = "PRP" it "cannot be internal transfer" do
record.referral_type = 2 record.referral_type = 3
record.referral = 3 record.referral = 1
record.homeless = 1 record.homeless = 7
household_validator.validate_referral(record) household_validator.validate_referral(record)
expect(record.errors["referral"]).to be_empty expect(record.errors["referral"]).to be_empty
expect(record.errors["referral_type"]).to be_empty expect(record.errors["referral_type"]).to be_empty
expect(record.errors["homeless"]).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
end end
context "when start year is 2026" do context "when start year is 2026 or later", metadata: { year: 26 } do
let(:startdate) { collection_start_date_for_year(2026) } 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" require "rails_helper"
RSpec.describe Validations::PropertyValidations do RSpec.describe Validations::PropertyValidations do
include CollectionTimeHelper
subject(:property_validator) { property_validator_class.new } subject(:property_validator) { property_validator_class.new }
let(:property_validator_class) { Class.new { include Validations::PropertyValidations } } let(:property_validator_class) { Class.new { include Validations::PropertyValidations } }
@ -316,42 +318,65 @@ RSpec.describe Validations::PropertyValidations do
end end
end end
context "and the local authority is active for supported housing log" do context "when 2025", metadata: { year: 25 } do
let(:location) { create(:location, location_code: la_ecode_active) } let(:startdate) { collection_start_date_for_year(2025) }
let(:log) { build(:lettings_log, :completed, needstype: 2, location:) }
it "does not add an error" do context "and the local authority is active for supported housing log" do
property_validator.validate_la_is_active(log) let(:location) { create(:location, location_code: la_ecode_active) }
expect(log.errors["scheme_id"]).to be_empty let(:log) { build(:lettings_log, :completed, startdate:, needstype: 2, location:) }
expect(log.errors["location_id"]).to be_empty
expect(log.errors["startdate"]).to be_empty it "does not add an error" do
expect(log.errors["la"]).to be_empty property_validator.validate_la_is_active(log)
expect(log.errors["postcode_full"]).to be_empty expect(log.errors["scheme_id"]).to be_empty
expect(log.errors["uprn"]).to be_empty expect(log.errors["location_id"]).to be_empty
expect(log.errors["uprn_selection"]).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 "and the local authority is inactive for supported housing log" do context "and the local authority is inactive for supported housing log" do
let(:location) { create(:location, location_code: la_ecode_inactive) } let(:location) { create(:location, location_code: la_ecode_inactive) }
let(:log) { build(:lettings_log, :completed, needstype: 2, location:) } 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 context "and the inactive local authority is linked to an active one" do
it "adds an error" do it "does not add an error" do
property_validator.validate_la_is_active(log) LocalAuthorityLink.create!(local_authority: local_authority_inactive, linked_local_authority: local_authority_active)
expect(log.errors["scheme_id"]).to include(I18n.t("validations.lettings.property.scheme_id.la_not_valid_for_date", la: local_authority_inactive.name)) property_validator.validate_la_is_active(log)
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["scheme_id"]).to be_empty
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["location_id"]).to be_empty
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["startdate"]).to be_empty
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["la"]).to be_empty
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["postcode_full"]).to be_empty
expect(log.errors["uprn_selection"]).to include(I18n.t("validations.lettings.property.uprn_selection.la_not_valid_for_date", la: local_authority_inactive.name)) expect(log.errors["uprn"]).to be_empty
expect(log.errors["uprn_selection"]).to be_empty
end
end 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 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) property_validator.validate_la_is_active(log)
expect(log.errors["scheme_id"]).to be_empty expect(log.errors["scheme_id"]).to be_empty
expect(log.errors["location_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 expect(log.errors["uprn_selection"]).to be_empty
end end
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
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 } subject(:sale_information_validator) { validator_class.new }
let(:validator_class) { Class.new { include Validations::Sales::SaleInformationValidations } } let(:validator_class) { Class.new { include Validations::Sales::SaleInformationValidations } }
let(:saledate) { current_collection_start_date }
describe "#validate_practical_completion_date" do describe "#validate_practical_completion_date" do
context "when hodate blank" do context "when hodate blank" do
@ -59,8 +60,10 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end end
context "when hodate 3 or more years before saledate" do 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 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 it "does add an error" do
sale_information_validator.validate_practical_completion_date(record) sale_information_validator.validate_practical_completion_date(record)
@ -71,7 +74,7 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end end
context "and form year is 2025 or later" do 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 it "does not add an error" do
sale_information_validator.validate_practical_completion_date(record) sale_information_validator.validate_practical_completion_date(record)
@ -376,10 +379,8 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end end
describe "#validate_discounted_ownership_value" do 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 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 context "and not provided" do
before do before do
@ -453,7 +454,7 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end end
context "when discount is routed to" do 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 context "and not provided" do
it "returns false" do it "returns false" do
@ -475,72 +476,159 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end end
context "and is provided" do context "and is provided" do
it "does not add errors if mortgage and deposit total equals market value - discount" do context "and is 2025" do
record.value = 30_000 let(:saledate) { collection_start_date_for_year(2025) }
record.mortgage = 10_000
record.deposit = 5_000
record.discount = 50
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 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["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.05% x market value tolerance of market value - discount" do expect(record.errors["mortgageused"]).to be_empty
record.value = 123_000 expect(record.errors["mortgage"]).to be_empty
record.mortgage = 66_112 expect(record.errors["value"]).to be_empty
record.deposit = 0 expect(record.errors["deposit"]).to be_empty
record.discount = 46.3 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 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["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.05% x market value tolerance of market value - discount" do expect(record.errors["mortgageused"]).to be_empty
record.value = 123_000 expect(record.errors["mortgage"]).to be_empty
record.mortgage = 66_113 expect(record.errors["value"]).to be_empty
record.deposit = 0 expect(record.errors["deposit"]).to be_empty
record.discount = 46.3 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.") it "does not add errors if mortgage and deposit total is exactly 0.05% x market value away from market value - discount" do
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.") record.value = 120_000
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.") record.mortgage = 64_500
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.") record.deposit = 0
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.") record.discount = 46.3
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.") 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 end
it "does not add errors if mortgage and deposit total is exactly 0.05% x market value away from market value - discount" do context "and is 2026" do
record.value = 120_000 let(:saledate) { collection_start_date_for_year(2026) }
record.mortgage = 64_500
record.deposit = 0
record.discount = 46.3
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 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["mortgageused"]).to be_empty
expect(record.errors["deposit"]).to be_empty expect(record.errors["mortgage"]).to be_empty
expect(record.errors["ownershipsch"]).to be_empty expect(record.errors["value"]).to be_empty
expect(record.errors["discount"]).to be_empty expect(record.errors["deposit"]).to be_empty
expect(record.errors["grant"]).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 end
end end
@ -663,58 +751,6 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
expect(record.errors["grant"]).to be_empty expect(record.errors["grant"]).to be_empty
end end
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 end
describe "#validate_outright_sale_value_matches_mortgage_plus_deposit" do 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 RSpec.describe Validations::Sales::SoftValidations do
include CollectionTimeHelper include CollectionTimeHelper
let(:record) { build(:sales_log) } let(:saledate) { current_collection_start_date }
let(:record) { build(:sales_log, saledate:) }
describe "income validations" do describe "income validations" do
context "when validating soft range based on ecstat" 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 expect(record).not_to be_mortgage_plus_deposit_less_than_discounted_value
end end
it "returns true if the deposit and mortgage add up to less than the discounted value" do context "and 2025", metadata: { year: 25 } do
record.value = 500_000 let(:saledate) { collection_start_date_for_year(2025) }
record.discount = 10
record.mortgage = 200_000 it "returns true if the deposit and mortgage add up to less than the discounted value" do
record.deposit = 200_000 record.value = 500_000
expect(record).to be_mortgage_plus_deposit_less_than_discounted_value 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
end end
context "when validating extra borrowing" do 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 it "returns false for logs from 2024 onwards" do
record.extrabor = 2 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) } let(:user) { create(:user, :support) }
before do 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(user).to receive(:need_two_factor_authentication?).and_return(false)
allow(storage_service).to receive(:file_exists?).and_return(true) allow(storage_service).to receive(:file_exists?).and_return(true)
sign_in user sign_in user
end end
after do
Timecop.return
end
it "displays collection resources" do it "displays collection resources" do
get collection_resources_path get collection_resources_path
@ -122,7 +126,7 @@ RSpec.describe CollectionResourcesController, type: :request do
context "when the collection year has not started yet" do context "when the collection year has not started yet" do
before 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 get collection_resources_path
end end
@ -131,8 +135,8 @@ RSpec.describe CollectionResourcesController, type: :request do
end end
it "displays next year banner" do 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("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 2025 to 2026 collection resources to users", href: confirm_mandatory_collection_resources_release_path(year: 2025)) 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
end end
@ -176,7 +180,7 @@ RSpec.describe CollectionResourcesController, type: :request do
context "when the collection year has not started yet" do context "when the collection year has not started yet" do
before 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 get collection_resources_path
end end
@ -185,8 +189,8 @@ RSpec.describe CollectionResourcesController, type: :request do
end end
it "displays next year banner" do 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("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 2025 to 2026 collection resources, you will be able to release them 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 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 it "displays a success banner" do
follow_redirect! 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
end end

76
spec/requests/start_controller_spec.rb

@ -1,6 +1,8 @@
require "rails_helper" require "rails_helper"
RSpec.describe StartController, type: :request do RSpec.describe StartController, type: :request do
include CollectionTimeHelper
let(:user) { create(:user) } let(:user) { create(:user) }
let(:page) { Capybara::Node::Simple.new(response.body) } let(:page) { Capybara::Node::Simple.new(response.body) }
let(:notify_client) { instance_double(Notifications::Client) } let(:notify_client) { instance_double(Notifications::Client) }
@ -322,41 +324,67 @@ RSpec.describe StartController, type: :request do
end end
end end
context "and 2024 collection window is open for editing" do context "and previous collection window is open for editing" do
before do before do
create(:collection_resource, :additional, year: 2024, log_type: "sales", display_name: "sales additional resource (2024 to 2025)") 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})")
allow(Time).to receive(:now).and_return(Time.zone.local(2025, 4, 1)) Timecop.freeze(current_collection_start_date)
end
after do
Timecop.return
end 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 get root_path
expect(page).to have_content("Lettings 25/26") expect(page).to have_content("Lettings #{current_collection_range_slash}")
expect(page).to have_content("Lettings 24/25") expect(page).to have_content("Lettings #{previous_collection_range_slash}")
expect(page).to have_content("Lettings 2025 to 2026") expect(page).to have_content("Lettings #{current_collection_range_to}")
expect(page).to have_content("Lettings 2024 to 2025") expect(page).to have_content("Lettings #{previous_collection_range_to}")
expect(page).to have_content("Sales 25/26") expect(page).to have_content("Sales #{current_collection_range_slash}")
expect(page).to have_content("Sales 24/25") expect(page).to have_content("Sales #{previous_collection_range_slash}")
expect(page).to have_content("Sales 2025 to 2026") expect(page).to have_content("Sales #{current_collection_range_to}")
expect(page).to have_content("Sales 2024 to 2025") expect(page).to have_content("Sales #{previous_collection_range_to}")
expect(page).to have_content("Download the sales additional resource (2024 to 2025)") expect(page).to have_content("Download the sales additional resource (#{previous_collection_range_to})")
end end
end end
context "and 2024 collection window is closed for editing" do context "and previous collection window is closed for editing" do
before 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 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 get root_path
expect(page).to have_content("Lettings 25/26") expect(page).to have_content("Lettings #{current_collection_range_slash}")
expect(page).not_to have_content("Lettings 24/25") expect(page).not_to have_content("Lettings #{previous_collection_range_slash}")
expect(page).to have_content("Lettings 2025 to 2026") expect(page).to have_content("Lettings #{current_collection_range_to}")
expect(page).not_to have_content("Lettings 2024 to 2025") expect(page).not_to have_content("Lettings #{previous_collection_range_to}")
expect(page).to have_content("Sales 25/26") expect(page).to have_content("Sales #{current_collection_range_slash}")
expect(page).not_to have_content("Sales 24/25") expect(page).not_to have_content("Sales #{previous_collection_range_slash}")
expect(page).to have_content("Sales 2025 to 2026") expect(page).to have_content("Sales #{current_collection_range_to}")
expect(page).not_to have_content("Sales 2024 to 2025") expect(page).not_to have_content("Sales #{previous_collection_range_to}")
end end
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 context "when file has too many columns" do
before 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 file.rewind
end end
after do
Timecop.return
end
it "is not valid" do it "is not valid" do
expect(validator).not_to be_valid expect(validator).not_to be_valid
end end

Loading…
Cancel
Save