Browse Source

CLDC-3755 Add 2025 shared ownership subsection (#2782)

* Add shared ownership initial purchase subsection

* Display correct subsection based on year

* Update some content

* Update LivingBeforePurchase routing

* Update routing and check for page existence in guidance

* Add management fee questions

* Only check the subsections relevant to the form

* Update content

* Update financial calculations text
pull/2804/head^2
kosiakkatrina 2 months ago committed by GitHub
parent
commit
6c10172c78
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 13
      app/models/form/sales/pages/estate_management_fee.rb
  2. 12
      app/models/form/sales/pages/living_before_purchase.rb
  3. 24
      app/models/form/sales/questions/has_management_fee.rb
  4. 12
      app/models/form/sales/questions/management_fee.rb
  5. 10
      app/models/form/sales/sections/sale_information.rb
  6. 48
      app/models/form/sales/subsections/shared_ownership_initial_purchase.rb
  7. 2
      app/models/form/sales/subsections/shared_ownership_scheme.rb
  8. 6
      app/views/form/guidance/_financial_calculations_shared_ownership.html.erb
  9. 21
      config/locales/forms/2025/sales/sale_information.en.yml
  10. 8
      db/migrate/20241114154215_add_management_fee_fields.rb
  11. 2
      db/schema.rb
  12. 5
      spec/features/accessibility_spec.rb
  13. 72
      spec/models/form/sales/pages/living_before_purchase_spec.rb
  14. 28
      spec/models/form/sales/sections/sale_information_spec.rb
  15. 95
      spec/models/form/sales/subsections/shared_ownership_initial_purchase_spec.rb

13
app/models/form/sales/pages/estate_management_fee.rb

@ -0,0 +1,13 @@
class Form::Sales::Pages::EstateManagementFee < ::Form::Page
def initialize(id, hsh, subsection)
super
@copy_key = "sales.sale_information.management_fee"
end
def questions
@questions ||= [
Form::Sales::Questions::HasManagementFee.new(nil, nil, self),
Form::Sales::Questions::ManagementFee.new(nil, nil, self),
]
end
end

12
app/models/form/sales/pages/living_before_purchase.rb

@ -19,11 +19,17 @@ class Form::Sales::Pages::LivingBeforePurchase < ::Form::Page
end
end
def depends_on
def routed_to?(log, _user)
super && page_routed_to?(log)
end
def page_routed_to?(log)
return false if form.start_year_2025_or_later? && log.resale != 2
if @joint_purchase
[{ "joint_purchase?" => true }]
log.joint_purchase?
else
[{ "not_joint_purchase?" => true }, { "jointpur" => nil }]
log.not_joint_purchase? || log.jointpur.nil?
end
end
end

24
app/models/form/sales/questions/has_management_fee.rb

@ -0,0 +1,24 @@
class Form::Sales::Questions::HasManagementFee < ::Form::Question
def initialize(id, hsh, subsection)
super
@id = "has_management_fee"
@copy_key = "sales.sale_information.management_fee.has_management_fee"
@type = "radio"
@answer_options = ANSWER_OPTIONS
@conditional_for = {
"management_fee" => [1],
}
@hidden_in_check_answers = {
"depends_on" => [
{
"has_management_fee" => 1,
},
],
}
end
ANSWER_OPTIONS = {
"1" => { "value" => "Yes" },
"0" => { "value" => "No" },
}.freeze
end

12
app/models/form/sales/questions/management_fee.rb

@ -0,0 +1,12 @@
class Form::Sales::Questions::ManagementFee < ::Form::Question
def initialize(id, hsh, subsection)
super
@id = "management_fee"
@copy_key = "sales.sale_information.management_fee.management_fee"
@type = "numeric"
@min = 1
@step = 0.01
@width = 5
@prefix = "£"
end
end

10
app/models/form/sales/sections/sale_information.rb

@ -5,9 +5,17 @@ class Form::Sales::Sections::SaleInformation < ::Form::Section
@label = "Sale information"
@description = ""
@subsections = [
Form::Sales::Subsections::SharedOwnershipScheme.new(nil, nil, self),
shared_ownership_scheme_subsection,
Form::Sales::Subsections::DiscountedOwnershipScheme.new(nil, nil, self),
Form::Sales::Subsections::OutrightSale.new(nil, nil, self),
] || []
end
def shared_ownership_scheme_subsection
if form.start_year_2025_or_later?
Form::Sales::Subsections::SharedOwnershipInitialPurchase.new(nil, nil, self)
else
Form::Sales::Subsections::SharedOwnershipScheme.new(nil, nil, self)
end
end
end

48
app/models/form/sales/subsections/shared_ownership_initial_purchase.rb

@ -0,0 +1,48 @@
class Form::Sales::Subsections::SharedOwnershipInitialPurchase < ::Form::Subsection
def initialize(id, hsh, section)
super
@id = "shared_ownership_initial_purchase"
@label = "Shared ownership - initial purchase"
@depends_on = [{ "ownershipsch" => 1, "setup_completed?" => true, "staircase" => 2 }]
end
def pages
@pages ||= [
Form::Sales::Pages::Resale.new(nil, nil, self),
Form::Sales::Pages::LivingBeforePurchase.new("living_before_purchase_shared_ownership_joint_purchase", nil, self, ownershipsch: 1, joint_purchase: true),
Form::Sales::Pages::LivingBeforePurchase.new("living_before_purchase_shared_ownership", nil, self, ownershipsch: 1, joint_purchase: false),
Form::Sales::Pages::HandoverDate.new(nil, nil, self),
Form::Sales::Pages::HandoverDateCheck.new(nil, nil, self),
Form::Sales::Pages::BuyerPrevious.new("buyer_previous_joint_purchase", nil, self, joint_purchase: true),
Form::Sales::Pages::BuyerPrevious.new("buyer_previous_not_joint_purchase", nil, self, joint_purchase: false),
Form::Sales::Pages::PreviousBedrooms.new(nil, nil, self),
Form::Sales::Pages::PreviousPropertyType.new(nil, nil, self),
Form::Sales::Pages::PreviousTenure.new(nil, nil, self),
Form::Sales::Pages::ValueSharedOwnership.new(nil, nil, self),
Form::Sales::Pages::AboutPriceValueCheck.new("about_price_shared_ownership_value_check", nil, self),
Form::Sales::Pages::Equity.new(nil, nil, self),
Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_equity_value_check", nil, self),
Form::Sales::Pages::Mortgageused.new("mortgage_used_shared_ownership", nil, self, ownershipsch: 1),
Form::Sales::Pages::MortgageValueCheck.new("mortgage_used_mortgage_value_check", nil, self),
Form::Sales::Pages::MortgageAmount.new("mortgage_amount_shared_ownership", nil, self, ownershipsch: 1),
Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_mortgage_amount_value_check", nil, self),
Form::Sales::Pages::MortgageValueCheck.new("mortgage_amount_mortgage_value_check", nil, self),
Form::Sales::Pages::MortgageLength.new("mortgage_length_shared_ownership", nil, self, ownershipsch: 1),
Form::Sales::Pages::Deposit.new("deposit_shared_ownership", nil, self, ownershipsch: 1, optional: false),
Form::Sales::Pages::Deposit.new("deposit_shared_ownership_optional", nil, self, ownershipsch: 1, optional: true),
Form::Sales::Pages::DepositValueCheck.new("deposit_joint_purchase_value_check", nil, self, joint_purchase: true),
Form::Sales::Pages::DepositValueCheck.new("deposit_value_check", nil, self, joint_purchase: false),
Form::Sales::Pages::DepositDiscount.new("deposit_discount", nil, self, optional: false),
Form::Sales::Pages::DepositDiscount.new("deposit_discount_optional", nil, self, optional: true),
Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_deposit_value_check", nil, self),
Form::Sales::Pages::MonthlyRent.new(nil, nil, self),
Form::Sales::Pages::LeaseholdCharges.new("leasehold_charges_shared_ownership", nil, self, ownershipsch: 1),
Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_shared_ownership_value_check", nil, self),
Form::Sales::Pages::EstateManagementFee.new("estate_management_fee", nil, self),
].compact
end
def displayed_in_tasklist?(log)
log.staircase == 2 && (log.ownershipsch.nil? || log.ownershipsch == 1)
end
end

2
app/models/form/sales/subsections/shared_ownership_scheme.rb

@ -11,7 +11,7 @@ class Form::Sales::Subsections::SharedOwnershipScheme < ::Form::Subsection
@pages ||= [
Form::Sales::Pages::LivingBeforePurchase.new("living_before_purchase_shared_ownership_joint_purchase", nil, self, ownershipsch: 1, joint_purchase: true),
Form::Sales::Pages::LivingBeforePurchase.new("living_before_purchase_shared_ownership", nil, self, ownershipsch: 1, joint_purchase: false),
(Form::Sales::Pages::Staircase.new(nil, nil, self) unless form.start_year_2025_or_later?),
Form::Sales::Pages::Staircase.new(nil, nil, self),
Form::Sales::Pages::AboutStaircase.new("about_staircasing_joint_purchase", nil, self, joint_purchase: true),
Form::Sales::Pages::AboutStaircase.new("about_staircasing_not_joint_purchase", nil, self, joint_purchase: false),
Form::Sales::Pages::StaircaseBoughtValueCheck.new(nil, nil, self),

6
app/views/form/guidance/_financial_calculations_shared_ownership.html.erb

@ -20,11 +20,11 @@
<% end %>
must equal
the purchase price <%= question_link("value", log, current_user) %>
<% stairbought_page = log.form.get_question("stairbought", log).page %>
<% if stairbought_page.routed_to?(log, current_user) %>
<% stairbought_page = log.form.get_question("stairbought", log)&.page %>
<% if stairbought_page&.routed_to?(log, current_user) %>
multiplied by the percentage bought <%= question_link("stairbought", log, current_user) %>
<% else %>
multiplied by the percentage equity stake <%= question_link("equity", log, current_user) %>
multiplied by the percentage equity share <%= question_link("equity", log, current_user) %>
<% end %>
</p>
<% end %>

21
config/locales/forms/2025/sales/sale_information.en.yml

@ -107,9 +107,9 @@ en:
equity:
page_header: "About the price of the property"
check_answer_label: "Initial percentage equity stake"
hint_text: "Enter the amount of initial equity held by the purchaser (for example, 25% or 50%)"
question_text: "What was the initial percentage equity stake purchased?"
check_answer_label: "Initial percentage equity share"
hint_text: "Enter the amount of initial equity share held by the purchaser (for example, 25% or 50%)"
question_text: "What was the initial percentage share purchased?"
mortgageused:
page_header: "Mortgage Amount"
@ -168,9 +168,9 @@ en:
leaseholdcharges:
page_header: ""
has_mscharge:
check_answer_label: "Does the property have any monthly leasehold charges?"
check_answer_label: "Does the property have any service charges?"
hint_text: "For example, service and management charges"
question_text: "Does the property have any monthly leasehold charges?"
question_text: "Does the property have any service charges?"
mscharge:
check_answer_label: "Monthly leasehold charges"
hint_text: ""
@ -199,3 +199,14 @@ en:
check_answer_label: "Amount of any loan, grant or subsidy"
hint_text: "For all schemes except Right to Buy (RTB), Preserved Right to Buy (PRTB), Voluntary Right to Buy (VRTB) and Rent to Buy"
question_text: "What was the amount of any loan, grant, discount or subsidy given?"
management_fee:
page_header: ""
has_management_fee:
check_answer_label: "Does the property have an estate management fee?"
hint_text: "Estate management fees are typically used for the maintenance of communal gardens, payments, private roads, car parks and/or play areas within new build estates."
question_text: "Does the property have an estate management fee?"
management_fee:
check_answer_label: "Monthly estate management fee"
hint_text: ""
question_text: "Enter the total monthly management fee"

8
db/migrate/20241114154215_add_management_fee_fields.rb

@ -0,0 +1,8 @@
class AddManagementFeeFields < ActiveRecord::Migration[7.0]
def change
change_table :sales_logs, bulk: true do |t|
t.column :has_management_fee, :integer
t.column :management_fee, :decimal, precision: 10, scale: 2
end
end
end

2
db/schema.rb

@ -758,6 +758,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_11_18_104046) do
t.integer "partner_under_16_value_check"
t.integer "multiple_partners_value_check"
t.bigint "created_by_id"
t.integer "has_management_fee"
t.decimal "management_fee", precision: 10, scale: 2
t.index ["assigned_to_id"], name: "index_sales_logs_on_assigned_to_id"
t.index ["bulk_upload_id"], name: "index_sales_logs_on_bulk_upload_id"
t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id"

5
spec/features/accessibility_spec.rb

@ -138,15 +138,18 @@ RSpec.describe "Accessibility", js: true do
routes = find_routes("sales-log", sales_log, bulk_upload)
routes.reject { |path|
routes = routes.reject { |path|
path.include?("/edit") || path.include?("/new") || path.include?("*page") ||
path.include?("/sales-logs/bulk-upload-logs/#{bulk_upload.id}") ||
path.include?("bulk-upload-soft-validations-check") || path.include?("filters/update") ||
path == "/sales-logs/bulk-upload-resume/#{bulk_upload.id}" ||
path == "/sales-logs/bulk-upload-logs" ||
path.include?("/check-answers") ||
other_form_page_ids.any? { |page_id| path.include?(page_id.dasherize) } ||
sales_log_pages.any? { |page| path.include?(page.id.dasherize) && !page.routed_to?(sales_log, user) }
}.uniq
routes + sales_log.form.subsections.map(&:id).map { |id| "/sales-logs/#{sales_log.id}/#{id.dasherize}/check-answers" }
end
before do

72
spec/models/form/sales/pages/living_before_purchase_spec.rb

@ -5,17 +5,19 @@ RSpec.describe Form::Sales::Pages::LivingBeforePurchase, type: :model do
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection) }
let(:start_year) { 2022 }
let(:form) { Form.new(nil, start_year, [], "sales") }
let(:subsection) { instance_double(Form::Subsection, depends_on: nil, form:) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
describe "questions" do
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date:)) }
let(:subsection) { instance_double(Form::Subsection, form:, depends_on: nil) }
context "when 2022" do
let(:start_date) { Time.utc(2022, 2, 8) }
let(:start_year) { 2022 }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[proplen])
@ -23,7 +25,7 @@ RSpec.describe Form::Sales::Pages::LivingBeforePurchase, type: :model do
end
context "when 2023" do
let(:start_date) { Time.utc(2023, 2, 8) }
let(:start_year) { 2023 }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[proplen_asked proplen])
@ -39,15 +41,63 @@ RSpec.describe Form::Sales::Pages::LivingBeforePurchase, type: :model do
expect(page.description).to be_nil
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "not_joint_purchase?" => true }, { "jointpur" => nil }])
end
context "when routing" do
context "with form before 2025" do
let(:start_year) { 2024 }
context "with joint purchase" do
subject(:page) { described_class.new(page_id, page_definition, subsection, ownershipsch: 1, joint_purchase: true) }
it "routes to the page when joint purchase is true" do
log = build(:sales_log, jointpur: 1)
expect(page.routed_to?(log, nil)).to eq(true)
end
it "does not route to the page when joint purchase is false" do
log = build(:sales_log, jointpur: 2)
expect(page.routed_to?(log, nil)).to eq(false)
end
it "does not route to the page when joint purchase is missing" do
log = build(:sales_log, jointpur: nil)
expect(page.routed_to?(log, nil)).to eq(false)
end
end
context "with non joint purchase" do
subject(:page) { described_class.new(page_id, page_definition, subsection, ownershipsch: 1, joint_purchase: false) }
it "routes to the page when joint purchase is false" do
log = build(:sales_log, jointpur: 2)
expect(page.routed_to?(log, nil)).to eq(true)
end
context "with joint purchase" do
subject(:page) { described_class.new(page_id, page_definition, subsection, ownershipsch: 1, joint_purchase: true) }
it "does not route to the page when joint purchase is true" do
log = build(:sales_log, jointpur: 1)
expect(page.routed_to?(log, nil)).to eq(false)
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "joint_purchase?" => true }])
it "routes to the page when joint purchase is missing" do
log = build(:sales_log, jointpur: nil)
expect(page.routed_to?(log, nil)).to eq(true)
end
end
end
context "with form on or after 2025" do
subject(:page) { described_class.new(page_id, page_definition, subsection, ownershipsch: 1, joint_purchase: true) }
let(:start_year) { 2025 }
it "routes to the page when resale is 2" do
log = build(:sales_log, jointpur: 1, resale: 2)
expect(page.routed_to?(log, nil)).to eq(true)
end
it "does not route to the page when resale is not 2" do
log = build(:sales_log, jointpur: 1, resale: nil)
expect(page.routed_to?(log, nil)).to eq(false)
end
end
end
end

28
spec/models/form/sales/sections/sale_information_spec.rb

@ -5,18 +5,32 @@ RSpec.describe Form::Sales::Sections::SaleInformation, type: :model do
let(:section_id) { nil }
let(:section_definition) { nil }
let(:form) { instance_double(Form) }
let(:form) { instance_double(Form, start_year_2025_or_later?: false) }
it "has correct form" do
expect(sale_information.form).to eq(form)
end
it "has correct subsections" do
expect(sale_information.subsections.map(&:id)).to eq(%w[
shared_ownership_scheme
discounted_ownership_scheme
outright_sale
])
context "when form is before 2025" do
it "has correct subsections" do
expect(sale_information.subsections.map(&:id)).to eq(%w[
shared_ownership_scheme
discounted_ownership_scheme
outright_sale
])
end
end
context "when form is 2025 or later" do
let(:form) { instance_double(Form, start_year_2025_or_later?: true) }
it "has correct subsections" do
expect(sale_information.subsections.map(&:id)).to eq(%w[
shared_ownership_initial_purchase
discounted_ownership_scheme
outright_sale
])
end
end
it "has the correct id" do

95
spec/models/form/sales/subsections/shared_ownership_initial_purchase_spec.rb

@ -0,0 +1,95 @@
require "rails_helper"
RSpec.describe Form::Sales::Subsections::SharedOwnershipInitialPurchase, type: :model do
subject(:shared_ownership_initial_purchase) { described_class.new(subsection_id, subsection_definition, section) }
let(:subsection_id) { nil }
let(:subsection_definition) { nil }
let(:section) { instance_double(Form::Sales::Sections::SaleInformation) }
before do
allow(section).to receive(:form).and_return(instance_double(Form, start_date: Time.zone.local(2025, 4, 1)))
end
it "has correct section" do
expect(shared_ownership_initial_purchase.section).to eq(section)
end
it "has correct pages" do
expect(shared_ownership_initial_purchase.pages.map(&:id)).to eq(
%w[
resale
living_before_purchase_shared_ownership_joint_purchase
living_before_purchase_shared_ownership
handover_date
handover_date_check
buyer_previous_joint_purchase
buyer_previous_not_joint_purchase
previous_bedrooms
previous_property_type
shared_ownership_previous_tenure
value_shared_ownership
about_price_shared_ownership_value_check
equity
shared_ownership_equity_value_check
mortgage_used_shared_ownership
mortgage_used_mortgage_value_check
mortgage_amount_shared_ownership
shared_ownership_mortgage_amount_value_check
mortgage_amount_mortgage_value_check
mortgage_length_shared_ownership
deposit_shared_ownership
deposit_shared_ownership_optional
deposit_joint_purchase_value_check
deposit_value_check
deposit_discount
deposit_discount_optional
shared_ownership_deposit_value_check
monthly_rent
leasehold_charges_shared_ownership
monthly_charges_shared_ownership_value_check
estate_management_fee
],
)
end
it "has the correct id" do
expect(shared_ownership_initial_purchase.id).to eq("shared_ownership_initial_purchase")
end
it "has the correct label" do
expect(shared_ownership_initial_purchase.label).to eq("Shared ownership - initial purchase")
end
it "has the correct depends_on" do
expect(shared_ownership_initial_purchase.depends_on).to eq([
{
"ownershipsch" => 1, "setup_completed?" => true, "staircase" => 2
},
])
end
context "when it is a shared ownership scheme and not staircase" do
let(:log) { FactoryBot.build(:sales_log, ownershipsch: 1, staircase: 2) }
it "is displayed in tasklist" do
expect(shared_ownership_initial_purchase.displayed_in_tasklist?(log)).to eq(true)
end
end
context "when it is not a shared ownership scheme" do
let(:log) { FactoryBot.build(:sales_log, ownershipsch: 2, staircase: 2) }
it "is displayed in tasklist" do
expect(shared_ownership_initial_purchase.displayed_in_tasklist?(log)).to eq(false)
end
end
context "when it is staircase" do
let(:log) { FactoryBot.build(:sales_log, ownershipsch: 1, staircase: 1) }
it "is displayed in tasklist" do
expect(shared_ownership_initial_purchase.displayed_in_tasklist?(log)).to eq(false)
end
end
end
Loading…
Cancel
Save