Browse Source

separate out shared ownership leasehold charge into separate service charge question

pull/3022/head
Carolyn 3 weeks ago
parent
commit
13a930dcb6
  1. 2
      app/helpers/bulk_upload/sales_log_to_csv.rb
  2. 1
      app/models/derived_variables/sales_log_variables.rb
  3. 14
      app/models/form/sales/pages/leasehold_charges.rb
  4. 25
      app/models/form/sales/pages/monthly_charges_value_check.rb
  5. 16
      app/models/form/sales/questions/has_leasehold_charges.rb
  6. 16
      app/models/form/sales/questions/leasehold_charges.rb
  7. 2
      app/models/form/sales/subsections/discounted_ownership_scheme.rb
  8. 2
      app/models/form/sales/subsections/outright_sale.rb
  9. 3
      app/models/form/sales/subsections/property_information.rb
  10. 4
      app/models/form/sales/subsections/shared_ownership_initial_purchase.rb
  11. 2
      app/models/form/sales/subsections/shared_ownership_scheme.rb
  12. 2
      app/models/form/sales/subsections/shared_ownership_staircasing_transaction.rb
  13. 4
      app/models/sales_log.rb
  14. 4
      app/models/validations/sales/financial_validations.rb
  15. 22
      app/models/validations/sales/soft_validations.rb
  16. 17
      app/services/bulk_upload/sales/year2025/row_parser.rb
  17. 2
      app/services/csv/sales_log_csv_service.rb
  18. 4
      app/services/exports/sales_log_export_service.rb
  19. 47
      config/locales/forms/2025/sales/sale_information.en.yml
  20. 4
      config/locales/validations/sales/financial.en.yml
  21. 8
      db/schema.rb
  22. 2
      spec/factories/sales_log.rb
  23. 140
      spec/models/validations/sales/soft_validations_spec.rb

2
app/helpers/bulk_upload/sales_log_to_csv.rb

@ -499,7 +499,7 @@ class BulkUpload::SalesLogToCsv
log.deposit, log.deposit,
log.cashdis, log.cashdis,
log.mrent, log.mrent,
log.mscharge, log.servicecharge,
log.management_fee, log.management_fee,
log.stairbought, log.stairbought,

1
app/models/derived_variables/sales_log_variables.rb

@ -7,6 +7,7 @@ module DerivedVariables::SalesLogVariables
self.pregblank = 1 if no_buyer_organisation? self.pregblank = 1 if no_buyer_organisation?
self.ethnic = 17 if ethnic_refused? self.ethnic = 17 if ethnic_refused?
self.mscharge = nil if no_monthly_leasehold_charges? self.mscharge = nil if no_monthly_leasehold_charges?
self.servicecharge = nil if no_monthly_service_charges?
if exdate.present? if exdate.present?
self.exday = exdate.day self.exday = exdate.day
self.exmonth = exdate.month self.exmonth = exdate.month

14
app/models/form/sales/pages/leasehold_charges.rb

@ -2,19 +2,7 @@ class Form::Sales::Pages::LeaseholdCharges < ::Form::Page
def initialize(id, hsh, subsection, ownershipsch:) def initialize(id, hsh, subsection, ownershipsch:)
super(id, hsh, subsection) super(id, hsh, subsection)
@ownershipsch = ownershipsch @ownershipsch = ownershipsch
end @copy_key = "sales.sale_information.leaseholdcharges"
def copy_key
if form.start_year_2025_or_later?
case @ownershipsch
when 1
"sales.sale_information.leaseholdcharges.shared_ownership"
when 2
"sales.sale_information.leaseholdcharges.discounted_ownership"
end
else
"sales.sale_information.leaseholdcharges"
end
end end
def questions def questions

25
app/models/form/sales/pages/monthly_charges_value_check.rb

@ -1,19 +1,20 @@
class Form::Sales::Pages::MonthlyChargesValueCheck < ::Form::Page class Form::Sales::Pages::MonthlyChargesValueCheck < ::Form::Page
def initialize(id, hsh, subsection) def initialize(id, hsh, subsection, ownershipsch:)
super super(id, hsh, subsection)
@depends_on = [ @depends_on = [
{ {
"monthly_charges_over_soft_max?" => true, "monthly_charges_over_soft_max?" => true,
}, },
] ]
@copy_key = "sales.soft_validations.monthly_charges_value_check" @copy_key = "sales.soft_validations.monthly_charges_value_check"
@ownershipsch = 2
@title_text = { @title_text = {
"translation" => "forms.#{form.start_date.year}.#{@copy_key}.title_text", "translation" => "forms.#{form.start_date.year}.#{@copy_key}.title_text",
"arguments" => [ "arguments" => [
{ {
"key" => "field_formatted_as_currency", "key" => "field_formatted_as_currency",
"arguments_for_key" => "mscharge", "arguments_for_key" => monthly_charge_name_from_ownershipsch(ownershipsch),
"i18n_template" => "mscharge", "i18n_template" => monthly_charge_name_from_ownershipsch(ownershipsch),
}, },
], ],
} }
@ -23,6 +24,15 @@ class Form::Sales::Pages::MonthlyChargesValueCheck < ::Form::Page
} }
end end
def monthly_charge_name_from_ownershipsch(ownershipsch)
case ownershipsch
when 1
"servicecharge"
when 2
"mscharge"
end
end
def questions def questions
@questions ||= [ @questions ||= [
Form::Sales::Questions::MonthlyChargesValueCheck.new(nil, nil, self), Form::Sales::Questions::MonthlyChargesValueCheck.new(nil, nil, self),
@ -30,6 +40,11 @@ class Form::Sales::Pages::MonthlyChargesValueCheck < ::Form::Page
end end
def interruption_screen_question_ids def interruption_screen_question_ids
%w[type mscharge proptype] case @ownershipsch
when 1
%w[type servicecharge proptype]
when 2
%w[type mscharge proptype]
end
end end
end end

16
app/models/form/sales/questions/has_leasehold_charges.rb

@ -15,22 +15,10 @@ class Form::Sales::Questions::HasLeaseholdCharges < ::Form::Question
], ],
} }
@ownershipsch = ownershipsch @ownershipsch = ownershipsch
@copy_key = "sales.sale_information.leaseholdcharges.has_mscharge"
@question_number = QUESTION_NUMBER_FROM_YEAR_AND_OWNERSHIP.fetch(form.start_date.year, QUESTION_NUMBER_FROM_YEAR_AND_OWNERSHIP.max_by { |k, _v| k }.last)[ownershipsch] @question_number = QUESTION_NUMBER_FROM_YEAR_AND_OWNERSHIP.fetch(form.start_date.year, QUESTION_NUMBER_FROM_YEAR_AND_OWNERSHIP.max_by { |k, _v| k }.last)[ownershipsch]
end end
def copy_key
if form.start_year_2025_or_later?
case @ownershipsch
when 1
"sales.sale_information.leaseholdcharges.shared_ownership.has_mscharge"
when 2
"sales.sale_information.leaseholdcharges.discounted_ownership.has_mscharge"
end
else
"sales.sale_information.leaseholdcharges.has_mscharge"
end
end
ANSWER_OPTIONS = { ANSWER_OPTIONS = {
"1" => { "value" => "Yes" }, "1" => { "value" => "Yes" },
"0" => { "value" => "No" }, "0" => { "value" => "No" },
@ -38,6 +26,6 @@ class Form::Sales::Questions::HasLeaseholdCharges < ::Form::Question
QUESTION_NUMBER_FROM_YEAR_AND_OWNERSHIP = { QUESTION_NUMBER_FROM_YEAR_AND_OWNERSHIP = {
2024 => { 1 => 99, 2 => 110, 3 => 117 }, 2024 => { 1 => 99, 2 => 110, 3 => 117 },
2025 => { 1 => 88, 2 => 111 }, 2025 => { 2 => 111 },
}.freeze }.freeze
end end

16
app/models/form/sales/questions/leasehold_charges.rb

@ -7,26 +7,14 @@ class Form::Sales::Questions::LeaseholdCharges < ::Form::Question
@step = 0.01 @step = 0.01
@width = 5 @width = 5
@prefix = "£" @prefix = "£"
@copy_key = "sales.sale_information.leaseholdcharges.mscharge"
@ownershipsch = ownershipsch @ownershipsch = ownershipsch
@question_number = QUESTION_NUMBER_FROM_YEAR_AND_OWNERSHIP.fetch(form.start_date.year, QUESTION_NUMBER_FROM_YEAR_AND_OWNERSHIP.max_by { |k, _v| k }.last)[ownershipsch] @question_number = QUESTION_NUMBER_FROM_YEAR_AND_OWNERSHIP.fetch(form.start_date.year, QUESTION_NUMBER_FROM_YEAR_AND_OWNERSHIP.max_by { |k, _v| k }.last)[ownershipsch]
end end
def copy_key
if form.start_year_2025_or_later?
case @ownershipsch
when 1
"sales.sale_information.leaseholdcharges.shared_ownership.mscharge"
when 2
"sales.sale_information.leaseholdcharges.discounted_ownership.mscharge"
end
else
"sales.sale_information.leaseholdcharges.mscharge"
end
end
QUESTION_NUMBER_FROM_YEAR_AND_OWNERSHIP = { QUESTION_NUMBER_FROM_YEAR_AND_OWNERSHIP = {
2023 => { 1 => 98, 2 => 109, 3 => 117 }, 2023 => { 1 => 98, 2 => 109, 3 => 117 },
2024 => { 1 => 99, 2 => 110, 3 => 117 }, 2024 => { 1 => 99, 2 => 110, 3 => 117 },
2025 => { 1 => 88, 2 => 111 }, 2025 => { 2 => 111 },
}.freeze }.freeze
end end

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

@ -40,7 +40,7 @@ class Form::Sales::Subsections::DiscountedOwnershipScheme < ::Form::Subsection
Form::Sales::Pages::DepositAndMortgageValueCheck.new("discounted_ownership_deposit_and_mortgage_value_check_after_deposit", nil, self), Form::Sales::Pages::DepositAndMortgageValueCheck.new("discounted_ownership_deposit_and_mortgage_value_check_after_deposit", nil, self),
Form::Sales::Pages::DiscountedSaleValueCheck.new("discounted_sale_deposit_value_check", nil, self), Form::Sales::Pages::DiscountedSaleValueCheck.new("discounted_sale_deposit_value_check", nil, self),
Form::Sales::Pages::LeaseholdCharges.new("leasehold_charges_discounted_ownership", nil, self, ownershipsch: 2), Form::Sales::Pages::LeaseholdCharges.new("leasehold_charges_discounted_ownership", nil, self, ownershipsch: 2),
Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_discounted_ownership_value_check", nil, self), Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_discounted_ownership_value_check", nil, self, ownershipsch: 2),
].flatten.compact ].flatten.compact
end end

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

@ -23,7 +23,7 @@ class Form::Sales::Subsections::OutrightSale < ::Form::Subsection
Form::Sales::Pages::DepositValueCheck.new("outright_sale_deposit_joint_purchase_value_check", nil, self, joint_purchase: true), Form::Sales::Pages::DepositValueCheck.new("outright_sale_deposit_joint_purchase_value_check", nil, self, joint_purchase: true),
Form::Sales::Pages::DepositValueCheck.new("outright_sale_deposit_value_check", nil, self, joint_purchase: false), Form::Sales::Pages::DepositValueCheck.new("outright_sale_deposit_value_check", nil, self, joint_purchase: false),
leasehold_charge_pages, leasehold_charge_pages,
Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_outright_sale_value_check", nil, self), Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_outright_sale_value_check", nil, self, ownershipsch: 3),
].flatten.compact ].flatten.compact
end end

3
app/models/form/sales/subsections/property_information.rb

@ -13,7 +13,8 @@ class Form::Sales::Subsections::PropertyInformation < ::Form::Subsection
Form::Sales::Pages::PropertyNumberOfBedrooms.new(nil, nil, self), Form::Sales::Pages::PropertyNumberOfBedrooms.new(nil, nil, self),
Form::Sales::Pages::AboutPriceValueCheck.new("about_price_bedrooms_value_check", nil, self), Form::Sales::Pages::AboutPriceValueCheck.new("about_price_bedrooms_value_check", nil, self),
(Form::Sales::Pages::PropertyUnitType.new(nil, nil, self) unless form.start_year_2025_or_later?), (Form::Sales::Pages::PropertyUnitType.new(nil, nil, self) unless form.start_year_2025_or_later?),
Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_property_type_value_check", nil, self), # TODO: refactor so that MonthlyChargesValueCheck gets ownershipsch (or so it doesn't need it?)
Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_property_type_value_check", nil, self, ownershipsch: 2),
Form::Sales::Pages::PercentageDiscountValueCheck.new("percentage_discount_proptype_value_check", nil, self), Form::Sales::Pages::PercentageDiscountValueCheck.new("percentage_discount_proptype_value_check", nil, self),
Form::Sales::Pages::PropertyBuildingType.new(nil, nil, self), Form::Sales::Pages::PropertyBuildingType.new(nil, nil, self),
(uprn_questions if form.start_date.year == 2023), (uprn_questions if form.start_date.year == 2023),

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

@ -37,8 +37,8 @@ class Form::Sales::Subsections::SharedOwnershipInitialPurchase < ::Form::Subsect
Form::Sales::Pages::DepositDiscount.new("deposit_discount_optional", nil, self, optional: true), 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::SharedOwnershipDepositValueCheck.new("shared_ownership_deposit_value_check", nil, self),
Form::Sales::Pages::MonthlyRent.new(nil, 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::ServiceCharge.new("service_charges_shared_ownership", nil, self, ownershipsch: 1),
Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_shared_ownership_value_check", nil, self), Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_shared_ownership_value_check", nil, self, ownershipsch: 1),
Form::Sales::Pages::EstateManagementFee.new("estate_management_fee", nil, self), Form::Sales::Pages::EstateManagementFee.new("estate_management_fee", nil, self),
].compact ].compact
end end

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

@ -49,7 +49,7 @@ class Form::Sales::Subsections::SharedOwnershipScheme < ::Form::Subsection
Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_deposit_value_check", nil, self), Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_deposit_value_check", nil, self),
Form::Sales::Pages::MonthlyRent.new(nil, nil, self), Form::Sales::Pages::MonthlyRent.new(nil, nil, self),
Form::Sales::Pages::LeaseholdCharges.new("leasehold_charges_shared_ownership", nil, self, ownershipsch: 1), 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::MonthlyChargesValueCheck.new("monthly_charges_shared_ownership_value_check", nil, self, ownershipsch: 1),
].compact ].compact
end end

2
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::Mortgageused.new("staircase_mortgage_used_shared_ownership", nil, self, ownershipsch: 1),
Form::Sales::Pages::MonthlyRentStaircasingOwned.new(nil, nil, self), Form::Sales::Pages::MonthlyRentStaircasingOwned.new(nil, nil, self),
Form::Sales::Pages::MonthlyRentStaircasing.new(nil, nil, self), Form::Sales::Pages::MonthlyRentStaircasing.new(nil, nil, self),
Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_shared_ownership_value_check", nil, self), Form::Sales::Pages::MonthlyChargesValueCheck.new("monthly_charges_shared_ownership_value_check", nil, self, ownershipsch: 1),
].compact ].compact
end end

4
app/models/sales_log.rb

@ -391,6 +391,10 @@ class SalesLog < Log
has_mscharge&.zero? has_mscharge&.zero?
end end
def no_monthly_service_charges?
has_servicecharge&.zero?
end
def no_buyer_organisation? def no_buyer_organisation?
pregyrha&.zero? && pregyrha&.zero? &&
pregla&.zero? && pregla&.zero? &&

4
app/models/validations/sales/financial_validations.rb

@ -40,6 +40,10 @@ module Validations::Sales::FinancialValidations
record.errors.add :mortgage, :cannot_be_0, message: I18n.t("validations.sales.financial.mortgage.mortgage_zero") if record.mortgage_used? && record.mortgage&.zero? record.errors.add :mortgage, :cannot_be_0, message: I18n.t("validations.sales.financial.mortgage.mortgage_zero") if record.mortgage_used? && record.mortgage&.zero?
end end
def validate_monthly_service_charges(record)
record.errors.add :servicecharge, I18n.t("validations.sales.financial.servicecharge.monthly_service_charges.not_zero") if record.servicecharge&.zero?
end
def validate_monthly_leasehold_charges(record) def validate_monthly_leasehold_charges(record)
record.errors.add :mscharge, I18n.t("validations.sales.financial.mscharge.monthly_leasehold_charges.not_zero") if record.mscharge&.zero? record.errors.add :mscharge, I18n.t("validations.sales.financial.mscharge.monthly_leasehold_charges.not_zero") if record.mscharge&.zero?
end end

22
app/models/validations/sales/soft_validations.rb

@ -149,11 +149,27 @@ module Validations::Sales::SoftValidations
!grant.between?(9_000, 16_000) !grant.between?(9_000, 16_000)
end end
def monthly_charges_over_soft_max? def service_charges_over_soft_max?
return unless type && mscharge && proptype return unless type && servicecharge && proptype
soft_max = old_persons_shared_ownership? ? 550 : 300 soft_max = old_persons_shared_ownership? ? 550 : 300
mscharge > soft_max servicecharge > soft_max
end
def monthly_charges_over_soft_max?
return unless type && proptype && ownershipsch
if discounted_ownership_sale?
return unless mscharge
soft_max = old_persons_shared_ownership? ? 550 : 300
mscharge > soft_max
elsif shared_ownership_scheme?
return unless servicecharge
soft_max = old_persons_shared_ownership? ? 550 : 300
servicecharge > soft_max
end
end end
(2..6).each do |person_num| (2..6).each do |person_num|

17
app/services/bulk_upload/sales/year2025/row_parser.rb

@ -727,8 +727,10 @@ private
cashdis: %i[field_92], cashdis: %i[field_92],
mrent: mrent_fields, mrent: mrent_fields,
has_mscharge: mscharge_fields, has_servicecharge: %i[field_94],
mscharge: mscharge_fields, servicecharge: %i[field_94],
has_mscharge: %i[field_121],
mscharge: %i[field_121],
grant: %i[field_114], grant: %i[field_114],
discount: %i[field_115], discount: %i[field_115],
owning_organisation_id: %i[field_4], owning_organisation_id: %i[field_4],
@ -888,7 +890,9 @@ private
attributes["cashdis"] = field_92 attributes["cashdis"] = field_92
attributes["mrent"] = mrent attributes["mrent"] = mrent
attributes["mscharge"] = mscharge if mscharge&.positive? attributes["servicecharge"] = field_94 if field_94&.positive?
attributes["has_servicecharge"] = attributes["servicecharge"].present? ? 1 : 0
attributes["mscharge"] = field_121 if field_121&.positive?
attributes["has_mscharge"] = attributes["mscharge"].present? ? 1 : 0 attributes["has_mscharge"] = attributes["mscharge"].present? ? 1 : 0
attributes["grant"] = field_114 attributes["grant"] = field_114
attributes["discount"] = field_115 attributes["discount"] = field_115
@ -1172,13 +1176,6 @@ private
%i[field_93 field_111] %i[field_93 field_111]
end end
def mscharge_fields
return [:field_94] if shared_ownership?
return [:field_121] if discounted_ownership?
%i[field_94 field_121]
end
def mortlen_fields def mortlen_fields
return [:field_90] if shared_ownership? return [:field_90] if shared_ownership?
return [:field_118] if discounted_ownership? return [:field_118] if discounted_ownership?

2
app/services/csv/sales_log_csv_service.rb

@ -148,6 +148,7 @@ module Csv
"soctenant" => "SOCTEN", "soctenant" => "SOCTEN",
"mortlen" => "MORTLEN1", "mortlen" => "MORTLEN1",
"has_mscharge" => "HASMSCHARGE", "has_mscharge" => "HASMSCHARGE",
"has_servicecharge" => "HASSERVICECHARGES",
"nationalbuy2" => "NATIONAL2", "nationalbuy2" => "NATIONAL2",
"uprn_confirmed" => "UPRNCONFIRMED", "uprn_confirmed" => "UPRNCONFIRMED",
}.freeze }.freeze
@ -212,6 +213,7 @@ module Csv
"managing_organisation_id" => %w[managing_organisation_name], "managing_organisation_id" => %w[managing_organisation_name],
"value" => %w[value value_value_check], "value" => %w[value value_value_check],
"mscharge" => %w[mscharge mscharge_value_check], "mscharge" => %w[mscharge mscharge_value_check],
"servicecharge" => %w[servicecharge mscharge_value_check],
} }
unless @user.support? && @year >= 2024 unless @user.support? && @year >= 2024
mappings["postcode_full"] = %w[pcode1 pcode2] mappings["postcode_full"] = %w[pcode1 pcode2]

4
app/services/exports/sales_log_export_service.rb

@ -71,8 +71,8 @@ module Exports
attribute_hash["previouslaknown"] = sales_log.previous_la_known attribute_hash["previouslaknown"] = sales_log.previous_la_known
attribute_hash["hasmscharge"] = sales_log.discounted_ownership_sale? ? sales_log.has_mscharge : nil attribute_hash["hasmscharge"] = sales_log.discounted_ownership_sale? ? sales_log.has_mscharge : nil
attribute_hash["mscharge"] = sales_log.discounted_ownership_sale? ? sales_log.mscharge : nil attribute_hash["mscharge"] = sales_log.discounted_ownership_sale? ? sales_log.mscharge : nil
attribute_hash["hasservicecharges"] = sales_log.shared_ownership_scheme? ? sales_log.has_mscharge : nil attribute_hash["hasservicecharges"] = sales_log.shared_ownership_scheme? ? sales_log.has_servicecharge : nil
attribute_hash["servicecharges"] = sales_log.shared_ownership_scheme? ? sales_log.mscharge : nil attribute_hash["servicecharges"] = sales_log.shared_ownership_scheme? ? sales_log.servicecharge : nil
attribute_hash["hoday"] = sales_log.hodate&.day attribute_hash["hoday"] = sales_log.hodate&.day
attribute_hash["homonth"] = sales_log.hodate&.month attribute_hash["homonth"] = sales_log.hodate&.month

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

@ -246,30 +246,29 @@ en:
question_text: "What is the basic monthly rent after staircasing?" question_text: "What is the basic monthly rent after staircasing?"
leaseholdcharges: leaseholdcharges:
shared_ownership: page_header: ""
page_header: "" has_mscharge:
has_mscharge: check_answer_label: "Property leasehold charges"
check_answer_label: "Property service charges" check_answer_prompt: "Enter leasehold charges if any"
check_answer_prompt: "Enter service charges if any" hint_text: "For example, service and management charges"
hint_text: "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." question_text: "Does the property have any monthly leasehold charges?"
question_text: "Does the property have any service charges?" mscharge:
mscharge: check_answer_label: "Monthly leasehold charges"
check_answer_label: "Monthly leasehold charges" check_answer_prompt: ""
check_answer_prompt: "" hint_text: ""
hint_text: "" question_text: "Enter the total monthly charge"
question_text: "Enter the total monthly charge" servicecharges:
discounted_ownership: page_header: ""
page_header: "" has_servicecharge:
has_mscharge: check_answer_label: "Property service charges"
check_answer_label: "Property leasehold charges" check_answer_prompt: "Enter service charges if any"
check_answer_prompt: "Enter leasehold charges if any" hint_text: "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."
hint_text: "For example, service and management charges" question_text: "Does the property have any service charges?"
question_text: "Does the property have any monthly leasehold charges?" servicecharge:
mscharge: check_answer_label: "Monthly service charges"
check_answer_label: "Monthly leasehold charges" check_answer_prompt: ""
check_answer_prompt: "" hint_text: ""
hint_text: "" question_text: "Enter the total monthly charge"
question_text: "Enter the total monthly charge"
purchase_price: purchase_price:
discounted_ownership: discounted_ownership:

4
config/locales/validations/sales/financial.en.yml

@ -49,6 +49,10 @@ en:
monthly_leasehold_charges: monthly_leasehold_charges:
not_zero: "Monthly leasehold charges cannot be £0 if the property has monthly charges." not_zero: "Monthly leasehold charges cannot be £0 if the property has monthly charges."
servicecharge:
monthly_service_charges:
not_zero: "Monthly service charges cannot be £0 if the property has monthly charges."
resale: resale:
equity_over_max: "The maximum initial equity stake is %{max_equity}%." equity_over_max: "The maximum initial equity stake is %{max_equity}%."

8
db/schema.rb

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.2].define(version: 2025_03_05_092900) do ActiveRecord::Schema[7.2].define(version: 2025_03_27_125423) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -84,7 +84,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_03_05_092900) do
t.datetime "last_accessed" t.datetime "last_accessed"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.check_constraint "log_type::text = ANY (ARRAY['lettings'::character varying, 'sales'::character varying]::text[])", name: "log_type_check" t.check_constraint "log_type::text = ANY (ARRAY['lettings'::character varying::text, 'sales'::character varying::text])", name: "log_type_check"
t.check_constraint "year >= 2000 AND year <= 2099", name: "year_check" t.check_constraint "year >= 2000 AND year <= 2099", name: "year_check"
end end
@ -373,8 +373,8 @@ ActiveRecord::Schema[7.2].define(version: 2025_03_05_092900) do
t.integer "partner_under_16_value_check" t.integer "partner_under_16_value_check"
t.integer "multiple_partners_value_check" t.integer "multiple_partners_value_check"
t.bigint "created_by_id" t.bigint "created_by_id"
t.integer "referral_type"
t.boolean "manual_address_entry_selected", default: false t.boolean "manual_address_entry_selected", default: false
t.integer "referral_type"
t.index ["assigned_to_id"], name: "index_lettings_logs_on_assigned_to_id" t.index ["assigned_to_id"], name: "index_lettings_logs_on_assigned_to_id"
t.index ["bulk_upload_id"], name: "index_lettings_logs_on_bulk_upload_id" t.index ["bulk_upload_id"], name: "index_lettings_logs_on_bulk_upload_id"
t.index ["created_by_id"], name: "index_lettings_logs_on_created_by_id" t.index ["created_by_id"], name: "index_lettings_logs_on_created_by_id"
@ -772,6 +772,8 @@ ActiveRecord::Schema[7.2].define(version: 2025_03_05_092900) do
t.datetime "lasttransaction" t.datetime "lasttransaction"
t.datetime "initialpurchase" t.datetime "initialpurchase"
t.boolean "manual_address_entry_selected", default: false t.boolean "manual_address_entry_selected", default: false
t.integer "servicecharge_known"
t.decimal "servicecharge", precision: 10, scale: 2
t.index ["assigned_to_id"], name: "index_sales_logs_on_assigned_to_id" 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 ["bulk_upload_id"], name: "index_sales_logs_on_bulk_upload_id"
t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id" t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id"

2
spec/factories/sales_log.rb

@ -198,6 +198,8 @@ FactoryBot.define do
socprevten { 3 } socprevten { 3 }
mrent { 900 } mrent { 900 }
equity { 30 } equity { 30 }
has_servicecharge { 1 }
servicecharge { 100 }
ppostcode_full { "SW1A 1AA" } ppostcode_full { "SW1A 1AA" }
hodate { Time.zone.today } hodate { Time.zone.today }
end end

140
spec/models/validations/sales/soft_validations_spec.rb

@ -933,63 +933,131 @@ RSpec.describe Validations::Sales::SoftValidations do
end end
describe "#monthly_charges_over_soft_max?" do describe "#monthly_charges_over_soft_max?" do
it "returns false if mscharge is not given" do context "discounted ownership" do
record.mscharge = nil let(:record) { FactoryBot.build(:sales_log, :discounted_ownership_setup_complete) }
record.proptype = 4
record.type = 2
expect(record).not_to be_monthly_charges_over_soft_max it "returns false if mscharge is not given" do
end record.mscharge = nil
record.proptype = 4
it "returns false if proptype is not given" do record.type = 2
record.mscharge = 999
record.proptype = nil
record.type = 2
expect(record).not_to be_monthly_charges_over_soft_max expect(record).not_to be_monthly_charges_over_soft_max
end end
it "returns false if type is not given" do it "returns false if proptype is not given" do
record.mscharge = 999 record.mscharge = 999
record.proptype = 4 record.proptype = nil
record.type = nil record.type = 2
expect(record).not_to be_monthly_charges_over_soft_max expect(record).not_to be_monthly_charges_over_soft_max
end end
context "with old persons shared ownership" do it "returns false if type is not given" do
it "returns false if the monthly charge is under 550" do record.mscharge = 999
record.mscharge = 540
record.proptype = 4 record.proptype = 4
record.type = 24 record.type = nil
expect(record).not_to be_monthly_charges_over_soft_max expect(record).not_to be_monthly_charges_over_soft_max
end end
it "returns true if the monthly charge is over 550" do context "with old persons shared ownership" do
record.mscharge = 999 it "returns false if the monthly charge is under 550" do
record.proptype = 4 record.mscharge = 540
record.type = 24 record.proptype = 4
record.type = 24
expect(record).not_to be_monthly_charges_over_soft_max
end
it "returns true if the monthly charge is over 550" do
record.mscharge = 999
record.proptype = 4
record.type = 24
expect(record).to be_monthly_charges_over_soft_max
end
end
context "with non old persons type of ownership" do
it "returns false if the monthly charge is under 300" do
record.mscharge = 280
record.proptype = 4
record.type = 18
expect(record).not_to be_monthly_charges_over_soft_max
end
it "returns true if the monthly charge is over 300" do
record.mscharge = 400
record.proptype = 4
record.type = 18
expect(record).to be_monthly_charges_over_soft_max expect(record).to be_monthly_charges_over_soft_max
end
end end
end end
context "with non old persons type of ownership" do context "shared ownership" do
it "returns false if the monthly charge is under 300" do let(:record) { FactoryBot.build(:sales_log, :shared_2025_completed, startdate: Time.zone.local(2025, 6, 3)) }
record.mscharge = 280
it "returns false if servicecharge is not given" do
record.servicecharge = nil
record.proptype = 4 record.proptype = 4
record.type = 18 record.type = 2
expect(record).not_to be_monthly_charges_over_soft_max
end
it "returns false if proptype is not given" do
record.servicecharge = 999
record.proptype = nil
record.type = 2
expect(record).not_to be_monthly_charges_over_soft_max expect(record).not_to be_monthly_charges_over_soft_max
end end
it "returns true if the monthly charge is over 300" do it "returns false if type is not given" do
record.mscharge = 400 record.servicecharge = 999
record.proptype = 4 record.proptype = 4
record.type = 18 record.type = nil
expect(record).to be_monthly_charges_over_soft_max expect(record).not_to be_monthly_charges_over_soft_max
end
context "with old persons shared ownership" do
it "returns false if the monthly charge is under 550" do
record.servicecharge = 540
record.proptype = 4
record.type = 24
expect(record).not_to be_monthly_charges_over_soft_max
end
it "returns true if the monthly charge is over 550" do
record.servicecharge = 999
record.proptype = 4
record.type = 24
expect(record).to be_monthly_charges_over_soft_max
end
end
context "with non old persons type of ownership" do
it "returns false if the monthly charge is under 300" do
record.servicecharge = 280
record.proptype = 4
record.type = 18
expect(record).not_to be_monthly_charges_over_soft_max
end
it "returns true if the monthly charge is over 300" do
record.servicecharge = 400
record.proptype = 4
record.type = 18
expect(record).to be_monthly_charges_over_soft_max
end
end end
end end
end end

Loading…
Cancel
Save