Browse Source

Merge branch 'main' into CLDC-3787-Autocomplete-address-search

pull/2922/head
Manny Dinssa 2 weeks ago committed by GitHub
parent
commit
3189330430
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 13
      .github/workflows/run_tests.yml
  2. 81
      app/helpers/collection_deadline_helper.rb
  3. 23
      app/helpers/collection_time_helper.rb
  4. 3
      app/models/derived_variables/lettings_log_variables.rb
  5. 21
      app/models/form/lettings/questions/previous_let_type.rb
  6. 80
      app/models/form/lettings/questions/reason.rb
  7. 13
      app/models/form/lettings/questions/rent_type.rb
  8. 9
      app/models/lettings_log.rb
  9. 7
      app/models/log.rb
  10. 32
      app/views/home/_upcoming_deadlines.html.erb
  11. 56
      config/locales/forms/2023/sales/soft_validations.en.yml
  12. 2
      config/locales/forms/2024/lettings/soft_validations.en.yml
  13. 60
      config/locales/forms/2024/sales/soft_validations.en.yml
  14. 60
      config/locales/forms/2025/sales/soft_validations.en.yml
  15. 2
      docs/adr/index.md
  16. 118
      docs/csv_downloads.md
  17. 2
      docs/documentation_website.md
  18. 5
      spec/factories/sales_log.rb
  19. 111
      spec/features/home_page_spec.rb
  20. 2
      spec/fixtures/files/sales_logs_csv_export_codes_23.csv
  21. 2
      spec/fixtures/files/sales_logs_csv_export_codes_24.csv
  22. 2
      spec/fixtures/files/sales_logs_csv_export_labels_23.csv
  23. 2
      spec/fixtures/files/sales_logs_csv_export_labels_24.csv
  24. 2
      spec/fixtures/files/sales_logs_csv_export_non_support_labels_24.csv
  25. 29
      spec/helpers/collection_deadline_helper_spec.rb
  26. 23
      spec/helpers/collection_time_helper_spec.rb
  27. 4
      spec/models/bulk_upload_spec.rb
  28. 1
      spec/models/form/lettings/pages/rent_type_spec.rb
  29. 21
      spec/models/form/lettings/questions/previous_let_type_spec.rb
  30. 45
      spec/models/form/lettings/questions/reason_spec.rb
  31. 9
      spec/models/form/lettings/questions/rent_type_spec.rb
  32. 36
      spec/models/lettings_log_derived_fields_spec.rb
  33. 24
      spec/models/log_spec.rb
  34. 3
      spec/models/validations/household_validations_spec.rb

13
.github/workflows/run_tests.yml

@ -69,6 +69,11 @@ jobs:
run: |
bundle exec rake assets:precompile
# This is temporary to fix flaky parallel tests due to `secret_key_base` being read before it's set
- name: Create local secret
run: |
echo $(ruby -e "require 'securerandom'; puts SecureRandom.hex(64)") > tmp/local_secret.txt
- name: Run tests
run: |
bundle exec rake parallel:spec['spec\/(?!features|models|requests|services)']
@ -244,6 +249,10 @@ jobs:
run: |
bundle exec rake assets:precompile
- name: Create local secret
run: |
echo $(ruby -e "require 'securerandom'; puts SecureRandom.hex(64)") > tmp/local_secret.txt
- name: Run tests
run: |
bundle exec rake parallel:spec['spec/requests']
@ -303,6 +312,10 @@ jobs:
run: |
bundle exec rake assets:precompile
- name: Create local secret
run: |
echo $(ruby -e "require 'securerandom'; puts SecureRandom.hex(64)") > tmp/local_secret.txt
- name: Run tests
run: |
bundle exec rake parallel:spec['spec\/services']

81
app/helpers/collection_deadline_helper.rb

@ -0,0 +1,81 @@
module CollectionDeadlineHelper
include CollectionTimeHelper
QUARTERLY_DEADLINES = {
2024 => {
first_quarter_deadline: Time.zone.local(2024, 7, 12),
second_quarter_deadline: Time.zone.local(2024, 10, 11),
third_quarter_deadline: Time.zone.local(2025, 1, 10),
fourth_quarter_deadline: Time.zone.local(2025, 6, 6), # Same as submission deadline
},
2025 => {
first_quarter_deadline: Time.zone.local(2025, 7, 11),
second_quarter_deadline: Time.zone.local(2025, 10, 10),
third_quarter_deadline: Time.zone.local(2026, 1, 16),
fourth_quarter_deadline: Time.zone.local(2026, 6, 5), # Same as submission deadline
},
}.freeze
def quarterly_cutoff_date(quarter, year)
send("#{quarter}_quarter", year)[:cutoff_date].strftime("%A %-d %B %Y")
end
def quarter_deadlines_for_year(year)
QUARTERLY_DEADLINES[year]
end
def first_quarter(year)
{
cutoff_date: quarter_deadlines_for_year(year)[:first_quarter_deadline],
start_date: Time.zone.local(year, 4, 1),
end_date: Time.zone.local(year, 6, 30),
}
end
def second_quarter(year)
{
cutoff_date: quarter_deadlines_for_year(year)[:second_quarter_deadline],
start_date: Time.zone.local(year, 7, 1),
end_date: Time.zone.local(year, 9, 30),
}
end
def third_quarter(year)
{
cutoff_date: quarter_deadlines_for_year(year)[:third_quarter_deadline],
start_date: Time.zone.local(year, 10, 1),
end_date: Time.zone.local(year, 12, 31),
}
end
def fourth_quarter(year)
{
cutoff_date: quarter_deadlines_for_year(year)[:fourth_quarter_deadline],
start_date: Time.zone.local(year + 1, 1, 1),
end_date: Time.zone.local(year + 1, 3, 31),
}
end
def quarter_dates(year)
[
first_quarter(year).merge(quarter: "Q1"),
second_quarter(year).merge(quarter: "Q2"),
third_quarter(year).merge(quarter: "Q3"),
]
end
def quarter_for_date(date: Time.zone.now)
quarters = quarter_dates(current_collection_start_year)
quarter = quarters.find { |q| date.between?(q[:start_date], q[:cutoff_date] + 1.day) }
return unless quarter
OpenStruct.new(
quarter: quarter[:quarter],
cutoff_date: quarter[:cutoff_date],
quarter_start_date: quarter[:start_date],
quarter_end_date: quarter[:end_date],
)
end
end

23
app/helpers/collection_time_helper.rb

@ -30,6 +30,10 @@ module CollectionTimeHelper
Time.zone.local(current_collection_start_year + 1, 3, 31).end_of_day
end
def current_collection_end_year
current_collection_start_year + 1
end
def previous_collection_end_date
current_collection_end_date - 1.year
end
@ -49,23 +53,4 @@ module CollectionTimeHelper
def archived_collection_start_year
current_collection_start_year - 2
end
def quarter_for_date(date: Time.zone.now)
quarters = [
{ quarter: "Q1", cutoff_date: Time.zone.local(2024, 7, 12), start_date: Time.zone.local(2024, 4, 1), end_date: Time.zone.local(2024, 6, 30) },
{ quarter: "Q2", cutoff_date: Time.zone.local(2024, 10, 11), start_date: Time.zone.local(2024, 7, 1), end_date: Time.zone.local(2024, 9, 30) },
{ quarter: "Q3", cutoff_date: Time.zone.local(2025, 1, 10), start_date: Time.zone.local(2024, 10, 1), end_date: Time.zone.local(2024, 12, 31) },
]
quarter = quarters.find { |q| date.between?(q[:start_date], q[:cutoff_date] + 1.day) }
return unless quarter
OpenStruct.new(
quarter: quarter[:quarter],
cutoff_date: quarter[:cutoff_date],
quarter_start_date: quarter[:start_date],
quarter_end_date: quarter[:end_date],
)
end
end

3
app/models/derived_variables/lettings_log_variables.rb

@ -8,6 +8,7 @@ module DerivedVariables::LettingsLogVariables
3 => 3, # "Rent to Buy" => "Intermediate Rent"
4 => 3, # "London Living Rent" => "Intermediate Rent"
5 => 3, # "Other intermediate rent product" => "Intermediate Rent"
6 => 4, # "Specified accommodation - exempt accommodation, managed properties, refuges and local authority hostels" => "Specified accommodation"
}.freeze
UNITLETAS_MAPPING = {
@ -17,6 +18,7 @@ module DerivedVariables::LettingsLogVariables
3 => 6, # "Rent to Buy" => "Rent to Buy basis"
4 => 7, # "London Living Rent" => "London Living Rent basis"
5 => 8, # "Other intermediate rent product" => "Another Intermediate Rent basis"
6 => 9, # "Specified accommodation - exempt accommodation, managed properties, refuges and local authority hostels" => "Specified accommodation - exempt accommodation, manged properties, refuges and local authority hostels"
}.freeze
RENTTYPE_DETAIL_MAPPING = {
@ -26,6 +28,7 @@ module DerivedVariables::LettingsLogVariables
3 => 4,
4 => 5,
5 => 6,
6 => 7,
}.freeze
def set_derived_fields!

21
app/models/form/lettings/questions/previous_let_type.rb

@ -4,7 +4,7 @@ class Form::Lettings::Questions::PreviousLetType < ::Form::Question
@id = "unitletas"
@type = "radio"
@check_answers_card_number = 0
@answer_options = form.start_year_2024_or_later? ? ANSWER_OPTIONS_AFTER_2024 : ANSWER_OPTIONS
@answer_options = answer_options
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end
@ -30,5 +30,24 @@ class Form::Lettings::Questions::PreviousLetType < ::Form::Question
"3" => { "value" => "Don’t know" },
}.freeze
ANSWER_OPTIONS_AFTER_2025 = {
"1" => { "value" => "Social rent basis" },
"2" => { "value" => "Affordable rent basis" },
"5" => { "value" => "London Affordable Rent basis" },
"6" => { "value" => "Rent to Buy basis" },
"7" => { "value" => "London Living Rent basis" },
"8" => { "value" => "Another Intermediate Rent basis" },
"9" => { "value" => "Specified accommodation - exempt accommodation, manged properties, refuges and local authority hostels" },
"divider" => { "value" => true },
"3" => { "value" => "Don’t know" },
}.freeze
QUESTION_NUMBER_FROM_YEAR = { 2023 => 16, 2024 => 17 }.freeze
def answer_options
return ANSWER_OPTIONS_AFTER_2025 if form.start_year_2025_or_later?
return ANSWER_OPTIONS_AFTER_2024 if form.start_year_2024_or_later?
ANSWER_OPTIONS
end
end

80
app/models/form/lettings/questions/reason.rb

@ -14,8 +14,8 @@ class Form::Lettings::Questions::Reason < ::Form::Question
end
def answer_options
if form.start_year_2024_or_later?
{
if form.start_year_2025_or_later?
return {
"50" => { "value" => "End of social or private sector tenancy - no fault" },
"51" => { "value" => "End of social or private sector tenancy - evicted due to anti-social behaviour (ASB)" },
"52" => { "value" => "End of social or private sector tenancy - evicted due to rent arrears" },
@ -25,6 +25,7 @@ class Form::Lettings::Questions::Reason < ::Form::Question
"45" => { "value" => "Discharged from prison" },
"46" => { "value" => "Discharged from long-stay hospital or similar institution" },
"4" => { "value" => "Loss of tied accommodation" },
"55" => { "value" => "Leaving foster care or children's home" },
"9" => { "value" => "Asked to leave by family or friends" },
"8" => { "value" => "Relationship breakdown (non-violent) with partner" },
"44" => { "value" => "Death of household member in last settled accommodation" },
@ -49,46 +50,83 @@ class Form::Lettings::Questions::Reason < ::Form::Question
"divider" => { "value" => true },
"47" => { "value" => "Tenant prefers not to say" },
}.freeze
else
{
"40" => { "value" => "End of assured shorthold tenancy (no fault)" },
"41" => { "value" => "End of assured shorthold tenancy (eviction or tenant at fault)" },
"42" => { "value" => "End of fixed term tenancy (no fault)" },
"43" => { "value" => "End of fixed term tenancy (eviction or tenant at fault)" },
end
if form.start_year_2024_or_later?
return {
"50" => { "value" => "End of social or private sector tenancy - no fault" },
"51" => { "value" => "End of social or private sector tenancy - evicted due to anti-social behaviour (ASB)" },
"52" => { "value" => "End of social or private sector tenancy - evicted due to rent arrears" },
"53" => { "value" => "End of social or private sector tenancy - evicted for any other reason" },
"1" => { "value" => "Permanently decanted from another property owned by this landlord" },
"46" => { "value" => "Discharged from long-stay hospital or similar institution" },
"45" => { "value" => "Discharged from prison" },
"2" => { "value" => "Left home country as a refugee" },
"45" => { "value" => "Discharged from prison" },
"46" => { "value" => "Discharged from long-stay hospital or similar institution" },
"4" => { "value" => "Loss of tied accommodation" },
"9" => { "value" => "Asked to leave by family or friends" },
"44" => { "value" => "Death of household member in last settled accommodation" },
"8" => { "value" => "Relationship breakdown (non-violent) with partner" },
"44" => { "value" => "Death of household member in last settled accommodation" },
"16" => { "value" => "To move nearer to family, friends or school" },
"17" => { "value" => "To move nearer to work" },
"48" => { "value" => "Domestic abuse - previously joint tenancy with partner" },
"49" => { "value" => "Domestic abuse - other" },
"31" => { "value" => "Hate crime" },
"10" => { "value" => "Racial harassment" },
"31" => { "value" => "Hate crime" },
"11" => { "value" => "Other problems with neighbours" },
"35" => { "value" => "Couldn’t afford fees attached to renewing the tenancy" },
"36" => { "value" => "Couldn’t afford increase in rent" },
"38" => { "value" => "Couldn’t afford rent or mortgage (employment)" },
"37" => { "value" => "Couldn’t afford rent or mortgage (welfare reforms)" },
"39" => { "value" => "Couldn’t afford rent or mortgage (other)" },
"34" => { "value" => "Repossession" },
"54" => { "value" => "Could no longer afford rent or mortgage" },
"12" => { "value" => "Property unsuitable because of overcrowding" },
"13" => { "value" => "Property unsuitable because of ill health or disability" },
"14" => { "value" => "Property unsuitable because of poor condition" },
"29" => { "value" => "Under occupation (offered incentive to downsize)" },
"30" => { "value" => "Under occupation (no incentive)" },
"18" => { "value" => "To move to accommodation with support" },
"19" => { "value" => "To move to independent accommodation" },
"30" => { "value" => "Under occupation (no incentive)" },
"29" => { "value" => "Under occupation (offered incentive to downsize)" },
"20" => { "value" => "Other" },
"47" => { "value" => "Tenant prefers not to say" },
"divider" => { "value" => true },
"28" => { "value" => "Don’t know" },
"divider" => { "value" => true },
"47" => { "value" => "Tenant prefers not to say" },
}.freeze
end
{
"40" => { "value" => "End of assured shorthold tenancy (no fault)" },
"41" => { "value" => "End of assured shorthold tenancy (eviction or tenant at fault)" },
"42" => { "value" => "End of fixed term tenancy (no fault)" },
"43" => { "value" => "End of fixed term tenancy (eviction or tenant at fault)" },
"1" => { "value" => "Permanently decanted from another property owned by this landlord" },
"46" => { "value" => "Discharged from long-stay hospital or similar institution" },
"45" => { "value" => "Discharged from prison" },
"2" => { "value" => "Left home country as a refugee" },
"4" => { "value" => "Loss of tied accommodation" },
"9" => { "value" => "Asked to leave by family or friends" },
"44" => { "value" => "Death of household member in last settled accommodation" },
"8" => { "value" => "Relationship breakdown (non-violent) with partner" },
"16" => { "value" => "To move nearer to family, friends or school" },
"17" => { "value" => "To move nearer to work" },
"48" => { "value" => "Domestic abuse - previously joint tenancy with partner" },
"49" => { "value" => "Domestic abuse - other" },
"31" => { "value" => "Hate crime" },
"10" => { "value" => "Racial harassment" },
"11" => { "value" => "Other problems with neighbours" },
"35" => { "value" => "Couldn’t afford fees attached to renewing the tenancy" },
"36" => { "value" => "Couldn’t afford increase in rent" },
"38" => { "value" => "Couldn’t afford rent or mortgage (employment)" },
"37" => { "value" => "Couldn’t afford rent or mortgage (welfare reforms)" },
"39" => { "value" => "Couldn’t afford rent or mortgage (other)" },
"34" => { "value" => "Repossession" },
"12" => { "value" => "Property unsuitable because of overcrowding" },
"13" => { "value" => "Property unsuitable because of ill health or disability" },
"14" => { "value" => "Property unsuitable because of poor condition" },
"18" => { "value" => "To move to accommodation with support" },
"19" => { "value" => "To move to independent accommodation" },
"30" => { "value" => "Under occupation (no incentive)" },
"29" => { "value" => "Under occupation (offered incentive to downsize)" },
"20" => { "value" => "Other" },
"47" => { "value" => "Tenant prefers not to say" },
"divider" => { "value" => true },
"28" => { "value" => "Don’t know" },
}.freeze
end
QUESTION_NUMBER_FROM_YEAR = { 2023 => 77, 2024 => 76 }.freeze

13
app/models/form/lettings/questions/rent_type.rb

@ -5,28 +5,33 @@ class Form::Lettings::Questions::RentType < ::Form::Question
@copy_key = "lettings.setup.rent_type.rent_type"
@type = "radio"
@top_guidance_partial = "rent_type_definitions"
@answer_options = form.start_year_2024_or_later? ? ANSWER_OPTIONS_2024 : ANSWER_OPTIONS
@answer_options = answer_options
@conditional_for = { "irproduct_other" => [5] }
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] if form.start_date.present?
end
ANSWER_OPTIONS = {
"0" => { "value" => "Social Rent" },
"1" => { "value" => "Affordable Rent" },
"2" => { "value" => "London Affordable Rent" },
"4" => { "value" => "London Living Rent" },
"3" => { "value" => "Rent to Buy" },
"0" => { "value" => "Social Rent" },
"4" => { "value" => "London Living Rent" },
"5" => { "value" => "Other intermediate rent product" },
}.freeze
ANSWER_OPTIONS_2024 = {
ANSWER_OPTIONS_2025 = {
"0" => { "value" => "Social Rent" },
"1" => { "value" => "Affordable Rent" },
"2" => { "value" => "London Affordable Rent" },
"3" => { "value" => "Rent to Buy" },
"4" => { "value" => "London Living Rent" },
"5" => { "value" => "Other intermediate rent product" },
"6" => { "value" => "Specified accommodation - exempt accommodation, managed properties, refuges and local authority hostels" },
}.freeze
QUESTION_NUMBER_FROM_YEAR = { 2023 => 6, 2024 => 8 }.freeze
def answer_options
form.start_year_2025_or_later? ? ANSWER_OPTIONS_2025 : ANSWER_OPTIONS
end
end

9
app/models/lettings_log.rb

@ -146,7 +146,7 @@ class LettingsLog < Log
AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze
OPTIONAL_FIELDS = %w[tenancycode propcode chcharge].freeze
RENT_TYPE_MAPPING_LABELS = { 1 => "Social Rent", 2 => "Affordable Rent", 3 => "Intermediate Rent" }.freeze
RENT_TYPE_MAPPING_LABELS = { 1 => "Social Rent", 2 => "Affordable Rent", 3 => "Intermediate Rent", 4 => "Specified accommodation" }.freeze
HAS_BENEFITS_OPTIONS = [1, 6, 8, 7].freeze
NUM_OF_WEEKS_FROM_PERIOD = { 2 => 26, 3 => 13, 4 => 12, 5 => 50, 6 => 49, 7 => 48, 8 => 47, 9 => 46, 1 => 52, 10 => 53 }.freeze
SUFFIX_FROM_PERIOD = { 2 => "every 2 weeks", 3 => "every 4 weeks", 4 => "every month" }.freeze
@ -158,6 +158,7 @@ class LettingsLog < Log
rent_to_buy: 3,
london_living_rent: 4,
other_intermediate_rent_product: 5,
specified_accommodation: 6,
}.freeze
def form
@ -830,6 +831,12 @@ private
elsif is_general_needs?
owning_organisation[:provider_type] == "PRP" ? 9 : 11
end
when "Specified accommodation"
if is_supported_housing?
owning_organisation[:provider_type] == "PRP" ? 14 : 16
elsif is_general_needs?
owning_organisation[:provider_type] == "PRP" ? 13 : 15
end
end
end

7
app/models/log.rb

@ -250,6 +250,11 @@ class Log < ApplicationRecord
end
def blank_compound_invalid_non_setup_fields!
if errors.attribute_names.include? :postcode_full
self.postcode_known = nil if lettings?
self.pcodenk = nil if sales?
end
self.ppcodenk = nil if errors.attribute_names.include? :ppostcode_full
self.previous_la_known = nil if errors.attribute_names.include? :prevloc
@ -260,6 +265,8 @@ class Log < ApplicationRecord
self.address_line2 = nil
self.town_or_city = nil
self.postcode_full = nil
self.postcode_known = nil if lettings?
self.pcodenk = nil if sales?
self.county = nil
process_postcode_changes!
end

32
app/views/home/_upcoming_deadlines.html.erb

@ -1,33 +1,33 @@
<h1 class="govuk-heading-l">Upcoming deadlines</h1>
<% previous_lettings_form = FormHandler.instance.previous_lettings_form %>
<% current_lettings_form = FormHandler.instance.current_lettings_form %>
<% crossover_before_submission_deadline = FormHandler.instance.in_crossover_period? && previous_lettings_form.submission_deadline > Time.zone.now %>
<% open_lettings_form = FormHandler.instance.in_crossover_period? ? FormHandler.instance.previous_lettings_form : FormHandler.instance.current_lettings_form %>
<% formatted_deadline = "#{open_lettings_form.submission_deadline.strftime('%A')} #{open_lettings_form.submission_deadline.to_formatted_s(:govuk_date)}" %>
<% if FormHandler.instance.in_crossover_period? %>
<p class="govuk-body govuk-body-m"><strong>End of year deadline - <%= formatted_deadline %>:</strong> Deadline to submit logs for tenancies starting between <%= open_lettings_form.start_date.to_formatted_s(:govuk_date) %> to <%= collection_end_date(open_lettings_form.start_date).to_formatted_s(:govuk_date) %></p>
<% if crossover_before_submission_deadline %>
<p class="govuk-body govuk-body-m"><strong>End of year deadline - <%= previous_lettings_form.submission_deadline.strftime("%A %-d %B %Y") %>:</strong> Deadline to submit logs for tenancies starting between <%= previous_lettings_form.start_date.to_formatted_s(:govuk_date) %> to <%= collection_end_date(previous_lettings_form.start_date).to_formatted_s(:govuk_date) %></p>
<% end %>
<% current_quarter = quarter_for_date(date: Time.zone.now) %>
<% if current_quarter.present? %>
<p class="govuk-body govuk-body-m"><strong><%= "#{current_quarter.quarter} - #{current_quarter.cutoff_date.strftime('%A')} #{current_quarter.cutoff_date.to_formatted_s(:govuk_date)}" %>:</strong> Quarterly cut off date for tenancies and sales starting between <%= current_quarter.quarter_start_date.to_formatted_s(:govuk_date) %> and <%= current_quarter.quarter_end_date.to_formatted_s(:govuk_date) %>.</p>
<p class="govuk-body govuk-body-m"><strong><%= "#{current_quarter.quarter} - #{current_quarter.cutoff_date.strftime('%A %-d %B %Y')}" %>:</strong> Quarterly cut off date for tenancies and sales starting between <%= current_quarter.quarter_start_date.to_formatted_s(:govuk_date) %> and <%= current_quarter.quarter_end_date.to_formatted_s(:govuk_date) %>.</p>
<% end %>
<% if !FormHandler.instance.in_crossover_period? %>
<% unless crossover_before_submission_deadline %>
<p class="govuk-body govuk-body-m">Try to complete your logs for each quarter by the cut-off date.</p>
<p class="govuk-body govuk-body-m">You can still create logs for a previous quarter after its cut-off date, as long as you complete them by the <strong>end-of-year deadline: <%= formatted_deadline %>.</strong></p>
<p class="govuk-body govuk-body-m">You can still create logs for a previous quarter after its cut-off date, as long as you complete them by the <strong>end of year deadline: <%= current_lettings_form.submission_deadline.strftime("%A %-d %B %Y") %>.</strong></p>
<% end %>
<% if FormHandler.instance.in_crossover_period? %>
<% previous_lettings_form = FormHandler.instance.previous_lettings_form %>
<p class="govuk-body govuk-body-m">Prioritise completing logs for the closing collection year. You must complete all <%= previous_lettings_form.start_date.year %> to <%= previous_lettings_form.submission_deadline.year %> logs must by the end-of-year deadline. You can still create <%= open_lettings_form.start_date.year %> to <%= open_lettings_form.submission_deadline.year %> logs for this quarter after the quarterly cut-off date.</p>
<% if crossover_before_submission_deadline %>
<p class="govuk-body govuk-body-m">Prioritise completing logs for the closing collection year. You must complete all <%= previous_lettings_form.start_date.year %> to <%= previous_lettings_form.submission_deadline.year %> logs by the end of year deadline. You can still create <%= current_lettings_form.start_date.year %> to <%= current_lettings_form.submission_deadline.year %> logs for this quarter after the quarterly cut-off date.</p>
<% end %>
<%= govuk_details(summary_text: "Quarterly cut-off dates for 2024 to 2025") do %>
<p class="govuk-body govuk-body-m">The 2024 to 2025 quarterly cut-off dates are:</p>
<%= govuk_details(summary_text: "Quarterly cut-off dates for #{current_collection_start_year} to #{current_collection_end_year}") do %>
<p class="govuk-body govuk-body-m">The <%= current_collection_start_year %> to <%= current_collection_end_year %> quarterly cut-off dates are:</p>
<ul class="govuk-list govuk-list--bullet">
<li class="govuk-!-padding-bottom-4"><strong>Q1 - Friday 12 July 2024:</strong> Quarterly cut-off date for tenancies and sales starting between 1 April 2024 and 30 June 2024.</li>
<li class="govuk-!-padding-bottom-4"><strong>Q2 - Friday 11 October 2024:</strong> Quarterly cut-off date for tenancies and sales starting between 1 July 2024 and 30 September 2024.</li>
<li class="govuk-!-padding-bottom-4"><strong>Q3 - Friday 10 January 2025:</strong> Quarterly cut-off date for tenancies and sales starting between 1 October 2024 and 31 December 2024.</li>
<li class="govuk-!-padding-bottom-4"><strong>End of year deadline - Friday 6 June 2025:</strong> Deadline for tenancies and sales starting between 1 January 2025 and 31 March 2025, plus any late submissions for the 2024 to 2025 collection year.</li>
<li class="govuk-!-padding-bottom-4"><strong>Q1 - <%= quarterly_cutoff_date("first", current_collection_start_year) %>:</strong> Quarterly cut-off date for tenancies and sales starting between 1 April <%= current_collection_start_year %> and 30 June <%= current_collection_start_year %>.</li>
<li class="govuk-!-padding-bottom-4"><strong>Q2 - <%= quarterly_cutoff_date("second", current_collection_start_year) %>:</strong> Quarterly cut-off date for tenancies and sales starting between 1 July <%= current_collection_start_year %> and 30 September <%= current_collection_start_year %>.</li>
<li class="govuk-!-padding-bottom-4"><strong>Q3 - <%= quarterly_cutoff_date("third", current_collection_start_year) %>:</strong> Quarterly cut-off date for tenancies and sales starting between 1 October <%= current_collection_start_year %> and 31 December <%= current_collection_start_year %>.</li>
<li class="govuk-!-padding-bottom-4"><strong>End of year deadline - <%= quarterly_cutoff_date("fourth", current_collection_start_year) %>:</strong> Deadline for tenancies and sales starting between 1 January <%= current_collection_end_year %> and 31 March <%= current_collection_end_year %>, plus any late submissions for the <%= current_collection_start_year %> to <%= current_collection_end_year %> collection year.</li>
</ul>
<p class="govuk-body govuk-body-m">It is important that you meet these cut-off dates because we submit data to the Office for National Statistics quarterly, helping them create essential inflation statistics.</p>
<p class="govuk-body govuk-body-m">Meeting these cut-off dates also gives you more accurate data for your own analysis, and reduces the burden at the end of the year.</p>

56
config/locales/forms/2023/sales/soft_validations.en.yml

@ -7,7 +7,7 @@ en:
max:
page_header: ""
check_answer_label: "Retirement confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm person isn't retired"
hint_text: ""
question_text: "Are you sure this person isn't retired?"
title_text: "You told us this person is over 66 and not retired."
@ -15,7 +15,7 @@ en:
min:
page_header: ""
check_answer_label: "Retirement confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm person is retired"
hint_text: ""
question_text: "Are you sure this person is retired?"
title_text: "You told us this person is aged %{age} years and retired."
@ -24,7 +24,7 @@ en:
old_persons_shared_ownership_value_check:
page_header: ""
check_answer_label: "Shared ownership confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm shared ownership scheme"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text:
@ -34,7 +34,7 @@ en:
income1_value_check:
check_answer_label: "Buyer 1 income confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm buyer 1 income"
hint_text: ""
question_text: "Are you sure this is correct?"
ecstat:
@ -47,7 +47,7 @@ en:
income2_value_check:
check_answer_label: "Buyer 2 income confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm buyer 2 income"
hint_text: ""
question_text: "Are you sure this is correct?"
ecstat:
@ -61,7 +61,7 @@ en:
combined_income_value_check:
page_header: ""
check_answer_label: "Combined income confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm combined income"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us the combined income of this household is %{combined_income}. This seems high. Are you sure this is correct?"
@ -69,7 +69,7 @@ en:
mortgage_value_check:
page_header: ""
check_answer_label: "Mortgage confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm mortgage amount"
hint_text: ""
question_text: "Are you sure that the mortgage is more than 5 times the income used for the mortgage application?"
title_text: "You told us that the mortgage amount is %{mortgage}."
@ -78,7 +78,7 @@ en:
savings_value_check:
page_header: ""
check_answer_label: "Savings confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm savings amount"
hint_text: ""
question_text: "Are you sure the savings are higher than £100,000?"
joint_purchase:
@ -91,7 +91,7 @@ en:
staircase_bought_value_check:
page_header: ""
check_answer_label: "Percentage bought confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm percentage bought"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that %{percentage}% was bought in this staircasing transaction."
@ -101,7 +101,7 @@ en:
joint_purchase:
page_header: ""
check_answer_label: "Percentage owned confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm percentage owned"
hint_text: ""
question_text: "Are you sure?"
title_text: "You told us that the buyers now own %{stairowned} of the property."
@ -109,7 +109,7 @@ en:
not_joint_purchase:
page_header: ""
check_answer_label: "Percentage owned confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm percentage owned"
hint_text: ""
question_text: "Are you sure?"
title_text: "You told us that the buyer now owns %{stairowned} of the property."
@ -118,7 +118,7 @@ en:
hodate_check:
page_header: ""
check_answer_label: "Practical completion or handover date check"
check_answer_prompt: ""
check_answer_prompt: "Confirm practical completion or handover date"
hint_text: ""
question_text: "Are you sure?"
title_text: "You told us practical completion or handover date is more than 3 years before sale completion date."
@ -126,7 +126,7 @@ en:
value_value_check:
page_header: ""
check_answer_label: "Purchase price confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm purchase price"
hint_text: ""
question_text: "Are you sure?"
title_text: "You told us the purchase price is %{value}."
@ -135,7 +135,7 @@ en:
shared_ownership_deposit_value_check:
page_header: ""
check_answer_label: "Shared ownership deposit confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm shared ownership deposit"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that the %{mortgage_deposit_and_discount_error_fields} add up to %{mortgage_deposit_and_discount_total}."
@ -144,7 +144,7 @@ en:
joint_purchase:
page_header: ""
check_answer_label: "Deposit confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm deposit amount"
hint_text: ""
question_text: "Are you sure that the deposit is this much higher than the buyer's savings?"
title_text: "You told us the buyers’ deposit was %{deposit} and their savings were %{savings}."
@ -152,7 +152,7 @@ en:
not_joint_purchase:
page_header: ""
check_answer_label: "Deposit confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm deposit amount"
hint_text: ""
question_text: "Are you sure that the deposit is this much higher than the buyer's savings?"
title_text: "You told us the buyer’s deposit was %{deposit} and their savings were %{savings}."
@ -161,7 +161,7 @@ en:
wheel_value_check:
page_header: ""
check_answer_label: "Household member uses a wheelchair"
check_answer_prompt: "Tell us if someone uses a wheelchair"
check_answer_prompt: "Confirm household member uses a wheelchair"
hint_text: ""
question_text: "You told us that someone in the household uses a wheelchair."
title_text: "You told us that someone in the household uses a wheelchair."
@ -170,7 +170,7 @@ en:
buyer1:
page_header: ""
check_answer_label: "Buyer live in confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm buyer 1 living situation"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that buyer 1 will not live in the property."
@ -178,7 +178,7 @@ en:
buyer2:
page_header: ""
check_answer_label: "Buyer live in confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm buyer 2 living situation"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that buyer 2 will not live in the property."
@ -187,7 +187,7 @@ en:
student_not_child_value_check:
page_header: ""
check_answer_label: "Student not a child confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm student not a child"
hint_text: ""
question_text: "Are you sure this person is not a child?"
title_text: "You told us this person is a student aged between 16 and 19."
@ -196,7 +196,7 @@ en:
partner_under_16_value_check:
page_header: ""
check_answer_label: "Partner under 16 confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm partner's age"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us this person is aged %{age} years and has 'Partner' relationship to buyer 1."
@ -205,7 +205,7 @@ en:
multiple_partners_value_check:
page_header: ""
check_answer_label: "Multiple partners confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm multiple partners"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us there are more than 1 persons with 'Partner' relationship to buyer 1."
@ -214,7 +214,7 @@ en:
monthly_charges_value_check:
page_header: ""
check_answer_label: "Monthly charges confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm monthly charges"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that the monthly charges were %{mscharge}."
@ -223,7 +223,7 @@ en:
extra_borrowing_value_check:
page_header: ""
check_answer_label: "Extra borrowing confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm extra borrowing"
hint_text: ""
question_text: "Are you sure there is no extra borrowing?"
title_text: "You told us that the mortgage and deposit total is %{mortgage_and_deposit_total}."
@ -232,7 +232,7 @@ en:
percentage_discount_value_check:
page_header: ""
check_answer_label: "Percentage discount confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm percentage discount"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that the percentage discount is %{discount}."
@ -241,7 +241,7 @@ en:
grant_value_check:
page_header: ""
check_answer_label: "Grant value confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm grant value"
hint_text: ""
question_text: "Are you sure? Grants are usually £9,000 - £16,000"
title_text: "You told us that the grant amount is %{grant}."
@ -250,7 +250,7 @@ en:
discounted_sale_value_check:
page_header: ""
check_answer_label: "Discounted sale value confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm discounted sale value"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "Mortgage, deposit, and grant total must equal %{value_with_discount}."
@ -259,7 +259,7 @@ en:
deposit_and_mortgage_value_check:
page_header: ""
check_answer_label: "Deposit and mortgage against discount confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm deposit and mortgage against discount"
hint_text: ""
question_text: "Are you sure? Mortgage and deposit usually equal or are more than (value - discount)"
title_text: "You told us the mortgage amount was %{mortgage}, the cash deposit was %{deposit} and the discount was %{discount}."

2
config/locales/forms/2024/lettings/soft_validations.en.yml

@ -150,7 +150,7 @@ en:
no_address_found:
page_header: ""
check_answer_label: "No address found"
check_answer_prompt: "Try find address"
check_answer_prompt: "Confirm no address found"
hint_text: ""
question_text: "We could not find an address that matches your search. You can search again or continue to enter the address manually."
title_text: "No address found"

60
config/locales/forms/2024/sales/soft_validations.en.yml

@ -7,7 +7,7 @@ en:
max:
page_header: ""
check_answer_label: "Retirement confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm person isn't retired"
hint_text: ""
question_text: "Are you sure this person isn't retired?"
title_text: "You told us this person is over 66 and not retired."
@ -15,7 +15,7 @@ en:
min:
page_header: ""
check_answer_label: "Retirement confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm person is retired"
hint_text: ""
question_text: "Are you sure this person is retired?"
title_text: "You told us this person is aged %{age} years and retired."
@ -23,7 +23,7 @@ en:
old_persons_shared_ownership_value_check:
page_header: ""
check_answer_label: "Shared ownership confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm shared ownership scheme"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text:
@ -32,7 +32,7 @@ en:
informative_text: "At least one buyer must be aged 65 years and over to use this scheme."
income1_value_check:
check_answer_label: "Buyer 1 income confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm buyer 1 income"
hint_text: ""
question_text: "Are you sure this is correct?"
ecstat:
@ -45,7 +45,7 @@ en:
income2_value_check:
check_answer_label: "Buyer 2 income confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm buyer 2 income"
hint_text: ""
question_text: "Are you sure this is correct?"
ecstat:
@ -59,7 +59,7 @@ en:
combined_income_value_check:
page_header: ""
check_answer_label: "Combined income confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm combined income"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us the combined income of this household is %{combined_income}. This seems high. Are you sure this is correct?"
@ -67,7 +67,7 @@ en:
mortgage_value_check:
page_header: ""
check_answer_label: "Mortgage confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm mortgage amount"
hint_text: ""
question_text: "Are you sure that the mortgage is more than 5 times the income used for the mortgage application?"
title_text: "You told us that the mortgage amount is %{mortgage}."
@ -76,7 +76,7 @@ en:
savings_value_check:
page_header: ""
check_answer_label: "Savings confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm savings amount"
hint_text: ""
question_text: "Are you sure the savings are higher than £100,000?"
joint_purchase:
@ -89,7 +89,7 @@ en:
staircase_bought_value_check:
page_header: ""
check_answer_label: "Percentage bought confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm percentage bought"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that %{percentage}% was bought in this staircasing transaction."
@ -99,7 +99,7 @@ en:
joint_purchase:
page_header: ""
check_answer_label: "Percentage owned confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm percentage owned"
hint_text: ""
question_text: "Are you sure?"
title_text: "You told us that the buyers now own %{stairowned} of the property."
@ -107,7 +107,7 @@ en:
not_joint_purchase:
page_header: ""
check_answer_label: "Percentage owned confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm percentage owned"
hint_text: ""
question_text: "Are you sure?"
title_text: "You told us that the buyer now owns %{stairowned} of the property."
@ -116,7 +116,7 @@ en:
hodate_check:
page_header: ""
check_answer_label: "Practical completion or handover date check"
check_answer_prompt: ""
check_answer_prompt: "Confirm practical completion or handover date"
hint_text: ""
question_text: "Are you sure?"
title_text: "You told us practical completion or handover date is more than 3 years before sale completion date."
@ -124,7 +124,7 @@ en:
value_value_check:
page_header: ""
check_answer_label: "Purchase price confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm purchase price"
hint_text: ""
question_text: "Are you sure?"
title_text: "You told us the purchase price is %{value}."
@ -133,7 +133,7 @@ en:
shared_ownership_deposit_value_check:
page_header: ""
check_answer_label: "Shared ownership deposit confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm shared ownership deposit"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that the %{mortgage_deposit_and_discount_error_fields} add up to %{mortgage_deposit_and_discount_total}."
@ -142,7 +142,7 @@ en:
joint_purchase:
page_header: ""
check_answer_label: "Deposit confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm deposit amount"
hint_text: ""
question_text: "Are you sure that the deposit is this much higher than the buyer's savings?"
title_text: "You told us the buyers’ deposit was %{deposit} and their savings were %{savings}."
@ -150,7 +150,7 @@ en:
not_joint_purchase:
page_header: ""
check_answer_label: "Deposit confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm deposit amount"
hint_text: ""
question_text: "Are you sure that the deposit is this much higher than the buyer's savings?"
title_text: "You told us the buyer’s deposit was %{deposit} and their savings were %{savings}."
@ -158,8 +158,8 @@ en:
address_search_value_check:
page_header: ""
check_answer_label: ""
check_answer_prompt: ""
check_answer_label: "No address found"
check_answer_prompt: "Confirm no address found"
hint_text: ""
question_text: ""
title_text: "No address found."
@ -168,7 +168,7 @@ en:
wheel_value_check:
page_header: ""
check_answer_label: "Household member uses a wheelchair"
check_answer_prompt: "Tell us if someone uses a wheelchair"
check_answer_prompt: "Confirm household member uses a wheelchair"
hint_text: ""
question_text: "You told us that someone in the household uses a wheelchair."
title_text: "You told us that someone in the household uses a wheelchair."
@ -177,7 +177,7 @@ en:
buyer1:
page_header: ""
check_answer_label: "Buyer live in confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm buyer 1 living situation"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that buyer 1 will not live in the property."
@ -185,7 +185,7 @@ en:
buyer2:
page_header: ""
check_answer_label: "Buyer live in confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm buyer 2 living situation"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that buyer 2 will not live in the property."
@ -194,7 +194,7 @@ en:
student_not_child_value_check:
page_header: ""
check_answer_label: "Student not a child confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm student not a child"
hint_text: ""
question_text: "Are you sure this person is not a child?"
title_text: "You told us this person is a student aged between 16 and 19."
@ -203,7 +203,7 @@ en:
partner_under_16_value_check:
page_header: ""
check_answer_label: "Partner under 16 confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm partner's age"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us this person is aged %{age} years and has 'Partner' relationship to buyer 1."
@ -212,7 +212,7 @@ en:
multiple_partners_value_check:
page_header: ""
check_answer_label: "Multiple partners confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm multiple partners"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us there are more than 1 persons with 'Partner' relationship to buyer 1."
@ -221,7 +221,7 @@ en:
monthly_charges_value_check:
page_header: ""
check_answer_label: "Monthly charges confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm monthly charges"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that the monthly charges were %{mscharge}."
@ -230,7 +230,7 @@ en:
extra_borrowing_value_check:
page_header: ""
check_answer_label: "Extra borrowing confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm extra borrowing"
hint_text: ""
question_text: "Are you sure there is no extra borrowing?"
title_text: "You told us that the mortgage and deposit total is %{mortgage_and_deposit_total}."
@ -239,7 +239,7 @@ en:
percentage_discount_value_check:
page_header: ""
check_answer_label: "Percentage discount confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm percentage discount"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that the percentage discount is %{discount}."
@ -248,7 +248,7 @@ en:
grant_value_check:
page_header: ""
check_answer_label: "Grant value confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm grant value"
hint_text: ""
question_text: "Are you sure? Grants are usually £9,000 - £16,000"
title_text: "You told us that the grant amount is %{grant}."
@ -257,7 +257,7 @@ en:
discounted_sale_value_check:
page_header: ""
check_answer_label: "Discounted sale value confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm discounted sale value"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "Mortgage, deposit, and grant total must equal %{value_with_discount}."
@ -266,7 +266,7 @@ en:
deposit_and_mortgage_value_check:
page_header: ""
check_answer_label: "Deposit and mortgage against discount confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm deposit and mortgage against discount"
hint_text: ""
question_text: "Are you sure? Mortgage and deposit usually equal or are more than (value - discount)"
title_text: "You told us the mortgage amount was %{mortgage}, the cash deposit was %{deposit} and the discount was %{discount}."

60
config/locales/forms/2025/sales/soft_validations.en.yml

@ -7,7 +7,7 @@ en:
max:
page_header: ""
check_answer_label: "Retirement confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm person isn't retired"
hint_text: ""
question_text: "Are you sure this person isn't retired?"
title_text: "You told us this person is over 66 and not retired."
@ -15,7 +15,7 @@ en:
min:
page_header: ""
check_answer_label: "Retirement confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm person is retired"
hint_text: ""
question_text: "Are you sure this person is retired?"
title_text: "You told us this person is aged %{age} years and retired."
@ -23,7 +23,7 @@ en:
old_persons_shared_ownership_value_check:
page_header: ""
check_answer_label: "Shared ownership confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm shared ownership scheme"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text:
@ -32,7 +32,7 @@ en:
informative_text: "At least one buyer must be aged 65 years and over to use this scheme."
income1_value_check:
check_answer_label: "Buyer 1 income confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm buyer 1 income"
hint_text: ""
question_text: "Are you sure this is correct?"
ecstat:
@ -45,7 +45,7 @@ en:
income2_value_check:
check_answer_label: "Buyer 2 income confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm buyer 2 income"
hint_text: ""
question_text: "Are you sure this is correct?"
ecstat:
@ -59,7 +59,7 @@ en:
combined_income_value_check:
page_header: ""
check_answer_label: "Combined income confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm combined income"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us the combined income of this household is %{combined_income}. This seems high for this sale type. Are you sure this is correct?"
@ -67,7 +67,7 @@ en:
mortgage_value_check:
page_header: ""
check_answer_label: "Mortgage confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm mortgage amount"
hint_text: ""
question_text: "Are you sure that the mortgage is more than 5 times the income used for the mortgage application?"
title_text: "You told us that the mortgage amount is %{mortgage}."
@ -76,7 +76,7 @@ en:
savings_value_check:
page_header: ""
check_answer_label: "Savings confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm savings amount"
hint_text: ""
question_text: "Are you sure the savings are higher than £100,000?"
joint_purchase:
@ -89,7 +89,7 @@ en:
staircase_bought_value_check:
page_header: ""
check_answer_label: "Percentage bought confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm percentage bought"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that %{percentage}% was bought in this staircasing transaction."
@ -99,7 +99,7 @@ en:
joint_purchase:
page_header: ""
check_answer_label: "Percentage owned confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm percentage owned"
hint_text: ""
question_text: "Are you sure?"
title_text: "You told us that the buyers now own %{stairowned} of the property."
@ -107,7 +107,7 @@ en:
not_joint_purchase:
page_header: ""
check_answer_label: "Percentage owned confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm percentage owned"
hint_text: ""
question_text: "Are you sure?"
title_text: "You told us that the buyer now owns %{stairowned} of the property."
@ -116,7 +116,7 @@ en:
hodate_check:
page_header: ""
check_answer_label: "Practical completion or handover date check"
check_answer_prompt: ""
check_answer_prompt: "Confirm practical completion or handover date"
hint_text: ""
question_text: "Are you sure?"
title_text: "You told us practical completion or handover date is more than 3 years before sale completion date."
@ -124,7 +124,7 @@ en:
value_value_check:
page_header: ""
check_answer_label: "Purchase price confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm purchase price"
hint_text: ""
question_text: "Are you sure?"
title_text: "You told us the purchase price is %{value}."
@ -133,7 +133,7 @@ en:
shared_ownership_deposit_value_check:
page_header: ""
check_answer_label: "Shared ownership deposit confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm shared ownership deposit"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that the %{mortgage_deposit_and_discount_error_fields} add up to %{mortgage_deposit_and_discount_total}."
@ -142,7 +142,7 @@ en:
joint_purchase:
page_header: ""
check_answer_label: "Deposit confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm deposit amount"
hint_text: ""
question_text: "Are you sure that the deposit is this much higher than the buyer's savings?"
title_text: "You told us the buyers’ deposit was %{deposit} and their savings were %{savings}."
@ -150,7 +150,7 @@ en:
not_joint_purchase:
page_header: ""
check_answer_label: "Deposit confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm deposit amount"
hint_text: ""
question_text: "Are you sure that the deposit is this much higher than the buyer's savings?"
title_text: "You told us the buyer’s deposit was %{deposit} and their savings were %{savings}."
@ -158,8 +158,8 @@ en:
address_search_value_check:
page_header: ""
check_answer_label: ""
check_answer_prompt: ""
check_answer_label: "No address found"
check_answer_prompt: "Confirm no address found"
hint_text: ""
question_text: ""
title_text: "No address found."
@ -168,7 +168,7 @@ en:
wheel_value_check:
page_header: ""
check_answer_label: "Household member uses a wheelchair"
check_answer_prompt: "Tell us if someone uses a wheelchair"
check_answer_prompt: "Confirm household member uses a wheelchair"
hint_text: ""
question_text: "You told us that someone in the household uses a wheelchair."
title_text: "You told us that someone in the household uses a wheelchair."
@ -177,7 +177,7 @@ en:
buyer1:
page_header: ""
check_answer_label: "Buyer live in confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm buyer 1 living situation"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that buyer 1 will not live in the property."
@ -185,7 +185,7 @@ en:
buyer2:
page_header: ""
check_answer_label: "Buyer live in confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm buyer 2 living situation"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that buyer 2 will not live in the property."
@ -194,7 +194,7 @@ en:
student_not_child_value_check:
page_header: ""
check_answer_label: "Student not a child confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm student not a child"
hint_text: ""
question_text: "Are you sure this person is not a child?"
title_text: "You told us this person is a student aged between 16 and 19."
@ -203,7 +203,7 @@ en:
partner_under_16_value_check:
page_header: ""
check_answer_label: "Partner under 16 confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm partner's age"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us this person is aged %{age} years and has 'Partner' relationship to buyer 1."
@ -212,7 +212,7 @@ en:
multiple_partners_value_check:
page_header: ""
check_answer_label: "Multiple partners confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm multiple partners"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us there are more than 1 persons with 'Partner' relationship to buyer 1."
@ -221,7 +221,7 @@ en:
monthly_charges_value_check:
page_header: ""
check_answer_label: "Monthly charges confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm monthly charges"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that the monthly charges were %{mscharge}."
@ -230,7 +230,7 @@ en:
extra_borrowing_value_check:
page_header: ""
check_answer_label: "Extra borrowing confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm extra borrowing"
hint_text: ""
question_text: "Are you sure there is no extra borrowing?"
title_text: "You told us that the mortgage and deposit total is %{mortgage_and_deposit_total}."
@ -239,7 +239,7 @@ en:
percentage_discount_value_check:
page_header: ""
check_answer_label: "Percentage discount confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm percentage discount"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "You told us that the percentage discount is %{discount}."
@ -248,7 +248,7 @@ en:
grant_value_check:
page_header: ""
check_answer_label: "Grant value confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm grant value"
hint_text: ""
question_text: "Are you sure? Grants are usually £9,000 - £16,000"
title_text: "You told us that the grant amount is %{grant}."
@ -257,7 +257,7 @@ en:
discounted_sale_value_check:
page_header: ""
check_answer_label: "Discounted sale value confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm discounted sale value"
hint_text: ""
question_text: "Are you sure this is correct?"
title_text: "Mortgage, deposit, and grant total must equal %{value_with_discount}."
@ -266,7 +266,7 @@ en:
deposit_and_mortgage_value_check:
page_header: ""
check_answer_label: "Deposit and mortgage against discount confirmation"
check_answer_prompt: ""
check_answer_prompt: "Confirm deposit and mortgage against discount"
hint_text: ""
question_text: "Are you sure? Mortgage and deposit usually equal or are more than (value - discount)"
title_text: "You told us the mortgage amount was %{mortgage}, the cash deposit was %{deposit} and the discount was %{discount}."

2
docs/adr/index.md

@ -1,6 +1,6 @@
---
has_children: true
nav_order: 10
nav_order: 11
---
# Architecture decisions

118
docs/csv_downloads.md

@ -0,0 +1,118 @@
---
nav_order: 10
---
# CSV Downloads
The CSV download functionality allows users to download various types of data from the service. This documentation provides an overview of how CSV downloads work, the different types of downloads available, and common development tasks related to CSV downloads.
## How CSV Downloads Work
CSV downloads are generated based on the data accessible to the user in the service. Sales and lettings data must be downloaded for a specific year due to differences in templates and collected data.
When a download is requested:
1. The request is queued using **Sidekiq** and processed in the background.
2. The generated CSV file is stored in an **S3 bucket**.
3. Users receive an email with a link to the service, where they can download the file via a **presigned URL**, valid for 2 days.
## Available Types of CSV Downloads
### For Support Users
Support users can download the following data:
- **Lettings Logs**: Either _labels_ or _codes_ version, one download per specific year.
- **Sales Logs**: Either _labels_ or _codes_ version, one download per specific year.
- **Schemes**
- **Locations**
- **Combined Schemes and Locations**: Contains the same data as above, but joined.
- **Users**
### For Non-Support Users
Non-support users can download:
- **Lettings Logs**: Logs owned or managed by their organisation (or merged organisations) in the _labels_ version only. One download per specific year.
- **Sales Logs**: Logs owned or reported by their organisation (or merged organisations) in the _labels_ version only. One download per specific year.
- **Schemes**: Available to their organisation.
- **Locations**: Available to their organisation.
- **Combined Schemes and Locations**: Available to their organisation.
### Applying Filters
Users can download a subset of this data by applying filters and search. **Year filter** is mandatory for logs downloads.
---
## Labels vs. Codes in CSV Downloads
### Labels
Labels represent the verbal answer options displayed in the service.
For a lettings log `reason` field with the data `"4" => { "value" => "Loss of tied accommodation" }`, the value in the _labels_ version would be `Loss of tied accommodation`.
### Codes
_Codes only_ exports use integers where possible as field values.
For the same `reason` field above, the value in the codes CSV download version would be `4`.
The integers for _codes only_ export should correspond to the numbers in bulk upload specification and CDS export.
Most of the codes saved internally align with the values exported, meaning the exported codes are typically identical to their internal representations.
In cases where internal values differ from the expected export format, such as the `rent_type` field (exported under the `renttype_detail` header), the values are mapped to the expected format directly in the CSV export service. In this case, the mapping is handled in the `renttype_detail_code` method.
### Things to note
- **Some fields are always exported as codes**: Such as `la`.
- **Some fields are always exported as labels**: Such as `la_label`.
- **Mapping**: For fields where internal values don’t match export requirements (e.g., `rent_type` - exported as `renttype_detail`), mappings are applied directly in the CSV export service.
- For fields without corresponding codes (e.g., `tenancycode`), the _codes_ version will have the same value as the _labels_ version.
---
## Common Development Tasks
### 1. Adding New Columns
- **Logs (Lettings/Sales)**:
- By default all of the question fields from the specific form will be exported in the CSV download, unless they're manually removed in the `lettings_log_attributes` or `sales_log_attributes` method.
- Update `lettings_log_attributes` or `sales_log_attributes` methods to add or remove fields.
- **Schemes/Locations**:
- Exported scheme/location CSV fields are hardcoded in `scheme_attributes` and `location_attributes`. Update `scheme_attributes` or `location_attributes` methods to add or remove fields.
- **Users**:
- Users CSV download is generated in users model `self.to_csv` and exported attributes are defined in `self.download_attributes`. Modify the `self.download_attributes` method in the `User` model to add or remove fields.
### 2. Reordering Columns
- **Logs (Lettings/Sales)**:
- Logs download question order coresponds to the order of the questions in the form flow and any additional ordering is applied in the `lettings_log_attributes` or `sales_log_attributes` methods.
- Modify the order in `lettings_log_attributes` or `sales_log_attributes` to update field order.
- **Schemes/Locations**:
- Adjust order in `scheme_attributes` or `location_attributes`.
- **Users**:
- Update column order in `self.download_attributes`.
### 3. Populating CSV Variable Definitions
CSV variable definitions describe each header in the logs downloads.
Definitions are saved in a `csv_variable_definitions` table and have been populated with initial values on production. The definitions are expected to be updated manually from `/admin` page when an update is needed, this is so that the definitions could be updated by support users.
To populate initial CSV variable definitions locally or on a review app run `data_import:add_variable_definitions` rake task with the folder path to the variable definitions `config/csv/definitions`
```
rake data_import:add_variable_definitions[config/csv/definitions]
```
## Viewing and Updating CSV variable definitions
To locate the CSV variable definitions:
- Log in as a support user
- Navigate to the admin page of the app `<base_url>/admin`
- Select `CSV Variable Definitions` table
A list of all the CSV variable definitions for logs download should be displayed and can be edited directly from here. Editing these would have an instant effect, so it might be worth trying it on staging environments first.

2
docs/documentation_website.md

@ -1,5 +1,5 @@
---
nav_order: 11
nav_order: 12
---
# This documentation website

5
spec/factories/sales_log.rb

@ -80,6 +80,7 @@ FactoryBot.define do
noint { 2 }
privacynotice { 1 }
age1_known { 0 }
staircase { 1 }
age1 { Faker::Number.within(range: 27..45) }
sex1 { %w[F M X R].sample }
national { 18 }
@ -110,10 +111,10 @@ FactoryBot.define do
age6_known { 0 }
age6 { 40 }
income1nk { 0 }
income1 { 10_000 }
income1 { 13_400 }
inc1mort { 1 }
income2nk { 0 }
income2 { 10_000 }
income2 { 13_400 }
inc2mort { 1 }
uprn_known { 0 }
address_line1 { "Address line 1" }

111
spec/features/home_page_spec.rb

@ -0,0 +1,111 @@
require "rails_helper"
RSpec.describe "Home Page Features" do
include CollectionTimeHelper
let!(:user) { FactoryBot.create(:user) }
let(:storage_service) { instance_double(Storage::S3Service, get_file_metadata: nil) }
before do
sign_in user
allow(Storage::S3Service).to receive(:new).and_return(storage_service)
allow(storage_service).to receive(:configuration).and_return(OpenStruct.new(bucket_name: "core-test-collection-resources"))
end
describe "_upcoming_deadlines" do
let(:current_collection_year) { 2024 }
let(:next_collection_year) { 2025 }
context "when visiting during the current collection year" do
before do
allow(FormHandler.instance).to receive(:in_crossover_period?).and_return(false)
# rubocop:disable RSpec/AnyInstance
allow_any_instance_of(CollectionTimeHelper).to receive(:current_collection_start_year).and_return(current_collection_year)
allow_any_instance_of(CollectionTimeHelper).to receive(:current_collection_end_year).and_return(current_collection_year + 1)
# rubocop:enable RSpec/AnyInstance
end
scenario "displays correct text for quarters" do
Timecop.freeze(Time.zone.local(current_collection_year, 4, 1)) do
visit root_path
find("span.govuk-details__summary-text", text: "Quarterly cut-off dates for 2024 to 2025").click
expect(page).to have_content("Q1 - Friday 12 July 2024")
expect(page).to have_content("Q2 - Friday 11 October 2024")
expect(page).to have_content("Q3 - Friday 10 January 2025")
expect(page).to have_content("End of year deadline - Friday 6 June 2025")
end
Timecop.return
end
scenario "displays correct current quarter as Q1" do
Timecop.freeze(Time.zone.local(current_collection_year, 4, 1)) do
visit root_path
expect(page).to have_content("Q1 - Friday 12 July 2024")
end
Timecop.return
end
scenario "displays correct current quarter as Q2" do
Timecop.freeze(Time.zone.local(current_collection_year, 8, 1)) do
visit root_path
expect(page).to have_content("Q2 - Friday 11 October 2024")
end
Timecop.return
end
scenario "displays correct current quarter as Q3" do
Timecop.freeze(Time.zone.local(current_collection_year, 11, 1)) do
visit root_path
expect(page).to have_content("Q3 - Friday 10 January 2025")
end
Timecop.return
end
end
context "when visiting during the next collection year" do
before do
allow(FormHandler.instance).to receive(:in_crossover_period?).and_return(false)
# rubocop:disable RSpec/AnyInstance
allow_any_instance_of(CollectionTimeHelper).to receive(:current_collection_start_year).and_return(next_collection_year)
allow_any_instance_of(CollectionTimeHelper).to receive(:current_collection_end_year).and_return(next_collection_year + 1)
# rubocop:enable RSpec/AnyInstance
end
scenario "displays correct text for quarters" do
Timecop.freeze(Time.zone.local(next_collection_year, 4, 1)) do
visit root_path
find("span.govuk-details__summary-text", text: "Quarterly cut-off dates for 2025 to 2026").click
expect(page).to have_content("Q1 - Friday 11 July 2025")
expect(page).to have_content("Q2 - Friday 10 October 2025")
expect(page).to have_content("Q3 - Friday 16 January 2026")
expect(page).to have_content("End of year deadline - Friday 5 June 2026")
end
Timecop.return
end
scenario "displays correct current quarter as Q1" do
Timecop.freeze(Time.zone.local(next_collection_year, 4, 1)) do
visit root_path
expect(page).to have_content("Q1 - Friday 11 July 2025")
end
Timecop.return
end
scenario "displays correct current quarter as Q2" do
Timecop.freeze(Time.zone.local(next_collection_year, 8, 1)) do
visit root_path
expect(page).to have_content("Q2 - Friday 10 October 2025")
end
Timecop.return
end
scenario "displays correct current quarter as Q3" do
Timecop.freeze(Time.zone.local(next_collection_year, 11, 1)) do
visit root_path
expect(page).to have_content("Q3 - Friday 16 January 2026")
end
Timecop.return
end
end
end
end

2
spec/fixtures/files/sales_logs_csv_export_codes_23.csv vendored

File diff suppressed because one or more lines are too long

2
spec/fixtures/files/sales_logs_csv_export_codes_24.csv vendored

File diff suppressed because one or more lines are too long

2
spec/fixtures/files/sales_logs_csv_export_labels_23.csv vendored

File diff suppressed because one or more lines are too long

2
spec/fixtures/files/sales_logs_csv_export_labels_24.csv vendored

File diff suppressed because one or more lines are too long

2
spec/fixtures/files/sales_logs_csv_export_non_support_labels_24.csv vendored

File diff suppressed because one or more lines are too long

29
spec/helpers/collection_deadline_helper_spec.rb

@ -0,0 +1,29 @@
require "rails_helper"
RSpec.describe CollectionDeadlineHelper do
let(:current_user) { create(:user, :data_coordinator) }
let(:user) { create(:user, :data_coordinator) }
describe "#quarter_for_date" do
it "returns correct cutoff date for the first quarter of 2024/25" do
quarter = quarter_for_date(date: Time.zone.local(2024, 4, 1))
expect(quarter.cutoff_date).to eq(Time.zone.local(2024, 7, 12))
expect(quarter.quarter_start_date).to eq(Time.zone.local(2024, 4, 1))
expect(quarter.quarter_end_date).to eq(Time.zone.local(2024, 6, 30))
end
it "returns correct cutoff date for the second quarter of 2024/25" do
quarter = quarter_for_date(date: Time.zone.local(2024, 9, 30))
expect(quarter.cutoff_date).to eq(Time.zone.local(2024, 10, 11))
expect(quarter.quarter_start_date).to eq(Time.zone.local(2024, 7, 1))
expect(quarter.quarter_end_date).to eq(Time.zone.local(2024, 9, 30))
end
it "returns correct cutoff date for the third quarter of 2024/25" do
quarter = quarter_for_date(date: Time.zone.local(2024, 10, 25))
expect(quarter.cutoff_date).to eq(Time.zone.local(2025, 1, 10))
expect(quarter.quarter_start_date).to eq(Time.zone.local(2024, 10, 1))
expect(quarter.quarter_end_date).to eq(Time.zone.local(2024, 12, 31))
end
end
end

23
spec/helpers/collection_time_helper_spec.rb

@ -106,27 +106,4 @@ RSpec.describe CollectionTimeHelper do
end
end
end
describe "#quarter_for_date" do
it "returns correct cutoff date for the first quarter of 2024/25" do
quarter = quarter_for_date(date: Time.zone.local(2024, 4, 1))
expect(quarter.cutoff_date).to eq(Time.zone.local(2024, 7, 12))
expect(quarter.quarter_start_date).to eq(Time.zone.local(2024, 4, 1))
expect(quarter.quarter_end_date).to eq(Time.zone.local(2024, 6, 30))
end
it "returns correct cutoff date for the second quarter of 2024/25" do
quarter = quarter_for_date(date: Time.zone.local(2024, 9, 30))
expect(quarter.cutoff_date).to eq(Time.zone.local(2024, 10, 11))
expect(quarter.quarter_start_date).to eq(Time.zone.local(2024, 7, 1))
expect(quarter.quarter_end_date).to eq(Time.zone.local(2024, 9, 30))
end
it "returns correct cutoff date for the third quarter of 2024/25" do
quarter = quarter_for_date(date: Time.zone.local(2024, 10, 25))
expect(quarter.cutoff_date).to eq(Time.zone.local(2025, 1, 10))
expect(quarter.quarter_start_date).to eq(Time.zone.local(2024, 10, 1))
expect(quarter.quarter_end_date).to eq(Time.zone.local(2024, 12, 31))
end
end
end

4
spec/models/bulk_upload_spec.rb

@ -21,10 +21,10 @@ RSpec.describe BulkUpload, type: :model do
describe "value check clearing" do
context "with a lettings log bulk upload" do
let(:log) { build(:lettings_log, :startdate_today, bulk_upload:) }
let(:log) { build(:lettings_log, startdate: Time.zone.local(2025, 4, 2), 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 carehome_charges_value_check scharge_value_check pscharge_value_check supcharg_value_check address_search_value_check multiple_partners_value_check partner_under_16_value_check reasonother_value_check]
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 address_search_value_check multiple_partners_value_check partner_under_16_value_check reasonother_value_check]
end
end

1
spec/models/form/lettings/pages/rent_type_spec.rb

@ -11,6 +11,7 @@ RSpec.describe Form::Lettings::Pages::RentType, type: :model do
before do
allow(subsection).to receive(:form).and_return(form)
allow(form).to receive(:start_year_2024_or_later?).and_return(true)
allow(form).to receive(:start_year_2025_or_later?).and_return(false)
end
it "has correct subsection" do

21
spec/models/form/lettings/questions/previous_let_type_spec.rb

@ -9,6 +9,7 @@ RSpec.describe Form::Lettings::Questions::PreviousLetType, type: :model do
before do
allow(form).to receive(:start_year_2024_or_later?).and_return(false)
allow(form).to receive(:start_year_2025_or_later?).and_return(false)
allow(page).to receive(:subsection).and_return(subsection)
allow(subsection).to receive(:form).and_return(form)
end
@ -60,4 +61,24 @@ RSpec.describe Form::Lettings::Questions::PreviousLetType, type: :model do
})
end
end
context "with collection year on or after 2025" do
before do
allow(form).to receive(:start_year_2025_or_later?).and_return(true)
end
it "has the correct answer options" do
expect(question.answer_options).to eq({
"1" => { "value" => "Social rent basis" },
"2" => { "value" => "Affordable rent basis" },
"5" => { "value" => "London Affordable Rent basis" },
"6" => { "value" => "Rent to Buy basis" },
"7" => { "value" => "London Living Rent basis" },
"8" => { "value" => "Another Intermediate Rent basis" },
"9" => { "value" => "Specified accommodation - exempt accommodation, manged properties, refuges and local authority hostels" },
"divider" => { "value" => true },
"3" => { "value" => "Don’t know" },
})
end
end
end

45
spec/models/form/lettings/questions/reason_spec.rb

@ -11,6 +11,7 @@ RSpec.describe Form::Lettings::Questions::Reason, type: :model do
before do
allow(form).to receive(:start_year_2024_or_later?).and_return(false)
allow(form).to receive(:start_year_2025_or_later?).and_return(false)
allow(page).to receive(:subsection).and_return(subsection)
allow(subsection).to receive(:form).and_return(form)
end
@ -124,4 +125,48 @@ RSpec.describe Form::Lettings::Questions::Reason, type: :model do
})
end
end
context "with 2025/26 form" do
before do
allow(form).to receive(:start_year_2025_or_later?).and_return(true)
end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"50" => { "value" => "End of social or private sector tenancy - no fault" },
"51" => { "value" => "End of social or private sector tenancy - evicted due to anti-social behaviour (ASB)" },
"52" => { "value" => "End of social or private sector tenancy - evicted due to rent arrears" },
"53" => { "value" => "End of social or private sector tenancy - evicted for any other reason" },
"1" => { "value" => "Permanently decanted from another property owned by this landlord" },
"2" => { "value" => "Left home country as a refugee" },
"45" => { "value" => "Discharged from prison" },
"46" => { "value" => "Discharged from long-stay hospital or similar institution" },
"4" => { "value" => "Loss of tied accommodation" },
"55" => { "value" => "Leaving foster care or children's home" },
"9" => { "value" => "Asked to leave by family or friends" },
"8" => { "value" => "Relationship breakdown (non-violent) with partner" },
"44" => { "value" => "Death of household member in last settled accommodation" },
"16" => { "value" => "To move nearer to family, friends or school" },
"17" => { "value" => "To move nearer to work" },
"48" => { "value" => "Domestic abuse - previously joint tenancy with partner" },
"49" => { "value" => "Domestic abuse - other" },
"10" => { "value" => "Racial harassment" },
"31" => { "value" => "Hate crime" },
"11" => { "value" => "Other problems with neighbours" },
"34" => { "value" => "Repossession" },
"54" => { "value" => "Could no longer afford rent or mortgage" },
"12" => { "value" => "Property unsuitable because of overcrowding" },
"13" => { "value" => "Property unsuitable because of ill health or disability" },
"14" => { "value" => "Property unsuitable because of poor condition" },
"29" => { "value" => "Under occupation (offered incentive to downsize)" },
"30" => { "value" => "Under occupation (no incentive)" },
"18" => { "value" => "To move to accommodation with support" },
"19" => { "value" => "To move to independent accommodation" },
"20" => { "value" => "Other" },
"28" => { "value" => "Don’t know" },
"divider" => { "value" => true },
"47" => { "value" => "Tenant prefers not to say" },
})
end
end
end

9
spec/models/form/lettings/questions/rent_type_spec.rb

@ -12,7 +12,7 @@ RSpec.describe Form::Lettings::Questions::RentType, type: :model do
before do
allow(page).to receive(:subsection).and_return(subsection)
allow(subsection).to receive(:form).and_return(form)
allow(form).to receive(:start_year_2024_or_later?).and_return(false)
allow(form).to receive(:start_year_2025_or_later?).and_return(false)
end
it "has correct page" do
@ -35,9 +35,9 @@ RSpec.describe Form::Lettings::Questions::RentType, type: :model do
expect(question.derived?(nil)).to be false
end
context "when 2023" do
context "when 2025" do
before do
allow(form).to receive(:start_year_2024_or_later?).and_return(false)
allow(form).to receive(:start_year_2025_or_later?).and_return(true)
end
it "has the correct answer_options" do
@ -48,6 +48,7 @@ RSpec.describe Form::Lettings::Questions::RentType, type: :model do
"3" => { "value" => "Rent to Buy" },
"0" => { "value" => "Social Rent" },
"5" => { "value" => "Other intermediate rent product" },
"6" => { "value" => "Specified accommodation - exempt accommodation, managed properties, refuges and local authority hostels" },
})
end
@ -58,7 +59,7 @@ RSpec.describe Form::Lettings::Questions::RentType, type: :model do
context "when 2024" do
before do
allow(form).to receive(:start_year_2024_or_later?).and_return(true)
allow(form).to receive(:start_year_2025_or_later?).and_return(false)
end
it "has the correct answer_options" do

36
spec/models/lettings_log_derived_fields_spec.rb

@ -187,6 +187,18 @@ RSpec.describe LettingsLog, type: :model do
needstype: 1,
expected_lettype: 1,
},
{
context: "when the rent type is Specified accommodation and supported housing",
rent_type: 6,
needstype: 2,
expected_lettype: 14,
},
{
context: "when the rent type is Specified accommodation and general needs housing",
rent_type: 6,
needstype: 1,
expected_lettype: 13,
},
].each do |test_case|
context test_case[:context] do
it "correctly derives lettype" do
@ -953,6 +965,11 @@ RSpec.describe LettingsLog, type: :model do
log.rent_type = 5
expect { log.set_derived_fields! }.to change(log, :renttype).to 3
end
it "when rent_type is Specified accommodation derives renttype as Specified accommodation" do
log.rent_type = 6
expect { log.set_derived_fields! }.to change(log, :renttype).to 4
end
end
describe "variables dependent on whether a letting is a renewal" do
@ -1168,6 +1185,25 @@ RSpec.describe LettingsLog, type: :model do
expect { persisted_renewal_lettings_log.update!(renewal: 0) }.to change(persisted_renewal_lettings_log, :unitletas).from(expected_unitletas).to nil
end
end
context "when rent_type is Specified accommodation " do
let(:rent_type) { 6 }
let(:expected_unitletas) { 9 }
before do
Timecop.freeze(Time.zone.local(2025, 5, 5))
end
it "derives the most recent let type as London Living Rent basis if it is a renewal" do
log.assign_attributes(renewal: 1, rent_type:)
expect { log.set_derived_fields! }.to change(log, :unitletas).to expected_unitletas
end
it "clears the most recent let type if it is not a renewal" do
expect { persisted_renewal_lettings_log.update!(renewal: 0) }.to change(persisted_renewal_lettings_log, :unitletas).from(expected_unitletas).to nil
end
end
end
end
end

24
spec/models/log_spec.rb

@ -101,5 +101,29 @@ RSpec.describe Log, type: :model do
expect(model.joint).to be_nil
end
end
context "when postcode_full is invalid" do
context "with a lettings log" do
subject(:model) { build_stubbed(:lettings_log, :setup_completed, postcode_full: "not a postcode", postcode_known: 1) }
it "blanks it and postcode_known" do
model.valid?
model.blank_invalid_non_setup_fields!
expect(model.postcode_full).to be_nil
expect(model.postcode_known).to be_nil
end
end
context "with a sales log" do
subject(:model) { build_stubbed(:sales_log, :discounted_ownership_setup_complete, postcode_full: "not a postcode", pcodenk: 0) }
it "blanks it and pcodenk" do
model.valid?
model.blank_invalid_non_setup_fields!
expect(model.postcode_full).to be_nil
expect(model.pcodenk).to be_nil
end
end
end
end
end

3
spec/models/validations/household_validations_spec.rb

@ -743,8 +743,9 @@ RSpec.describe Validations::HouseholdValidations do
record.referral = 1
record.prevten = prevten[:code]
household_validator.validate_previous_housing_situation(record)
label = record.form.start_year_2025_or_later? && prevten[:code] == 28 ? "Living with friends and family (long-term)" : prevten[:label]
expect(record.errors["prevten"])
.to include(match I18n.t("validations.lettings.household.prevten.internal_transfer", prevten: prevten[:label]))
.to include(match I18n.t("validations.lettings.household.prevten.internal_transfer", prevten: label))
expect(record.errors["referral"])
.to include(match I18n.t("validations.lettings.household.referral.prevten_invalid", prevten: ""))
end

Loading…
Cancel
Save