From 80329f5c9ad7d3c766142c472fb25683c1039f67 Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis <94526761+natdeanlewissoftwire@users.noreply.github.com> Date: Fri, 20 Mar 2026 13:38:10 +0000 Subject: [PATCH 01/19] CLDC-4249: reorder answer options (#3220) --- .../form/lettings/questions/previous_tenure.rb | 12 ++++++------ .../form/lettings/questions/previous_tenure_spec.rb | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/models/form/lettings/questions/previous_tenure.rb b/app/models/form/lettings/questions/previous_tenure.rb index 65a3911b2..d0ba644ed 100644 --- a/app/models/form/lettings/questions/previous_tenure.rb +++ b/app/models/form/lettings/questions/previous_tenure.rb @@ -52,22 +52,22 @@ class Form::Lettings::Questions::PreviousTenure < ::Form::Question "35" => { "value" => "Extra care housing" }, "38" => { "value" => "Older people’s housing for tenants with low support needs" }, "6" => { "value" => "Other supported housing" }, - "3" => { "value" => "Private sector tenancy" }, "27" => { "value" => "Owner occupation (low-cost home ownership)" }, "26" => { "value" => "Owner occupation (private)" }, + "3" => { "value" => "Private sector tenancy" }, "28" => { "value" => "Living with friends or family (long-term)" }, "39" => { "value" => "Sofa surfing (moving regularly between family or friends, no permanent bed)" }, "14" => { "value" => "Bed and breakfast" }, "7" => { "value" => "Direct access hostel" }, "10" => { "value" => "Hospital" }, "29" => { "value" => "Prison or approved probation hostel" }, - "19" => { "value" => "Rough sleeping" }, "18" => { "value" => "Any other temporary accommodation" }, + "19" => { "value" => "Rough sleeping" }, + "21" => { "value" => "Refuge" }, "13" => { "value" => "Children’s home or foster care" }, "24" => { "value" => "Home Office Asylum Support" }, "37" => { "value" => "Host family or similar refugee accommodation" }, "23" => { "value" => "Mobile home or caravan" }, - "21" => { "value" => "Refuge" }, "9" => { "value" => "Residential care home" }, "4" => { "value" => "Tied housing or rented with job" }, "25" => { "value" => "Any other accommodation" }, @@ -82,22 +82,22 @@ class Form::Lettings::Questions::PreviousTenure < ::Form::Question "35" => { "value" => "Extra care housing" }, "38" => { "value" => "Older people’s housing for tenants with low support needs" }, "6" => { "value" => "Other supported housing" }, - "3" => { "value" => "Private sector tenancy" }, "27" => { "value" => "Owner occupation (low-cost home ownership)" }, "26" => { "value" => "Owner occupation (private)" }, + "3" => { "value" => "Private sector tenancy" }, "28" => { "value" => "Living with friends or family (long-term)" }, "39" => { "value" => "Sofa surfing (moving regularly between family or friends, no permanent bed)" }, "14" => { "value" => "Bed and breakfast" }, "7" => { "value" => "Direct access hostel" }, "10" => { "value" => "Hospital" }, "29" => { "value" => "Prison or approved probation hostel" }, - "19" => { "value" => "Rough sleeping" }, "18" => { "value" => "Any other temporary accommodation" }, + "19" => { "value" => "Rough sleeping" }, + "21" => { "value" => "Refuge" }, "13" => { "value" => "Children’s home or foster care" }, "24" => { "value" => "Home Office Asylum Support" }, "37" => { "value" => "Host family or similar refugee accommodation" }, "23" => { "value" => "Mobile home or caravan" }, - "21" => { "value" => "Refuge" }, "9" => { "value" => "Residential care home" }, "4" => { "value" => "Tied housing or rented with job" }, "25" => { "value" => "Any other accommodation" }, diff --git a/spec/models/form/lettings/questions/previous_tenure_spec.rb b/spec/models/form/lettings/questions/previous_tenure_spec.rb index af891e568..21e9c7226 100644 --- a/spec/models/form/lettings/questions/previous_tenure_spec.rb +++ b/spec/models/form/lettings/questions/previous_tenure_spec.rb @@ -75,21 +75,21 @@ RSpec.describe Form::Lettings::Questions::PreviousTenure, type: :model do "35" => { "value" => "Extra care housing" }, "38" => { "value" => "Older people’s housing for tenants with low support needs" }, "6" => { "value" => "Other supported housing" }, - "3" => { "value" => "Private sector tenancy" }, "27" => { "value" => "Owner occupation (low-cost home ownership)" }, "26" => { "value" => "Owner occupation (private)" }, + "3" => { "value" => "Private sector tenancy" }, "28" => { "value" => "Living with friends or family (long-term)" }, "39" => { "value" => "Sofa surfing (moving regularly between family or friends, no permanent bed)" }, "14" => { "value" => "Bed and breakfast" }, "7" => { "value" => "Direct access hostel" }, "10" => { "value" => "Hospital" }, "29" => { "value" => "Prison or approved probation hostel" }, - "19" => { "value" => "Rough sleeping" }, "18" => { "value" => "Any other temporary accommodation" }, + "19" => { "value" => "Rough sleeping" }, + "21" => { "value" => "Refuge" }, "13" => { "value" => "Children’s home or foster care" }, "24" => { "value" => "Home Office Asylum Support" }, "23" => { "value" => "Mobile home or caravan" }, - "21" => { "value" => "Refuge" }, "9" => { "value" => "Residential care home" }, "4" => { "value" => "Tied housing or rented with job" }, "37" => { "value" => "Host family or similar refugee accommodation" }, @@ -113,21 +113,21 @@ RSpec.describe Form::Lettings::Questions::PreviousTenure, type: :model do "35" => { "value" => "Extra care housing" }, "38" => { "value" => "Older people’s housing for tenants with low support needs" }, "6" => { "value" => "Other supported housing" }, - "3" => { "value" => "Private sector tenancy" }, "27" => { "value" => "Owner occupation (low-cost home ownership)" }, "26" => { "value" => "Owner occupation (private)" }, + "3" => { "value" => "Private sector tenancy" }, "28" => { "value" => "Living with friends or family (long-term)" }, "39" => { "value" => "Sofa surfing (moving regularly between family or friends, no permanent bed)" }, "14" => { "value" => "Bed and breakfast" }, "7" => { "value" => "Direct access hostel" }, "10" => { "value" => "Hospital" }, "29" => { "value" => "Prison or approved probation hostel" }, - "19" => { "value" => "Rough sleeping" }, "18" => { "value" => "Any other temporary accommodation" }, + "19" => { "value" => "Rough sleeping" }, + "21" => { "value" => "Refuge" }, "13" => { "value" => "Children’s home or foster care" }, "24" => { "value" => "Home Office Asylum Support" }, "23" => { "value" => "Mobile home or caravan" }, - "21" => { "value" => "Refuge" }, "9" => { "value" => "Residential care home" }, "4" => { "value" => "Tied housing or rented with job" }, "37" => { "value" => "Host family or similar refugee accommodation" }, From 3305e3017bb07d70cdefd53044978f91503b2a83 Mon Sep 17 00:00:00 2001 From: Samuel Young Date: Fri, 20 Mar 2026 13:47:42 +0000 Subject: [PATCH 02/19] CLDC-4306: Correct sales still serving options for 2026 (#3248) --- .../sales/questions/buyer_still_serving.rb | 26 +++++++++---- .../sales/pages/buyer_still_serving_spec.rb | 4 +- .../questions/buyer_still_serving_spec.rb | 37 ++++++++++++++----- 3 files changed, 49 insertions(+), 18 deletions(-) diff --git a/app/models/form/sales/questions/buyer_still_serving.rb b/app/models/form/sales/questions/buyer_still_serving.rb index 3f2d9d8d9..876cc91af 100644 --- a/app/models/form/sales/questions/buyer_still_serving.rb +++ b/app/models/form/sales/questions/buyer_still_serving.rb @@ -3,17 +3,27 @@ class Form::Sales::Questions::BuyerStillServing < ::Form::Question super @id = "hhregresstill" @type = "radio" - @answer_options = ANSWER_OPTIONS + @answer_options = answer_options @question_number = get_question_number_from_hash(QUESTION_NUMBER_FROM_YEAR) end - ANSWER_OPTIONS = { - "4" => { "value" => "Yes" }, - "5" => { "value" => "No" }, - "6" => { "value" => "Buyer prefers not to say" }, - "divider" => { "value" => true }, - "7" => { "value" => "Don’t know" }, - }.freeze + def answer_options + if form.start_year_2026_or_later? + { + "4" => { "value" => "Yes" }, + "5" => { "value" => "No - they left up to and including 2 years ago" }, + "6" => { "value" => "No - they left more than 2 years ago" }, + }.freeze + else + { + "4" => { "value" => "Yes" }, + "5" => { "value" => "No" }, + "6" => { "value" => "Buyer prefers not to say" }, + "divider" => { "value" => true }, + "7" => { "value" => "Don’t know" }, + }.freeze + end + end QUESTION_NUMBER_FROM_YEAR = { 2023 => 63, 2024 => 65, 2025 => 62, 2026 => 70 }.freeze end diff --git a/spec/models/form/sales/pages/buyer_still_serving_spec.rb b/spec/models/form/sales/pages/buyer_still_serving_spec.rb index af000539d..83fc7d0db 100644 --- a/spec/models/form/sales/pages/buyer_still_serving_spec.rb +++ b/spec/models/form/sales/pages/buyer_still_serving_spec.rb @@ -1,11 +1,13 @@ require "rails_helper" RSpec.describe Form::Sales::Pages::BuyerStillServing, type: :model do + include CollectionTimeHelper + subject(:page) { described_class.new(page_id, page_definition, subsection) } let(:page_id) { nil } let(:page_definition) { nil } - let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1))) } + let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: current_collection_start_date, start_year_2026_or_later?: true)) } it "has correct subsection" do expect(page.subsection).to eq(subsection) diff --git a/spec/models/form/sales/questions/buyer_still_serving_spec.rb b/spec/models/form/sales/questions/buyer_still_serving_spec.rb index fc4893db4..137c95d4c 100644 --- a/spec/models/form/sales/questions/buyer_still_serving_spec.rb +++ b/spec/models/form/sales/questions/buyer_still_serving_spec.rb @@ -1,11 +1,14 @@ require "rails_helper" RSpec.describe Form::Sales::Questions::BuyerStillServing, type: :model do + include CollectionTimeHelper + subject(:question) { described_class.new(question_id, question_definition, page) } let(:question_id) { nil } let(:question_definition) { nil } - let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1)))) } + let(:start_year_2026_or_later?) { true } + let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: current_collection_start_date, start_year_2026_or_later?: start_year_2026_or_later?))) } it "has correct page" do expect(question.page).to eq(page) @@ -23,13 +26,29 @@ RSpec.describe Form::Sales::Questions::BuyerStillServing, type: :model do expect(question.derived?(nil)).to be false end - it "has the correct answer_options" do - expect(question.answer_options).to eq({ - "4" => { "value" => "Yes" }, - "5" => { "value" => "No" }, - "6" => { "value" => "Buyer prefers not to say" }, - "divider" => { "value" => true }, - "7" => { "value" => "Don’t know" }, - }) + context "when 2025", metadata: { year: 25 } do + let(:start_year_2026_or_later?) { false } + + it "has the correct answer_options" do + expect(question.answer_options).to eq({ + "4" => { "value" => "Yes" }, + "5" => { "value" => "No" }, + "6" => { "value" => "Buyer prefers not to say" }, + "divider" => { "value" => true }, + "7" => { "value" => "Don’t know" }, + }) + end + end + + context "when 2026", metadata: { year: 26 } do + let(:start_year_2026_or_later?) { true } + + it "has the correct answer_options" do + expect(question.answer_options).to eq({ + "4" => { "value" => "Yes" }, + "5" => { "value" => "No - they left up to and including 2 years ago" }, + "6" => { "value" => "No - they left more than 2 years ago" }, + }) + end end end From a4b87955230835b9daed8188df5a42b49f342c52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2026 14:15:59 +0000 Subject: [PATCH 03/19] Bump json from 2.18.1 to 2.19.2 in /docs (#3254) Bumps [json](https://github.com/ruby/json) from 2.18.1 to 2.19.2. - [Release notes](https://github.com/ruby/json/releases) - [Changelog](https://github.com/ruby/json/blob/master/CHANGES.md) - [Commits](https://github.com/ruby/json/compare/v2.18.1...v2.19.2) --- updated-dependencies: - dependency-name: json dependency-version: 2.19.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 5a10a9f23..9e014a8bd 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -198,7 +198,7 @@ GEM gemoji (~> 3.0) html-pipeline (~> 2.2) jekyll (>= 3.0, < 5.0) - json (2.18.1) + json (2.19.2) kramdown (2.3.2) rexml kramdown-parser-gfm (1.1.0) From 749e69d5ea8b7db10a99b4ca821911df8ed50e15 Mon Sep 17 00:00:00 2001 From: Samuel Young Date: Fri, 20 Mar 2026 17:31:37 +0000 Subject: [PATCH 04/19] CLDC-4239: Widen age range for pregnancy soft validation (#3192) * CLDC-4239: Update age range for pregnancy soft validation for 2026 * CLDC-4239: Update tests looks like the 2026 tests were testing the 2025 method, so have updated * fixup! CLDC-4239: Update tests * fixup! CLDC-4239: Update tests add boundary cases for 2025 * fixup! CLDC-4239: Update tests make 8 household memmbers clear --- app/models/validations/soft_validations.rb | 9 +- .../validations/soft_validations_spec.rb | 116 ++++++++++++++---- 2 files changed, 101 insertions(+), 24 deletions(-) diff --git a/app/models/validations/soft_validations.rb b/app/models/validations/soft_validations.rb index ea17d0601..0ff08afb1 100644 --- a/app/models/validations/soft_validations.rb +++ b/app/models/validations/soft_validations.rb @@ -76,7 +76,8 @@ module Validations::SoftValidations end def no_household_member_likely_to_be_pregnant? - all_male_tenants_in_a_pregnant_household? || non_males_in_pregnant_household_not_in_pregnancy_range? + # in this 2026 check we used a wider age range + all_male_tenants_in_a_pregnant_household? || non_males_in_pregnant_household_not_in_range?(13, 55) end def all_male_tenants_in_a_pregnant_household? @@ -84,7 +85,7 @@ module Validations::SoftValidations end def non_males_in_pregnant_household_not_in_pregnancy_range? - all_tenants_age_and_gender_information_completed? && non_males_in_the_household? && !any_non_male_in_expected_pregnancy_age_range(16, 50) && preg_occ == 1 + non_males_in_pregnant_household_not_in_range?(16, 50) end TWO_YEARS_IN_DAYS = 730 @@ -252,6 +253,10 @@ private public_send("details_known_#{tenant_number}").zero? end + def non_males_in_pregnant_household_not_in_range?(min, max) + all_tenants_age_and_gender_information_completed? && non_males_in_the_household? && !any_non_male_in_expected_pregnancy_age_range(min, max) && preg_occ == 1 + end + def any_non_male_in_expected_pregnancy_age_range(min, max) person_count = hhmemb || 8 diff --git a/spec/models/validations/soft_validations_spec.rb b/spec/models/validations/soft_validations_spec.rb index 9026b03dc..4b0ac6711 100644 --- a/spec/models/validations/soft_validations_spec.rb +++ b/spec/models/validations/soft_validations_spec.rb @@ -180,7 +180,7 @@ RSpec.describe Validations::SoftValidations do end end - context "when all tenants are male and household members are over 8" do + context "when all tenants are male and more than 8 household members" do it "does not show the interruption screen" do (1..8).each do |n| record.send("sex#{n}=", "M") @@ -196,7 +196,7 @@ RSpec.describe Validations::SoftValidations do context "when female tenants are under 16" do it "shows the interruption screen" do - record.age2 = 14 + record.age2 = 15 record.sex2 = "F" record.preg_occ = 1 record.hhmemb = 2 @@ -209,9 +209,20 @@ RSpec.describe Validations::SoftValidations do end end + context "when female tenants are 16 and over" do + it "does not show the interruption screen" do + record.age1 = 16 + record.sex1 = "F" + record.preg_occ = 1 + record.hhmemb = 1 + record.age1_known = 0 + expect(record.non_males_in_pregnant_household_not_in_pregnancy_range?).to be false + end + end + context "when female tenants are over 50" do it "shows the interruption screen" do - record.age1 = 54 + record.age1 = 51 record.sex1 = "F" record.preg_occ = 1 record.hhmemb = 1 @@ -220,9 +231,20 @@ RSpec.describe Validations::SoftValidations do end end + context "when female tenants are 50 or under" do + it "shows the interruption screen" do + record.age1 = 50 + record.sex1 = "F" + record.preg_occ = 1 + record.hhmemb = 1 + record.age1_known = 0 + expect(record.non_males_in_pregnant_household_not_in_pregnancy_range?).to be false + end + end + context "when non-binary tenants are under 16" do it "does not show the interruption screen" do - record.age2 = 14 + record.age2 = 15 record.sex2 = "X" record.preg_occ = 1 record.hhmemb = 2 @@ -237,7 +259,7 @@ RSpec.describe Validations::SoftValidations do context "when non-binary tenants are over 50" do it "does not show the interruption screen" do - record.age1 = 54 + record.age1 = 51 record.sex1 = "X" record.preg_occ = 1 record.hhmemb = 1 @@ -298,7 +320,7 @@ RSpec.describe Validations::SoftValidations do end it "shows the interruption screen" do - expect(record.all_male_tenants_in_a_pregnant_household?).to be true + expect(record.no_household_member_likely_to_be_pregnant?).to be true end end @@ -310,11 +332,11 @@ RSpec.describe Validations::SoftValidations do end it "shows the interruption screen" do - expect(record.all_male_tenants_in_a_pregnant_household?).to be true + expect(record.no_household_member_likely_to_be_pregnant?).to be true end end - context "when all tenants are male and household members are over 8" do + context "when all tenants are male and more than 8 household members" do before do (1..8).each do |n| record.send("sexrab#{n}=", "M") @@ -328,11 +350,23 @@ RSpec.describe Validations::SoftValidations do end it "does not show the interruption screen" do - expect(record.all_male_tenants_in_a_pregnant_household?).to be false + expect(record.no_household_member_likely_to_be_pregnant?).to be false end end - context "when female tenants are under 16" do + context "when female tenants are 13 or over" do + before do + record.age1 = 13 + record.sexrab1 = "F" + record.gender_same_as_sex1 = 1 + end + + it "does not show the interruption screen" do + expect(record.no_household_member_likely_to_be_pregnant?).to be false + end + end + + context "when female tenants are under 13" do before do record.age1 = 12 record.sexrab1 = "F" @@ -340,23 +374,48 @@ RSpec.describe Validations::SoftValidations do end it "shows the interruption screen" do - expect(record.non_males_in_pregnant_household_not_in_pregnancy_range?).to be true + expect(record.no_household_member_likely_to_be_pregnant?).to be true end end - context "when female tenants are over 50" do + context "when female tenants are 55 or under" do + before do + record.age1 = 55 + record.sexrab1 = "F" + record.gender_same_as_sex1 = 1 + end + + it "does not show the interruption screen" do + expect(record.no_household_member_likely_to_be_pregnant?).to be false + end + end + + context "when female tenants are over 55" do before do - record.age1 = 60 + record.age1 = 56 record.sexrab1 = "F" record.gender_same_as_sex1 = 1 end it "shows the interruption screen" do - expect(record.non_males_in_pregnant_household_not_in_pregnancy_range?).to be true + expect(record.no_household_member_likely_to_be_pregnant?).to be true + end + end + + context "when non binary tenants are 13 or over" do + before do + record.age1 = 13 + record.sexrab1 = "M" + record.gender_same_as_sex1 = 2 + record.gender_description1 = "Non-binary" + end + + it "does not show the interruption screen" do + expect(record.no_household_member_likely_to_be_pregnant?).to be false end end - context "when non binary tenants are under 16" do + context "when non binary tenants are under 13" do before do record.age1 = 12 record.sexrab1 = "M" @@ -365,20 +424,33 @@ RSpec.describe Validations::SoftValidations do end it "shows the interruption screen" do - expect(record.non_males_in_pregnant_household_not_in_pregnancy_range?).to be true + expect(record.no_household_member_likely_to_be_pregnant?).to be true end end - context "when non binary tenants are over 50" do + context "when non binary tenants are 55 or under" do before do - record.age1 = 60 + record.age1 = 55 + record.sexrab1 = "M" + record.gender_same_as_sex1 = 2 + record.gender_description1 = "Non-binary" + end + + it "does not show the interruption screen" do + expect(record.no_household_member_likely_to_be_pregnant?).to be false + end + end + + context "when non binary tenants are over 55" do + before do + record.age1 = 56 record.sexrab1 = "M" record.gender_same_as_sex1 = 2 record.gender_description1 = "Non-binary" end it "shows the interruption screen" do - expect(record.non_males_in_pregnant_household_not_in_pregnancy_range?).to be true + expect(record.no_household_member_likely_to_be_pregnant?).to be true end end @@ -390,7 +462,7 @@ RSpec.describe Validations::SoftValidations do end it "does not show the interruption screen" do - expect(record.non_males_in_pregnant_household_not_in_pregnancy_range?).to be false + expect(record.no_household_member_likely_to_be_pregnant?).to be false end end @@ -405,7 +477,7 @@ RSpec.describe Validations::SoftValidations do end it "does not show the interruption screen" do - expect(record.non_males_in_pregnant_household_not_in_pregnancy_range?).to be false + expect(record.no_household_member_likely_to_be_pregnant?).to be false end end @@ -423,7 +495,7 @@ RSpec.describe Validations::SoftValidations do end it "does not show the interruption screen" do - expect(record.non_males_in_pregnant_household_not_in_pregnancy_range?).to be false + expect(record.no_household_member_likely_to_be_pregnant?).to be false end end end From dddf661f636fcd6fb3596385cbc25f00a4499256 Mon Sep 17 00:00:00 2001 From: Samuel Young Date: Mon, 23 Mar 2026 10:07:35 +0000 Subject: [PATCH 05/19] CLDC-4157: Remove nursing care from scheme level of support (#3224) * CLDC-4157: Remove nursing care home level of support * CLDC-4157: Add a helper script to mark impacted schemes & logs as incomplete * CLDC-4157: Update scheme factory * CLDC-4157: Remove redundant scheme hint functions * fixup! CLDC-4157: Add a helper script to mark impacted schemes & logs as incomplete correct script name --- app/models/scheme.rb | 6 ++---- app/views/schemes/support.html.erb | 12 ++---------- ...hemes_logs_impacted_by_nursing_level_removal.rake | 10 ++++++++++ spec/factories/scheme.rb | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) create mode 100644 lib/tasks/update_schemes_logs_impacted_by_nursing_level_removal.rake diff --git a/app/models/scheme.rb b/app/models/scheme.rb index cf13ff29d..0aa481a1e 100644 --- a/app/models/scheme.rb +++ b/app/models/scheme.rb @@ -170,7 +170,6 @@ class Scheme < ApplicationRecord "Low level": 2, "Medium level": 3, "High level": 4, - "Nursing care in a care home": 5, "Floating support": 6, }.freeze @@ -265,7 +264,7 @@ class Scheme < ApplicationRecord Scheme.registered_under_care_acts.keys.map { |key, _| OpenStruct.new(id: key, name: key.to_s) } end - def support_level_options_with_hints + def self.support_level_options_with_hints hints = { "Low level": "Staff visiting once a week, fortnightly or less.", "Medium level": "Staff on site daily or making frequent visits with some out-of-hours cover.", @@ -274,13 +273,12 @@ class Scheme < ApplicationRecord Scheme.support_types.keys.excluding("Missing").excluding("Floating support").map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize, description: hints[key.to_sym]) } end - def intended_length_of_stay_options_with_hints + def self.intended_length_of_stay_options_with_hints hints = { "Very short stay": "Up to one month.", "Short stay": "Up to one year.", "Medium stay": "More than one year but with an expectation to move on.", "Permanent": "Provides a home for life with no requirement for the tenant to move.", - } Scheme.intended_stays.keys.excluding("Missing").map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize, description: hints[key.to_sym]) } end diff --git a/app/views/schemes/support.html.erb b/app/views/schemes/support.html.erb index eca72014f..bdf2312d9 100644 --- a/app/views/schemes/support.html.erb +++ b/app/views/schemes/support.html.erb @@ -12,23 +12,15 @@ <%= render partial: "organisations/headings", locals: { main: "What support does this scheme provide?", sub: @scheme.service_name } %> <%= govuk_inset_text(text: "Only update a scheme if you’re fixing an error. If the scheme is changing, create a new scheme.") if @scheme.confirmed? %> - <% support_level_options_hints = { "Low level": "Staff visiting once a week, fortnightly or less.", "Medium level": "Staff on site daily or making frequent visits with some out-of-hours cover.", "High level": "Intensive level of staffing provided on a 24-hour basis." } %> - - <% support_level_options_with_hints = Scheme.support_types.keys.excluding("Missing").excluding("Floating support").map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize, description: support_level_options_hints[key.to_sym]) } %> - <%= f.govuk_collection_radio_buttons :support_type, - support_level_options_with_hints, + Scheme.support_level_options_with_hints, :id, :name, :description, legend: { text: "Level of support given", size: "m" } %> - <% intended_length_of_stay_options_hints = { "Very short stay": "Up to one month.", "Short stay": "Up to one year.", "Medium stay": "More than one year but with an expectation to move on.", "Permanent": "Provides a home for life with no requirement for the tenant to move." } %> - - <% intended_length_of_stay_options_with_hints = Scheme.intended_stays.keys.excluding("Missing").map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize, description: intended_length_of_stay_options_hints[key.to_sym]) } %> - <%= f.govuk_collection_radio_buttons :intended_stay, - intended_length_of_stay_options_with_hints, + Scheme.intended_length_of_stay_options_with_hints, :id, :name, :description, diff --git a/lib/tasks/update_schemes_logs_impacted_by_nursing_level_removal.rake b/lib/tasks/update_schemes_logs_impacted_by_nursing_level_removal.rake new file mode 100644 index 000000000..cd906a4d0 --- /dev/null +++ b/lib/tasks/update_schemes_logs_impacted_by_nursing_level_removal.rake @@ -0,0 +1,10 @@ +desc "Update schemes that had a level of support of 'Nursing' to be incomplete, and mark all logs that use that scheme as incomplete" +task update_schemes_logs_impacted_by_nursing_level_removal: :environment do + ActiveRecord::Base.transaction do + impacted_schemes = Scheme.where(support_type: 5) + impacted_logs = LettingsLog.filter_by_year_or_later(2025).where(scheme: impacted_schemes) + + impacted_schemes.update!(support_type: nil, confirmed: false) + impacted_logs.update!(scheme: nil) + end +end diff --git a/spec/factories/scheme.rb b/spec/factories/scheme.rb index e7ecc8b60..a882fd9ea 100644 --- a/spec/factories/scheme.rb +++ b/spec/factories/scheme.rb @@ -3,7 +3,7 @@ FactoryBot.define do service_name { "#{Faker::Name.name}'s Housing & Co." } sensitive { Faker::Number.within(range: 0..1) } registered_under_care_act { 1 } - support_type { [0, 2, 3, 4, 5].sample } + support_type { [0, 2, 3, 4].sample } scheme_type { 4 } arrangement_type { "D" } intended_stay { %w[M P S V X].sample } From ea74e31a64d9d35c91d456514ad458a703f2b7bb Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis <94526761+natdeanlewissoftwire@users.noreply.github.com> Date: Mon, 23 Mar 2026 10:59:44 +0000 Subject: [PATCH 06/19] CLDC-4152: update guidance copy (#3252) --- app/views/start/guidance.html.erb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/start/guidance.html.erb b/app/views/start/guidance.html.erb index ff6436fb0..98ec08764 100644 --- a/app/views/start/guidance.html.erb +++ b/app/views/start/guidance.html.erb @@ -26,6 +26,7 @@ <%= govuk_list [ "Tenants in general needs housing allocated a new letting. This includes tenants moving into the social rented sector from outside, existing social tenants moving between properties or landlords, and existing social tenants renewing lettings in the same property. If fixed-term and social or affordable rent, only include tenancies of 2 years or more.", "Tenants in supported housing (social housing and sheltered accommodation) allocated a new letting. This includes tenants moving into the social rented sector from outside, existing social tenants moving between properties or landlords, and existing social tenants renewing lettings in the same property. All supported housing tenancies should be reported regardless of length.", + "Tenancies in leased properties being let on social housing terms, regardless of whether the property had previously been used as social stock.", "Starter tenancies provided by local authorities (LAs) and lettings with an introductory period provided by private registered providers (PRPs) should be completed in CORE at the beginning of the starter or introductory period. The tenancy type and length entered should be based on the tenancy the tenant will roll onto once the starter or introductory period has been completed. You do not need to submit another CORE log once the period has been completed.", "Room moves within a shared housing unit that result in a different property type or support needs – this is classed as an internal transfer of an existing social tenant to another property.", "Existing tenants who are issued with a new tenancy agreement when stock is acquired, transferred or permanently decanted.", From 9e36fe7d34723405323791e9ebd1028c5e7f0f56 Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis <94526761+natdeanlewissoftwire@users.noreply.github.com> Date: Mon, 23 Mar 2026 11:28:44 +0000 Subject: [PATCH 07/19] CLDC-4314: Monthly service charge soft validation redirect bug (#3249) * CLDC-4314: set unique urls for shared ownership soft validations * CLDC-4314: update specs --- .../sales/subsections/shared_ownership_initial_purchase.rb | 2 +- .../subsections/shared_ownership_staircasing_transaction.rb | 2 +- .../subsections/shared_ownership_initial_purchase_spec.rb | 6 +++--- .../shared_ownership_staircasing_transaction_spec.rb | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/models/form/sales/subsections/shared_ownership_initial_purchase.rb b/app/models/form/sales/subsections/shared_ownership_initial_purchase.rb index 7ac9c0543..462f4b14c 100644 --- a/app/models/form/sales/subsections/shared_ownership_initial_purchase.rb +++ b/app/models/form/sales/subsections/shared_ownership_initial_purchase.rb @@ -40,7 +40,7 @@ class Form::Sales::Subsections::SharedOwnershipInitialPurchase < ::Form::Subsect Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_deposit_value_check", nil, self), Form::Sales::Pages::MonthlyRent.new(nil, nil, self), Form::Sales::Pages::ServiceCharge.new("service_charge", nil, self), - Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_shared_ownership_value_check", nil, self), + Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_initial_purchase_value_check", nil, self), Form::Sales::Pages::EstateManagementFee.new("estate_management_fee", nil, self), ].compact end diff --git a/app/models/form/sales/subsections/shared_ownership_staircasing_transaction.rb b/app/models/form/sales/subsections/shared_ownership_staircasing_transaction.rb index 1af25a140..0ff054f18 100644 --- a/app/models/form/sales/subsections/shared_ownership_staircasing_transaction.rb +++ b/app/models/form/sales/subsections/shared_ownership_staircasing_transaction.rb @@ -27,7 +27,7 @@ class Form::Sales::Subsections::SharedOwnershipStaircasingTransaction < ::Form:: Form::Sales::Pages::MonthlyRentStaircasing.new(nil, nil, self), (Form::Sales::Pages::ServiceChargeStaircasing.new("service_charge_staircasing", nil, self) if form.start_year_2026_or_later?), (Form::Sales::Pages::ServiceChargeChanged.new(nil, nil, self) if form.start_year_2026_or_later?), - Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_shared_ownership_value_check", nil, self), + Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_staircasing_value_check", nil, self), ].compact end diff --git a/spec/models/form/sales/subsections/shared_ownership_initial_purchase_spec.rb b/spec/models/form/sales/subsections/shared_ownership_initial_purchase_spec.rb index 984247af6..ce154af3d 100644 --- a/spec/models/form/sales/subsections/shared_ownership_initial_purchase_spec.rb +++ b/spec/models/form/sales/subsections/shared_ownership_initial_purchase_spec.rb @@ -59,7 +59,7 @@ RSpec.describe Form::Sales::Subsections::SharedOwnershipInitialPurchase, type: : shared_ownership_deposit_value_check monthly_rent service_charge - monthly_charges_shared_ownership_value_check + monthly_charges_initial_purchase_value_check estate_management_fee ], ) @@ -102,7 +102,7 @@ RSpec.describe Form::Sales::Subsections::SharedOwnershipInitialPurchase, type: : shared_ownership_deposit_value_check monthly_rent service_charge - monthly_charges_shared_ownership_value_check + monthly_charges_initial_purchase_value_check estate_management_fee ], ) @@ -145,7 +145,7 @@ RSpec.describe Form::Sales::Subsections::SharedOwnershipInitialPurchase, type: : shared_ownership_deposit_value_check monthly_rent service_charge - monthly_charges_shared_ownership_value_check + monthly_charges_initial_purchase_value_check estate_management_fee ], ) diff --git a/spec/models/form/sales/subsections/shared_ownership_staircasing_transaction_spec.rb b/spec/models/form/sales/subsections/shared_ownership_staircasing_transaction_spec.rb index cc811682b..e70e4b613 100644 --- a/spec/models/form/sales/subsections/shared_ownership_staircasing_transaction_spec.rb +++ b/spec/models/form/sales/subsections/shared_ownership_staircasing_transaction_spec.rb @@ -48,7 +48,7 @@ RSpec.describe Form::Sales::Subsections::SharedOwnershipStaircasingTransaction, staircase_mortgage_used_shared_ownership monthly_rent_staircasing_owned monthly_rent_staircasing - monthly_charges_shared_ownership_value_check + monthly_charges_staircasing_value_check ], ) end @@ -78,7 +78,7 @@ RSpec.describe Form::Sales::Subsections::SharedOwnershipStaircasingTransaction, monthly_rent_staircasing service_charge_staircasing service_charge_changed - monthly_charges_shared_ownership_value_check + monthly_charges_staircasing_value_check ], ) end From 4aae303e099e6999d5155585b051457226ae3253 Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis <94526761+natdeanlewissoftwire@users.noreply.github.com> Date: Mon, 23 Mar 2026 11:55:13 +0000 Subject: [PATCH 08/19] CLDC-4290: filter address responses to England only (#3256) * CLDC-4290: filter address responses to england only * CLDC-4290: update test stubs * CLDC-4290: update other stubbed urls --- app/services/address_client.rb | 1 + app/services/uprn_client.rb | 1 + .../lettings/questions/address_search_spec.rb | 2 +- .../sales/questions/address_search_spec.rb | 2 +- spec/request_helper.rb | 20 +++++++++---------- .../address_search_controller_spec.rb | 20 +++++++++---------- spec/services/address_client_spec.rb | 2 +- .../lettings_log_export_service_spec.rb | 2 +- spec/services/uprn_client_spec.rb | 2 +- 9 files changed, 27 insertions(+), 25 deletions(-) diff --git a/app/services/address_client.rb b/app/services/address_client.rb index 3fca0a0d0..2d5289eb0 100644 --- a/app/services/address_client.rb +++ b/app/services/address_client.rb @@ -46,6 +46,7 @@ private key: ENV["OS_DATA_KEY"], maxresults: @options[:maxresults] || 10, minmatch: @options[:minmatch] || 0.4, + fq: ["COUNTRY_CODE:E"], } uri.query = URI.encode_www_form(params) uri.to_s diff --git a/app/services/uprn_client.rb b/app/services/uprn_client.rb index 8dcd2e7a0..b61bc1ec3 100644 --- a/app/services/uprn_client.rb +++ b/app/services/uprn_client.rb @@ -49,6 +49,7 @@ private uprn:, key: ENV["OS_DATA_KEY"], dataset: %w[DPA LPI].join(","), + fq: ["COUNTRY_CODE:E"], } uri.query = URI.encode_www_form(params) uri.to_s diff --git a/spec/models/form/lettings/questions/address_search_spec.rb b/spec/models/form/lettings/questions/address_search_spec.rb index 8635aa017..256531a65 100644 --- a/spec/models/form/lettings/questions/address_search_spec.rb +++ b/spec/models/form/lettings/questions/address_search_spec.rb @@ -48,7 +48,7 @@ RSpec.describe Form::Lettings::Questions::AddressSearch, type: :model do ], }.to_json - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=123") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&uprn=123") .to_return(status: 200, body:, headers: {}) end diff --git a/spec/models/form/sales/questions/address_search_spec.rb b/spec/models/form/sales/questions/address_search_spec.rb index a2a2b51c9..e8dfb9038 100644 --- a/spec/models/form/sales/questions/address_search_spec.rb +++ b/spec/models/form/sales/questions/address_search_spec.rb @@ -48,7 +48,7 @@ RSpec.describe Form::Sales::Questions::AddressSearch, type: :model do ], }.to_json - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=123") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&uprn=123") .to_return(status: 200, body:, headers: {}) end diff --git a/spec/request_helper.rb b/spec/request_helper.rb index 5b867ecde..ac6e2659d 100644 --- a/spec/request_helper.rb +++ b/spec/request_helper.rb @@ -18,14 +18,14 @@ module RequestHelper .to_return(status: 200, body: "{\"status\":200,\"result\":{\"postcode\":\"ZZ1 1ZZ\",\"admin_district\":\"Westminster\",\"codes\":{\"admin_district\":\"E09000033\"}}}", headers: {}) body = { results: [{ DPA: { UPRN: "10033558653" } }] }.to_json - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?key&maxresults=10&minmatch=0.4&query=Address%20line%201,%20SW1A%201AA") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?fq=COUNTRY_CODE%3AE&key&maxresults=10&minmatch=0.4&query=Address%20line%201,%20SW1A%201AA") .to_return(status: 200, body:, headers: {}) body = { results: [{ DPA: { "POSTCODE": "SW1A 1AA", "POST_TOWN": "London", "PO_BOX_NUMBER": "The Mall, City Of Westminster" } }] }.to_json - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key&uprn=1") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&fq=COUNTRY_CODE%3AE&key&uprn=1") .to_return(status: 200, body:, headers: {}) - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key&uprn=10033558653") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&fq=COUNTRY_CODE%3AE&key&uprn=10033558653") .to_return(status: 200, body:, headers: {}) - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=10033558653") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&uprn=10033558653") .to_return(status: 200, body:, headers: {}) WebMock.stub_request(:post, /api.notifications.service.gov.uk\/v2\/notifications\/email/) @@ -45,7 +45,7 @@ module RequestHelper ], }.to_json - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=1") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&uprn=1") .to_return(status: 200, body:, headers: {}) body = { @@ -61,7 +61,7 @@ module RequestHelper ], }.to_json - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key&uprn=121") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&fq=COUNTRY_CODE%3AE&key&uprn=121") .to_return(status: 200, body:, headers: {}) body = { @@ -76,7 +76,7 @@ module RequestHelper ], }.to_json - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=123") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&uprn=123") .to_return(status: 200, body:, headers: {}) body = { @@ -91,13 +91,13 @@ module RequestHelper ], }.to_json - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=12") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&uprn=12") .to_return(status: 200, body:, headers: {}) - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=1234567890123") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&uprn=1234567890123") .to_return(status: 404, body: "", headers: {}) - template = Addressable::Template.new "https://api.os.uk/search/places/v1/find?key=OS_DATA_KEY&maxresults=10&minmatch=0.4&query={+address_query}" + template = Addressable::Template.new "https://api.os.uk/search/places/v1/find?fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&maxresults=10&minmatch=0.4&query={+address_query}" WebMock.stub_request(:get, template) .to_return do |request| address = request.uri.query_values["query"].split(",") diff --git a/spec/requests/address_search_controller_spec.rb b/spec/requests/address_search_controller_spec.rb index a9fff4ace..9287689fe 100644 --- a/spec/requests/address_search_controller_spec.rb +++ b/spec/requests/address_search_controller_spec.rb @@ -152,9 +152,9 @@ RSpec.describe AddressSearchController, type: :request do before do body = { results: [{ DPA: { "ADDRESS": "100, Test Street", "UPRN": "100" } }] }.to_json uprn_body = { results: [{ DPA: nil }] }.to_json - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?key=OS_DATA_KEY&maxresults=10&minmatch=0.2&query=100") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&maxresults=10&minmatch=0.2&query=100") .to_return(status: 200, body:, headers: {}) - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=100") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&uprn=100") .to_return(status: 200, body: uprn_body, headers: {}) end @@ -170,9 +170,9 @@ RSpec.describe AddressSearchController, type: :request do before do body = { results: [{ DPA: nil }] }.to_json uprn_body = { results: [{ DPA: { "ADDRESS": "321, Test Street", UPRN: "321" } }] }.to_json - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?key=OS_DATA_KEY&maxresults=10&minmatch=0.2&query=321") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&maxresults=10&minmatch=0.2&query=321") .to_return(status: 200, body:, headers: {}) - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=321") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&uprn=321") .to_return(status: 200, body: uprn_body, headers: {}) end @@ -199,9 +199,9 @@ RSpec.describe AddressSearchController, type: :request do before do address_body = { results: [{ DPA: { "ADDRESS": "Path not taken", UPRN: "111" } }] }.to_json uprn_body = { results: [{ DPA: { "ADDRESS": "2, Test Street", UPRN: "123456" } }] }.to_json - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?key=OS_DATA_KEY&maxresults=10&minmatch=0.2&query=123456") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&maxresults=10&minmatch=0.2&query=123456") .to_return(status: 200, body: address_body, headers: {}) - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=123456") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&uprn=123456") .to_return(status: 200, body: uprn_body, headers: {}) end @@ -217,9 +217,9 @@ RSpec.describe AddressSearchController, type: :request do before do address_body = { results: [{ DPA: { "ADDRESS": "70, Test Street", UPRN: "123777" } }] }.to_json uprn_body = { results: [{ DPA: { "ADDRESS": "Path not taken", UPRN: "111" } }] }.to_json - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?key=OS_DATA_KEY&maxresults=10&minmatch=0.2&query=70,") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&maxresults=10&minmatch=0.2&query=70,") .to_return(status: 200, body: address_body, headers: {}) - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=70,") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&uprn=70,") .to_return(status: 200, body: uprn_body, headers: {}) end @@ -235,9 +235,9 @@ RSpec.describe AddressSearchController, type: :request do before do address_body = { results: [{ DPA: { "ADDRESS": "111, Test Street", UPRN: "123777" } }] }.to_json uprn_body = { results: [{ DPA: { "ADDRESS": "70 Bean Road", UPRN: "111" } }] }.to_json - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?key=OS_DATA_KEY&maxresults=10&minmatch=0.2&query=111") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&maxresults=10&minmatch=0.2&query=111") .to_return(status: 200, body: address_body, headers: {}) - WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=111") + WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&uprn=111") .to_return(status: 200, body: uprn_body, headers: {}) end diff --git a/spec/services/address_client_spec.rb b/spec/services/address_client_spec.rb index 0a222f0c7..cdfa6d672 100644 --- a/spec/services/address_client_spec.rb +++ b/spec/services/address_client_spec.rb @@ -8,7 +8,7 @@ describe AddressClient do end def stub_api_request(body:, status: 200) - stub_request(:get, "https://api.os.uk/search/places/v1/find?key=OS_DATA_KEY&maxresults=10&minmatch=0.4&query=123") + stub_request(:get, "https://api.os.uk/search/places/v1/find?fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&maxresults=10&minmatch=0.4&query=123") .to_return(status:, body:, headers: {}) end diff --git a/spec/services/exports/lettings_log_export_service_spec.rb b/spec/services/exports/lettings_log_export_service_spec.rb index 53dd0fd0f..335617aec 100644 --- a/spec/services/exports/lettings_log_export_service_spec.rb +++ b/spec/services/exports/lettings_log_export_service_spec.rb @@ -158,7 +158,7 @@ RSpec.describe Exports::LettingsLogExportService do before do Timecop.freeze(start_time) Singleton.__init__(FormHandler) - stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=100023336956") + stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&uprn=100023336956") .to_return(status: 200, body: '{"status":200,"results":[{"DPA":{ "PO_BOX_NUMBER": "fake", "ORGANISATION_NAME": "org", diff --git a/spec/services/uprn_client_spec.rb b/spec/services/uprn_client_spec.rb index 8d5c45ed9..f574be20f 100644 --- a/spec/services/uprn_client_spec.rb +++ b/spec/services/uprn_client_spec.rb @@ -8,7 +8,7 @@ describe UprnClient do end def stub_api_request(body:, status: 200) - stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=123") + stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA%2CLPI&fq=COUNTRY_CODE%3AE&key=OS_DATA_KEY&uprn=123") .to_return(status:, body:, headers: {}) end From 9600112cdab8a46035230fb3f5c90ff05d7d4624 Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis <94526761+natdeanlewissoftwire@users.noreply.github.com> Date: Mon, 23 Mar 2026 11:57:39 +0000 Subject: [PATCH 09/19] CLDC-4318: update min for 26 onwards and test (#3257) --- .../questions/living_before_purchase_years.rb | 2 +- .../living_before_purchase_years_spec.rb | 91 +++++++------------ 2 files changed, 34 insertions(+), 59 deletions(-) diff --git a/app/models/form/sales/questions/living_before_purchase_years.rb b/app/models/form/sales/questions/living_before_purchase_years.rb index 30ff5fe8a..1e554e3fb 100644 --- a/app/models/form/sales/questions/living_before_purchase_years.rb +++ b/app/models/form/sales/questions/living_before_purchase_years.rb @@ -4,7 +4,7 @@ class Form::Sales::Questions::LivingBeforePurchaseYears < ::Form::Question @id = "proplen" @copy_key = "sales.sale_information.living_before_purchase.#{joint_purchase ? 'joint_purchase' : 'not_joint_purchase'}.proplen" @type = "numeric" - @min = 0 + @min = form.start_year_2026_or_later? ? 1 : 0 @max = 80 @step = 1 @width = 5 diff --git a/spec/models/form/sales/questions/living_before_purchase_years_spec.rb b/spec/models/form/sales/questions/living_before_purchase_years_spec.rb index e16a70518..04be7f00a 100644 --- a/spec/models/form/sales/questions/living_before_purchase_years_spec.rb +++ b/spec/models/form/sales/questions/living_before_purchase_years_spec.rb @@ -1,83 +1,58 @@ require "rails_helper" RSpec.describe Form::Sales::Questions::LivingBeforePurchaseYears, type: :model do + include CollectionTimeHelper + subject(:question) { described_class.new(question_id, question_definition, page, ownershipsch: 1, joint_purchase: true) } let(:question_id) { nil } let(:question_definition) { nil } - let(:start_date) { Time.utc(2024, 2, 8) } - let(:subsection) { instance_double(Form::Subsection, id: "shared_ownership_initial_purchase", form: instance_double(Form, start_date:, start_year_2026_or_later?: false)) } - let(:page) { instance_double(Form::Page, subsection:) } + let(:start_year) { current_collection_start_year } + let(:start_year_2026_or_later?) { false } + let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, id: "shared_ownership_initial_purchase", form: instance_double(Form, start_date: collection_start_date_for_year(start_year), start_year_2026_or_later?: start_year_2026_or_later?))) } - context "when 2022" do - let(:start_date) { Time.utc(2022, 2, 8) } + it "has correct page" do + expect(question.page).to eq(page) + end - it "has correct page" do - expect(question.page).to eq(page) - end + it "has the correct id" do + expect(question.id).to eq("proplen") + end - it "has the correct id" do - expect(question.id).to eq("proplen") - end + it "has the correct type" do + expect(question.type).to eq("numeric") + end - it "has the correct type" do - expect(question.type).to eq("numeric") - end + it "is not marked as derived" do + expect(question.derived?(nil)).to be false + end - it "is not marked as derived" do - expect(question.derived?(nil)).to be false - end + it "has correct width" do + expect(question.width).to eq(5) + end - it "has correct width" do - expect(question.width).to eq(5) - end + it "has correct step" do + expect(question.step).to eq(1) + end - it "has correct step" do - expect(question.step).to eq(1) - end + it "has correct max" do + expect(question.max).to eq(80) + end + + context "with year 2025", metadata: { year: 25 } do + let(:start_year) { 2025 } it "has correct min" do expect(question.min).to eq(0) end - - it "has correct max" do - expect(question.max).to eq(80) - end end - context "when 2023" do - let(:start_date) { Time.utc(2023, 2, 8) } - - it "has correct page" do - expect(question.page).to eq(page) - end - - it "has the correct id" do - expect(question.id).to eq("proplen") - end - - it "has the correct type" do - expect(question.type).to eq("numeric") - end - - it "is not marked as derived" do - expect(question.derived?(nil)).to be false - end - - it "has correct width" do - expect(question.width).to eq(5) - end - - it "has correct step" do - expect(question.step).to eq(1) - end + context "with year 2026", metadata: { year: 26 } do + let(:start_year) { 2026 } + let(:start_year_2026_or_later?) { true } it "has correct min" do - expect(question.min).to eq(0) - end - - it "has correct max" do - expect(question.max).to eq(80) + expect(question.min).to eq(1) end end From d66771e5918b32108f92b7966d8621a685f6fecc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 12:06:34 +0000 Subject: [PATCH 10/19] Bump bcrypt from 3.1.21 to 3.1.22 (#3258) Bumps [bcrypt](https://github.com/bcrypt-ruby/bcrypt-ruby) from 3.1.21 to 3.1.22. - [Release notes](https://github.com/bcrypt-ruby/bcrypt-ruby/releases) - [Changelog](https://github.com/bcrypt-ruby/bcrypt-ruby/blob/master/CHANGELOG) - [Commits](https://github.com/bcrypt-ruby/bcrypt-ruby/compare/v3.1.21...v3.1.22) --- updated-dependencies: - dependency-name: bcrypt dependency-version: 3.1.22 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index e261730d1..e87181aa4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -112,7 +112,7 @@ GEM ice_nine (~> 0.11.0) thread_safe (~> 0.3, >= 0.3.1) base64 (0.3.0) - bcrypt (3.1.21) + bcrypt (3.1.22) benchmark (0.5.0) better_html (2.2.0) actionview (>= 7.0) From 319af27e75f512e711f948874d8ed1830c66260d Mon Sep 17 00:00:00 2001 From: Samuel Young Date: Mon, 23 Mar 2026 12:26:54 +0000 Subject: [PATCH 11/19] CLDC-4215: Don't run validate_discounted_ownership_value if mortgageused is 3 (#3239) * CLDC-4215: Don't run validate_discounted_ownership_value if mortgageused is 3 I don't think this should have ever been turned on. if we don't know the mortgage we can't assume info in this check * CLDC-4215: Add a verifying test --- .../sales/sale_information_validations.rb | 2 +- .../sales/sale_information_validations_spec.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/models/validations/sales/sale_information_validations.rb b/app/models/validations/sales/sale_information_validations.rb index 9d65bc799..7cce2a590 100644 --- a/app/models/validations/sales/sale_information_validations.rb +++ b/app/models/validations/sales/sale_information_validations.rb @@ -77,7 +77,7 @@ module Validations::Sales::SaleInformationValidations def validate_discounted_ownership_value(record) return unless record.saledate && record.form.start_year_2024_or_later? return unless record.value && record.deposit && record.ownershipsch - return unless record.mortgage || record.mortgageused == 2 || record.mortgageused == 3 + return unless record.mortgage || record.mortgageused == 2 return unless record.discount || record.grant || record.type == 29 tolerance = record.value_with_discount_tolerance diff --git a/spec/models/validations/sales/sale_information_validations_spec.rb b/spec/models/validations/sales/sale_information_validations_spec.rb index 7a4537322..4104823f0 100644 --- a/spec/models/validations/sales/sale_information_validations_spec.rb +++ b/spec/models/validations/sales/sale_information_validations_spec.rb @@ -703,6 +703,18 @@ RSpec.describe Validations::Sales::SaleInformationValidations do end end end + + context "when mortgageused is don't know" do + let(:record) { FactoryBot.build(:sales_log, :saledate_today, mortgageused: 3, deposit: 10_000, value: 100_000, discount: 10, ownershipsch: 2, type: 9) } + + it "does not add an error" do + sale_information_validator.validate_discounted_ownership_value(record) + expect(record.errors["mortgage"]).to be_empty + expect(record.errors["value"]).to be_empty + expect(record.errors["deposit"]).to be_empty + expect(record.errors["discount"]).to be_empty + end + end end describe "#validate_outright_sale_value_matches_mortgage_plus_deposit" do From edae85af99c48506bc1b6ec0a5de890b20e52ae1 Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis <94526761+natdeanlewissoftwire@users.noreply.github.com> Date: Mon, 23 Mar 2026 12:27:06 +0000 Subject: [PATCH 12/19] Reapply "CLDC-4246: Starter tenancy type copy updates (#3229)" (#3242) (#3246) This reverts commit 384435fdc64a4ca49ec0c7efb0ddbfdcf5193e72. --- .../2025/lettings/tenancy_information.en.yml | 16 ++++++++-------- .../2026/lettings/tenancy_information.en.yml | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/config/locales/forms/2025/lettings/tenancy_information.en.yml b/config/locales/forms/2025/lettings/tenancy_information.en.yml index fa0fa9089..02d35a2ca 100644 --- a/config/locales/forms/2025/lettings/tenancy_information.en.yml +++ b/config/locales/forms/2025/lettings/tenancy_information.en.yml @@ -21,27 +21,27 @@ en: tenancy_type: page_header: "" tenancy: - check_answer_label: "Type of main tenancy" + check_answer_label: "Type of tenancy" check_answer_prompt: "" hint_text: "" question_text: "What is the type of tenancy?" tenancyother: - check_answer_label: "" + check_answer_label: "Type of tenancy (other)" check_answer_prompt: "" hint_text: "" - question_text: "Please state the tenancy type" + question_text: "Please state the type of tenancy" starter_tenancy_type: page_header: "" tenancy: - check_answer_label: "Type of main tenancy after the starter or introductory period has ended" + check_answer_label: "Type of tenancy" check_answer_prompt: "" - hint_text: "" - question_text: "What is the type of tenancy after the starter or introductory period has ended?" + hint_text: "This is for the main tenancy after any starter or introductory period." + question_text: "What is the type of tenancy?" tenancyother: - check_answer_label: "" + check_answer_label: "Type of tenancy (other)" check_answer_prompt: "" hint_text: "" - question_text: "Please state the tenancy type" + question_text: "Please state the type of tenancy" tenancylength: tenancy_length: diff --git a/config/locales/forms/2026/lettings/tenancy_information.en.yml b/config/locales/forms/2026/lettings/tenancy_information.en.yml index 65f110e93..6a481095a 100644 --- a/config/locales/forms/2026/lettings/tenancy_information.en.yml +++ b/config/locales/forms/2026/lettings/tenancy_information.en.yml @@ -21,27 +21,27 @@ en: tenancy_type: page_header: "" tenancy: - check_answer_label: "Type of main tenancy" + check_answer_label: "Type of tenancy" check_answer_prompt: "" hint_text: "" question_text: "What is the type of tenancy?" tenancyother: - check_answer_label: "" + check_answer_label: "Type of tenancy (other)" check_answer_prompt: "" hint_text: "" - question_text: "Please state the tenancy type" + question_text: "Please state the type of tenancy" starter_tenancy_type: page_header: "" tenancy: - check_answer_label: "Type of main tenancy after the starter or introductory period has ended" + check_answer_label: "Type of tenancy" check_answer_prompt: "" - hint_text: "" - question_text: "What is the type of tenancy after the starter or introductory period has ended?" + hint_text: "This is for the main tenancy after any starter or introductory period." + question_text: "What is the type of tenancy?" tenancyother: - check_answer_label: "" + check_answer_label: "Type of tenancy (other)" check_answer_prompt: "" hint_text: "" - question_text: "Please state the tenancy type" + question_text: "Please state the type of tenancy" tenancylength: tenancy_length: From 328ea6272b3acf58ac6697f8b1fdefdaa90b59c8 Mon Sep 17 00:00:00 2001 From: Samuel Young Date: Mon, 23 Mar 2026 12:27:43 +0000 Subject: [PATCH 13/19] CLDC-4309: Add copy for staircasing transaction mortgage used (#3251) * CLDC-4309: Add copy for staircasing transaction mortgage used * CLDC-4309: Update tests --- .../form/sales/questions/mortgageused.rb | 6 ++++ .../forms/2026/sales/sale_information.en.yml | 14 ++++++--- .../form/sales/questions/mortgageused_spec.rb | 30 +++++++++++++++++-- 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/app/models/form/sales/questions/mortgageused.rb b/app/models/form/sales/questions/mortgageused.rb index 67fd900d3..547437d93 100644 --- a/app/models/form/sales/questions/mortgageused.rb +++ b/app/models/form/sales/questions/mortgageused.rb @@ -6,6 +6,12 @@ class Form::Sales::Questions::Mortgageused < ::Form::Question @answer_options = ANSWER_OPTIONS @ownershipsch = ownershipsch @question_number = get_question_number_from_hash(QUESTION_NUMBER_FROM_YEAR_AND_SECTION, value_key: form.start_year_2025_or_later? ? subsection.id : ownershipsch) + sub_copy_key = if subsection.id == "shared_ownership_staircasing_transaction" + "staircase_equity" + else + "non_staircase_equity" + end + @copy_key = "#{form.type}.#{subsection.copy_key}.#{@id}.#{sub_copy_key}" if form.start_year_2026_or_later? @top_guidance_partial = top_guidance_partial end diff --git a/config/locales/forms/2026/sales/sale_information.en.yml b/config/locales/forms/2026/sales/sale_information.en.yml index d41683352..b9c3bc8e9 100644 --- a/config/locales/forms/2026/sales/sale_information.en.yml +++ b/config/locales/forms/2026/sales/sale_information.en.yml @@ -171,10 +171,16 @@ en: mortgageused: page_header: "" - check_answer_label: "Mortgage used" - check_answer_prompt: "Tell us if a mortgage was used" - hint_text: "" - question_text: "Was a mortgage used for the purchase of this property?" + staircase_equity: + check_answer_label: "Mortgage used" + check_answer_prompt: "Tell us if a mortgage was used" + hint_text: "" + question_text: "Was a mortgage used for this staircasing transaction?" + non_staircase_equity: + check_answer_label: "Mortgage used" + check_answer_prompt: "Tell us if a mortgage was used" + hint_text: "" + question_text: "Was a mortgage used for the purchase of this property?" mortgage: page_header: "" diff --git a/spec/models/form/sales/questions/mortgageused_spec.rb b/spec/models/form/sales/questions/mortgageused_spec.rb index 804067348..6106fb4ef 100644 --- a/spec/models/form/sales/questions/mortgageused_spec.rb +++ b/spec/models/form/sales/questions/mortgageused_spec.rb @@ -15,8 +15,8 @@ RSpec.describe Form::Sales::Questions::Mortgageused, type: :model do let(:start_year_2025_or_later?) { true } let(:start_year_2026_or_later?) { true } let(:subsection_id) { "shared_ownership_initial_purchase" } - let(:form) { instance_double(Form, start_date: saledate, start_year_2024_or_later?: start_year_2024_or_later?, start_year_2025_or_later?: start_year_2025_or_later?, start_year_2026_or_later?: start_year_2026_or_later?) } - let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form:, id: subsection_id)) } + let(:form) { instance_double(Form, type: "sales", start_date: saledate, start_year_2024_or_later?: start_year_2024_or_later?, start_year_2025_or_later?: start_year_2025_or_later?, start_year_2026_or_later?: start_year_2026_or_later?) } + let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form:, id: subsection_id, copy_key: subsection_id)) } context "when it is a shared ownership scheme" do let(:ownershipsch) { 1 } @@ -120,6 +120,10 @@ RSpec.describe Form::Sales::Questions::Mortgageused, type: :model do expect(question.question_number).to eq 106 end + it "has the correct copy_key" do + expect(question.copy_key).to eq "sales.discounted_ownership_scheme.mortgageused" + end + it "does not show the don't know option" do expect_the_question_not_to_show_dont_know end @@ -134,6 +138,11 @@ RSpec.describe Form::Sales::Questions::Mortgageused, type: :model do context "and it is a staircasing transaction" do let(:staircase) { 1 } + let(:subsection_id) { "shared_ownership_staircasing_transaction" } + + it "has the correct copy_key" do + expect(question.copy_key).to eq "sales.shared_ownership_staircasing_transaction.mortgageused" + end it "shows the don't know option" do expect_the_question_to_show_dont_know @@ -150,10 +159,15 @@ RSpec.describe Form::Sales::Questions::Mortgageused, type: :model do context "and it is not a staircasing transaction" do let(:staircase) { 2 } + let(:subsection_id) { "shared_ownership_initial_purchase" } it "does not show the don't know option" do expect_the_question_not_to_show_dont_know end + + it "has the correct copy_key" do + expect(question.copy_key).to eq "sales.shared_ownership_initial_purchase.mortgageused" + end end end end @@ -169,6 +183,10 @@ RSpec.describe Form::Sales::Questions::Mortgageused, type: :model do expect(question.question_number).to eq 116 end + it "has the correct copy_key" do + expect(question.copy_key).to eq "sales.discounted_ownership_scheme.mortgageused.non_staircase_equity" + end + it "shows the don't know option" do expect_the_question_to_show_dont_know end @@ -185,6 +203,10 @@ RSpec.describe Form::Sales::Questions::Mortgageused, type: :model do expect(question.question_number).to eq 107 end + it "has the correct copy_key" do + expect(question.copy_key).to eq "sales.shared_ownership_staircasing_transaction.mortgageused.staircase_equity" + end + it "shows the don't know option" do expect_the_question_to_show_dont_know end @@ -198,6 +220,10 @@ RSpec.describe Form::Sales::Questions::Mortgageused, type: :model do expect(question.question_number).to eq 90 end + it "has the correct copy_key" do + expect(question.copy_key).to eq "sales.shared_ownership_initial_purchase.mortgageused.non_staircase_equity" + end + it "shows the don't know option" do expect_the_question_to_show_dont_know end From 05abccd7bcc85d7c42977cb20379e931e9614d98 Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis <94526761+natdeanlewissoftwire@users.noreply.github.com> Date: Mon, 23 Mar 2026 13:33:41 +0000 Subject: [PATCH 14/19] CLDC-4312: Fix broken purchase price soft validation (#3253) * CLDC-4297: set derived for sales beds * CLDC-4312: update tests --- .../questions/property_number_of_bedrooms.rb | 4 ++++ .../property_number_of_bedrooms_spec.rb | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/models/form/sales/questions/property_number_of_bedrooms.rb b/app/models/form/sales/questions/property_number_of_bedrooms.rb index 3d130b1c8..7e3c281ee 100644 --- a/app/models/form/sales/questions/property_number_of_bedrooms.rb +++ b/app/models/form/sales/questions/property_number_of_bedrooms.rb @@ -11,5 +11,9 @@ class Form::Sales::Questions::PropertyNumberOfBedrooms < ::Form::Question @question_number = get_question_number_from_hash(QUESTION_NUMBER_FROM_YEAR) end + def derived?(log) + log.is_bedsit? + end + QUESTION_NUMBER_FROM_YEAR = { 2023 => 11, 2024 => 18, 2025 => 17, 2026 => 18 }.freeze end diff --git a/spec/models/form/sales/questions/property_number_of_bedrooms_spec.rb b/spec/models/form/sales/questions/property_number_of_bedrooms_spec.rb index 7887a84ae..8e12b19aa 100644 --- a/spec/models/form/sales/questions/property_number_of_bedrooms_spec.rb +++ b/spec/models/form/sales/questions/property_number_of_bedrooms_spec.rb @@ -19,8 +19,22 @@ RSpec.describe Form::Sales::Questions::PropertyNumberOfBedrooms, type: :model do expect(question.type).to eq("numeric") end - it "is not marked as derived" do - expect(question.derived?(nil)).to be false + describe "#derived?" do + context "when the log is a bedsit" do + let(:log) { build(:sales_log, proptype: 2) } + + it "is marked as derived" do + expect(question.derived?(log)).to be true + end + end + + context "when the log is not a bedsit" do + let(:log) { build(:sales_log, proptype: 1) } + + it "is not marked as derived" do + expect(question.derived?(log)).to be false + end + end end it "has the correct min" do From 66bcfd7f16301b749cb18b8b3fb134f3517e4903 Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis <94526761+natdeanlewissoftwire@users.noreply.github.com> Date: Mon, 23 Mar 2026 13:33:56 +0000 Subject: [PATCH 15/19] CLDC-4302: Align buyer interviewed copy with paper form (#3255) * CLDC-4301: copy updates * CLDC-4302: row parser and fixture updates * CLDC-4302: typo fix * CLDC-4302: bu validation update * CLDC-4302: update log var def spec --- app/services/bulk_upload/sales/year2026/row_parser.rb | 2 +- config/locales/forms/2026/sales/setup.en.yml | 4 ++-- config/locales/validations/sales/2026/bulk_upload.en.yml | 2 +- spec/fixtures/files/2026_27_sales_bulk_upload.csv | 4 ++-- spec/fixtures/files/blank_bulk_upload_sales.csv | 2 +- spec/fixtures/files/sales_logs_csv_export_codes_26.csv | 2 +- spec/fixtures/files/sales_logs_csv_export_labels_26.csv | 2 +- .../files/sales_logs_csv_export_non_support_codes_26.csv | 2 +- .../files/sales_logs_csv_export_non_support_labels_26.csv | 2 +- spec/fixtures/variable_definitions/sales_download_26_27.csv | 1 + spec/lib/tasks/log_variable_definitions_spec.rb | 2 +- 11 files changed, 13 insertions(+), 12 deletions(-) diff --git a/app/services/bulk_upload/sales/year2026/row_parser.rb b/app/services/bulk_upload/sales/year2026/row_parser.rb index 5d7fb8ad5..ed8a334fc 100644 --- a/app/services/bulk_upload/sales/year2026/row_parser.rb +++ b/app/services/bulk_upload/sales/year2026/row_parser.rb @@ -18,7 +18,7 @@ class BulkUpload::Sales::Year2026::RowParser field_11: "What is the type of discounted ownership sale?", field_12: "Is this a joint purchase?", field_13: "Are there more than 2 joint buyers of this property?", - field_14: "Did you interview the buyer to answer these questions?", + field_14: "Did you interview the buyer(s) to answer these questions?", field_15: "Has the buyer seen or been given access to the MHCLG privacy notice?", field_16: "If known, enter this property’s UPRN", diff --git a/config/locales/forms/2026/sales/setup.en.yml b/config/locales/forms/2026/sales/setup.en.yml index 1c545a58a..386294c9d 100644 --- a/config/locales/forms/2026/sales/setup.en.yml +++ b/config/locales/forms/2026/sales/setup.en.yml @@ -86,13 +86,13 @@ en: check_answer_label: "Buyers interviewed in person" check_answer_prompt: "Tell us if buyers interviewed in person" hint_text: "You should still try to answer all questions even if the buyers weren’t interviewed in person" - question_text: "Were the buyers interviewed for any of the answers you will provide on this log?" + question_text: "Did you interview the buyers to answer these questions?" not_joint_purchase: page_header: "" check_answer_label: "Buyer interviewed in person" check_answer_prompt: "Tell us if buyer interviewed in person" hint_text: "You should still try to answer all questions even if the buyer wasn’t interviewed in person" - question_text: "Was the buyer interviewed for any of the answers you will provide on this log?" + question_text: "Did you interview the buyer to answer these questions?" privacynotice: joint_purchase: diff --git a/config/locales/validations/sales/2026/bulk_upload.en.yml b/config/locales/validations/sales/2026/bulk_upload.en.yml index a45ade4ea..6a406269b 100644 --- a/config/locales/validations/sales/2026/bulk_upload.en.yml +++ b/config/locales/validations/sales/2026/bulk_upload.en.yml @@ -46,4 +46,4 @@ en: invalid: "Select a valid nationality." mortlen: invalid: "Mortgage length must be a number or the letter R" - invalid_for_interviewed: "You indicated that the buyer was interviewed, but selected “Don’t know” for mortgage length. Please provide the mortgage length or update your response." + invalid_for_interviewed: "You indicated that you interviewed the buyer(s), but selected “Don’t know” for mortgage length. Please provide the mortgage length or update your response." diff --git a/spec/fixtures/files/2026_27_sales_bulk_upload.csv b/spec/fixtures/files/2026_27_sales_bulk_upload.csv index 01c78956c..5629922b5 100644 --- a/spec/fixtures/files/2026_27_sales_bulk_upload.csv +++ b/spec/fixtures/files/2026_27_sales_bulk_upload.csv @@ -1,12 +1,12 @@ Section,Setting up this sales log,,,,,,,,,,,,,,,Property information,,,,,,,,,,,,,Household characteristics,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Household situation,,,,,,,Other household information,,,,,"Income, benefits and outgoings",,,,,,,,Shared ownership - initial purchase,,,,,,,,,,,,,,,,,,Shared ownership - staircasing transaction,,,,,,,,,,,,,,,,,,Discounted ownership,,,,,,,,, -Question,What is the sale completion date? - day DD,What is the sale completion date? - month MM,What is the sale completion date? - year YY,Which organisation owned this property before the sale?,Which organisation is reporting this sale?,What is the CORE username of the account this sale log should be assigned to? ,What is the purchaser code?,Is this a shared ownership or discounted ownership sale?,What is the type of shared ownership sale?,Is this a staircasing transaction?,What is the type of discounted ownership sale?,Is this a joint purchase?,Are there more than 2 joint buyers of this property?,Did you interview the buyer to answer these questions?,Has the buyer seen or been given access to the MHCLG privacy notice?,"If known, enter this property’s UPRN",Address Line 1,Address Line 2,Town or city,County,Part 1 of the property's postcode,Part 2 of the property's postcode,What is the property's local authority?,What type of unit is the property?,What is the building height classification?,How many bedrooms does the property have?,Which type of building is the property?,Is the property built or adapted to wheelchair-user standards?,What is buyer 1’s age?,What is buyer 1's sex?,Is the gender buyer 1 identifies with the same as their sex registered at birth?,"If 'No', enter buyer 1's gender identity",Which of the following best describes buyer 1's ethnic background?,What is buyer 1's nationality?,Which of these best describes buyer 1’s working situation? ,Will buyer 1 live in the property?,Is buyer 2 or person 2 the partner of buyer 1?,What is buyer 2's or person 2's age?,What is buyer 2 or person 2's sex?,Is the gender buyer 2 or person 2 identifies with the same as their sex registered at birth?,"If 'No', enter buyer 2 or person 2's gender identity",Which of the following best describes buyer 2 or person 2's ethnic background?,What is buyer 2 or person 2's nationality?,Which of these best describes buyer 2 or person 2’s working situation? ,Will buyer 2 or person 2 live in the property?,"In total, how many people live in the property?",Is person 3 the partner of buyer 1?,What is person 3's age?,What is person 3's sex?,Is the gender person 3's identifies with the same as their sex registered at birth?,"If 'No', enter person 3's gender identity",Which of these best describes person 3’s working situation? ,Is person 4 the partner of buyer 1?,What is person 4's age?,What is person 4's sex?,Is the gender person 4's identifies with the same as their sex registered at birth?,"If 'No', enter person 4's gender identity",Which of these best describes person 4’s working situation? ,Is person 5 the partner of buyer 1?,What is person 5's age?,What is person 5's sex?,Is the gender person 5's identifies with the same as their sex registered at birth?,Is the gender person 5's identifies with the same as their sex registered at birth?,Which of these best describes person 5’s working situation? ,Is person 6 the partner of buyer 1?,What is person 6's age?,What is person 6's sex?,Is the gender person 6's identifies with the same as their sex registered at birth?,Is the gender person 6's identifies with the same as their sex registered at birth?,Which of these best describes person 6’s working situation? ,What was buyer 1's previous tenure?,Do you know the postcode of buyer 1's last settled accommodation?,Part 1 of postcode of buyer 1's last settled accommodation,Part 2 of postcode of buyer 1's last settled accommodation,What is the local authority of buyer 1's last settled accommodation?,"At the time of purchase, was buyer 2 living at the same address as buyer 1?",What was buyer 2's previous tenure?,"Have any of the buyers ever served as a regular in the UK armed forces? +Question,What is the sale completion date? - day DD,What is the sale completion date? - month MM,What is the sale completion date? - year YY,Which organisation owned this property before the sale?,Which organisation is reporting this sale?,What is the CORE username of the account this sale log should be assigned to? ,What is the purchaser code?,Is this a shared ownership or discounted ownership sale?,What is the type of shared ownership sale?,Is this a staircasing transaction?,What is the type of discounted ownership sale?,Is this a joint purchase?,Are there more than 2 joint buyers of this property?,Did you interview the buyer(s) to answer these questions?,Has the buyer seen or been given access to the MHCLG privacy notice?,"If known, enter this property’s UPRN",Address Line 1,Address Line 2,Town or city,County,Part 1 of the property's postcode,Part 2 of the property's postcode,What is the property's local authority?,What type of unit is the property?,What is the building height classification?,How many bedrooms does the property have?,Which type of building is the property?,Is the property built or adapted to wheelchair-user standards?,What is buyer 1’s age?,What is buyer 1's sex?,Is the gender buyer 1 identifies with the same as their sex registered at birth?,"If 'No', enter buyer 1's gender identity",Which of the following best describes buyer 1's ethnic background?,What is buyer 1's nationality?,Which of these best describes buyer 1’s working situation? ,Will buyer 1 live in the property?,Is buyer 2 or person 2 the partner of buyer 1?,What is buyer 2's or person 2's age?,What is buyer 2 or person 2's sex?,Is the gender buyer 2 or person 2 identifies with the same as their sex registered at birth?,"If 'No', enter buyer 2 or person 2's gender identity",Which of the following best describes buyer 2 or person 2's ethnic background?,What is buyer 2 or person 2's nationality?,Which of these best describes buyer 2 or person 2’s working situation? ,Will buyer 2 or person 2 live in the property?,"In total, how many people live in the property?",Is person 3 the partner of buyer 1?,What is person 3's age?,What is person 3's sex?,Is the gender person 3's identifies with the same as their sex registered at birth?,"If 'No', enter person 3's gender identity",Which of these best describes person 3’s working situation? ,Is person 4 the partner of buyer 1?,What is person 4's age?,What is person 4's sex?,Is the gender person 4's identifies with the same as their sex registered at birth?,"If 'No', enter person 4's gender identity",Which of these best describes person 4’s working situation? ,Is person 5 the partner of buyer 1?,What is person 5's age?,What is person 5's sex?,Is the gender person 5's identifies with the same as their sex registered at birth?,Is the gender person 5's identifies with the same as their sex registered at birth?,Which of these best describes person 5’s working situation? ,Is person 6 the partner of buyer 1?,What is person 6's age?,What is person 6's sex?,Is the gender person 6's identifies with the same as their sex registered at birth?,Is the gender person 6's identifies with the same as their sex registered at birth?,Which of these best describes person 6’s working situation? ,What was buyer 1's previous tenure?,Do you know the postcode of buyer 1's last settled accommodation?,Part 1 of postcode of buyer 1's last settled accommodation,Part 2 of postcode of buyer 1's last settled accommodation,What is the local authority of buyer 1's last settled accommodation?,"At the time of purchase, was buyer 2 living at the same address as buyer 1?",What was buyer 2's previous tenure?,"Have any of the buyers ever served as a regular in the UK armed forces? ",Is the buyer still serving in the UK armed forces?,Are any of the buyers a spouse or civil partner of a UK armed forces regular who died in service within the last 2 years?,Does anyone in the household consider themselves to have a disability?,Does anyone in the household use a wheelchair?,What is buyer 1's annual income?,Was buyer 1's income used for a mortgage application?,What is buyer 2's annual income?,Was buyer 2's income used for a mortgage application?,Were the buyers receiving any of these housing-related benefits immediately before buying this property?,What is the total amount the buyers had in savings before they paid any deposit for the property?,Have any of the buyers previously owned a property?,Was the previous property under shared ownership?,Is this a resale?,How long did the buyer(s) live in the property before purchasing it?,What is the day of the practical completion or handover date? - DD,What is the month of the practical completion or handover date? - MM,What is the year of the practical completion or handover date? - YY,How many bedrooms did the buyer's previous property have?,What was the previous property type?,What was the buyer’s previous tenure?,What is the full purchase price?,What was the initial percentage share purchased?,Was a mortgage used to buy this property?,What is the mortgage amount?,What is the length of the mortgage?,How much was the cash deposit paid on the property?,How much cash discount was given through Social HomeBuy?,What is the basic monthly rent?,What are the total monthly service charges for the property?,What are the total monthly estate management fees for the property?,What percentage of the property has been bought in this staircasing transaction?,What percentage of the property do the buyers now own in total?,Was this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?,Is this the first time the buyer has engaged in staircasing in the home?,What was the day of the initial purchase of a share in the property? DD,What was the month of the initial purchase of a share in the property? MM,What was the year of the initial purchase of a share in the property? YYYY,"Including this time, how many times has the shared owner engaged in staircasing in the home?",What was the day of the last staircasing transaction? DD,What was the month of the last staircasing transaction? MM,What was the year of the last staircasing transaction? YYYY,What is the full purchase price for this staircasing transaction?,What was the percentage share purchased in the initial transaction?,Was a mortgage used for this staircasing transaction?,What was the basic monthly rent prior to staircasing?,What is the basic monthly rent after staircasing?,What are the monthly service charges for the property?,"If the service charges will change after this staircasing transaction takes places, what is the new monthly service charge amount?",How long did the buyer(s) live in the property before purchasing it?,What is the full purchase price?,"What was the amount of any loan, grant, discount or subsidy given?",What was the percentage discount?,Was a mortgage used to buy this property?,What is the mortgage amount?,What is the length of the mortgage?,Does this include any extra borrowing?,How much was the cash deposit paid on the property?,What are the total monthly leasehold charges for the property? Additional info,,,,"You can find the org ID on the CORE service under 'Stock owners' or, if your organisation is the stock owner, under 'About your organisation'","You can find the org ID on the CORE service under 'Managing agents' or, if your organisation is the managing agent, under 'About your organisation'","If left empty, the sales log will be assigned to the account used to upload the log.",This is how you usually refer to this buyer on your own systems,"Sales logs are no longer required for outright and other sales. A shared ownership sale is when the purchaser buys an initial share of up to 75% of the property value and pays rent to the Private Registered Provider (PRP) on the remaining portion, or a subsequent staircasing transaction",See specification for full description of each scheme,"A staircasing transaction is when the household purchases more shares in their property, increasing the proportion they own and decreasing the proportion the housing association owns. Once the household purchases 100% of the shares, they own the property.",See specification for full description of each scheme,This is where two or more people are named as legal owners of the property after the purchase.,,You should still try to answer all questions even if the buyer wasn't interviewed in person,"Make sure the buyer has seen or been given access to the Ministry of Housing, Communities and Local Government (MHCLG) privacy notice before completing this log. This is a legal requirement under data protection legislation.","The Unique Property Reference Number (UPRN) is a unique number system created by Ordnance Survey and used by housing providers and various industries across the UK. An example UPRN is 10010457355. The UPRN may not be the same as the property reference assigned by your organisation.",,,,,Combined with field 28 it should be a postcode which lies within the local authority given in field 29.,Combined with field 27 it should be a postcode which lies within the local authority given in field 29.,,,"Only complete this section if the answer to Q16 (What type of unit is the property?) is ‘Flat or maisonette’, ‘Bedsit’ or ‘Other’. -High-rise residential buildings are those containing 2 or more residential units and either have 7 or more storeys or are at least 18 metres in height. If unsure, answer based on the number of storeys.","For bedsits, enter ‘1’",,"This is whether someone who uses a wheelchair is able to make full use of all of the property’s rooms and facilities, including use of both inside and outside space, and entering and exiting the property.","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.",This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,"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, they should decide which country to enter.","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.",,,,This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,"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, they should decide which country to enter.",,,Include all people who are living or will live in the property,,,This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,,,This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,,,This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,,,This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,,This is also known as the household's 'last settled home',Combined with field 74 it should be a postcode which lies within the local authority given in field 75.,Combined with field 73 it should be a postcode which lies within the local authority given in field 75.,This is also known as the household's 'last settled home',,,"A regular is somebody who has served in the Royal Navy, the Royal Marines, the Royal Air Force or Army full time and does not include reserve forces",,"A regular is somebody who has served in the Royal Navy, the Royal Marines, the Royal Air Force or army full time and does not include reserve forces",This includes any long-term health condition that has an impact on the person's day-to-day life,This can be inside or outside the home,"Provide the gross annual income (i.e. salary before tax) plus the annual amount of benefits, Universal Credit or pensions, and income from investments",,"Provide the gross annual income (i.e. salary before tax) plus the annual amount of benefits, Universal Credit or pensions, and income from investments",,,Enter their total savings to the nearest £10,,For any buyer,"If the social landlord has previously sold the property to another buyer and is now reselling the property, enter 'yes'. If this is the first time the property has been sold, enter 'no'.","If the buyers haven't been living in the property, enter '0'",This is the date on which the building contractor hands over responsibility for the completed property to the private registered provider (PRP).,,,"For bedsits, enter ‘1’",,,"Enter the full purchase price of the property before any discounts are applied. For shared ownership, enter the full purchase price paid for 100% equity (this is equal to the value of the share owned by the PRP plus the value bought by the purchaser).","This is the initial stake purchased. Enter the amount of initial equity held by the purchaser (for example, 25% or 50%)",,Enter the amount of mortgage agreed with the mortgage lender. Exclude any deposits or cash payments.,You should round up to the nearest year. Value should not exceed 60 years. You can only select ‘Don’t Know’ if you answered ‘No’ to Q11 'Did you interview the buyer to answer these questions?’.,Enter the total cash sum paid by the buyer towards the property that was not funded by the mortgage,Enter the total cash discount given on the property being purchased through the Social HomeBuy scheme,Before any charges,"This includes any charges for day-to-day maintenance and repairs, buildings insurance, and any contributions to a sinking or reserve fund. It does not include estate management fees.","Estate management fees are typically used for the maintenance of communal gardens, payments, private roads, car parks or play areas within new build estates.",,,Back-to-back staircasing transactions are used as a way for shared owners who own less than 100% of their property to sell on the open market. It involves the shared owner purchasing the remaining share from their landlord and immediately selling 100% of the property to a buyer on the open market. The landlord is then reimbursed for the staircasing transaction through the proceeds of sale to the buyer.,,,,,,,,,"Enter the full purchase price of the property before any discounts are applied. For shared ownership, enter the full purchase price paid for 100% equity (this is equal to the value of the share owned by the PRP plus the value bought by the purchaser).","This is the initial stake purchased. Enter the amount of initial equity held by the purchaser (for example, 25% or 50%)",,,,"This includes any charges for day-to-day maintenance and repairs, building insurance, and any contributions to a sinking or reserved fund. It does not include estate management fees","This includes any charges for day-to-day maintenance and repairs, building insurance, and any contributions to a sinking or reserved fund. It does not include estate management fees",,"For all schemes, including Right to Acquire (RTA), Right to Buy (RTB), or Preserved Right to Buy (PRTB) sales, enter the full price of the property without any discount.","For all schemes except Right to Buy (RTB), Preserved Right to Buy(PRTB) and Rent to Buy.","For Right to Buy (RTB) and Preserved Right to Buy (PRTB). For capped discount, enter capped %. If property is sold to an existing tenant under RTB or PRTB, enter % discount from full market value.",,Enter the amount of mortgage agreed with the mortgage lender. Exclude any deposits or cash payments.,You should round up to the nearest year. Value should not exceed 60 years. You can only select ‘Don’t Know’ if you answered ‘No’ to Q11 'Did you interview the buyer to answer these questions?’.,This is 'Yes' if the mortgage includes borrowing beyond the purchase price of the property,Enter the total cash sum paid by the purchaser towards the property that was not funded by the mortgage. This excludes any grant or loan.,"For example, service and management charges" +High-rise residential buildings are those containing 2 or more residential units and either have 7 or more storeys or are at least 18 metres in height. If unsure, answer based on the number of storeys.","For bedsits, enter ‘1’",,"This is whether someone who uses a wheelchair is able to make full use of all of the property’s rooms and facilities, including use of both inside and outside space, and entering and exiting the property.","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.",This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,"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, they should decide which country to enter.","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.",,,,This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,"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, they should decide which country to enter.",,,Include all people who are living or will live in the property,,,This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,,,This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,,,This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,,,This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,,This is also known as the household's 'last settled home',Combined with field 74 it should be a postcode which lies within the local authority given in field 75.,Combined with field 73 it should be a postcode which lies within the local authority given in field 75.,This is also known as the household's 'last settled home',,,"A regular is somebody who has served in the Royal Navy, the Royal Marines, the Royal Air Force or Army full time and does not include reserve forces",,"A regular is somebody who has served in the Royal Navy, the Royal Marines, the Royal Air Force or army full time and does not include reserve forces",This includes any long-term health condition that has an impact on the person's day-to-day life,This can be inside or outside the home,"Provide the gross annual income (i.e. salary before tax) plus the annual amount of benefits, Universal Credit or pensions, and income from investments",,"Provide the gross annual income (i.e. salary before tax) plus the annual amount of benefits, Universal Credit or pensions, and income from investments",,,Enter their total savings to the nearest £10,,For any buyer,"If the social landlord has previously sold the property to another buyer and is now reselling the property, enter 'yes'. If this is the first time the property has been sold, enter 'no'.","If the buyers haven't been living in the property, enter '0'",This is the date on which the building contractor hands over responsibility for the completed property to the private registered provider (PRP).,,,"For bedsits, enter ‘1’",,,"Enter the full purchase price of the property before any discounts are applied. For shared ownership, enter the full purchase price paid for 100% equity (this is equal to the value of the share owned by the PRP plus the value bought by the purchaser).","This is the initial stake purchased. Enter the amount of initial equity held by the purchaser (for example, 25% or 50%)",,Enter the amount of mortgage agreed with the mortgage lender. Exclude any deposits or cash payments.,You should round up to the nearest year. Value should not exceed 60 years. You can only select ‘Don’t Know’ if you answered ‘No’ to Q11 'Did you interview the buyer(s) to answer these questions?’.,Enter the total cash sum paid by the buyer towards the property that was not funded by the mortgage,Enter the total cash discount given on the property being purchased through the Social HomeBuy scheme,Before any charges,"This includes any charges for day-to-day maintenance and repairs, buildings insurance, and any contributions to a sinking or reserve fund. It does not include estate management fees.","Estate management fees are typically used for the maintenance of communal gardens, payments, private roads, car parks or play areas within new build estates.",,,Back-to-back staircasing transactions are used as a way for shared owners who own less than 100% of their property to sell on the open market. It involves the shared owner purchasing the remaining share from their landlord and immediately selling 100% of the property to a buyer on the open market. The landlord is then reimbursed for the staircasing transaction through the proceeds of sale to the buyer.,,,,,,,,,"Enter the full purchase price of the property before any discounts are applied. For shared ownership, enter the full purchase price paid for 100% equity (this is equal to the value of the share owned by the PRP plus the value bought by the purchaser).","This is the initial stake purchased. Enter the amount of initial equity held by the purchaser (for example, 25% or 50%)",,,,"This includes any charges for day-to-day maintenance and repairs, building insurance, and any contributions to a sinking or reserved fund. It does not include estate management fees","This includes any charges for day-to-day maintenance and repairs, building insurance, and any contributions to a sinking or reserved fund. It does not include estate management fees",,"For all schemes, including Right to Acquire (RTA), Right to Buy (RTB), or Preserved Right to Buy (PRTB) sales, enter the full price of the property without any discount.","For all schemes except Right to Buy (RTB), Preserved Right to Buy(PRTB) and Rent to Buy.","For Right to Buy (RTB) and Preserved Right to Buy (PRTB). For capped discount, enter capped %. If property is sold to an existing tenant under RTB or PRTB, enter % discount from full market value.",,Enter the amount of mortgage agreed with the mortgage lender. Exclude any deposits or cash payments.,You should round up to the nearest year. Value should not exceed 60 years. You can only select ‘Don’t Know’ if you answered ‘No’ to Q11 'Did you interview the buyer(s) to answer these questions?’.,This is 'Yes' if the mortgage includes borrowing beyond the purchase price of the property,Enter the total cash sum paid by the purchaser towards the property that was not funded by the mortgage. This excludes any grant or loan.,"For example, service and management charges" Values,1 - 31,1 - 12,26 - 27,Alphanumeric,Alphanumeric,Email format,"Alphanumeric, punctuation can be used as long as not the field separator. Max 9 characters.",1 - 2,"2, 16, 18, 24, 28 or 30 - 32",1 - 2,"8, 9, 14, 21, 22, or 29",1 - 2,1 - 3,1 - 2,1,"Numeric, max 12 digits",Alphanumeric,,Text,,"Alphanumeric, 2 - 4 characters","Alphanumeric, diff --git a/spec/fixtures/files/blank_bulk_upload_sales.csv b/spec/fixtures/files/blank_bulk_upload_sales.csv index 249b966b1..a8a7a54b9 100644 --- a/spec/fixtures/files/blank_bulk_upload_sales.csv +++ b/spec/fixtures/files/blank_bulk_upload_sales.csv @@ -1,4 +1,4 @@ -Question,What is the purchaser code?,What is the day of the sale completion date? - DD,What is the month of the sale completion date? - MM,What is the year of the sale completion date? - YY,[BLANK],Was the buyer interviewed for any of the answers you will provide on this log?,Age of Buyer 1,Age of Buyer 2 or Person 2,Age of Person 3,Age of Person 4,Age of Person 5,Age of Person 6,Gender identity of Buyer 1,Gender identity of Buyer 2 or Person 2,Gender identity of Person 3,Gender identity of Person 4,Gender identity of Person 5,Gender identity of Person 6,Person 2's relationship to lead tenant,Person 3's relationship to lead tenant,Person 4's relationship to lead tenant,Person 5's relationship to lead tenant,Person 6's relationship to lead tenant,Working situation of Buyer 1,Working situation of Buyer 2 or Person 2,Working situation of Person 3,Working situation of Person 4,Working situation of Person 5,Working situation of Person 6,What is the buyer 1's ethnic group?,What is buyer 1's nationality?,What is buyer 1's gross annual income?,What is buyer 2's gross annual income?,Was buyer 1's income used for a mortgage application?,Was buyer 2's income used for a mortgage application?,"What is the total amount the buyers had in savings before they paid any deposit for the property? +Question,What is the purchaser code?,What is the day of the sale completion date? - DD,What is the month of the sale completion date? - MM,What is the year of the sale completion date? - YY,[BLANK],Did you interview the buyer to answer these questions?,Age of Buyer 1,Age of Buyer 2 or Person 2,Age of Person 3,Age of Person 4,Age of Person 5,Age of Person 6,Gender identity of Buyer 1,Gender identity of Buyer 2 or Person 2,Gender identity of Person 3,Gender identity of Person 4,Gender identity of Person 5,Gender identity of Person 6,Person 2's relationship to lead tenant,Person 3's relationship to lead tenant,Person 4's relationship to lead tenant,Person 5's relationship to lead tenant,Person 6's relationship to lead tenant,Working situation of Buyer 1,Working situation of Buyer 2 or Person 2,Working situation of Person 3,Working situation of Person 4,Working situation of Person 5,Working situation of Person 6,What is the buyer 1's ethnic group?,What is buyer 1's nationality?,What is buyer 1's gross annual income?,What is buyer 2's gross annual income?,Was buyer 1's income used for a mortgage application?,Was buyer 2's income used for a mortgage application?,"What is the total amount the buyers had in savings before they paid any deposit for the property? To the nearest £10",Have any of the buyers previously owned a property?,[BLANK],What was buyer 1's previous tenure?,What is the local authority of buyer 1's last settled home,Part 1 of postcode of buyer 1's last settled home,Part 2 of postcode of buyer 1's last settled home,Do you know the postcode of buyer 1's last settled home?,Was the buyer registered with their PRP (HA)?,Was the buyer registered with the local authority?,Was the buyer registered with a Help to Buy agent?,Was the buyer registered with another PRP (HA)?,Does anyone in the household consider themselves to have a disability?,Does anyone in the household use a wheelchair?,How many bedrooms does the property have?,What type of unit is the property?,Which type of building is the property?,What is the local authority of the property?,Part 1 of postcode of property,Part 2 of postcode of property,Is the property built or adapted to wheelchair-user standards?,What is the type of shared ownership sale?,"Is this a resale? diff --git a/spec/fixtures/files/sales_logs_csv_export_codes_26.csv b/spec/fixtures/files/sales_logs_csv_export_codes_26.csv index 236dd31c5..5f2ccdd61 100644 --- a/spec/fixtures/files/sales_logs_csv_export_codes_26.csv +++ b/spec/fixtures/files/sales_logs_csv_export_codes_26.csv @@ -1,3 +1,3 @@ -Log ID,Status of log,ID of a set of duplicate logs,Which organisation owned this property before the sale?,,Which organisation reported the sale?,,Time and date the log was created,User that created the log,,User the log is assigned to,,Time and date the log was last updated,,,Was the log submitted in-service or via bulk upload?,ID of a set of bulk uploaded logs,Year collection period opened,Day of sale completion date,Month of sale completion date,Year of sale completion date,What is the purchaser code?,Was this purchase made through an ownership scheme?,What is the type of shared ownership/discounted ownership/outright sale?,Is this a staircasing transaction?,Is this a joint purchase?,Are there more than 2 joint buyers of this property?,Did you interview the buyer to answer these questions?,Has the buyer seen the MHCLG privacy notice?,What is the UPRN of the property?,Address line 1,Address line 2,Town/City,County,Postcode,The internal value to indicate if the LA was inferred from the postcode,LA name,LA code,UPRN of the address selected,Was the 'No address found' page seen?,Address line 1 input from address matching feature,Postcode input from address matching feature,Address line 1 entered in bulk upload file,Address line 2 entered in bulk upload file,Town or city entered in bulk upload file,County entered in bulk upload file,Postcode entered in bulk upload file,Local authority entered in bulk upload file,What type of unit is the property?,What is the building height classification?,How many bedrooms does the property have?,Which type of building is the property?,Is the property built or adapted to wheelchair-user standards?,What is buyer 1's age?,What was buyer 1's sex at birth?,Is the gender buyer 1 identifies with the same as their sex registered at birth?,"If 'No', enter buyer 1's gender identity",What is buyer 1's ethnic group?,Which of the following best describes buyer 1's ethnic background?,What is buyer 1's nationality?,Which of these best describes buyer 1's working situation?,Will buyer 1 live in the property?,What is buyer 2 or person 2's relationship to buyer 1?,What is buyer 2 or person 2's age?,What was buyer/person 2's sex at birth?,Is the gender buyer/person 2 identifies with the same as their sex registered at birth?,"If 'No', enter buyer/person 2's gender identity",What is buyer 2's ethnic group?,Which of the following best describes buyer 2's ethnic background?,What is buyer 2's nationality?,What is buyer 2 or person 2's working situation?,Will buyer 2 live in the property?,"In total, how many people live in the property?",What is person 3's relationship to buyer 1?,What is person 3's age?,What was person 3's sex at birth?,Is the gender person 3 identifies with the same as their sex registered at birth?,"If 'No', enter person 3's gender identity",What is person 3's working situation?,What is person 4's relationship to buyer 1?,What is person 4's age?,What was person 4's sex at birth?,Is the gender person 4 identifies with the same as their sex registered at birth?,"If 'No', enter person 4's gender identity",What is person 4's working situation?,What is person 5's relationship to buyer 1?,What is person 5's age?,What was person 5's sex at birth?,Is the gender person 5 identifies with the same as their sex registered at birth?,"If 'No', enter person 5's gender identity",What is person 5's working situation?,What is person 6's relationship to buyer 1?,What is person 6's age?,What was person 6's sex at birth?,Is the gender person 6 identifies with the same as their sex registered at birth?,"If 'No', enter person 6's gender identity",What is person 6's working situation?,Household type,What was buyer 1's previous tenure?,Do you know the postcode of buyer 1's last settled accommodation?,Part 1 of postcode of buyer 1's last settled accommodation,Part 2 of postcode of buyer 1's last settled accommodation,Do you know the local authority of buyer 1's last settled accommodation?,The local authority code of buyer 1's last settled accommodation,The local authority name of buyer 1's last settled accommodation,"At the time of purchase, was buyer 2 living at the same address as buyer 1?",What was buyer 2's previous tenure?,Have any of the buyers ever served as a regular in the UK armed forces?,Is the buyer still serving in the UK armed forces?,Are any of the buyers a spouse or civil partner of a UK armed forces regular who died in service within the last 2 years?,Does anyone in the household consider themselves to have a disability?,Does anyone in the household use a wheelchair?,Is buyer 1's annual income known?,What is buyer 1's annual income?,Was buyer 1's income used for a mortgage application?,Is buyer 1's annual income known?,What is buyer 2's annual income?,Was buyer 2's income used for a mortgage application?,Were the buyers receiving any of these housing-related benefits immediately before buying this property?,Is the the total amount the buyers had in savings known?,What is the total amount the buyers had in savings before they paid any deposit for the property?,Have any of the buyers previously owned a property?,Was the previous property under shared ownership?,Is this a resale?,How long did the buyer(s) live in the property before purchasing it?,Day of the practical completion or handover date,Month of the practical completion or handover date,Year of the practical completion or handover date,How many bedrooms did the buyer's previous property have?,What was the previous property type?,What was the rent type of buyer's previous tenure?,What is the full purchase price?,Populated if a soft validation is confirmed.,What was the initial percentage equity stake purchased?,Was a mortgage used to buy this property?,What is the mortgage amount?,What is the length of the mortgage in years?,How much was the cash deposit paid on the property?,How much cash discount was given through Social Homebuy?,What is the basic monthly rent?,,,,,What percentage of the property has been bought in this staircasing transaction?,What percentage of the property do the buyers now own in total?,Was this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?,,,,,,,,,,"What was the amount of any loan, grant, discount or subsidy given?",What was the percentage discount?,Does this include any extra borrowing?,Does the property have any monthly leasehold charges?,What are the total monthly leasehold charges for the property?,Populated if a soft validation is confirmed.,Will the service charge change after this staircasing transaction takes place?,What are the new total monthly service charges for the property? +Log ID,Status of log,ID of a set of duplicate logs,Which organisation owned this property before the sale?,,Which organisation reported the sale?,,Time and date the log was created,User that created the log,,User the log is assigned to,,Time and date the log was last updated,,,Was the log submitted in-service or via bulk upload?,ID of a set of bulk uploaded logs,Year collection period opened,Day of sale completion date,Month of sale completion date,Year of sale completion date,What is the purchaser code?,Was this purchase made through an ownership scheme?,What is the type of shared ownership/discounted ownership/outright sale?,Is this a staircasing transaction?,Is this a joint purchase?,Are there more than 2 joint buyers of this property?,Did you interview the buyer(s) to answer these questions?,Has the buyer seen the MHCLG privacy notice?,What is the UPRN of the property?,Address line 1,Address line 2,Town/City,County,Postcode,The internal value to indicate if the LA was inferred from the postcode,LA name,LA code,UPRN of the address selected,Was the 'No address found' page seen?,Address line 1 input from address matching feature,Postcode input from address matching feature,Address line 1 entered in bulk upload file,Address line 2 entered in bulk upload file,Town or city entered in bulk upload file,County entered in bulk upload file,Postcode entered in bulk upload file,Local authority entered in bulk upload file,What type of unit is the property?,What is the building height classification?,How many bedrooms does the property have?,Which type of building is the property?,Is the property built or adapted to wheelchair-user standards?,What is buyer 1's age?,What was buyer 1's sex at birth?,Is the gender buyer 1 identifies with the same as their sex registered at birth?,"If 'No', enter buyer 1's gender identity",What is buyer 1's ethnic group?,Which of the following best describes buyer 1's ethnic background?,What is buyer 1's nationality?,Which of these best describes buyer 1's working situation?,Will buyer 1 live in the property?,What is buyer 2 or person 2's relationship to buyer 1?,What is buyer 2 or person 2's age?,What was buyer/person 2's sex at birth?,Is the gender buyer/person 2 identifies with the same as their sex registered at birth?,"If 'No', enter buyer/person 2's gender identity",What is buyer 2's ethnic group?,Which of the following best describes buyer 2's ethnic background?,What is buyer 2's nationality?,What is buyer 2 or person 2's working situation?,Will buyer 2 live in the property?,"In total, how many people live in the property?",What is person 3's relationship to buyer 1?,What is person 3's age?,What was person 3's sex at birth?,Is the gender person 3 identifies with the same as their sex registered at birth?,"If 'No', enter person 3's gender identity",What is person 3's working situation?,What is person 4's relationship to buyer 1?,What is person 4's age?,What was person 4's sex at birth?,Is the gender person 4 identifies with the same as their sex registered at birth?,"If 'No', enter person 4's gender identity",What is person 4's working situation?,What is person 5's relationship to buyer 1?,What is person 5's age?,What was person 5's sex at birth?,Is the gender person 5 identifies with the same as their sex registered at birth?,"If 'No', enter person 5's gender identity",What is person 5's working situation?,What is person 6's relationship to buyer 1?,What is person 6's age?,What was person 6's sex at birth?,Is the gender person 6 identifies with the same as their sex registered at birth?,"If 'No', enter person 6's gender identity",What is person 6's working situation?,Household type,What was buyer 1's previous tenure?,Do you know the postcode of buyer 1's last settled accommodation?,Part 1 of postcode of buyer 1's last settled accommodation,Part 2 of postcode of buyer 1's last settled accommodation,Do you know the local authority of buyer 1's last settled accommodation?,The local authority code of buyer 1's last settled accommodation,The local authority name of buyer 1's last settled accommodation,"At the time of purchase, was buyer 2 living at the same address as buyer 1?",What was buyer 2's previous tenure?,Have any of the buyers ever served as a regular in the UK armed forces?,Is the buyer still serving in the UK armed forces?,Are any of the buyers a spouse or civil partner of a UK armed forces regular who died in service within the last 2 years?,Does anyone in the household consider themselves to have a disability?,Does anyone in the household use a wheelchair?,Is buyer 1's annual income known?,What is buyer 1's annual income?,Was buyer 1's income used for a mortgage application?,Is buyer 1's annual income known?,What is buyer 2's annual income?,Was buyer 2's income used for a mortgage application?,Were the buyers receiving any of these housing-related benefits immediately before buying this property?,Is the the total amount the buyers had in savings known?,What is the total amount the buyers had in savings before they paid any deposit for the property?,Have any of the buyers previously owned a property?,Was the previous property under shared ownership?,Is this a resale?,How long did the buyer(s) live in the property before purchasing it?,Day of the practical completion or handover date,Month of the practical completion or handover date,Year of the practical completion or handover date,How many bedrooms did the buyer's previous property have?,What was the previous property type?,What was the rent type of buyer's previous tenure?,What is the full purchase price?,Populated if a soft validation is confirmed.,What was the initial percentage equity stake purchased?,Was a mortgage used to buy this property?,What is the mortgage amount?,What is the length of the mortgage in years?,How much was the cash deposit paid on the property?,How much cash discount was given through Social Homebuy?,What is the basic monthly rent?,,,,,What percentage of the property has been bought in this staircasing transaction?,What percentage of the property do the buyers now own in total?,Was this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?,,,,,,,,,,"What was the amount of any loan, grant, discount or subsidy given?",What was the percentage discount?,Does this include any extra borrowing?,Does the property have any monthly leasehold charges?,What are the total monthly leasehold charges for the property?,Populated if a soft validation is confirmed.,Will the service charge change after this staircasing transaction takes place?,What are the new total monthly service charges for the property? ID,STATUS,DUPLICATESET,OWNINGORGNAME,OWNINGORGID,MANINGORGNAME,MANINGORGID,CREATEDDATE,CREATEDBY,CREATEDBYID,USERNAME,USERNAMEID,UPLOADDATE,AMENDEDBY,AMENDEDBYID,CREATIONMETHOD,BULKUPLOADID,COLLECTIONYEAR,DAY,MONTH,YEAR,PURCHID,OWNERSHIP,TYPE,STAIRCASE,JOINT,JOINTMORE,NOINT,PRIVACYNOTICE,UPRN,ADDRESS1,ADDRESS2,TOWNCITY,COUNTY,POSTCODE,ISLAINFERRED,LANAME,LA,UPRNSELECTED,ADDRESS_SEARCH_VALUE_CHECK,ADDRESS1INPUT,POSTCODEINPUT,BULKADDRESS1,BULKADDRESS2,BULKTOWNCITY,BULKCOUNTY,BULKPOSTCODE,BULKLA,PROPTYPE,BUILDHEIGHTCLASS,BEDS,BUILTYPE,WCHAIR,AGE1,SEXRAB1,GENDER_SAME_AS_SEX1,GENDER_DESCRIPTION1,ETHNICGROUP1,ETHNIC,NATIONALITYALL1,ECSTAT1,LIVEINBUYER1,RELAT2,AGE2,SEXRAB2,GENDER_SAME_AS_SEX2,GENDER_DESCRIPTION2,ETHNICGROUP2,ETHNIC2,NATIONALITYALL2,ECSTAT2,LIVEINBUYER2,HHOLDCOUNT,RELAT3,AGE3,SEXRAB3,GENDER_SAME_AS_SEX3,GENDER_DESCRIPTION3,ECSTAT3,RELAT4,AGE4,SEXRAB4,GENDER_SAME_AS_SEX4,GENDER_DESCRIPTION4,ECSTAT4,RELAT5,AGE5,SEXRAB5,GENDER_SAME_AS_SEX5,GENDER_DESCRIPTION5,ECSTAT5,RELAT6,AGE6,SEXRAB6,GENDER_SAME_AS_SEX6,GENDER_DESCRIPTION6,ECSTAT6,HHTYPE,PREVTEN,PPCODENK,PPOSTC1,PPOSTC2,PREVIOUSLAKNOWN,PREVLOC,PREVLOCNAME,BUY2LIVING,PREVTEN2,HHREGRES,HHREGRESSTILL,ARMEDFORCESSPOUSE,DISABLED,WHEEL,INC1NK,INCOME1,INC1MORT,INC2NK,INCOME2,INC2MORT,HB,SAVINGSNK,SAVINGS,PREVOWN,PREVSHARED,RESALE,PROPLEN,HODAY,HOMONTH,HOYEAR,FROMBEDS,FROMPROP,SOCPREVTEN,VALUE,VALUE_VALUE_CHECK,EQUITY,MORTGAGEUSED,MORTGAGE,MORTLEN1,DEPOSIT,CASHDIS,MRENT,HASSERVICECHARGES,SERVICECHARGES,HASESTATEFEE,ESTATEFEE,STAIRBOUGHT,STAIROWNED,STAIRCASETOSALE,FIRSTSTAIR,NUMSTAIR,STAIRLASTDAY,STAIRLASTMONTH,STAIRLASTYEAR,STAIRINITIALDAY,STAIRINITIALMONTH,STAIRINITIALYEAR,MRENTPRESTAIRCASING,GRANT,DISCOUNT,EXTRABOR,HASMSCHARGE,MSCHARGE,MSCHARGE_VALUE_CHECK,HASSERVICECHARGESCHANGED,NEWSERVICECHARGES ,completed,,MHCLG,,MHCLG,,2026-04-01T00:00:00+01:00,billyboy@eyeklaud.com,,billyboy@eyeklaud.com,,2026-04-01T00:00:00+01:00,,,1,,2026,1,4,2026,,2,8,,1,1,2,1,1,"1, Test Street",,Test Town,,AA1 1AA,true,Westminster,E09000033,1,,,,address line 1 as entered,address line 2 as entered,town or city as entered,county as entered,AB1 2CD,la as entered,1,2,2,1,1,30,F,1,,17,17,826,1,1,P,35,M,2,Nonbinary,17,,826,1,1,5,X,14,F,1,,9,X,-9,R,2,Genderfluid,3,R,-9,R,3,,10,,,,,,,6,1,0,SW1A,1AA,1,E09000033,Westminster,3,,1,4,5,1,1,0,13400,1,0,13400,1,4,1,,1,2,,10,,,,,,,110000.0,,,1,20000.0,10,80000.0,,,,,,,,,,,,,,,,,,,10000.0,,1,1,100.0,,, diff --git a/spec/fixtures/files/sales_logs_csv_export_labels_26.csv b/spec/fixtures/files/sales_logs_csv_export_labels_26.csv index 30fa7a1a6..2cf82726d 100644 --- a/spec/fixtures/files/sales_logs_csv_export_labels_26.csv +++ b/spec/fixtures/files/sales_logs_csv_export_labels_26.csv @@ -1,3 +1,3 @@ -Log ID,Status of log,ID of a set of duplicate logs,Which organisation owned this property before the sale?,,Which organisation reported the sale?,,Time and date the log was created,User that created the log,,User the log is assigned to,,Time and date the log was last updated,,,Was the log submitted in-service or via bulk upload?,ID of a set of bulk uploaded logs,Year collection period opened,Day of sale completion date,Month of sale completion date,Year of sale completion date,What is the purchaser code?,Was this purchase made through an ownership scheme?,What is the type of shared ownership/discounted ownership/outright sale?,Is this a staircasing transaction?,Is this a joint purchase?,Are there more than 2 joint buyers of this property?,Did you interview the buyer to answer these questions?,Has the buyer seen the MHCLG privacy notice?,What is the UPRN of the property?,Address line 1,Address line 2,Town/City,County,Postcode,The internal value to indicate if the LA was inferred from the postcode,LA name,LA code,UPRN of the address selected,Was the 'No address found' page seen?,Address line 1 input from address matching feature,Postcode input from address matching feature,Address line 1 entered in bulk upload file,Address line 2 entered in bulk upload file,Town or city entered in bulk upload file,County entered in bulk upload file,Postcode entered in bulk upload file,Local authority entered in bulk upload file,What type of unit is the property?,What is the building height classification?,How many bedrooms does the property have?,Which type of building is the property?,Is the property built or adapted to wheelchair-user standards?,What is buyer 1's age?,What was buyer 1's sex at birth?,Is the gender buyer 1 identifies with the same as their sex registered at birth?,"If 'No', enter buyer 1's gender identity",What is buyer 1's ethnic group?,Which of the following best describes buyer 1's ethnic background?,What is buyer 1's nationality?,Which of these best describes buyer 1's working situation?,Will buyer 1 live in the property?,What is buyer 2 or person 2's relationship to buyer 1?,What is buyer 2 or person 2's age?,What was buyer/person 2's sex at birth?,Is the gender buyer/person 2 identifies with the same as their sex registered at birth?,"If 'No', enter buyer/person 2's gender identity",What is buyer 2's ethnic group?,Which of the following best describes buyer 2's ethnic background?,What is buyer 2's nationality?,What is buyer 2 or person 2's working situation?,Will buyer 2 live in the property?,"In total, how many people live in the property?",What is person 3's relationship to buyer 1?,What is person 3's age?,What was person 3's sex at birth?,Is the gender person 3 identifies with the same as their sex registered at birth?,"If 'No', enter person 3's gender identity",What is person 3's working situation?,What is person 4's relationship to buyer 1?,What is person 4's age?,What was person 4's sex at birth?,Is the gender person 4 identifies with the same as their sex registered at birth?,"If 'No', enter person 4's gender identity",What is person 4's working situation?,What is person 5's relationship to buyer 1?,What is person 5's age?,What was person 5's sex at birth?,Is the gender person 5 identifies with the same as their sex registered at birth?,"If 'No', enter person 5's gender identity",What is person 5's working situation?,What is person 6's relationship to buyer 1?,What is person 6's age?,What was person 6's sex at birth?,Is the gender person 6 identifies with the same as their sex registered at birth?,"If 'No', enter person 6's gender identity",What is person 6's working situation?,Household type,What was buyer 1's previous tenure?,Do you know the postcode of buyer 1's last settled accommodation?,Part 1 of postcode of buyer 1's last settled accommodation,Part 2 of postcode of buyer 1's last settled accommodation,Do you know the local authority of buyer 1's last settled accommodation?,The local authority code of buyer 1's last settled accommodation,The local authority name of buyer 1's last settled accommodation,"At the time of purchase, was buyer 2 living at the same address as buyer 1?",What was buyer 2's previous tenure?,Have any of the buyers ever served as a regular in the UK armed forces?,Is the buyer still serving in the UK armed forces?,Are any of the buyers a spouse or civil partner of a UK armed forces regular who died in service within the last 2 years?,Does anyone in the household consider themselves to have a disability?,Does anyone in the household use a wheelchair?,Is buyer 1's annual income known?,What is buyer 1's annual income?,Was buyer 1's income used for a mortgage application?,Is buyer 1's annual income known?,What is buyer 2's annual income?,Was buyer 2's income used for a mortgage application?,Were the buyers receiving any of these housing-related benefits immediately before buying this property?,Is the the total amount the buyers had in savings known?,What is the total amount the buyers had in savings before they paid any deposit for the property?,Have any of the buyers previously owned a property?,Was the previous property under shared ownership?,Is this a resale?,How long did the buyer(s) live in the property before purchasing it?,Day of the practical completion or handover date,Month of the practical completion or handover date,Year of the practical completion or handover date,How many bedrooms did the buyer's previous property have?,What was the previous property type?,What was the rent type of buyer's previous tenure?,What is the full purchase price?,Populated if a soft validation is confirmed.,What was the initial percentage equity stake purchased?,Was a mortgage used to buy this property?,What is the mortgage amount?,What is the length of the mortgage in years?,How much was the cash deposit paid on the property?,How much cash discount was given through Social Homebuy?,What is the basic monthly rent?,,,,,What percentage of the property has been bought in this staircasing transaction?,What percentage of the property do the buyers now own in total?,Was this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?,,,,,,,,,,"What was the amount of any loan, grant, discount or subsidy given?",What was the percentage discount?,Does this include any extra borrowing?,Does the property have any monthly leasehold charges?,What are the total monthly leasehold charges for the property?,Populated if a soft validation is confirmed.,Will the service charge change after this staircasing transaction takes place?,What are the new total monthly service charges for the property? +Log ID,Status of log,ID of a set of duplicate logs,Which organisation owned this property before the sale?,,Which organisation reported the sale?,,Time and date the log was created,User that created the log,,User the log is assigned to,,Time and date the log was last updated,,,Was the log submitted in-service or via bulk upload?,ID of a set of bulk uploaded logs,Year collection period opened,Day of sale completion date,Month of sale completion date,Year of sale completion date,What is the purchaser code?,Was this purchase made through an ownership scheme?,What is the type of shared ownership/discounted ownership/outright sale?,Is this a staircasing transaction?,Is this a joint purchase?,Are there more than 2 joint buyers of this property?,Did you interview the buyer(s) to answer these questions?,Has the buyer seen the MHCLG privacy notice?,What is the UPRN of the property?,Address line 1,Address line 2,Town/City,County,Postcode,The internal value to indicate if the LA was inferred from the postcode,LA name,LA code,UPRN of the address selected,Was the 'No address found' page seen?,Address line 1 input from address matching feature,Postcode input from address matching feature,Address line 1 entered in bulk upload file,Address line 2 entered in bulk upload file,Town or city entered in bulk upload file,County entered in bulk upload file,Postcode entered in bulk upload file,Local authority entered in bulk upload file,What type of unit is the property?,What is the building height classification?,How many bedrooms does the property have?,Which type of building is the property?,Is the property built or adapted to wheelchair-user standards?,What is buyer 1's age?,What was buyer 1's sex at birth?,Is the gender buyer 1 identifies with the same as their sex registered at birth?,"If 'No', enter buyer 1's gender identity",What is buyer 1's ethnic group?,Which of the following best describes buyer 1's ethnic background?,What is buyer 1's nationality?,Which of these best describes buyer 1's working situation?,Will buyer 1 live in the property?,What is buyer 2 or person 2's relationship to buyer 1?,What is buyer 2 or person 2's age?,What was buyer/person 2's sex at birth?,Is the gender buyer/person 2 identifies with the same as their sex registered at birth?,"If 'No', enter buyer/person 2's gender identity",What is buyer 2's ethnic group?,Which of the following best describes buyer 2's ethnic background?,What is buyer 2's nationality?,What is buyer 2 or person 2's working situation?,Will buyer 2 live in the property?,"In total, how many people live in the property?",What is person 3's relationship to buyer 1?,What is person 3's age?,What was person 3's sex at birth?,Is the gender person 3 identifies with the same as their sex registered at birth?,"If 'No', enter person 3's gender identity",What is person 3's working situation?,What is person 4's relationship to buyer 1?,What is person 4's age?,What was person 4's sex at birth?,Is the gender person 4 identifies with the same as their sex registered at birth?,"If 'No', enter person 4's gender identity",What is person 4's working situation?,What is person 5's relationship to buyer 1?,What is person 5's age?,What was person 5's sex at birth?,Is the gender person 5 identifies with the same as their sex registered at birth?,"If 'No', enter person 5's gender identity",What is person 5's working situation?,What is person 6's relationship to buyer 1?,What is person 6's age?,What was person 6's sex at birth?,Is the gender person 6 identifies with the same as their sex registered at birth?,"If 'No', enter person 6's gender identity",What is person 6's working situation?,Household type,What was buyer 1's previous tenure?,Do you know the postcode of buyer 1's last settled accommodation?,Part 1 of postcode of buyer 1's last settled accommodation,Part 2 of postcode of buyer 1's last settled accommodation,Do you know the local authority of buyer 1's last settled accommodation?,The local authority code of buyer 1's last settled accommodation,The local authority name of buyer 1's last settled accommodation,"At the time of purchase, was buyer 2 living at the same address as buyer 1?",What was buyer 2's previous tenure?,Have any of the buyers ever served as a regular in the UK armed forces?,Is the buyer still serving in the UK armed forces?,Are any of the buyers a spouse or civil partner of a UK armed forces regular who died in service within the last 2 years?,Does anyone in the household consider themselves to have a disability?,Does anyone in the household use a wheelchair?,Is buyer 1's annual income known?,What is buyer 1's annual income?,Was buyer 1's income used for a mortgage application?,Is buyer 1's annual income known?,What is buyer 2's annual income?,Was buyer 2's income used for a mortgage application?,Were the buyers receiving any of these housing-related benefits immediately before buying this property?,Is the the total amount the buyers had in savings known?,What is the total amount the buyers had in savings before they paid any deposit for the property?,Have any of the buyers previously owned a property?,Was the previous property under shared ownership?,Is this a resale?,How long did the buyer(s) live in the property before purchasing it?,Day of the practical completion or handover date,Month of the practical completion or handover date,Year of the practical completion or handover date,How many bedrooms did the buyer's previous property have?,What was the previous property type?,What was the rent type of buyer's previous tenure?,What is the full purchase price?,Populated if a soft validation is confirmed.,What was the initial percentage equity stake purchased?,Was a mortgage used to buy this property?,What is the mortgage amount?,What is the length of the mortgage in years?,How much was the cash deposit paid on the property?,How much cash discount was given through Social Homebuy?,What is the basic monthly rent?,,,,,What percentage of the property has been bought in this staircasing transaction?,What percentage of the property do the buyers now own in total?,Was this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?,,,,,,,,,,"What was the amount of any loan, grant, discount or subsidy given?",What was the percentage discount?,Does this include any extra borrowing?,Does the property have any monthly leasehold charges?,What are the total monthly leasehold charges for the property?,Populated if a soft validation is confirmed.,Will the service charge change after this staircasing transaction takes place?,What are the new total monthly service charges for the property? ID,STATUS,DUPLICATESET,OWNINGORGNAME,OWNINGORGID,MANINGORGNAME,MANINGORGID,CREATEDDATE,CREATEDBY,CREATEDBYID,USERNAME,USERNAMEID,UPLOADDATE,AMENDEDBY,AMENDEDBYID,CREATIONMETHOD,BULKUPLOADID,COLLECTIONYEAR,DAY,MONTH,YEAR,PURCHID,OWNERSHIP,TYPE,STAIRCASE,JOINT,JOINTMORE,NOINT,PRIVACYNOTICE,UPRN,ADDRESS1,ADDRESS2,TOWNCITY,COUNTY,POSTCODE,ISLAINFERRED,LANAME,LA,UPRNSELECTED,ADDRESS_SEARCH_VALUE_CHECK,ADDRESS1INPUT,POSTCODEINPUT,BULKADDRESS1,BULKADDRESS2,BULKTOWNCITY,BULKCOUNTY,BULKPOSTCODE,BULKLA,PROPTYPE,BUILDHEIGHTCLASS,BEDS,BUILTYPE,WCHAIR,AGE1,SEXRAB1,GENDER_SAME_AS_SEX1,GENDER_DESCRIPTION1,ETHNICGROUP1,ETHNIC,NATIONALITYALL1,ECSTAT1,LIVEINBUYER1,RELAT2,AGE2,SEXRAB2,GENDER_SAME_AS_SEX2,GENDER_DESCRIPTION2,ETHNICGROUP2,ETHNIC2,NATIONALITYALL2,ECSTAT2,LIVEINBUYER2,HHOLDCOUNT,RELAT3,AGE3,SEXRAB3,GENDER_SAME_AS_SEX3,GENDER_DESCRIPTION3,ECSTAT3,RELAT4,AGE4,SEXRAB4,GENDER_SAME_AS_SEX4,GENDER_DESCRIPTION4,ECSTAT4,RELAT5,AGE5,SEXRAB5,GENDER_SAME_AS_SEX5,GENDER_DESCRIPTION5,ECSTAT5,RELAT6,AGE6,SEXRAB6,GENDER_SAME_AS_SEX6,GENDER_DESCRIPTION6,ECSTAT6,HHTYPE,PREVTEN,PPCODENK,PPOSTC1,PPOSTC2,PREVIOUSLAKNOWN,PREVLOC,PREVLOCNAME,BUY2LIVING,PREVTEN2,HHREGRES,HHREGRESSTILL,ARMEDFORCESSPOUSE,DISABLED,WHEEL,INC1NK,INCOME1,INC1MORT,INC2NK,INCOME2,INC2MORT,HB,SAVINGSNK,SAVINGS,PREVOWN,PREVSHARED,RESALE,PROPLEN,HODAY,HOMONTH,HOYEAR,FROMBEDS,FROMPROP,SOCPREVTEN,VALUE,VALUE_VALUE_CHECK,EQUITY,MORTGAGEUSED,MORTGAGE,MORTLEN1,DEPOSIT,CASHDIS,MRENT,HASSERVICECHARGES,SERVICECHARGES,HASESTATEFEE,ESTATEFEE,STAIRBOUGHT,STAIROWNED,STAIRCASETOSALE,FIRSTSTAIR,NUMSTAIR,STAIRLASTDAY,STAIRLASTMONTH,STAIRLASTYEAR,STAIRINITIALDAY,STAIRINITIALMONTH,STAIRINITIALYEAR,MRENTPRESTAIRCASING,GRANT,DISCOUNT,EXTRABOR,HASMSCHARGE,MSCHARGE,MSCHARGE_VALUE_CHECK,HASSERVICECHARGESCHANGED,NEWSERVICECHARGES ,completed,,MHCLG,,MHCLG,,2026-04-01T00:00:00+01:00,billyboy@eyeklaud.com,,billyboy@eyeklaud.com,,2026-04-01T00:00:00+01:00,,,single log,,2026,1,4,2026,,Discounted Ownership,Right to Acquire (RTA),,Yes,Yes,Yes,1,1,"1, Test Street",,Test Town,,AA1 1AA,Yes,Westminster,E09000033,1,,,,address line 1 as entered,address line 2 as entered,town or city as entered,county as entered,AB1 2CD,la as entered,Flat or maisonette,Low-rise,2,Purpose built,Yes,30,Female,Yes,,Buyer prefers not to say,17,United Kingdom,Full-time – 30 hours or more per week,Yes,Yes,35,Male,"No, enter gender identity",Nonbinary,Buyer prefers not to say,,United Kingdom,Full-time – 30 hours or more per week,Yes,5,No,14,Female,Yes,,Child under 16,No,Not known,Prefers not to say,"No, enter gender identity",Genderfluid,In government training into work,Prefers not to say,Not known,Prefers not to say,Prefers not to say,,Prefers not to say,,,,,,,6,Local authority tenant,Yes,SW1A,1AA,Yes,E09000033,Westminster,Don’t know,,Yes,Yes,No,Yes,Yes,Yes,13400,Yes,Yes,13400,Yes,Don’t know ,No,,Yes,No,,10,,,,,,,110000.0,,,Yes,20000.0,10,80000.0,,,,,,,,,,,,,,,,,,,10000.0,,Yes,Yes,100.0,,, diff --git a/spec/fixtures/files/sales_logs_csv_export_non_support_codes_26.csv b/spec/fixtures/files/sales_logs_csv_export_non_support_codes_26.csv index a701c8899..0b0f117b5 100644 --- a/spec/fixtures/files/sales_logs_csv_export_non_support_codes_26.csv +++ b/spec/fixtures/files/sales_logs_csv_export_non_support_codes_26.csv @@ -1,3 +1,3 @@ -Log ID,Status of log,ID of a set of duplicate logs,Which organisation owned this property before the sale?,Which organisation owned this property before the sale? (ID),Which organisation reported the sale?,Which organisation reported the sale? (ID),Time and date the log was created,User the log is assigned to (email),User the log is assigned to (ID),Time and date the log was last updated,User that last amended the log (email),User that last amended the log (ID),Was the log submitted in-service or via bulk upload?,ID of a set of bulk uploaded logs,Year collection period opened,Day of sale completion date,Month of sale completion date,Year of sale completion date,What is the purchaser code?,Shared or discounted ownership,Type of ownership,Is this a staircasing transaction?,Is this a joint purchase?,Are there more than 2 joint buyers of this property?,Did you interview the buyer to answer these questions?,Has the buyer seen the MHCLG privacy notice?,What is the UPRN of the property?,We found an address that might be this property. Is this the property address?,Address line 1 input from address matching feature,Postcode input from address matching feature,UPRN of the address selected,Address line 1,Address line 2,Town/City,County,Part 1 of the property's postcode,Part 2 of the property's postcode,LA code,LA name,What type of unit is the property?,What is the building height classification?,How many bedrooms does the property have?,Which type of building is the property?,Is the property built or adapted to wheelchair-user standards?,What is buyer 1's age?,What is buyer 1's sex registered at birth?,Is the gender buyer 1 identifies with the same as their sex registered at birth?,"If 'No', enter buyer 1's gender identity",What is buyer 1's ethnic group?,Which of the following best describes buyer 1's ethnic background?,What is buyer 1's nationality?,Which of these best describes buyer 1's working situation?,Will buyer 1 live in the property?,Is buyer 2 or person 2 the partner of buyer 1?,What is buyer 2 or person 2's age?,What is buyer 2 or person 2's sex registered at birth?,Is the gender buyer/person 2 identifies with the same as their sex registered at birth?,"If 'No', enter buyer/person 2's gender identity",What is buyer 2's ethnic group?,Which of the following best describes buyer 2's ethnic background?,What is buyer 2's nationality?,What is buyer 2 or person 2's working situation?,Will buyer 2 live in the property?,"In total, how many people live in the property?",Is person 3 the partner of buyer 1?,What is person 3's age?,What is person 3's sex registered at birth?,Is the gender person 3 identifies with the same as their sex registered at birth?,"If 'No', enter person 3's gender identity",What is person 3's working situation?,Is person 4 the partner of buyer 1?,What is person 4's age?,What is person 4's sex registered at birth?,Is the gender person 4 identifies with the same as their sex registered at birth?,"If 'No', enter person 4's gender identity",What is person 4's working situation?,Is person 5 the partner of buyer 1?,What is person 5's age?,What is person 5's sex registered at birth?,Is the gender person 5 identifies with the same as their sex registered at birth?,"If 'No', enter person 5's gender identity",What is person 5's working situation?,Is person 6 the partner of buyer 1?,What is person 6's age?,What is person 6's sex registered at birth?,Is the gender person 6 identifies with the same as their sex registered at birth?,"If 'No', enter person 6's gender identity",What is person 6's working situation?,Household type,What was buyer 1's previous tenure?,Do you know the postcode of buyer 1's last settled accommodation?,Part 1 of postcode of buyer 1's last settled accommodation,Part 2 of postcode of buyer 1's last settled accommodation,Do you know the local authority of buyer 1's last settled accommodation?,The local authority code of buyer 1's last settled accommodation,The local authority name of buyer 1's last settled accommodation,"At the time of purchase, was buyer 2 living at the same address as buyer 1?",What was buyer 2's previous tenure?,Have any of the buyers ever served as a regular in the UK armed forces?,Is the buyer still serving in the UK armed forces?,Are any of the buyers a spouse or civil partner of a UK armed forces regular who died in service within the last 2 years?,Does anyone in the household consider themselves to have a disability?,Does anyone in the household use a wheelchair?,Is buyer 1's annual income known?,What is buyer 1's annual income?,Was buyer 1's income used for a mortgage application?,Is buyer 1's annual income known?,What is buyer 2's annual income?,Was buyer 2's income used for a mortgage application?,Were the buyers receiving any of these housing-related benefits immediately before buying this property?,Is the the total amount the buyers had in savings known?,What is the total amount the buyers had in savings before they paid any deposit for the property?,Have any of the buyers previously owned a property?,Was the previous property under shared ownership?,Is this a resale?,How long did the buyer(s) live in the property before purchasing it?,Day of the practical completion or handover date,Month of the practical completion or handover date,Year of the practical completion or handover date,How many bedrooms did the buyer's previous property have?,What was the previous property type?,What was the rent type of buyer's previous tenure?,What is the full purchase price?,What was the initial percentage equity stake purchased?,Was a mortgage used to buy this property?,What is the mortgage amount?,What is the length of the mortgage in years?,How much was the cash deposit paid on the property?,How much cash discount was given through Social Homebuy?,What is the basic monthly rent?,Does the property have any service charges?,Monthly service charges amount,Does the property have an estate management fee?,Monthly estate management fee amount,What percentage of the property has been bought in this staircasing transaction?,What percentage of the property do the buyers now own in total?,Was this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?,Is this the first time the shared owner has engaged in staircasing in the home?,"Including this time, how many times has the shared owner engaged in staircasing in the home?",Day of last staircasing transaction,Month of last staircasing transaction,Year of last staircasing transaction,Day of initial staircasing transaction,Month of initial staircasing transaction,Year of initial staircasing transaction,What was the basic monthly rent prior to staircasing?,"What was the amount of any loan, grant, discount or subsidy given?",What was the percentage discount?,Does this include any extra borrowing?,Does the property have any monthly leasehold charges?,What are the total monthly leasehold charges for the property?,Will the service charge change after this staircasing transaction takes place?,What are the new total monthly service charges for the property? +Log ID,Status of log,ID of a set of duplicate logs,Which organisation owned this property before the sale?,Which organisation owned this property before the sale? (ID),Which organisation reported the sale?,Which organisation reported the sale? (ID),Time and date the log was created,User the log is assigned to (email),User the log is assigned to (ID),Time and date the log was last updated,User that last amended the log (email),User that last amended the log (ID),Was the log submitted in-service or via bulk upload?,ID of a set of bulk uploaded logs,Year collection period opened,Day of sale completion date,Month of sale completion date,Year of sale completion date,What is the purchaser code?,Shared or discounted ownership,Type of ownership,Is this a staircasing transaction?,Is this a joint purchase?,Are there more than 2 joint buyers of this property?,Did you interview the buyer(s) to answer these questions?,Has the buyer seen the MHCLG privacy notice?,What is the UPRN of the property?,We found an address that might be this property. Is this the property address?,Address line 1 input from address matching feature,Postcode input from address matching feature,UPRN of the address selected,Address line 1,Address line 2,Town/City,County,Part 1 of the property's postcode,Part 2 of the property's postcode,LA code,LA name,What type of unit is the property?,What is the building height classification?,How many bedrooms does the property have?,Which type of building is the property?,Is the property built or adapted to wheelchair-user standards?,What is buyer 1's age?,What is buyer 1's sex registered at birth?,Is the gender buyer 1 identifies with the same as their sex registered at birth?,"If 'No', enter buyer 1's gender identity",What is buyer 1's ethnic group?,Which of the following best describes buyer 1's ethnic background?,What is buyer 1's nationality?,Which of these best describes buyer 1's working situation?,Will buyer 1 live in the property?,Is buyer 2 or person 2 the partner of buyer 1?,What is buyer 2 or person 2's age?,What is buyer 2 or person 2's sex registered at birth?,Is the gender buyer/person 2 identifies with the same as their sex registered at birth?,"If 'No', enter buyer/person 2's gender identity",What is buyer 2's ethnic group?,Which of the following best describes buyer 2's ethnic background?,What is buyer 2's nationality?,What is buyer 2 or person 2's working situation?,Will buyer 2 live in the property?,"In total, how many people live in the property?",Is person 3 the partner of buyer 1?,What is person 3's age?,What is person 3's sex registered at birth?,Is the gender person 3 identifies with the same as their sex registered at birth?,"If 'No', enter person 3's gender identity",What is person 3's working situation?,Is person 4 the partner of buyer 1?,What is person 4's age?,What is person 4's sex registered at birth?,Is the gender person 4 identifies with the same as their sex registered at birth?,"If 'No', enter person 4's gender identity",What is person 4's working situation?,Is person 5 the partner of buyer 1?,What is person 5's age?,What is person 5's sex registered at birth?,Is the gender person 5 identifies with the same as their sex registered at birth?,"If 'No', enter person 5's gender identity",What is person 5's working situation?,Is person 6 the partner of buyer 1?,What is person 6's age?,What is person 6's sex registered at birth?,Is the gender person 6 identifies with the same as their sex registered at birth?,"If 'No', enter person 6's gender identity",What is person 6's working situation?,Household type,What was buyer 1's previous tenure?,Do you know the postcode of buyer 1's last settled accommodation?,Part 1 of postcode of buyer 1's last settled accommodation,Part 2 of postcode of buyer 1's last settled accommodation,Do you know the local authority of buyer 1's last settled accommodation?,The local authority code of buyer 1's last settled accommodation,The local authority name of buyer 1's last settled accommodation,"At the time of purchase, was buyer 2 living at the same address as buyer 1?",What was buyer 2's previous tenure?,Have any of the buyers ever served as a regular in the UK armed forces?,Is the buyer still serving in the UK armed forces?,Are any of the buyers a spouse or civil partner of a UK armed forces regular who died in service within the last 2 years?,Does anyone in the household consider themselves to have a disability?,Does anyone in the household use a wheelchair?,Is buyer 1's annual income known?,What is buyer 1's annual income?,Was buyer 1's income used for a mortgage application?,Is buyer 1's annual income known?,What is buyer 2's annual income?,Was buyer 2's income used for a mortgage application?,Were the buyers receiving any of these housing-related benefits immediately before buying this property?,Is the the total amount the buyers had in savings known?,What is the total amount the buyers had in savings before they paid any deposit for the property?,Have any of the buyers previously owned a property?,Was the previous property under shared ownership?,Is this a resale?,How long did the buyer(s) live in the property before purchasing it?,Day of the practical completion or handover date,Month of the practical completion or handover date,Year of the practical completion or handover date,How many bedrooms did the buyer's previous property have?,What was the previous property type?,What was the rent type of buyer's previous tenure?,What is the full purchase price?,What was the initial percentage equity stake purchased?,Was a mortgage used to buy this property?,What is the mortgage amount?,What is the length of the mortgage in years?,How much was the cash deposit paid on the property?,How much cash discount was given through Social Homebuy?,What is the basic monthly rent?,Does the property have any service charges?,Monthly service charges amount,Does the property have an estate management fee?,Monthly estate management fee amount,What percentage of the property has been bought in this staircasing transaction?,What percentage of the property do the buyers now own in total?,Was this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?,Is this the first time the shared owner has engaged in staircasing in the home?,"Including this time, how many times has the shared owner engaged in staircasing in the home?",Day of last staircasing transaction,Month of last staircasing transaction,Year of last staircasing transaction,Day of initial staircasing transaction,Month of initial staircasing transaction,Year of initial staircasing transaction,What was the basic monthly rent prior to staircasing?,"What was the amount of any loan, grant, discount or subsidy given?",What was the percentage discount?,Does this include any extra borrowing?,Does the property have any monthly leasehold charges?,What are the total monthly leasehold charges for the property?,Will the service charge change after this staircasing transaction takes place?,What are the new total monthly service charges for the property? id,status,duplicate_set_id,owning_organisation_name,owning_organisation_id,managing_organisation_name,managing_organisation_id,created_at,assigned_to,assigned_to_id,updated_at,updated_by,updated_by_id,creation_method,bulk_upload_id,collection_start_year,day,month,year,purchid,ownershipsch,type,staircase,jointpur,jointmore,noint,privacynotice,uprn,uprn_confirmed,address_line1_input,postcode_full_input,uprn_selection,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la,la_label,proptype,buildheightclass,beds,builtype,wchair,age1,sexrab1,gender_same_as_sex1,gender_description1,ethnic_group,ethnic,nationality_all,ecstat1,buy1livein,relat2,age2,sexrab2,gender_same_as_sex2,gender_description2,ethnic_group2,ethnicbuy2,nationality_all_buyer2,ecstat2,buy2livein,hholdcount,relat3,age3,sexrab3,gender_same_as_sex3,gender_description3,ecstat3,relat4,age4,sexrab4,gender_same_as_sex4,gender_description4,ecstat4,relat5,age5,sexrab5,gender_same_as_sex5,gender_description5,ecstat5,relat6,age6,sexrab6,gender_same_as_sex6,gender_description6,ecstat6,hhtype,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,resale,proplen,hoday,homonth,hoyear,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortlen,deposit,cashdis,mrent,has_servicecharges,servicecharges,has_management_fee,management_fee,stairbought,stairowned,staircasesale,firststair,numstair,stairlastday,stairlastmonth,stairlastyear,stairinitialday,stairinitialmonth,stairinitialyear,mrentprestaircasing,grant,discount,extrabor,has_mscharge,mscharge,hasservicechargeschanged,newservicecharges ,completed,,MHCLG,,MHCLG,,2026-04-01T00:00:00+01:00,billyboy@eyeklaud.com,,2026-04-01T00:00:00+01:00,,,1,,2026,1,4,2026,,2,8,,1,1,2,1,1,1,,,1,"1, Test Street",,Test Town,,SW1A,1AA,E09000033,Westminster,1,2,2,1,1,30,F,1,,17,17,826,1,1,P,35,M,2,Nonbinary,17,,826,1,1,5,X,14,F,1,,9,X,-9,R,2,Genderfluid,3,R,-9,R,3,,10,,,,,,,6,1,0,SW1A,1AA,1,E09000033,Westminster,3,,1,4,5,1,1,0,13400,1,0,13400,1,4,1,,1,2,,10,,,,,,,110000.0,,1,20000.0,10,80000.0,,,,,,,,,,,,,,,,,,,10000.0,,1,1,100.0,, diff --git a/spec/fixtures/files/sales_logs_csv_export_non_support_labels_26.csv b/spec/fixtures/files/sales_logs_csv_export_non_support_labels_26.csv index 732829543..dd8a657fb 100644 --- a/spec/fixtures/files/sales_logs_csv_export_non_support_labels_26.csv +++ b/spec/fixtures/files/sales_logs_csv_export_non_support_labels_26.csv @@ -1,3 +1,3 @@ -Log ID,Status of log,ID of a set of duplicate logs,Which organisation owned this property before the sale?,Which organisation owned this property before the sale? (ID),Which organisation reported the sale?,Which organisation reported the sale? (ID),Time and date the log was created,User the log is assigned to (email),User the log is assigned to (ID),Time and date the log was last updated,User that last amended the log (email),User that last amended the log (ID),Was the log submitted in-service or via bulk upload?,ID of a set of bulk uploaded logs,Year collection period opened,Day of sale completion date,Month of sale completion date,Year of sale completion date,What is the purchaser code?,Shared or discounted ownership,Type of ownership,Is this a staircasing transaction?,Is this a joint purchase?,Are there more than 2 joint buyers of this property?,Did you interview the buyer to answer these questions?,Has the buyer seen the MHCLG privacy notice?,What is the UPRN of the property?,We found an address that might be this property. Is this the property address?,Address line 1 input from address matching feature,Postcode input from address matching feature,UPRN of the address selected,Address line 1,Address line 2,Town/City,County,Part 1 of the property's postcode,Part 2 of the property's postcode,LA code,LA name,What type of unit is the property?,What is the building height classification?,How many bedrooms does the property have?,Which type of building is the property?,Is the property built or adapted to wheelchair-user standards?,What is buyer 1's age?,What is buyer 1's sex registered at birth?,Is the gender buyer 1 identifies with the same as their sex registered at birth?,"If 'No', enter buyer 1's gender identity",What is buyer 1's ethnic group?,Which of the following best describes buyer 1's ethnic background?,What is buyer 1's nationality?,Which of these best describes buyer 1's working situation?,Will buyer 1 live in the property?,Is buyer 2 or person 2 the partner of buyer 1?,What is buyer 2 or person 2's age?,What is buyer 2 or person 2's sex registered at birth?,Is the gender buyer/person 2 identifies with the same as their sex registered at birth?,"If 'No', enter buyer/person 2's gender identity",What is buyer 2's ethnic group?,Which of the following best describes buyer 2's ethnic background?,What is buyer 2's nationality?,What is buyer 2 or person 2's working situation?,Will buyer 2 live in the property?,"In total, how many people live in the property?",Is person 3 the partner of buyer 1?,What is person 3's age?,What is person 3's sex registered at birth?,Is the gender person 3 identifies with the same as their sex registered at birth?,"If 'No', enter person 3's gender identity",What is person 3's working situation?,Is person 4 the partner of buyer 1?,What is person 4's age?,What is person 4's sex registered at birth?,Is the gender person 4 identifies with the same as their sex registered at birth?,"If 'No', enter person 4's gender identity",What is person 4's working situation?,Is person 5 the partner of buyer 1?,What is person 5's age?,What is person 5's sex registered at birth?,Is the gender person 5 identifies with the same as their sex registered at birth?,"If 'No', enter person 5's gender identity",What is person 5's working situation?,Is person 6 the partner of buyer 1?,What is person 6's age?,What is person 6's sex registered at birth?,Is the gender person 6 identifies with the same as their sex registered at birth?,"If 'No', enter person 6's gender identity",What is person 6's working situation?,Household type,What was buyer 1's previous tenure?,Do you know the postcode of buyer 1's last settled accommodation?,Part 1 of postcode of buyer 1's last settled accommodation,Part 2 of postcode of buyer 1's last settled accommodation,Do you know the local authority of buyer 1's last settled accommodation?,The local authority code of buyer 1's last settled accommodation,The local authority name of buyer 1's last settled accommodation,"At the time of purchase, was buyer 2 living at the same address as buyer 1?",What was buyer 2's previous tenure?,Have any of the buyers ever served as a regular in the UK armed forces?,Is the buyer still serving in the UK armed forces?,Are any of the buyers a spouse or civil partner of a UK armed forces regular who died in service within the last 2 years?,Does anyone in the household consider themselves to have a disability?,Does anyone in the household use a wheelchair?,Is buyer 1's annual income known?,What is buyer 1's annual income?,Was buyer 1's income used for a mortgage application?,Is buyer 1's annual income known?,What is buyer 2's annual income?,Was buyer 2's income used for a mortgage application?,Were the buyers receiving any of these housing-related benefits immediately before buying this property?,Is the the total amount the buyers had in savings known?,What is the total amount the buyers had in savings before they paid any deposit for the property?,Have any of the buyers previously owned a property?,Was the previous property under shared ownership?,Is this a resale?,How long did the buyer(s) live in the property before purchasing it?,Day of the practical completion or handover date,Month of the practical completion or handover date,Year of the practical completion or handover date,How many bedrooms did the buyer's previous property have?,What was the previous property type?,What was the rent type of buyer's previous tenure?,What is the full purchase price?,What was the initial percentage equity stake purchased?,Was a mortgage used to buy this property?,What is the mortgage amount?,What is the length of the mortgage in years?,How much was the cash deposit paid on the property?,How much cash discount was given through Social Homebuy?,What is the basic monthly rent?,Does the property have any service charges?,Monthly service charges amount,Does the property have an estate management fee?,Monthly estate management fee amount,What percentage of the property has been bought in this staircasing transaction?,What percentage of the property do the buyers now own in total?,Was this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?,Is this the first time the shared owner has engaged in staircasing in the home?,"Including this time, how many times has the shared owner engaged in staircasing in the home?",Day of last staircasing transaction,Month of last staircasing transaction,Year of last staircasing transaction,Day of initial staircasing transaction,Month of initial staircasing transaction,Year of initial staircasing transaction,What was the basic monthly rent prior to staircasing?,"What was the amount of any loan, grant, discount or subsidy given?",What was the percentage discount?,Does this include any extra borrowing?,Does the property have any monthly leasehold charges?,What are the total monthly leasehold charges for the property?,Will the service charge change after this staircasing transaction takes place?,What are the new total monthly service charges for the property? +Log ID,Status of log,ID of a set of duplicate logs,Which organisation owned this property before the sale?,Which organisation owned this property before the sale? (ID),Which organisation reported the sale?,Which organisation reported the sale? (ID),Time and date the log was created,User the log is assigned to (email),User the log is assigned to (ID),Time and date the log was last updated,User that last amended the log (email),User that last amended the log (ID),Was the log submitted in-service or via bulk upload?,ID of a set of bulk uploaded logs,Year collection period opened,Day of sale completion date,Month of sale completion date,Year of sale completion date,What is the purchaser code?,Shared or discounted ownership,Type of ownership,Is this a staircasing transaction?,Is this a joint purchase?,Are there more than 2 joint buyers of this property?,Did you interview the buyer(s) to answer these questions?,Has the buyer seen the MHCLG privacy notice?,What is the UPRN of the property?,We found an address that might be this property. Is this the property address?,Address line 1 input from address matching feature,Postcode input from address matching feature,UPRN of the address selected,Address line 1,Address line 2,Town/City,County,Part 1 of the property's postcode,Part 2 of the property's postcode,LA code,LA name,What type of unit is the property?,What is the building height classification?,How many bedrooms does the property have?,Which type of building is the property?,Is the property built or adapted to wheelchair-user standards?,What is buyer 1's age?,What is buyer 1's sex registered at birth?,Is the gender buyer 1 identifies with the same as their sex registered at birth?,"If 'No', enter buyer 1's gender identity",What is buyer 1's ethnic group?,Which of the following best describes buyer 1's ethnic background?,What is buyer 1's nationality?,Which of these best describes buyer 1's working situation?,Will buyer 1 live in the property?,Is buyer 2 or person 2 the partner of buyer 1?,What is buyer 2 or person 2's age?,What is buyer 2 or person 2's sex registered at birth?,Is the gender buyer/person 2 identifies with the same as their sex registered at birth?,"If 'No', enter buyer/person 2's gender identity",What is buyer 2's ethnic group?,Which of the following best describes buyer 2's ethnic background?,What is buyer 2's nationality?,What is buyer 2 or person 2's working situation?,Will buyer 2 live in the property?,"In total, how many people live in the property?",Is person 3 the partner of buyer 1?,What is person 3's age?,What is person 3's sex registered at birth?,Is the gender person 3 identifies with the same as their sex registered at birth?,"If 'No', enter person 3's gender identity",What is person 3's working situation?,Is person 4 the partner of buyer 1?,What is person 4's age?,What is person 4's sex registered at birth?,Is the gender person 4 identifies with the same as their sex registered at birth?,"If 'No', enter person 4's gender identity",What is person 4's working situation?,Is person 5 the partner of buyer 1?,What is person 5's age?,What is person 5's sex registered at birth?,Is the gender person 5 identifies with the same as their sex registered at birth?,"If 'No', enter person 5's gender identity",What is person 5's working situation?,Is person 6 the partner of buyer 1?,What is person 6's age?,What is person 6's sex registered at birth?,Is the gender person 6 identifies with the same as their sex registered at birth?,"If 'No', enter person 6's gender identity",What is person 6's working situation?,Household type,What was buyer 1's previous tenure?,Do you know the postcode of buyer 1's last settled accommodation?,Part 1 of postcode of buyer 1's last settled accommodation,Part 2 of postcode of buyer 1's last settled accommodation,Do you know the local authority of buyer 1's last settled accommodation?,The local authority code of buyer 1's last settled accommodation,The local authority name of buyer 1's last settled accommodation,"At the time of purchase, was buyer 2 living at the same address as buyer 1?",What was buyer 2's previous tenure?,Have any of the buyers ever served as a regular in the UK armed forces?,Is the buyer still serving in the UK armed forces?,Are any of the buyers a spouse or civil partner of a UK armed forces regular who died in service within the last 2 years?,Does anyone in the household consider themselves to have a disability?,Does anyone in the household use a wheelchair?,Is buyer 1's annual income known?,What is buyer 1's annual income?,Was buyer 1's income used for a mortgage application?,Is buyer 1's annual income known?,What is buyer 2's annual income?,Was buyer 2's income used for a mortgage application?,Were the buyers receiving any of these housing-related benefits immediately before buying this property?,Is the the total amount the buyers had in savings known?,What is the total amount the buyers had in savings before they paid any deposit for the property?,Have any of the buyers previously owned a property?,Was the previous property under shared ownership?,Is this a resale?,How long did the buyer(s) live in the property before purchasing it?,Day of the practical completion or handover date,Month of the practical completion or handover date,Year of the practical completion or handover date,How many bedrooms did the buyer's previous property have?,What was the previous property type?,What was the rent type of buyer's previous tenure?,What is the full purchase price?,What was the initial percentage equity stake purchased?,Was a mortgage used to buy this property?,What is the mortgage amount?,What is the length of the mortgage in years?,How much was the cash deposit paid on the property?,How much cash discount was given through Social Homebuy?,What is the basic monthly rent?,Does the property have any service charges?,Monthly service charges amount,Does the property have an estate management fee?,Monthly estate management fee amount,What percentage of the property has been bought in this staircasing transaction?,What percentage of the property do the buyers now own in total?,Was this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?,Is this the first time the shared owner has engaged in staircasing in the home?,"Including this time, how many times has the shared owner engaged in staircasing in the home?",Day of last staircasing transaction,Month of last staircasing transaction,Year of last staircasing transaction,Day of initial staircasing transaction,Month of initial staircasing transaction,Year of initial staircasing transaction,What was the basic monthly rent prior to staircasing?,"What was the amount of any loan, grant, discount or subsidy given?",What was the percentage discount?,Does this include any extra borrowing?,Does the property have any monthly leasehold charges?,What are the total monthly leasehold charges for the property?,Will the service charge change after this staircasing transaction takes place?,What are the new total monthly service charges for the property? id,status,duplicate_set_id,owning_organisation_name,owning_organisation_id,managing_organisation_name,managing_organisation_id,created_at,assigned_to,assigned_to_id,updated_at,updated_by,updated_by_id,creation_method,bulk_upload_id,collection_start_year,day,month,year,purchid,ownershipsch,type,staircase,jointpur,jointmore,noint,privacynotice,uprn,uprn_confirmed,address_line1_input,postcode_full_input,uprn_selection,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la,la_label,proptype,buildheightclass,beds,builtype,wchair,age1,sexrab1,gender_same_as_sex1,gender_description1,ethnic_group,ethnic,nationality_all,ecstat1,buy1livein,relat2,age2,sexrab2,gender_same_as_sex2,gender_description2,ethnic_group2,ethnicbuy2,nationality_all_buyer2,ecstat2,buy2livein,hholdcount,relat3,age3,sexrab3,gender_same_as_sex3,gender_description3,ecstat3,relat4,age4,sexrab4,gender_same_as_sex4,gender_description4,ecstat4,relat5,age5,sexrab5,gender_same_as_sex5,gender_description5,ecstat5,relat6,age6,sexrab6,gender_same_as_sex6,gender_description6,ecstat6,hhtype,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,resale,proplen,hoday,homonth,hoyear,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortlen,deposit,cashdis,mrent,has_servicecharges,servicecharges,has_management_fee,management_fee,stairbought,stairowned,staircasesale,firststair,numstair,stairlastday,stairlastmonth,stairlastyear,stairinitialday,stairinitialmonth,stairinitialyear,mrentprestaircasing,grant,discount,extrabor,has_mscharge,mscharge,hasservicechargeschanged,newservicecharges ,completed,,MHCLG,,MHCLG,,2026-04-01T00:00:00+01:00,billyboy@eyeklaud.com,,2026-04-01T00:00:00+01:00,,,single log,,2026,1,4,2026,,Discounted Ownership,Right to Acquire (RTA),,Yes,Yes,Yes,1,1,Yes,,,1,"1, Test Street",,Test Town,,SW1A,1AA,E09000033,Westminster,Flat or maisonette,Low-rise,2,Purpose built,Yes,30,Female,Yes,,Buyer prefers not to say,17,United Kingdom,Full-time – 30 hours or more per week,Yes,Yes,35,Male,"No, enter gender identity",Nonbinary,Buyer prefers not to say,,United Kingdom,Full-time – 30 hours or more per week,Yes,5,No,14,Female,Yes,,Child under 16,No,Not known,Prefers not to say,"No, enter gender identity",Genderfluid,In government training into work,Prefers not to say,Not known,Prefers not to say,Prefers not to say,,Prefers not to say,,,,,,,6,Local authority tenant,Yes,SW1A,1AA,Yes,E09000033,Westminster,Don’t know,,Yes,Yes,No,Yes,Yes,Yes,13400,Yes,Yes,13400,Yes,Don’t know ,No,,Yes,No,,10,,,,,,,110000.0,,Yes,20000.0,10,80000.0,,,,,,,,,,,,,,,,,,,10000.0,,Yes,Yes,100.0,, diff --git a/spec/fixtures/variable_definitions/sales_download_26_27.csv b/spec/fixtures/variable_definitions/sales_download_26_27.csv index ab817bc89..3be6478d7 100644 --- a/spec/fixtures/variable_definitions/sales_download_26_27.csv +++ b/spec/fixtures/variable_definitions/sales_download_26_27.csv @@ -21,3 +21,4 @@ hasservicechargeschanged,Will the service charge change after this staircasing t newservicecharges,What are the new total monthly service charges for the property? hholdcount,In total, how many people live in the property? hhtype,Household type +noint,Did you interview the buyer(s) to answer these questions? diff --git a/spec/lib/tasks/log_variable_definitions_spec.rb b/spec/lib/tasks/log_variable_definitions_spec.rb index 4dc378719..0b9172cc5 100644 --- a/spec/lib/tasks/log_variable_definitions_spec.rb +++ b/spec/lib/tasks/log_variable_definitions_spec.rb @@ -6,7 +6,7 @@ RSpec.describe "log_variable_definitions" do subject(:task) { Rake::Task["data_import:add_variable_definitions"] } let(:path) { "spec/fixtures/variable_definitions" } - let(:total_variable_definitions_count) { 467 } + let(:total_variable_definitions_count) { 468 } before do Rake.application.rake_require("tasks/log_variable_definitions") From 18d061143d818b1bf70ec411a8d115991570105d Mon Sep 17 00:00:00 2001 From: Samuel Young Date: Mon, 23 Mar 2026 14:32:22 +0000 Subject: [PATCH 16/19] CLDC-4270: Fix text being coerced to 0 on integer fields (#3230) * CLDC-4270: Add an error for fields with the wrong type for 2026 lettings * CLDC-4270: Add tests * CLDC-4270: Port changes to other years' parsers sales 2025 2026 lettings 2026 * fixup! CLDC-4270: Add an error for fields with the wrong type for 2026 lettings * fixup! CLDC-4270: Port changes to other years' parsers * CLDC-4270: Fix tests post merge * fixup! CLDC-4270: Add tests * CLDC-4270: Add ? to boolean method --- .../lettings/year2025/csv_parser.rb | 39 +++++++++++++++++- .../lettings/year2025/row_parser.rb | 17 ++++++++ .../lettings/year2026/csv_parser.rb | 39 +++++++++++++++++- .../lettings/year2026/row_parser.rb | 17 ++++++++ .../bulk_upload/sales/year2025/csv_parser.rb | 40 ++++++++++++++++++- .../bulk_upload/sales/year2025/row_parser.rb | 19 ++++++++- .../bulk_upload/sales/year2026/csv_parser.rb | 40 ++++++++++++++++++- .../bulk_upload/sales/year2026/row_parser.rb | 19 ++++++++- .../lettings/year2025/csv_parser_spec.rb | 19 +++++++++ .../lettings/year2025/row_parser_spec.rb | 16 ++++++++ .../lettings/year2026/csv_parser_spec.rb | 19 +++++++++ .../lettings/year2026/row_parser_spec.rb | 16 ++++++++ .../sales/year2025/csv_parser_spec.rb | 19 +++++++++ .../sales/year2025/row_parser_spec.rb | 16 ++++++++ .../sales/year2026/csv_parser_spec.rb | 19 +++++++++ .../sales/year2026/row_parser_spec.rb | 16 ++++++++ 16 files changed, 360 insertions(+), 10 deletions(-) diff --git a/app/services/bulk_upload/lettings/year2025/csv_parser.rb b/app/services/bulk_upload/lettings/year2025/csv_parser.rb index ec6c33b6d..fd71e2644 100644 --- a/app/services/bulk_upload/lettings/year2025/csv_parser.rb +++ b/app/services/bulk_upload/lettings/year2025/csv_parser.rb @@ -9,6 +9,8 @@ class BulkUpload::Lettings::Year2025::CsvParser attr_reader :path + ROW_PARSER_CLASS = BulkUpload::Lettings::Year2025::RowParser + def initialize(path:) @path = path end @@ -33,11 +35,30 @@ class BulkUpload::Lettings::Year2025::CsvParser @row_parsers ||= body_rows.map { |row| next if row.empty? + invalid_fields = [] stripped_row = row[col_offset..] - hash = Hash[field_numbers.zip(stripped_row)] + hash_rows = field_numbers + .zip(stripped_row) + .map do |field, value| + field_is_valid = value_is_valid_for_field?(field, value) + + correct_value = field_is_valid ? value : nil + + invalid_fields << field unless field_is_valid + + [field, correct_value] + end + + hash = Hash[hash_rows] + + row_parser = ROW_PARSER_CLASS.new(hash) - BulkUpload::Lettings::Year2025::RowParser.new(hash) + invalid_fields.each do |field| + row_parser.add_invalid_field(field) + end + + row_parser }.compact end @@ -110,6 +131,20 @@ private @normalised_string end + # this is needed as a string passed to an int attribute is by default mapped to '0'. + # this is bad as some questions will accept a '0'. so you could enter something invalid and not be told about it + def value_is_valid_for_field?(field, value) + field_type = ROW_PARSER_CLASS.attribute_types[field] + + if field_type.is_a?(ActiveModel::Type::Integer) + value.nil? || Integer(value, exception: false).present? + elsif field_type.is_a?(ActiveModel::Type::Decimal) + value.nil? || Float(value, exception: false).present? + else + true + end + end + def first_record_start_date if with_headers? year = row_parsers.first.field_10.to_s.strip.length.between?(1, 2) ? row_parsers.first.field_10.to_i + 2000 : row_parsers.first.field_10.to_i diff --git a/app/services/bulk_upload/lettings/year2025/row_parser.rb b/app/services/bulk_upload/lettings/year2025/row_parser.rb index ea52aa9f9..d20a361e9 100644 --- a/app/services/bulk_upload/lettings/year2025/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2025/row_parser.rb @@ -506,6 +506,8 @@ class BulkUpload::Lettings::Year2025::RowParser end end + add_errors_for_invalid_fields + @valid = errors.blank? end @@ -582,6 +584,10 @@ class BulkUpload::Lettings::Year2025::RowParser end end + def add_invalid_field(field) + invalid_fields << field + end + private def normalise_case_insensitive_fields @@ -1020,6 +1026,17 @@ private end end + def invalid_fields + @invalid_fields ||= [] + end + + def add_errors_for_invalid_fields + invalid_fields.each do |field| + errors.delete(field) # take precedence over any other errors as this is a BU format issue + errors.add(field, I18n.t("#{ERROR_BASE_KEY}.invalid_option", question: QUESTIONS[field.to_sym])) + end + end + def field_mapping_for_errors { lettype: [:field_11], diff --git a/app/services/bulk_upload/lettings/year2026/csv_parser.rb b/app/services/bulk_upload/lettings/year2026/csv_parser.rb index 90b009f50..c1a570f3e 100644 --- a/app/services/bulk_upload/lettings/year2026/csv_parser.rb +++ b/app/services/bulk_upload/lettings/year2026/csv_parser.rb @@ -8,6 +8,8 @@ class BulkUpload::Lettings::Year2026::CsvParser attr_reader :path + ROW_PARSER_CLASS = BulkUpload::Lettings::Year2026::RowParser + def initialize(path:) @path = path end @@ -32,11 +34,30 @@ class BulkUpload::Lettings::Year2026::CsvParser @row_parsers ||= body_rows.map { |row| next if row.empty? + invalid_fields = [] stripped_row = row[col_offset..] - hash = Hash[field_numbers.zip(stripped_row)] + hash_rows = field_numbers + .zip(stripped_row) + .map do |field, value| + field_is_valid = value_is_valid_for_field?(field, value) + + correct_value = field_is_valid ? value : nil + + invalid_fields << field unless field_is_valid + + [field, correct_value] + end + + hash = Hash[hash_rows] + + row_parser = ROW_PARSER_CLASS.new(hash) - BulkUpload::Lettings::Year2026::RowParser.new(hash) + invalid_fields.each do |field| + row_parser.add_invalid_field(field) + end + + row_parser }.compact end @@ -118,4 +139,18 @@ private Date.new(year, rows.first[8].to_i, rows.first[7].to_i) end end + + # this is needed as a string passed to an int attribute is by default mapped to '0'. + # this is bad as some questions will accept a '0'. so you could enter something invalid and not be told about it + def value_is_valid_for_field?(field, value) + field_type = ROW_PARSER_CLASS.attribute_types[field] + + if field_type.is_a?(ActiveModel::Type::Integer) + value.nil? || Integer(value, exception: false).present? + elsif field_type.is_a?(ActiveModel::Type::Decimal) + value.nil? || Float(value, exception: false).present? + else + true + end + end end diff --git a/app/services/bulk_upload/lettings/year2026/row_parser.rb b/app/services/bulk_upload/lettings/year2026/row_parser.rb index 577a48190..c05bfe667 100644 --- a/app/services/bulk_upload/lettings/year2026/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2026/row_parser.rb @@ -541,6 +541,8 @@ class BulkUpload::Lettings::Year2026::RowParser end end + add_errors_for_invalid_fields + @valid = errors.blank? end @@ -620,6 +622,10 @@ class BulkUpload::Lettings::Year2026::RowParser end end + def add_invalid_field(field) + invalid_fields << field + end + private def normalise_case_insensitive_fields @@ -1098,6 +1104,17 @@ private end end + def invalid_fields + @invalid_fields ||= [] + end + + def add_errors_for_invalid_fields + invalid_fields.each do |field| + errors.delete(field) # take precedence over any other errors as this is a BU format issue + errors.add(field, I18n.t("#{ERROR_BASE_KEY}.invalid_option", question: QUESTIONS[field.to_sym])) + end + end + def field_mapping_for_errors { lettype: [:field_11], diff --git a/app/services/bulk_upload/sales/year2025/csv_parser.rb b/app/services/bulk_upload/sales/year2025/csv_parser.rb index ec052dbfb..9e60bb04f 100644 --- a/app/services/bulk_upload/sales/year2025/csv_parser.rb +++ b/app/services/bulk_upload/sales/year2025/csv_parser.rb @@ -9,6 +9,8 @@ class BulkUpload::Sales::Year2025::CsvParser attr_reader :path + ROW_PARSER_CLASS = BulkUpload::Sales::Year2025::RowParser + def initialize(path:) @path = path end @@ -33,10 +35,30 @@ class BulkUpload::Sales::Year2025::CsvParser @row_parsers ||= body_rows.map { |row| next if row.empty? + invalid_fields = [] stripped_row = row[col_offset..] - hash = Hash[field_numbers.zip(stripped_row)] - BulkUpload::Sales::Year2025::RowParser.new(hash) + hash_rows = field_numbers + .zip(stripped_row) + .map do |field, value| + field_is_valid = value_is_valid_for_field?(field, value) + + correct_value = field_is_valid ? value : nil + + invalid_fields << field unless field_is_valid + + [field, correct_value] + end + + hash = Hash[hash_rows] + + row_parser = ROW_PARSER_CLASS.new(hash) + + invalid_fields.each do |field| + row_parser.add_invalid_field(field) + end + + row_parser }.compact end @@ -112,6 +134,20 @@ private @normalised_string end + # this is needed as a string passed to an int attribute is by default mapped to '0'. + # this is bad as some questions will accept a '0'. so you could enter something invalid and not be told about it + def value_is_valid_for_field?(field, value) + field_type = ROW_PARSER_CLASS.attribute_types[field] + + if field_type.is_a?(ActiveModel::Type::Integer) + value.nil? || Integer(value, exception: false).present? + elsif field_type.is_a?(ActiveModel::Type::Decimal) + value.nil? || Float(value, exception: false).present? + else + true + end + end + def first_record_start_date if with_headers? year = row_parsers.first.field_3.to_s.strip.length.between?(1, 2) ? row_parsers.first.field_3.to_i + 2000 : row_parsers.first.field_3.to_i diff --git a/app/services/bulk_upload/sales/year2025/row_parser.rb b/app/services/bulk_upload/sales/year2025/row_parser.rb index 6f5e2470e..bcb00ffd9 100644 --- a/app/services/bulk_upload/sales/year2025/row_parser.rb +++ b/app/services/bulk_upload/sales/year2025/row_parser.rb @@ -288,7 +288,7 @@ class BulkUpload::Sales::Year2025::RowParser attribute :field_112, :integer attribute :field_113, :decimal - attribute :field_114, :integer + attribute :field_114, :decimal attribute :field_115, :decimal attribute :field_116, :integer attribute :field_117, :decimal @@ -503,6 +503,8 @@ class BulkUpload::Sales::Year2025::RowParser end end + add_errors_for_invalid_fields + errors.blank? end @@ -549,6 +551,10 @@ class BulkUpload::Sales::Year2025::RowParser end end + def add_invalid_field(field) + invalid_fields << field + end + private def normalise_case_insensitive_fields @@ -677,6 +683,17 @@ private [9, 14, 27, 29].include?(field_11) end + def invalid_fields + @invalid_fields ||= [] + end + + def add_errors_for_invalid_fields + invalid_fields.each do |field| + errors.delete(field) # take precedence over any other errors as this is a BU format issue + errors.add(field, I18n.t("#{ERROR_BASE_KEY}.invalid_option", question: QUESTIONS[field.to_sym])) + end + end + def field_mapping_for_errors { purchid: %i[field_7], diff --git a/app/services/bulk_upload/sales/year2026/csv_parser.rb b/app/services/bulk_upload/sales/year2026/csv_parser.rb index 9101959c1..a5d8c77ac 100644 --- a/app/services/bulk_upload/sales/year2026/csv_parser.rb +++ b/app/services/bulk_upload/sales/year2026/csv_parser.rb @@ -8,6 +8,8 @@ class BulkUpload::Sales::Year2026::CsvParser attr_reader :path + ROW_PARSER_CLASS = BulkUpload::Sales::Year2026::RowParser + def initialize(path:) @path = path end @@ -32,10 +34,30 @@ class BulkUpload::Sales::Year2026::CsvParser @row_parsers ||= body_rows.map { |row| next if row.empty? + invalid_fields = [] stripped_row = row[col_offset..] - hash = Hash[field_numbers.zip(stripped_row)] - BulkUpload::Sales::Year2026::RowParser.new(hash) + hash_rows = field_numbers + .zip(stripped_row) + .map do |field, value| + field_is_valid = value_is_valid_for_field?(field, value) + + correct_value = field_is_valid ? value : nil + + invalid_fields << field unless field_is_valid + + [field, correct_value] + end + + hash = Hash[hash_rows] + + row_parser = ROW_PARSER_CLASS.new(hash) + + invalid_fields.each do |field| + row_parser.add_invalid_field(field) + end + + row_parser }.compact end @@ -111,6 +133,20 @@ private @normalised_string end + # this is needed as a string passed to an int attribute is by default mapped to '0'. + # this is bad as some questions will accept a '0'. so you could enter something invalid and not be told about it + def value_is_valid_for_field?(field, value) + field_type = ROW_PARSER_CLASS.attribute_types[field] + + if field_type.is_a?(ActiveModel::Type::Integer) + value.nil? || Integer(value, exception: false).present? + elsif field_type.is_a?(ActiveModel::Type::Decimal) + value.nil? || Float(value, exception: false).present? + else + true + end + end + def first_record_start_date if with_headers? year = row_parsers.first.field_3.to_s.strip.length.between?(1, 2) ? row_parsers.first.field_3.to_i + 2000 : row_parsers.first.field_3.to_i diff --git a/app/services/bulk_upload/sales/year2026/row_parser.rb b/app/services/bulk_upload/sales/year2026/row_parser.rb index ed8a334fc..e2c320659 100644 --- a/app/services/bulk_upload/sales/year2026/row_parser.rb +++ b/app/services/bulk_upload/sales/year2026/row_parser.rb @@ -320,7 +320,7 @@ class BulkUpload::Sales::Year2026::RowParser attribute :field_127, :integer attribute :field_128, :decimal - attribute :field_129, :integer + attribute :field_129, :decimal attribute :field_130, :decimal attribute :field_131, :integer attribute :field_132, :decimal @@ -552,6 +552,8 @@ class BulkUpload::Sales::Year2026::RowParser end end + add_errors_for_invalid_fields + errors.blank? end @@ -598,6 +600,10 @@ class BulkUpload::Sales::Year2026::RowParser end end + def add_invalid_field(field) + invalid_fields << field + end + private def normalise_case_insensitive_fields @@ -730,6 +736,17 @@ private [9, 14, 29].include?(field_11) end + def invalid_fields + @invalid_fields ||= [] + end + + def add_errors_for_invalid_fields + invalid_fields.each do |field| + errors.delete(field) # take precedence over any other errors as this is a BU format issue + errors.add(field, I18n.t("#{ERROR_BASE_KEY}.invalid_option", question: QUESTIONS[field.to_sym])) + end + end + def field_mapping_for_errors { purchid: %i[field_7], diff --git a/spec/services/bulk_upload/lettings/year2025/csv_parser_spec.rb b/spec/services/bulk_upload/lettings/year2025/csv_parser_spec.rb index b53222f7b..098347e62 100644 --- a/spec/services/bulk_upload/lettings/year2025/csv_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2025/csv_parser_spec.rb @@ -251,4 +251,23 @@ RSpec.describe BulkUpload::Lettings::Year2025::CsvParser do end end end + + context "when parsing csv with data of the wrong type" do + let(:log_to_csv) { BulkUpload::LettingsLogToCsv.new(log:) } + let(:field_numbers) { log_to_csv.default_2025_field_numbers } + let(:field_values) { log_to_csv.to_2025_row } + + before do + field_46_index = field_numbers.index(46) + field_values[field_46_index] = "GBR" # should be a 3 digit code + + file.write(log_to_csv.custom_field_numbers_row(field_numbers:)) + file.write(log_to_csv.to_custom_csv_row(field_values:)) + file.rewind + end + + it "sets the invalid data to nil" do + expect(service.row_parsers[0].field_46).to be_nil + end + end end diff --git a/spec/services/bulk_upload/lettings/year2025/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2025/row_parser_spec.rb index 7f4384410..a62685970 100644 --- a/spec/services/bulk_upload/lettings/year2025/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2025/row_parser_spec.rb @@ -644,6 +644,22 @@ RSpec.describe BulkUpload::Lettings::Year2025::RowParser do expect(parser.errors[:field_116]).to include(match I18n.t("validations.lettings.2025.bulk_upload.invalid_option", question: "")) end end + + describe "invalid fields" do + let(:attributes) { setup_section_params.merge({ field_45: 0 }) } + + context "when a field has been marked as invalid" do + before do + parser.add_invalid_field("field_45") + end + + it "sets a single error on that field" do + parser.valid? + expect(parser.errors[:field_45].size).to eq(1) + expect(parser.errors[:field_45]).to include(I18n.t("validations.lettings.2025.bulk_upload.invalid_option", question: "What is the lead tenant’s nationality?")) + end + end + end end end diff --git a/spec/services/bulk_upload/lettings/year2026/csv_parser_spec.rb b/spec/services/bulk_upload/lettings/year2026/csv_parser_spec.rb index 0452116be..9278d133a 100644 --- a/spec/services/bulk_upload/lettings/year2026/csv_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2026/csv_parser_spec.rb @@ -251,4 +251,23 @@ RSpec.describe BulkUpload::Lettings::Year2026::CsvParser do end end end + + context "when parsing csv with data of the wrong type" do + let(:log_to_csv) { BulkUpload::LettingsLogToCsv.new(log:) } + let(:field_numbers) { log_to_csv.default_2026_field_numbers } + let(:field_values) { log_to_csv.to_2026_row } + + before do + field_46_index = field_numbers.index(46) + field_values[field_46_index] = "GBR" # should be a 3 digit code + + file.write(log_to_csv.custom_field_numbers_row(field_numbers:)) + file.write(log_to_csv.to_custom_csv_row(field_values:)) + file.rewind + end + + it "sets the invalid data to nil" do + expect(service.row_parsers[0].field_46).to be_nil + end + end end diff --git a/spec/services/bulk_upload/lettings/year2026/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2026/row_parser_spec.rb index 27c8b7672..b312f0b5e 100644 --- a/spec/services/bulk_upload/lettings/year2026/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2026/row_parser_spec.rb @@ -536,6 +536,22 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do end end end + + describe "invalid fields" do + let(:attributes) { setup_section_params.merge({ field_46: 0 }) } + + context "when a field has been marked as invalid" do + before do + parser.add_invalid_field("field_46") + end + + it "sets a single error on that field" do + parser.valid? + expect(parser.errors[:field_46].size).to eq(1) + expect(parser.errors[:field_46]).to include(match(I18n.t("validations.lettings.2026.bulk_upload.invalid_option", question: "What is the lead tenant’s nationality?"))) + end + end + end end context "when setup section not complete" do diff --git a/spec/services/bulk_upload/sales/year2025/csv_parser_spec.rb b/spec/services/bulk_upload/sales/year2025/csv_parser_spec.rb index 6a46c16ad..30993c76c 100644 --- a/spec/services/bulk_upload/sales/year2025/csv_parser_spec.rb +++ b/spec/services/bulk_upload/sales/year2025/csv_parser_spec.rb @@ -188,4 +188,23 @@ RSpec.describe BulkUpload::Sales::Year2025::CsvParser do expect(service.row_parsers[0].field_16).to eql(log.uprn) end end + + context "when parsing csv with data of the wrong type" do + let(:log_to_csv) { BulkUpload::SalesLogToCsv.new(log:) } + let(:field_numbers) { log_to_csv.default_field_numbers_for_year(2025) } + let(:field_values) { log_to_csv.to_2025_row } + + before do + field_32_index = field_numbers.index(32) + field_values[field_32_index] = "abc" # should be an integer + + file.write(log_to_csv.custom_field_numbers_row(field_numbers:)) + file.write(log_to_csv.to_custom_csv_row(field_values:)) + file.rewind + end + + it "sets the invalid data to nil" do + expect(service.row_parsers[0].field_32).to be_nil + end + end end diff --git a/spec/services/bulk_upload/sales/year2025/row_parser_spec.rb b/spec/services/bulk_upload/sales/year2025/row_parser_spec.rb index a1df099ad..d9c6032b5 100644 --- a/spec/services/bulk_upload/sales/year2025/row_parser_spec.rb +++ b/spec/services/bulk_upload/sales/year2025/row_parser_spec.rb @@ -336,6 +336,22 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do expect(parser.errors[:field_32]).to include(match I18n.t("validations.sales.2025.bulk_upload.invalid_option", question: "")) end end + + describe "invalid fields" do + let(:attributes) { setup_section_params.merge({ field_31: 0 }) } + + context "when a field has been marked as invalid" do + before do + parser.add_invalid_field("field_31") + end + + it "sets a single error on that field" do + parser.valid? + expect(parser.errors[:field_31].size).to eq(1) + expect(parser.errors[:field_31]).to include(match(I18n.t("validations.sales.2025.bulk_upload.invalid_option", question: "What is buyer 1’s nationality?"))) + end + end + end end end diff --git a/spec/services/bulk_upload/sales/year2026/csv_parser_spec.rb b/spec/services/bulk_upload/sales/year2026/csv_parser_spec.rb index abbcc7b9d..f347f6500 100644 --- a/spec/services/bulk_upload/sales/year2026/csv_parser_spec.rb +++ b/spec/services/bulk_upload/sales/year2026/csv_parser_spec.rb @@ -188,4 +188,23 @@ RSpec.describe BulkUpload::Sales::Year2026::CsvParser do expect(service.row_parsers[0].field_16).to eql(log.uprn) end end + + context "when parsing csv with data of the wrong type" do + let(:log_to_csv) { BulkUpload::SalesLogToCsv.new(log:) } + let(:field_numbers) { log_to_csv.default_field_numbers_for_year(2026) } + let(:field_values) { log_to_csv.to_2026_row } + + before do + field_34_index = field_numbers.index(34) + field_values[field_34_index] = "abc" # should be an integer + + file.write(log_to_csv.custom_field_numbers_row(field_numbers:)) + file.write(log_to_csv.to_custom_csv_row(field_values:)) + file.rewind + end + + it "sets the invalid data to nil" do + expect(service.row_parsers[0].field_34).to be_nil + end + end end diff --git a/spec/services/bulk_upload/sales/year2026/row_parser_spec.rb b/spec/services/bulk_upload/sales/year2026/row_parser_spec.rb index b40c7e65d..605674914 100644 --- a/spec/services/bulk_upload/sales/year2026/row_parser_spec.rb +++ b/spec/services/bulk_upload/sales/year2026/row_parser_spec.rb @@ -342,6 +342,22 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do expect(parser.errors[:field_35]).to include(match I18n.t("validations.sales.2026.bulk_upload.invalid_option", question: "")) end end + + describe "invalid fields" do + let(:attributes) { setup_section_params.merge({ field_34: 0 }) } + + context "when a field has been marked as invalid" do + before do + parser.add_invalid_field("field_34") + end + + it "sets a single error on that field" do + parser.valid? + expect(parser.errors[:field_34].size).to eq(1) + expect(parser.errors[:field_34]).to include(match(I18n.t("validations.sales.2026.bulk_upload.invalid_option", question: "What is buyer 1's nationality?"))) + end + end + end end end From dc02504f4c844d04c2d067cf12a617f070e1cc54 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 14:39:58 +0000 Subject: [PATCH 17/19] Bump flatted from 3.3.1 to 3.4.2 (#3259) Bumps [flatted](https://github.com/WebReflection/flatted) from 3.3.1 to 3.4.2. - [Commits](https://github.com/WebReflection/flatted/compare/v3.3.1...v3.4.2) --- updated-dependencies: - dependency-name: flatted dependency-version: 3.4.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9798885e6..b74386e3a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2619,9 +2619,9 @@ flatpickr@^4.6.9: integrity sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw== flatted@^3.2.9, flatted@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" - integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + version "3.4.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726" + integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA== for-each@^0.3.3: version "0.3.3" From d67c949474384b800aa92ba748b3fa4c9bec2e34 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 14:41:55 +0000 Subject: [PATCH 18/19] Bump json from 2.18.1 to 2.19.2 (#3260) Bumps [json](https://github.com/ruby/json) from 2.18.1 to 2.19.2. - [Release notes](https://github.com/ruby/json/releases) - [Changelog](https://github.com/ruby/json/blob/master/CHANGES.md) - [Commits](https://github.com/ruby/json/compare/v2.18.1...v2.19.2) --- updated-dependencies: - dependency-name: json dependency-version: 2.19.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index e87181aa4..54b5b82a1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -246,7 +246,7 @@ GEM jmespath (1.6.2) jsbundling-rails (1.3.0) railties (>= 6.0.0) - json (2.18.1) + json (2.19.2) json-schema (4.1.1) addressable (>= 2.8) jwt (2.8.0) From b2ed8405a3d4a37c715d98f3cf38a9eee1072dc2 Mon Sep 17 00:00:00 2001 From: Samuel Young Date: Mon, 23 Mar 2026 15:02:05 +0000 Subject: [PATCH 19/19] CLDC-4221: Add 2026 collection deadlines (#3218) * CLDC-4221: Add 2026 collection deadlines * CLDC-4221: Update tests * CLDC-4221: Use form submission deadline for 4th quarter --- app/helpers/collection_deadline_helper.rb | 14 +++---- spec/features/home_page_spec.rb | 36 +++++++++--------- .../collection_deadline_helper_spec.rb | 30 +++++++-------- spec/helpers/tasklist_helper_spec.rb | 12 +++--- spec/requests/start_controller_spec.rb | 38 +++++++++---------- 5 files changed, 65 insertions(+), 65 deletions(-) diff --git a/app/helpers/collection_deadline_helper.rb b/app/helpers/collection_deadline_helper.rb index de9c12075..6d850d062 100644 --- a/app/helpers/collection_deadline_helper.rb +++ b/app/helpers/collection_deadline_helper.rb @@ -2,17 +2,17 @@ module CollectionDeadlineHelper include CollectionTimeHelper QUARTERLY_DEADLINES = { - 2024 => { - first_quarter_deadline: Time.zone.local(2024, 7, 12), - second_quarter_deadline: Time.zone.local(2024, 10, 11), - third_quarter_deadline: Time.zone.local(2025, 1, 10), - fourth_quarter_deadline: Time.zone.local(2025, 6, 6), # Same as submission deadline - }, 2025 => { first_quarter_deadline: Time.zone.local(2025, 7, 11), second_quarter_deadline: Time.zone.local(2025, 10, 10), third_quarter_deadline: Time.zone.local(2026, 1, 16), - fourth_quarter_deadline: Time.zone.local(2026, 6, 5), # Same as submission deadline + fourth_quarter_deadline: Form::DEADLINES[2025][:submission_deadline], + }, + 2026 => { + first_quarter_deadline: Time.zone.local(2026, 7, 10), + second_quarter_deadline: Time.zone.local(2026, 10, 9), + third_quarter_deadline: Time.zone.local(2027, 1, 15), + fourth_quarter_deadline: Form::DEADLINES[2026][:submission_deadline], }, }.freeze diff --git a/spec/features/home_page_spec.rb b/spec/features/home_page_spec.rb index 0d6bc7570..c77c1002d 100644 --- a/spec/features/home_page_spec.rb +++ b/spec/features/home_page_spec.rb @@ -13,8 +13,8 @@ RSpec.describe "Home Page Features" do end describe "_upcoming_deadlines" do - let(:current_collection_year) { 2024 } - let(:next_collection_year) { 2025 } + let(:current_collection_year) { 2025 } + let(:next_collection_year) { 2026 } context "when visiting during the current collection year" do before do @@ -28,11 +28,11 @@ RSpec.describe "Home Page Features" do scenario "displays correct text for quarters" do Timecop.freeze(Time.zone.local(current_collection_year, 4, 1)) do visit root_path - find("span.govuk-details__summary-text", text: "Quarterly cut-off dates for 2024 to 2025").click - expect(page).to have_content("Q1 - Friday 12 July 2024") - expect(page).to have_content("Q2 - Friday 11 October 2024") - expect(page).to have_content("Q3 - Friday 10 January 2025") - expect(page).to have_content("End of year deadline - Friday 6 June 2025") + find("span.govuk-details__summary-text", text: "Quarterly cut-off dates for 2025 to 2026").click + expect(page).to have_content("Q1 - Friday 11 July 2025") + expect(page).to have_content("Q2 - Friday 10 October 2025") + expect(page).to have_content("Q3 - Friday 16 January 2026") + expect(page).to have_content("End of year deadline - Friday 5 June 2026") end Timecop.return end @@ -40,7 +40,7 @@ RSpec.describe "Home Page Features" do scenario "displays correct current quarter as Q1" do Timecop.freeze(Time.zone.local(current_collection_year, 4, 1)) do visit root_path - expect(page).to have_content("Q1 - Friday 12 July 2024") + expect(page).to have_content("Q1 - Friday 11 July 2025") end Timecop.return end @@ -48,7 +48,7 @@ RSpec.describe "Home Page Features" do scenario "displays correct current quarter as Q2" do Timecop.freeze(Time.zone.local(current_collection_year, 8, 1)) do visit root_path - expect(page).to have_content("Q2 - Friday 11 October 2024") + expect(page).to have_content("Q2 - Friday 10 October 2025") end Timecop.return end @@ -56,7 +56,7 @@ RSpec.describe "Home Page Features" do scenario "displays correct current quarter as Q3" do Timecop.freeze(Time.zone.local(current_collection_year, 11, 1)) do visit root_path - expect(page).to have_content("Q3 - Friday 10 January 2025") + expect(page).to have_content("Q3 - Friday 16 January 2026") end Timecop.return end @@ -74,11 +74,11 @@ RSpec.describe "Home Page Features" do scenario "displays correct text for quarters" do Timecop.freeze(Time.zone.local(next_collection_year, 4, 1)) do visit root_path - find("span.govuk-details__summary-text", text: "Quarterly cut-off dates for 2025 to 2026").click - expect(page).to have_content("Q1 - Friday 11 July 2025") - expect(page).to have_content("Q2 - Friday 10 October 2025") - expect(page).to have_content("Q3 - Friday 16 January 2026") - expect(page).to have_content("End of year deadline - Friday 5 June 2026") + find("span.govuk-details__summary-text", text: "Quarterly cut-off dates for 2026 to 2027").click + expect(page).to have_content("Q1 - Friday 10 July 2026") + expect(page).to have_content("Q2 - Friday 9 October 2026") + expect(page).to have_content("Q3 - Friday 15 January 2027") + expect(page).to have_content("End of year deadline - Friday 4 June 2027") end Timecop.return end @@ -86,7 +86,7 @@ RSpec.describe "Home Page Features" do scenario "displays correct current quarter as Q1" do Timecop.freeze(Time.zone.local(next_collection_year, 4, 1)) do visit root_path - expect(page).to have_content("Q1 - Friday 11 July 2025") + expect(page).to have_content("Q1 - Friday 10 July 2026") end Timecop.return end @@ -94,7 +94,7 @@ RSpec.describe "Home Page Features" do scenario "displays correct current quarter as Q2" do Timecop.freeze(Time.zone.local(next_collection_year, 8, 1)) do visit root_path - expect(page).to have_content("Q2 - Friday 10 October 2025") + expect(page).to have_content("Q2 - Friday 9 October 2026") end Timecop.return end @@ -102,7 +102,7 @@ RSpec.describe "Home Page Features" do scenario "displays correct current quarter as Q3" do Timecop.freeze(Time.zone.local(next_collection_year, 11, 1)) do visit root_path - expect(page).to have_content("Q3 - Friday 16 January 2026") + expect(page).to have_content("Q3 - Friday 15 January 2027") end Timecop.return end diff --git a/spec/helpers/collection_deadline_helper_spec.rb b/spec/helpers/collection_deadline_helper_spec.rb index 95f430fd6..f9f16abef 100644 --- a/spec/helpers/collection_deadline_helper_spec.rb +++ b/spec/helpers/collection_deadline_helper_spec.rb @@ -5,25 +5,25 @@ RSpec.describe CollectionDeadlineHelper do let(:user) { create(:user, :data_coordinator) } describe "#quarter_for_date" do - it "returns correct cutoff date for the first quarter of 2024/25" do - quarter = quarter_for_date(date: Time.zone.local(2024, 4, 1)) - expect(quarter.cutoff_date).to eq(Time.zone.local(2024, 7, 12)) - expect(quarter.quarter_start_date).to eq(Time.zone.local(2024, 4, 1)) - expect(quarter.quarter_end_date).to eq(Time.zone.local(2024, 6, 30)) + it "returns correct cutoff date for the first quarter of 2025/26" do + quarter = quarter_for_date(date: Time.zone.local(2025, 4, 1)) + expect(quarter.cutoff_date).to eq(Time.zone.local(2025, 7, 11)) + expect(quarter.quarter_start_date).to eq(Time.zone.local(2025, 4, 1)) + expect(quarter.quarter_end_date).to eq(Time.zone.local(2025, 6, 30)) end - it "returns correct cutoff date for the second quarter of 2024/25" do - quarter = quarter_for_date(date: Time.zone.local(2024, 9, 30)) - expect(quarter.cutoff_date).to eq(Time.zone.local(2024, 10, 11)) - expect(quarter.quarter_start_date).to eq(Time.zone.local(2024, 7, 1)) - expect(quarter.quarter_end_date).to eq(Time.zone.local(2024, 9, 30)) + it "returns correct cutoff date for the second quarter of 2025/26" do + quarter = quarter_for_date(date: Time.zone.local(2025, 9, 30)) + expect(quarter.cutoff_date).to eq(Time.zone.local(2025, 10, 10)) + expect(quarter.quarter_start_date).to eq(Time.zone.local(2025, 7, 1)) + expect(quarter.quarter_end_date).to eq(Time.zone.local(2025, 9, 30)) end - it "returns correct cutoff date for the third quarter of 2024/25" do - quarter = quarter_for_date(date: Time.zone.local(2024, 10, 25)) - expect(quarter.cutoff_date).to eq(Time.zone.local(2025, 1, 10)) - expect(quarter.quarter_start_date).to eq(Time.zone.local(2024, 10, 1)) - expect(quarter.quarter_end_date).to eq(Time.zone.local(2024, 12, 31)) + it "returns correct cutoff date for the third quarter of 2025/26" do + quarter = quarter_for_date(date: Time.zone.local(2025, 10, 25)) + expect(quarter.cutoff_date).to eq(Time.zone.local(2026, 1, 16)) + expect(quarter.quarter_start_date).to eq(Time.zone.local(2025, 10, 1)) + expect(quarter.quarter_end_date).to eq(Time.zone.local(2025, 12, 31)) end end end diff --git a/spec/helpers/tasklist_helper_spec.rb b/spec/helpers/tasklist_helper_spec.rb index 512645dfe..b4d79a6c0 100644 --- a/spec/helpers/tasklist_helper_spec.rb +++ b/spec/helpers/tasklist_helper_spec.rb @@ -232,20 +232,20 @@ RSpec.describe TasklistHelper do end context "when today is the deadline for log with sale/start date" do - let(:log) { build(:sales_log, saledate: Time.zone.local(2025, 2, 1)) } + let(:log) { build(:sales_log, saledate: Time.zone.local(2026, 2, 1)) } it "returns the overdue text" do - allow(Time.zone).to receive(:today).and_return(Time.zone.local(2025, 6, 6)) - expect(deadline_text(log)).to include("Upcoming Q4 deadline: 6 June 2025.") + allow(Time.zone).to receive(:today).and_return(Time.zone.local(2026, 6, 5)) + expect(deadline_text(log)).to include("Upcoming Q4 deadline: 5 June 2026.") end end context "when today is after the deadline for log with sale/start date" do - let(:log) { build(:sales_log, saledate: Time.zone.local(2025, 2, 1)) } + let(:log) { build(:sales_log, saledate: Time.zone.local(2026, 2, 1)) } it "returns the overdue text" do - allow(Time.zone).to receive(:today).and_return(Time.zone.local(2025, 6, 7)) - expect(deadline_text(log)).to include("Overdue: Q4 deadline 6 June 2025.") + allow(Time.zone).to receive(:today).and_return(Time.zone.local(2026, 6, 6)) + expect(deadline_text(log)).to include("Overdue: Q4 deadline 5 June 2026.") end end end diff --git a/spec/requests/start_controller_spec.rb b/spec/requests/start_controller_spec.rb index b3846809c..14f7046f1 100644 --- a/spec/requests/start_controller_spec.rb +++ b/spec/requests/start_controller_spec.rb @@ -322,41 +322,41 @@ RSpec.describe StartController, type: :request do end end - context "and 2023 collection window is open for editing" do + context "and 2024 collection window is open for editing" do before do - create(:collection_resource, :additional, year: 2023, log_type: "sales", display_name: "sales additional resource (2023 to 2024)") - allow(Time).to receive(:now).and_return(Time.zone.local(2024, 4, 1)) + create(:collection_resource, :additional, year: 2024, log_type: "sales", display_name: "sales additional resource (2024 to 2025)") + allow(Time).to receive(:now).and_return(Time.zone.local(2025, 4, 1)) end - it "displays correct resources for 2023/24 and 2024/25 collection years" do + it "displays correct resources for 2024/25 and 2025/26 collection years" do get root_path + expect(page).to have_content("Lettings 25/26") expect(page).to have_content("Lettings 24/25") - expect(page).to have_content("Lettings 23/24") + expect(page).to have_content("Lettings 2025 to 2026") expect(page).to have_content("Lettings 2024 to 2025") - expect(page).to have_content("Lettings 2023 to 2024") + expect(page).to have_content("Sales 25/26") expect(page).to have_content("Sales 24/25") - expect(page).to have_content("Sales 23/24") + expect(page).to have_content("Sales 2025 to 2026") expect(page).to have_content("Sales 2024 to 2025") - expect(page).to have_content("Sales 2023 to 2024") - expect(page).to have_content("Download the sales additional resource (2023 to 2024)") + expect(page).to have_content("Download the sales additional resource (2024 to 2025)") end end - context "and 2023 collection window is closed for editing" do + context "and 2024 collection window is closed for editing" do before do - allow(Time).to receive(:now).and_return(Time.zone.local(2024, 12, 1)) + allow(Time).to receive(:now).and_return(Time.zone.local(2025, 12, 1)) end it "displays correct resources" do get root_path - expect(page).to have_content("Lettings 24/25") - expect(page).not_to have_content("Lettings 23/24") - expect(page).to have_content("Lettings 2024 to 2025") - expect(page).not_to have_content("Lettings 2023 to 2024") - expect(page).to have_content("Sales 24/25") - expect(page).not_to have_content("Sales 23/24") - expect(page).to have_content("Sales 2024 to 2025") - expect(page).not_to have_content("Sales 2023 to 2024") + expect(page).to have_content("Lettings 25/26") + expect(page).not_to have_content("Lettings 24/25") + expect(page).to have_content("Lettings 2025 to 2026") + expect(page).not_to have_content("Lettings 2024 to 2025") + expect(page).to have_content("Sales 25/26") + expect(page).not_to have_content("Sales 24/25") + expect(page).to have_content("Sales 2025 to 2026") + expect(page).not_to have_content("Sales 2024 to 2025") end end