diff --git a/Dockerfile b/Dockerfile
index da6d4ba6f..a65696f84 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,7 +10,7 @@ RUN apk add --update --no-cache tzdata && \
# build-base: compilation tools for bundle
# yarn: node package manager
# postgresql-dev: postgres driver and libraries
-RUN apk add --no-cache build-base=0.5-r3 nodejs-current=20.8.1-r0 yarn=1.22.19-r0 postgresql13-dev=13.13-r0 git=2.40.1-r0 bash=5.2.15-r5
+RUN apk add --no-cache build-base=0.5-r3 nodejs-current=20.8.1-r0 yarn=1.22.19-r0 postgresql13-dev=13.14-r0 git=2.40.1-r0 bash=5.2.15-r5
# Bundler version should be the same version as what the Gemfile.lock was bundled with
RUN gem install bundler:2.3.14 --no-document
diff --git a/app/frontend/styles/_unread-notification.scss b/app/frontend/styles/_unread-notification.scss
index d76b36fa2..a42cd2d05 100644
--- a/app/frontend/styles/_unread-notification.scss
+++ b/app/frontend/styles/_unread-notification.scss
@@ -5,3 +5,7 @@
.app-unread-notification p {
color: govuk-colour("white");
}
+
+.app-unread-notification a {
+ color: govuk-colour("white");
+}
diff --git a/app/models/derived_variables/sales_log_variables.rb b/app/models/derived_variables/sales_log_variables.rb
index f662ab3df..ec8d21b30 100644
--- a/app/models/derived_variables/sales_log_variables.rb
+++ b/app/models/derived_variables/sales_log_variables.rb
@@ -17,6 +17,15 @@ module DerivedVariables::SalesLogVariables
self.hoyear = hodate.year
end
self.deposit = value if outright_sale? && mortgage_not_used?
+
+ if saledate && form.start_year_after_2024? && discounted_ownership_sale?
+ self.ppostcode_full = postcode_full
+ self.ppcodenk = 0 if postcode_full.present?
+ self.prevloc = la
+ self.is_previous_la_inferred = is_la_inferred
+ self.previous_la_known = la_known
+ end
+
self.pcode1, self.pcode2 = postcode_full.split if postcode_full.present?
self.ppostc1, self.ppostc2 = ppostcode_full.split if ppostcode_full.present?
self.totchild = total_child
diff --git a/app/models/form/lettings/questions/declaration.rb b/app/models/form/lettings/questions/declaration.rb
index 03fc72f4a..4805a9f1e 100644
--- a/app/models/form/lettings/questions/declaration.rb
+++ b/app/models/form/lettings/questions/declaration.rb
@@ -6,10 +6,17 @@ class Form::Lettings::Questions::Declaration < ::Form::Question
@header = "Declaration"
@type = "checkbox"
@check_answers_card_number = 0
- @top_guidance_partial = "privacy_notice_tenant"
- @answer_options = ANSWER_OPTIONS
+ @top_guidance_partial = form.start_year_after_2024? ? "privacy_notice_tenant_2024" : "privacy_notice_tenant"
@question_number = 30
end
- ANSWER_OPTIONS = { "declaration" => { "value" => "The tenant has seen the DLUHC privacy notice" } }.freeze
+ def answer_options
+ declaration_text = if form.start_year_after_2024?
+ "The tenant has seen or been given access to the DLUHC privacy notice"
+ else
+ "The tenant has seen the DLUHC privacy notice"
+ end
+
+ { "declaration" => { "value" => declaration_text } }.freeze
+ end
end
diff --git a/app/models/form/lettings/questions/letting_allocation.rb b/app/models/form/lettings/questions/letting_allocation.rb
index 18c0a3042..09fd82121 100644
--- a/app/models/form/lettings/questions/letting_allocation.rb
+++ b/app/models/form/lettings/questions/letting_allocation.rb
@@ -7,15 +7,27 @@ class Form::Lettings::Questions::LettingAllocation < ::Form::Question
@type = "checkbox"
@check_answers_card_number = 0
@hint_text = "Select all that apply."
- @answer_options = ANSWER_OPTIONS
@question_number = 84
end
- ANSWER_OPTIONS = {
- "cbl" => { "value" => "Choice-based lettings (CBL)" },
- "cap" => { "value" => "Common Allocation Policy (CAP)" },
- "chr" => { "value" => "Common housing register (CHR)" },
- "divider" => { "value" => true },
- "letting_allocation_unknown" => { "value" => "None of these allocation systems" },
- }.freeze
+ def answer_options
+ if form.start_year_after_2024?
+ {
+ "cbl" => { "value" => "Choice-based lettings (CBL)", "hint" => "Where available vacant properties are advertised and applicants are able to bid for specific properties." },
+ "cap" => { "value" => "Common Allocation Policy (CAP)", "hint" => "Where a common system agreed between a group of housing providers is used to determine applicant’s priority for housing." },
+ "chr" => { "value" => "Common housing register (CHR)", "hint" => "Where a single waiting list is used by a group of housing providers to receive and process housing applications. Providers may use different approaches to determine priority." },
+ "accessible_register" => { "value" => "Accessible housing register", "hint" => "Where the ‘access category’ or another descriptor of whether an available vacant property meets a range of access needs is displayed to applicants during the allocations process." },
+ "divider" => { "value" => true },
+ "letting_allocation_unknown" => { "value" => "None of these allocation systems" },
+ }.freeze
+ else
+ {
+ "cbl" => { "value" => "Choice-based lettings (CBL)" },
+ "cap" => { "value" => "Common Allocation Policy (CAP)" },
+ "chr" => { "value" => "Common housing register (CHR)" },
+ "divider" => { "value" => true },
+ "letting_allocation_unknown" => { "value" => "None of these allocation systems" },
+ }.freeze
+ end
+ end
end
diff --git a/app/models/form/lettings/questions/nationality_all_group.rb b/app/models/form/lettings/questions/nationality_all_group.rb
index 2ba9e5cde..49b000a16 100644
--- a/app/models/form/lettings/questions/nationality_all_group.rb
+++ b/app/models/form/lettings/questions/nationality_all_group.rb
@@ -6,7 +6,7 @@ class Form::Lettings::Questions::NationalityAllGroup < ::Form::Question
@header = "What is the nationality of the lead tenant?"
@type = "radio"
@check_answers_card_number = 1
- @hint_text = "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest."
+ @hint_text = "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest. If the lead tenant is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, the tenant should decide which country to enter."
@answer_options = ANSWER_OPTIONS
@question_number = 36
@conditional_for = { "nationality_all" => [12] }
diff --git a/app/models/form/lettings/subsections/household_characteristics.rb b/app/models/form/lettings/subsections/household_characteristics.rb
index bb8533c12..1e442cb18 100644
--- a/app/models/form/lettings/subsections/household_characteristics.rb
+++ b/app/models/form/lettings/subsections/household_characteristics.rb
@@ -8,7 +8,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
def pages
@pages ||= [
- Form::Lettings::Pages::Declaration.new(nil, nil, self),
+ (Form::Lettings::Pages::Declaration.new(nil, nil, self) unless form.start_year_after_2024?),
Form::Lettings::Pages::HouseholdMembers.new(nil, nil, self),
Form::Lettings::Pages::NoFemalesPregnantHouseholdLeadHhmembValueCheck.new(nil, nil, self),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdLeadHhmembValueCheck.new(nil, nil, self),
diff --git a/app/models/form/lettings/subsections/setup.rb b/app/models/form/lettings/subsections/setup.rb
index 3b833927d..85af7d2c4 100644
--- a/app/models/form/lettings/subsections/setup.rb
+++ b/app/models/form/lettings/subsections/setup.rb
@@ -19,6 +19,7 @@ class Form::Lettings::Subsections::Setup < ::Form::Subsection
Form::Lettings::Pages::RentType.new(nil, nil, self),
Form::Lettings::Pages::TenantCode.new(nil, nil, self),
Form::Lettings::Pages::PropertyReference.new(nil, nil, self),
+ (Form::Lettings::Pages::Declaration.new(nil, nil, self) if form.start_year_after_2024?),
].compact
end
diff --git a/app/models/form/sales/pages/about_deposit_with_discount.rb b/app/models/form/sales/pages/about_deposit_with_discount.rb
index 1c684b0bc..5d1e41a95 100644
--- a/app/models/form/sales/pages/about_deposit_with_discount.rb
+++ b/app/models/form/sales/pages/about_deposit_with_discount.rb
@@ -1,15 +1,22 @@
class Form::Sales::Pages::AboutDepositWithDiscount < ::Form::Page
- def initialize(id, hsh, subsection)
- super
- @id = "about_deposit_with_discount"
+ def initialize(id, hsh, subsection, optional:)
+ super(id, hsh, subsection)
@header = "About the deposit"
- @depends_on = [{ "is_type_discount?" => true }]
+ @optional = optional
end
def questions
@questions ||= [
- Form::Sales::Questions::DepositAmount.new(nil, nil, self, ownershipsch: 1),
+ Form::Sales::Questions::DepositAmount.new(nil, nil, self, ownershipsch: 1, optional: @optional),
Form::Sales::Questions::DepositDiscount.new(nil, nil, self),
]
end
+
+ def depends_on
+ if form.start_year_after_2024?
+ [{ "is_type_discount?" => true, "stairowned_100?" => @optional }]
+ else
+ [{ "is_type_discount?" => true }]
+ end
+ end
end
diff --git a/app/models/form/sales/pages/about_deposit_without_discount.rb b/app/models/form/sales/pages/about_deposit_without_discount.rb
index 1114b8f84..fdd74cf31 100644
--- a/app/models/form/sales/pages/about_deposit_without_discount.rb
+++ b/app/models/form/sales/pages/about_deposit_without_discount.rb
@@ -1,16 +1,26 @@
class Form::Sales::Pages::AboutDepositWithoutDiscount < ::Form::Page
- def initialize(id, hsh, subsection, ownershipsch:)
+ def initialize(id, hsh, subsection, ownershipsch:, optional:)
super(id, hsh, subsection)
@header = "About the deposit"
- @depends_on = [{ "is_type_discount?" => false, "ownershipsch" => 1 },
- { "ownershipsch" => 2 },
- { "ownershipsch" => 3, "mortgageused" => 1 }]
@ownershipsch = ownershipsch
+ @optional = optional
end
def questions
@questions ||= [
- Form::Sales::Questions::DepositAmount.new(nil, nil, self, ownershipsch: @ownershipsch),
+ Form::Sales::Questions::DepositAmount.new(nil, nil, self, ownershipsch: @ownershipsch, optional: @optional),
]
end
+
+ def depends_on
+ if form.start_year_after_2024?
+ [{ "is_type_discount?" => false, "ownershipsch" => 1, "stairowned_100?" => @optional },
+ { "ownershipsch" => 2 },
+ { "ownershipsch" => 3, "mortgageused" => 1 }]
+ else
+ [{ "is_type_discount?" => false, "ownershipsch" => 1 },
+ { "ownershipsch" => 2 },
+ { "ownershipsch" => 3, "mortgageused" => 1 }]
+ end
+ end
end
diff --git a/app/models/form/sales/pages/last_accommodation.rb b/app/models/form/sales/pages/last_accommodation.rb
index 373a20511..2d3448399 100644
--- a/app/models/form/sales/pages/last_accommodation.rb
+++ b/app/models/form/sales/pages/last_accommodation.rb
@@ -10,4 +10,10 @@ class Form::Sales::Pages::LastAccommodation < ::Form::Page
Form::Sales::Questions::PreviousPostcode.new(nil, nil, self),
]
end
+
+ def routed_to?(log, _user)
+ return false if log.form.start_year_after_2024? && log.discounted_ownership_sale?
+
+ super
+ end
end
diff --git a/app/models/form/sales/pages/last_accommodation_la.rb b/app/models/form/sales/pages/last_accommodation_la.rb
index 6e4211e12..615a30196 100644
--- a/app/models/form/sales/pages/last_accommodation_la.rb
+++ b/app/models/form/sales/pages/last_accommodation_la.rb
@@ -13,4 +13,10 @@ class Form::Sales::Pages::LastAccommodationLa < ::Form::Page
Form::Sales::Questions::Prevloc.new(nil, nil, self),
]
end
+
+ def routed_to?(log, _user)
+ return false if log.form.start_year_after_2024? && log.discounted_ownership_sale?
+
+ super
+ end
end
diff --git a/app/models/form/sales/questions/deposit_amount.rb b/app/models/form/sales/questions/deposit_amount.rb
index 784bb56a1..689299e56 100644
--- a/app/models/form/sales/questions/deposit_amount.rb
+++ b/app/models/form/sales/questions/deposit_amount.rb
@@ -1,5 +1,5 @@
class Form::Sales::Questions::DepositAmount < ::Form::Question
- def initialize(id, hsh, subsection, ownershipsch:)
+ def initialize(id, hsh, subsection, ownershipsch:, optional:)
super(id, hsh, subsection)
@id = "deposit"
@check_answer_label = "Cash deposit"
@@ -10,10 +10,10 @@ class Form::Sales::Questions::DepositAmount < ::Form::Question
@step = 1
@width = 5
@prefix = "£"
- @hint_text = "Enter the total cash sum paid by the buyer towards the property that was not funded by the mortgage"
@derived = true
@ownershipsch = ownershipsch
@question_number = question_number
+ @optional = optional
end
def selected_answer_option_is_derived?(_log)
@@ -30,4 +30,12 @@ class Form::Sales::Questions::DepositAmount < ::Form::Question
116
end
end
+
+ def hint_text
+ if @optional
+ "Enter the total cash sum paid by the buyer towards the property that was not funded by the mortgage. As this is a fully staircased sale this question is optional. If you do not have the information available click save and continue"
+ else
+ "Enter the total cash sum paid by the buyer towards the property that was not funded by the mortgage"
+ end
+ end
end
diff --git a/app/models/form/sales/questions/mortgage_lender.rb b/app/models/form/sales/questions/mortgage_lender.rb
index e2f57306e..ee78ca639 100644
--- a/app/models/form/sales/questions/mortgage_lender.rb
+++ b/app/models/form/sales/questions/mortgage_lender.rb
@@ -7,7 +7,6 @@ class Form::Sales::Questions::MortgageLender < ::Form::Question
@type = "select"
@hint_text = ""
@page = page
- @answer_options = ANSWER_OPTIONS
@bottom_guidance_partial = "mortgage_lender"
@ownershipsch = ownershipsch
@question_number = question_number
@@ -54,54 +53,24 @@ class Form::Sales::Questions::MortgageLender < ::Form::Question
"37" => "Virgin Money",
"38" => "West Bromwich Building Society",
"39" => "Yorkshire Building Society",
+ "41" => "Kent Reliance",
"40" => "Other",
"0" => "Don’t know",
}.freeze
+ OPTIONS_INTRODUCED_2024 = %w[41].freeze
+ OPTIONS_NOT_DISPLAYED = %w[0].freeze
+
+ def answer_options
+ if form.start_year_after_2024?
+ ANSWER_OPTIONS
+ else
+ ANSWER_OPTIONS.dup.reject { |k, _v| OPTIONS_INTRODUCED_2024.include?(k) }
+ end
+ end
+
def displayed_answer_options(_log, _user = nil)
- {
- "" => "Select an option",
- "1" => "Atom Bank",
- "2" => "Barclays Bank PLC",
- "3" => "Bath Building Society",
- "4" => "Buckinghamshire Building Society",
- "5" => "Cambridge Building Society",
- "6" => "Coventry Building Society",
- "7" => "Cumberland Building Society",
- "8" => "Darlington Building Society",
- "9" => "Dudley Building Society",
- "10" => "Ecology Building Society",
- "11" => "Halifax",
- "12" => "Hanley Economic Building Society",
- "13" => "Hinckley and Rugby Building Society",
- "14" => "Holmesdale Building Society",
- "15" => "Ipswich Building Society",
- "16" => "Leeds Building Society",
- "17" => "Lloyds Bank",
- "18" => "Mansfield Building Society",
- "19" => "Market Harborough Building Society",
- "20" => "Melton Mowbray Building Society",
- "21" => "Nationwide Building Society",
- "22" => "Natwest",
- "23" => "Nedbank Private Wealth",
- "24" => "Newbury Building Society",
- "25" => "OneSavings Bank",
- "26" => "Parity Trust",
- "27" => "Penrith Building Society",
- "28" => "Pepper Homeloans",
- "29" => "Royal Bank of Scotland",
- "30" => "Santander",
- "31" => "Skipton Building Society",
- "32" => "Teachers Building Society",
- "33" => "The Co-operative Bank",
- "34" => "Tipton & Coseley Building Society",
- "35" => "TSB",
- "36" => "Ulster Bank",
- "37" => "Virgin Money",
- "38" => "West Bromwich Building Society",
- "39" => "Yorkshire Building Society",
- "40" => "Other",
- }
+ answer_options.reject { |k, _v| OPTIONS_NOT_DISPLAYED.include?(k) }
end
def question_number
diff --git a/app/models/form/sales/questions/nationality_all_group.rb b/app/models/form/sales/questions/nationality_all_group.rb
index c9b1e71bc..faf29487e 100644
--- a/app/models/form/sales/questions/nationality_all_group.rb
+++ b/app/models/form/sales/questions/nationality_all_group.rb
@@ -10,6 +10,7 @@ class Form::Sales::Questions::NationalityAllGroup < ::Form::Question
@question_number = buyer_index == 1 ? 24 : 32
@conditional_for = buyer_index == 1 ? { "nationality_all" => [12] } : { "nationality_all_buyer2" => [12] }
@hidden_in_check_answers = { "depends_on" => [{ id => 12 }] }
+ @buyer_index = buyer_index
end
ANSWER_OPTIONS = {
@@ -17,4 +18,12 @@ class Form::Sales::Questions::NationalityAllGroup < ::Form::Question
"12" => { "value" => "Other" },
"0" => { "value" => "Buyer prefers not to say" },
}.freeze
+
+ def hint_text
+ if @buyer_index == 1
+ "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest. If buyer 1 is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, the buyer should decide which country to enter."
+ else
+ "If buyer 2 is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, the buyer should decide which country to enter."
+ end
+ end
end
diff --git a/app/models/form/sales/questions/privacy_notice.rb b/app/models/form/sales/questions/privacy_notice.rb
index d440e414b..62eab5d8f 100644
--- a/app/models/form/sales/questions/privacy_notice.rb
+++ b/app/models/form/sales/questions/privacy_notice.rb
@@ -5,12 +5,17 @@ class Form::Sales::Questions::PrivacyNotice < ::Form::Question
@check_answer_label = "Buyer has seen the privacy notice?"
@header = "Declaration"
@type = "checkbox"
- @answer_options = ANSWER_OPTIONS
- @top_guidance_partial = "privacy_notice_buyer"
+ @top_guidance_partial = form.start_year_after_2024? ? "privacy_notice_buyer_2024" : "privacy_notice_buyer"
@question_number = 19
end
- ANSWER_OPTIONS = {
- "privacynotice" => { "value" => "The buyer has seen the DLUHC privacy notice" },
- }.freeze
+ def answer_options
+ declaration_text = if form.start_year_after_2024?
+ "The buyer has seen or been given access to the DLUHC privacy notice"
+ else
+ "The buyer has seen the DLUHC privacy notice"
+ end
+
+ { "privacynotice" => { "value" => declaration_text } }.freeze
+ end
end
diff --git a/app/models/form/sales/subsections/discounted_ownership_scheme.rb b/app/models/form/sales/subsections/discounted_ownership_scheme.rb
index 52ee9e95c..200565ab6 100644
--- a/app/models/form/sales/subsections/discounted_ownership_scheme.rb
+++ b/app/models/form/sales/subsections/discounted_ownership_scheme.rb
@@ -31,7 +31,7 @@ class Form::Sales::Subsections::DiscountedOwnershipScheme < ::Form::Subsection
Form::Sales::Pages::MortgageLength.new("mortgage_length_discounted_ownership", nil, self, ownershipsch: 2),
Form::Sales::Pages::ExtraBorrowing.new("extra_borrowing_discounted_ownership", nil, self, ownershipsch: 2),
Form::Sales::Pages::ExtraBorrowingValueCheck.new("extra_borrowing_value_check", nil, self),
- Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_discounted_ownership", nil, self, ownershipsch: 2),
+ Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_discounted_ownership", nil, self, ownershipsch: 2, optional: false),
Form::Sales::Pages::ExtraBorrowingValueCheck.new("extra_borrowing_deposit_value_check", nil, self),
Form::Sales::Pages::DepositValueCheck.new("discounted_ownership_deposit_value_check", nil, self),
Form::Sales::Pages::DepositAndMortgageValueCheck.new("discounted_ownership_deposit_and_mortgage_value_check_after_deposit", nil, self),
diff --git a/app/models/form/sales/subsections/household_characteristics.rb b/app/models/form/sales/subsections/household_characteristics.rb
index 187f7f34e..ac006b453 100644
--- a/app/models/form/sales/subsections/household_characteristics.rb
+++ b/app/models/form/sales/subsections/household_characteristics.rb
@@ -9,7 +9,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection
def pages
@pages ||= [
(Form::Sales::Pages::BuyerInterview.new(nil, nil, self) unless form.start_year_after_2024?),
- Form::Sales::Pages::PrivacyNotice.new(nil, nil, self),
+ (Form::Sales::Pages::PrivacyNotice.new(nil, nil, self) unless form.start_year_after_2024?),
Form::Sales::Pages::Age1.new(nil, nil, self),
Form::Sales::Pages::RetirementValueCheck.new("age_1_retirement_value_check", nil, self, person_index: 1),
Form::Sales::Pages::OldPersonsSharedOwnershipValueCheck.new("age_1_old_persons_shared_ownership_value_check", nil, self),
diff --git a/app/models/form/sales/subsections/outright_sale.rb b/app/models/form/sales/subsections/outright_sale.rb
index 245cbcb10..39275d7b2 100644
--- a/app/models/form/sales/subsections/outright_sale.rb
+++ b/app/models/form/sales/subsections/outright_sale.rb
@@ -18,7 +18,7 @@ class Form::Sales::Subsections::OutrightSale < ::Form::Subsection
(Form::Sales::Pages::MortgageLenderOther.new("mortgage_lender_other_outright_sale", nil, self, ownershipsch: 3) unless form.start_year_after_2024?),
Form::Sales::Pages::MortgageLength.new("mortgage_length_outright_sale", nil, self, ownershipsch: 3),
Form::Sales::Pages::ExtraBorrowing.new("extra_borrowing_outright_sale", nil, self, ownershipsch: 3),
- Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_outright_sale", nil, self, ownershipsch: 3),
+ Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_outright_sale", nil, self, ownershipsch: 3, optional: false),
Form::Sales::Pages::DepositValueCheck.new("outright_sale_deposit_value_check", nil, self),
leasehold_charge_pages,
Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_outright_sale_value_check", nil, self),
diff --git a/app/models/form/sales/subsections/setup.rb b/app/models/form/sales/subsections/setup.rb
index aef107585..49e9392bb 100644
--- a/app/models/form/sales/subsections/setup.rb
+++ b/app/models/form/sales/subsections/setup.rb
@@ -21,6 +21,7 @@ class Form::Sales::Subsections::Setup < ::Form::Subsection
Form::Sales::Pages::JointPurchase.new(nil, nil, self),
Form::Sales::Pages::NumberJointBuyers.new(nil, nil, self),
(Form::Sales::Pages::BuyerInterview.new(nil, nil, self) if form.start_year_after_2024?),
+ (Form::Sales::Pages::PrivacyNotice.new(nil, nil, self) if form.start_year_after_2024?),
].flatten.compact
end
end
diff --git a/app/models/form/sales/subsections/shared_ownership_scheme.rb b/app/models/form/sales/subsections/shared_ownership_scheme.rb
index 66a711dcd..51abe3656 100644
--- a/app/models/form/sales/subsections/shared_ownership_scheme.rb
+++ b/app/models/form/sales/subsections/shared_ownership_scheme.rb
@@ -37,14 +37,16 @@ class Form::Sales::Subsections::SharedOwnershipScheme < ::Form::Subsection
Form::Sales::Pages::MortgageLenderOther.new("mortgage_lender_other_shared_ownership", nil, self, ownershipsch: 1),
Form::Sales::Pages::MortgageLength.new("mortgage_length_shared_ownership", nil, self, ownershipsch: 1),
Form::Sales::Pages::ExtraBorrowing.new("extra_borrowing_shared_ownership", nil, self, ownershipsch: 1),
- Form::Sales::Pages::AboutDepositWithDiscount.new(nil, nil, self),
- Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_shared_ownership", nil, self, ownershipsch: 1),
+ Form::Sales::Pages::AboutDepositWithDiscount.new("about_deposit_with_discount", nil, self, optional: false),
+ (Form::Sales::Pages::AboutDepositWithDiscount.new("about_deposit_with_discount_optional", nil, self, optional: true) if form.start_year_after_2024?),
+ Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_shared_ownership", nil, self, ownershipsch: 1, optional: false),
+ (Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_shared_ownership_optional", nil, self, ownershipsch: 1, optional: true) if form.start_year_after_2024?),
Form::Sales::Pages::DepositValueCheck.new("deposit_value_check", nil, self),
Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_deposit_value_check", nil, self),
Form::Sales::Pages::MonthlyRent.new(nil, nil, self),
Form::Sales::Pages::LeaseholdCharges.new("leasehold_charges_shared_ownership", nil, self, ownershipsch: 1),
Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_shared_ownership_value_check", nil, self),
- ]
+ ].compact
end
def displayed_in_tasklist?(log)
diff --git a/app/models/form_handler.rb b/app/models/form_handler.rb
index 267fa2968..32968fa53 100644
--- a/app/models/form_handler.rb
+++ b/app/models/form_handler.rb
@@ -50,11 +50,10 @@ class FormHandler
end
def ordered_sales_questions_for_all_years
- sales_forms = forms.filter { |name, _form| name.end_with? "sales" }.values
- ordered_questions = sales_forms.pop.questions.uniq(&:id)
- question_ids = ordered_questions.map(&:id)
- all_questions_from_previous_forms = sales_forms.flat_map(&:questions)
- deprecated_questions_by_preceding_question_id(question_ids, all_questions_from_previous_forms).each do |preceding_question_id, deprecated_question|
+ ordered_questions = current_sales_form.questions.uniq(&:id)
+ all_sales_forms = forms.filter { |name, _form| name.end_with? "sales" }.values
+ all_questions_from_available_sales_forms = all_sales_forms.flat_map(&:questions)
+ deprecated_questions_by_preceding_question_id(ordered_questions, all_questions_from_available_sales_forms).each do |preceding_question_id, deprecated_question|
index_of_preceding_question = ordered_questions.index { |q| q.id == preceding_question_id }
ordered_questions.insert(index_of_preceding_question + 1, deprecated_question)
end
@@ -62,23 +61,28 @@ class FormHandler
end
def ordered_lettings_questions_for_all_years
- lettings_forms = forms.filter { |name, _form| name.end_with? "lettings" }.values
- ordered_questions = lettings_forms.pop.questions.uniq(&:id)
- question_ids = ordered_questions.map(&:id)
- all_questions_from_previous_forms = lettings_forms.flat_map(&:questions)
- deprecated_questions_by_preceding_question_id(question_ids, all_questions_from_previous_forms).each do |preceding_question_id, deprecated_question|
+ ordered_questions = current_lettings_form.questions.uniq(&:id)
+ all_lettings_forms = forms.filter { |name, _form| name.end_with? "lettings" }.values
+ all_questions_from_available_lettings_forms = all_lettings_forms.flat_map(&:questions)
+ deprecated_questions_by_preceding_question_id(ordered_questions, all_questions_from_available_lettings_forms).each do |preceding_question_id, deprecated_question|
index_of_preceding_question = ordered_questions.index { |q| q.id == preceding_question_id }
ordered_questions.insert(index_of_preceding_question + 1, deprecated_question)
end
ordered_questions
end
- def deprecated_questions_by_preceding_question_id(current_form_question_ids, all_questions_from_previous_forms)
+ def deprecated_questions_by_preceding_question_id(current_form_questions, all_questions_from_previous_forms)
+ current_form_question_ids = current_form_questions.map(&:id)
deprecated_questions = {}
all_questions_from_previous_forms.each_cons(2) do |preceding_question, question|
next if current_form_question_ids.include?(question.id) || deprecated_questions.values.map(&:id).include?(question.id)
- deprecated_questions[preceding_question.id] = question
+ if question.subsection.id == preceding_question.subsection.id
+ deprecated_questions[preceding_question.id] = question
+ else
+ last_in_preceding_subsection = current_form_questions.rindex { |q| q.subsection.id == preceding_question.subsection.id }
+ deprecated_questions[current_form_questions[last_in_preceding_subsection].id] = question
+ end
end
deprecated_questions
end
diff --git a/app/models/sales_log.rb b/app/models/sales_log.rb
index 76c43b09e..13d16a786 100644
--- a/app/models/sales_log.rb
+++ b/app/models/sales_log.rb
@@ -121,6 +121,7 @@ class SalesLog < Log
not_required << "proplen" if proplen_optional?
not_required << "mortlen" if mortlen_optional?
not_required << "frombeds" if frombeds_optional?
+ not_required << "deposit" if form.start_year_after_2024? && stairowned_100?
not_required |= %w[address_line2 county postcode_full] if saledate && collection_start_year_for_date(saledate) >= 2023
@@ -495,4 +496,8 @@ class SalesLog < Log
def is_not_staircasing?
staircase == 2 || staircase == 3
end
+
+ def stairowned_100?
+ stairowned == 100
+ end
end
diff --git a/app/models/validations/financial_validations.rb b/app/models/validations/financial_validations.rb
index a1d799624..890c28284 100644
--- a/app/models/validations/financial_validations.rb
+++ b/app/models/validations/financial_validations.rb
@@ -24,7 +24,7 @@ module Validations::FinancialValidations
end
def validate_net_income(record)
- if record.ecstat1 && record.hhmemb && record.weekly_net_income
+ if record.ecstat1 && record.hhmemb && record.weekly_net_income && record.startdate && record.form.start_date.year >= 2023
if record.weekly_net_income > record.applicable_income_range.hard_max
frequency = record.form.get_question("incfreq", record).label_from_value(record.incfreq).downcase
hard_max = format_as_currency(record.applicable_income_range.hard_max)
diff --git a/app/models/validations/sales/household_validations.rb b/app/models/validations/sales/household_validations.rb
index 6e10b89b5..c23a72609 100644
--- a/app/models/validations/sales/household_validations.rb
+++ b/app/models/validations/sales/household_validations.rb
@@ -11,15 +11,6 @@ module Validations::Sales::HouseholdValidations
shared_validate_partner_count(record, 6)
end
- def validate_previous_postcode(record)
- return unless record.postcode_full && record.ppostcode_full && record.discounted_ownership_sale?
-
- unless record.postcode_full == record.ppostcode_full
- record.errors.add :postcode_full, :postcodes_not_matching, message: I18n.t("validations.household.postcode.discounted_ownership")
- record.errors.add :ppostcode_full, :postcodes_not_matching, message: I18n.t("validations.household.postcode.discounted_ownership")
- end
- end
-
def validate_buyers_living_in_property(record)
return unless record.form.start_date.year >= 2023
diff --git a/app/models/validations/sales/property_validations.rb b/app/models/validations/sales/property_validations.rb
index 3a8f6a463..fc8e4759b 100644
--- a/app/models/validations/sales/property_validations.rb
+++ b/app/models/validations/sales/property_validations.rb
@@ -1,5 +1,6 @@
module Validations::Sales::PropertyValidations
def validate_postcodes_match_if_discounted_ownership(record)
+ return unless record.saledate && !record.form.start_year_after_2024?
return unless record.ppostcode_full.present? && record.postcode_full.present?
if record.discounted_ownership_sale? && record.ppostcode_full != record.postcode_full
diff --git a/app/models/validations/sales/sale_information_validations.rb b/app/models/validations/sales/sale_information_validations.rb
index 0aaa77a93..c8a4e46dc 100644
--- a/app/models/validations/sales/sale_information_validations.rb
+++ b/app/models/validations/sales/sale_information_validations.rb
@@ -110,4 +110,14 @@ module Validations::Sales::SaleInformationValidations
end
end
end
+
+ def validate_mortgage_used_and_stairbought(record)
+ return unless record.stairowned && record.mortgageused
+ return unless record.saledate && record.form.start_year_after_2024?
+
+ if !record.stairowned_100? && record.mortgageused == 3
+ record.errors.add :stairowned, I18n.t("validations.sale_information.stairowned.mortgageused_dont_know")
+ record.errors.add :mortgageused, I18n.t("validations.sale_information.stairowned.mortgageused_dont_know")
+ end
+ end
end
diff --git a/app/services/bulk_upload/lettings/year2024/row_parser.rb b/app/services/bulk_upload/lettings/year2024/row_parser.rb
index 5f05b6e10..34823ee91 100644
--- a/app/services/bulk_upload/lettings/year2024/row_parser.rb
+++ b/app/services/bulk_upload/lettings/year2024/row_parser.rb
@@ -377,8 +377,6 @@ class BulkUpload::Lettings::Year2024::RowParser
validate :validate_created_by_exists, on: :after_log
validate :validate_created_by_related, on: :after_log
- validate :validate_declaration_acceptance, on: :after_log
-
validate :validate_nulls, on: :after_log
validate :validate_uprn_exists_if_any_key_address_fields_are_blank, on: :after_log, unless: -> { supported_housing? }
@@ -494,12 +492,6 @@ class BulkUpload::Lettings::Year2024::RowParser
private
- def validate_declaration_acceptance
- unless field_15 == 1
- errors.add(:field_15, I18n.t("validations.declaration.missing"), category: :setup)
- end
- end
-
def validate_valid_radio_option
log.attributes.each do |question_id, _v|
question = log.form.get_question(question_id, log)
@@ -659,10 +651,11 @@ private
end
def validate_lettings_allocation
- if cbl.blank? && cap.blank? && chr.blank?
+ if cbl.blank? && cap.blank? && chr.blank? && accessible_register.blank?
errors.add(:field_112, I18n.t("validations.not_answered", question: "was the letting made under the Choice-Based Lettings (CBL)?"))
errors.add(:field_113, I18n.t("validations.not_answered", question: "was the letting made under the Common Allocation Policy (CAP)?"))
errors.add(:field_114, I18n.t("validations.not_answered", question: "was the letting made under the Common Housing Register (CHR)?"))
+ errors.add(:field_115, I18n.t("validations.not_answered", question: "was the letting made under the Accessible Register?"))
end
end
@@ -953,9 +946,10 @@ private
rp_dontknow: %i[field_111],
cbl: %i[field_112],
- chr: %i[field_114],
cap: %i[field_113],
- letting_allocation: %i[field_112 field_113 field_114],
+ chr: %i[field_114],
+ accessible_register: %i[field_115],
+ letting_allocation: %i[field_112 field_113 field_114 field_115],
referral: %i[field_116],
@@ -1141,6 +1135,7 @@ private
attributes["cbl"] = cbl
attributes["chr"] = chr
attributes["cap"] = cap
+ attributes["accessible_register"] = accessible_register
attributes["letting_allocation_unknown"] = letting_allocation_unknown
attributes["referral"] = field_116
@@ -1390,6 +1385,15 @@ private
end
end
+ def cap
+ case field_113
+ when 2
+ 0
+ when 1
+ 1
+ end
+ end
+
def chr
case field_114
when 2
@@ -1399,8 +1403,8 @@ private
end
end
- def cap
- case field_113
+ def accessible_register
+ case field_115
when 2
0
when 1
@@ -1409,7 +1413,7 @@ private
end
def letting_allocation_unknown
- [cbl, chr, cap].all?(0) ? 1 : 0
+ [cbl, chr, cap, accessible_register].all?(0) ? 1 : 0
end
def net_income_known
diff --git a/app/services/bulk_upload/sales/year2024/row_parser.rb b/app/services/bulk_upload/sales/year2024/row_parser.rb
index 28e7ef100..0e1ee650e 100644
--- a/app/services/bulk_upload/sales/year2024/row_parser.rb
+++ b/app/services/bulk_upload/sales/year2024/row_parser.rb
@@ -341,6 +341,14 @@ class BulkUpload::Sales::Year2024::RowParser
},
on: :before_log
+ validates :field_103,
+ inclusion: {
+ in: [1, 2],
+ if: proc { field_88 != 100 },
+ question: QUESTIONS[:field_103],
+ },
+ on: :before_log
+
validates :field_9,
presence: {
message: I18n.t("validations.not_answered", question: "type of shared ownership sale"),
@@ -460,7 +468,6 @@ class BulkUpload::Sales::Year2024::RowParser
validate :validate_address_fields, on: :after_log
validate :validate_if_log_already_exists, on: :after_log, if: -> { FeatureToggle.bulk_upload_duplicate_log_check_enabled? }
- validate :validate_data_protection_answered, on: :after_log
validate :validate_buyers_organisations, on: :after_log
def self.question_for_field(field)
@@ -557,12 +564,6 @@ class BulkUpload::Sales::Year2024::RowParser
private
- def validate_data_protection_answered
- unless field_18 == 1
- errors.add(:field_18, I18n.t("validations.not_answered", question: QUESTIONS[:field_18].downcase), category: :setup)
- end
- end
-
def validate_buyers_organisations
organisations_fields = %i[field_66 field_67 field_68 field_69]
if organisations_fields.all? { |field| attributes[field.to_s].blank? }
diff --git a/app/services/csv/lettings_log_csv_service.rb b/app/services/csv/lettings_log_csv_service.rb
index c853590b6..e9ebd875a 100644
--- a/app/services/csv/lettings_log_csv_service.rb
+++ b/app/services/csv/lettings_log_csv_service.rb
@@ -263,7 +263,7 @@ module Csv
"renttype" => RENTTYPE_LABELS,
}.freeze
- CONVENTIONAL_YES_NO_ATTRIBUTES = %w[illness_type_1 illness_type_2 illness_type_3 illness_type_4 illness_type_5 illness_type_6 illness_type_7 illness_type_8 illness_type_9 illness_type_10 refused cbl cap chr letting_allocation_none housingneeds_a housingneeds_b housingneeds_c housingneeds_d housingneeds_e housingneeds_f housingneeds_g housingneeds_h has_benefits nocharge postcode_known].freeze
+ CONVENTIONAL_YES_NO_ATTRIBUTES = %w[illness_type_1 illness_type_2 illness_type_3 illness_type_4 illness_type_5 illness_type_6 illness_type_7 illness_type_8 illness_type_9 illness_type_10 refused cbl cap chr accessible_register letting_allocation_none housingneeds_a housingneeds_b housingneeds_c housingneeds_d housingneeds_e housingneeds_f housingneeds_g housingneeds_h has_benefits nocharge postcode_known].freeze
YES_OR_BLANK_ATTRIBUTES = %w[declaration rp_homeless rp_insan_unsat rp_medwel rp_hardship rp_dontknow].freeze
diff --git a/app/services/exports/lettings_log_export_service.rb b/app/services/exports/lettings_log_export_service.rb
index 404b9f0e6..e63219f7d 100644
--- a/app/services/exports/lettings_log_export_service.rb
+++ b/app/services/exports/lettings_log_export_service.rb
@@ -12,9 +12,9 @@ module Exports
start_time = Time.zone.now
daily_run_number = get_daily_run_number
archives_for_manifest = {}
- base_number = LogsExport.where(empty_export: false).maximum(:base_number) || 1
recent_export = LogsExport.order("started_at").last
collection_years_to_export(collection_year).each do |collection|
+ base_number = LogsExport.where(empty_export: false, collection:).maximum(:base_number) || 1
export = build_export_run(collection, start_time, base_number, full_update)
archives = write_export_archive(export, collection, start_time, recent_export, full_update)
diff --git a/app/views/bulk_upload_lettings_resume/deletion_report.html.erb b/app/views/bulk_upload_lettings_resume/deletion_report.html.erb
index 2624012d4..881f9026c 100644
--- a/app/views/bulk_upload_lettings_resume/deletion_report.html.erb
+++ b/app/views/bulk_upload_lettings_resume/deletion_report.html.erb
@@ -18,7 +18,9 @@
<% @bulk_upload.bulk_upload_errors.order_by_row.order_by_cell.group_by(&:row).each do |_row, errors_for_row| %>
- <%= render BulkUploadErrorRowComponent.new(bulk_upload_errors: all_answers_to_be_cleared(errors_for_row)) %>
+ <% if all_answers_to_be_cleared(errors_for_row).present? %>
+ <%= render BulkUploadErrorRowComponent.new(bulk_upload_errors: all_answers_to_be_cleared(errors_for_row)) %>
+ <% end %>
<% end %>
diff --git a/app/views/bulk_upload_sales_resume/deletion_report.html.erb b/app/views/bulk_upload_sales_resume/deletion_report.html.erb
index 3b6088c3c..299cff4f4 100644
--- a/app/views/bulk_upload_sales_resume/deletion_report.html.erb
+++ b/app/views/bulk_upload_sales_resume/deletion_report.html.erb
@@ -18,7 +18,9 @@
<% @bulk_upload.bulk_upload_errors.order_by_row.order_by_cell.group_by(&:row).each do |_row, errors_for_row| %>
- <%= render BulkUploadErrorRowComponent.new(bulk_upload_errors: all_answers_to_be_cleared(errors_for_row)) %>
+ <% if all_answers_to_be_cleared(errors_for_row).present? %>
+ <%= render BulkUploadErrorRowComponent.new(bulk_upload_errors: all_answers_to_be_cleared(errors_for_row)) %>
+ <% end %>
<% end %>
diff --git a/app/views/form/guidance/_privacy_notice_buyer_2024.erb b/app/views/form/guidance/_privacy_notice_buyer_2024.erb
new file mode 100644
index 000000000..9977bf20b
--- /dev/null
+++ b/app/views/form/guidance/_privacy_notice_buyer_2024.erb
@@ -0,0 +1 @@
+Make sure the buyer has seen or been given access to <%= govuk_link_to "the Department for Levelling Up, Housing & Communities (DLUHC) privacy notice", privacy_notice_path, target: :_blank %> before completing this log. This is a legal requirement under data protection legislation.
diff --git a/app/views/form/guidance/_privacy_notice_tenant_2024.erb b/app/views/form/guidance/_privacy_notice_tenant_2024.erb
new file mode 100644
index 000000000..3007c8904
--- /dev/null
+++ b/app/views/form/guidance/_privacy_notice_tenant_2024.erb
@@ -0,0 +1 @@
+Make sure the lead tenant has seen or been given access to <%= govuk_link_to "the Department for Levelling Up, Housing & Communities (DLUHC) privacy notice", privacy_notice_path, target: :_blank %> before completing this log. This is a legal requirement under data protection legislation.
diff --git a/app/views/notifications/_notification_banner.html.erb b/app/views/notifications/_notification_banner.html.erb
index 230fe458a..cd7dfffac 100644
--- a/app/views/notifications/_notification_banner.html.erb
+++ b/app/views/notifications/_notification_banner.html.erb
@@ -6,7 +6,7 @@
<% if notification_count > 1 && current_user.present? %>
Notification 1 of <%= notification_count %>
<% end %>
- <%= notification.title %>
+ <%= notification.title.html_safe %>
<% if notification.page_content.present? %>
<%= govuk_link_to notification.link_text, notifications_path, class: "govuk-link--inverse govuk-!-font-weight-bold" %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 204a20b4e..37239d234 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -623,6 +623,8 @@ en:
over_discounted_london_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £136,400 for properties in London."
over_discounted_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £102,400 for properties outside of London."
non_staircasing_mortgage: "The mortgage and deposit added together is %{mortgage_and_deposit_total} and the purchase price times by the equity is %{expected_shared_ownership_deposit_value}. These figures should be the same."
+ stairowned:
+ mortgageused_dont_know: "The percentage owned has to be 100% if the mortgage used is 'Don’t know'"
merge_request:
organisation_part_of_another_merge: "This organisation is part of another merge - select a different one"
organisation_not_selected: "Select an organisation from the search list"
diff --git a/db/migrate/20240129161037_add_accessible_register_to_lettings_logs.rb b/db/migrate/20240129161037_add_accessible_register_to_lettings_logs.rb
new file mode 100644
index 000000000..623a95306
--- /dev/null
+++ b/db/migrate/20240129161037_add_accessible_register_to_lettings_logs.rb
@@ -0,0 +1,5 @@
+class AddAccessibleRegisterToLettingsLogs < ActiveRecord::Migration[7.0]
+ def change
+ add_column :lettings_logs, :accessible_register, :integer
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index e6f2de905..658e82190 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -305,6 +305,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_01_30_084707) do
t.integer "duplicate_set_id"
t.integer "nationality_all"
t.integer "nationality_all_group"
+ t.integer "accessible_register"
t.index ["bulk_upload_id"], name: "index_lettings_logs_on_bulk_upload_id"
t.index ["created_by_id"], name: "index_lettings_logs_on_created_by_id"
t.index ["location_id"], name: "index_lettings_logs_on_location_id"
diff --git a/lib/tasks/clear_invalidated_earnings.rake b/lib/tasks/clear_invalidated_earnings.rake
new file mode 100644
index 000000000..b474a675a
--- /dev/null
+++ b/lib/tasks/clear_invalidated_earnings.rake
@@ -0,0 +1,12 @@
+desc "Clear earnings for lettings logs that fail validation"
+task clear_invalidated_earnings: :environment do
+ LettingsLog.filter_by_year(2023).find_each do |lettings_log|
+ lettings_log.validate_net_income(lettings_log)
+ if lettings_log.errors[:earnings].present?
+ Rails.logger.info "Clearing earnings for lettings log #{lettings_log.id}, owning_organisation_id: #{lettings_log.owning_organisation_id}, managing_organisation_id: #{lettings_log.managing_organisation_id}, startdate: #{lettings_log.startdate.to_date}, tenancy reference: #{lettings_log.tenancycode}, property reference: #{lettings_log.propcode}, created_by: #{lettings_log.created_by.email}(#{lettings_log.created_by_id})"
+ lettings_log.earnings = nil
+ lettings_log.incfreq = nil
+ lettings_log.save!(validate: false)
+ end
+ end
+end
diff --git a/public/files/2024_25_sales_paper_form.pdf b/public/files/2024_25_sales_paper_form.pdf
index 4d06bc351..9bf01d08b 100644
Binary files a/public/files/2024_25_sales_paper_form.pdf and b/public/files/2024_25_sales_paper_form.pdf differ
diff --git a/spec/factories/lettings_log.rb b/spec/factories/lettings_log.rb
index f0493fc2d..23ba06bf1 100644
--- a/spec/factories/lettings_log.rb
+++ b/spec/factories/lettings_log.rb
@@ -112,6 +112,7 @@ FactoryBot.define do
cbl { 0 }
chr { 1 }
cap { 0 }
+ accessible_register { 0 }
reasonother { nil }
housingneeds { 1 }
housingneeds_type { 0 }
diff --git a/spec/fixtures/files/lettings_log_csv_export_codes.csv b/spec/fixtures/files/lettings_log_csv_export_codes_23.csv
similarity index 95%
rename from spec/fixtures/files/lettings_log_csv_export_codes.csv
rename to spec/fixtures/files/lettings_log_csv_export_codes_23.csv
index c68b53ae2..873c703b0 100644
--- a/spec/fixtures/files/lettings_log_csv_export_codes.csv
+++ b/spec/fixtures/files/lettings_log_csv_export_codes_23.csv
@@ -1,2 +1,2 @@
-id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,postcode_known,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate
-,completed,,s.port@jeemayle.com,false,2023-11-26T00:00:00+00:00,,2023-11-26T00:00:00+00:00,1,,,2023,DLUHC,DLUHC,1,7,0,2023-11-26,2,2,1,,2,HIJKLMN,ABCDEFG,1,0,,,fake address,,London,,NW9 5LL,false,Barnet,E09000003,0,2,6,2,2,7,1,1,3,2023-11-24,,,1,2023-11-25,,3,1,4,,2,,1,4,,1,4,0,0,2,35,,F,0,2,13,,0,0,P,32,M,6,1,R,-9,R,10,0,R,-9,R,10,,,,,,,,,,,,,,,,,,,,,1,4,1,2,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,7,4,,6,2,1,0,TN23 6LZ,1,false,Ashford,E07000105,1,0,1,0,0,0,0,0,1,,2,,0,0,268,1,,6,1,1,,0,2,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,1,0,12.0,6.0,,,,,,,,,,,,,,,,,,,,
+id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,postcode_known,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,nationality_all,national,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate
+,completed,,s.port@jeemayle.com,false,2023-11-26T00:00:00+00:00,,2023-11-26T00:00:00+00:00,1,,,2023,DLUHC,DLUHC,1,7,0,2023-11-26,2,2,1,,2,HIJKLMN,ABCDEFG,1,0,,,fake address,,London,,NW9 5LL,false,Barnet,E09000003,0,2,6,2,2,7,1,1,3,2023-11-24,,,1,2023-11-25,,3,1,4,,2,,1,4,,1,4,0,0,2,35,,F,0,2,,13,0,0,P,32,M,6,1,R,-9,R,10,0,R,-9,R,10,,,,,,,,,,,,,,,,,,,,,1,4,1,2,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,7,4,,6,2,1,0,TN23 6LZ,1,false,Ashford,E07000105,1,0,1,0,0,0,0,0,1,,2,,0,0,268,1,,6,1,1,,0,2,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,1,0,12.0,6.0,,,,,,,,,,,,,,,,,,,,
diff --git a/spec/fixtures/files/lettings_log_csv_export_codes_24.csv b/spec/fixtures/files/lettings_log_csv_export_codes_24.csv
new file mode 100644
index 000000000..832ceed45
--- /dev/null
+++ b/spec/fixtures/files/lettings_log_csv_export_codes_24.csv
@@ -0,0 +1,2 @@
+id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,declaration,postcode_known,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,accessible_register,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate
+,completed,,s.port@jeemayle.com,false,2023-11-26T00:00:00+00:00,,2024-04-01T00:00:00+01:00,1,,,2023,DLUHC,DLUHC,1,7,0,2023-11-26,2,2,1,,2,HIJKLMN,ABCDEFG,1,1,0,,,fake address,,London,,NW9 5LL,false,Barnet,E09000003,0,2,6,2,2,7,1,1,3,2023-11-24,,,1,2023-11-25,,3,1,4,,2,,4,,1,4,0,0,2,35,,F,0,2,13,,0,0,P,32,M,6,1,R,-9,R,10,0,R,-9,R,10,,,,,,,,,,,,,,,,,,,,,1,4,1,2,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,7,4,,6,2,1,0,TN23 6LZ,1,false,Ashford,E07000105,1,0,1,0,0,0,0,0,1,0,,2,,0,0,268,1,,6,1,1,,0,2,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,1,0,12.0,6.0,,,,,,,,,,,,,,,,,,,,
diff --git a/spec/fixtures/files/lettings_log_csv_export_labels.csv b/spec/fixtures/files/lettings_log_csv_export_labels_23.csv
similarity index 96%
rename from spec/fixtures/files/lettings_log_csv_export_labels.csv
rename to spec/fixtures/files/lettings_log_csv_export_labels_23.csv
index 8722ed90c..a9c491ed9 100644
--- a/spec/fixtures/files/lettings_log_csv_export_labels.csv
+++ b/spec/fixtures/files/lettings_log_csv_export_labels_23.csv
@@ -1,2 +1,2 @@
-id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,postcode_known,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate
-,completed,,s.port@jeemayle.com,false,2023-11-26T00:00:00+00:00,,2023-11-26T00:00:00+00:00,single log,,,2023,DLUHC,DLUHC,General needs,Affordable rent general needs local authority,No,2023-11-26,Affordable Rent,Affordable Rent,Rent to Buy,,No,HIJKLMN,ABCDEFG,Yes,No,,,fake address,,London,,NW9 5LL,No,Barnet,E09000003,No,Affordable rent basis,Tenant abandoned property,No,2,House,Purpose built,Yes,3,2023-11-24,,,Yes,2023-11-25,,Don’t know,Yes,Assured Shorthold Tenancy (AST) – Fixed term,,2,,Yes,4,,Yes,4,0,0,2,35,,Female,White,Irish,Tenant prefers not to say,,Other,Yes,Partner,32,Male,Not seeking work,No,Prefers not to say,Not known,Prefers not to say,Prefers not to say,Yes,Person prefers not to say,Not known,Person prefers not to say,Person prefers not to say,,,,,,,,,,,,,,,,,,,,,Yes – the person is a current or former regular,No – they left up to and including 5 years ago,Yes,No,Yes,Fully wheelchair accessible housing,Yes,No,No,No,No,No,No,Yes,No,No,Yes,No,No,No,No,No,No,No,Less than 1 year,1 year but under 2 years,Loss of tied accommodation,,Other supported housing,2,No,Yes,TN23 6LZ,Yes,No,Ashford,E07000105,Yes,,Yes,,,,No,No,Yes,,Tenant applied directly (no referral or nomination),,Yes,No,268,Weekly,,Universal Credit housing element,Yes,All,,No,Every 2 weeks,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,Yes,Yes,12.0,6.0,,,,,,,,,,,,,,,,,,,,
+id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,postcode_known,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,nationality_all,national,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate
+,completed,,s.port@jeemayle.com,false,2023-11-26T00:00:00+00:00,,2023-11-26T00:00:00+00:00,single log,,,2023,DLUHC,DLUHC,General needs,Affordable rent general needs local authority,No,2023-11-26,Affordable Rent,Affordable Rent,Rent to Buy,,No,HIJKLMN,ABCDEFG,Yes,No,,,fake address,,London,,NW9 5LL,No,Barnet,E09000003,No,Affordable rent basis,Tenant abandoned property,No,2,House,Purpose built,Yes,3,2023-11-24,,,Yes,2023-11-25,,Don’t know,Yes,Assured Shorthold Tenancy (AST) – Fixed term,,2,,Yes,4,,Yes,4,0,0,2,35,,Female,White,Irish,,Tenant prefers not to say,Other,Yes,Partner,32,Male,Not seeking work,No,Prefers not to say,Not known,Prefers not to say,Prefers not to say,Yes,Person prefers not to say,Not known,Person prefers not to say,Person prefers not to say,,,,,,,,,,,,,,,,,,,,,Yes – the person is a current or former regular,No – they left up to and including 5 years ago,Yes,No,Yes,Fully wheelchair accessible housing,Yes,No,No,No,No,No,No,Yes,No,No,Yes,No,No,No,No,No,No,No,Less than 1 year,1 year but under 2 years,Loss of tied accommodation,,Other supported housing,2,No,Yes,TN23 6LZ,Yes,No,Ashford,E07000105,Yes,,Yes,,,,No,No,Yes,,Tenant applied directly (no referral or nomination),,Yes,No,268,Weekly,,Universal Credit housing element,Yes,All,,No,Every 2 weeks,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,Yes,Yes,12.0,6.0,,,,,,,,,,,,,,,,,,,,
diff --git a/spec/fixtures/files/lettings_log_csv_export_labels_24.csv b/spec/fixtures/files/lettings_log_csv_export_labels_24.csv
new file mode 100644
index 000000000..0e345b837
--- /dev/null
+++ b/spec/fixtures/files/lettings_log_csv_export_labels_24.csv
@@ -0,0 +1,2 @@
+id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,declaration,postcode_known,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,accessible_register,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate
+,completed,,s.port@jeemayle.com,false,2023-11-26T00:00:00+00:00,,2024-04-01T00:00:00+01:00,single log,,,2023,DLUHC,DLUHC,General needs,Affordable rent general needs local authority,No,2023-11-26,Affordable Rent,Affordable Rent,Rent to Buy,,No,HIJKLMN,ABCDEFG,Yes,Yes,No,,,fake address,,London,,NW9 5LL,No,Barnet,E09000003,No,Affordable rent basis,Tenant abandoned property,No,2,House,Purpose built,Yes,3,2023-11-24,,,Yes,2023-11-25,,Don’t know,Yes,Assured Shorthold Tenancy (AST) – Fixed term,,2,,4,,Yes,4,0,0,2,35,,Female,White,Irish,Tenant prefers not to say,,Other,Yes,Partner,32,Male,Not seeking work,No,Prefers not to say,Not known,Prefers not to say,Prefers not to say,Yes,Person prefers not to say,Not known,Person prefers not to say,Person prefers not to say,,,,,,,,,,,,,,,,,,,,,Yes – the person is a current or former regular,No – they left up to and including 5 years ago,Yes,No,Yes,Fully wheelchair accessible housing,Yes,No,No,No,No,No,No,Yes,No,No,Yes,No,No,No,No,No,No,No,Less than 1 year,1 year but under 2 years,Loss of tied accommodation,,Other supported housing,2,No,Yes,TN23 6LZ,Yes,No,Ashford,E07000105,Yes,,Yes,,,,No,No,Yes,No,,Tenant applied directly (no referral or nomination),,Yes,No,268,Weekly,,Universal Credit housing element,Yes,All,,No,Every 2 weeks,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,Yes,Yes,12.0,6.0,,,,,,,,,,,,,,,,,,,,
diff --git a/spec/fixtures/files/lettings_log_csv_export_non_support_codes.csv b/spec/fixtures/files/lettings_log_csv_export_non_support_codes_23.csv
similarity index 95%
rename from spec/fixtures/files/lettings_log_csv_export_non_support_codes.csv
rename to spec/fixtures/files/lettings_log_csv_export_non_support_codes_23.csv
index 6989c0b9b..2cf49f785 100644
--- a/spec/fixtures/files/lettings_log_csv_export_non_support_codes.csv
+++ b/spec/fixtures/files/lettings_log_csv_export_non_support_codes_23.csv
@@ -1,2 +1,2 @@
-id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,uprn_known,uprn,address_line1,address_line2,town_or_city,county,postcode_full,la_label,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,refused,age1,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,relat2,age2,sex2,ecstat2,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,relat7,age7,sex7,ecstat7,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,homeless,ppcodenk,ppostcode_full,prevloc_label,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,incref,earnings,incfreq,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,scharge,pscharge,supcharg,tcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate
-,completed,,choreographer@owtluk.com,false,2023-11-26T00:00:00+00:00,,2023-11-26T00:00:00+00:00,1,2023,DLUHC,DLUHC,1,7,0,2023-11-26,2,2,1,,2,HIJKLMN,ABCDEFG,0,,fake address,,London,,NW9 5LL,Barnet,2,6,2,2,7,1,1,3,2023-11-24,1,,1,2023-11-25,,3,1,4,,2,,1,4,1,35,F,0,2,13,,0,P,32,M,6,R,-9,R,10,R,-9,R,10,,,,,,,,,,,,,,,,,1,4,1,2,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,7,4,,6,1,0,TN23 6LZ,Ashford,1,0,1,0,0,0,0,0,1,,2,,0,268,1,6,1,1,,0,2,,,,,200.0,50.0,40.0,35.0,325.0,,,,1,12.0,,,,,,,,,,,,,,,,,,,,
+id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,uprn_known,uprn,address_line1,address_line2,town_or_city,county,postcode_full,la_label,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,refused,age1,sex1,ethnic_group,ethnic,nationality_all,national,ecstat1,relat2,age2,sex2,ecstat2,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,relat7,age7,sex7,ecstat7,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,homeless,ppcodenk,ppostcode_full,prevloc_label,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,incref,earnings,incfreq,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,scharge,pscharge,supcharg,tcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate
+,completed,,choreographer@owtluk.com,false,2023-11-26T00:00:00+00:00,,2023-11-26T00:00:00+00:00,1,2023,DLUHC,DLUHC,1,7,0,2023-11-26,2,2,1,,2,HIJKLMN,ABCDEFG,0,,fake address,,London,,NW9 5LL,Barnet,2,6,2,2,7,1,1,3,2023-11-24,1,,1,2023-11-25,,3,1,4,,2,,1,4,1,35,F,0,2,,13,0,P,32,M,6,R,-9,R,10,R,-9,R,10,,,,,,,,,,,,,,,,,1,4,1,2,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,7,4,,6,1,0,TN23 6LZ,Ashford,1,0,1,0,0,0,0,0,1,,2,,0,268,1,6,1,1,,0,2,,,,,200.0,50.0,40.0,35.0,325.0,,,,1,12.0,,,,,,,,,,,,,,,,,,,,
diff --git a/spec/fixtures/files/lettings_log_csv_export_non_support_codes_24.csv b/spec/fixtures/files/lettings_log_csv_export_non_support_codes_24.csv
new file mode 100644
index 000000000..29c2db295
--- /dev/null
+++ b/spec/fixtures/files/lettings_log_csv_export_non_support_codes_24.csv
@@ -0,0 +1,2 @@
+id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,declaration,uprn_known,uprn,address_line1,address_line2,town_or_city,county,postcode_full,la_label,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,hhmemb,refused,age1,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,relat2,age2,sex2,ecstat2,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,relat7,age7,sex7,ecstat7,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,homeless,ppcodenk,ppostcode_full,prevloc_label,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,accessible_register,letting_allocation_none,referral,referral_value_check,incref,earnings,incfreq,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,scharge,pscharge,supcharg,tcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate
+,completed,,choreographer@owtluk.com,false,2023-11-26T00:00:00+00:00,,2024-04-01T00:00:00+01:00,1,2023,DLUHC,DLUHC,1,7,0,2023-11-26,2,2,1,,2,HIJKLMN,ABCDEFG,1,0,,fake address,,London,,NW9 5LL,Barnet,2,6,2,2,7,1,1,3,2023-11-24,1,,1,2023-11-25,,3,1,4,,2,,4,1,35,F,0,2,13,,0,P,32,M,6,R,-9,R,10,R,-9,R,10,,,,,,,,,,,,,,,,,1,4,1,2,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,7,4,,6,1,0,TN23 6LZ,Ashford,1,0,1,0,0,0,0,0,1,0,,2,,0,268,1,6,1,1,,0,2,,,,,200.0,50.0,40.0,35.0,325.0,,,,1,12.0,,,,,,,,,,,,,,,,,,,,
diff --git a/spec/fixtures/files/lettings_log_csv_export_non_support_labels.csv b/spec/fixtures/files/lettings_log_csv_export_non_support_labels_23.csv
similarity index 96%
rename from spec/fixtures/files/lettings_log_csv_export_non_support_labels.csv
rename to spec/fixtures/files/lettings_log_csv_export_non_support_labels_23.csv
index 33a852a21..2c2b63ee4 100644
--- a/spec/fixtures/files/lettings_log_csv_export_non_support_labels.csv
+++ b/spec/fixtures/files/lettings_log_csv_export_non_support_labels_23.csv
@@ -1,2 +1,2 @@
-id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,uprn_known,uprn,address_line1,address_line2,town_or_city,county,postcode_full,la_label,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,refused,age1,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,relat2,age2,sex2,ecstat2,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,relat7,age7,sex7,ecstat7,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,homeless,ppcodenk,ppostcode_full,prevloc_label,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,incref,earnings,incfreq,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,scharge,pscharge,supcharg,tcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate
-,completed,,choreographer@owtluk.com,false,2023-11-26T00:00:00+00:00,,2023-11-26T00:00:00+00:00,single log,2023,DLUHC,DLUHC,General needs,Affordable rent general needs local authority,No,2023-11-26,Affordable Rent,Affordable Rent,Rent to Buy,,No,HIJKLMN,ABCDEFG,No,,fake address,,London,,NW9 5LL,Barnet,Affordable rent basis,Tenant abandoned property,No,2,House,Purpose built,Yes,3,2023-11-24,1,,Yes,2023-11-25,,Don’t know,Yes,Assured Shorthold Tenancy (AST) – Fixed term,,2,,Yes,4,Yes,35,Female,White,Irish,Tenant prefers not to say,,Other,Partner,32,Male,Not seeking work,Prefers not to say,Not known,Prefers not to say,Prefers not to say,Person prefers not to say,Not known,Person prefers not to say,Person prefers not to say,,,,,,,,,,,,,,,,,Yes – the person is a current or former regular,No – they left up to and including 5 years ago,Yes,No,Yes,Fully wheelchair accessible housing,Yes,No,No,No,No,No,No,Yes,No,No,Yes,No,No,No,No,No,No,No,Less than 1 year,1 year but under 2 years,Loss of tied accommodation,,Other supported housing,No,Yes,TN23 6LZ,Ashford,Yes,,Yes,,,,No,No,Yes,,Tenant applied directly (no referral or nomination),,No,268,Weekly,Universal Credit housing element,Yes,All,,No,Every 2 weeks,,,,,200.0,50.0,40.0,35.0,325.0,,,,Yes,12.0,,,,,,,,,,,,,,,,,,,,
+id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,uprn_known,uprn,address_line1,address_line2,town_or_city,county,postcode_full,la_label,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,refused,age1,sex1,ethnic_group,ethnic,nationality_all,national,ecstat1,relat2,age2,sex2,ecstat2,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,relat7,age7,sex7,ecstat7,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,homeless,ppcodenk,ppostcode_full,prevloc_label,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,incref,earnings,incfreq,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,scharge,pscharge,supcharg,tcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate
+,completed,,choreographer@owtluk.com,false,2023-11-26T00:00:00+00:00,,2023-11-26T00:00:00+00:00,single log,2023,DLUHC,DLUHC,General needs,Affordable rent general needs local authority,No,2023-11-26,Affordable Rent,Affordable Rent,Rent to Buy,,No,HIJKLMN,ABCDEFG,No,,fake address,,London,,NW9 5LL,Barnet,Affordable rent basis,Tenant abandoned property,No,2,House,Purpose built,Yes,3,2023-11-24,1,,Yes,2023-11-25,,Don’t know,Yes,Assured Shorthold Tenancy (AST) – Fixed term,,2,,Yes,4,Yes,35,Female,White,Irish,,Tenant prefers not to say,Other,Partner,32,Male,Not seeking work,Prefers not to say,Not known,Prefers not to say,Prefers not to say,Person prefers not to say,Not known,Person prefers not to say,Person prefers not to say,,,,,,,,,,,,,,,,,Yes – the person is a current or former regular,No – they left up to and including 5 years ago,Yes,No,Yes,Fully wheelchair accessible housing,Yes,No,No,No,No,No,No,Yes,No,No,Yes,No,No,No,No,No,No,No,Less than 1 year,1 year but under 2 years,Loss of tied accommodation,,Other supported housing,No,Yes,TN23 6LZ,Ashford,Yes,,Yes,,,,No,No,Yes,,Tenant applied directly (no referral or nomination),,No,268,Weekly,Universal Credit housing element,Yes,All,,No,Every 2 weeks,,,,,200.0,50.0,40.0,35.0,325.0,,,,Yes,12.0,,,,,,,,,,,,,,,,,,,,
diff --git a/spec/fixtures/files/lettings_log_csv_export_non_support_labels_24.csv b/spec/fixtures/files/lettings_log_csv_export_non_support_labels_24.csv
new file mode 100644
index 000000000..cef9f5c72
--- /dev/null
+++ b/spec/fixtures/files/lettings_log_csv_export_non_support_labels_24.csv
@@ -0,0 +1,2 @@
+id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,renttype_detail,irproduct,irproduct_other,lar,tenancycode,propcode,declaration,uprn_known,uprn,address_line1,address_line2,town_or_city,county,postcode_full,la_label,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,hhmemb,refused,age1,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,relat2,age2,sex2,ecstat2,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,relat7,age7,sex7,ecstat7,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,homeless,ppcodenk,ppostcode_full,prevloc_label,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,accessible_register,letting_allocation_none,referral,referral_value_check,incref,earnings,incfreq,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,scharge,pscharge,supcharg,tcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate
+,completed,,choreographer@owtluk.com,false,2023-11-26T00:00:00+00:00,,2024-04-01T00:00:00+01:00,single log,2023,DLUHC,DLUHC,General needs,Affordable rent general needs local authority,No,2023-11-26,Affordable Rent,Affordable Rent,Rent to Buy,,No,HIJKLMN,ABCDEFG,Yes,No,,fake address,,London,,NW9 5LL,Barnet,Affordable rent basis,Tenant abandoned property,No,2,House,Purpose built,Yes,3,2023-11-24,1,,Yes,2023-11-25,,Don’t know,Yes,Assured Shorthold Tenancy (AST) – Fixed term,,2,,4,Yes,35,Female,White,Irish,Tenant prefers not to say,,Other,Partner,32,Male,Not seeking work,Prefers not to say,Not known,Prefers not to say,Prefers not to say,Person prefers not to say,Not known,Person prefers not to say,Person prefers not to say,,,,,,,,,,,,,,,,,Yes – the person is a current or former regular,No – they left up to and including 5 years ago,Yes,No,Yes,Fully wheelchair accessible housing,Yes,No,No,No,No,No,No,Yes,No,No,Yes,No,No,No,No,No,No,No,Less than 1 year,1 year but under 2 years,Loss of tied accommodation,,Other supported housing,No,Yes,TN23 6LZ,Ashford,Yes,,Yes,,,,No,No,Yes,No,,Tenant applied directly (no referral or nomination),,No,268,Weekly,Universal Credit housing element,Yes,All,,No,Every 2 weeks,,,,,200.0,50.0,40.0,35.0,325.0,,,,Yes,12.0,,,,,,,,,,,,,,,,,,,,
diff --git a/spec/fixtures/files/sales_logs_csv_export_codes_23.csv b/spec/fixtures/files/sales_logs_csv_export_codes_23.csv
new file mode 100644
index 000000000..ae93dd76c
--- /dev/null
+++ b/spec/fixtures/files/sales_logs_csv_export_codes_23.csv
@@ -0,0 +1,2 @@
+id,status,duplicate_set_id,created_at,updated_at,old_form_id,collection_start_year,creation_method,is_dpo,owning_organisation_name,managing_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,beds,proptype,builtype,pcodenk,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,wchair,noint,privacynotice,age1,sex1,ethnic_group,ethnic,nationality_all,national,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationality_all_buyer2,nationalbuy2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant
+,completed,,2023-12-08T00:00:00+00:00,2024-01-01T00:00:00+00:00,,2023,1,false,DLUHC,DLUHC,billyboy@eyeklaud.com,8,12,2023,,2,8,,,,1,1,2,1,1,0,,,Address line 1,,Town or city,,SW1A,1AA,1,E09000003,Barnet,1,2,1,30,X,17,17,,18,1,1,P,35,X,17,,,13,1,1,3,C,14,X,9,X,-9,X,3,R,-9,R,10,,,,,1,1,,,0,,,1,1,1,1,,3,,1,4,5,1,1,0,10000,1,0,10000,1,4,1,,1,2,10,,,,,,,,,,,,,,,,,110000.0,,1,20000.0,5,,10,1,80000.0,,,1,100.0,,10000.0
diff --git a/spec/fixtures/files/sales_logs_csv_export_codes.csv b/spec/fixtures/files/sales_logs_csv_export_codes_24.csv
similarity index 77%
rename from spec/fixtures/files/sales_logs_csv_export_codes.csv
rename to spec/fixtures/files/sales_logs_csv_export_codes_24.csv
index ba344fed7..cdce0a509 100644
--- a/spec/fixtures/files/sales_logs_csv_export_codes.csv
+++ b/spec/fixtures/files/sales_logs_csv_export_codes_24.csv
@@ -1,2 +1,2 @@
-id,status,duplicate_set_id,created_at,updated_at,old_form_id,collection_start_year,creation_method,is_dpo,owning_organisation_name,managing_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,noint,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,beds,proptype,builtype,pcodenk,wchair,privacynotice,age1,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,nationality_all_buyer2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant
-,completed,,2023-12-08T00:00:00+00:00,2023-12-08T00:00:00+00:00,,2023,1,false,DLUHC,DLUHC,billyboy@eyeklaud.com,8,12,2023,,2,8,,,,1,1,2,,,Address line 1,,Town or city,,SW1A,1AA,1,E09000003,Barnet,2,1,1,0,1,1,30,X,17,17,18,,1,1,P,35,X,17,,13,,1,1,3,C,14,X,9,X,-9,X,3,R,-9,R,10,,,,,1,1,,,0,,,1,1,1,1,,3,,1,4,5,1,1,0,10000,1,0,10000,1,4,1,,1,2,10,,,,,,,,,,,,,,,,,110000.0,,1,20000.0,5,,10,1,80000.0,,,1,100.0,,10000.0
+id,status,duplicate_set_id,created_at,updated_at,old_form_id,collection_start_year,creation_method,is_dpo,owning_organisation_name,managing_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,noint,privacynotice,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,beds,proptype,builtype,pcodenk,wchair,age1,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,nationality_all_buyer2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant
+,completed,,2023-12-08T00:00:00+00:00,2024-05-01T00:00:00+01:00,,2023,1,false,DLUHC,DLUHC,billyboy@eyeklaud.com,8,12,2023,,2,8,,,,1,1,2,1,,,Address line 1,,Town or city,,SW1A,1AA,1,E09000003,Barnet,2,1,1,0,1,30,X,17,17,18,,1,1,P,35,X,17,,13,,1,1,3,C,14,X,9,X,-9,X,3,R,-9,R,10,,,,,1,1,,,0,,,1,1,1,1,,3,,1,4,5,1,1,0,10000,1,0,10000,1,4,1,,1,2,10,,,,,,,,,,,,,,,,,110000.0,,1,20000.0,5,,10,1,80000.0,,,1,100.0,,10000.0
diff --git a/spec/fixtures/files/sales_logs_csv_export_labels_23.csv b/spec/fixtures/files/sales_logs_csv_export_labels_23.csv
new file mode 100644
index 000000000..e0e986df2
--- /dev/null
+++ b/spec/fixtures/files/sales_logs_csv_export_labels_23.csv
@@ -0,0 +1,2 @@
+id,status,duplicate_set_id,created_at,updated_at,old_form_id,collection_start_year,creation_method,is_dpo,owning_organisation_name,managing_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,beds,proptype,builtype,pcodenk,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,wchair,noint,privacynotice,age1,sex1,ethnic_group,ethnic,nationality_all,national,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationality_all_buyer2,nationalbuy2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant
+,completed,,2023-12-08T00:00:00+00:00,2024-01-01T00:00:00+00:00,,2023,single log,false,DLUHC,DLUHC,billyboy@eyeklaud.com,8,12,2023,,Yes - a discounted ownership scheme,Right to Acquire (RTA),,,,Yes,Yes,2,Flat or maisonette,Purpose built,0,,,Address line 1,,Town or city,,SW1A,1AA,1,E09000003,Barnet,Yes,Yes,1,30,Non-binary,Buyer 1 prefers not to say,17,,United Kingdom,Full-time - 30 hours or more,Yes,Partner,35,Non-binary,Buyer 2 prefers not to say,,,Buyer prefers not to say,Full-time - 30 hours or more,Yes,3,Child,14,Non-binary,Child under 16,Other,Not known,Non-binary,"In government training into work, such as New Deal",Prefers not to say,Not known,Prefers not to say,Prefers not to say,,,,,Local authority tenant,No,,,No,,,1,1,1,1,,Don't know,,Yes,Yes,No,Yes,Yes,Yes,10000,Yes,Yes,10000,Yes,"Don’t know ",No,,Yes,No,10,,,,,,,,,,,,,,,,,110000.0,,Yes,20000.0,Cambridge Building Society,,10,Yes,80000.0,,,Yes,100.0,,10000.0
diff --git a/spec/fixtures/files/sales_logs_csv_export_labels.csv b/spec/fixtures/files/sales_logs_csv_export_labels_24.csv
similarity index 82%
rename from spec/fixtures/files/sales_logs_csv_export_labels.csv
rename to spec/fixtures/files/sales_logs_csv_export_labels_24.csv
index 7ab05f910..2bd4670ec 100644
--- a/spec/fixtures/files/sales_logs_csv_export_labels.csv
+++ b/spec/fixtures/files/sales_logs_csv_export_labels_24.csv
@@ -1,2 +1,2 @@
-id,status,duplicate_set_id,created_at,updated_at,old_form_id,collection_start_year,creation_method,is_dpo,owning_organisation_name,managing_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,noint,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,beds,proptype,builtype,pcodenk,wchair,privacynotice,age1,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,nationality_all_buyer2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant
-,completed,,2023-12-08T00:00:00+00:00,2023-12-08T00:00:00+00:00,,2023,single log,false,DLUHC,DLUHC,billyboy@eyeklaud.com,8,12,2023,,Yes - a discounted ownership scheme,Right to Acquire (RTA),,,,Yes,Yes,Yes,,,Address line 1,,Town or city,,SW1A,1AA,1,E09000003,Barnet,2,Flat or maisonette,Purpose built,0,Yes,1,30,Non-binary,Buyer 1 prefers not to say,17,United Kingdom,,Full-time - 30 hours or more,Yes,Partner,35,Non-binary,Buyer 2 prefers not to say,,Buyer prefers not to say,,Full-time - 30 hours or more,Yes,3,Child,14,Non-binary,Child under 16,Other,Not known,Non-binary,"In government training into work, such as New Deal",Prefers not to say,Not known,Prefers not to say,Prefers not to say,,,,,Local authority tenant,No,,,No,,,1,1,1,1,,Don't know,,Yes,Yes,No,Yes,Yes,Yes,10000,Yes,Yes,10000,Yes,"Don’t know ",No,,Yes,No,10,,,,,,,,,,,,,,,,,110000.0,,Yes,20000.0,Cambridge Building Society,,10,Yes,80000.0,,,Yes,100.0,,10000.0
+id,status,duplicate_set_id,created_at,updated_at,old_form_id,collection_start_year,creation_method,is_dpo,owning_organisation_name,managing_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,noint,privacynotice,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,beds,proptype,builtype,pcodenk,wchair,age1,sex1,ethnic_group,ethnic,national,nationality_all,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,nationality_all_buyer2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant
+,completed,,2023-12-08T00:00:00+00:00,2024-05-01T00:00:00+01:00,,2023,single log,false,DLUHC,DLUHC,billyboy@eyeklaud.com,8,12,2023,,Yes - a discounted ownership scheme,Right to Acquire (RTA),,,,Yes,Yes,Yes,1,,,Address line 1,,Town or city,,SW1A,1AA,1,E09000003,Barnet,2,Flat or maisonette,Purpose built,0,Yes,30,Non-binary,Buyer 1 prefers not to say,17,United Kingdom,,Full-time - 30 hours or more,Yes,Partner,35,Non-binary,Buyer 2 prefers not to say,,Buyer prefers not to say,,Full-time - 30 hours or more,Yes,3,Child,14,Non-binary,Child under 16,Other,Not known,Non-binary,"In government training into work, such as New Deal",Prefers not to say,Not known,Prefers not to say,Prefers not to say,,,,,Local authority tenant,No,,,No,,,1,1,1,1,,Don't know,,Yes,Yes,No,Yes,Yes,Yes,10000,Yes,Yes,10000,Yes,"Don’t know ",No,,Yes,No,10,,,,,,,,,,,,,,,,,110000.0,,Yes,20000.0,Cambridge Building Society,,10,Yes,80000.0,,,Yes,100.0,,10000.0
diff --git a/spec/lib/tasks/clear_invalidated_earnings_spec.rb b/spec/lib/tasks/clear_invalidated_earnings_spec.rb
new file mode 100644
index 000000000..b7cd617ff
--- /dev/null
+++ b/spec/lib/tasks/clear_invalidated_earnings_spec.rb
@@ -0,0 +1,111 @@
+require "rails_helper"
+require "rake"
+
+RSpec.describe "clear_invalidated_earnings" do
+ describe ":clear_invalidated_earnings", type: :task do
+ subject(:task) { Rake::Task["clear_invalidated_earnings"] }
+
+ before do
+ Rake.application.rake_require("tasks/clear_invalidated_earnings")
+ Rake::Task.define_task(:environment)
+ task.reenable
+ FormHandler.instance.use_real_forms!
+ end
+
+ context "when the rake task is run" do
+ context "and there are 2023 logs with invalid earnings" do
+ let(:user) { create(:user) }
+ let!(:lettings_log) { create(:lettings_log, :completed, created_by: user, voiddate: nil, mrcdate: nil, tenancycode: "123", propcode: "321") }
+
+ before do
+ lettings_log.startdate = Time.zone.local(2023, 4, 4)
+ lettings_log.incfreq = 1
+ lettings_log.earnings = 20
+ lettings_log.hhmemb = 1
+ lettings_log.ecstat1 = 1
+ lettings_log.save!(validate: false)
+ end
+
+ it "clears earnings" do
+ initial_updated_at = lettings_log.updated_at
+ expect(lettings_log.incfreq).to eq(1)
+ expect(lettings_log.earnings).to eq(20)
+ expect(lettings_log.hhmemb).to eq(1)
+ expect(lettings_log.ecstat1).to eq(1)
+ expect(Rails.logger).to receive(:info).with("Clearing earnings for lettings log #{lettings_log.id}, owning_organisation_id: #{lettings_log.owning_organisation_id}, managing_organisation_id: #{lettings_log.managing_organisation_id}, startdate: 2023-04-04, tenancy reference: 123, property reference: 321, created_by: #{user.email}(#{user.id})")
+
+ task.invoke
+ lettings_log.reload
+
+ expect(lettings_log.incfreq).to eq(nil)
+ expect(lettings_log.earnings).to eq(nil)
+ expect(lettings_log.hhmemb).to eq(1)
+ expect(lettings_log.ecstat1).to eq(1)
+ expect(lettings_log.updated_at).not_to eq(initial_updated_at)
+ end
+ end
+
+ context "and there are valid 2023 logs" do
+ let(:user) { create(:user) }
+ let!(:lettings_log) { create(:lettings_log, :completed, created_by: user, voiddate: nil, mrcdate: nil) }
+
+ before do
+ lettings_log.startdate = Time.zone.local(2023, 4, 4)
+ lettings_log.incfreq = 1
+ lettings_log.earnings = 95
+ lettings_log.hhmemb = 1
+ lettings_log.ecstat1 = 1
+ lettings_log.save!
+ end
+
+ it "does not update the logs" do
+ initial_updated_at = lettings_log.updated_at
+ expect(lettings_log.incfreq).to eq(1)
+ expect(lettings_log.earnings).to eq(95)
+ expect(lettings_log.hhmemb).to eq(1)
+ expect(lettings_log.ecstat1).to eq(1)
+
+ task.invoke
+ lettings_log.reload
+
+ expect(lettings_log.incfreq).to eq(1)
+ expect(lettings_log.earnings).to eq(95)
+ expect(lettings_log.hhmemb).to eq(1)
+ expect(lettings_log.ecstat1).to eq(1)
+ expect(lettings_log.updated_at).to eq(initial_updated_at)
+ end
+ end
+
+ context "and there are 2022 logs" do
+ let(:user) { create(:user) }
+ let!(:lettings_log) { create(:lettings_log, :completed, created_by: user, voiddate: nil, mrcdate: nil) }
+
+ before do
+ lettings_log.startdate = Time.zone.local(2022, 4, 4)
+ lettings_log.incfreq = 1
+ lettings_log.earnings = 20
+ lettings_log.hhmemb = 1
+ lettings_log.ecstat1 = 1
+ lettings_log.save!(validate: false)
+ end
+
+ it "does not update the logs" do
+ initial_updated_at = lettings_log.updated_at
+ expect(lettings_log.incfreq).to eq(1)
+ expect(lettings_log.earnings).to eq(20)
+ expect(lettings_log.hhmemb).to eq(1)
+ expect(lettings_log.ecstat1).to eq(1)
+
+ task.invoke
+ lettings_log.reload
+
+ expect(lettings_log.incfreq).to eq(1)
+ expect(lettings_log.earnings).to eq(20)
+ expect(lettings_log.hhmemb).to eq(1)
+ expect(lettings_log.ecstat1).to eq(1)
+ expect(lettings_log.updated_at).to eq(initial_updated_at)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/form/lettings/questions/declaration_spec.rb b/spec/models/form/lettings/questions/declaration_spec.rb
new file mode 100644
index 000000000..4a22ea453
--- /dev/null
+++ b/spec/models/form/lettings/questions/declaration_spec.rb
@@ -0,0 +1,81 @@
+require "rails_helper"
+
+RSpec.describe Form::Lettings::Questions::Declaration, type: :model do
+ subject(:question) { described_class.new(question_id, question_definition, page) }
+
+ let(:question_id) { nil }
+ let(:question_definition) { nil }
+ let(:page) { instance_double(Form::Page) }
+ let(:subsection) { instance_double(Form::Subsection) }
+ let(:form) { instance_double(Form) }
+
+ before do
+ allow(form).to receive(:start_year_after_2024?)
+ allow(page).to receive(:subsection).and_return(subsection)
+ allow(subsection).to receive(:form).and_return(form)
+ end
+
+ it "has correct page" do
+ expect(question.page).to eq(page)
+ end
+
+ it "has the correct id" do
+ expect(question.id).to eq("declaration")
+ end
+
+ it "has the correct header" do
+ expect(question.header).to eq("Declaration")
+ end
+
+ it "has the correct check_answer_label" do
+ expect(question.check_answer_label).to eq("Tenant has seen the privacy notice")
+ end
+
+ it "has the correct type" do
+ expect(question.type).to eq("checkbox")
+ end
+
+ it "is not marked as derived" do
+ expect(question.derived?).to be false
+ end
+
+ it "has the correct hint" do
+ expect(question.hint_text).to be_nil
+ end
+
+ context "when the form year is before 2024" do
+ before do
+ allow(form).to receive(:start_year_after_2024?).and_return(false)
+ end
+
+ it "has the correct answer_options" do
+ expect(question.answer_options).to eq({
+ "declaration" => { "value" => "The tenant has seen the DLUHC privacy notice" },
+ })
+ end
+
+ it "uses the expected top guidance partial" do
+ expect(question.top_guidance_partial).to eq("privacy_notice_tenant")
+ end
+ end
+
+ context "when the form year is >= 2024" do
+ before do
+ allow(form).to receive(:start_year_after_2024?).and_return(true)
+ end
+
+ it "has the correct answer_options" do
+ expect(question.answer_options).to eq({
+ "declaration" => { "value" => "The tenant has seen or been given access to the DLUHC privacy notice" },
+ })
+ end
+
+ it "uses the expected top guidance partial" do
+ expect(question.top_guidance_partial).to eq("privacy_notice_tenant_2024")
+ end
+ end
+
+ it "returns correct unanswered_error_message" do
+ expect(question.unanswered_error_message).to eq("You must show the DLUHC privacy notice to the tenant before you can submit this log.")
+ end
+end
diff --git a/spec/models/form/lettings/questions/letting_allocation_spec.rb b/spec/models/form/lettings/questions/letting_allocation_spec.rb
new file mode 100644
index 000000000..ab268d840
--- /dev/null
+++ b/spec/models/form/lettings/questions/letting_allocation_spec.rb
@@ -0,0 +1,74 @@
+require "rails_helper"
+
+RSpec.describe Form::Lettings::Questions::LettingAllocation, type: :model do
+ subject(:question) { described_class.new(question_id, question_definition, page) }
+
+ let(:question_id) { nil }
+ let(:question_definition) { nil }
+ let(:page) { instance_double(Form::Page) }
+ let(:subsection) { instance_double(Form::Subsection) }
+ let(:form) { instance_double(Form) }
+
+ before do
+ allow(form).to receive(:start_year_after_2024?).and_return(false)
+ allow(page).to receive(:subsection).and_return(subsection)
+ allow(subsection).to receive(:form).and_return(form)
+ end
+
+ it "has correct page" do
+ expect(question.page).to eq(page)
+ end
+
+ it "has the correct id" do
+ expect(question.id).to eq("letting_allocation")
+ end
+
+ it "has the correct header" do
+ expect(question.header).to eq("How was this letting allocated?")
+ end
+
+ it "has the correct check_answer_label" do
+ expect(question.check_answer_label).to eq("Allocation system")
+ end
+
+ it "has the correct type" do
+ expect(question.type).to eq("checkbox")
+ end
+
+ it "is not marked as derived" do
+ expect(question.derived?).to be false
+ end
+
+ context "with 2023/24 form" do
+ it "has the correct answer_options" do
+ expect(question.answer_options).to eq({
+ "cbl" => { "value" => "Choice-based lettings (CBL)" },
+ "cap" => { "value" => "Common Allocation Policy (CAP)" },
+ "chr" => { "value" => "Common housing register (CHR)" },
+ "divider" => { "value" => true },
+ "letting_allocation_unknown" => { "value" => "None of these allocation systems" },
+ })
+ end
+ end
+
+ context "with 2024/25 form" do
+ before do
+ allow(form).to receive(:start_year_after_2024?).and_return(true)
+ end
+
+ it "has the correct answer_options" do
+ expect(question.answer_options).to eq({
+ "cbl" => { "value" => "Choice-based lettings (CBL)", "hint" => "Where available vacant properties are advertised and applicants are able to bid for specific properties." },
+ "cap" => { "value" => "Common Allocation Policy (CAP)", "hint" => "Where a common system agreed between a group of housing providers is used to determine applicant’s priority for housing." },
+ "chr" => { "value" => "Common housing register (CHR)", "hint" => "Where a single waiting list is used by a group of housing providers to receive and process housing applications. Providers may use different approaches to determine priority." },
+ "accessible_register" => { "value" => "Accessible housing register", "hint" => "Where the ‘access category’ or another descriptor of whether an available vacant property meets a range of access needs is displayed to applicants during the allocations process." },
+ "divider" => { "value" => true },
+ "letting_allocation_unknown" => { "value" => "None of these allocation systems" },
+ })
+ end
+ end
+
+ it "has the correct check_answers_card_number" do
+ expect(question.check_answers_card_number).to eq(0)
+ end
+end
diff --git a/spec/models/form/lettings/questions/nationality_all_group_spec.rb b/spec/models/form/lettings/questions/nationality_all_group_spec.rb
index ff8a47b59..d92551546 100644
--- a/spec/models/form/lettings/questions/nationality_all_group_spec.rb
+++ b/spec/models/form/lettings/questions/nationality_all_group_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe Form::Lettings::Questions::NationalityAllGroup, type: :model do
end
it "has the correct hint_text" do
- expect(question.hint_text).to eq("The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest.")
+ expect(question.hint_text).to eq("The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest. If the lead tenant is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, the tenant should decide which country to enter.")
end
it "has the correct answer_options" do
diff --git a/spec/models/form/lettings/subsections/household_characteristics_spec.rb b/spec/models/form/lettings/subsections/household_characteristics_spec.rb
index 4586d5592..34738ed7a 100644
--- a/spec/models/form/lettings/subsections/household_characteristics_spec.rb
+++ b/spec/models/form/lettings/subsections/household_characteristics_spec.rb
@@ -6,144 +6,295 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
let(:subsection_id) { nil }
let(:subsection_definition) { nil }
let(:section) { instance_double(Form::Lettings::Sections::Household) }
+ let(:form) { instance_double(Form) }
+
+ before do
+ allow(section).to receive(:form).and_return(form)
+ end
it "has correct section" do
expect(household_characteristics.section).to eq(section)
end
- it "has correct pages" do
- expect(household_characteristics.pages.map(&:id)).to eq(
- %w[
- declaration
- household_members
- no_females_pregnant_household_lead_hhmemb_value_check
- females_in_soft_age_range_in_pregnant_household_lead_hhmemb_value_check
- lead_tenant_age
- no_females_pregnant_household_lead_age_value_check
- females_in_soft_age_range_in_pregnant_household_lead_age_value_check
- age_lead_tenant_under_retirement_value_check
- age_lead_tenant_over_retirement_value_check
- lead_tenant_gender_identity
- no_females_pregnant_household_lead_value_check
- females_in_soft_age_range_in_pregnant_household_lead_value_check
- gender_lead_tenant_over_retirement_value_check
- lead_tenant_ethnic_group
- lead_tenant_ethnic_background_arab
- lead_tenant_ethnic_background_asian
- lead_tenant_ethnic_background_black
- lead_tenant_ethnic_background_mixed
- lead_tenant_ethnic_background_white
- lead_tenant_nationality
- lead_tenant_working_situation
- working_situation_lead_tenant_under_retirement_value_check
- working_situation_lead_tenant_over_retirement_value_check
- person_2_known
- person_2_relationship_to_lead
- person_2_age_child
- person_2_age_non_child
- no_females_pregnant_household_person_2_age_value_check
- females_in_soft_age_range_in_pregnant_household_person_2_age_value_check
- age_2_under_retirement_value_check
- age_2_over_retirement_value_check
- person_2_gender_identity
- no_females_pregnant_household_person_2_value_check
- females_in_soft_age_range_in_pregnant_household_person_2_value_check
- gender_2_over_retirement_value_check
- person_2_working_situation
- working_situation_2_under_retirement_value_check
- working_situation_2_over_retirement_value_check
- person_3_known
- person_3_relationship_to_lead
- person_3_age_child
- person_3_age_non_child
- no_females_pregnant_household_person_3_age_value_check
- females_in_soft_age_range_in_pregnant_household_person_3_age_value_check
- age_3_under_retirement_value_check
- age_3_over_retirement_value_check
- person_3_gender_identity
- no_females_pregnant_household_person_3_value_check
- females_in_soft_age_range_in_pregnant_household_person_3_value_check
- gender_3_over_retirement_value_check
- person_3_working_situation
- working_situation_3_under_retirement_value_check
- working_situation_3_over_retirement_value_check
- person_4_known
- person_4_relationship_to_lead
- person_4_age_child
- person_4_age_non_child
- no_females_pregnant_household_person_4_age_value_check
- females_in_soft_age_range_in_pregnant_household_person_4_age_value_check
- age_4_under_retirement_value_check
- age_4_over_retirement_value_check
- person_4_gender_identity
- no_females_pregnant_household_person_4_value_check
- females_in_soft_age_range_in_pregnant_household_person_4_value_check
- gender_4_over_retirement_value_check
- person_4_working_situation
- working_situation_4_under_retirement_value_check
- working_situation_4_over_retirement_value_check
- person_5_known
- person_5_relationship_to_lead
- person_5_age_child
- person_5_age_non_child
- no_females_pregnant_household_person_5_age_value_check
- females_in_soft_age_range_in_pregnant_household_person_5_age_value_check
- age_5_under_retirement_value_check
- age_5_over_retirement_value_check
- person_5_gender_identity
- no_females_pregnant_household_person_5_value_check
- females_in_soft_age_range_in_pregnant_household_person_5_value_check
- gender_5_over_retirement_value_check
- person_5_working_situation
- working_situation_5_under_retirement_value_check
- working_situation_5_over_retirement_value_check
- person_6_known
- person_6_relationship_to_lead
- person_6_age_child
- person_6_age_non_child
- no_females_pregnant_household_person_6_age_value_check
- females_in_soft_age_range_in_pregnant_household_person_6_age_value_check
- age_6_under_retirement_value_check
- age_6_over_retirement_value_check
- person_6_gender_identity
- no_females_pregnant_household_person_6_value_check
- females_in_soft_age_range_in_pregnant_household_person_6_value_check
- gender_6_over_retirement_value_check
- person_6_working_situation
- working_situation_6_under_retirement_value_check
- working_situation_6_over_retirement_value_check
- person_7_known
- person_7_relationship_to_lead
- person_7_age_child
- person_7_age_non_child
- no_females_pregnant_household_person_7_age_value_check
- females_in_soft_age_range_in_pregnant_household_person_7_age_value_check
- age_7_under_retirement_value_check
- age_7_over_retirement_value_check
- person_7_gender_identity
- no_females_pregnant_household_person_7_value_check
- females_in_soft_age_range_in_pregnant_household_person_7_value_check
- gender_7_over_retirement_value_check
- person_7_working_situation
- working_situation_7_under_retirement_value_check
- working_situation_7_over_retirement_value_check
- person_8_known
- person_8_relationship_to_lead
- person_8_age_child
- person_8_age_non_child
- no_females_pregnant_household_person_8_age_value_check
- females_in_soft_age_range_in_pregnant_household_person_8_age_value_check
- age_8_under_retirement_value_check
- age_8_over_retirement_value_check
- person_8_gender_identity
- no_females_pregnant_household_person_8_value_check
- females_in_soft_age_range_in_pregnant_household_person_8_value_check
- gender_8_over_retirement_value_check
- person_8_working_situation
- working_situation_8_under_retirement_value_check
- working_situation_8_over_retirement_value_check
- ],
- )
+ context "with start year before 2024" do
+ before do
+ allow(form).to receive(:start_year_after_2024?).and_return(false)
+ end
+
+ it "has correct pages" do
+ expect(household_characteristics.pages.map(&:id)).to eq(
+ %w[
+ declaration
+ household_members
+ no_females_pregnant_household_lead_hhmemb_value_check
+ females_in_soft_age_range_in_pregnant_household_lead_hhmemb_value_check
+ lead_tenant_age
+ no_females_pregnant_household_lead_age_value_check
+ females_in_soft_age_range_in_pregnant_household_lead_age_value_check
+ age_lead_tenant_under_retirement_value_check
+ age_lead_tenant_over_retirement_value_check
+ lead_tenant_gender_identity
+ no_females_pregnant_household_lead_value_check
+ females_in_soft_age_range_in_pregnant_household_lead_value_check
+ gender_lead_tenant_over_retirement_value_check
+ lead_tenant_ethnic_group
+ lead_tenant_ethnic_background_arab
+ lead_tenant_ethnic_background_asian
+ lead_tenant_ethnic_background_black
+ lead_tenant_ethnic_background_mixed
+ lead_tenant_ethnic_background_white
+ lead_tenant_nationality
+ lead_tenant_working_situation
+ working_situation_lead_tenant_under_retirement_value_check
+ working_situation_lead_tenant_over_retirement_value_check
+ person_2_known
+ person_2_relationship_to_lead
+ person_2_age_child
+ person_2_age_non_child
+ no_females_pregnant_household_person_2_age_value_check
+ females_in_soft_age_range_in_pregnant_household_person_2_age_value_check
+ age_2_under_retirement_value_check
+ age_2_over_retirement_value_check
+ person_2_gender_identity
+ no_females_pregnant_household_person_2_value_check
+ females_in_soft_age_range_in_pregnant_household_person_2_value_check
+ gender_2_over_retirement_value_check
+ person_2_working_situation
+ working_situation_2_under_retirement_value_check
+ working_situation_2_over_retirement_value_check
+ person_3_known
+ person_3_relationship_to_lead
+ person_3_age_child
+ person_3_age_non_child
+ no_females_pregnant_household_person_3_age_value_check
+ females_in_soft_age_range_in_pregnant_household_person_3_age_value_check
+ age_3_under_retirement_value_check
+ age_3_over_retirement_value_check
+ person_3_gender_identity
+ no_females_pregnant_household_person_3_value_check
+ females_in_soft_age_range_in_pregnant_household_person_3_value_check
+ gender_3_over_retirement_value_check
+ person_3_working_situation
+ working_situation_3_under_retirement_value_check
+ working_situation_3_over_retirement_value_check
+ person_4_known
+ person_4_relationship_to_lead
+ person_4_age_child
+ person_4_age_non_child
+ no_females_pregnant_household_person_4_age_value_check
+ females_in_soft_age_range_in_pregnant_household_person_4_age_value_check
+ age_4_under_retirement_value_check
+ age_4_over_retirement_value_check
+ person_4_gender_identity
+ no_females_pregnant_household_person_4_value_check
+ females_in_soft_age_range_in_pregnant_household_person_4_value_check
+ gender_4_over_retirement_value_check
+ person_4_working_situation
+ working_situation_4_under_retirement_value_check
+ working_situation_4_over_retirement_value_check
+ person_5_known
+ person_5_relationship_to_lead
+ person_5_age_child
+ person_5_age_non_child
+ no_females_pregnant_household_person_5_age_value_check
+ females_in_soft_age_range_in_pregnant_household_person_5_age_value_check
+ age_5_under_retirement_value_check
+ age_5_over_retirement_value_check
+ person_5_gender_identity
+ no_females_pregnant_household_person_5_value_check
+ females_in_soft_age_range_in_pregnant_household_person_5_value_check
+ gender_5_over_retirement_value_check
+ person_5_working_situation
+ working_situation_5_under_retirement_value_check
+ working_situation_5_over_retirement_value_check
+ person_6_known
+ person_6_relationship_to_lead
+ person_6_age_child
+ person_6_age_non_child
+ no_females_pregnant_household_person_6_age_value_check
+ females_in_soft_age_range_in_pregnant_household_person_6_age_value_check
+ age_6_under_retirement_value_check
+ age_6_over_retirement_value_check
+ person_6_gender_identity
+ no_females_pregnant_household_person_6_value_check
+ females_in_soft_age_range_in_pregnant_household_person_6_value_check
+ gender_6_over_retirement_value_check
+ person_6_working_situation
+ working_situation_6_under_retirement_value_check
+ working_situation_6_over_retirement_value_check
+ person_7_known
+ person_7_relationship_to_lead
+ person_7_age_child
+ person_7_age_non_child
+ no_females_pregnant_household_person_7_age_value_check
+ females_in_soft_age_range_in_pregnant_household_person_7_age_value_check
+ age_7_under_retirement_value_check
+ age_7_over_retirement_value_check
+ person_7_gender_identity
+ no_females_pregnant_household_person_7_value_check
+ females_in_soft_age_range_in_pregnant_household_person_7_value_check
+ gender_7_over_retirement_value_check
+ person_7_working_situation
+ working_situation_7_under_retirement_value_check
+ working_situation_7_over_retirement_value_check
+ person_8_known
+ person_8_relationship_to_lead
+ person_8_age_child
+ person_8_age_non_child
+ no_females_pregnant_household_person_8_age_value_check
+ females_in_soft_age_range_in_pregnant_household_person_8_age_value_check
+ age_8_under_retirement_value_check
+ age_8_over_retirement_value_check
+ person_8_gender_identity
+ no_females_pregnant_household_person_8_value_check
+ females_in_soft_age_range_in_pregnant_household_person_8_value_check
+ gender_8_over_retirement_value_check
+ person_8_working_situation
+ working_situation_8_under_retirement_value_check
+ working_situation_8_over_retirement_value_check
+ ],
+ )
+ end
+ end
+
+ context "with start year >= 2024" do
+ before do
+ allow(form).to receive(:start_year_after_2024?).and_return(true)
+ end
+
+ it "has correct pages" do
+ expect(household_characteristics.pages.map(&:id)).to eq(
+ %w[
+ household_members
+ no_females_pregnant_household_lead_hhmemb_value_check
+ females_in_soft_age_range_in_pregnant_household_lead_hhmemb_value_check
+ lead_tenant_age
+ no_females_pregnant_household_lead_age_value_check
+ females_in_soft_age_range_in_pregnant_household_lead_age_value_check
+ age_lead_tenant_under_retirement_value_check
+ age_lead_tenant_over_retirement_value_check
+ lead_tenant_gender_identity
+ no_females_pregnant_household_lead_value_check
+ females_in_soft_age_range_in_pregnant_household_lead_value_check
+ gender_lead_tenant_over_retirement_value_check
+ lead_tenant_ethnic_group
+ lead_tenant_ethnic_background_arab
+ lead_tenant_ethnic_background_asian
+ lead_tenant_ethnic_background_black
+ lead_tenant_ethnic_background_mixed
+ lead_tenant_ethnic_background_white
+ lead_tenant_nationality
+ lead_tenant_working_situation
+ working_situation_lead_tenant_under_retirement_value_check
+ working_situation_lead_tenant_over_retirement_value_check
+ person_2_known
+ person_2_relationship_to_lead
+ person_2_age_child
+ person_2_age_non_child
+ no_females_pregnant_household_person_2_age_value_check
+ females_in_soft_age_range_in_pregnant_household_person_2_age_value_check
+ age_2_under_retirement_value_check
+ age_2_over_retirement_value_check
+ person_2_gender_identity
+ no_females_pregnant_household_person_2_value_check
+ females_in_soft_age_range_in_pregnant_household_person_2_value_check
+ gender_2_over_retirement_value_check
+ person_2_working_situation
+ working_situation_2_under_retirement_value_check
+ working_situation_2_over_retirement_value_check
+ person_3_known
+ person_3_relationship_to_lead
+ person_3_age_child
+ person_3_age_non_child
+ no_females_pregnant_household_person_3_age_value_check
+ females_in_soft_age_range_in_pregnant_household_person_3_age_value_check
+ age_3_under_retirement_value_check
+ age_3_over_retirement_value_check
+ person_3_gender_identity
+ no_females_pregnant_household_person_3_value_check
+ females_in_soft_age_range_in_pregnant_household_person_3_value_check
+ gender_3_over_retirement_value_check
+ person_3_working_situation
+ working_situation_3_under_retirement_value_check
+ working_situation_3_over_retirement_value_check
+ person_4_known
+ person_4_relationship_to_lead
+ person_4_age_child
+ person_4_age_non_child
+ no_females_pregnant_household_person_4_age_value_check
+ females_in_soft_age_range_in_pregnant_household_person_4_age_value_check
+ age_4_under_retirement_value_check
+ age_4_over_retirement_value_check
+ person_4_gender_identity
+ no_females_pregnant_household_person_4_value_check
+ females_in_soft_age_range_in_pregnant_household_person_4_value_check
+ gender_4_over_retirement_value_check
+ person_4_working_situation
+ working_situation_4_under_retirement_value_check
+ working_situation_4_over_retirement_value_check
+ person_5_known
+ person_5_relationship_to_lead
+ person_5_age_child
+ person_5_age_non_child
+ no_females_pregnant_household_person_5_age_value_check
+ females_in_soft_age_range_in_pregnant_household_person_5_age_value_check
+ age_5_under_retirement_value_check
+ age_5_over_retirement_value_check
+ person_5_gender_identity
+ no_females_pregnant_household_person_5_value_check
+ females_in_soft_age_range_in_pregnant_household_person_5_value_check
+ gender_5_over_retirement_value_check
+ person_5_working_situation
+ working_situation_5_under_retirement_value_check
+ working_situation_5_over_retirement_value_check
+ person_6_known
+ person_6_relationship_to_lead
+ person_6_age_child
+ person_6_age_non_child
+ no_females_pregnant_household_person_6_age_value_check
+ females_in_soft_age_range_in_pregnant_household_person_6_age_value_check
+ age_6_under_retirement_value_check
+ age_6_over_retirement_value_check
+ person_6_gender_identity
+ no_females_pregnant_household_person_6_value_check
+ females_in_soft_age_range_in_pregnant_household_person_6_value_check
+ gender_6_over_retirement_value_check
+ person_6_working_situation
+ working_situation_6_under_retirement_value_check
+ working_situation_6_over_retirement_value_check
+ person_7_known
+ person_7_relationship_to_lead
+ person_7_age_child
+ person_7_age_non_child
+ no_females_pregnant_household_person_7_age_value_check
+ females_in_soft_age_range_in_pregnant_household_person_7_age_value_check
+ age_7_under_retirement_value_check
+ age_7_over_retirement_value_check
+ person_7_gender_identity
+ no_females_pregnant_household_person_7_value_check
+ females_in_soft_age_range_in_pregnant_household_person_7_value_check
+ gender_7_over_retirement_value_check
+ person_7_working_situation
+ working_situation_7_under_retirement_value_check
+ working_situation_7_over_retirement_value_check
+ person_8_known
+ person_8_relationship_to_lead
+ person_8_age_child
+ person_8_age_non_child
+ no_females_pregnant_household_person_8_age_value_check
+ females_in_soft_age_range_in_pregnant_household_person_8_age_value_check
+ age_8_under_retirement_value_check
+ age_8_over_retirement_value_check
+ person_8_gender_identity
+ no_females_pregnant_household_person_8_value_check
+ females_in_soft_age_range_in_pregnant_household_person_8_value_check
+ gender_8_over_retirement_value_check
+ person_8_working_situation
+ working_situation_8_under_retirement_value_check
+ working_situation_8_over_retirement_value_check
+ ],
+ )
+ end
end
it "has the correct id" do
diff --git a/spec/models/form/lettings/subsections/setup_spec.rb b/spec/models/form/lettings/subsections/setup_spec.rb
index d7da750a8..b31cb61c5 100644
--- a/spec/models/form/lettings/subsections/setup_spec.rb
+++ b/spec/models/form/lettings/subsections/setup_spec.rb
@@ -6,38 +6,45 @@ RSpec.describe Form::Lettings::Subsections::Setup, type: :model do
let(:subsection_id) { nil }
let(:subsection_definition) { nil }
let(:section) { instance_double(Form::Lettings::Sections::Setup) }
+ let(:form) { instance_double(Form) }
+
+ before do
+ allow(section).to receive(:form).and_return(form)
+ end
it "has correct section" do
expect(setup.section).to eq(section)
end
- it "has correct pages" do
- expect(setup.pages.map(&:id)).to eq(
- %w[
- stock_owner
- managing_organisation
- created_by
- needs_type
- scheme
- location
- renewal
- tenancy_start_date
- rent_type
- tenant_code
- property_reference
- ],
- )
- end
+ context "with start year before 2024" do
+ before do
+ allow(form).to receive(:start_year_after_2024?).and_return(false)
+ end
- it "has the correct id" do
- expect(setup.id).to eq("setup")
+ it "has correct pages" do
+ expect(setup.pages.map(&:id)).to eq(
+ %w[
+ stock_owner
+ managing_organisation
+ created_by
+ needs_type
+ scheme
+ location
+ renewal
+ tenancy_start_date
+ rent_type
+ tenant_code
+ property_reference
+ ],
+ )
+ end
end
- it "has the correct label" do
- expect(setup.label).to eq("Set up this lettings log")
- end
+ context "with start year >= 2024" do
+ before do
+ allow(form).to receive(:start_year_after_2024?).and_return(true)
+ end
- context "when not production" do
it "has correct pages" do
expect(setup.pages.map(&:id)).to eq(
%w[
@@ -52,8 +59,17 @@ RSpec.describe Form::Lettings::Subsections::Setup, type: :model do
rent_type
tenant_code
property_reference
+ declaration
],
)
end
end
+
+ it "has the correct id" do
+ expect(setup.id).to eq("setup")
+ end
+
+ it "has the correct label" do
+ expect(setup.label).to eq("Set up this lettings log")
+ end
end
diff --git a/spec/models/form/sales/pages/about_deposit_with_discount_spec.rb b/spec/models/form/sales/pages/about_deposit_with_discount_spec.rb
index cc33a9641..89db397f1 100644
--- a/spec/models/form/sales/pages/about_deposit_with_discount_spec.rb
+++ b/spec/models/form/sales/pages/about_deposit_with_discount_spec.rb
@@ -1,12 +1,16 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::AboutDepositWithDiscount, type: :model do
- subject(:page) { described_class.new(page_id, page_definition, subsection) }
+ subject(:page) { described_class.new(page_id, page_definition, subsection, optional: false) }
- let(:page_id) { nil }
+ let(:page_id) { "about_deposit_with_discount" }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
+ before do
+ allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: false))
+ end
+
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
@@ -32,4 +36,36 @@ RSpec.describe Form::Sales::Pages::AboutDepositWithDiscount, type: :model do
[{ "is_type_discount?" => true }],
)
end
+
+ context "when optional" do
+ subject(:page) { described_class.new(page_id, page_definition, subsection, optional: true) }
+
+ it "has correct depends_on" do
+ expect(page.depends_on).to eq(
+ [{ "is_type_discount?" => true }],
+ )
+ end
+ end
+
+ context "when it's a 2024 form" do
+ before do
+ allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: true))
+ end
+
+ it "has correct depends_on" do
+ expect(page.depends_on).to eq(
+ [{ "is_type_discount?" => true, "stairowned_100?" => false }],
+ )
+ end
+
+ context "and optional" do
+ subject(:page) { described_class.new(page_id, page_definition, subsection, optional: true) }
+
+ it "has correct depends_on" do
+ expect(page.depends_on).to eq(
+ [{ "is_type_discount?" => true, "stairowned_100?" => true }],
+ )
+ end
+ end
+ end
end
diff --git a/spec/models/form/sales/pages/about_deposit_without_discount_spec.rb b/spec/models/form/sales/pages/about_deposit_without_discount_spec.rb
index 1a0b5ef4d..1a4e420c6 100644
--- a/spec/models/form/sales/pages/about_deposit_without_discount_spec.rb
+++ b/spec/models/form/sales/pages/about_deposit_without_discount_spec.rb
@@ -1,12 +1,16 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::AboutDepositWithoutDiscount, type: :model do
- subject(:page) { described_class.new(page_id, page_definition, subsection, ownershipsch: 1) }
+ subject(:page) { described_class.new(page_id, page_definition, subsection, ownershipsch: 1, optional: false) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
+ before do
+ allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: false))
+ end
+
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
@@ -34,4 +38,42 @@ RSpec.describe Form::Sales::Pages::AboutDepositWithoutDiscount, type: :model do
{ "ownershipsch" => 3, "mortgageused" => 1 }],
)
end
+
+ context "when optional is true" do
+ subject(:page) { described_class.new(page_id, page_definition, subsection, ownershipsch: 1, optional: true) }
+
+ it "has correct depends_on" do
+ expect(page.depends_on).to eq(
+ [{ "is_type_discount?" => false, "ownershipsch" => 1 },
+ { "ownershipsch" => 2 },
+ { "ownershipsch" => 3, "mortgageused" => 1 }],
+ )
+ end
+ end
+
+ context "when it's a 2024 form" do
+ before do
+ allow(subsection).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: true))
+ end
+
+ it "has correct depends_on" do
+ expect(page.depends_on).to eq(
+ [{ "is_type_discount?" => false, "ownershipsch" => 1, "stairowned_100?" => false },
+ { "ownershipsch" => 2 },
+ { "ownershipsch" => 3, "mortgageused" => 1 }],
+ )
+ end
+
+ context "and optional is true" do
+ subject(:page) { described_class.new(page_id, page_definition, subsection, ownershipsch: 1, optional: true) }
+
+ it "has correct depends_on" do
+ expect(page.depends_on).to eq(
+ [{ "is_type_discount?" => false, "ownershipsch" => 1, "stairowned_100?" => true },
+ { "ownershipsch" => 2 },
+ { "ownershipsch" => 3, "mortgageused" => 1 }],
+ )
+ end
+ end
+ end
end
diff --git a/spec/models/form/sales/pages/last_accommodation_la_spec.rb b/spec/models/form/sales/pages/last_accommodation_la_spec.rb
index 20daf0525..54df5a365 100644
--- a/spec/models/form/sales/pages/last_accommodation_la_spec.rb
+++ b/spec/models/form/sales/pages/last_accommodation_la_spec.rb
@@ -5,8 +5,22 @@ RSpec.describe Form::Sales::Pages::LastAccommodationLa, type: :model do
let(:page_id) { nil }
let(:page_definition) { nil }
- let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date:)) }
+ let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, depends_on_met: true)) }
let(:start_date) { Time.utc(2022, 4, 1) }
+ let(:log) { create(:sales_log, :completed, saledate: now) }
+ let(:now) { Time.zone.local(2023, 4, 4) }
+
+ before do
+ Timecop.freeze(now)
+ Singleton.__init__(FormHandler)
+ allow(subsection).to receive(:depends_on).and_return(nil)
+ allow(subsection).to receive(:enabled?).and_return(true)
+ end
+
+ after do
+ Timecop.return
+ Singleton.__init__(FormHandler)
+ end
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
@@ -33,4 +47,23 @@ RSpec.describe Form::Sales::Pages::LastAccommodationLa, type: :model do
"is_previous_la_inferred" => false,
}])
end
+
+ it "is routed to" do
+ log.ownershipsch = 2
+ expect(page).to be_routed_to(log, nil)
+ end
+
+ context "with 2024 form" do
+ let(:now) { Time.zone.local(2024, 4, 4) }
+
+ it "is routed to for 2024 non discounted sale logs" do
+ log.update!(ownershipsch: 1)
+ expect(page).to be_routed_to(log, nil)
+ end
+
+ it "is not routed to for 2024 discounted sale logs" do
+ log.update!(ownershipsch: 2)
+ expect(page).not_to be_routed_to(log, nil)
+ end
+ end
end
diff --git a/spec/models/form/sales/pages/last_accommodation_spec.rb b/spec/models/form/sales/pages/last_accommodation_spec.rb
index 753aa7dc8..a972d96c5 100644
--- a/spec/models/form/sales/pages/last_accommodation_spec.rb
+++ b/spec/models/form/sales/pages/last_accommodation_spec.rb
@@ -3,10 +3,24 @@ require "rails_helper"
RSpec.describe Form::Sales::Pages::LastAccommodation, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
+ let(:log) { create(:sales_log, :completed, saledate: now) }
+ let(:now) { Time.zone.local(2023, 4, 4) }
+
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
+ before do
+ Timecop.freeze(now)
+ Singleton.__init__(FormHandler)
+ allow(subsection).to receive(:depends_on).and_return(nil)
+ end
+
+ after do
+ Timecop.return
+ Singleton.__init__(FormHandler)
+ end
+
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
@@ -30,4 +44,23 @@ RSpec.describe Form::Sales::Pages::LastAccommodation, type: :model do
it "has correct depends_on" do
expect(page.depends_on).to be_nil
end
+
+ it "is routed to" do
+ log.ownershipsch = 2
+ expect(page).to be_routed_to(log, nil)
+ end
+
+ context "with 2024 form" do
+ let(:now) { Time.zone.local(2024, 4, 4) }
+
+ it "is routed to for 2024 non discounted sale logs" do
+ log.update!(ownershipsch: 1)
+ expect(page).to be_routed_to(log, nil)
+ end
+
+ it "is not routed to for 2024 discounted sale logs" do
+ log.update!(ownershipsch: 2)
+ expect(page).not_to be_routed_to(log, nil)
+ end
+ end
end
diff --git a/spec/models/form/sales/pages/privacy_notice_spec.rb b/spec/models/form/sales/pages/privacy_notice_spec.rb
index d8e25a3d8..9c2ce1a64 100644
--- a/spec/models/form/sales/pages/privacy_notice_spec.rb
+++ b/spec/models/form/sales/pages/privacy_notice_spec.rb
@@ -6,6 +6,12 @@ RSpec.describe Form::Sales::Pages::PrivacyNotice, type: :model do
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
+ let(:form) { instance_double(Form) }
+
+ before do
+ allow(subsection).to receive(:form).and_return(form)
+ allow(form).to receive(:start_year_after_2024?)
+ end
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
diff --git a/spec/models/form/sales/questions/deposit_amount_spec.rb b/spec/models/form/sales/questions/deposit_amount_spec.rb
index e0a77b7fc..80429b5af 100644
--- a/spec/models/form/sales/questions/deposit_amount_spec.rb
+++ b/spec/models/form/sales/questions/deposit_amount_spec.rb
@@ -1,7 +1,7 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::DepositAmount, type: :model do
- subject(:question) { described_class.new(question_id, question_definition, page, ownershipsch: 1) }
+ subject(:question) { described_class.new(question_id, question_definition, page, ownershipsch: 1, optional: false) }
let(:question_id) { nil }
let(:question_definition) { nil }
@@ -50,4 +50,12 @@ RSpec.describe Form::Sales::Questions::DepositAmount, type: :model do
it "has correct max" do
expect(question.max).to eq(999_999)
end
+
+ context "when optional iis true" do
+ subject(:question) { described_class.new(question_id, question_definition, page, ownershipsch: 1, optional: true) }
+
+ it "has a correct hint_text" do
+ expect(question.hint_text).to eq("Enter the total cash sum paid by the buyer towards the property that was not funded by the mortgage. As this is a fully staircased sale this question is optional. If you do not have the information available click save and continue")
+ end
+ end
end
diff --git a/spec/models/form/sales/questions/mortgage_lender_spec.rb b/spec/models/form/sales/questions/mortgage_lender_spec.rb
index 1fcf928a3..e50dafb2b 100644
--- a/spec/models/form/sales/questions/mortgage_lender_spec.rb
+++ b/spec/models/form/sales/questions/mortgage_lender_spec.rb
@@ -6,6 +6,14 @@ RSpec.describe Form::Sales::Questions::MortgageLender, type: :model do
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
+ let(:subsection) { instance_double(Form::Subsection) }
+ let(:form) { instance_double(Form) }
+
+ before do
+ allow(form).to receive(:start_year_after_2024?)
+ allow(page).to receive(:subsection).and_return(subsection)
+ allow(subsection).to receive(:form).and_return(form)
+ end
it "has correct page" do
expect(question.page).to eq(page)
@@ -44,96 +52,203 @@ RSpec.describe Form::Sales::Questions::MortgageLender, type: :model do
expect(question.top_guidance_partial).to be_nil
end
- it "has the correct answer_options" do
- expect(question.answer_options).to eq({
- "" => "Select an option",
- "0" => "Don’t know",
- "1" => "Atom Bank",
- "2" => "Barclays Bank PLC",
- "3" => "Bath Building Society",
- "4" => "Buckinghamshire Building Society",
- "5" => "Cambridge Building Society",
- "6" => "Coventry Building Society",
- "7" => "Cumberland Building Society",
- "8" => "Darlington Building Society",
- "9" => "Dudley Building Society",
- "10" => "Ecology Building Society",
- "11" => "Halifax",
- "12" => "Hanley Economic Building Society",
- "13" => "Hinckley and Rugby Building Society",
- "14" => "Holmesdale Building Society",
- "15" => "Ipswich Building Society",
- "16" => "Leeds Building Society",
- "17" => "Lloyds Bank",
- "18" => "Mansfield Building Society",
- "19" => "Market Harborough Building Society",
- "20" => "Melton Mowbray Building Society",
- "21" => "Nationwide Building Society",
- "22" => "Natwest",
- "23" => "Nedbank Private Wealth",
- "24" => "Newbury Building Society",
- "25" => "OneSavings Bank",
- "26" => "Parity Trust",
- "27" => "Penrith Building Society",
- "28" => "Pepper Homeloans",
- "29" => "Royal Bank of Scotland",
- "30" => "Santander",
- "31" => "Skipton Building Society",
- "32" => "Teachers Building Society",
- "33" => "The Co-operative Bank",
- "34" => "Tipton & Coseley Building Society",
- "35" => "TSB",
- "36" => "Ulster Bank",
- "37" => "Virgin Money",
- "38" => "West Bromwich Building Society",
- "39" => "Yorkshire Building Society",
- "40" => "Other",
- })
+ context "when form year is before 2024" do
+ before do
+ allow(form).to receive(:start_year_after_2024?).and_return(false)
+ end
+
+ it "has the correct answer_options" do
+ expect(question.answer_options).to eq({
+ "" => "Select an option",
+ "0" => "Don’t know",
+ "1" => "Atom Bank",
+ "2" => "Barclays Bank PLC",
+ "3" => "Bath Building Society",
+ "4" => "Buckinghamshire Building Society",
+ "5" => "Cambridge Building Society",
+ "6" => "Coventry Building Society",
+ "7" => "Cumberland Building Society",
+ "8" => "Darlington Building Society",
+ "9" => "Dudley Building Society",
+ "10" => "Ecology Building Society",
+ "11" => "Halifax",
+ "12" => "Hanley Economic Building Society",
+ "13" => "Hinckley and Rugby Building Society",
+ "14" => "Holmesdale Building Society",
+ "15" => "Ipswich Building Society",
+ "16" => "Leeds Building Society",
+ "17" => "Lloyds Bank",
+ "18" => "Mansfield Building Society",
+ "19" => "Market Harborough Building Society",
+ "20" => "Melton Mowbray Building Society",
+ "21" => "Nationwide Building Society",
+ "22" => "Natwest",
+ "23" => "Nedbank Private Wealth",
+ "24" => "Newbury Building Society",
+ "25" => "OneSavings Bank",
+ "26" => "Parity Trust",
+ "27" => "Penrith Building Society",
+ "28" => "Pepper Homeloans",
+ "29" => "Royal Bank of Scotland",
+ "30" => "Santander",
+ "31" => "Skipton Building Society",
+ "32" => "Teachers Building Society",
+ "33" => "The Co-operative Bank",
+ "34" => "Tipton & Coseley Building Society",
+ "35" => "TSB",
+ "36" => "Ulster Bank",
+ "37" => "Virgin Money",
+ "38" => "West Bromwich Building Society",
+ "39" => "Yorkshire Building Society",
+ "40" => "Other",
+ })
+ end
+
+ it "has the correct displayed_answer_options" do
+ expect(question.displayed_answer_options(nil, nil)).to eq({
+ "" => "Select an option",
+ "1" => "Atom Bank",
+ "2" => "Barclays Bank PLC",
+ "3" => "Bath Building Society",
+ "4" => "Buckinghamshire Building Society",
+ "5" => "Cambridge Building Society",
+ "6" => "Coventry Building Society",
+ "7" => "Cumberland Building Society",
+ "8" => "Darlington Building Society",
+ "9" => "Dudley Building Society",
+ "10" => "Ecology Building Society",
+ "11" => "Halifax",
+ "12" => "Hanley Economic Building Society",
+ "13" => "Hinckley and Rugby Building Society",
+ "14" => "Holmesdale Building Society",
+ "15" => "Ipswich Building Society",
+ "16" => "Leeds Building Society",
+ "17" => "Lloyds Bank",
+ "18" => "Mansfield Building Society",
+ "19" => "Market Harborough Building Society",
+ "20" => "Melton Mowbray Building Society",
+ "21" => "Nationwide Building Society",
+ "22" => "Natwest",
+ "23" => "Nedbank Private Wealth",
+ "24" => "Newbury Building Society",
+ "25" => "OneSavings Bank",
+ "26" => "Parity Trust",
+ "27" => "Penrith Building Society",
+ "28" => "Pepper Homeloans",
+ "29" => "Royal Bank of Scotland",
+ "30" => "Santander",
+ "31" => "Skipton Building Society",
+ "32" => "Teachers Building Society",
+ "33" => "The Co-operative Bank",
+ "34" => "Tipton & Coseley Building Society",
+ "35" => "TSB",
+ "36" => "Ulster Bank",
+ "37" => "Virgin Money",
+ "38" => "West Bromwich Building Society",
+ "39" => "Yorkshire Building Society",
+ "40" => "Other",
+ })
+ end
end
- it "has the correct displayed_answer_options" do
- expect(question.displayed_answer_options(nil, nil)).to eq({
- "" => "Select an option",
- "1" => "Atom Bank",
- "2" => "Barclays Bank PLC",
- "3" => "Bath Building Society",
- "4" => "Buckinghamshire Building Society",
- "5" => "Cambridge Building Society",
- "6" => "Coventry Building Society",
- "7" => "Cumberland Building Society",
- "8" => "Darlington Building Society",
- "9" => "Dudley Building Society",
- "10" => "Ecology Building Society",
- "11" => "Halifax",
- "12" => "Hanley Economic Building Society",
- "13" => "Hinckley and Rugby Building Society",
- "14" => "Holmesdale Building Society",
- "15" => "Ipswich Building Society",
- "16" => "Leeds Building Society",
- "17" => "Lloyds Bank",
- "18" => "Mansfield Building Society",
- "19" => "Market Harborough Building Society",
- "20" => "Melton Mowbray Building Society",
- "21" => "Nationwide Building Society",
- "22" => "Natwest",
- "23" => "Nedbank Private Wealth",
- "24" => "Newbury Building Society",
- "25" => "OneSavings Bank",
- "26" => "Parity Trust",
- "27" => "Penrith Building Society",
- "28" => "Pepper Homeloans",
- "29" => "Royal Bank of Scotland",
- "30" => "Santander",
- "31" => "Skipton Building Society",
- "32" => "Teachers Building Society",
- "33" => "The Co-operative Bank",
- "34" => "Tipton & Coseley Building Society",
- "35" => "TSB",
- "36" => "Ulster Bank",
- "37" => "Virgin Money",
- "38" => "West Bromwich Building Society",
- "39" => "Yorkshire Building Society",
- "40" => "Other",
- })
+ context "when form year is >= 2024" do
+ before do
+ allow(form).to receive(:start_year_after_2024?).and_return(true)
+ end
+
+ it "has the correct answer_options" do
+ expect(question.answer_options).to eq({
+ "" => "Select an option",
+ "0" => "Don’t know",
+ "1" => "Atom Bank",
+ "2" => "Barclays Bank PLC",
+ "3" => "Bath Building Society",
+ "4" => "Buckinghamshire Building Society",
+ "5" => "Cambridge Building Society",
+ "6" => "Coventry Building Society",
+ "7" => "Cumberland Building Society",
+ "8" => "Darlington Building Society",
+ "9" => "Dudley Building Society",
+ "10" => "Ecology Building Society",
+ "11" => "Halifax",
+ "12" => "Hanley Economic Building Society",
+ "13" => "Hinckley and Rugby Building Society",
+ "14" => "Holmesdale Building Society",
+ "15" => "Ipswich Building Society",
+ "16" => "Leeds Building Society",
+ "17" => "Lloyds Bank",
+ "18" => "Mansfield Building Society",
+ "19" => "Market Harborough Building Society",
+ "20" => "Melton Mowbray Building Society",
+ "21" => "Nationwide Building Society",
+ "22" => "Natwest",
+ "23" => "Nedbank Private Wealth",
+ "24" => "Newbury Building Society",
+ "25" => "OneSavings Bank",
+ "26" => "Parity Trust",
+ "27" => "Penrith Building Society",
+ "28" => "Pepper Homeloans",
+ "29" => "Royal Bank of Scotland",
+ "30" => "Santander",
+ "31" => "Skipton Building Society",
+ "32" => "Teachers Building Society",
+ "33" => "The Co-operative Bank",
+ "34" => "Tipton & Coseley Building Society",
+ "35" => "TSB",
+ "36" => "Ulster Bank",
+ "37" => "Virgin Money",
+ "38" => "West Bromwich Building Society",
+ "39" => "Yorkshire Building Society",
+ "41" => "Kent Reliance",
+ "40" => "Other",
+ })
+ end
+
+ it "has the correct displayed_answer_options" do
+ expect(question.displayed_answer_options(nil, nil)).to eq({
+ "" => "Select an option",
+ "1" => "Atom Bank",
+ "2" => "Barclays Bank PLC",
+ "3" => "Bath Building Society",
+ "4" => "Buckinghamshire Building Society",
+ "5" => "Cambridge Building Society",
+ "6" => "Coventry Building Society",
+ "7" => "Cumberland Building Society",
+ "8" => "Darlington Building Society",
+ "9" => "Dudley Building Society",
+ "10" => "Ecology Building Society",
+ "11" => "Halifax",
+ "12" => "Hanley Economic Building Society",
+ "13" => "Hinckley and Rugby Building Society",
+ "14" => "Holmesdale Building Society",
+ "15" => "Ipswich Building Society",
+ "16" => "Leeds Building Society",
+ "17" => "Lloyds Bank",
+ "18" => "Mansfield Building Society",
+ "19" => "Market Harborough Building Society",
+ "20" => "Melton Mowbray Building Society",
+ "21" => "Nationwide Building Society",
+ "22" => "Natwest",
+ "23" => "Nedbank Private Wealth",
+ "24" => "Newbury Building Society",
+ "25" => "OneSavings Bank",
+ "26" => "Parity Trust",
+ "27" => "Penrith Building Society",
+ "28" => "Pepper Homeloans",
+ "29" => "Royal Bank of Scotland",
+ "30" => "Santander",
+ "31" => "Skipton Building Society",
+ "32" => "Teachers Building Society",
+ "33" => "The Co-operative Bank",
+ "34" => "Tipton & Coseley Building Society",
+ "35" => "TSB",
+ "36" => "Ulster Bank",
+ "37" => "Virgin Money",
+ "38" => "West Bromwich Building Society",
+ "39" => "Yorkshire Building Society",
+ "41" => "Kent Reliance",
+ "40" => "Other",
+ })
+ end
end
end
diff --git a/spec/models/form/sales/questions/nationality_all_group_spec.rb b/spec/models/form/sales/questions/nationality_all_group_spec.rb
index b0bfbbfc8..cd70b2a12 100644
--- a/spec/models/form/sales/questions/nationality_all_group_spec.rb
+++ b/spec/models/form/sales/questions/nationality_all_group_spec.rb
@@ -47,7 +47,7 @@ RSpec.describe Form::Sales::Questions::NationalityAllGroup, type: :model do
end
it "has the correct hint" do
- expect(question.hint_text).to eq "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest."
+ expect(question.hint_text).to eq "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest. If buyer 1 is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, the buyer should decide which country to enter."
end
it "has the correct header" do
@@ -75,7 +75,7 @@ RSpec.describe Form::Sales::Questions::NationalityAllGroup, type: :model do
end
it "has the correct hint" do
- expect(question.hint_text).to eq("")
+ expect(question.hint_text).to eq "If buyer 2 is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, the buyer should decide which country to enter."
end
it "has the correct header" do
diff --git a/spec/models/form/sales/questions/privacy_notice_spec.rb b/spec/models/form/sales/questions/privacy_notice_spec.rb
index 1fec22b6e..c37f6f920 100644
--- a/spec/models/form/sales/questions/privacy_notice_spec.rb
+++ b/spec/models/form/sales/questions/privacy_notice_spec.rb
@@ -6,6 +6,14 @@ RSpec.describe Form::Sales::Questions::PrivacyNotice, type: :model do
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
+ let(:subsection) { instance_double(Form::Subsection) }
+ let(:form) { instance_double(Form) }
+
+ before do
+ allow(form).to receive(:start_year_after_2024?)
+ allow(page).to receive(:subsection).and_return(subsection)
+ allow(subsection).to receive(:form).and_return(form)
+ end
it "has correct page" do
expect(question.page).to eq(page)
@@ -35,10 +43,36 @@ RSpec.describe Form::Sales::Questions::PrivacyNotice, type: :model do
expect(question.hint_text).to be_nil
end
- it "has the correct answer_options" do
- expect(question.answer_options).to eq({
- "privacynotice" => { "value" => "The buyer has seen the DLUHC privacy notice" },
- })
+ context "when the form year is before 2024" do
+ before do
+ allow(form).to receive(:start_year_after_2024?).and_return(false)
+ end
+
+ it "has the correct answer_options" do
+ expect(question.answer_options).to eq({
+ "privacynotice" => { "value" => "The buyer has seen the DLUHC privacy notice" },
+ })
+ end
+
+ it "uses the expected top guidance partial" do
+ expect(question.top_guidance_partial).to eq("privacy_notice_buyer")
+ end
+ end
+
+ context "when the form year is >= 2024" do
+ before do
+ allow(form).to receive(:start_year_after_2024?).and_return(true)
+ end
+
+ it "has the correct answer_options" do
+ expect(question.answer_options).to eq({
+ "privacynotice" => { "value" => "The buyer has seen or been given access to the DLUHC privacy notice" },
+ })
+ end
+
+ it "uses the expected top guidance partial" do
+ expect(question.top_guidance_partial).to eq("privacy_notice_buyer_2024")
+ end
end
it "returns correct unanswered_error_message" do
diff --git a/spec/models/form/sales/subsections/household_characteristics_spec.rb b/spec/models/form/sales/subsections/household_characteristics_spec.rb
index 00e96450f..842dec8bc 100644
--- a/spec/models/form/sales/subsections/household_characteristics_spec.rb
+++ b/spec/models/form/sales/subsections/household_characteristics_spec.rb
@@ -240,7 +240,6 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
it "has correct pages" do
expect(household_characteristics.pages.map(&:id)).to eq(
%w[
- privacy_notice
buyer_1_age
age_1_retirement_value_check
age_1_old_persons_shared_ownership_value_check
diff --git a/spec/models/form/sales/subsections/setup_spec.rb b/spec/models/form/sales/subsections/setup_spec.rb
index 2ddb64e19..eb92e3753 100644
--- a/spec/models/form/sales/subsections/setup_spec.rb
+++ b/spec/models/form/sales/subsections/setup_spec.rb
@@ -68,6 +68,7 @@ RSpec.describe Form::Sales::Subsections::Setup, type: :model do
joint_purchase
number_joint_buyers
buyer_interview
+ privacy_notice
],
)
end
diff --git a/spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb b/spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb
index 192f7cbda..3746768d0 100644
--- a/spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb
+++ b/spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb
@@ -7,6 +7,10 @@ RSpec.describe Form::Sales::Subsections::SharedOwnershipScheme, type: :model do
let(:subsection_definition) { nil }
let(:section) { instance_double(Form::Sales::Sections::SaleInformation) }
+ before do
+ allow(section).to receive(:form).and_return(instance_double(Form, start_year_after_2024?: false))
+ end
+
it "has correct section" do
expect(shared_ownership_scheme.section).to eq(section)
end
diff --git a/spec/models/form_handler_spec.rb b/spec/models/form_handler_spec.rb
index fe340f71b..257c41b43 100644
--- a/spec/models/form_handler_spec.rb
+++ b/spec/models/form_handler_spec.rb
@@ -271,7 +271,7 @@ RSpec.describe FormHandler do
sales_form = FormFactory.new(year: 1936, type: "sales")
.with_sections([section])
.build
- described_class.instance.use_fake_forms!({ only_sales: sales_form })
+ described_class.instance.use_fake_forms!({ "current_sales" => sales_form })
expect(result).to(satisfy { |result| result.all? { |element| element.is_a?(Form::Question) } })
end
@@ -281,7 +281,7 @@ RSpec.describe FormHandler do
sales_form = FormFactory.new(year: 1936, type: "sales")
.with_sections([first_section, second_section])
.build
- described_class.instance.use_fake_forms!({ only_sales: sales_form })
+ described_class.instance.use_fake_forms!({ "current_sales" => sales_form })
expect(result.map(&:id)).to eq %w[1 2 3 4 5]
end
@@ -291,7 +291,7 @@ RSpec.describe FormHandler do
sales_form = FormFactory.new(year: 1945, type: "sales")
.with_sections([first_section, second_section])
.build
- described_class.instance.use_fake_forms!({ only_sales: sales_form })
+ described_class.instance.use_fake_forms!({ "current_sales" => sales_form })
expect(result.map(&:id)).to eq %w[1 2 3 4 5 6]
end
@@ -305,12 +305,49 @@ RSpec.describe FormHandler do
.with_sections([new_section])
.build
fake_forms = {
- earlier_sales: original_form,
- newer_sales: new_form,
+ "previous_sales" => original_form,
+ "current_sales" => new_form,
}
described_class.instance.use_fake_forms!(fake_forms)
expect(result.map(&:id)).to eq %w[1 1a_deprecated 2 2a_new 3]
end
+
+ it "inserts questions from previous years that would start a section after new questions in the previous section" do
+ original_first_section = build(:section)
+ original_first_section.subsections = [build(:subsection, :with_questions, question_ids: %w[1 2], id: "1", section: original_first_section)]
+ new_first_section = build(:section)
+ new_first_section.subsections = [build(:subsection, :with_questions, question_ids: %w[1 2 extra], id: "1", section: new_first_section)]
+ original_second_section = build(:section)
+ original_second_section.subsections = [build(:subsection, :with_questions, question_ids: %w[3 4], id: "2")]
+ new_second_section = build(:section)
+ new_second_section.subsections = [build(:subsection, :with_questions, question_ids: %w[3_new 4], id: "2")]
+
+ original_form = FormFactory.new(year: 2022, type: "sales").with_sections([original_first_section, original_second_section]).build
+ new_form = FormFactory.new(year: 2023, type: "sales").with_sections([new_first_section, new_second_section]).build
+
+ fake_forms = {
+ "current_sales" => new_form,
+ "previous_sales" => original_form,
+ }
+ described_class.instance.use_fake_forms!(fake_forms)
+ expect(result.map(&:id)).to eq %w[1 2 extra 3 3_new 4]
+ end
+
+ it "builds the ordering based on the current form" do
+ archived_section = build(:section, :with_questions, question_ids: %w[0 1 2 3])
+ previous_section = build(:section, :with_questions, question_ids: %w[0 1 3 2])
+ current_section = build(:section, :with_questions, question_ids: %w[3 2 0 1])
+ next_section = build(:section, :with_questions, question_ids: %w[3 2 1 0])
+
+ fake_forms = {
+ "current_sales" => FormFactory.new(year: 2023, type: "sales").with_sections([current_section]).build,
+ "previous_sales" => FormFactory.new(year: 2022, type: "sales").with_sections([previous_section]).build,
+ "next_sales" => FormFactory.new(year: 2021, type: "sales").with_sections([next_section]).build,
+ "archived_sales" => FormFactory.new(year: 2020, type: "sales").with_sections([archived_section]).build,
+ }
+ described_class.instance.use_fake_forms!(fake_forms)
+ expect(result.map(&:id)).to eq %w[3 2 0 1]
+ end
end
describe "#ordered_lettings_questions_for_all_years" do
@@ -322,7 +359,7 @@ RSpec.describe FormHandler do
lettings_form = FormFactory.new(year: 2936, type: "lettings")
.with_sections([section])
.build
- described_class.instance.use_fake_forms!({ only_lettings: lettings_form })
+ described_class.instance.use_fake_forms!({ "current_lettings" => lettings_form })
expect(result).to(satisfy { |result| result.all? { |element| element.is_a?(Form::Question) } })
end
@@ -332,7 +369,7 @@ RSpec.describe FormHandler do
lettings_form = FormFactory.new(year: 2936, type: "lettings")
.with_sections([first_section, second_section])
.build
- described_class.instance.use_fake_forms!({ only_lettings: lettings_form })
+ described_class.instance.use_fake_forms!({ "current_lettings" => lettings_form })
expect(result.map(&:id)).to eq %w[1 2 3 4 5]
end
@@ -342,7 +379,7 @@ RSpec.describe FormHandler do
lettings_form = FormFactory.new(year: 2945, type: "lettings")
.with_sections([first_section, second_section])
.build
- described_class.instance.use_fake_forms!({ only_lettings: lettings_form })
+ described_class.instance.use_fake_forms!({ "current_lettings" => lettings_form })
expect(result.map(&:id)).to eq %w[1 2 3 4 5 6]
end
@@ -356,12 +393,49 @@ RSpec.describe FormHandler do
.with_sections([new_section])
.build
fake_forms = {
- earlier_lettings: original_form,
- newer_lettings: new_form,
+ "previous_lettings" => original_form,
+ "current_lettings" => new_form,
}
described_class.instance.use_fake_forms!(fake_forms)
expect(result.map(&:id)).to eq %w[1 1a_deprecated 2 2a_new 3]
end
+
+ it "inserts questions from previous years that would start a section after new questions in the previous section" do
+ original_first_section = build(:section)
+ original_first_section.subsections = [build(:subsection, :with_questions, question_ids: %w[1 2], id: "1", section: original_first_section)]
+ new_first_section = build(:section)
+ new_first_section.subsections = [build(:subsection, :with_questions, question_ids: %w[1 2 extra], id: "1", section: new_first_section)]
+ original_second_section = build(:section)
+ original_second_section.subsections = [build(:subsection, :with_questions, question_ids: %w[3 4], id: "2")]
+ new_second_section = build(:section)
+ new_second_section.subsections = [build(:subsection, :with_questions, question_ids: %w[3_new 4], id: "2")]
+
+ original_form = FormFactory.new(year: 2023, type: "lettings").with_sections([original_first_section, original_second_section]).build
+ new_form = FormFactory.new(year: 2024, type: "lettings").with_sections([new_first_section, new_second_section]).build
+
+ fake_forms = {
+ "current_lettings" => new_form,
+ "previous_lettings" => original_form,
+ }
+ described_class.instance.use_fake_forms!(fake_forms)
+ expect(result.map(&:id)).to eq %w[1 2 extra 3 3_new 4]
+ end
+
+ it "builds the ordering based on the current form" do
+ archived_section = build(:section, :with_questions, question_ids: %w[0 1 2 3])
+ previous_section = build(:section, :with_questions, question_ids: %w[0 1 3 2])
+ current_section = build(:section, :with_questions, question_ids: %w[3 2 0 1])
+ next_section = build(:section, :with_questions, question_ids: %w[3 2 1 0])
+
+ fake_forms = {
+ "current_lettings" => FormFactory.new(year: 2023, type: "sales").with_sections([current_section]).build,
+ "previous_lettings" => FormFactory.new(year: 2022, type: "sales").with_sections([previous_section]).build,
+ "next_lettings" => FormFactory.new(year: 2021, type: "sales").with_sections([next_section]).build,
+ "archived_lettings" => FormFactory.new(year: 2020, type: "sales").with_sections([archived_section]).build,
+ }
+ described_class.instance.use_fake_forms!(fake_forms)
+ expect(result.map(&:id)).to eq %w[3 2 0 1]
+ end
end
# rubocop:enable RSpec/PredicateMatcher
end
diff --git a/spec/models/sales_log_spec.rb b/spec/models/sales_log_spec.rb
index 42aa64fec..00792b0bd 100644
--- a/spec/models/sales_log_spec.rb
+++ b/spec/models/sales_log_spec.rb
@@ -657,7 +657,7 @@ RSpec.describe SalesLog, type: :model do
end
before do
- WebMock.stub_request(:get, /api.postcodes.io\/postcodes\/CA101AA/)
+ WebMock.stub_request(:get, /api\.postcodes\.io\/postcodes\/CA101AA/)
.to_return(status: 200, body: '{"status":200,"result":{"admin_district":"Cumberland","codes":{"admin_district":"E06000064"}}}', headers: {})
Timecop.freeze(2023, 5, 1)
@@ -687,7 +687,7 @@ RSpec.describe SalesLog, type: :model do
end
before do
- WebMock.stub_request(:get, /api.postcodes.io\/postcodes\/CA101AA/)
+ WebMock.stub_request(:get, /api\.postcodes\.io\/postcodes\/CA101AA/)
.to_return(status: 200, body: '{"status":200,"result":{"admin_district":"Eden","codes":{"admin_district":"E07000030"}}}', headers: {})
Timecop.freeze(2023, 5, 2)
@@ -703,6 +703,56 @@ RSpec.describe SalesLog, type: :model do
expect(address_sales_log_23_24.la).to eq("E06000064")
expect(record_from_db["la"]).to eq("E06000064")
end
+
+ it "does not set previous postcode or previous la for discounted sale" do
+ address_sales_log_23_24.update!(ownershipsch: 2, ppostcode_full: nil, prevloc: nil)
+ record_from_db = described_class.find(address_sales_log_23_24.id)
+ expect(address_sales_log_23_24.ppostcode_full).to eq(nil)
+ expect(record_from_db["ppostcode_full"]).to eq(nil)
+ expect(record_from_db["prevloc"]).to eq(nil)
+ end
+ end
+
+ context "with 24/25 logs" do
+ let(:address_sales_log_24_25) do
+ described_class.create({
+ owning_organisation:,
+ created_by: created_by_user,
+ ppcodenk: 1,
+ postcode_full: "CA10 1AA",
+ ppostcode_full: nil,
+ prevloc: nil,
+ saledate: Time.zone.local(2024, 5, 2),
+ })
+ end
+
+ before do
+ WebMock.stub_request(:get, /api\.postcodes\.io\/postcodes\/CA101AA/)
+ .to_return(status: 200, body: '{"status":200,"result":{"admin_district":"Eden","codes":{"admin_district":"E07000030"}}}', headers: {})
+
+ Timecop.freeze(2024, 5, 2)
+ Singleton.__init__(FormHandler)
+ end
+
+ after do
+ Timecop.unfreeze
+ end
+
+ it "sets previous postcode for discounted sale" do
+ address_sales_log_24_25.update!(ownershipsch: 2, ppostcode_full: nil)
+ record_from_db = described_class.find(address_sales_log_24_25.id)
+ expect(address_sales_log_24_25.ppostcode_full).to eq("CA10 1AA")
+ expect(record_from_db["ppostcode_full"]).to eq("CA10 1AA")
+ expect(record_from_db["prevloc"]).to eq("E06000064")
+ end
+
+ it "does not set previous postcode for non discounted sale" do
+ address_sales_log_24_25.update!(ownershipsch: 1, ppostcode_full: nil)
+ record_from_db = described_class.find(address_sales_log_24_25.id)
+ expect(address_sales_log_24_25.ppostcode_full).to eq(nil)
+ expect(record_from_db["ppostcode_full"]).to eq(nil)
+ expect(record_from_db["prevloc"]).to eq(nil)
+ end
end
it "errors if the property postcode is emptied" do
diff --git a/spec/models/validations/financial_validations_spec.rb b/spec/models/validations/financial_validations_spec.rb
index 1a6672e08..c68a3b7b2 100644
--- a/spec/models/validations/financial_validations_spec.rb
+++ b/spec/models/validations/financial_validations_spec.rb
@@ -188,6 +188,7 @@ RSpec.describe Validations::FinancialValidations do
describe "net income validations" do
it "validates that the net income is within the expected range for the household’s employment status" do
+ record.startdate = Time.zone.local(2023, 5, 1)
record.earnings = 200
record.incfreq = 1
record.hhmemb = 1
@@ -198,6 +199,7 @@ RSpec.describe Validations::FinancialValidations do
context "when the net income is higher than the hard max for their employment status" do
it "adds an error" do
+ record.startdate = Time.zone.local(2023, 5, 1)
record.earnings = 5000
record.incfreq = 1
record.hhmemb = 1
@@ -214,6 +216,7 @@ RSpec.describe Validations::FinancialValidations do
context "when the net income is lower than the hard min for their employment status" do
it "adds an error" do
+ record.startdate = Time.zone.local(2023, 5, 1)
record.earnings = 50
record.incfreq = 1
record.hhmemb = 1
@@ -230,6 +233,7 @@ RSpec.describe Validations::FinancialValidations do
context "when there is more than one household member" do
it "allows income levels based on all working situations combined" do
+ record.startdate = Time.zone.local(2023, 5, 1)
record.earnings = 5000
record.incfreq = 1
record.hhmemb = 4
@@ -242,6 +246,7 @@ RSpec.describe Validations::FinancialValidations do
end
it "uses the combined value in error messages" do
+ record.startdate = Time.zone.local(2023, 5, 1)
record.earnings = 100
record.incfreq = 1
record.hhmemb = 3
@@ -254,6 +259,7 @@ RSpec.describe Validations::FinancialValidations do
end
it "adds errors to relevant fields for each tenant when income is too high" do
+ record.startdate = Time.zone.local(2023, 5, 1)
record.earnings = 5000
record.incfreq = 1
record.hhmemb = 3
@@ -277,6 +283,7 @@ RSpec.describe Validations::FinancialValidations do
end
it "adds errors to relevant fields for each tenant when income is too low" do
+ record.startdate = Time.zone.local(2023, 5, 1)
record.earnings = 50
record.incfreq = 1
record.hhmemb = 3
@@ -293,6 +300,20 @@ RSpec.describe Validations::FinancialValidations do
expect(record.errors["ecstat#{n}"]).to be_empty
end
end
+
+ context "when the net income is lower than the hard min for their employment status for 22/23 collection" do
+ it "does not add an error" do
+ record.startdate = Time.zone.local(2022, 5, 1)
+ record.earnings = 50
+ record.incfreq = 1
+ record.hhmemb = 1
+ record.ecstat1 = 1
+ financial_validator.validate_net_income(record)
+ expect(record.errors["earnings"]).to be_empty
+ expect(record.errors["ecstat1"]).to be_empty
+ expect(record.errors["hhmemb"]).to be_empty
+ end
+ end
end
end
diff --git a/spec/models/validations/sales/household_validations_spec.rb b/spec/models/validations/sales/household_validations_spec.rb
index 898a47fcc..deddfa8ef 100644
--- a/spec/models/validations/sales/household_validations_spec.rb
+++ b/spec/models/validations/sales/household_validations_spec.rb
@@ -154,64 +154,6 @@ RSpec.describe Validations::Sales::HouseholdValidations do
end
end
- describe "previous postcode validations" do
- let(:record) { build(:sales_log) }
-
- context "with a discounted sale" do
- before do
- record.ownershipsch = 2
- end
-
- it "adds an error when previous and current postcodes are not the same" do
- record.postcode_full = "SO32 3PT"
- record.ppostcode_full = "DN6 7FB"
- household_validator.validate_previous_postcode(record)
- expect(record.errors["postcode_full"])
- .to include(match I18n.t("validations.household.postcode.discounted_ownership"))
- expect(record.errors["ppostcode_full"])
- .to include(match I18n.t("validations.household.postcode.discounted_ownership"))
- end
-
- it "allows same postcodes" do
- record.postcode_full = "SO32 3PT"
- record.ppostcode_full = "SO32 3PT"
- household_validator.validate_previous_postcode(record)
- expect(record.errors["postcode_full"]).to be_empty
- expect(record.errors["ppostcode_full"]).to be_empty
- end
-
- it "does not add an error when postcode is missing" do
- record.postcode_full = nil
- record.ppostcode_full = "SO32 3PT"
- household_validator.validate_previous_postcode(record)
- expect(record.errors["postcode_full"]).to be_empty
- expect(record.errors["ppostcode_full"]).to be_empty
- end
-
- it "does not add an error when previous postcode is missing" do
- record.postcode_full = "SO32 3PT"
- record.ppostcode_full = nil
- household_validator.validate_previous_postcode(record)
- expect(record.errors["postcode_full"]).to be_empty
- expect(record.errors["ppostcode_full"]).to be_empty
- end
- end
-
- context "without a discounted sale" do
- before do
- record.ownershipsch = 1
- end
-
- it "allows different postcodes" do
- record.postcode_full = "SO32 3PT"
- record.ppostcode_full = "DN6 7FB"
- household_validator.validate_previous_postcode(record)
- expect(record.errors["postcode_full"]).to be_empty
- expect(record.errors["ppostcode_full"]).to be_empty
- end
- end
- end
-
describe "validating fields about buyers living in the property" do
let(:sales_log) { FactoryBot.create(:sales_log, :outright_sale_setup_complete, noint: 1, companybuy: 2, buylivein:, jointpur:, jointmore:, buy1livein:) }
diff --git a/spec/models/validations/sales/property_validations_spec.rb b/spec/models/validations/sales/property_validations_spec.rb
index 0152428d8..f84f65966 100644
--- a/spec/models/validations/sales/property_validations_spec.rb
+++ b/spec/models/validations/sales/property_validations_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe Validations::Sales::PropertyValidations do
end
context "when ownership scheme is discounted ownership" do
- let(:record) { build(:sales_log, ownershipsch: 2) }
+ let(:record) { build(:sales_log, ownershipsch: 2, saledate: Time.zone.local(2023, 4, 5)) }
it "when ppostcode_full is not present no error is added" do
record.postcode_full = "SW1A 1AA"
@@ -54,6 +54,16 @@ RSpec.describe Validations::Sales::PropertyValidations do
expect(record.errors["ppostcode_full"]).to include(match I18n.t("validations.property.postcode.must_match_previous"))
expect(record.errors["ownershipsch"]).to include(match I18n.t("validations.property.postcode.must_match_previous"))
end
+
+ it "does not add error for 2024 log" do
+ record.postcode_full = "SW1A 1AA"
+ record.ppostcode_full = "SW1A 0AA"
+ record.saledate = Time.zone.local(2024, 4, 5)
+ property_validator.validate_postcodes_match_if_discounted_ownership(record)
+ expect(record.errors["postcode_full"]).to be_empty
+ expect(record.errors["ppostcode_full"]).to be_empty
+ expect(record.errors["ownershipsch"]).to be_empty
+ end
end
end
diff --git a/spec/models/validations/sales/sale_information_validations_spec.rb b/spec/models/validations/sales/sale_information_validations_spec.rb
index 73243e9fa..3d323112e 100644
--- a/spec/models/validations/sales/sale_information_validations_spec.rb
+++ b/spec/models/validations/sales/sale_information_validations_spec.rb
@@ -756,4 +756,48 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end
end
end
+
+ describe "#validate_mortgage_used_and_stairbought" do
+ let(:now) { Time.zone.local(2024, 4, 4) }
+
+ before do
+ Timecop.freeze(now)
+ Singleton.__init__(FormHandler)
+ end
+
+ after do
+ Timecop.return
+ Singleton.__init__(FormHandler)
+ end
+
+ context "when mortgageused don't know" do
+ let(:record) { build(:sales_log, ownershipsch: 1, type: 9, saledate: now, mortgageused: 3) }
+
+ it "does not add an error if stairowned 100" do
+ record.stairowned = 100
+ sale_information_validator.validate_mortgage_used_and_stairbought(record)
+
+ expect(record.errors).to be_empty
+ end
+
+ it "adds an error if stairowned is not 100" do
+ record.stairowned = 90
+ sale_information_validator.validate_mortgage_used_and_stairbought(record)
+
+ expect(record.errors[:stairowned]).to include("The percentage owned has to be 100% if the mortgage used is 'Don’t know'")
+ expect(record.errors[:mortgageused]).to include("The percentage owned has to be 100% if the mortgage used is 'Don’t know'")
+ end
+ end
+
+ context "when the collection year is before 2024" do
+ let(:record) { build(:sales_log, ownershipsch: 1, type: 9, saledate: now, mortgageused: 3, stairowned: 90) }
+ let(:now) { Time.zone.local(2023, 4, 4) }
+
+ it "does not add an error" do
+ sale_information_validator.validate_mortgage_used_and_stairbought(record)
+
+ expect(record.errors).to be_empty
+ end
+ end
+ end
end
diff --git a/spec/requests/bulk_upload_lettings_resume_controller_spec.rb b/spec/requests/bulk_upload_lettings_resume_controller_spec.rb
index ce2c3b7c6..c91211d5a 100644
--- a/spec/requests/bulk_upload_lettings_resume_controller_spec.rb
+++ b/spec/requests/bulk_upload_lettings_resume_controller_spec.rb
@@ -211,5 +211,22 @@ RSpec.describe BulkUploadLettingsResumeController, type: :request do
expect(response).to redirect_to("/lettings-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/chosen")
end
end
+
+ context "and has a row with all non-cleared errors" do
+ let(:bulk_upload_errors) { [create(:bulk_upload_error, row: 1), create(:bulk_upload_error, row: 2, category: :not_answered), create(:bulk_upload_error, row: 3, category: :soft_validation), create(:bulk_upload_error, row: 4)] }
+ let(:bulk_upload) { create(:bulk_upload, :lettings, user:, bulk_upload_errors:) }
+
+ it "renders the page correctly" do
+ get "/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/deletion-report"
+
+ expect(response).to be_successful
+
+ expect(response.body).to include("Bulk upload for lettings")
+ expect(response.body).to include("2023/24")
+ expect(response.body).to include("These 2 answers will be deleted if you upload the log")
+ expect(response.body).to include(bulk_upload.filename)
+ expect(response.body).to include("Clear this data and upload the logs")
+ end
+ end
end
end
diff --git a/spec/requests/bulk_upload_sales_resume_controller_spec.rb b/spec/requests/bulk_upload_sales_resume_controller_spec.rb
index 36c853097..47acc1ae6 100644
--- a/spec/requests/bulk_upload_sales_resume_controller_spec.rb
+++ b/spec/requests/bulk_upload_sales_resume_controller_spec.rb
@@ -211,5 +211,22 @@ RSpec.describe BulkUploadSalesResumeController, type: :request do
expect(response).to redirect_to("/sales-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/chosen")
end
end
+
+ context "and has a row with all non-cleared errors" do
+ let(:bulk_upload_errors) { [create(:bulk_upload_error, row: 1), create(:bulk_upload_error, row: 2, category: :not_answered), create(:bulk_upload_error, row: 3, category: :soft_validation), create(:bulk_upload_error, row: 4)] }
+ let(:bulk_upload) { create(:bulk_upload, :sales, user:, bulk_upload_errors:) }
+
+ it "renders the page correctly" do
+ get "/sales-logs/bulk-upload-resume/#{bulk_upload.id}/deletion-report"
+
+ expect(response).to be_successful
+
+ expect(response.body).to include("Bulk upload for sales")
+ expect(response.body).to include("2023/24")
+ expect(response.body).to include("These 2 answers will be deleted if you upload the log")
+ expect(response.body).to include(bulk_upload.filename)
+ expect(response.body).to include("Clear this data and upload the logs")
+ end
+ end
end
end
diff --git a/spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb
index 58be059fc..4501d25be 100644
--- a/spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb
+++ b/spec/services/bulk_upload/lettings/year2024/row_parser_spec.rb
@@ -1119,6 +1119,7 @@ RSpec.describe BulkUpload::Lettings::Year2024::RowParser do
expect(parser.errors[:field_112]).to include("You must answer was the letting made under the Choice-Based Lettings (CBL)?")
expect(parser.errors[:field_113]).to include("You must answer was the letting made under the Common Allocation Policy (CAP)?")
expect(parser.errors[:field_114]).to include("You must answer was the letting made under the Common Housing Register (CHR)?")
+ expect(parser.errors[:field_115]).to include("You must answer was the letting made under the Accessible Register?")
end
end
end
@@ -1889,16 +1890,16 @@ RSpec.describe BulkUpload::Lettings::Year2024::RowParser do
end
describe "#letting_allocation_unknown" do
- context "when field_112, 117, 118 are no ie 2" do
- let(:attributes) { { bulk_upload:, field_112: 2, field_113: 2, field_114: 2 } }
+ context "when field_112, 113, 114, 115 are no ie 2" do
+ let(:attributes) { { bulk_upload:, field_112: 2, field_113: 2, field_114: 2, field_115: 2 } }
it "sets value to 1" do
expect(parser.log.letting_allocation_unknown).to be(1)
end
end
- context "when any one of field_112, 117, 118 is yes ie 1" do
- let(:attributes) { { bulk_upload:, field_112: 1 } }
+ context "when any one of field_112, 113, 114, 115 is yes ie 1" do
+ let(:attributes) { { bulk_upload:, field_115: 1 } }
it "sets value to 0" do
expect(parser.log.letting_allocation_unknown).to be(0)
diff --git a/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb b/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb
index 93e6fdca1..d93453a22 100644
--- a/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb
+++ b/spec/services/bulk_upload/sales/year2024/row_parser_spec.rb
@@ -975,6 +975,36 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do
end
end
+ describe "#field_103" do # shared ownership mortgageused
+ context "when invalid value" do
+ let(:attributes) { setup_section_params.merge(field_103: "4") }
+
+ it "returns correct errors" do
+ expect(parser.errors[:field_103]).to include("Enter a valid value for Was a mortgage used for the purchase of this property? - Shared ownership")
+ end
+ end
+
+ context "when value is 3 and stairowned is not 100" do
+ let(:attributes) { setup_section_params.merge(field_103: "3", field_86: "1", field_87: "50", field_88: "99", field_109: nil) }
+
+ it "returns correct errors" do
+ expect(parser.errors[:field_103]).to include("Enter a valid value for Was a mortgage used for the purchase of this property? - Shared ownership")
+ end
+ end
+
+ context "when value is 3 and stairowned is 100" do
+ let(:attributes) { setup_section_params.merge(field_103: "3", field_86: "1", field_87: "50", field_88: "100", field_109: nil) }
+
+ it "does not add errors and sets mortgage used to 3" do
+ expect(parser.log.mortgageused).to be(3)
+ expect(parser.log.stairowned).to be(100)
+ expect(parser.log.deposit).to be(nil)
+ expect(parser.errors[:field_103]).to be_empty
+ expect(parser.errors[:field_109]).to be_empty
+ end
+ end
+ end
+
describe "soft validations" do
context "when soft validation is triggered" do
let(:attributes) { valid_attributes.merge({ field_31: 22, field_35: 5 }) }
diff --git a/spec/services/csv/lettings_log_csv_service_spec.rb b/spec/services/csv/lettings_log_csv_service_spec.rb
index ac6d14f0f..7109d0e94 100644
--- a/spec/services/csv/lettings_log_csv_service_spec.rb
+++ b/spec/services/csv/lettings_log_csv_service_spec.rb
@@ -2,7 +2,7 @@ require "rails_helper"
RSpec.describe Csv::LettingsLogCsvService do
before do
- Timecop.freeze(fixed_time)
+ Timecop.freeze(now)
Singleton.__init__(FormHandler)
FormHandler.instance.use_real_forms!
log.irproduct = 1
@@ -17,13 +17,14 @@ RSpec.describe Csv::LettingsLogCsvService do
let(:form_handler_mock) { instance_double(FormHandler) }
let(:organisation) { create(:organisation) }
let(:fixed_time) { Time.zone.local(2023, 11, 26) }
+ let(:now) { Time.zone.now }
let(:log) do
create(
:lettings_log,
:completed,
startdate: fixed_time,
created_at: fixed_time,
- updated_at: fixed_time,
+ updated_at: now,
mrcdate: fixed_time - 1.day,
voiddate: fixed_time - 2.days,
propcode: "ABCDEFG",
@@ -100,7 +101,7 @@ RSpec.describe Csv::LettingsLogCsvService do
let(:questions) do
[
build(:question, id: "condition_effects", type: "checkbox", answer_options: { "illness_type_1" => {}, "illness_type_2" => {}, "illness_type_3" => {} }),
- build(:question, id: "letting_allocation", type: "checkbox", answer_options: { "cbl" => {}, "cap" => {}, "chr" => {} }),
+ build(:question, id: "letting_allocation", type: "checkbox", answer_options: { "cbl" => {}, "cap" => {}, "chr" => {}, "accessible_register" => {} }),
]
end
@@ -162,14 +163,32 @@ RSpec.describe Csv::LettingsLogCsvService do
expect(la_label_value).to eq "Barnet"
end
- it "exports the CSV with all values correct" do
- expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_labels.csv")
- values_to_delete = %w[id vacdays]
- values_to_delete.each do |attribute|
- index = csv.first.index(attribute)
- csv.second[index] = nil
+ context "when the current form is 2024" do
+ let(:now) { Time.zone.local(2024, 4, 1) }
+
+ it "exports the CSV with 2024 ordering and all values correct" do
+ expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_labels_24.csv")
+ values_to_delete = %w[id vacdays]
+ values_to_delete.each do |attribute|
+ index = csv.first.index(attribute)
+ csv.second[index] = nil
+ end
+ expect(csv).to eq expected_content
+ end
+ end
+
+ context "when the current form is 2023" do
+ let(:now) { Time.zone.local(2023, 11, 26) }
+
+ it "exports the CSV with 2023 ordering and all values correct" do
+ expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_labels_23.csv")
+ values_to_delete = %w[id vacdays]
+ values_to_delete.each do |attribute|
+ index = csv.first.index(attribute)
+ csv.second[index] = nil
+ end
+ expect(csv).to eq expected_content
end
- expect(csv).to eq expected_content
end
context "when the log has a duplicate log reference" do
@@ -212,14 +231,32 @@ RSpec.describe Csv::LettingsLogCsvService do
expect(la_label_value).to eq "Barnet"
end
- it "exports the CSV with all values correct" do
- expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_codes.csv")
- values_to_delete = %w[id vacdays]
- values_to_delete.each do |attribute|
- index = csv.first.index(attribute)
- csv.second[index] = nil
+ context "when the current form is 2024" do
+ let(:now) { Time.zone.local(2024, 4, 1) }
+
+ it "exports the CSV with all values correct" do
+ expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_codes_24.csv")
+ values_to_delete = %w[id vacdays]
+ values_to_delete.each do |attribute|
+ index = csv.first.index(attribute)
+ csv.second[index] = nil
+ end
+ expect(csv).to eq expected_content
+ end
+ end
+
+ context "when the current form is 2023" do
+ let(:now) { Time.zone.local(2023, 11, 26) }
+
+ it "exports the CSV with all values correct" do
+ expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_codes_23.csv")
+ values_to_delete = %w[id vacdays]
+ values_to_delete.each do |attribute|
+ index = csv.first.index(attribute)
+ csv.second[index] = nil
+ end
+ expect(csv).to eq expected_content
end
- expect(csv).to eq expected_content
end
context "when the log has a duplicate log reference" do
@@ -242,31 +279,67 @@ RSpec.describe Csv::LettingsLogCsvService do
expect(headers).not_to include(*%w[wrent wscharge wpschrge wsupchrg wtcharge])
end
- context "and exporting with labels" do
- let(:export_type) { "labels" }
+ context "and the current form is 2024" do
+ let(:now) { Time.zone.local(2024, 4, 1) }
- it "exports the CSV with all values correct" do
- expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_non_support_labels.csv")
- values_to_delete = %w[id]
- values_to_delete.each do |attribute|
- index = csv.first.index(attribute)
- csv.second[index] = nil
+ context "and exporting with labels" do
+ let(:export_type) { "labels" }
+
+ it "exports the CSV with all values correct" do
+ expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_non_support_labels_24.csv")
+ values_to_delete = %w[id]
+ values_to_delete.each do |attribute|
+ index = csv.first.index(attribute)
+ csv.second[index] = nil
+ end
+ expect(csv).to eq expected_content
+ end
+ end
+
+ context "and exporting values as codes" do
+ let(:export_type) { "codes" }
+
+ it "exports the CSV with all values correct" do
+ expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_non_support_codes_24.csv")
+ values_to_delete = %w[id]
+ values_to_delete.each do |attribute|
+ index = csv.first.index(attribute)
+ csv.second[index] = nil
+ end
+ expect(csv).to eq expected_content
end
- expect(csv).to eq expected_content
end
end
- context "and exporting values as codes" do
- let(:export_type) { "codes" }
+ context "and the current form is 2023" do
+ let(:now) { Time.zone.local(2023, 11, 26) }
- it "exports the CSV with all values correct" do
- expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_non_support_codes.csv")
- values_to_delete = %w[id]
- values_to_delete.each do |attribute|
- index = csv.first.index(attribute)
- csv.second[index] = nil
+ context "and exporting with labels" do
+ let(:export_type) { "labels" }
+
+ it "exports the CSV with all values correct" do
+ expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_non_support_labels_23.csv")
+ values_to_delete = %w[id]
+ values_to_delete.each do |attribute|
+ index = csv.first.index(attribute)
+ csv.second[index] = nil
+ end
+ expect(csv).to eq expected_content
+ end
+ end
+
+ context "and exporting values as codes" do
+ let(:export_type) { "codes" }
+
+ it "exports the CSV with all values correct" do
+ expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_non_support_codes_23.csv")
+ values_to_delete = %w[id]
+ values_to_delete.each do |attribute|
+ index = csv.first.index(attribute)
+ csv.second[index] = nil
+ end
+ expect(csv).to eq expected_content
end
- expect(csv).to eq expected_content
end
end
end
diff --git a/spec/services/csv/sales_log_csv_service_spec.rb b/spec/services/csv/sales_log_csv_service_spec.rb
index bbc7af0f2..79efaf920 100644
--- a/spec/services/csv/sales_log_csv_service_spec.rb
+++ b/spec/services/csv/sales_log_csv_service_spec.rb
@@ -4,6 +4,7 @@ RSpec.describe Csv::SalesLogCsvService do
let(:form_handler_mock) { instance_double(FormHandler) }
let(:organisation) { create(:organisation) }
let(:fixed_time) { Time.zone.local(2023, 12, 8) }
+ let(:now) { Time.zone.now }
let(:user) { create(:user, email: "billyboy@eyeKLAUD.com") }
let(:log) do
create(
@@ -12,7 +13,7 @@ RSpec.describe Csv::SalesLogCsvService do
created_by: user,
saledate: fixed_time,
created_at: fixed_time,
- updated_at: fixed_time,
+ updated_at: now,
owning_organisation: organisation,
purchid: nil,
hholdcount: 3,
@@ -29,11 +30,15 @@ RSpec.describe Csv::SalesLogCsvService do
let(:csv) { CSV.parse(service.prepare_csv(SalesLog.all)) }
before do
- allow(Time).to receive(:now).and_return(fixed_time)
+ Timecop.freeze(now)
Singleton.__init__(FormHandler)
log
end
+ after do
+ Timecop.return
+ end
+
it "calls the form handler to get all questions in order when initialized" do
allow(FormHandler).to receive(:instance).and_return(form_handler_mock)
allow(form_handler_mock).to receive(:ordered_sales_questions_for_all_years).and_return([])
@@ -154,14 +159,32 @@ RSpec.describe Csv::SalesLogCsvService do
expect(la_label_value).to eq "Barnet"
end
- it "exports the CSV with all values correct" do
- expected_content = CSV.read("spec/fixtures/files/sales_logs_csv_export_labels.csv")
- values_to_delete = %w[id]
- values_to_delete.each do |attribute|
- index = csv.first.index(attribute)
- csv.second[index] = nil
+ context "when the current form is 2024" do
+ let(:now) { Time.zone.local(2024, 5, 1) }
+
+ it "exports the CSV with the 2024 ordering and all values correct" do
+ expected_content = CSV.read("spec/fixtures/files/sales_logs_csv_export_labels_24.csv")
+ values_to_delete = %w[id]
+ values_to_delete.each do |attribute|
+ index = csv.first.index(attribute)
+ csv.second[index] = nil
+ end
+ expect(csv).to eq expected_content
+ end
+ end
+
+ context "when the current form is 2023" do
+ let(:now) { Time.zone.local(2024, 1, 1) }
+
+ it "exports the CSV with the 2023 ordering and all values correct" do
+ expected_content = CSV.read("spec/fixtures/files/sales_logs_csv_export_labels_23.csv")
+ values_to_delete = %w[id]
+ values_to_delete.each do |attribute|
+ index = csv.first.index(attribute)
+ csv.second[index] = nil
+ end
+ expect(csv).to eq expected_content
end
- expect(csv).to eq expected_content
end
context "when the log has a duplicate log reference" do
@@ -214,14 +237,32 @@ RSpec.describe Csv::SalesLogCsvService do
expect(la_label_value).to eq "Barnet"
end
- it "exports the CSV with all values correct" do
- expected_content = CSV.read("spec/fixtures/files/sales_logs_csv_export_codes.csv")
- values_to_delete = %w[id]
- values_to_delete.each do |attribute|
- index = csv.first.index(attribute)
- csv.second[index] = nil
+ context "when the current form is 2024" do
+ let(:now) { Time.zone.local(2024, 5, 1) }
+
+ it "exports the CSV with all values correct" do
+ expected_content = CSV.read("spec/fixtures/files/sales_logs_csv_export_codes_24.csv")
+ values_to_delete = %w[id]
+ values_to_delete.each do |attribute|
+ index = csv.first.index(attribute)
+ csv.second[index] = nil
+ end
+ expect(csv).to eq expected_content
+ end
+ end
+
+ context "when the current form is 2023" do
+ let(:now) { Time.zone.local(2024, 1, 1) }
+
+ it "exports the CSV with all values correct" do
+ expected_content = CSV.read("spec/fixtures/files/sales_logs_csv_export_codes_23.csv")
+ values_to_delete = %w[id]
+ values_to_delete.each do |attribute|
+ index = csv.first.index(attribute)
+ csv.second[index] = nil
+ end
+ expect(csv).to eq expected_content
end
- expect(csv).to eq expected_content
end
context "when the log has a duplicate log reference" do
diff --git a/spec/services/exports/lettings_log_export_service_spec.rb b/spec/services/exports/lettings_log_export_service_spec.rb
index 4e76364c4..65a41df26 100644
--- a/spec/services/exports/lettings_log_export_service_spec.rb
+++ b/spec/services/exports/lettings_log_export_service_spec.rb
@@ -256,6 +256,32 @@ RSpec.describe Exports::LettingsLogExportService do
export_service.export_xml_lettings_logs(collection_year: 2022)
end
+
+ context "and previous full exports are different for previous years" do
+ let(:expected_zip_filename) { "core_2021_2022_apr_mar_f0007_inc0004.zip" }
+ let(:expected_zip_filename2) { "core_2022_2023_apr_mar_f0001_inc0001.zip" }
+
+ before do
+ LogsExport.new(started_at: Time.zone.yesterday, base_number: 7, increment_number: 3, collection: 2021).save!
+ end
+
+ it "generates multiple ZIP export files with different base numbers in the filenames" do
+ expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args)
+ expect(storage_service).to receive(:write_file).with(expected_zip_filename2, any_args)
+ expect(Rails.logger).to receive(:info).with("Building export run for 2021")
+ expect(Rails.logger).to receive(:info).with("Creating core_2021_2022_apr_mar_f0007_inc0004 - 1 logs")
+ expect(Rails.logger).to receive(:info).with("Added core_2021_2022_apr_mar_f0007_inc0004_pt001.xml")
+ expect(Rails.logger).to receive(:info).with("Writing core_2021_2022_apr_mar_f0007_inc0004.zip")
+ expect(Rails.logger).to receive(:info).with("Building export run for 2022")
+ expect(Rails.logger).to receive(:info).with("Creating core_2022_2023_apr_mar_f0001_inc0001 - 1 logs")
+ expect(Rails.logger).to receive(:info).with("Added core_2022_2023_apr_mar_f0001_inc0001_pt001.xml")
+ expect(Rails.logger).to receive(:info).with("Writing core_2022_2023_apr_mar_f0001_inc0001.zip")
+ expect(Rails.logger).to receive(:info).with("Building export run for 2023")
+ expect(Rails.logger).to receive(:info).with("Creating core_2023_2024_apr_mar_f0001_inc0001 - 0 logs")
+
+ export_service.export_xml_lettings_logs
+ end
+ end
end
end