From a571e4b7ce9fd052d1e79481ea48f7bf3587470f Mon Sep 17 00:00:00 2001 From: Samuel Young Date: Fri, 6 Feb 2026 10:04:20 +0000 Subject: [PATCH 01/10] add the question --- app/models/form/sales/pages/service_charge.rb | 4 ++-- .../sales/pages/service_charge_staircasing.rb | 13 +++++++++++++ .../form/sales/questions/has_service_charge.rb | 15 +++++++++++---- .../form/sales/questions/service_charge.rb | 16 ++++++++++++---- .../shared_ownership_staircasing_transaction.rb | 1 + 5 files changed, 39 insertions(+), 10 deletions(-) create mode 100644 app/models/form/sales/pages/service_charge_staircasing.rb diff --git a/app/models/form/sales/pages/service_charge.rb b/app/models/form/sales/pages/service_charge.rb index a0102eda4..e938a3e56 100644 --- a/app/models/form/sales/pages/service_charge.rb +++ b/app/models/form/sales/pages/service_charge.rb @@ -6,8 +6,8 @@ class Form::Sales::Pages::ServiceCharge < ::Form::Page def questions @questions ||= [ - Form::Sales::Questions::HasServiceCharge.new(nil, nil, self), - Form::Sales::Questions::ServiceCharge.new(nil, nil, self), + Form::Sales::Questions::HasServiceCharge.new(nil, nil, self, staircasing: false), + Form::Sales::Questions::ServiceCharge.new(nil, nil, self, staircasing: false), ] end end diff --git a/app/models/form/sales/pages/service_charge_staircasing.rb b/app/models/form/sales/pages/service_charge_staircasing.rb new file mode 100644 index 000000000..4e4f65c9e --- /dev/null +++ b/app/models/form/sales/pages/service_charge_staircasing.rb @@ -0,0 +1,13 @@ +class Form::Sales::Pages::ServiceChargeStaircasing < ::Form::Page + def initialize(id, hsh, subsection) + super + @copy_key = "sales.sale_information.servicecharges" + end + + def questions + @questions ||= [ + Form::Sales::Questions::HasServiceCharge.new(nil, nil, self, staircasing: true), + Form::Sales::Questions::ServiceCharge.new(nil, nil, self, staircasing: true), + ] + end +end diff --git a/app/models/form/sales/questions/has_service_charge.rb b/app/models/form/sales/questions/has_service_charge.rb index 41bf5f809..a5cfde1fa 100644 --- a/app/models/form/sales/questions/has_service_charge.rb +++ b/app/models/form/sales/questions/has_service_charge.rb @@ -1,6 +1,6 @@ class Form::Sales::Questions::HasServiceCharge < ::Form::Question - def initialize(id, hsh, subsection) - super + def initialize(id, hsh, subsection, staircasing:) + super(id, hsh, subsection) @id = "has_mscharge" @type = "radio" @answer_options = ANSWER_OPTIONS @@ -15,7 +15,8 @@ class Form::Sales::Questions::HasServiceCharge < ::Form::Question ], } @copy_key = "sales.sale_information.servicecharges.has_servicecharge" - @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] + @staircasing = staircasing + @question_number = question_number_from_year[form.start_date.year] || question_number_from_year[question_number_from_year.keys.max] end ANSWER_OPTIONS = { @@ -23,5 +24,11 @@ class Form::Sales::Questions::HasServiceCharge < ::Form::Question "0" => { "value" => "No" }, }.freeze - QUESTION_NUMBER_FROM_YEAR = { 2025 => 88 }.freeze + def question_number_from_year + if @staircasing + { 2026 => 0 }.freeze + else + { 2025 => 88 }.freeze + end + end end diff --git a/app/models/form/sales/questions/service_charge.rb b/app/models/form/sales/questions/service_charge.rb index 6b9a76a94..cd49caee6 100644 --- a/app/models/form/sales/questions/service_charge.rb +++ b/app/models/form/sales/questions/service_charge.rb @@ -1,16 +1,24 @@ class Form::Sales::Questions::ServiceCharge < ::Form::Question - def initialize(id, hsh, subsection) - super + def initialize(id, hsh, subsection, staircasing:) + super(id, hsh, subsection) @id = "mscharge" @type = "numeric" @min = 1 + @max = 9999.99 @step = 0.01 @width = 5 @prefix = "£" @copy_key = "sales.sale_information.servicecharges.servicecharge" - @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] + @staircasing = staircasing + @question_number = question_number_from_year[form.start_date.year] || question_number_from_year[question_number_from_year.keys.max] @strip_commas = true end - QUESTION_NUMBER_FROM_YEAR = { 2025 => 88 }.freeze + def question_number_from_year + if @staircasing + { 2026 => 0 }.freeze + else + { 2025 => 88 }.freeze + end + end 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 cc10ed28c..7c1119f99 100644 --- a/app/models/form/sales/subsections/shared_ownership_staircasing_transaction.rb +++ b/app/models/form/sales/subsections/shared_ownership_staircasing_transaction.rb @@ -25,6 +25,7 @@ class Form::Sales::Subsections::SharedOwnershipStaircasingTransaction < ::Form:: Form::Sales::Pages::Mortgageused.new("staircase_mortgage_used_shared_ownership", nil, self, ownershipsch: 1), Form::Sales::Pages::MonthlyRentStaircasingOwned.new(nil, nil, self), Form::Sales::Pages::MonthlyRentStaircasing.new(nil, nil, self), + Form::Sales::Pages::ServiceChargeStaircasing.new("service_charge_staircasing", nil, self), Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_shared_ownership_value_check", nil, self), ].compact end From 80a50c03b2f3b74d9ac6f7ba88dd34dd446f8ce7 Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis Date: Tue, 17 Feb 2026 16:46:31 +0000 Subject: [PATCH 02/10] CLDC-4174: update question number for 2026 --- app/models/form/sales/questions/has_service_charge.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/form/sales/questions/has_service_charge.rb b/app/models/form/sales/questions/has_service_charge.rb index a5cfde1fa..708c32f0d 100644 --- a/app/models/form/sales/questions/has_service_charge.rb +++ b/app/models/form/sales/questions/has_service_charge.rb @@ -28,7 +28,7 @@ class Form::Sales::Questions::HasServiceCharge < ::Form::Question if @staircasing { 2026 => 0 }.freeze else - { 2025 => 88 }.freeze + { 2025 => 88, 2026 => 0 }.freeze end end end From 7e3a10450df6b1467a4ee87fc30587830220001f Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis Date: Tue, 17 Feb 2026 17:22:07 +0000 Subject: [PATCH 03/10] CLDc-4174: update question number for 2026 --- app/models/form/sales/questions/service_charge.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/form/sales/questions/service_charge.rb b/app/models/form/sales/questions/service_charge.rb index cd49caee6..f3aec4a2b 100644 --- a/app/models/form/sales/questions/service_charge.rb +++ b/app/models/form/sales/questions/service_charge.rb @@ -18,7 +18,7 @@ class Form::Sales::Questions::ServiceCharge < ::Form::Question if @staircasing { 2026 => 0 }.freeze else - { 2025 => 88 }.freeze + { 2025 => 88, 2026 => 0 }.freeze end end end From 226ac37344d6eeddf4d7711c582655a44fd13fe9 Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis Date: Tue, 17 Feb 2026 17:22:22 +0000 Subject: [PATCH 04/10] CLDc-4174: update subsection --- .../subsections/shared_ownership_staircasing_transaction.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7c1119f99..cfd00e5e1 100644 --- a/app/models/form/sales/subsections/shared_ownership_staircasing_transaction.rb +++ b/app/models/form/sales/subsections/shared_ownership_staircasing_transaction.rb @@ -25,7 +25,7 @@ class Form::Sales::Subsections::SharedOwnershipStaircasingTransaction < ::Form:: Form::Sales::Pages::Mortgageused.new("staircase_mortgage_used_shared_ownership", nil, self, ownershipsch: 1), Form::Sales::Pages::MonthlyRentStaircasingOwned.new(nil, nil, self), Form::Sales::Pages::MonthlyRentStaircasing.new(nil, nil, self), - Form::Sales::Pages::ServiceChargeStaircasing.new("service_charge_staircasing", nil, self), + (Form::Sales::Pages::ServiceChargeStaircasing.new("service_charge_staircasing", nil, self) if form.start_year_2026_or_later?), Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_shared_ownership_value_check", nil, self), ].compact end From d4a29a15833700571fb5908fd215133f4e6935a8 Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis Date: Tue, 17 Feb 2026 17:22:29 +0000 Subject: [PATCH 05/10] CLDc-4174: update question specs --- .../questions/has_service_charge_spec.rb | 50 +++++++++++++++++-- .../sales/questions/service_charge_spec.rb | 47 ++++++++++++++++- 2 files changed, 92 insertions(+), 5 deletions(-) diff --git a/spec/models/form/sales/questions/has_service_charge_spec.rb b/spec/models/form/sales/questions/has_service_charge_spec.rb index bf0bd9d25..d8a8b2132 100644 --- a/spec/models/form/sales/questions/has_service_charge_spec.rb +++ b/spec/models/form/sales/questions/has_service_charge_spec.rb @@ -1,12 +1,15 @@ require "rails_helper" RSpec.describe Form::Sales::Questions::HasServiceCharge, type: :model do - subject(:question) { described_class.new(question_id, question_definition, page) } + subject(:question) { described_class.new(question_id, question_definition, page, staircasing:) } - let(:form) { instance_double(Form, start_date: Time.zone.local(2025, 4, 4)) } let(:question_id) { nil } let(:question_definition) { nil } - let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, id: "shared_ownership", form:)) } + let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date:)) } + let(:page) { instance_double(Form::Page, subsection:) } + let(:start_date) { Time.utc(2025, 5, 1) } + let(:staircasing) { false } + it "has correct page" do expect(question.page).to eq(page) @@ -46,4 +49,45 @@ RSpec.describe Form::Sales::Questions::HasServiceCharge, type: :model do ], }) end + + context "with 2025/26 form" do + let(:start_date) { Time.utc(2025, 4, 1) } + + before do + allow(subsection.form).to receive(:start_year_2025_or_later?).and_return(true) + end + + context "when not staircasing" do + let(:staircasing) { false } + + it "has the correct question number" do + expect(question.question_number).to eq(88) + end + end + end + + context "with 2026/27 form" do + let(:start_date) { Time.utc(2026, 4, 1) } + + before do + allow(subsection.form).to receive(:start_year_2026_or_later?).and_return(true) + end + + context "when staircasing" do + let(:staircasing) { true } + + it "has the correct question number" do + expect(question.question_number).to eq(0) + end + end + + context "when not staircasing" do + let(:staircasing) { false } + + it "has the correct question number" do + expect(question.question_number).to eq(0) + end + end + end + end diff --git a/spec/models/form/sales/questions/service_charge_spec.rb b/spec/models/form/sales/questions/service_charge_spec.rb index 56dee01cf..21104d2b5 100644 --- a/spec/models/form/sales/questions/service_charge_spec.rb +++ b/spec/models/form/sales/questions/service_charge_spec.rb @@ -1,11 +1,14 @@ require "rails_helper" RSpec.describe Form::Sales::Questions::ServiceCharge, type: :model do - subject(:question) { described_class.new(question_id, question_definition, page) } + subject(:question) { described_class.new(question_id, question_definition, page, staircasing:) } 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(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date:)) } + let(:page) { instance_double(Form::Page, subsection:) } + let(:start_date) { Time.utc(2023, 4, 1) } + let(:staircasing) { false } it "has correct page" do expect(question.page).to eq(page) @@ -34,4 +37,44 @@ RSpec.describe Form::Sales::Questions::ServiceCharge, type: :model do it "has the correct prefix" do expect(question.prefix).to eq("£") end + + context "with 2025/26 form" do + let(:start_date) { Time.utc(2025, 4, 1) } + + before do + allow(subsection.form).to receive(:start_year_2025_or_later?).and_return(true) + end + + context "when not staircasing" do + let(:staircasing) { false } + + it "has the correct question number" do + expect(question.question_number).to eq(88) + end + end + end + + context "with 2026/27 form" do + let(:start_date) { Time.utc(2026, 4, 1) } + + before do + allow(subsection.form).to receive(:start_year_2026_or_later?).and_return(true) + end + + context "when staircasing" do + let(:staircasing) { true } + + it "has the correct question number" do + expect(question.question_number).to eq(0) + end + end + + context "when not staircasing" do + let(:staircasing) { false } + + it "has the correct question number" do + expect(question.question_number).to eq(0) + end + end + end end From c83febef4165692bffcf904313e9ac1f02918447 Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis Date: Wed, 18 Feb 2026 09:04:29 +0000 Subject: [PATCH 06/10] CLDC-4174: add subsection spec --- ..._ownership_staircasing_transaction_spec.rb | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 spec/models/form/sales/subsections/shared_ownership_staircasing_transaction_spec.rb 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 new file mode 100644 index 000000000..3efe482d8 --- /dev/null +++ b/spec/models/form/sales/subsections/shared_ownership_staircasing_transaction_spec.rb @@ -0,0 +1,85 @@ +require "rails_helper" + +RSpec.describe Form::Sales::Subsections::SharedOwnershipStaircasingTransaction, type: :model do + subject(:shared_ownership_staircasing_transaction) { described_class.new(nil, nil, section) } + + let(:form) { instance_double(Form, start_year_2026_or_later?: false) } + let(:section) { instance_double(Form::Sales::Sections::SaleInformation, form:) } + + it "has correct section" do + expect(shared_ownership_staircasing_transaction.section).to eq(section) + end + + it "has the correct depends_on" do + expect(shared_ownership_staircasing_transaction.depends_on).to eq([{ "ownershipsch" => 1, "setup_completed?" => true, "staircase" => 1 }]) + end + + it "has the correct id" do + expect(shared_ownership_staircasing_transaction.id).to eq("shared_ownership_staircasing_transaction") + end + + it "has the correct label" do + expect(shared_ownership_staircasing_transaction.label).to eq("Shared ownership - staircasing transaction") + end + + it "has the correct copy key" do + expect(shared_ownership_staircasing_transaction.copy_key).to eq("sale_information") + end + + context "when the start year is 2025" do + let(:form) { instance_double(Form, start_year_2025_or_later?: true, start_year_2026_or_later?: false, start_date: Time.utc(2025, 4, 1)) } + + it "has correct pages" do + expect(shared_ownership_staircasing_transaction.pages.map(&:id)).to eq( + %w[ + about_staircasing_joint_purchase + about_staircasing_not_joint_purchase + staircase_sale + staircase_bought_value_check + staircase_owned_value_check_joint_purchase + staircase_owned_value_check_not_joint_purchase + staircase_first_time + staircase_previous + staircase_initial_date + value_shared_ownership_staircase + about_price_shared_ownership_value_check_staircasing + staircase_equity + shared_ownership_equity_value_check_staircasing + staircase_mortgage_used_shared_ownership + monthly_rent_staircasing_owned + monthly_rent_staircasing + monthly_charges_shared_ownership_value_check + ], + ) + end + end + + context "when the start year is 2026" do + let(:form) { instance_double(Form, start_year_2025_or_later?: true, start_year_2026_or_later?: true, start_date: Time.utc(2026, 4, 1)) } + + it "has correct pages" do + expect(shared_ownership_staircasing_transaction.pages.map(&:id)).to eq( + %w[ + about_staircasing_joint_purchase + about_staircasing_not_joint_purchase + staircase_sale + staircase_bought_value_check + staircase_owned_value_check_joint_purchase + staircase_owned_value_check_not_joint_purchase + staircase_first_time + staircase_previous + staircase_initial_date + value_shared_ownership_staircase + about_price_shared_ownership_value_check_staircasing + staircase_equity + shared_ownership_equity_value_check_staircasing + staircase_mortgage_used_shared_ownership + monthly_rent_staircasing_owned + monthly_rent_staircasing + service_charge_staircasing + monthly_charges_shared_ownership_value_check + ], + ) + end + end +end From f10976619162c2a0bbc783c9bb303b135cf94a2d Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis Date: Wed, 18 Feb 2026 09:08:39 +0000 Subject: [PATCH 07/10] CLDc-4174: lint --- spec/models/form/sales/questions/has_service_charge_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/models/form/sales/questions/has_service_charge_spec.rb b/spec/models/form/sales/questions/has_service_charge_spec.rb index d8a8b2132..d64609bb0 100644 --- a/spec/models/form/sales/questions/has_service_charge_spec.rb +++ b/spec/models/form/sales/questions/has_service_charge_spec.rb @@ -10,7 +10,6 @@ RSpec.describe Form::Sales::Questions::HasServiceCharge, type: :model do let(:start_date) { Time.utc(2025, 5, 1) } let(:staircasing) { false } - it "has correct page" do expect(question.page).to eq(page) end @@ -89,5 +88,4 @@ RSpec.describe Form::Sales::Questions::HasServiceCharge, type: :model do end end end - end From 6a32e441b069e0c5bac0dc9d57e4ff95c01ea258 Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis Date: Wed, 18 Feb 2026 09:45:32 +0000 Subject: [PATCH 08/10] CLDc-4174: add new page test --- .../pages/service_charge_staircasing_spec.rb | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 spec/models/form/sales/pages/service_charge_staircasing_spec.rb diff --git a/spec/models/form/sales/pages/service_charge_staircasing_spec.rb b/spec/models/form/sales/pages/service_charge_staircasing_spec.rb new file mode 100644 index 000000000..d935cd748 --- /dev/null +++ b/spec/models/form/sales/pages/service_charge_staircasing_spec.rb @@ -0,0 +1,29 @@ +require "rails_helper" + +RSpec.describe Form::Sales::Pages::ServiceChargeStaircasing, type: :model do + 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(2026, 4, 1))) } + + it "has correct subsection" do + expect(page.subsection).to eq(subsection) + end + + it "has correct questions" do + expect(page.questions.map(&:id)).to eq(%w[has_mscharge mscharge]) + end + + it "has the correct id" do + expect(page.id).to eq(nil) + end + + it "has the correct description" do + expect(page.description).to be_nil + end + + it "has correct depends_on" do + expect(page.depends_on).to be_nil + end +end From 0acbf4ca47f2eebe481565b5afc51891d39f1814 Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis Date: Wed, 25 Feb 2026 10:33:08 +0000 Subject: [PATCH 09/10] CLDC-4241: lint --- spec/models/form/sales/pages/service_charge_staircasing_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/form/sales/pages/service_charge_staircasing_spec.rb b/spec/models/form/sales/pages/service_charge_staircasing_spec.rb index d935cd748..28f057ba1 100644 --- a/spec/models/form/sales/pages/service_charge_staircasing_spec.rb +++ b/spec/models/form/sales/pages/service_charge_staircasing_spec.rb @@ -12,7 +12,7 @@ RSpec.describe Form::Sales::Pages::ServiceChargeStaircasing, type: :model do end it "has correct questions" do - expect(page.questions.map(&:id)).to eq(%w[has_mscharge mscharge]) + expect(page.questions.map(&:id)).to be(%w[has_mscharge mscharge]) end it "has the correct id" do From 562841eaf0fee2e520d5f7747b97e7a47117c26c Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis Date: Wed, 25 Feb 2026 10:50:06 +0000 Subject: [PATCH 10/10] CLDC-4241: lint --- .../form/sales/pages/service_charge_staircasing_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/form/sales/pages/service_charge_staircasing_spec.rb b/spec/models/form/sales/pages/service_charge_staircasing_spec.rb index 28f057ba1..a29df7b82 100644 --- a/spec/models/form/sales/pages/service_charge_staircasing_spec.rb +++ b/spec/models/form/sales/pages/service_charge_staircasing_spec.rb @@ -12,11 +12,11 @@ RSpec.describe Form::Sales::Pages::ServiceChargeStaircasing, type: :model do end it "has correct questions" do - expect(page.questions.map(&:id)).to be(%w[has_mscharge mscharge]) + expect(page.questions.map(&:id)).to eq(%w[has_mscharge mscharge]) end it "has the correct id" do - expect(page.id).to eq(nil) + expect(page.id).to be_nil end it "has the correct description" do