Browse Source

CLDC-4175: add new service charge question sales (#3200)

* add the question

* CLDC-4174: update question number for 2026

* CLDc-4174: update question number for 2026

* CLDc-4174: update subsection

* CLDc-4174: update question specs

* CLDC-4174: add subsection spec

* CLDc-4174: lint

* CLDc-4174: add new page test

* CLDC-4241: lint

* CLDC-4241: lint

* CLDC-4175: db updates

* CLDC-4175: add new question to ui flow

* CLDC-4175: csv label for new question

* CLDC-4175: row parser updates

* CLDC-4175: export updates

* CLDC-4174: copy updates

* CLDC-4174: add validation that new value is different

* CLDC-4174: parser updates

* CLDC-4174: migration linting

* CLDC-4174: add tests

* CLDC-4174: fixture updates

* CLDC-4174: linting

* CLDC-4175: export and test updates

* CLDC-4175: row parser field renumbering

* CLDC-4175: test fixes

* CLDC-4175: test and schema updates post merge

* CLDC-4175: lint

* CLDC-4175: lint

* CLDC-4175: lint

* CLDC-4175: add reverse validation

* CLDC-4175: cleanup sales_log_to_csv.rb

* CLDC-4226: test fixture updates

* CLDC-4175: update export spec

* CLDC-4175: update validation message

* CLDC-4175: remove redundant range validation

* CLDC-4175: test with real values in row parser

* CLDC-4175: export test updates

* CLDC-4175: update row parser spec and examples

* CLDC-4175: update row parser spec and examples

* CLDC-4175: update row parser spec and examples

* CLDC-4175: update row parser spec and examples

* CLDC-4175: update row parser spec and examples

* CLDC-4248: respond to comments

* CLDC-4175: remove newservicecharges custom call chain

* CLDC-4175: include new spec requirement

* CLDC-4175: update financial_validations_spec.rb

---------

Co-authored-by: Samuel Young <samuel.young@softwire.com>
pull/3223/head
Nat Dean-Lewis 1 week ago committed by GitHub
parent
commit
f747034261
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      app/helpers/bulk_upload/sales_log_to_csv.rb
  2. 14
      app/models/form/sales/pages/service_charge_changed.rb
  3. 27
      app/models/form/sales/questions/has_service_charges_changed.rb
  4. 17
      app/models/form/sales/questions/new_service_charges.rb
  5. 1
      app/models/form/sales/subsections/shared_ownership_staircasing_transaction.rb
  6. 4
      app/models/sales_log.rb
  7. 9
      app/models/validations/sales/financial_validations.rb
  8. 4
      app/services/bulk_upload/sales/year2026/csv_parser.rb
  9. 12
      app/services/bulk_upload/sales/year2026/row_parser.rb
  10. 6
      app/services/exports/sales_log_export_constants.rb
  11. 3
      app/services/exports/sales_log_export_service.rb
  12. 13
      config/locales/forms/2026/sales/sale_information.en.yml
  13. 4
      config/locales/validations/sales/financial.en.yml
  14. 8
      db/migrate/20260305095832_add_service_charge_changed_to_sales_logs.rb
  15. 6
      db/schema.rb
  16. 2
      spec/fixtures/exports/sales_log_26_27.xml
  17. 10
      spec/fixtures/files/2026_27_sales_bulk_upload.csv
  18. 6
      spec/fixtures/files/sales_logs_csv_export_codes_26.csv
  19. 6
      spec/fixtures/files/sales_logs_csv_export_labels_26.csv
  20. 6
      spec/fixtures/files/sales_logs_csv_export_non_support_codes_26.csv
  21. 6
      spec/fixtures/files/sales_logs_csv_export_non_support_labels_26.csv
  22. 2
      spec/fixtures/variable_definitions/sales_download_26_27.csv
  23. 2
      spec/lib/tasks/log_variable_definitions_spec.rb
  24. 31
      spec/models/form/sales/pages/service_charge_changed_spec.rb
  25. 56
      spec/models/form/sales/questions/has_service_charges_changed_spec.rb
  26. 53
      spec/models/form/sales/questions/new_service_charges_spec.rb
  27. 1
      spec/models/form/sales/subsections/shared_ownership_staircasing_transaction_spec.rb
  28. 59
      spec/models/validations/sales/financial_validations_spec.rb
  29. 2
      spec/services/bulk_upload/sales/year2026/row_parser_spec.rb
  30. 101
      spec/services/exports/sales_log_export_service_spec.rb

4
app/helpers/bulk_upload/sales_log_to_csv.rb

@ -678,7 +678,9 @@ class BulkUpload::SalesLogToCsv
log.gender_same_as_sex5,
log.gender_description5,
log.gender_same_as_sex6,
log.gender_description6, # 134
log.gender_description6,
log.hasservicechargeschanged,
log.newservicecharges, # 136
]
end

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

@ -0,0 +1,14 @@
class Form::Sales::Pages::ServiceChargeChanged < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "service_charge_changed"
@copy_key = "sales.sale_information.servicecharges_changed"
end
def questions
@questions ||= [
Form::Sales::Questions::HasServiceChargesChanged.new(nil, nil, self),
Form::Sales::Questions::NewServiceCharges.new(nil, nil, self),
]
end
end

27
app/models/form/sales/questions/has_service_charges_changed.rb

@ -0,0 +1,27 @@
class Form::Sales::Questions::HasServiceChargesChanged < ::Form::Question
def initialize(id, hsh, page)
super
@id = "hasservicechargeschanged"
@type = "radio"
@answer_options = ANSWER_OPTIONS
@conditional_for = {
"newservicecharges" => [1],
}
@hidden_in_check_answers = {
"depends_on" => [
{
"hasservicechargeschanged" => 1,
},
],
}
@copy_key = "sales.sale_information.servicecharges_changed.has_service_charges_changed"
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end
ANSWER_OPTIONS = {
"1" => { "value" => "Yes" },
"2" => { "value" => "No" },
}.freeze
QUESTION_NUMBER_FROM_YEAR = { 2026 => 0 }.freeze
end

17
app/models/form/sales/questions/new_service_charges.rb

@ -0,0 +1,17 @@
class Form::Sales::Questions::NewServiceCharges < ::Form::Question
def initialize(id, hsh, page)
super
@id = "newservicecharges"
@type = "numeric"
@min = 0
@max = 9999.99
@step = 0.01
@width = 5
@prefix = "£"
@copy_key = "sales.sale_information.servicecharges_changed.new_service_charges"
@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 = { 2026 => 0 }.freeze
end

1
app/models/form/sales/subsections/shared_ownership_staircasing_transaction.rb

@ -26,6 +26,7 @@ class Form::Sales::Subsections::SharedOwnershipStaircasingTransaction < ::Form::
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) 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),
].compact
end

4
app/models/sales_log.rb

@ -583,4 +583,8 @@ class SalesLog < Log
def mscharge_value
mscharge if discounted_ownership_sale? || !form.start_year_2025_or_later?
end
def hasservicechargeschanged_label
form.get_question(:hasservicechargeschanged, self)&.label_from_value(hasservicechargeschanged)
end
end

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

@ -139,6 +139,15 @@ module Validations::Sales::FinancialValidations
end
end
def validate_newservicecharges_different_from_mscharge(record)
return unless record.hasservicechargeschanged == 1 && record.newservicecharges && record.has_mscharge == 1 && record.mscharge
if record.newservicecharges == record.mscharge
record.errors.add :newservicecharges, I18n.t("validations.sales.financial.newservicecharges.same_as_previous")
record.errors.add :mscharge, I18n.t("validations.sales.financial.mscharge.same_as_new")
end
end
private
def is_relationship_child?(relationship)

4
app/services/bulk_upload/sales/year2026/csv_parser.rb

@ -4,7 +4,7 @@ class BulkUpload::Sales::Year2026::CsvParser
include CollectionTimeHelper
# TODO: CLDC-4162: Update when 2026 format is known
FIELDS = 134
FIELDS = 136
FORM_YEAR = 2026
attr_reader :path
@ -27,7 +27,7 @@ class BulkUpload::Sales::Year2026::CsvParser
def cols
# TODO: CLDC-4162: Update when 2026 format is known
@cols ||= ("A".."ED").to_a
@cols ||= ("A".."EF").to_a
end
def row_parsers

12
app/services/bulk_upload/sales/year2026/row_parser.rb

@ -149,6 +149,8 @@ class BulkUpload::Sales::Year2026::RowParser
field_132: "If 'No', enter person 5's gender identity",
field_133: "Is the gender person 6 identifies with the same as their sex registered at birth?",
field_134: "If 'No', enter person 6's gender identity",
field_135: "Will the service charge change after this staircasing transaction takes place?",
field_136: "What are the new total monthly service charges for the property?",
}.freeze
ERROR_BASE_KEY = "validations.sales.2026.bulk_upload".freeze
@ -328,6 +330,9 @@ class BulkUpload::Sales::Year2026::RowParser
attribute :field_133, :integer
attribute :field_134, :string
attribute :field_135, :integer
attribute :field_136, :decimal
validates :field_1,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "sale completion date (day)."),
@ -863,6 +868,7 @@ private
sexrab4: %i[field_48],
sexrab5: %i[field_52],
sexrab6: %i[field_56],
buildheightclass: %i[field_122],
gender_same_as_sex1: %i[field_123],
@ -877,6 +883,9 @@ private
gender_description5: %i[field_132],
gender_same_as_sex6: %i[field_133],
gender_description6: %i[field_134],
hasservicechargeschanged: %i[field_135],
newservicecharges: %i[field_136],
}
end
@ -926,6 +935,9 @@ private
attributes["gender_same_as_sex6"] = field_133
attributes["gender_description6"] = field_134
attributes["hasservicechargeschanged"] = field_135
attributes["newservicecharges"] = field_136
attributes["relat2"] = relationship_from_is_partner(field_34)
attributes["relat3"] = relationship_from_is_partner(field_42)
attributes["relat4"] = relationship_from_is_partner(field_46)

6
app/services/exports/sales_log_export_constants.rb

@ -157,7 +157,11 @@ module Exports::SalesLogExportConstants
YEAR_2025_EXPORT_FIELDS << "SEX#{index}"
end
YEAR_2026_EXPORT_FIELDS = Set["BUILDHEIGHTCLASS"]
YEAR_2026_EXPORT_FIELDS = Set[
"BUILDHEIGHTCLASS",
"HASSERVICECHARGESCHANGED",
"NEWSERVICECHARGES",
]
(1..6).each do |index|
YEAR_2026_EXPORT_FIELDS << "SEXRAB#{index}"

3
app/services/exports/sales_log_export_service.rb

@ -135,6 +135,9 @@ module Exports
attribute_hash["hasestatefee"] = sales_log.has_management_fee
attribute_hash["estatefee"] = sales_log.management_fee
attribute_hash["hasservicechargeschanged"] = sales_log.hasservicechargeschanged
attribute_hash["newservicecharges"] = sales_log.newservicecharges
attribute_hash["stairlastday"] = sales_log.lasttransaction&.day
attribute_hash["stairlastmonth"] = sales_log.lasttransaction&.month
attribute_hash["stairlastyear"] = sales_log.lasttransaction&.year

13
config/locales/forms/2026/sales/sale_information.en.yml

@ -285,6 +285,19 @@ en:
hint_text: ""
question_text: "Enter the total monthly charge"
servicecharges_changed:
page_header: ""
has_service_charges_changed:
check_answer_label: "Service charge will change"
check_answer_prompt: "Tell us if the service charge will change"
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: "Will the service charge change after this staircasing transaction takes place?"
new_service_charges:
check_answer_label: "New monthly service charges"
check_answer_prompt: ""
hint_text: ""
question_text: "Enter the new total monthly charge"
purchase_price:
discounted_ownership:
page_header: "About the price of the property"

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

@ -48,6 +48,10 @@ en:
mscharge:
monthly_leasehold_charges:
not_zero: "Monthly leasehold charges cannot be £0 if the property has monthly charges."
same_as_new: "You said that the service charge will change and you entered the same amount as the new service charge. If the service charge will not change, answer 'No' to that question before entering the service charge here."
newservicecharges:
same_as_previous: "You said that the service charge will change and you entered the same amount as the previous question. If the service charge will not change, answer 'No'."
resale:
equity_over_max: "The maximum initial equity stake is %{max_equity}%."

8
db/migrate/20260305095832_add_service_charge_changed_to_sales_logs.rb

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

6
db/schema.rb

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.2].define(version: 2026_02_25_135309) do
ActiveRecord::Schema[7.2].define(version: 2026_03_05_095832) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -824,8 +824,10 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_25_135309) do
t.string "sexrab4"
t.string "sexrab5"
t.string "sexrab6"
t.integer "mortlen_known"
t.integer "buildheightclass"
t.integer "mortlen_known"
t.integer "hasservicechargeschanged"
t.decimal "newservicecharges", precision: 10, scale: 2
t.integer "gender_same_as_sex1"
t.integer "gender_same_as_sex2"
t.integer "gender_same_as_sex3"

2
spec/fixtures/exports/sales_log_26_27.xml vendored

@ -163,5 +163,7 @@
<MSCHARGE_VALUE_CHECK/>
<DUPLICATESET/>
<STAIRCASETOSALE/>
<HASSERVICECHARGESCHANGED/>
<NEWSERVICECHARGES/>
</form>
</forms>

10
spec/fixtures/files/2026_27_sales_bulk_upload.csv vendored

File diff suppressed because one or more lines are too long

6
spec/fixtures/files/sales_logs_csv_export_codes_26.csv vendored

File diff suppressed because one or more lines are too long

6
spec/fixtures/files/sales_logs_csv_export_labels_26.csv vendored

File diff suppressed because one or more lines are too long

6
spec/fixtures/files/sales_logs_csv_export_non_support_codes_26.csv vendored

File diff suppressed because one or more lines are too long

6
spec/fixtures/files/sales_logs_csv_export_non_support_labels_26.csv vendored

File diff suppressed because one or more lines are too long

2
spec/fixtures/variable_definitions/sales_download_26_27.csv vendored

@ -17,3 +17,5 @@ gender_same_as_sex5,Is the gender person 5 identifies with the same as their sex
gender_description5,If 'No', enter person 5's gender identity
gender_same_as_sex6,Is the gender person 6 identifies with the same as their sex registered at birth?
gender_description6,If 'No', enter person 6's gender identity
hasservicechargeschanged,Will the service charge change after this staircasing transaction takes place?
newservicecharges,What are the new total monthly service charges for the property?

1 sexrab1,What was buyer 1's sex at birth?
17 gender_description5,If 'No', enter person 5's gender identity
18 gender_same_as_sex6,Is the gender person 6 identifies with the same as their sex registered at birth?
19 gender_description6,If 'No', enter person 6's gender identity
20 hasservicechargeschanged,Will the service charge change after this staircasing transaction takes place?
21 newservicecharges,What are the new total monthly service charges for the property?

2
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) { 463 }
let(:total_variable_definitions_count) { 465 }
before do
Rake.application.rake_require("tasks/log_variable_definitions")

31
spec/models/form/sales/pages/service_charge_changed_spec.rb

@ -0,0 +1,31 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::ServiceChargeChanged, 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: collection_start_date_for_year(2026))) }
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[hasservicechargeschanged newservicecharges])
end
it "has the correct id" do
expect(page.id).to eq("service_charge_changed")
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

56
spec/models/form/sales/questions/has_service_charges_changed_spec.rb

@ -0,0 +1,56 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::HasServiceChargesChanged, type: :model do
include CollectionTimeHelper
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date:)) }
let(:page) { instance_double(Form::Page, subsection:) }
let(:start_date) { collection_start_date_for_year(2026) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("hasservicechargeschanged")
end
it "has the correct type" do
expect(question.type).to eq("radio")
end
it "is not marked as derived" do
expect(question.derived?(nil)).to be false
end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"1" => { "value" => "Yes" },
"2" => { "value" => "No" },
})
end
it "has correct conditional for" do
expect(question.conditional_for).to eq({
"newservicecharges" => [1],
})
end
it "has correct hidden_in_check_answers for" do
expect(question.hidden_in_check_answers).to eq({
"depends_on" => [
{
"hasservicechargeschanged" => 1,
},
],
})
end
it "has the correct question number" do
expect(question.question_number).to eq(0)
end
end

53
spec/models/form/sales/questions/new_service_charges_spec.rb

@ -0,0 +1,53 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::NewServiceCharges, type: :model do
include CollectionTimeHelper
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date:)) }
let(:page) { instance_double(Form::Page, subsection:) }
let(:start_date) { collection_start_date_for_year(2026) }
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("newservicecharges")
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 the correct width" do
expect(question.width).to be 5
end
it "has the correct min" do
expect(question.min).to be 0
end
it "has the correct max" do
expect(question.max).to be 9999.99
end
it "has the correct step" do
expect(question.step).to be 0.01
end
it "has the correct prefix" do
expect(question.prefix).to eq("£")
end
it "has the correct question number" do
expect(question.question_number).to eq(0)
end
end

1
spec/models/form/sales/subsections/shared_ownership_staircasing_transaction_spec.rb

@ -77,6 +77,7 @@ RSpec.describe Form::Sales::Subsections::SharedOwnershipStaircasingTransaction,
monthly_rent_staircasing_owned
monthly_rent_staircasing
service_charge_staircasing
service_charge_changed
monthly_charges_shared_ownership_value_check
],
)

59
spec/models/validations/sales/financial_validations_spec.rb

@ -478,4 +478,63 @@ RSpec.describe Validations::Sales::FinancialValidations do
expect(record.errors).to be_empty
end
end
describe "#validate_newservicecharges_different_from_mscharge" do
let(:record) { FactoryBot.build(:sales_log, ownershipsch: 1, staircase: 1) }
it "does not add errors when hasservicechargeschanged is nil" do
record.hasservicechargeschanged = nil
record.newservicecharges = 100
record.has_mscharge = 1
record.mscharge = 100
financial_validator.validate_newservicecharges_different_from_mscharge(record)
expect(record.errors).to be_empty
end
it "does not add errors when hasservicechargeschanged is 2 (No)" do
record.hasservicechargeschanged = 2
record.newservicecharges = 100
record.has_mscharge = 1
record.mscharge = 100
financial_validator.validate_newservicecharges_different_from_mscharge(record)
expect(record.errors).to be_empty
end
it "does not add errors when newservicecharges is nil" do
record.hasservicechargeschanged = 1
record.newservicecharges = nil
record.has_mscharge = 1
record.mscharge = 100
financial_validator.validate_newservicecharges_different_from_mscharge(record)
expect(record.errors).to be_empty
end
it "does not add errors when mscharge is nil" do
record.hasservicechargeschanged = 1
record.newservicecharges = 100
record.has_mscharge = 2
record.mscharge = nil
financial_validator.validate_newservicecharges_different_from_mscharge(record)
expect(record.errors).to be_empty
end
it "does not add errors when newservicecharges is different from mscharge" do
record.hasservicechargeschanged = 1
record.newservicecharges = 150
record.has_mscharge = 1
record.mscharge = 100
financial_validator.validate_newservicecharges_different_from_mscharge(record)
expect(record.errors).to be_empty
end
it "adds an error when hasservicechargeschanged is 1 (Yes) and newservicecharges equals mscharge" do
record.hasservicechargeschanged = 1
record.newservicecharges = 100
record.has_mscharge = 1
record.mscharge = 100
financial_validator.validate_newservicecharges_different_from_mscharge(record)
expect(record.errors["newservicecharges"]).to include(match I18n.t("validations.sales.financial.newservicecharges.same_as_previous"))
expect(record.errors["mscharge"]).to include(match I18n.t("validations.sales.financial.mscharge.same_as_new"))
end
end
end

2
spec/services/bulk_upload/sales/year2026/row_parser_spec.rb

@ -116,6 +116,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do
field_123: "1",
field_125: "2",
field_126: "Non-binary",
field_135: "1",
field_136: "150",
}
end

101
spec/services/exports/sales_log_export_service_spec.rb

@ -451,18 +451,12 @@ RSpec.describe Exports::SalesLogExportService do
end
context "with shared ownership and mscharge" do
let!(:sales_log) { FactoryBot.create(:sales_log, :export, ownershipsch: 1, staircase: 2, type: 30, mscharge: 321, has_management_fee: 1, management_fee: 222) }
def replace_mscharge_and_shared_ownership_values(export_file)
def replace_shared_ownership_values(export_file)
export_file.sub!("<HASSERVICECHARGES/>", "<HASSERVICECHARGES>1</HASSERVICECHARGES>")
export_file.sub!("<SERVICECHARGES/>", "<SERVICECHARGES>321.0</SERVICECHARGES>")
export_file.sub!("<HASESTATEFEE/>", "<HASESTATEFEE>1</HASESTATEFEE>")
export_file.sub!("<ESTATEFEE/>", "<ESTATEFEE>222.0</ESTATEFEE>")
export_file.sub!("<MSCHARGE>100.0</MSCHARGE>", "<MSCHARGE/>")
export_file.sub!("<HASMSCHARGE>1</HASMSCHARGE>", "<HASMSCHARGE/>")
export_file.sub!("<TYPE>8</TYPE>", "<TYPE>30</TYPE>")
export_file.sub!("<STAIRCASE/>", "<STAIRCASE>2</STAIRCASE>")
export_file.sub!("<GRANT>10000.0</GRANT>", "<GRANT/>")
export_file.sub!("<PPCODENK>0</PPCODENK>", "<PPCODENK>1</PPCODENK>")
export_file.sub!("<PPOSTC1>SW1A</PPOSTC1>", "<PPOSTC1/>")
@ -474,16 +468,93 @@ RSpec.describe Exports::SalesLogExportService do
export_file.sub!("<PREVLOCNAME>Westminster</PREVLOCNAME>", "<PREVLOCNAME/>")
end
it "exports mscharge fields as hasmscharge and mscharge" do
expected_content = replace_entity_ids(sales_log, xml_export_file.read)
expected_content = replace_mscharge_and_shared_ownership_values(expected_content)
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) do |_, content|
entry = Zip::File.open_buffer(content).find_entry(expected_data_filename)
expect(entry).not_to be_nil
expect(entry.get_input_stream.read).to have_same_xml_contents_as(expected_content)
context "when not staircasing" do
let!(:sales_log) { FactoryBot.create(:sales_log, :export, ownershipsch: 1, staircase: 2, type: 30, mscharge: 321, has_management_fee: 1, management_fee: 222) }
def replace_non_staircasing_values(export_file)
export_file.sub!("<HASESTATEFEE/>", "<HASESTATEFEE>1</HASESTATEFEE>")
export_file.sub!("<ESTATEFEE/>", "<ESTATEFEE>222.0</ESTATEFEE>")
export_file.sub!("<TYPE>8</TYPE>", "<TYPE>30</TYPE>")
export_file.sub!("<STAIRCASE/>", "<STAIRCASE>2</STAIRCASE>")
end
export_service.export_xml_sales_logs
it "exports mscharge fields as hasmscharge and mscharge" do
expected_content = replace_entity_ids(sales_log, xml_export_file.read)
replace_shared_ownership_values(expected_content)
replace_non_staircasing_values(expected_content)
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) do |_, content|
entry = Zip::File.open_buffer(content).find_entry(expected_data_filename)
expect(entry).not_to be_nil
expect(entry.get_input_stream.read).to have_same_xml_contents_as(expected_content)
end
export_service.export_xml_sales_logs
end
end
context "when staircasing" do
context "when exporting only 26/27 collection period", metadata: { year: 26 } do
let(:start_time) { collection_start_date_for_year(2026) }
let(:expected_zip_filename) { "core_sales_2026_2027_apr_mar_f0001_inc0001.zip" }
let(:expected_data_filename) { "core_sales_2026_2027_apr_mar_f0001_inc0001_pt001.xml" }
let(:xml_export_file) { File.open("spec/fixtures/exports/sales_log_26_27.xml", "r:UTF-8") }
let!(:sales_log) { FactoryBot.create(:sales_log, :export, ownershipsch: 1, staircase: 1, type: 2, mscharge: 321, has_management_fee: 1, management_fee: 222, hasservicechargeschanged: 1, newservicecharges: 150) }
def replace_staircasing_values(export_file)
export_file.sub!("<HASESTATEFEE/>", "<HASESTATEFEE>1</HASESTATEFEE>")
export_file.sub!("<ESTATEFEE/>", "<ESTATEFEE>222.0</ESTATEFEE>")
export_file.sub!("<TYPE>8</TYPE>", "<TYPE>2</TYPE>")
export_file.sub!("<STAIRCASE/>", "<STAIRCASE>1</STAIRCASE>")
export_file.sub!("<ARMEDFORCESSPOUSE>5</ARMEDFORCESSPOUSE>", "<ARMEDFORCESSPOUSE/>")
export_file.sub!("<BUILTYPE>1</BUILTYPE>", "<BUILTYPE/>")
export_file.sub!("<BUY2LIVING>3</BUY2LIVING>", "<BUY2LIVING/>")
export_file.sub!("<DEPOSIT>80000.0</DEPOSIT>", "<DEPOSIT/>")
export_file.sub!("<DISABLED>1</DISABLED>", "<DISABLED/>")
export_file.sub!("<ECSTAT1>1</ECSTAT1>", "<ECSTAT1/>")
export_file.sub!("<ECSTAT2>1</ECSTAT2>", "<ECSTAT2/>")
export_file.sub!("<ESTATEFEE>222.0</ESTATEFEE>", "<ESTATEFEE/>")
export_file.sub!("<ETHNIC>17</ETHNIC>", "<ETHNIC/>")
export_file.sub!("<ETHNICGROUP1>17</ETHNICGROUP1>", "<ETHNICGROUP1/>")
export_file.sub!("<ETHNICGROUP2>17</ETHNICGROUP2>", "<ETHNICGROUP2/>")
export_file.sub!("<HASESTATEFEE>1</HASESTATEFEE>", "<HASESTATEFEE/>")
export_file.sub!("<HB>4</HB>", "<HB/>")
export_file.sub!("<HHOLDCOUNT>4</HHOLDCOUNT>", "<HHOLDCOUNT/>")
export_file.sub!("<HHREGRES>7</HHREGRES>", "<HHREGRES/>")
export_file.sub!("<INC1MORT>1</INC1MORT>", "<INC1MORT/>")
export_file.sub!("<INC1NK>0</INC1NK>", "<INC1NK/>")
export_file.sub!("<INC2MORT>1</INC2MORT>", "<INC2MORT/>")
export_file.sub!("<INC2NK>0</INC2NK>", "<INC2NK/>")
export_file.sub!("<INCOME1>10000</INCOME1>", "<INCOME1/>")
export_file.sub!("<INCOME2>10000</INCOME2>", "<INCOME2/>")
export_file.sub!("<LIVEINBUYER1>1</LIVEINBUYER1>", "<LIVEINBUYER1/>")
export_file.sub!("<LIVEINBUYER2>1</LIVEINBUYER2>", "<LIVEINBUYER2/>")
export_file.sub!("<MORTGAGE>20000.0</MORTGAGE>", "<MORTGAGE/>")
export_file.sub!("<MORTLEN1>10</MORTLEN1>", "<MORTLEN1/>")
export_file.sub!("<NATIONALITYALL1>826</NATIONALITYALL1>", "<NATIONALITYALL1/>")
export_file.sub!("<NATIONALITYALL2>826</NATIONALITYALL2>", "<NATIONALITYALL2/>")
export_file.sub!("<PREVOWN>1</PREVOWN>", "<PREVOWN/>")
export_file.sub!("<PREVSHARED>2</PREVSHARED>", "<PREVSHARED/>")
export_file.sub!("<PREVTEN>1</PREVTEN>", "<PREVTEN/>")
export_file.sub!("<SAVINGSNK>1</SAVINGSNK>", "<SAVINGSNK/>")
export_file.sub!("<WCHAIR>1</WCHAIR>", "<WCHAIR/>")
export_file.sub!("<WHEEL>1</WHEEL>", "<WHEEL/>")
export_file.sub!("<HASSERVICECHARGESCHANGED/>", "<HASSERVICECHARGESCHANGED>1</HASSERVICECHARGESCHANGED>")
export_file.sub!("<NEWSERVICECHARGES/>", "<NEWSERVICECHARGES>150.0</NEWSERVICECHARGES>")
end
it "exports mscharge fields and hasservicechargeschanged and newservicecharges" do
expected_content = replace_entity_ids(sales_log, xml_export_file.read)
replace_shared_ownership_values(expected_content)
replace_staircasing_values(expected_content)
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) do |_, content|
entry = Zip::File.open_buffer(content).find_entry(expected_data_filename)
expect(entry).not_to be_nil
expect(entry.get_input_stream.read).to have_same_xml_contents_as(expected_content)
end
export_service.export_xml_sales_logs(full_update: true, collection_year: 2026)
end
end
end
end
end

Loading…
Cancel
Save