Browse Source

Merge branch 'main' into CLDC-4154-needs-type-hint-text

pull/3166/head
Samuel Young 5 days ago committed by GitHub
parent
commit
0e62508634
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      Gemfile
  2. 8
      Gemfile.lock
  3. 155
      app/helpers/bulk_upload/lettings_log_to_csv.rb
  4. 142
      app/helpers/bulk_upload/sales_log_to_csv.rb
  5. 13
      app/models/form/lettings/pages/lead_tenant_sex_registered_at_birth.rb
  6. 16
      app/models/form/lettings/pages/person_sex_registered_at_birth.rb
  7. 19
      app/models/form/lettings/questions/lead_tenant_sex_registered_at_birth.rb
  8. 23
      app/models/form/lettings/questions/person_sex_registered_at_birth.rb
  9. 38
      app/models/form/lettings/questions/previous_tenure.rb
  10. 8
      app/models/form/lettings/subsections/household_characteristics.rb
  11. 16
      app/models/form/sales/pages/person_sex_registered_at_birth.rb
  12. 20
      app/models/form/sales/pages/sex_registered_at_birth1.rb
  13. 23
      app/models/form/sales/pages/sex_registered_at_birth2.rb
  14. 19
      app/models/form/sales/questions/person_sex_registered_at_birth.rb
  15. 19
      app/models/form/sales/questions/sex_registered_at_birth1.rb
  16. 20
      app/models/form/sales/questions/sex_registered_at_birth2.rb
  17. 7
      app/models/form/sales/subsections/household_characteristics.rb
  18. 5
      app/services/bulk_upload/lettings/year2026/csv_parser.rb
  19. 53
      app/services/bulk_upload/lettings/year2026/row_parser.rb
  20. 2
      app/services/bulk_upload/sales/validator.rb
  21. 3
      app/services/bulk_upload/sales/year2026/csv_parser.rb
  22. 40
      app/services/bulk_upload/sales/year2026/row_parser.rb
  23. 1
      app/services/csv/lettings_log_csv_service.rb
  24. 1
      app/services/csv/sales_log_csv_service.rb
  25. 99
      app/services/exports/lettings_log_export_constants.rb
  26. 46
      app/services/exports/lettings_log_export_service.rb
  27. 268
      app/services/exports/sales_log_export_constants.rb
  28. 26
      app/services/exports/sales_log_export_service.rb
  29. 2
      config/initializers/ostruct.rb
  30. 56
      config/locales/forms/2026/lettings/household_characteristics.en.yml
  31. 2
      config/locales/forms/2026/lettings/household_situation.en.yml
  32. 2
      config/locales/forms/2026/lettings/income_and_benefits.en.yml
  33. 49
      config/locales/forms/2026/sales/household_characteristics.en.yml
  34. 8881
      config/rent_range_data/2026.csv
  35. 12
      db/migrate/20260113143404_add_sex_registered_at_birth_to_sales_logs.rb
  36. 14
      db/migrate/20260128121417_add_sex_registered_at_birth_to_lettings_logs.rb
  37. 16
      db/schema.rb
  38. 16
      docs/Gemfile.lock
  39. 3
      spec/factories/lettings_log.rb
  40. 13
      spec/factories/sales_log.rb
  41. 8
      spec/fixtures/exports/general_needs_log_26_27.xml
  42. 42
      spec/fixtures/files/2026_27_lettings_bulk_upload.csv
  43. 20
      spec/fixtures/files/2026_27_sales_bulk_upload.csv
  44. 6
      spec/fixtures/files/lettings_log_csv_export_codes_26.csv
  45. 6
      spec/fixtures/files/lettings_log_csv_export_labels_26.csv
  46. 6
      spec/fixtures/files/lettings_log_csv_export_non_support_codes_26.csv
  47. 6
      spec/fixtures/files/lettings_log_csv_export_non_support_labels_26.csv
  48. 227
      spec/fixtures/variable_definitions/lettings_download_26_27.csv
  49. 287
      spec/fixtures/variable_definitions/sales_download_26_27.csv
  50. 5
      spec/lib/tasks/log_variable_definitions_spec.rb
  51. 29
      spec/models/form/lettings/pages/lead_tenant_sex_registered_at_birth_spec.rb
  52. 138
      spec/models/form/lettings/pages/person_sex_registered_at_birth_spec.rb
  53. 2
      spec/models/form/lettings/pages/previous_housing_situation_spec.rb
  54. 53
      spec/models/form/lettings/questions/lead_tenant_sex_registered_at_birth_spec.rb
  55. 185
      spec/models/form/lettings/questions/person_sex_registered_at_birth_spec.rb
  56. 52
      spec/models/form/lettings/questions/previous_tenure_spec.rb
  57. 8
      spec/models/form/lettings/subsections/household_characteristics_spec.rb
  58. 104
      spec/models/form/sales/pages/person_sex_registered_at_birth_spec.rb
  59. 29
      spec/models/form/sales/pages/sex_registered_at_birth1_spec.rb
  60. 38
      spec/models/form/sales/pages/sex_registered_at_birth2_spec.rb
  61. 123
      spec/models/form/sales/questions/person_sex_registered_at_birth_spec.rb
  62. 49
      spec/models/form/sales/questions/sex_registered_at_birth1_spec.rb
  63. 49
      spec/models/form/sales/questions/sex_registered_at_birth2_spec.rb
  64. 131
      spec/models/form/sales/subsections/household_characteristics_spec.rb
  65. 6
      spec/services/bulk_upload/lettings/validator_spec.rb
  66. 8
      spec/services/bulk_upload/lettings/year2026/csv_parser_spec.rb
  67. 11
      spec/services/bulk_upload/lettings/year2026/row_parser_spec.rb
  68. 2
      spec/services/bulk_upload/sales/validator_spec.rb
  69. 8
      spec/services/bulk_upload/sales/year2026/csv_parser_spec.rb
  70. 8
      spec/services/bulk_upload/sales/year2026/row_parser_spec.rb
  71. 3
      spec/services/csv/lettings_log_csv_service_spec.rb
  72. 4
      spec/services/exports/lettings_log_export_service_spec.rb
  73. 4
      spec/services/imports/variable_definitions_service_spec.rb

3
Gemfile

@ -117,3 +117,6 @@ gem "cssbundling-rails"
gem "tzinfo-data", platforms: %i[mingw mswin x64_mingw jruby]
gem "excon", "~> 0.111.0"
# faraday is a transitive dependency, but we need to force the version to 2.14.1 to avoid a vulnerability.
gem "faraday", ">= 2.14.1"

8
Gemfile.lock

@ -203,8 +203,10 @@ GEM
railties (>= 5.0.0)
faker (3.2.3)
i18n (>= 1.8.11, < 2)
faraday (2.9.0)
faraday-net_http (>= 2.0, < 3.2)
faraday (2.14.1)
faraday-net_http (>= 2.0, < 3.5)
json
logger
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-net_http (3.1.0)
@ -242,6 +244,7 @@ GEM
jmespath (1.6.2)
jsbundling-rails (1.3.0)
railties (>= 6.0.0)
json (2.18.1)
json-schema (4.1.1)
addressable (>= 2.8)
jwt (2.8.0)
@ -568,6 +571,7 @@ DEPENDENCIES
excon (~> 0.111.0)
factory_bot_rails
faker
faraday (>= 2.14.1)
govuk-components (~> 5.7)
govuk_design_system_formbuilder (~> 5.7)
govuk_markdown

155
app/helpers/bulk_upload/lettings_log_to_csv.rb

@ -92,13 +92,162 @@ class BulkUpload::LettingsLogToCsv
end
def default_2026_field_numbers
# TODO: CLDC-4162 Replace with actual field numbers when 2026 format is known
(1..129).to_a
(1..BulkUpload::Lettings::Year2026::CsvParser::FIELDS).to_a
end
def to_2026_row
# TODO: CLDC-4162: Implement when 2026 format is known
to_2025_row
[
overrides[:organisation_id] || log.owning_organisation&.old_visible_id, # 1
overrides[:managing_organisation_id] || log.managing_organisation&.old_visible_id,
log.assigned_to&.email,
log.needstype,
log.scheme&.id ? "S#{log.scheme&.id}" : "",
log.location&.id,
renewal,
log.startdate&.day,
log.startdate&.month,
log.startdate&.strftime("%y"), # 10
rent_type,
log.irproduct_other,
log.tenancycode,
log.propcode,
log.declaration,
log.rsnvac,
log.unitletas,
log.uprn,
log.address_line1&.tr(",", " "),
log.address_line2&.tr(",", " "), # 20
log.town_or_city&.tr(",", " "),
log.county&.tr(",", " "),
((log.postcode_full || "").split(" ") || [""]).first,
((log.postcode_full || "").split(" ") || [""]).last,
log.la,
log.unittype_gn,
log.builtype,
log.wchair,
log.beds,
log.voiddate&.day, # 30
log.voiddate&.month,
log.voiddate&.strftime("%y"),
log.mrcdate&.day,
log.mrcdate&.month,
log.mrcdate&.strftime("%y"),
log.sheltered,
log.joint,
log.startertenancy,
log.tenancy,
log.tenancyother, # 40
log.tenancylength,
log.age1 || overrides[:age1],
log.sex1,
log.ethnic,
log.nationality_all_group,
log.ecstat1,
relat_number(log.relat2),
log.age2 || overrides[:age2],
log.sex2,
log.ecstat2, # 50
relat_number(log.relat3),
log.age3 || overrides[:age3],
log.sex3,
log.ecstat3,
relat_number(log.relat4),
log.age4 || overrides[:age4],
log.sex4,
log.ecstat4,
relat_number(log.relat5),
log.age5 || overrides[:age5], # 60
log.sex5,
log.ecstat5,
relat_number(log.relat6),
log.age6 || overrides[:age6],
log.sex6,
log.ecstat6,
relat_number(log.relat7),
log.age7 || overrides[:age7],
log.sex7,
log.ecstat7, # 70
relat_number(log.relat8),
log.age8 || overrides[:age8],
log.sex8,
log.ecstat8,
log.armedforces,
log.leftreg,
log.reservist,
log.preg_occ,
log.housingneeds_a,
log.housingneeds_b, # 80
log.housingneeds_c,
log.housingneeds_f,
log.housingneeds_g,
log.housingneeds_h,
overrides[:illness] || log.illness,
log.illness_type_1,
log.illness_type_2,
log.illness_type_3,
log.illness_type_4,
log.illness_type_5, # 90
log.illness_type_6,
log.illness_type_7,
log.illness_type_8,
log.illness_type_9,
log.illness_type_10,
log.layear,
log.waityear,
log.reason,
log.reasonother,
log.prevten, # 100
homeless,
previous_postcode_known,
((log.ppostcode_full || "").split(" ") || [""]).first,
((log.ppostcode_full || "").split(" ") || [""]).last,
log.prevloc,
log.reasonpref,
log.rp_homeless,
log.rp_insan_unsat,
log.rp_medwel,
log.rp_hardship, # 110
log.rp_dontknow,
cbl,
chr,
cap,
accessible_register,
log.referral,
net_income_known,
log.incfreq,
log.earnings,
log.hb, # 120
log.benefits,
log.household_charge,
log.period,
log.brent,
log.scharge,
log.pscharge,
log.supcharg,
log.hbrentshortfall,
log.tshortfall,
log.sexrab1, # 130
log.sexrab2,
log.sexrab3,
log.sexrab4,
log.sexrab5,
log.sexrab6,
log.sexrab7,
log.sexrab8, # 137
]
end
def to_2025_row

142
app/helpers/bulk_upload/sales_log_to_csv.rb

@ -70,8 +70,7 @@ class BulkUpload::SalesLogToCsv
when 2025
(1..121).to_a
when 2026
# TODO: CLDC-4162: Replace with actual field numbers when 2026 format is known
(1..121).to_a
(1..BulkUpload::Sales::Year2026::CsvParser::FIELDS).to_a
else
raise NotImplementedError "No mapping function implemented for year #{year}"
end
@ -537,7 +536,144 @@ class BulkUpload::SalesLogToCsv
def to_2026_row
# TODO: CLDC-4162: Implement when 2026 template is available
to_2025_row
[
log.saledate&.day,
log.saledate&.month,
log.saledate&.strftime("%y"),
overrides[:organisation_id] || log.owning_organisation&.old_visible_id,
overrides[:managing_organisation_id] || log.managing_organisation&.old_visible_id,
log.assigned_to&.email,
log.purchid,
log.ownershipsch,
log.ownershipsch == 1 ? log.type : "", # field_9: "What is the type of shared ownership sale?",
log.staircase, # 10
log.ownershipsch == 2 ? log.type : "", # field_11: "What is the type of discounted ownership sale?",
log.jointpur,
log.jointmore,
log.noint,
log.privacynotice,
log.uprn,
log.address_line1&.tr(",", " "), # 20
log.address_line2&.tr(",", " "),
log.town_or_city&.tr(",", " "),
log.county&.tr(",", " "),
((log.postcode_full || "").split(" ") || [""]).first,
((log.postcode_full || "").split(" ") || [""]).last,
log.la,
log.proptype,
log.beds,
log.builtype,
log.wchair,
log.age1,
log.sex1,
log.ethnic, # 30
log.nationality_all_group,
log.ecstat1,
log.buy1livein,
{ "P" => 1, "X" => 2, "R" => 3 }[log.relat2],
log.age2,
log.sex2,
log.ethnic_group2,
log.nationality_all_buyer2_group,
log.ecstat2,
log.buy2livein, # 40
log.hholdcount,
{ "P" => 1, "X" => 2, "R" => 3 }[log.relat3],
log.age3,
log.sex3,
log.ecstat3,
{ "P" => 1, "X" => 2, "R" => 3 }[log.relat4],
log.age4,
log.sex4,
log.ecstat4,
{ "P" => 1, "X" => 2, "R" => 3 }[log.relat5], # 50
log.age5,
log.sex5,
log.ecstat5,
{ "P" => 1, "X" => 2, "R" => 3 }[log.relat6],
log.age6,
log.sex6,
log.ecstat6,
log.prevten,
log.ppcodenk,
((log.ppostcode_full || "").split(" ") || [""]).first, # 60
((log.ppostcode_full || "").split(" ") || [""]).last,
log.prevloc,
log.buy2living,
log.prevtenbuy2,
log.hhregres,
log.hhregresstill,
log.armedforcesspouse,
log.disabled,
log.wheel,
log.income1, # 70
log.inc1mort,
log.income2,
log.inc2mort,
log.hb,
log.savings.present? || "R",
log.prevown,
log.prevshared,
log.resale,
log.proplen,
log.hodate&.day, # 80
log.hodate&.month,
log.hodate&.strftime("%y"),
log.frombeds,
log.fromprop,
log.socprevten,
log.value,
log.equity,
log.mortgageused,
log.mortgage,
log.mortlen, # 90
log.deposit,
log.cashdis,
log.mrent,
log.mscharge,
log.management_fee,
log.stairbought,
log.stairowned,
log.staircasesale,
log.firststair,
log.initialpurchase&.day, # 100
log.initialpurchase&.month,
log.initialpurchase&.strftime("%y"),
log.numstair,
log.lasttransaction&.day,
log.lasttransaction&.month,
log.lasttransaction&.strftime("%y"),
log.value,
log.equity,
log.mortgageused,
log.mrentprestaircasing, # 110
log.mrent,
log.proplen,
log.value,
log.grant,
log.discount,
log.mortgageused,
log.mortgage,
log.mortlen,
log.extrabor,
log.deposit, # 120
log.mscharge,
log.sexrab1,
log.sexrab2,
log.sexrab3,
log.sexrab4,
log.sexrab5,
log.sexrab6, # 127
]
end
def custom_field_numbers_row(seed: nil, field_numbers: nil)

13
app/models/form/lettings/pages/lead_tenant_sex_registered_at_birth.rb

@ -0,0 +1,13 @@
class Form::Lettings::Pages::LeadTenantSexRegisteredAtBirth < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "lead_tenant_sex_registered_at_birth"
@depends_on = [{ "declaration" => 1 }]
end
def questions
@questions ||= [
Form::Lettings::Questions::LeadTenantSexRegisteredAtBirth.new(nil, nil, self),
]
end
end

16
app/models/form/lettings/pages/person_sex_registered_at_birth.rb

@ -0,0 +1,16 @@
class Form::Lettings::Pages::PersonSexRegisteredAtBirth < ::Form::Page
def initialize(id, hsh, subsection, person_index:)
super(id, hsh, subsection)
@id = "person_#{person_index}_sex_registered_at_birth"
@person_index = person_index
@depends_on = [
{ "details_known_#{person_index}" => 0 },
]
end
def questions
@questions ||= [
Form::Lettings::Questions::PersonSexRegisteredAtBirth.new("sexrab#{@person_index}", nil, self, person_index: @person_index),
]
end
end

19
app/models/form/lettings/questions/lead_tenant_sex_registered_at_birth.rb

@ -0,0 +1,19 @@
class Form::Lettings::Questions::LeadTenantSexRegisteredAtBirth < ::Form::Question
def initialize(id, hsh, page)
super
@id = "sexrab1"
@type = "radio"
@check_answers_card_number = 1
@answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year]
end
ANSWER_OPTIONS = {
"F" => { "value" => "Female" },
"M" => { "value" => "Male" },
"divider" => { "value" => true },
"R" => { "value" => "Lead tenant prefers not to say" },
}.freeze
QUESTION_NUMBER_FROM_YEAR = { 2026 => 31 }.freeze
end

23
app/models/form/lettings/questions/person_sex_registered_at_birth.rb

@ -0,0 +1,23 @@
class Form::Lettings::Questions::PersonSexRegisteredAtBirth < ::Form::Question
def initialize(id, hsh, page, person_index:)
super(id, hsh, page)
@type = "radio"
@check_answers_card_number = person_index
@answer_options = ANSWER_OPTIONS
@person_index = person_index
@question_number = question_number
end
ANSWER_OPTIONS = {
"F" => { "value" => "Female" },
"M" => { "value" => "Male" },
"divider" => { "value" => true },
"R" => { "value" => "Person prefers not to say" },
}.freeze
def question_number
base_question_number = 29
base_question_number + (5 * @person_index)
end
end

38
app/models/form/lettings/questions/previous_tenure.rb

@ -5,7 +5,13 @@ class Form::Lettings::Questions::PreviousTenure < ::Form::Question
@copy_key = "lettings.household_situation.prevten.not_renewal"
@type = "radio"
@check_answers_card_number = 0
@answer_options = form.start_year_2025_or_later? ? ANSWER_OPTIONS_2025 : ANSWER_OPTIONS
@answer_options = if form.start_year_2026_or_later?
ANSWER_OPTIONS_2026_OR_LATER
elsif form.start_date.year == 2025
ANSWER_OPTIONS_2025
else
ANSWER_OPTIONS
end
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end
@ -67,5 +73,35 @@ class Form::Lettings::Questions::PreviousTenure < ::Form::Question
"25" => { "value" => "Any other accommodation" },
}.freeze
ANSWER_OPTIONS_2026_OR_LATER = {
"30" => { "value" => "Fixed-term local authority general needs tenancy" },
"32" => { "value" => "Fixed-term private registered provider (PRP) general needs tenancy" },
"31" => { "value" => "Lifetime local authority general needs tenancy" },
"33" => { "value" => "Lifetime private registered provider (PRP) general needs tenancy" },
"40" => { "value" => "Other general needs" },
"35" => { "value" => "Extra care housing" },
"38" => { "value" => "Older people’s housing for tenants with low support needs" },
"6" => { "value" => "Other supported housing" },
"3" => { "value" => "Private sector tenancy" },
"27" => { "value" => "Owner occupation (low-cost home ownership)" },
"26" => { "value" => "Owner occupation (private)" },
"28" => { "value" => "Living with friends or family (long-term)" },
"39" => { "value" => "Sofa surfing (moving regularly between family or friends, no permanent bed)" },
"14" => { "value" => "Bed and breakfast" },
"7" => { "value" => "Direct access hostel" },
"10" => { "value" => "Hospital" },
"29" => { "value" => "Prison or approved probation hostel" },
"19" => { "value" => "Rough sleeping" },
"18" => { "value" => "Any other temporary accommodation" },
"13" => { "value" => "Children’s home or foster care" },
"24" => { "value" => "Home Office Asylum Support" },
"37" => { "value" => "Host family or similar refugee accommodation" },
"23" => { "value" => "Mobile home or caravan" },
"21" => { "value" => "Refuge" },
"9" => { "value" => "Residential care home" },
"4" => { "value" => "Tied housing or rented with job" },
"25" => { "value" => "Any other accommodation" },
}.freeze
QUESTION_NUMBER_FROM_YEAR = { 2023 => 78, 2024 => 77 }.freeze
end

8
app/models/form/lettings/subsections/household_characteristics.rb

@ -17,6 +17,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdLeadAgeValueCheck.new(nil, nil, self),
Form::Lettings::Pages::LeadTenantUnderRetirementValueCheck.new("age_lead_tenant_under_retirement_value_check", nil, self),
Form::Lettings::Pages::LeadTenantOverRetirementValueCheck.new("age_lead_tenant_over_retirement_value_check", nil, self),
(Form::Lettings::Pages::LeadTenantSexRegisteredAtBirth.new(nil, nil, self) if form.start_year_2026_or_later?),
Form::Lettings::Pages::LeadTenantGenderIdentity.new(nil, nil, self),
Form::Lettings::Pages::NoFemalesPregnantHouseholdLeadValueCheck.new(nil, nil, self),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdLeadValueCheck.new(nil, nil, self),
@ -45,6 +46,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("age_2_under_retirement_value_check", nil, self, person_index: 2),
Form::Lettings::Pages::PersonOverRetirementValueCheck.new("age_2_over_retirement_value_check", nil, self, person_index: 2),
(Form::Lettings::Pages::PartnerUnder16ValueCheck.new("age_2_partner_under_16_value_check", nil, self, person_index: 2) if form.start_year_2024_or_later? && !form.start_year_2026_or_later?),
(Form::Lettings::Pages::PersonSexRegisteredAtBirth.new(nil, nil, self, person_index: 2) if form.start_year_2026_or_later?),
Form::Lettings::Pages::PersonGenderIdentity.new(nil, nil, self, person_index: 2),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.new(nil, nil, self, person_index: 2),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
@ -67,6 +69,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("age_3_under_retirement_value_check", nil, self, person_index: 3),
Form::Lettings::Pages::PersonOverRetirementValueCheck.new("age_3_over_retirement_value_check", nil, self, person_index: 3),
(Form::Lettings::Pages::PartnerUnder16ValueCheck.new("age_3_partner_under_16_value_check", nil, self, person_index: 3) if form.start_year_2024_or_later? && !form.start_year_2026_or_later?),
(Form::Lettings::Pages::PersonSexRegisteredAtBirth.new(nil, nil, self, person_index: 3) if form.start_year_2026_or_later?),
Form::Lettings::Pages::PersonGenderIdentity.new(nil, nil, self, person_index: 3),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.new(nil, nil, self, person_index: 3),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
@ -89,6 +92,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("age_4_under_retirement_value_check", nil, self, person_index: 4),
Form::Lettings::Pages::PersonOverRetirementValueCheck.new("age_4_over_retirement_value_check", nil, self, person_index: 4),
(Form::Lettings::Pages::PartnerUnder16ValueCheck.new("age_4_partner_under_16_value_check", nil, self, person_index: 4) if form.start_year_2024_or_later? && !form.start_year_2026_or_later?),
(Form::Lettings::Pages::PersonSexRegisteredAtBirth.new(nil, nil, self, person_index: 4) if form.start_year_2026_or_later?),
Form::Lettings::Pages::PersonGenderIdentity.new(nil, nil, self, person_index: 4),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.new(nil, nil, self, person_index: 4),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
@ -111,6 +115,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("age_5_under_retirement_value_check", nil, self, person_index: 5),
Form::Lettings::Pages::PersonOverRetirementValueCheck.new("age_5_over_retirement_value_check", nil, self, person_index: 5),
(Form::Lettings::Pages::PartnerUnder16ValueCheck.new("age_5_partner_under_16_value_check", nil, self, person_index: 5) if form.start_year_2024_or_later? && !form.start_year_2026_or_later?),
(Form::Lettings::Pages::PersonSexRegisteredAtBirth.new(nil, nil, self, person_index: 5) if form.start_year_2026_or_later?),
Form::Lettings::Pages::PersonGenderIdentity.new(nil, nil, self, person_index: 5),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.new(nil, nil, self, person_index: 5),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
@ -133,6 +138,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("age_6_under_retirement_value_check", nil, self, person_index: 6),
Form::Lettings::Pages::PersonOverRetirementValueCheck.new("age_6_over_retirement_value_check", nil, self, person_index: 6),
(Form::Lettings::Pages::PartnerUnder16ValueCheck.new("age_6_partner_under_16_value_check", nil, self, person_index: 6) if form.start_year_2024_or_later? && !form.start_year_2026_or_later?),
(Form::Lettings::Pages::PersonSexRegisteredAtBirth.new(nil, nil, self, person_index: 6) if form.start_year_2026_or_later?),
Form::Lettings::Pages::PersonGenderIdentity.new(nil, nil, self, person_index: 6),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.new(nil, nil, self, person_index: 6),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
@ -155,6 +161,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("age_7_under_retirement_value_check", nil, self, person_index: 7),
Form::Lettings::Pages::PersonOverRetirementValueCheck.new("age_7_over_retirement_value_check", nil, self, person_index: 7),
(Form::Lettings::Pages::PartnerUnder16ValueCheck.new("age_7_partner_under_16_value_check", nil, self, person_index: 7) if form.start_year_2024_or_later? && !form.start_year_2026_or_later?),
(Form::Lettings::Pages::PersonSexRegisteredAtBirth.new(nil, nil, self, person_index: 7) if form.start_year_2026_or_later?),
Form::Lettings::Pages::PersonGenderIdentity.new(nil, nil, self, person_index: 7),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.new(nil, nil, self, person_index: 7),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
@ -177,6 +184,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("age_8_under_retirement_value_check", nil, self, person_index: 8),
Form::Lettings::Pages::PersonOverRetirementValueCheck.new("age_8_over_retirement_value_check", nil, self, person_index: 8),
(Form::Lettings::Pages::PartnerUnder16ValueCheck.new("age_8_partner_under_16_value_check", nil, self, person_index: 8) if form.start_year_2024_or_later? && !form.start_year_2026_or_later?),
(Form::Lettings::Pages::PersonSexRegisteredAtBirth.new(nil, nil, self, person_index: 8) if form.start_year_2026_or_later?),
Form::Lettings::Pages::PersonGenderIdentity.new(nil, nil, self, person_index: 8),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.new(nil, nil, self, person_index: 8),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,

16
app/models/form/sales/pages/person_sex_registered_at_birth.rb

@ -0,0 +1,16 @@
class Form::Sales::Pages::PersonSexRegisteredAtBirth < ::Form::Page
def initialize(id, hsh, subsection, person_index:)
super(id, hsh, subsection)
@copy_key = "sales.household_characteristics.sexrab2.person" if person_index == 2
@person_index = person_index
@depends_on = [
{ "details_known_#{person_index}" => 1 },
]
end
def questions
@questions ||= [
Form::Sales::Questions::PersonSexRegisteredAtBirth.new("sexrab#{@person_index}", nil, self, person_index: @person_index),
]
end
end

20
app/models/form/sales/pages/sex_registered_at_birth1.rb

@ -0,0 +1,20 @@
class Form::Sales::Pages::SexRegisteredAtBirth1 < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "buyer_1_sex_registered_at_birth"
@depends_on = [
{
"buyer_has_seen_privacy_notice?" => true,
},
{
"buyer_not_interviewed?" => true,
},
]
end
def questions
@questions ||= [
Form::Sales::Questions::SexRegisteredAtBirth1.new(nil, nil, self),
]
end
end

23
app/models/form/sales/pages/sex_registered_at_birth2.rb

@ -0,0 +1,23 @@
class Form::Sales::Pages::SexRegisteredAtBirth2 < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "buyer_2_sex_registered_at_birth"
@copy_key = "sales.household_characteristics.sexrab2.buyer"
@depends_on = [
{
"joint_purchase?" => true,
"buyer_has_seen_privacy_notice?" => true,
},
{
"joint_purchase?" => true,
"buyer_not_interviewed?" => true,
},
]
end
def questions
@questions ||= [
Form::Sales::Questions::SexRegisteredAtBirth2.new(nil, nil, self),
]
end
end

19
app/models/form/sales/questions/person_sex_registered_at_birth.rb

@ -0,0 +1,19 @@
class Form::Sales::Questions::PersonSexRegisteredAtBirth < ::Form::Question
def initialize(id, hsh, page, person_index:)
super(id, hsh, page)
@type = "radio"
@copy_key = "sales.household_characteristics.sexrab2.person" if person_index == 2
@check_answers_card_number = person_index
@answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end
ANSWER_OPTIONS = {
"F" => { "value" => "Female" },
"M" => { "value" => "Male" },
"divider" => { "value" => true },
"R" => { "value" => "Person prefers not to say" },
}.freeze
QUESTION_NUMBER_FROM_YEAR = { 2026 => 0 }.freeze
end

19
app/models/form/sales/questions/sex_registered_at_birth1.rb

@ -0,0 +1,19 @@
class Form::Sales::Questions::SexRegisteredAtBirth1 < ::Form::Question
def initialize(id, hsh, page)
super
@id = "sexrab1"
@type = "radio"
@check_answers_card_number = 1
@answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end
ANSWER_OPTIONS = {
"F" => { "value" => "Female" },
"M" => { "value" => "Male" },
"divider" => { "value" => true },
"R" => { "value" => "Buyer prefers not to say" },
}.freeze
QUESTION_NUMBER_FROM_YEAR = { 2026 => 0 }.freeze
end

20
app/models/form/sales/questions/sex_registered_at_birth2.rb

@ -0,0 +1,20 @@
class Form::Sales::Questions::SexRegisteredAtBirth2 < ::Form::Question
def initialize(id, hsh, page)
super
@id = "sexrab2"
@type = "radio"
@copy_key = "sales.household_characteristics.sexrab2.buyer"
@check_answers_card_number = 2
@answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end
ANSWER_OPTIONS = {
"F" => { "value" => "Female" },
"M" => { "value" => "Male" },
"divider" => { "value" => true },
"R" => { "value" => "Buyer prefers not to say" },
}.freeze
QUESTION_NUMBER_FROM_YEAR = { 2026 => 0 }.freeze
end

7
app/models/form/sales/subsections/household_characteristics.rb

@ -24,6 +24,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection
(Form::Sales::Pages::NotRetiredValueCheck.new("age_1_not_retired_value_check", nil, self, person_index: 1) if form.start_year_2024_or_later?),
Form::Sales::Pages::OldPersonsSharedOwnershipValueCheck.new("age_1_old_persons_shared_ownership_joint_purchase_value_check", nil, self, joint_purchase: true),
Form::Sales::Pages::OldPersonsSharedOwnershipValueCheck.new("age_1_old_persons_shared_ownership_value_check", nil, self, joint_purchase: false),
(Form::Sales::Pages::SexRegisteredAtBirth1.new(nil, nil, self) if form.start_year_2026_or_later?),
Form::Sales::Pages::GenderIdentity1.new(nil, nil, self),
Form::Sales::Pages::Buyer1EthnicGroup.new(nil, nil, self),
Form::Sales::Pages::Buyer1EthnicBackgroundBlack.new(nil, nil, self),
@ -46,6 +47,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Sales::Pages::RetirementValueCheck.new("age_2_buyer_retirement_value_check", nil, self, person_index: 2),
(Form::Sales::Pages::NotRetiredValueCheck.new("age_2_buyer_not_retired_value_check", nil, self, person_index: 2) if form.start_year_2024_or_later?),
(Form::Sales::Pages::PersonStudentNotChildValueCheck.new("buyer_2_age_student_not_child_value_check", nil, self, person_index: 2) unless form.start_year_2025_or_later?),
(Form::Sales::Pages::SexRegisteredAtBirth2.new(nil, nil, self) if form.start_year_2026_or_later?),
Form::Sales::Pages::GenderIdentity2.new(nil, nil, self),
buyer_2_ethnicity_nationality_pages,
Form::Sales::Pages::Buyer2WorkingSituation.new(nil, nil, self),
@ -67,6 +69,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection
(Form::Sales::Pages::NotRetiredValueCheck.new("age_2_not_retired_value_check", nil, self, person_index: 2) if form.start_year_2024_or_later?),
(Form::Sales::Pages::PersonStudentNotChildValueCheck.new("age_2_student_not_child_value_check", nil, self, person_index: 2) unless form.start_year_2025_or_later?),
(Form::Sales::Pages::PartnerUnder16ValueCheck.new("age_2_partner_under_16_value_check", nil, self, person_index: 2) if form.start_year_2024_or_later?),
(Form::Sales::Pages::PersonSexRegisteredAtBirth.new("person_2_sex_registered_at_birth", nil, self, person_index: 2) if form.start_year_2026_or_later?),
Form::Sales::Pages::PersonGenderIdentity.new("person_2_gender_identity", nil, self, person_index: 2),
Form::Sales::Pages::PersonWorkingSituation.new("person_2_working_situation", nil, self, person_index: 2),
Form::Sales::Pages::RetirementValueCheck.new("working_situation_2_retirement_value_check", nil, self, person_index: 2),
@ -82,6 +85,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection
(Form::Sales::Pages::NotRetiredValueCheck.new("age_3_not_retired_value_check", nil, self, person_index: 3) if form.start_year_2024_or_later?),
(Form::Sales::Pages::PersonStudentNotChildValueCheck.new("age_3_student_not_child_value_check", nil, self, person_index: 3) unless form.start_year_2025_or_later?),
(Form::Sales::Pages::PartnerUnder16ValueCheck.new("age_3_partner_under_16_value_check", nil, self, person_index: 3) if form.start_year_2024_or_later?),
(Form::Sales::Pages::PersonSexRegisteredAtBirth.new("person_3_sex_registered_at_birth", nil, self, person_index: 3) if form.start_year_2026_or_later?),
Form::Sales::Pages::PersonGenderIdentity.new("person_3_gender_identity", nil, self, person_index: 3),
Form::Sales::Pages::PersonWorkingSituation.new("person_3_working_situation", nil, self, person_index: 3),
Form::Sales::Pages::RetirementValueCheck.new("working_situation_3_retirement_value_check", nil, self, person_index: 3),
@ -97,6 +101,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection
(Form::Sales::Pages::NotRetiredValueCheck.new("age_4_not_retired_value_check", nil, self, person_index: 4) if form.start_year_2024_or_later?),
(Form::Sales::Pages::PersonStudentNotChildValueCheck.new("age_4_student_not_child_value_check", nil, self, person_index: 4) unless form.start_year_2025_or_later?),
(Form::Sales::Pages::PartnerUnder16ValueCheck.new("age_4_partner_under_16_value_check", nil, self, person_index: 4) if form.start_year_2024_or_later?),
(Form::Sales::Pages::PersonSexRegisteredAtBirth.new("person_4_sex_registered_at_birth", nil, self, person_index: 4) if form.start_year_2026_or_later?),
Form::Sales::Pages::PersonGenderIdentity.new("person_4_gender_identity", nil, self, person_index: 4),
Form::Sales::Pages::PersonWorkingSituation.new("person_4_working_situation", nil, self, person_index: 4),
Form::Sales::Pages::RetirementValueCheck.new("working_situation_4_retirement_value_check", nil, self, person_index: 4),
@ -112,6 +117,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection
(Form::Sales::Pages::NotRetiredValueCheck.new("age_5_not_retired_value_check", nil, self, person_index: 5) if form.start_year_2024_or_later?),
(Form::Sales::Pages::PersonStudentNotChildValueCheck.new("age_5_student_not_child_value_check", nil, self, person_index: 5) unless form.start_year_2025_or_later?),
(Form::Sales::Pages::PartnerUnder16ValueCheck.new("age_5_partner_under_16_value_check", nil, self, person_index: 5) if form.start_year_2024_or_later?),
(Form::Sales::Pages::PersonSexRegisteredAtBirth.new("person_5_sex_registered_at_birth", nil, self, person_index: 5) if form.start_year_2026_or_later?),
Form::Sales::Pages::PersonGenderIdentity.new("person_5_gender_identity", nil, self, person_index: 5),
Form::Sales::Pages::PersonWorkingSituation.new("person_5_working_situation", nil, self, person_index: 5),
Form::Sales::Pages::RetirementValueCheck.new("working_situation_5_retirement_value_check", nil, self, person_index: 5),
@ -127,6 +133,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection
(Form::Sales::Pages::NotRetiredValueCheck.new("age_6_not_retired_value_check", nil, self, person_index: 6) if form.start_year_2024_or_later?),
(Form::Sales::Pages::PersonStudentNotChildValueCheck.new("age_6_student_not_child_value_check", nil, self, person_index: 6) unless form.start_year_2025_or_later?),
(Form::Sales::Pages::PartnerUnder16ValueCheck.new("age_6_partner_under_16_value_check", nil, self, person_index: 6) if form.start_year_2024_or_later?),
(Form::Sales::Pages::PersonSexRegisteredAtBirth.new("person_6_sex_registered_at_birth", nil, self, person_index: 6) if form.start_year_2026_or_later?),
Form::Sales::Pages::PersonGenderIdentity.new("person_6_gender_identity", nil, self, person_index: 6),
Form::Sales::Pages::PersonWorkingSituation.new("person_6_working_situation", nil, self, person_index: 6),
Form::Sales::Pages::RetirementValueCheck.new("working_situation_6_retirement_value_check", nil, self, person_index: 6),

5
app/services/bulk_upload/lettings/year2026/csv_parser.rb

@ -4,8 +4,7 @@ class BulkUpload::Lettings::Year2026::CsvParser
include CollectionTimeHelper
# TODO: CLDC-4162: Update when 2026 format is known
FIELDS = 129
MAX_COLUMNS = 130
FIELDS = 137
FORM_YEAR = 2026
attr_reader :path
@ -66,7 +65,7 @@ class BulkUpload::Lettings::Year2026::CsvParser
max_columns_count = body_rows.map(&:size).max - col_offset
max_columns_count > MAX_COLUMNS
max_columns_count > FIELDS
end
def wrong_template_for_year?

53
app/services/bulk_upload/lettings/year2026/row_parser.rb

@ -134,6 +134,15 @@ class BulkUpload::Lettings::Year2026::RowParser
field_127: "What is the support charge?",
field_128: "After the household has received any housing-related benefits, will they still need to pay for rent and charges?",
field_129: "What do you expect the outstanding amount to be?",
field_130: "Lead tenant's sex, as registered at birth",
field_131: "Person 2's sex, as registered at birth",
field_132: "Person 3's sex, as registered at birth",
field_133: "Person 4's sex, as registered at birth",
field_134: "Person 5's sex, as registered at birth",
field_135: "Person 6's sex, as registered at birth",
field_136: "Person 7's sex, as registered at birth",
field_137: "Person 8's sex, as registered at birth",
}.freeze
RENT_TYPE_BU_MAPPING = {
@ -283,6 +292,15 @@ class BulkUpload::Lettings::Year2026::RowParser
attribute :field_128, :integer
attribute :field_129, :decimal
attribute :field_130, :string
attribute :field_131, :string
attribute :field_132, :string
attribute :field_133, :string
attribute :field_134, :string
attribute :field_135, :string
attribute :field_136, :string
attribute :field_137, :string
validate :validate_valid_radio_option, on: :before_log
validates :field_11,
@ -541,6 +559,7 @@ class BulkUpload::Lettings::Year2026::RowParser
!supported_housing? ? "field_24" : nil, # postcode # TODO: CLDC-4119: add postcode to hash for supported housing
"field_42", # age1
"field_43", # sex1
"field_130", # sexrab1
"field_46", # ecstat1
)
if [field_124, field_125, field_126, field_127].all?(&:present?)
@ -689,6 +708,7 @@ private
[
"startdate",
"age1",
"sexrab1",
"sex1",
"ecstat1",
"owning_organisation",
@ -979,6 +999,7 @@ private
errors.add(:field_24, error_message) unless supported_housing? # postcode_full # TODO: CLDC-4119: add postcode to error fields for supported housing
errors.add(:field_25, error_message) unless supported_housing? # la # TODO: CLDC-4119: add LA to error fields for supported housing
errors.add(:field_42, error_message) # age1
errors.add(:field_130, error_message) # sexrab1
errors.add(:field_43, error_message) # sex1
errors.add(:field_46, error_message) # ecstat1
errors.add(:field_122, error_message) unless general_needs? # household_charge
@ -1150,6 +1171,15 @@ private
town_or_city: [:field_21],
county: [:field_22],
uprn_selection: [:field_19],
sexrab1: %i[field_130],
sexrab2: %i[field_131],
sexrab3: %i[field_132],
sexrab4: %i[field_133],
sexrab5: %i[field_134],
sexrab6: %i[field_135],
sexrab7: %i[field_136],
sexrab8: %i[field_137],
}.compact
end
@ -1356,6 +1386,15 @@ private
attributes["postcode_full_input"] = postcode_full
attributes["select_best_address_match"] = true if field_18.blank?
attributes["sexrab1"] = field_130
attributes["sexrab2"] = field_131
attributes["sexrab3"] = field_132
attributes["sexrab4"] = field_133
attributes["sexrab5"] = field_134
attributes["sexrab6"] = field_135
attributes["sexrab7"] = field_136
attributes["sexrab8"] = field_137
attributes
end
@ -1462,31 +1501,31 @@ private
end
def person_2_present?
field_47.present? || field_48.present? || field_49.present?
field_47.present? || field_48.present? || field_49.present? || field_131.present?
end
def person_3_present?
field_51.present? || field_52.present? || field_53.present?
field_51.present? || field_52.present? || field_53.present? || field_132.present?
end
def person_4_present?
field_55.present? || field_56.present? || field_57.present?
field_55.present? || field_56.present? || field_57.present? || field_133.present?
end
def person_5_present?
field_59.present? || field_60.present? || field_61.present?
field_59.present? || field_60.present? || field_61.present? || field_134.present?
end
def person_6_present?
field_63.present? || field_64.present? || field_65.present?
field_63.present? || field_64.present? || field_65.present? || field_135.present?
end
def person_7_present?
field_67.present? || field_68.present? || field_69.present?
field_67.present? || field_68.present? || field_69.present? || field_136.present?
end
def person_8_present?
field_71.present? || field_72.present? || field_73.present?
field_71.present? || field_72.present? || field_73.present? || field_137.present?
end
def leftreg

2
app/services/bulk_upload/sales/validator.rb

@ -167,7 +167,7 @@ private
column_count = rows.map(&:size).max
errors.add(:base, I18n.t("validations.sales.#{@bulk_upload.year}.bulk_upload.wrong_template.over_max_column_count")) if column_count > csv_parser.class::MAX_COLUMNS
errors.add(:base, I18n.t("validations.sales.#{@bulk_upload.year}.bulk_upload.wrong_template.over_max_column_count")) if column_count > csv_parser.class::FIELDS + 1
end
def validate_correct_template

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

@ -4,8 +4,7 @@ class BulkUpload::Sales::Year2026::CsvParser
include CollectionTimeHelper
# TODO: CLDC-4162: Update when 2026 format is known
FIELDS = 121
MAX_COLUMNS = 142
FIELDS = 127
FORM_YEAR = 2026
attr_reader :path

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

@ -135,6 +135,13 @@ class BulkUpload::Sales::Year2026::RowParser
field_119: "Does this include any extra borrowing?",
field_120: "How much was the cash deposit paid on the property?",
field_121: "What are the total monthly leasehold charges for the property?",
field_122: "Buyer 1's sex, as registered at birth",
field_123: "Buyer/Person 2's sex, as registered at birth",
field_124: "Person 3's sex, as registered at birth",
field_125: "Person 4's sex, as registered at birth",
field_126: "Person 5's sex, as registered at birth",
field_127: "Person 6's sex, as registered at birth",
}.freeze
ERROR_BASE_KEY = "validations.sales.2026.bulk_upload".freeze
@ -275,6 +282,13 @@ class BulkUpload::Sales::Year2026::RowParser
attribute :field_120, :decimal
attribute :field_121, :decimal
attribute :field_122, :string
attribute :field_123, :string
attribute :field_124, :string
attribute :field_125, :string
attribute :field_126, :string
attribute :field_127, :string
validates :field_1,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "sale completion date (day)."),
@ -513,6 +527,7 @@ class BulkUpload::Sales::Year2026::RowParser
"field_22", # postcode
"field_28", # age1
"field_29", # sex1
"field_122", # sexrab1
"field_32", # ecstat1
)
end
@ -780,6 +795,12 @@ private
lasttransaction: %i[field_104 field_105 field_106],
initialpurchase: %i[field_100 field_101 field_102],
sexrab1: %i[field_122],
sexrab2: %i[field_123],
sexrab3: %i[field_124],
sexrab4: %i[field_125],
sexrab5: %i[field_126],
sexrab6: %i[field_127],
}
end
@ -815,6 +836,13 @@ private
attributes["sex5"] = field_52
attributes["sex6"] = field_56
attributes["sexrab1"] = field_122
attributes["sexrab2"] = field_123
attributes["sexrab3"] = field_124
attributes["sexrab4"] = field_125
attributes["sexrab5"] = field_126
attributes["sexrab6"] = field_127
attributes["relat2"] = relationship_from_is_partner(field_34)
attributes["relat3"] = relationship_from_is_partner(field_42)
attributes["relat4"] = relationship_from_is_partner(field_46)
@ -1013,23 +1041,23 @@ private
end
def person_2_present?
field_35.present? || field_36.present? || field_34.present?
field_35.present? || field_36.present? || field_34.present? || field_123.present?
end
def person_3_present?
field_43.present? || field_44.present? || field_42.present?
field_43.present? || field_44.present? || field_42.present? || field_124.present?
end
def person_4_present?
field_47.present? || field_48.present? || field_46.present?
field_47.present? || field_48.present? || field_46.present? || field_125.present?
end
def person_5_present?
field_51.present? || field_52.present? || field_50.present?
field_51.present? || field_52.present? || field_50.present? || field_126.present?
end
def person_6_present?
field_55.present? || field_56.present? || field_54.present?
field_55.present? || field_56.present? || field_54.present? || field_127.present?
end
def relationship_from_is_partner(is_partner)
@ -1241,6 +1269,7 @@ private
%w[
saledate
age1
sexrab1
sex1
ecstat1
owning_organisation
@ -1419,6 +1448,7 @@ private
errors.add(:field_22, error_message) # Postcode
errors.add(:field_28, error_message) # Buyer 1 age
errors.add(:field_29, error_message) # Buyer 1 gender
errors.add(:field_122, error_message) # Buyer 1 sex registered at birth
errors.add(:field_32, error_message) # Buyer 1 working situation
errors.add(:field_7, error_message) # Purchaser code
end

1
app/services/csv/lettings_log_csv_service.rb

@ -137,6 +137,7 @@ module Csv
hash["age1"] = { "refused_code" => "-9", "refused_label" => "Not known", "age_known_field" => "age1_known" }
(2..8).each do |i|
hash["age#{i}"] = { "refused_code" => "-9", "refused_label" => "Not known", "details_known_field" => "details_known_#{i}", "age_known_field" => "age#{i}_known" }
hash["sexrab#{i}"] = { "refused_code" => "R", "refused_label" => "Prefers not to say", "details_known_field" => "details_known_#{i}" }
hash["sex#{i}"] = { "refused_code" => "R", "refused_label" => "Prefers not to say", "details_known_field" => "details_known_#{i}" }
hash["relat#{i}"] = { "refused_code" => "R", "refused_label" => "Prefers not to say", "details_known_field" => "details_known_#{i}" }
hash["ecstat#{i}"] = { "refused_code" => "10", "refused_label" => "Prefers not to say", "details_known_field" => "details_known_#{i}" }

1
app/services/csv/sales_log_csv_service.rb

@ -114,6 +114,7 @@ module Csv
hash["age1"] = { "refused_code" => "-9", "refused_label" => "Not known", "age_known_field" => "age1_known" }
(2..6).each do |i|
hash["age#{i}"] = { "refused_code" => "-9", "refused_label" => "Not known", "details_known_field" => "details_known_#{i}", "age_known_field" => "age#{i}_known" }
hash["sexrab#{i}"] = { "refused_code" => "R", "refused_label" => "Prefers not to say", "details_known_field" => "details_known_#{i}" }
hash["sex#{i}"] = { "refused_code" => "R", "refused_label" => "Prefers not to say", "details_known_field" => "details_known_#{i}" }
hash["relat#{i}"] = { "refused_code" => "R", "refused_label" => "Prefers not to say", "details_known_field" => "details_known_#{i}" }
hash["ecstat#{i}"] = { "refused_code" => "10", "refused_label" => "Prefers not to say", "details_known_field" => "details_known_#{i}" }

99
app/services/exports/lettings_log_export_constants.rb

@ -7,15 +7,13 @@ module Exports::LettingsLogExportConstants
csv: 2,
}.freeze
EXPORT_FIELDS = Set[
ALL_YEAR_EXPORT_FIELDS = Set[
"armedforces",
"beds",
"benefits",
"brent",
"builtype",
"cap",
"cbl",
"chcharge",
"chr",
"cligrp1",
"cligrp2",
@ -53,12 +51,10 @@ module Exports::LettingsLogExportConstants
"mantype",
"mobstand",
"mrcdate",
"national",
"needstype",
"new_old",
"newprop",
"nocharge",
"offered",
"owningorgid",
"owningorgname",
"period",
@ -138,6 +134,49 @@ module Exports::LettingsLogExportConstants
"location_status",
"amended_by",
"duplicate_set_id",
"assigned_to",
"created_by",
]
(1..8).each do |index|
ALL_YEAR_EXPORT_FIELDS << "age#{index}"
ALL_YEAR_EXPORT_FIELDS << "ecstat#{index}"
ALL_YEAR_EXPORT_FIELDS << "sex#{index}"
end
(2..8).each do |index|
ALL_YEAR_EXPORT_FIELDS << "relat#{index}"
end
(1..10).each do |index|
ALL_YEAR_EXPORT_FIELDS << "illness_type_#{index}"
end
%w[a b c d e f g h].each do |letter|
ALL_YEAR_EXPORT_FIELDS << "housingneeds_#{letter}"
end
YEAR_2021_EXPORT_FIELDS = Set[
"builtype",
"chcharge",
"national",
"offered",
]
YEAR_2022_EXPORT_FIELDS = Set[
"builtype",
"chcharge",
"national",
"offered",
]
YEAR_2023_EXPORT_FIELDS = Set[
"builtype",
"chcharge",
"national",
"offered",
]
YEAR_2024_EXPORT_FIELDS = Set[
"builtype",
"chcharge",
"accessible_register",
"nationality_all",
"bulk_upload_id",
@ -153,26 +192,10 @@ module Exports::LettingsLogExportConstants
"pscharge_value_check",
"supcharg_value_check",
"carehome_charges_value_check",
"assigned_to",
"created_by",
]
(1..8).each do |index|
EXPORT_FIELDS << "age#{index}"
EXPORT_FIELDS << "ecstat#{index}"
EXPORT_FIELDS << "sex#{index}"
end
(2..8).each do |index|
EXPORT_FIELDS << "relat#{index}"
end
(1..10).each do |index|
EXPORT_FIELDS << "illness_type_#{index}"
end
%w[a b c d e f g h].each do |letter|
EXPORT_FIELDS << "housingneeds_#{letter}"
end
POST_2024_EXPORT_FIELDS = Set[
YEAR_2025_EXPORT_FIELDS = Set[
"builtype",
"accessible_register",
"nationality_all",
"bulk_upload_id",
@ -187,20 +210,26 @@ module Exports::LettingsLogExportConstants
"scharge_value_check",
"pscharge_value_check",
"supcharg_value_check",
"carehome_charges_value_check",
]
PRE_2024_EXPORT_FIELDS = Set[
"national",
"offered"
]
PRE_2025_EXPORT_FIELDS = Set[
"carehome_charges_value_check",
"chcharge"
YEAR_2026_EXPORT_FIELDS = Set[
"accessible_register",
"nationality_all",
"bulk_upload_id",
"address_line1_as_entered",
"address_line2_as_entered",
"town_or_city_as_entered",
"county_as_entered",
"postcode_full_as_entered",
"la_as_entered",
"net_income_value_check",
"rent_value_check",
"scharge_value_check",
"pscharge_value_check",
"supcharg_value_check",
]
PRE_2026_EXPORT_FIELDS = Set[
"builtype"
]
(1..8).each do |index|
YEAR_2026_EXPORT_FIELDS << "sexrab#{index}"
end
end

46
app/services/exports/lettings_log_export_service.rb

@ -131,6 +131,7 @@ module Exports
attribute_hash["age#{index}"] = -9
attribute_hash["sex#{index}"] = "R"
attribute_hash["sexrab#{index}"] = "R"
attribute_hash["relat#{index}"] = "R"
attribute_hash["ecstat#{index}"] = 10
end
@ -162,16 +163,34 @@ module Exports
attribute_hash["location_status"] = location.status_at(attribute_hash["startdate"])
end
def is_omitted_field?(field_name, lettings_log)
pattern_age = /age\d_known/
details_known_prefix = "details_known_"
field_name.starts_with?(details_known_prefix) ||
pattern_age.match(field_name) ||
!EXPORT_FIELDS.include?(field_name) ||
(lettings_log.form.start_year_2024_or_later? && PRE_2024_EXPORT_FIELDS.include?(field_name)) ||
(!lettings_log.form.start_year_2024_or_later? && POST_2024_EXPORT_FIELDS.include?(field_name)) ||
(lettings_log.form.start_year_2025_or_later? && PRE_2025_EXPORT_FIELDS.include?(field_name)) ||
(lettings_log.form.start_year_2026_or_later? && PRE_2026_EXPORT_FIELDS.include?(field_name))
def is_included_field?(field_name, included_fields)
included_fields.include?(field_name)
end
def get_included_fields(lettings_log)
included_fields = Set[]
included_fields.merge(ALL_YEAR_EXPORT_FIELDS)
year_fields = case lettings_log.collection_start_year
when 2021
YEAR_2021_EXPORT_FIELDS
when 2022
YEAR_2022_EXPORT_FIELDS
when 2023
YEAR_2023_EXPORT_FIELDS
when 2024
YEAR_2024_EXPORT_FIELDS
when 2025
YEAR_2025_EXPORT_FIELDS
when 2026
YEAR_2026_EXPORT_FIELDS
else
Set[]
end
included_fields.merge(year_fields)
included_fields
end
def build_export_xml(lettings_logs)
@ -181,11 +200,12 @@ module Exports
attribute_hash = apply_cds_transformation(lettings_log, EXPORT_MODE[:xml])
form = doc.create_element("form")
doc.at("forms") << form
included_fields = get_included_fields(lettings_log)
attribute_hash.each do |key, value|
if is_omitted_field?(key, lettings_log)
next
else
if is_included_field?(key, included_fields)
form << doc.create_element(key, value)
else
next
end
end
form << doc.create_element("providertype", lettings_log.owning_organisation&.read_attribute_before_type_cast(:provider_type))

268
app/services/exports/sales_log_export_constants.rb

@ -7,140 +7,148 @@ module Exports::SalesLogExportConstants
csv: 2,
}.freeze
EXPORT_FIELDS = Set["ID",
"STATUS",
"DAY",
"MONTH",
"YEAR",
"DUPLICATESET",
"CREATEDDATE",
"UPLOADDATE",
"OWNINGORGID",
"OWNINGORGNAME",
"MANINGORGID",
"MANINGORGNAME",
"USERNAME",
"USERNAMEID",
"PURCHID",
"TYPE",
"OWNERSHIP",
"COLLECTIONYEAR",
"JOINTMORE",
"JOINT",
"BEDS",
"ETHNIC",
"ETHNICGROUP1",
"LIVEINBUYER1",
"BUILTYPE",
"PROPTYPE",
"NOINT",
"LIVEINBUYER2",
"PRIVACYNOTICE",
"WHEEL",
"HHOLDCOUNT",
"LA",
"INCOME1",
"INC1NK",
"INC1MORT",
"INCOME2",
"INC2NK",
"SAVINGSNK",
"SAVINGS",
"PREVOWN",
"AMENDEDBY",
"AMENDEDBYID",
"MORTGAGE",
"INC2MORT",
"HB",
"FROMBEDS",
"STAIRCASE",
"STAIRBOUGHT",
"STAIROWNED",
"MRENT",
"MRENTPRESTAIRCASING",
"RESALE",
"DEPOSIT",
"CASHDIS",
"DISABLED",
"VALUE",
"EQUITY",
"DISCOUNT",
"GRANT",
"PPCODENK",
"PPOSTC1",
"PPOSTC2",
"PREVLOC",
"PREVLOCNAME",
"PREVIOUSLAKNOWN",
"HHREGRES",
"HHREGRESSTILL",
"PROPLEN",
"HASMSCHARGE",
"MSCHARGE",
"PREVTEN",
"MORTGAGEUSED",
"WCHAIR",
"ARMEDFORCESSPOUSE",
"HODAY",
"HOMONTH",
"HOYEAR",
"FROMPROP",
"SOCPREVTEN",
"MORTLEN1",
"EXTRABOR",
"HHTYPE",
"POSTCODE",
"ISLAINFERRED",
"BULKUPLOADID",
"VALUE_VALUE_CHECK",
"PREVSHARED",
"STAIRCASETOSALE",
"ETHNICGROUP2",
"ETHNIC2",
"BUY2LIVING",
"PREVTEN2",
"UPRN",
"ADDRESS1",
"ADDRESS2",
"TOWNCITY",
"COUNTY",
"LANAME",
"CREATIONMETHOD",
"NATIONALITYALL1",
"NATIONALITYALL2",
"MSCHARGE_VALUE_CHECK",
"ADDRESS1INPUT",
"POSTCODEINPUT",
"ADDRESS_SEARCH_VALUE_CHECK",
"UPRNSELECTED",
"BULKADDRESS1",
"BULKADDRESS2",
"BULKTOWNCITY",
"BULKCOUNTY",
"BULKPOSTCODE",
"BULKLA",
"CREATEDBY",
"CREATEDBYID",
"HASESTATEFEE",
"ESTATEFEE",
"FIRSTSTAIR",
"NUMSTAIR",
"STAIRLASTDAY",
"STAIRLASTMONTH",
"STAIRLASTYEAR",
"STAIRINITIALYEAR",
"STAIRINITIALMONTH",
"STAIRINITIALDAY",
"HASSERVICECHARGES",
"SERVICECHARGES",]
ALL_YEAR_EXPORT_FIELDS = Set[
"ID",
"STATUS",
"DAY",
"MONTH",
"YEAR",
"DUPLICATESET",
"CREATEDDATE",
"UPLOADDATE",
"OWNINGORGID",
"OWNINGORGNAME",
"MANINGORGID",
"MANINGORGNAME",
"USERNAME",
"USERNAMEID",
"PURCHID",
"TYPE",
"OWNERSHIP",
"COLLECTIONYEAR",
"JOINTMORE",
"JOINT",
"BEDS",
"ETHNIC",
"ETHNICGROUP1",
"LIVEINBUYER1",
"BUILTYPE",
"PROPTYPE",
"NOINT",
"LIVEINBUYER2",
"PRIVACYNOTICE",
"WHEEL",
"HHOLDCOUNT",
"LA",
"INCOME1",
"INC1NK",
"INC1MORT",
"INCOME2",
"INC2NK",
"SAVINGSNK",
"SAVINGS",
"PREVOWN",
"AMENDEDBY",
"AMENDEDBYID",
"MORTGAGE",
"INC2MORT",
"HB",
"FROMBEDS",
"STAIRCASE",
"STAIRBOUGHT",
"STAIROWNED",
"MRENT",
"MRENTPRESTAIRCASING",
"RESALE",
"DEPOSIT",
"CASHDIS",
"DISABLED",
"VALUE",
"EQUITY",
"DISCOUNT",
"GRANT",
"PPCODENK",
"PPOSTC1",
"PPOSTC2",
"PREVLOC",
"PREVLOCNAME",
"PREVIOUSLAKNOWN",
"HHREGRES",
"HHREGRESSTILL",
"PROPLEN",
"HASMSCHARGE",
"MSCHARGE",
"PREVTEN",
"MORTGAGEUSED",
"WCHAIR",
"ARMEDFORCESSPOUSE",
"HODAY",
"HOMONTH",
"HOYEAR",
"FROMPROP",
"SOCPREVTEN",
"MORTLEN1",
"EXTRABOR",
"HHTYPE",
"POSTCODE",
"ISLAINFERRED",
"BULKUPLOADID",
"VALUE_VALUE_CHECK",
"PREVSHARED",
"STAIRCASETOSALE",
"ETHNICGROUP2",
"ETHNIC2",
"BUY2LIVING",
"PREVTEN2",
"UPRN",
"ADDRESS1",
"ADDRESS2",
"TOWNCITY",
"COUNTY",
"LANAME",
"CREATIONMETHOD",
"NATIONALITYALL1",
"NATIONALITYALL2",
"MSCHARGE_VALUE_CHECK",
"ADDRESS1INPUT",
"POSTCODEINPUT",
"ADDRESS_SEARCH_VALUE_CHECK",
"UPRNSELECTED",
"BULKADDRESS1",
"BULKADDRESS2",
"BULKTOWNCITY",
"BULKCOUNTY",
"BULKPOSTCODE",
"BULKLA",
"CREATEDBY",
"CREATEDBYID",
"HASESTATEFEE",
"ESTATEFEE",
"FIRSTSTAIR",
"NUMSTAIR",
"STAIRLASTDAY",
"STAIRLASTMONTH",
"STAIRLASTYEAR",
"STAIRINITIALYEAR",
"STAIRINITIALMONTH",
"STAIRINITIALDAY",
"HASSERVICECHARGES",
"SERVICECHARGES",
]
(1..6).each do |index|
EXPORT_FIELDS << "AGE#{index}"
EXPORT_FIELDS << "ECSTAT#{index}"
EXPORT_FIELDS << "SEX#{index}"
ALL_YEAR_EXPORT_FIELDS << "AGE#{index}"
ALL_YEAR_EXPORT_FIELDS << "ECSTAT#{index}"
ALL_YEAR_EXPORT_FIELDS << "SEX#{index}"
end
(2..6).each do |index|
EXPORT_FIELDS << "RELAT#{index}"
ALL_YEAR_EXPORT_FIELDS << "RELAT#{index}"
end
YEAR_2026_EXPORT_FIELDS = Set[]
(1..6).each do |index|
YEAR_2026_EXPORT_FIELDS << "SEXRAB#{index}"
end
end

26
app/services/exports/sales_log_export_service.rb

@ -150,8 +150,23 @@ module Exports
attribute_hash
end
def is_omitted_field?(field_name, _sales_log)
!EXPORT_FIELDS.include?(field_name)
def is_included_field?(field_name, included_fields)
included_fields.include?(field_name)
end
def get_included_fields(sales_log)
included_fields = Set[]
included_fields.merge(ALL_YEAR_EXPORT_FIELDS)
year_fields = case sales_log.collection_start_year
when 2026
YEAR_2026_EXPORT_FIELDS
else
Set[]
end
included_fields.merge(year_fields)
included_fields
end
def build_export_xml(sales_logs)
@ -161,11 +176,12 @@ module Exports
attribute_hash = apply_cds_transformation(sales_log, EXPORT_MODE[:xml])
form = doc.create_element("form")
doc.at("forms") << form
included_fields = get_included_fields(sales_log)
attribute_hash.each do |key, value|
if is_omitted_field?(key, sales_log)
next
else
if is_included_field?(key, included_fields)
form << doc.create_element(key, value)
else
next
end
end
end

2
config/initializers/ostruct.rb

@ -0,0 +1,2 @@
# make OpenStruct available globally
require "ostruct"

56
config/locales/forms/2026/lettings/household_characteristics.en.yml

@ -23,6 +23,13 @@ en:
hint_text: ""
question_text: "Age"
sexrab1:
page_header: ""
check_answer_label: "Lead tenant’s sex registered at birth"
check_answer_prompt: ""
hint_text: "This is the sex that was registered at birth. The next question will ask about the lead tenant's gender identity."
question_text: "What was the lead tenant's sex at birth?"
sex1:
page_header: ""
check_answer_label: "Lead tenant’s gender identity"
@ -116,6 +123,13 @@ en:
hint_text: "Answer 1 for children aged under 1 year old"
question_text: "Age"
sexrab2:
page_header: ""
check_answer_label: "Person 2’s sex registered at birth"
check_answer_prompt: ""
hint_text: "This is the sex that was registered at birth. The next question will ask about their gender identity."
question_text: "What was person 2's sex at birth?"
sex2:
page_header: ""
check_answer_label: "Person 2’s gender identity"
@ -157,6 +171,13 @@ en:
hint_text: "Answer 1 for children aged under 1 year old"
question_text: "Age"
sexrab3:
page_header: ""
check_answer_label: "Person 3’s sex registered at birth"
check_answer_prompt: ""
hint_text: "This is the sex that was registered at birth. The next question will ask about their gender identity."
question_text: "What was person 3's sex at birth?"
sex3:
page_header: ""
check_answer_label: "Person 3’s gender identity"
@ -198,6 +219,13 @@ en:
hint_text: "Answer 1 for children aged under 1 year old"
question_text: "Age"
sexrab4:
page_header: ""
check_answer_label: "Person 4’s sex registered at birth"
check_answer_prompt: ""
hint_text: "This is the sex that was registered at birth. The next question will ask about their gender identity."
question_text: "What was person 4's sex at birth?"
sex4:
page_header: ""
check_answer_label: "Person 4’s gender identity"
@ -239,6 +267,13 @@ en:
hint_text: "Answer 1 for children aged under 1 year old"
question_text: "Age"
sexrab5:
page_header: ""
check_answer_label: "Person 5’s sex registered at birth"
check_answer_prompt: ""
hint_text: "This is the sex that was registered at birth. The next question will ask about their gender identity."
question_text: "What was person 5's sex at birth?"
sex5:
page_header: ""
check_answer_label: "Person 5’s gender identity"
@ -280,6 +315,13 @@ en:
hint_text: "Answer 1 for children aged under 1 year old"
question_text: "Age"
sexrab6:
page_header: ""
check_answer_label: "Person 6’s sex registered at birth"
check_answer_prompt: ""
hint_text: "This is the sex that was registered at birth. The next question will ask about their gender identity."
question_text: "What was person 6's sex at birth?"
sex6:
page_header: ""
check_answer_label: "Person 6’s gender identity"
@ -321,6 +363,13 @@ en:
hint_text: "Answer 1 for children aged under 1 year old"
question_text: "Age"
sexrab7:
page_header: ""
check_answer_label: "Person 7’s sex registered at birth"
check_answer_prompt: ""
hint_text: "This is the sex that was registered at birth. The next question will ask about their gender identity."
question_text: "What was person 7's sex at birth?"
sex7:
page_header: ""
check_answer_label: "Person 7’s gender identity"
@ -362,6 +411,13 @@ en:
hint_text: "Answer 1 for children aged under 1 year old"
question_text: "Age"
sexrab8:
page_header: ""
check_answer_label: "Person 8’s sex registered at birth"
check_answer_prompt: ""
hint_text: "This is the sex that was registered at birth. The next question will ask about their gender identity."
question_text: "What was person 8's sex at birth?"
sex8:
page_header: ""
check_answer_label: "Person 8’s gender identity"

2
config/locales/forms/2026/lettings/household_situation.en.yml

@ -35,7 +35,7 @@ en:
reason:
check_answer_label: "Reason for leaving last settled home"
check_answer_prompt: ""
hint_text: "The tenant’s ‘last settled home’ is their last long-standing home. For tenants who were in temporary accommodation, sleeping rough or otherwise homeless, their last settled home is where they were living previously."
hint_text: "The tenant’s ‘last settled home’ is their last long-standing home. For tenants who were in temporary accommodation, sleeping rough or otherwise homeless, their last settled home is where they were living immediately before that period."
question_text: "What is the tenant’s main reason for the household leaving their last settled home?"
reasonother:
check_answer_label: ""

2
config/locales/forms/2026/lettings/income_and_benefits.en.yml

@ -48,7 +48,7 @@ en:
page_header: ""
check_answer_label: "Frequency of household rent and charges"
check_answer_prompt: ""
hint_text: "Select how often the household is charged. This may be different to how often they pay."
hint_text: "Select how often the household is charged. This may be different to how often they pay.<br><br>Only options relevant to your organisation are shown below. To change these, go to the 'About your organisation' page or contact our helpdesk using the link at the bottom of this page."
question_text: "How often does the household pay rent and other charges?"
care_home:

49
config/locales/forms/2026/sales/household_characteristics.en.yml

@ -16,6 +16,13 @@ en:
hint_text: ""
question_text: "Age"
sexrab1:
page_header: ""
check_answer_label: "Buyer 1’s sex registered at birth"
check_answer_prompt: ""
hint_text: "This is the sex that was registered at birth. The next question will ask about the buyer's gender identity."
question_text: "What was buyer 1's sex at birth?"
sex1:
page_header: ""
check_answer_label: "Buyer 1’s gender identity"
@ -130,6 +137,20 @@ en:
hint_text: ""
question_text: "Age"
sexrab2:
buyer:
page_header: ""
check_answer_label: "Buyer 2’s sex registered at birth"
check_answer_prompt: ""
hint_text: "This is the sex that was registered at birth. The next question will ask about the buyer's gender identity."
question_text: "What was buyer 2's sex at birth?"
person:
page_header: ""
check_answer_label: "Person 2’s sex registered at birth"
check_answer_prompt: ""
hint_text: "This is the sex that was registered at birth. The next question will ask about the person's gender identity."
question_text: "What was person 2's sex at birth?"
sex2:
buyer:
page_header: ""
@ -266,6 +287,13 @@ en:
hint_text: ""
question_text: "Age"
sexrab3:
page_header: ""
check_answer_label: "Person 3’s sex registered at birth"
check_answer_prompt: ""
hint_text: "This is the sex that was registered at birth. The next question will ask about the person's gender identity."
question_text: "What was person 3's sex at birth?"
sex3:
page_header: ""
check_answer_label: "Person 3’s gender identity"
@ -307,6 +335,13 @@ en:
hint_text: ""
question_text: "Age"
sexrab4:
page_header: ""
check_answer_label: "Person 4’s sex registered at birth"
check_answer_prompt: ""
hint_text: "This is the sex that was registered at birth. The next question will ask about the person's gender identity."
question_text: "What was person 4's sex at birth?"
sex4:
page_header: ""
check_answer_label: "Person 4’s gender identity"
@ -348,6 +383,13 @@ en:
hint_text: ""
question_text: "Age"
sexrab5:
page_header: ""
check_answer_label: "Person 5’s sex registered at birth"
check_answer_prompt: ""
hint_text: "This is the sex that was registered at birth. The next question will ask about the person's gender identity."
question_text: "What was person 5's sex at birth?"
sex5:
page_header: ""
check_answer_label: "Person 5’s gender identity"
@ -389,6 +431,13 @@ en:
hint_text: ""
question_text: "Age"
sexrab6:
page_header: ""
check_answer_label: "Person 6’s sex registered at birth"
check_answer_prompt: ""
hint_text: "This is the sex that was registered at birth. The next question will ask about the person's gender identity."
question_text: "What was person 6's sex at birth?"
sex6:
page_header: ""
check_answer_label: "Person 6’s gender identity"

8881
config/rent_range_data/2026.csv

File diff suppressed because it is too large Load Diff

12
db/migrate/20260113143404_add_sex_registered_at_birth_to_sales_logs.rb

@ -0,0 +1,12 @@
class AddSexRegisteredAtBirthToSalesLogs < ActiveRecord::Migration[7.2]
def change
change_table :sales_logs, bulk: true do |t|
t.column :sexrab1, :string
t.column :sexrab2, :string
t.column :sexrab3, :string
t.column :sexrab4, :string
t.column :sexrab5, :string
t.column :sexrab6, :string
end
end
end

14
db/migrate/20260128121417_add_sex_registered_at_birth_to_lettings_logs.rb

@ -0,0 +1,14 @@
class AddSexRegisteredAtBirthToLettingsLogs < ActiveRecord::Migration[7.2]
def change
change_table :lettings_logs, bulk: true do |t|
t.column :sexrab1, :string
t.column :sexrab2, :string
t.column :sexrab3, :string
t.column :sexrab4, :string
t.column :sexrab5, :string
t.column :sexrab6, :string
t.column :sexrab7, :string
t.column :sexrab8, :string
end
end
end

16
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_01_23_150201) do
ActiveRecord::Schema[7.2].define(version: 2026_01_28_121417) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -376,6 +376,14 @@ ActiveRecord::Schema[7.2].define(version: 2026_01_23_150201) do
t.boolean "manual_address_entry_selected", default: false
t.integer "referral_type"
t.integer "working_situation_illness_check"
t.string "sexrab1"
t.string "sexrab2"
t.string "sexrab3"
t.string "sexrab4"
t.string "sexrab5"
t.string "sexrab6"
t.string "sexrab7"
t.string "sexrab8"
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 ["created_by_id"], name: "index_lettings_logs_on_created_by_id"
@ -788,6 +796,12 @@ ActiveRecord::Schema[7.2].define(version: 2026_01_23_150201) do
t.datetime "lasttransaction"
t.datetime "initialpurchase"
t.boolean "manual_address_entry_selected", default: false
t.string "sexrab1"
t.string "sexrab2"
t.string "sexrab3"
t.string "sexrab4"
t.string "sexrab5"
t.string "sexrab6"
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"

16
docs/Gemfile.lock

@ -24,10 +24,12 @@ GEM
ffi (>= 1.15.0)
eventmachine (1.2.7)
execjs (2.8.1)
faraday (2.7.4)
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.2)
faraday (2.14.1)
faraday-net_http (>= 2.0, < 3.5)
json
logger
faraday-net_http (3.4.2)
net-http (~> 0.5)
ffi (1.15.5)
forwardable-extended (2.6.0)
gemoji (3.0.1)
@ -196,6 +198,7 @@ GEM
gemoji (~> 3.0)
html-pipeline (~> 2.2)
jekyll (>= 3.0, < 5.0)
json (2.18.1)
kramdown (2.3.2)
rexml
kramdown-parser-gfm (1.1.0)
@ -204,12 +207,15 @@ GEM
listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
logger (1.7.0)
mercenary (0.3.6)
minima (2.5.1)
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.19.0)
net-http (0.9.1)
uri (>= 0.11.1)
nokogiri (1.18.9-arm64-darwin)
racc (~> 1.4)
nokogiri (1.18.9-x86_64-darwin)
@ -228,7 +234,6 @@ GEM
ffi (~> 1.0)
rexml (3.4.2)
rouge (3.26.0)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
safe_yaml (1.0.5)
sass (3.7.4)
@ -251,6 +256,7 @@ GEM
unf_ext
unf_ext (0.0.8.2)
unicode-display_width (1.8.0)
uri (1.1.1)
webrick (1.8.2)
PLATFORMS

3
spec/factories/lettings_log.rb

@ -40,6 +40,7 @@ FactoryBot.define do
declaration { 1 }
age1_known { 0 }
age1 { 18 }
sexrab1 { "M" }
sex1 { "M" }
hhmemb { 1 }
ecstat1 { 0 }
@ -58,6 +59,7 @@ FactoryBot.define do
tenancycode { Faker::Name.initials(number: 10) }
age1_known { 0 }
age1 { Faker::Number.within(range: 25..45) }
sexrab1 { %w[F M R].sample }
sex1 { %w[F M X R].sample }
ethnic_group { 0 }
ethnic { 2 }
@ -69,6 +71,7 @@ FactoryBot.define do
age2_known { 0 }
details_known_2 { 0 }
age2 { Faker::Number.within(range: 25..45) }
sexrab2 { %w[F M R].sample }
sex2 { %w[F M X R].sample }
ecstat2 { 6 }
homeless { 1 }

13
spec/factories/sales_log.rb

@ -64,6 +64,7 @@ FactoryBot.define do
saledate_today
age1_known { 1 }
age1 { 20 }
sexrab1 { "F" }
sex1 { "F" }
ecstat1 { 1 }
postcode_full { "A1 1AA" }
@ -83,6 +84,7 @@ FactoryBot.define do
privacynotice { 1 }
age1_known { 0 }
age1 { Faker::Number.within(range: 27..45) }
sexrab1 { %w[F M R].sample }
sex1 { %w[F M X R].sample }
national { 18 }
buy1livein { 1 }
@ -93,6 +95,7 @@ FactoryBot.define do
builtype { 1 }
ethnic { 3 }
ethnic_group { 17 }
sexrab2 { %w[F M R].sample }
sex2 { "X" }
buy2livein { "1" }
ecstat1 { "1" }
@ -125,9 +128,13 @@ FactoryBot.define do
savingsnk { 1 }
prevown { 1 }
prevshared { 2 }
sexrab3 { %w[F M R].sample }
sex3 { %w[F M X R].sample }
sexrab4 { %w[F M R].sample }
sex4 { %w[F M X R].sample }
sexrab5 { %w[F M R].sample }
sex5 { %w[F M X R].sample }
sexrab6 { %w[F M R].sample }
sex6 { %w[F M X R].sample }
mortgage { 20_000 }
ecstat3 { 9 }
@ -280,6 +287,7 @@ FactoryBot.define do
privacynotice { 1 }
age1_known { 0 }
age1 { 27 }
sexrab1 { "F" }
sex1 { "F" }
national { 18 }
buy1livein { 1 }
@ -290,6 +298,7 @@ FactoryBot.define do
builtype { 1 }
ethnic { 3 }
ethnic_group { 17 }
sexrab2 { "X" }
sex2 { "X" }
buy2livein { "1" }
ecstat1 { "1" }
@ -322,9 +331,13 @@ FactoryBot.define do
savingsnk { 1 }
prevown { 1 }
prevshared { 2 }
sexrab3 { "F" }
sex3 { "F" }
sexrab4 { "X" }
sex4 { "X" }
sexrab5 { "M" }
sex5 { "M" }
sexrab6 { "X" }
sex6 { "X" }
mortgage { 20_000 }
ecstat3 { 9 }

8
spec/fixtures/exports/general_needs_log_26_27.xml vendored

@ -4,30 +4,38 @@
<status>2</status>
<tenancycode>BZ737</tenancycode>
<age1>35</age1>
<sexrab1>F</sexrab1>
<sex1>F</sex1>
<ethnic>2</ethnic>
<prevten>6</prevten>
<ecstat1>0</ecstat1>
<hhmemb>2</hhmemb>
<age2>32</age2>
<sexrab2>M</sexrab2>
<sex2>M</sex2>
<ecstat2>6</ecstat2>
<age3/>
<sexrab3/>
<sex3/>
<ecstat3/>
<age4/>
<sexrab4/>
<sex4/>
<ecstat4/>
<age5/>
<sexrab5/>
<sex5/>
<ecstat5/>
<age6/>
<sexrab6/>
<sex6/>
<ecstat6/>
<age7/>
<sexrab7/>
<sex7/>
<ecstat7/>
<age8/>
<sexrab8/>
<sex8/>
<ecstat8/>
<homeless>1</homeless>

42
spec/fixtures/files/2026_27_lettings_bulk_upload.csv vendored

@ -1,26 +1,26 @@
Section,Setting up this lettings log,,,,,,,,,,,,,,,Property information,,,,,,,,,,,,,,,,,,,,,Tenancy information,,,,,Household characteristics,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Household needs,,,,,,,,,,,,,,,,,,,,,Household situation,,,,,,,,,,,,,,,,,,,,,"Income, benefits and outgoings",,,,,,,,,,,,
Section,Setting up this lettings log,,,,,,,,,,,,,,,Property information,,,,,,,,,,,,,,,,,,,,,Tenancy information,,,,,Household characteristics,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Household needs,,,,,,,,,,,,,,,,,,,,,Household situation,,,,,,,,,,,,,,,,,,,,,"Income, benefits and outgoings",,,,,,,,,,,,,,,,,,,,
Question,Which organisation owns this property?,Which organisation manages this letting?,What is the CORE username of the account this letting log should be assigned to? ,What is the needs type?,What scheme does this letting belong to?,Which location is this letting for?,Is this letting a renewal of social housing to the same tenant in the same property?,What is the tenancy start date? - day DD,What is the tenancy start date? - month MM,What is the tenancy start date? - year YY,What is the rent type?,Which 'Other' type of Intermediate Rent is this letting?,What is the tenant code?,What is the property reference?,Has the tenant seen or been given access to the MHCLG privacy notice?,What is the reason for the property being vacant?,What type was the property most recently let as?,"If known, provide this property’s UPRN",Address Line 1,Address Line 2,Town or city,County,Part 1 of the property's postcode,Part 2 of the property's postcode,What is the property's local authority?,What type of unit is the property?,Which type of building is the property?,Is the property built or adapted to wheelchair-user standards?,How many bedrooms does the property have?,What is the void date? - day DD,What is the void date? - month MM,What is the void date? - year YY,What date were any major repairs completed on? - day DD,What date were any major repairs completed on? - month MM,What date were any major repairs completed on? - year YY,Is this property older people's housing?,Is this a joint tenancy?,Is this a starter tenancy?,What is the type of tenancy?,"If 'Other', what is the type of tenancy?",What is the length of the fixed-term tenancy to the nearest year?,What is the lead tenant’s age?,Which of these best describes the lead tenant’s gender identity? ,Which of these best describes the lead tenant's ethnic background?,What is the lead tenant’s nationality?,Which of these best describes the lead tenant’s working situation?,Is person 2 the partner of the lead tenant?,What is person 2's age?,Which of these best describes person 2's gender identity?,Which of these best describes person 2's working situation?,Is person 3 the partner of the lead tenant?,What is person 3's age?,Which of these best describes person 3's gender identity?,Which of these best describes person 3's working situation?,Is person 4 the partner of the lead tenant?,What is person 4's age?,Which of these best describes person 4's gender identity?,Which of these best describes person 4's working situation?,Is person 5 the partner of the lead tenant?,What is person 5's age?,Which of these best describes person 5's gender identity?,Which of these best describes person 5's working situation?,Is person 6 the partner of the lead tenant?,What is person 6's age?,Which of these best describes person 6's gender identity?,Which of these best describes person 6's working situation?,Is person 7 the partner of the lead tenant?,What is person 7's age?,Which of these best describes person 7's gender identity?,Which of these best describes person 7's working situation?,Is person 8 the partner of the lead tenant?,What is person 8's age?,Which of these best describes person 8's gender identity?,Which of these best describes person 8's working situation?,Does anybody in the household have links to the UK armed forces?,Is this person still serving in the UK armed forces?,Was this person seriously injured or ill as a result of serving in the UK armed forces?,Is anybody in the household pregnant?,"Disabled access needs
a) Fully wheelchair-accessible housing","Disabled access needs
b) Wheelchair access to essential rooms","Disabled access needs
c) Level access housing","Disabled access needs
f) Other disabled access needs","Disabled access needs
g) No disabled access needs","Disabled access needs
h) Don’t know",Does anybody in the household have a physical or mental health condition (or other illness) expected to last 12 months or more?,Does this person's condition affect their dexterity?,Does this person's condition affect their learning or understanding or concentrating?,Does this person's condition affect their hearing?,Does this person's condition affect their memory?,Does this person's condition affect their mental health?,Does this person's condition affect their mobility?,Does this person's condition affect them socially or behaviourally?,Does this person's condition affect their stamina or breathing or fatigue?,Does this person's condition affect their vision?,Does this person's condition affect them in another way?,How long has the household continuously lived in the local authority area of the new letting?,How long has the household been on the local authority housing register (or waiting list) for the area of the new letting?,What is the tenant’s main reason for the household leaving their last settled home?,"If 'Other', what was the main reason for leaving their last settled home?",Where was the household immediately before this letting?,Did the household experience homelessness immediately before this letting?,Do you know the postcode of the household's last settled home?,Part 1 of postcode of last settled home,Part 2 of postcode of last settled home,What is the local authority of the household's last settled home?,Was the household given 'reasonable preference' by the local authority?,"Reasonable preference reason
They were homeless or about to lose their home (within 56 days)","Reasonable preference reason
They were living in unsanitary, overcrowded or unsatisfactory housing","Reasonable preference reason
They needed to move due to medical and welfare reasons (including disability)","Reasonable preference reason
They needed to move to avoid hardship to themselves or others","Reasonable preference reason
Don't know","How was this letting allocated?
Choice based Lettings (CBL)","How was this letting allocated?
@ -29,14 +29,14 @@ Common Allocations Policy (CAP)","How was this letting allocated?
Common Housing Register (CHR)","How was this letting allocated?
Accessible Housing Register",What was the source of referral for this letting?,Do you know the household's combined total income after tax?,How often does the household receive income?,How much income does the household have in total?,Is the tenant likely to be receiving any of these housing-related benefits?,"How much of the household's income is from Universal Credit, state pensions or benefits?",Does the household pay rent or other charges for the accommodation?,How often does the household pay rent and other charges?,What is the basic rent?,What is the service charge?,What is the personal service charge?,What is the support charge?,"After the household has received any housing-related benefits, will they still need to pay for rent and charges?",What do you expect the outstanding amount to be?
Accessible Housing Register",What was the source of referral for this letting?,Do you know the household's combined total income after tax?,How often does the household receive income?,How much income does the household have in total?,Is the tenant likely to be receiving any of these housing-related benefits?,"How much of the household's income is from Universal Credit, state pensions or benefits?",Does the household pay rent or other charges for the accommodation?,How often does the household pay rent and other charges?,What is the basic rent?,What is the service charge?,What is the personal service charge?,What is the support charge?,"After the household has received any housing-related benefits, will they still need to pay for rent and charges?",What do you expect the outstanding amount to be?,What was the lead tenant's sex at birth?,What was person 2's sex registered at birth?,What was person 3's sex registered at birth?,What was person 4's sex registered at birth?,What was person 5's sex registered at birth?,What was person 6's sex registered at birth?,What was person 7's sex registered at birth?,What was person 8's sex registered at birth?
Additional info,"You can find the org ID on the CORE service under 'Stock owners' or, if your organisation is the stock owner, under 'About your organisation'","You can find the org ID on the CORE service under 'Managing agents' or, if your organisation is the managing agent, under 'About your organisation'","If left empty, the letting log will be assigned to the account used to upload the log.","General needs housing includes both self-contained and shared housing without support or specific adaptations. Supported housing includes direct access hostels, group homes, residential care and nursing homes.","Scheme code. Include the 'S' at the beginning if it has one.
You can find the scheme code on the CORE service under 'Schemes', either by searching for the specific scheme or downloading a csv.","Location code.
You can find the location code on the CORE service under 'Schemes', either by searching for the specific location or downloading a csv.","If the property was previously being used as temporary accommodation, then answer 'no'.",,,,See specification for definitions,,This is how you usually refer to this tenancy on your own systems.,This is how you usually refer to this property on your own systems.,"Make sure the lead tenant has seen or been given access to the Ministry of Housing, Communities and Local Government (MHCLG) privacy notice before completing this log. This is a legal requirement under data protection legislation.","Internal transfer - Where a tenant moved from one social housing property to another property. Their landlord may be the same or may have changed.
Renewal of a fixed term tenancy - to the same tenant in the same property, except if was previously used as temporary accommodation.",This is the rent type of the previous tenancy in this property.,"The Unique Property Reference Number (UPRN) is a unique number system created by Ordnance Survey and used by housing providers and various industries across the UK. An example UPRN is 10010457355.
Renewal of a fixed term tenancy - to the same tenant in the same property, except if was previously used as temporary accommodation.",This is the rent type of the previous tenancy in this property.,"The Unique Property Reference Number (UPRN) is a unique number system created by Ordnance Survey and used by housing providers and various industries across the UK. An example UPRN is 10010457355.
The UPRN may not be the same as the property reference assigned by your organisation.",,,,,Combined with field 22 it should be a postcode which lies within the local authority given in field 25.,Combined with field 21 it should be a postcode which lies within the local authority given in field 25.,,,,"This is whether someone who uses a wheelchair is able to make full use of all of the property’s rooms and facilities, including use of both inside and outside space, and entering and exiting the property.","If shared accommodation, enter the number of bedrooms occupied by this household. A bedsit has 1 bedroom.","Date the property was (legally/contractually) available to let, or for:
- re-lets: the day after previous tenant’s contract end
- new-builds: the day the landlord legally owned the property ('completion date’)
@ -45,7 +45,7 @@ The UPRN may not be the same as the property reference assigned by your organisa
Extra care housing is for tenants with medium to high care and support needs, often with 24 hour access to support staff provided by an agency registered with the Care Quality Commission.",This is where two or more people are named on the tenancy agreement.,"If the tenancy has an ‘introductory period’ answer ‘yes’.
You should submit a CORE log at the beginning of the starter tenancy or introductory period, with the best information you have at the time. You do not need to submit a log when a tenant later rolls onto the main tenancy.",This is about the main tenancy after any starter or introductory period. See specification for definitions.,,Do not include the starter or introductory period. The minimum period is 2 years for social or affordable rent general needs logs. You do not need to submit CORE logs for these types of tenancies if they are shorter than 2 years.,"This is the household member who does the most paid work. If several people do the same amount of paid work, it's the oldest household member.",This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth.,,"If the lead tenant is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, the tenant should decide which country to enter.","This is the household member who does the most paid work. If several people do the same amount of paid work, it's the oldest household member.",,Answer 1 for children aged under 1 year old,This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth.,,,Answer 1 for children aged under 1 year old,This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth.,,,Answer 1 for children aged under 1 year old,This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth.,,,Answer 1 for children aged under 1 year old,This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth.,,,Answer 1 for children aged under 1 year old,This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth.,,,Answer 1 for children aged under 1 year old,This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth.,,,Answer 1 for children aged under 1 year old,This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth.,,"This excludes national service.
If several household members have these links, answer for regular first. If no regular, answer for reserve. If no reserve, answer for spouses or civil partners.",,,,,,,,,,,"For example, lifting and carrying objects, or using a keyboard",,"For example, deafness or partial hearing",,"For example, depression or anxiety","For example, walking short distances or climbing stairs","For example, anything associated with autism spectrum disorder (ASD), including Asperger’s or attention deficit hyperactivity disorder (ADHD)",,"For example, blindness or partial sight",,,,"The tenant's ‘last settled home' is their last long-standing home. For tenants who had temporary accommodation, sleeping rough or otherwise homeless, their last settled home is where they were living previously.",,,,"This is the tenant’s last long-standing home. It is where the tenant was living before any period in temporary accommodation, sleeping rough or otherwise homeless.","Combined with field 104, it should be a postcode which lies within the local authority given in field 105.","Combined with field 103, it should be a postcode which lies within the local authority given in field 105.","This is the tenant’s last long-standing home. It is where the tenant was living before any period in temporary accommodation, sleeping rough or otherwise homeless.",Households may be given ‘reasonable preference’ for social housing under one or more specific category by the local authority. This is also known as ‘priority need’.,,,,,,Where available vacant properties are advertised and applicants are able to bid for specific properties.,Where a common system agreed between a group of housing providers is used to determine applicants' priority for housing.,Where a single waiting list is used by a group of housing providers to receive and process housing applications. Providers may use different approaches to determine priority.,Where the 'access category' or another descriptor of whether an available vacant property meets a range of access needs is displayed to applicants during the allocations process.,,,,"Include any income after tax from employment, pensions, and Universal Credit. Don't include National Insurance (NI) contributions and tax, housing benefit, child benefit, or council tax support.","This is about when the tenant is in their new let. If they are unsure about the situation for their new let and their financial and working situation hasn’t changed significantly, answer based on what housing-related benefits they currently receive.",,"If rent is charged on the property then answer Yes, even if tenants do not pay it themselves.",,"This is the amount paid before any charges are added for services (for example, hot water or cleaning). Households may receive housing benefit or Universal Credit towards basic rent.","For example, cleaning. Households may get household benefits towards the service charge.",For example heating or hot water. This doesn’t include housing benefit or Universal Credit.,Any charges made to fund support services included in the tenancy agreement.,Also known as the 'outstanding amount',You only need to give an approximate figure.
If several household members have these links, answer for regular first. If no regular, answer for reserve. If no reserve, answer for spouses or civil partners.",,,,,,,,,,,"For example, lifting and carrying objects, or using a keyboard",,"For example, deafness or partial hearing",,"For example, depression or anxiety","For example, walking short distances or climbing stairs","For example, anything associated with autism spectrum disorder (ASD), including Asperger’s or attention deficit hyperactivity disorder (ADHD)",,"For example, blindness or partial sight",,,,"The tenant's ‘last settled home' is their last long-standing home. For tenants who had temporary accommodation, sleeping rough or otherwise homeless, their last settled home is where they were living previously.",,,,"This is the tenant’s last long-standing home. It is where the tenant was living before any period in temporary accommodation, sleeping rough or otherwise homeless.","Combined with field 104, it should be a postcode which lies within the local authority given in field 105.","Combined with field 103, it should be a postcode which lies within the local authority given in field 105.","This is the tenant’s last long-standing home. It is where the tenant was living before any period in temporary accommodation, sleeping rough or otherwise homeless.",Households may be given ‘reasonable preference’ for social housing under one or more specific category by the local authority. This is also known as ‘priority need’.,,,,,,Where available vacant properties are advertised and applicants are able to bid for specific properties.,Where a common system agreed between a group of housing providers is used to determine applicants' priority for housing.,Where a single waiting list is used by a group of housing providers to receive and process housing applications. Providers may use different approaches to determine priority.,Where the 'access category' or another descriptor of whether an available vacant property meets a range of access needs is displayed to applicants during the allocations process.,,,,"Include any income after tax from employment, pensions, and Universal Credit. Don't include National Insurance (NI) contributions and tax, housing benefit, child benefit, or council tax support.","This is about when the tenant is in their new let. If they are unsure about the situation for their new let and their financial and working situation hasn’t changed significantly, answer based on what housing-related benefits they currently receive.",,"If rent is charged on the property then answer Yes, even if tenants do not pay it themselves.",,"This is the amount paid before any charges are added for services (for example, hot water or cleaning). Households may receive housing benefit or Universal Credit towards basic rent.","For example, cleaning. Households may get household benefits towards the service charge.",For example heating or hot water. This doesn’t include housing benefit or Universal Credit.,Any charges made to fund support services included in the tenancy agreement.,Also known as the 'outstanding amount',You only need to give an approximate figure.,This is the sex that was registered at birth.,This is the sex that was registered at birth.,This is the sex that was registered at birth.,This is the sex that was registered at birth.,This is the sex that was registered at birth.,This is the sex that was registered at birth.,This is the sex that was registered at birth.,This is the sex that was registered at birth.
Values,Alphanumeric,,Email format,01-Feb,Alphanumeric,Numeric,01-Feb,Jan-31,01-Dec,25 - 26,01-Jul,Text,"Alphanumeric, max 13 characters","Alphanumeric, max 12 characters",1,"5 - 6, or 8 - 22",1 - 3 or 5 - 9,Numeric,Alphanumeric,,Text,,"Alphanumeric,
2 - 4 characters","Alphanumeric,
3 characters","9 character ONS code, beginning with 'E' (https://www.get-information-schools.service.gov.uk/Guidance/LaNameCodes) ","1 - 2, 4 or 6 - 10",01-Feb,01-Feb,01-Jul,Jan-31,01-Dec,Jun-26,Jan-31,01-Dec,Jun-26,"2 - 4, 7 - 8",01-Mar,01-Feb,02-Aug,Text,"1 - 99, see specification for more detail",16 - 120 or R,"F, M, X or R",Jan-20,"3 digit ISO country code, see specification",0 - 10,01-Mar,"Numeric, range 1 - 120 or text (upper case 'R')
@ -77,11 +77,11 @@ Must be >= 16 if working situation = 1 - 8 or 0
Must be <16 if working situation = 9","F, M, X or R","0 - 10
Must be 9 if age <16",01-Jun,03-Jun,01-Mar,,1 or empty,,,,,,01-Mar,1 or empty,,,,,,,,,,1 - 2 or 6 - 12,2 or 6 - 13,"1 - 2, 4, 8 - 14, 16 - 20, 28 - 31, 34 or 44 - 55",Text,"3 - 4, 6 - 7, 9 - 10, 13 - 14, 18 - 19, 21, 23 - 33, 35, 37 - 39 ",1 or 11,01-Feb,"Alphanumeric, 2 - 4 characters","Alphanumeric,
3 characters","9 character ONS code, beginning with 'E' (https://www.get-information-schools.service.gov.uk/Guidance/LaNameCodes) ",01-Mar,1 or empty,,,,,01-Feb,,,,"1 - 4, 7 - 10, 14 - 24",01-Mar,01-Mar,0 - 99999,"1, 3, 6, 9 or 10",01-Apr,0 - 1,01-Oct,xxxx.xx,,,,01-Mar,xxxx.xx
3 characters","9 character ONS code, beginning with 'E' (https://www.get-information-schools.service.gov.uk/Guidance/LaNameCodes) ",01-Mar,1 or empty,,,,,01-Feb,,,,"1 - 4, 7 - 10, 14 - 24",01-Mar,01-Mar,0 - 99999,"1, 3, 6, 9 or 10",01-Apr,0 - 1,01-Oct,xxxx.xx,,,,01-Mar,xxxx.xx,"F, M or R","F, M or R","F, M or R","F, M or R","F, M or R","F, M or R","F, M or R","F, M or R"
Can be empty?,No,,Yes,No,"Yes, if letting is general needs (if field 4 = 1)","Yes, if letting is general needs (if field 4 = 1)",No,,,,,"Yes, if letting is not 'Other intermediate rent product' (if field 11 is not 6)",Yes,,No,"Yes, if letting is a renewal (if field 7 = 1)","Yes, if letting is a renewal (if field 7 = 1) or a first-time let (if field 16 = 15 - 17)","Yes, if letting is supported housing (if field 4 = 2) or if the property's postcode is not empty (if fields 23 and 24 contain full and valid entries)","Yes, if letting is supported housing (if field 4 = 2) or if property's UPRN and local authority are known (if fields 18 and 25 are not empty)",Yes,"Yes, if letting is supported housing (if field 4 = 2) or if property's UPRN and local authority are known (if fields 18 and 25 are not empty)",Yes,"Yes, if letting is supported housing (if field 4 = 2) or if property's UPRN and local authority are known (if fields 18 and 25 are not empty)",,"Yes, if letting is supported housing (if field 4 = 2)",,,,,"Yes, if letting is a renewal (if field 7 = 1)",,,Yes,,,"Yes, if letting is general needs (if field 4 = 1)",No,,,"Yes, if 'Other' is not selected for tenancy type (if field 39 is not 3)","Yes, if letting is not a fixed-term tenancy (if field 39 is not 4 or 6)",No,,,,,"Yes, if all fields about person 2 are empty (fields 47 - 50)",,,,"Yes, if all fields about person 3 are empty (fields 51 - 54)",,,,"Yes, if all fields about person 4 are empty (fields 55 - 58)",,,,"Yes, if all fields about person 5 are empty (fields 59 - 62)",,,,"Yes, if all fields about person 6 are empty (fields 63 - 66)",,,,"Yes, if all fields about person 7 are empty (fields 67 - 70)",,,,"Yes, if all fields about person 8 are empty (fields 71 - 74)",,,,No,"Yes, if no one in the household is a current or former regular (if field 75 is not 1)","Yes, if no one in the household is a current or former regular or reserve (if field 75 is not 1 or 4)",No,"Yes, if no household members have access needs or if it is unknown (if field 83 or 84 = 1)",,,,"Yes, if a household member has an access need (if at least one of fields 79 to 82 = 1)",,No,"Yes, if a household member has an access need (if at least one of fields 79 to 82 = 1)
If someone in the household does have such a condition (if field 89 = 1), then at least 1 of these fields must be 1.",,,,,,,,,,No,"Yes, if letting is a renewal (if field 7 = 1)",No,"Yes, if 'Other' is not selected for reason for leaving last settled home (if field 98 is not 20)",No,No,,"Yes, if postcode of household's last settled home is not known (if 102 = 2)",,Yes,No,"If household was given 'reasonable preference' (if field 107 = 1), at least one of these fields must be 1
If household was not given 'reasonable preference' (if field 106 = 2 or 3), these fields will be ignored.",,,,,No,,,,"Yes, if letting is a renewal (if field 7 = 1)",No,"Yes, if household's income is unknown (if field 117 = 2 or 3)",,No,,"Yes, if letting is supported housing (if field 4 = 2)",No,"Yes, if the household does not pay rent (if field 122 = 1)",,,,"Yes, if the household doesn't receive housing benefits, or if it is unknown (if field 120 = 3, 9 or 10)","Yes, if the household does not need to pay rent or charges after receiving housing benefits (if field 128 is not 1)"
Type of letting the question applies to,,,,,Supported housing only,,,,,,,Other Intermediate Rent only,,,,,,General needs only,,,,,,,,,,,,,,,,,,Supported housing only,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Supported housing only,,,,,,,
Duplicate check field?,Yes,,,,Yes,,,Yes,,,,,Yes,,,,,,,,,,Yes,,,,,,,,,,,,,,,,,,,Yes,,,,Yes,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Field number,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129
,ORG1,ORG1,support@example.com,1,,,2,1,4,26,1,,1,1,1,5,1,,a,a,a,a,a1,1aa,E09000001,1,1,1,1,1,4,25,,,,,3,1,2,,,20,F,1,GBR,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,6,3,1,1,,,,,,1,1,,,,,,,,,,1,2,50,,30,1,2,,,,1,1,,,,,1,1,1,1,7,2,,,1,2,1,1,50,0,0,0,3,
If household was not given 'reasonable preference' (if field 106 = 2 or 3), these fields will be ignored.",,,,,No,,,,"Yes, if letting is a renewal (if field 7 = 1)",No,"Yes, if household's income is unknown (if field 117 = 2 or 3)",,No,,"Yes, if letting is supported housing (if field 4 = 2)",No,"Yes, if the household does not pay rent (if field 122 = 1)",,,,"Yes, if the household doesn't receive housing benefits, or if it is unknown (if field 120 = 3, 9 or 10)","Yes, if the household does not need to pay rent or charges after receiving housing benefits (if field 128 is not 1)",,,,,,,,
Type of letting the question applies to,,,,,Supported housing only,,,,,,,Other Intermediate Rent only,,,,,,General needs only,,,,,,,,,,,,,,,,,,Supported housing only,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Supported housing only,,,,,,,,,,,,,,,
Duplicate check field?,Yes,,,,Yes,,,Yes,,,,,Yes,,,,,,,,,,Yes,,,,,,,,,,,,,,,,,,,Yes,,,,Yes,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Field number,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137
,ORG1,ORG1,support@example.com,1,,,2,1,4,26,1,,1,1,1,5,1,,a,a,a,a,a1,1aa,E09000001,1,1,1,1,1,4,25,,,,,3,1,2,,,20,F,1,GBR,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,6,3,1,1,,,,,,1,1,,,,,,,,,,1,2,50,,30,1,2,,,,1,1,,,,,1,1,1,1,7,2,,,1,2,1,1,50,0,0,0,3,,F,,,,,,,

1 Section Setting up this lettings log Property information Tenancy information Household characteristics Household needs Household situation Income, benefits and outgoings
2 Question Which organisation owns this property? Which organisation manages this letting? What is the CORE username of the account this letting log should be assigned to? What is the needs type? What scheme does this letting belong to? Which location is this letting for? Is this letting a renewal of social housing to the same tenant in the same property? What is the tenancy start date? - day DD What is the tenancy start date? - month MM What is the tenancy start date? - year YY What is the rent type? Which 'Other' type of Intermediate Rent is this letting? What is the tenant code? What is the property reference? Has the tenant seen or been given access to the MHCLG privacy notice? What is the reason for the property being vacant? What type was the property most recently let as? If known, provide this property’s UPRN Address Line 1 Address Line 2 Town or city County Part 1 of the property's postcode Part 2 of the property's postcode What is the property's local authority? What type of unit is the property? Which type of building is the property? Is the property built or adapted to wheelchair-user standards? How many bedrooms does the property have? What is the void date? - day DD What is the void date? - month MM What is the void date? - year YY What date were any major repairs completed on? - day DD What date were any major repairs completed on? - month MM What date were any major repairs completed on? - year YY Is this property older people's housing? Is this a joint tenancy? Is this a starter tenancy? What is the type of tenancy? If 'Other', what is the type of tenancy? What is the length of the fixed-term tenancy to the nearest year? What is the lead tenant’s age? Which of these best describes the lead tenant’s gender identity? Which of these best describes the lead tenant's ethnic background? What is the lead tenant’s nationality? Which of these best describes the lead tenant’s working situation? Is person 2 the partner of the lead tenant? What is person 2's age? Which of these best describes person 2's gender identity? Which of these best describes person 2's working situation? Is person 3 the partner of the lead tenant? What is person 3's age? Which of these best describes person 3's gender identity? Which of these best describes person 3's working situation? Is person 4 the partner of the lead tenant? What is person 4's age? Which of these best describes person 4's gender identity? Which of these best describes person 4's working situation? Is person 5 the partner of the lead tenant? What is person 5's age? Which of these best describes person 5's gender identity? Which of these best describes person 5's working situation? Is person 6 the partner of the lead tenant? What is person 6's age? Which of these best describes person 6's gender identity? Which of these best describes person 6's working situation? Is person 7 the partner of the lead tenant? What is person 7's age? Which of these best describes person 7's gender identity? Which of these best describes person 7's working situation? Is person 8 the partner of the lead tenant? What is person 8's age? Which of these best describes person 8's gender identity? Which of these best describes person 8's working situation? Does anybody in the household have links to the UK armed forces? Is this person still serving in the UK armed forces? Was this person seriously injured or ill as a result of serving in the UK armed forces? Is anybody in the household pregnant? Disabled access needs a) Fully wheelchair-accessible housing Disabled access needs a) Fully wheelchair-accessible housing Disabled access needs b) Wheelchair access to essential rooms Disabled access needs b) Wheelchair access to essential rooms Disabled access needs c) Level access housing Disabled access needs c) Level access housing Disabled access needs f) Other disabled access needs Disabled access needs f) Other disabled access needs Disabled access needs g) No disabled access needs Disabled access needs g) No disabled access needs Disabled access needs h) Don’t know Disabled access needs h) Don’t know Does anybody in the household have a physical or mental health condition (or other illness) expected to last 12 months or more? Does this person's condition affect their dexterity? Does this person's condition affect their learning or understanding or concentrating? Does this person's condition affect their hearing? Does this person's condition affect their memory? Does this person's condition affect their mental health? Does this person's condition affect their mobility? Does this person's condition affect them socially or behaviourally? Does this person's condition affect their stamina or breathing or fatigue? Does this person's condition affect their vision? Does this person's condition affect them in another way? How long has the household continuously lived in the local authority area of the new letting? How long has the household been on the local authority housing register (or waiting list) for the area of the new letting? What is the tenant’s main reason for the household leaving their last settled home? If 'Other', what was the main reason for leaving their last settled home? Where was the household immediately before this letting? Did the household experience homelessness immediately before this letting? Do you know the postcode of the household's last settled home? Part 1 of postcode of last settled home Part 2 of postcode of last settled home What is the local authority of the household's last settled home? Was the household given 'reasonable preference' by the local authority? Reasonable preference reason They were homeless or about to lose their home (within 56 days) Reasonable preference reason They were homeless or about to lose their home (within 56 days) Reasonable preference reason They were living in unsanitary, overcrowded or unsatisfactory housing Reasonable preference reason They were living in unsanitary, overcrowded or unsatisfactory housing Reasonable preference reason They needed to move due to medical and welfare reasons (including disability) Reasonable preference reason They needed to move due to medical and welfare reasons (including disability) Reasonable preference reason They needed to move to avoid hardship to themselves or others Reasonable preference reason They needed to move to avoid hardship to themselves or others Reasonable preference reason Don't know Reasonable preference reason Don't know How was this letting allocated? Choice based Lettings (CBL) How was this letting allocated? Common Allocations Policy (CAP) How was this letting allocated? Common Housing Register (CHR) How was this letting allocated? Accessible Housing Register What was the source of referral for this letting? Do you know the household's combined total income after tax? How often does the household receive income? How much income does the household have in total? Is the tenant likely to be receiving any of these housing-related benefits? How much of the household's income is from Universal Credit, state pensions or benefits? Does the household pay rent or other charges for the accommodation? How often does the household pay rent and other charges? What is the basic rent? What is the service charge? What is the personal service charge? What is the support charge? After the household has received any housing-related benefits, will they still need to pay for rent and charges? What do you expect the outstanding amount to be? What was the lead tenant's sex at birth? What was person 2's sex registered at birth? What was person 3's sex registered at birth? What was person 4's sex registered at birth? What was person 5's sex registered at birth? What was person 6's sex registered at birth? What was person 7's sex registered at birth? What was person 8's sex registered at birth?
3 Additional info You can find the org ID on the CORE service under 'Stock owners' or, if your organisation is the stock owner, under 'About your organisation' You can find the org ID on the CORE service under 'Managing agents' or, if your organisation is the managing agent, under 'About your organisation' If left empty, the letting log will be assigned to the account used to upload the log. General needs housing includes both self-contained and shared housing without support or specific adaptations. Supported housing includes direct access hostels, group homes, residential care and nursing homes. Scheme code. Include the 'S' at the beginning if it has one. You can find the scheme code on the CORE service under 'Schemes', either by searching for the specific scheme or downloading a csv. Location code. You can find the location code on the CORE service under 'Schemes', either by searching for the specific location or downloading a csv. If the property was previously being used as temporary accommodation, then answer 'no'. See specification for definitions This is how you usually refer to this tenancy on your own systems. This is how you usually refer to this property on your own systems. Make sure the lead tenant has seen or been given access to the Ministry of Housing, Communities and Local Government (MHCLG) privacy notice before completing this log. This is a legal requirement under data protection legislation. Internal transfer - Where a tenant moved from one social housing property to another property. Their landlord may be the same or may have changed. Renewal of a fixed term tenancy - to the same tenant in the same property, except if was previously used as temporary accommodation. This is the rent type of the previous tenancy in this property. The Unique Property Reference Number (UPRN) is a unique number system created by Ordnance Survey and used by housing providers and various industries across the UK. An example UPRN is 10010457355. The UPRN may not be the same as the property reference assigned by your organisation. The Unique Property Reference Number (UPRN) is a unique number system created by Ordnance Survey and used by housing providers and various industries across the UK. An example UPRN is 10010457355. The UPRN may not be the same as the property reference assigned by your organisation. Combined with field 22 it should be a postcode which lies within the local authority given in field 25. Combined with field 21 it should be a postcode which lies within the local authority given in field 25. This is whether someone who uses a wheelchair is able to make full use of all of the property’s rooms and facilities, including use of both inside and outside space, and entering and exiting the property. If shared accommodation, enter the number of bedrooms occupied by this household. A bedsit has 1 bedroom. Date the property was (legally/contractually) available to let, or for: - re-lets: the day after previous tenant’s contract end - new-builds: the day the landlord legally owned the property ('completion date’) - new conversions or acquisitions: the completion date, or the day after any rehabilitation work ended - new leases: the day the landlord got contractual property rights, and could let it out to tenants. Major repairs are works that could not be reasonably carried out with a tenant living at the property. For example, structural repairs. This includes retirement living, sheltered housing and extra care housing. There is no national set limit for “older people”, please answer based on your own policies. Extra care housing is for tenants with medium to high care and support needs, often with 24 hour access to support staff provided by an agency registered with the Care Quality Commission. This is where two or more people are named on the tenancy agreement. If the tenancy has an ‘introductory period’ answer ‘yes’. You should submit a CORE log at the beginning of the starter tenancy or introductory period, with the best information you have at the time. You do not need to submit a log when a tenant later rolls onto the main tenancy. This is about the main tenancy after any starter or introductory period. See specification for definitions. Do not include the starter or introductory period. The minimum period is 2 years for social or affordable rent general needs logs. You do not need to submit CORE logs for these types of tenancies if they are shorter than 2 years. This is the household member who does the most paid work. If several people do the same amount of paid work, it's the oldest household member. This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth. If the lead tenant is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, the tenant should decide which country to enter. This is the household member who does the most paid work. If several people do the same amount of paid work, it's the oldest household member. Answer 1 for children aged under 1 year old This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth. Answer 1 for children aged under 1 year old This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth. Answer 1 for children aged under 1 year old This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth. Answer 1 for children aged under 1 year old This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth. Answer 1 for children aged under 1 year old This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth. Answer 1 for children aged under 1 year old This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth. Answer 1 for children aged under 1 year old This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth. This excludes national service. If several household members have these links, answer for regular first. If no regular, answer for reserve. If no reserve, answer for spouses or civil partners. For example, lifting and carrying objects, or using a keyboard For example, deafness or partial hearing For example, depression or anxiety For example, walking short distances or climbing stairs For example, anything associated with autism spectrum disorder (ASD), including Asperger’s or attention deficit hyperactivity disorder (ADHD) For example, blindness or partial sight The tenant's ‘last settled home' is their last long-standing home. For tenants who had temporary accommodation, sleeping rough or otherwise homeless, their last settled home is where they were living previously. This is the tenant’s last long-standing home. It is where the tenant was living before any period in temporary accommodation, sleeping rough or otherwise homeless. Combined with field 104, it should be a postcode which lies within the local authority given in field 105. Combined with field 103, it should be a postcode which lies within the local authority given in field 105. This is the tenant’s last long-standing home. It is where the tenant was living before any period in temporary accommodation, sleeping rough or otherwise homeless. Households may be given ‘reasonable preference’ for social housing under one or more specific category by the local authority. This is also known as ‘priority need’. Where available vacant properties are advertised and applicants are able to bid for specific properties. Where a common system agreed between a group of housing providers is used to determine applicants' priority for housing. Where a single waiting list is used by a group of housing providers to receive and process housing applications. Providers may use different approaches to determine priority. Where the 'access category' or another descriptor of whether an available vacant property meets a range of access needs is displayed to applicants during the allocations process. Include any income after tax from employment, pensions, and Universal Credit. Don't include National Insurance (NI) contributions and tax, housing benefit, child benefit, or council tax support. This is about when the tenant is in their new let. If they are unsure about the situation for their new let and their financial and working situation hasn’t changed significantly, answer based on what housing-related benefits they currently receive. If rent is charged on the property then answer Yes, even if tenants do not pay it themselves. This is the amount paid before any charges are added for services (for example, hot water or cleaning). Households may receive housing benefit or Universal Credit towards basic rent. For example, cleaning. Households may get household benefits towards the service charge. For example heating or hot water. This doesn’t include housing benefit or Universal Credit. Any charges made to fund support services included in the tenancy agreement. Also known as the 'outstanding amount' You only need to give an approximate figure. This is the sex that was registered at birth. This is the sex that was registered at birth. This is the sex that was registered at birth. This is the sex that was registered at birth. This is the sex that was registered at birth. This is the sex that was registered at birth. This is the sex that was registered at birth. This is the sex that was registered at birth.
4 Values Alphanumeric Email format 01-Feb Alphanumeric Numeric 01-Feb Jan-31 01-Dec 25 - 26 01-Jul Text Alphanumeric, max 13 characters Alphanumeric, max 12 characters 1 5 - 6, or 8 - 22 1 - 3 or 5 - 9 Numeric Alphanumeric Text Alphanumeric, 2 - 4 characters Alphanumeric, 3 characters 9 character ONS code, beginning with 'E' (https://www.get-information-schools.service.gov.uk/Guidance/LaNameCodes) 1 - 2, 4 or 6 - 10 01-Feb 01-Feb 01-Jul Jan-31 01-Dec Jun-26 Jan-31 01-Dec Jun-26 2 - 4, 7 - 8 01-Mar 01-Feb 02-Aug Text 1 - 99, see specification for more detail 16 - 120 or R F, M, X or R Jan-20 3 digit ISO country code, see specification 0 - 10 01-Mar Numeric, range 1 - 120 or text (upper case 'R') Must be >= 16 if working situation = 1 - 8 or 0 Must be <16 if working situation = 9 F, M, X or R 0 - 10 Must be 9 if age <16 01-Mar Numeric, range 1 - 120 or text (upper case 'R') Must be >= 16 if working situation = 1 - 8 or 0 Must be <16 if working situation = 9 F, M, X or R 0 - 10 Must be 9 if age <16 01-Mar Numeric, range 1 - 120 or text (upper case 'R') Must be >= 16 if working situation = 1 - 8 or 0 Must be <16 if working situation = 9 F, M, X or R 0 - 10 Must be 9 if age <16 01-Mar Numeric, range 1 - 120 or text (upper case 'R') Must be >= 16 if working situation = 1 - 8 or 0 Must be <16 if working situation = 9 F, M, X or R 0 - 10 Must be 9 if age <16 01-Mar Numeric, range 1 - 120 or text (upper case 'R') Must be >= 16 if working situation = 1 - 8 or 0 Must be <16 if working situation = 9 F, M, X or R 0 - 10 Must be 9 if age <16 01-Mar Numeric, range 1 - 120 or text (upper case 'R') Must be >= 16 if working situation = 1 - 8 or 0 Must be <16 if working situation = 9 F, M, X or R 0 - 10 Must be 9 if age <16 01-Mar Numeric, range 1 - 120 or text (upper case 'R') Must be >= 16 if working situation = 1 - 8 or 0 Must be <16 if working situation = 9 F, M, X or R 0 - 10 Must be 9 if age <16 01-Jun 03-Jun 01-Mar 1 or empty 01-Mar 1 or empty 1 - 2 or 6 - 12 2 or 6 - 13 1 - 2, 4, 8 - 14, 16 - 20, 28 - 31, 34 or 44 - 55 Text 3 - 4, 6 - 7, 9 - 10, 13 - 14, 18 - 19, 21, 23 - 33, 35, 37 - 39 1 or 11 01-Feb Alphanumeric, 2 - 4 characters Alphanumeric, 3 characters 9 character ONS code, beginning with 'E' (https://www.get-information-schools.service.gov.uk/Guidance/LaNameCodes) 01-Mar 1 or empty 01-Feb 1 - 4, 7 - 10, 14 - 24 01-Mar 01-Mar 0 - 99999 1, 3, 6, 9 or 10 01-Apr 0 - 1 01-Oct xxxx.xx 01-Mar xxxx.xx F, M or R F, M or R F, M or R F, M or R F, M or R F, M or R F, M or R F, M or R
5 Can be empty? No Yes No Yes, if letting is general needs (if field 4 = 1) Yes, if letting is general needs (if field 4 = 1) No Yes, if letting is not 'Other intermediate rent product' (if field 11 is not 6) Yes No Yes, if letting is a renewal (if field 7 = 1) Yes, if letting is a renewal (if field 7 = 1) or a first-time let (if field 16 = 15 - 17) Yes, if letting is supported housing (if field 4 = 2) or if the property's postcode is not empty (if fields 23 and 24 contain full and valid entries) Yes, if letting is supported housing (if field 4 = 2) or if property's UPRN and local authority are known (if fields 18 and 25 are not empty) Yes Yes, if letting is supported housing (if field 4 = 2) or if property's UPRN and local authority are known (if fields 18 and 25 are not empty) Yes Yes, if letting is supported housing (if field 4 = 2) or if property's UPRN and local authority are known (if fields 18 and 25 are not empty) Yes, if letting is supported housing (if field 4 = 2) Yes, if letting is a renewal (if field 7 = 1) Yes Yes, if letting is general needs (if field 4 = 1) No Yes, if 'Other' is not selected for tenancy type (if field 39 is not 3) Yes, if letting is not a fixed-term tenancy (if field 39 is not 4 or 6) No Yes, if all fields about person 2 are empty (fields 47 - 50) Yes, if all fields about person 3 are empty (fields 51 - 54) Yes, if all fields about person 4 are empty (fields 55 - 58) Yes, if all fields about person 5 are empty (fields 59 - 62) Yes, if all fields about person 6 are empty (fields 63 - 66) Yes, if all fields about person 7 are empty (fields 67 - 70) Yes, if all fields about person 8 are empty (fields 71 - 74) No Yes, if no one in the household is a current or former regular (if field 75 is not 1) Yes, if no one in the household is a current or former regular or reserve (if field 75 is not 1 or 4) No Yes, if no household members have access needs or if it is unknown (if field 83 or 84 = 1) Yes, if a household member has an access need (if at least one of fields 79 to 82 = 1) No Yes, if a household member has an access need (if at least one of fields 79 to 82 = 1) If someone in the household does have such a condition (if field 89 = 1), then at least 1 of these fields must be 1. No Yes, if letting is a renewal (if field 7 = 1) No Yes, if 'Other' is not selected for reason for leaving last settled home (if field 98 is not 20) No No Yes, if postcode of household's last settled home is not known (if 102 = 2) Yes No If household was given 'reasonable preference' (if field 107 = 1), at least one of these fields must be 1 If household was not given 'reasonable preference' (if field 106 = 2 or 3), these fields will be ignored. No Yes, if letting is a renewal (if field 7 = 1) No Yes, if household's income is unknown (if field 117 = 2 or 3) No Yes, if letting is supported housing (if field 4 = 2) No Yes, if the household does not pay rent (if field 122 = 1) Yes, if the household doesn't receive housing benefits, or if it is unknown (if field 120 = 3, 9 or 10) Yes, if the household does not need to pay rent or charges after receiving housing benefits (if field 128 is not 1)
6 Type of letting the question applies to Supported housing only Other Intermediate Rent only General needs only Supported housing only Supported housing only
7 Duplicate check field? Yes Yes Yes Yes Yes Yes Yes
8 Field number 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
9 ORG1 ORG1 support@example.com 1 2 1 4 26 1 1 1 1 5 1 a a a a a1 1aa E09000001 1 1 1 1 1 4 25 3 1 2 20 F 1 GBR 1 1 6 3 1 1 1 1 1 2 50 30 1 2 1 1 1 1 1 1 7 2 1 2 1 1 50 0 0 0 3 F
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
29
30
31
32
33
34
35
36
37
38
39
40
41
42
45
46
47
48
49
50
51
77
78
79
80
81
82
83
84
85
86
87

20
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/lettings_log_csv_export_codes_26.csv vendored

File diff suppressed because one or more lines are too long

6
spec/fixtures/files/lettings_log_csv_export_labels_26.csv vendored

File diff suppressed because one or more lines are too long

6
spec/fixtures/files/lettings_log_csv_export_non_support_codes_26.csv vendored

File diff suppressed because one or more lines are too long

6
spec/fixtures/files/lettings_log_csv_export_non_support_labels_26.csv vendored

File diff suppressed because one or more lines are too long

227
spec/fixtures/variable_definitions/lettings_download_26_27.csv vendored

@ -1 +1,228 @@
id,Log ID
status,Status of log
duplicate_set_id,ID of a set of duplicate logs
created_by,User the log is created by
is_dpo,Is the user in the assigned_to column the data protection officer?
created_at,Time and date the log was created
updated_by,User who last updated the log
updated_at,Time and date the log was last updated
creation_method,Was the log submitted in-service or via bulk upload?
collection_start_year,Year collection period opened
address_line1_as_entered,Address line 1 entered in bulk upload file
address_line2_as_entered,Address line 2 entered in bulk upload file
town_or_city_as_entered,Town or city entered in bulk upload file
county_as_entered,County entered in bulk upload file
postcode_full_as_entered,Postcode entered in bulk upload file
la_as_entered,Local authority entered in bulk upload file
bulk_upload_id,ID of a set of bulk uploaded logs
assigned_to,User the log is assigned to
owning_organisation_name,Which organisation owns this property?
managing_organisation_name,Which organisation manages this letting?
needstype,What is the needs type?
lettype,What is the letting type?
renewal,Is this letting a renewal?
startdate,What is the tenancy start date?
renttype,What is the rent type? (grouped into SR, IR or AR)
renttype_detail,What is the rent type?
irproduct,Which type of Intermediate Rent is this letting?
irproduct_other,Which 'Other' type of Intermediate Rent is this letting?
lar,Is this a London Affordable Rent letting?
tenancycode,What is the tenant code?
propcode,What is the property reference?
uprn_known,Is the UPRN known?
uprn,If known, property's UPRN
address_line1_input,Address line 1 input from address matching feature
postcode_full_input,Postcode input from address matching feature
address_search_value_check,Was the 'No address found' page seen?
uprn_selection,UPRN of the address selected
address_line1,Address line 1
address_line2,Address line 2
town_or_city,Town or City
county,County
postcode_full,Postcode
is_la_inferred,The internal value to indicate if the LA was inferred from the postcode
la_label,What is the property's local authority?
la,Local authority code
first_time_property_let_as_social_housing,Is this the first time the property has been let as social housing?
unitletas,What rent product was the property most recently let as?
rsnvac,What is the reason for the property being vacant?
newprop,Is this property new to the social rented sector?
unittype_gn,What type of unit is the property?
wchair,Is the property built or adapted to wheelchair-user standards?
beds,How many bedrooms does the property have?
voiddate,What is the void date?
vacdays,Number of days the property was vacant
void_date_value_check,The following soft validation was confirmed: You told us that the property has been vacant for more than 2 years. This is higher than we would expect.
majorrepairs,Were any major repairs carried out during the void period?
mrcdate,What date were any major repairs completed on?
major_repairs_date_value_check,The following soft validation was confirmed: You told us the property has been vacant for 2 years. This is higher than we would expect.
joint,Is this a joint tenancy?
startertenancy,Is this a starter tenancy?
tenancy,What is the type of tenancy?
tenancyother,If 'Other', what is the type of tenancy?
tenancylength,What is the length of the fixed-term tenancy to the nearest year?
sheltered,Is this letting in sheltered accommodation?
declaration,Has the tenant seen the MHCLG privacy notice?
hhmemb,How many people live in the household at this letting?
pregnancy_value_check,The following soft validation was confirmed: You told us somebody in the household is pregnant. You also told us there are no female tenants living at the property.
refused,Where household characteristics have a 'Refused' option for some or all of: AGE1-AGE8, SEX1-SEX8, RELAT2-RELAT8, ECSTAT1-ECSTAT8
hhtype,Type of household 1 = 1 elder; 2 = 2 adults, including elder(s); 3 = 1 adult; 4 = 2 adults; 5 = 1 adult & 1+ children; 6 = 2+ adults & 1+ children; 9 = Other
totchild,Total number of dependent children in the household (Sum of when RELAT2-8 = C)
totelder,Total number of elders in household (Sum of when AGE1-8 >= 60)
totadult,Total number of adults in household
age1,What is the lead tenant's age?
retirement_value_check,The following soft validation was confirmed: You told us this person is aged %{age} years and retired. The minimum expected retirement age for %{gender} in England is %{age}.
sex1,Which of these best describes the lead tenant's gender identity?
ethnic_group,What is the lead tenant's ethnic group?
ethnic,Which of these best describes the lead tenant's ethnic background?
nationality_all,What is the lead tenant's nationality?
ecstat1,Which of these best describes the lead tenant's working situation?
details_known_2,Are the details of tenant 2 known?
relat2,What is person 2's relationship to the lead tenant?
partner_under_16_value_check,The following soft validation was confirmed: You said that [person X]'s relationship to lead tenant is partner, and that their age is [AGEX]. Are you sure this is correct?
multiple_partners_value_check,The following soft validation was confirmed: You said that more than one person in the household is the partner of the lead tenant. Are you sure this is correct?
age2,What is person 2's age?
sex2,Which of these best describes person 2's gender identity?
ecstat2,Which of these best describes person 2's working situation?
details_known_3,Are the details of tenant 3 known?
relat3,What is person 3's relationship to the lead tenant?
age3,What is person 3's age?
sex3,Which of these best describes person 3's gender identity?
ecstat3,Which of these best describes person 3's working situation?
details_known_4,Are the details of tenant 4 known?
relat4,What is person 4's relationship to the lead tenant?
age4,What is person 4's age?
sex4,Which of these best describes person 4's gender identity?
ecstat4,Which of these best describes person 4's working situation?
details_known_5,Are the details of tenant 5 known?
relat5,What is person 5's relationship to the lead tenant?
age5,What is person 5's age?
sex5,Which of these best describes person 5's gender identity?
ecstat5,Which of these best describes person 5's working situation?
details_known_6,Are the details of tenant 6 known?
relat6,What is person 6's relationship to the lead tenant?
age6,What is person 6's age?
sex6,Which of these best describes person 6's gender identity?
ecstat6,Which of these best describes person 6's working situation?
details_known_7,Are the details of tenant 7 known?
relat7,What is person 7's relationship to the lead tenant?
age7,What is person 7's age?
sex7,Which of these best describes person 7's gender identity?
ecstat7,Which of these best describes person 7's working situation?
details_known_8,Are the details of tenant 8 known?
relat8,What is person 8's relationship to the lead tenant?
age8,What is person 8's age?
sex8,Which of these best describes person 8's gender identity?
ecstat8,Which of these best describes person 8's working situation?
armedforces,Does anybody in the household have links to the UK armed forces?
leftreg,Is this person still serving in the UK armed forces?
reservist,Was this person seriously injured or ill as a result of serving in the UK armed forces?
preg_occ,Is anybody in the household pregnant?
housingneeds,Does anybody in the household have any disabled access needs?
housingneeds_type,What access needs do they have? (Fully wheelchair-accessible housing, Level access housing or Wheelchair access to essential rooms)
housingneeds_a,Disabled access needs a) Fully wheelchair-accessible housing
housingneeds_b,Disabled access needs b) Wheelchair access to essential rooms
housingneeds_c,Disabled access needs c) Level access housing
housingneeds_f,Disabled access needs f) Other disabled access needs
housingneeds_g,Disabled access needs g) No disabled access needs
housingneeds_h,Disabled access needs h) Don't know
housingneeds_other,Do they have any other disabled access needs?
illness,Does anybody in the household have a physical or mental health condition (or other illness) expected to last 12 months or more?
illness_type_4,Does this person's condition affect their dexterity?
illness_type_5,Does this person's condition affect their learning or understanding or concentrating?
illness_type_2,Does this person's condition affect their hearing?
illness_type_6,Does this person's condition affect their memory?
illness_type_7,Does this person's condition affect their mental health?
illness_type_3,Does this person's condition affect their mobility?
illness_type_9,Does this person's condition affect them socially or behaviourally?
illness_type_8,Does this person's condition affect their stamina or breathing or fatigue?
illness_type_1,Does this person's condition affect their vision?
illness_type_10,Does this person's condition affect them in another way?
layear,How long has the household continuously lived in the local authority area of the new letting?
waityear,How long has the household been on the local authority waiting list for the new letting?
reason,What is the tenant's main reason for the household leaving their last settled home?
reasonother,If 'Other', what was the main reason for leaving their last settled home?
reasonother_value_check,The soft validation was confirmed
prevten,Where was the household immediately before this letting?
homeless,Did the household experience homelessness immediately before this letting?
ppcodenk,Previous postcode unknown or previous accommodation was temporary
ppostcode_full,What is the postcode of the household's last settled home?
previous_la_known,Was the local authority of the household's last settled home known?
is_previous_la_inferred,The internal value to indicate if the previous LA was inferred from the postcode
prevloc_label,Previous location LA name
prevloc,Previous location's ONS LA Code
reasonpref,Was the household given reasonable preference by the local authority?
rp_homeless,Reasonable preference reason - They were homeless or about to lose their home (within 56 days)
rp_insan_unsat,Reasonable preference reason - They were living in insanitary, overcrowded or unisatisfactory housing
rp_medwel,Reasonable preference reason - They needed to move on medical and welfare reasons (including disability)
rp_hardship,Reasonable preference reason - They needed to move to avoid hardship to themselves or others
rp_dontknow,Reasonable preference reason - Don't Know
cbl,Was the letting made under Choice-Based Lettings (CBL)?
cap,Was the letting made under the Common Allocation Policy (CAP)?
chr,Was the letting made under the Common Housing Register (CHR)?
accessible_register,Was the letting made under the Accessible Register?
letting_allocation_none,The letting was not allocated under CBL, CAP, CHR or Accessible Register.
referral,What was the source of referral for this letting?
referral_value_check,The following soft validation was confirmed: Are you sure? This is a general needs log, and this referral type is for supported housing.
net_income_known,Do you know the household's combined income after tax?
incref,Was the household income refused?
earnings,How much income does the household have in total?
incfreq,How often does the household receive income?
net_income_value_check,Populated when someone hits the soft validation and confirmed in the service
hb,Is the tenant likely to be receiving any of these housing-related benefits?
has_benefits,Does the tenant receive housing-related benefits? Yes if hb = Universal Credit housing element or Housing benefit, No if hb = Don't Know, Neither, Tenant prefers not to say or blank
benefits,How much of the household's income is from Universal Credit, state pensions or benefits?
household_charge,Does the household pay rent or other charges for the accommodation?
nocharge,Does the household pay rent or other charges for the accommodation? - flag for when household_charge is answered no
period,How often does the household pay rent and other charges?
is_carehome,Is this accommodation a care home?
chcharge,If this is a care home, how much does the household pay every [time period]?
wchchrg,Weekly care home charge
carehome_charges_value_check,Populated when the soft validation and confirmed in the service
brent,What is the basic rent?
wrent,Weekly rent
rent_value_check,Populated when the soft validation and confirmed in the service
scharge,What is the service charge?
wscharge,Weekly service charge
pscharge,What is the personal service charge?
wpschrge,Weekly personal service charge
supcharg,What is the support charge?
wsupchrg,Weekly support charge
tcharge,Total charge to the tenant
wtcharge,Weekly total charge to the tenant
scharge_value_check,Populated when the soft validation and confirmed in the service
pscharge_value_check,Populated when the soft validation and confirmed in the service
supcharg_value_check,Populated when the soft validation and confirmed in the service
hbrentshortfall,After the household has received any housing-related benefits, will they still need to pay for rent and charges?
tshortfall_known,Can you estimate the outstanding amount?
tshortfall,Estimated outstanding amount
wtshortfall,Weekly total rent shortfall charge for tenant receiving housing benefit
scheme_code,What scheme does this letting belong to?
scheme_service_name,From scheme code, we map to the scheme name
scheme_confidential,Does the scheme contain confidential information?
SCHTYPE,What is this type of scheme? (Direct access hostel), Foyer, Housing for older people or Other supported housing
scheme_registered_under_care_act,Is this scheme registered under the Care Standards Act 2000?
scheme_owning_organisation_name,Which organisation owns the housing stock for this scheme?
scheme_primary_client_group,What client group is this scheme intended for?
scheme_has_other_client_group,Does this scheme provide for another client group?
scheme_secondary_client_group,What is the other client group?
scheme_support_type,What support does this scheme provide?
scheme_intended_stay,Intended length of stay
scheme_created_at,Date scheme was created
location_code,Which location is this letting for?
location_postcode,What is the postcode for this location?
location_name,What is the name of this location?
location_units,How many units are at this location?
location_type_of_unit,What is the most common type of unit at this location?
location_mobility_type,What are the mobility standards for the majority of the units in this location?
location_local_authority,What is the local authority of this postcode?
location_startdate,When did the first property in this location become available under this scheme?
working_situation_illness_check,The following soft validation was confirmed: You have said that at least one person's situation is 'Unable to work because of long-term sickness or disability'.
sexrab1,What was the lead tenant's sex at birth?
sexrab2,What was person 2's sex at birth?
sexrab3,What was person 3's sex at birth?
sexrab4,What was person 4's sex at birth?
sexrab5,What was person 5's sex at birth?
sexrab6,What was person 6's sex at birth?
sexrab7,What was person 7's sex at birth?
sexrab8,What was person 8's sex at birth?

Can't render this file because it has a wrong number of fields in line 25.

287
spec/fixtures/variable_definitions/sales_download_26_27.csv vendored

@ -0,0 +1,287 @@
ID,Log ID
STATUS,Status of log
DUPLICATESET,ID of a set of duplicate logs
CREATEDDATE,Time and date the log was created
UPLOADDATE,Time and date the log was last updated
COLLECTIONYEAR,Year collection period opened
CREATIONMETHOD,Was the log submitted in-service or via bulk upload?
BULKUPLOADID,ID of a set of bulk uploaded logs
DATAPROTECT,Is the user in the created_by column the data protection officer?
OWNINGORGNAME,Which organisation owned this property before the sale?
MANINGORGNAME,Which organisation reported the sale?
CREATEDBY,User that created the log
USERNAME,User the log is assigned to
DAY,Day of sale completion date
MONTH,Month of sale completion date
YEAR,Year of sale completion date
PURCHID,What is the purchaser code?
OWNERSHIP,Was this purchase made through an ownership scheme?
TYPE,What is the type of shared ownership/discounted ownership/outright sale?
OTHTYPE,If type = 'Other', what is the type of outright sale?
COMPANY,Is the buyer a company?
LIVEINBUYER,Will the buyer(s) live in the property?
JOINT,Is this a joint purchase?
JOINTMORE,Are there more than 2 joint buyers of this property?
NOINT,Did you interview the buyer to answer these questions?
PRIVACYNOTICE,Has the buyer seen the MHCLG privacy notice?
UPRN,What is the UPRN of the property?
ADDRESS1,Address line 1
ADDRESS2,Address line 2
TOWNCITY,Town/City
COUNTY,County
POSTCODE,Postcode
ISLAINFERRED,The internal value to indicate if the LA was inferred from the postcode
LANAME,LA name
LA,LA code
UPRNSELECTED,UPRN of the address selected
ADDRESS_SEARCH_VALUE_CHECK,Was the 'No address found' page seen?
ADDRESS1INPUT,Address line 1 input from address matching feature
POSTCODEINPUT,Postcode input from address matching feature
BULKADDRESS1,Address line 1 entered in bulk upload file
BULKADDRESS2,Address line 2 entered in bulk upload file
BULKTOWNCITY,Town or city entered in bulk upload file
BULKCOUNTY,County entered in bulk upload file
BULKPOSTCODE,Postcode entered in bulk upload file
BULKLA,Local authority entered in bulk upload file
BEDS,How many bedrooms does the property have?
PROPTYPE,What type of unit is the property?
BUILTYPE,Which type of building is the property?
WCHAIR,Is the property built or adapted to wheelchair-user standards?
AGE1,What is buyer 1's age?
SEX1,Which of these best describes buyer 1's gender identity?
ETHNICGROUP1,What is buyer 1's ethnic group?
ETHNIC,Which of the following best describes buyer 1's ethnic background?
NATIONALITYALL1,What is buyer 1's nationality?
ECSTAT1,Which of these best describes buyer 1's working situation?
LIVEINBUYER1,Will buyer 1 live in the property?
RELAT2,What is buyer 2 or person 2's relationship to buyer 1?
AGE2,What is buyer 2 or person 2's age?
SEX2,Which of these best describes buyer 2 or person 2's gender identity?
ETHNICGROUP2,What is buyer 2's ethnic group?
ETHNIC2,Which of the following best describes buyer 2's ethnic background?
NATIONALITYALL2,What is buyer 2's nationality?
ECSTAT2,What is buyer 2 or person 2's working situation?
LIVEINBUYER2,Will buyer 2 live in the property?
HHTYPE,Besides the buyer(s), how many other people live or will live in the property?
RELAT3,What is person 3's relationship to buyer 1?
AGE3,What is person 3's age?
SEX3,What is person 3's gender identity?
ECSTAT3,What is person 3's working situation?
RELAT4,What is person 4's relationship to buyer 1?
AGE4,What is person 4's age?
SEX4,What is person 4's gender identity?
ECSTAT4,What is person 4's working situation?
RELAT5,What is person 5's relationship to buyer 1?
AGE5,What is person 5's age?
SEX5,What is person 5's gender identity?
ECSTAT5,What is person 5's working situation?
RELAT6,What is person 6's relationship to buyer 1?
AGE6,What is person 6's age?
SEX6,What is person 6's gender identity?
ECSTAT6,What is person 6's working situation?
PREVTEN,What was buyer 1's previous tenure?
PPCODENK,Do you know the postcode of buyer 1's last settled accommodation?
PPOSTC1,Part 1 of postcode of buyer 1's last settled accommodation
PPOSTC2,Part 2 of postcode of buyer 1's last settled accommodation
PREVIOUSLAKNOWN,Do you know the local authority of buyer 1's last settled accommodation?
PREVLOC,The local authority code of buyer 1's last settled accommodation
PREVLOCNAME,The local authority name of buyer 1's last settled accommodation
PREGYRHA,Was the buyer registered with their PRP (HA)?
PREGOTHER,Was the buyer registered with another PRP (HA)?
PREGLA,Was the buyer registered with the local authority?
PREGGHB,Was the buyer registered with a Help to Buy agent?
PREGBLANK,Populated if pregyrha, pregother, pregla and pregghb are blank
BUY2LIVING,At the time of purchase, was buyer 2 living at the same address as buyer 1?
PREVTEN2,What was buyer 2's previous tenure?
HHREGRES,Have any of the buyers ever served as a regular in the UK armed forces?
HHREGRESSTILL,Is the buyer still serving in the UK armed forces?
ARMEDFORCESSPOUSE,Are any of the buyers a spouse or civil partner of a UK armed forces regular who died in service within the last 2 years?
DISABLED,Does anyone in the household consider themselves to have a disability?
WHEEL,Does anyone in the household use a wheelchair?
INC1NK,Is buyer 1's annual income known?
INCOME1,What is buyer 1's annual income?
INC1MORT,Was buyer 1's income used for a mortgage application?
INC2NK,Is buyer 1's annual income known?
INCOME2,What is buyer 2's annual income?
INC2MORT,Was buyer 2's income used for a mortgage application?
HB,Were the buyers receiving any of these housing-related benefits immediately before buying this property?
SAVINGSNK,Is the the total amount the buyers had in savings known?
SAVINGS,What is the total amount the buyers had in savings before they paid any deposit for the property?
PREVOWN,Have any of the buyers previously owned a property?
PREVSHARED,Was the previous property under shared ownership?
PROPLEN,How long did the buyer(s) live in the property before purchasing it?
STAIRCASE,Is this a staircasing transaction?
STAIRBOUGHT,What percentage of the property has been bought in this staircasing transaction?
STAIROWNED,What percentage of the property do the buyers now own in total?
STAIRCASETOSALE,Was this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?
RESALE,Is this a resale?
EXDAY,Day of the exchange of contracts
EXMONTH,Month of the exchange of contracts
EXYEAR,Year of the exchange of contracts
HODAY,Day of the practical completion or handover date
HOMONTH,Month of the practical completion or handover date
HOYEAR,Year of the practical completion or handover date
LANOMAGR,Was the household rehoused under a local authority nominations agreement?
SOCTEN,Was the buyer a private registered provider, housing association or local authority tenant immediately before this sale?
FROMBEDS,How many bedrooms did the buyer's previous property have?
FROMPROP,What was the previous property type?
SOCPREVTEN,What was the rent type of buyer's previous tenure?
VALUE,What is the full purchase price?
VALUE_VALUE_CHECK,Populated if a soft validation is confirmed.
EQUITY,What was the initial percentage equity stake purchased?
MORTGAGEUSED,Was a mortgage used to buy this property?
MORTGAGE,What is the mortgage amount?
MORTGAGELENDER,What is the name of the mortgage lender?
MORTGAGELENDEROTHER,If mortgagelender = 'Other', what is the name of the mortgage lender?
MORTLEN1,What is the length of the mortgage in years?
EXTRABOR,Does this include any extra borrowing?
DEPOSIT,How much was the cash deposit paid on the property?
CASHDIS,How much cash discount was given through Social Homebuy?
MRENT,What is the basic monthly rent?
HASMSCHARGE,Does the property have any monthly leasehold charges?
MSCHARGE,What are the total monthly leasehold charges for the property?
MSCHARGE_VALUE_CHECK,Populated if a soft validation is confirmed.
DISCOUNT,What was the percentage discount?
GRANT,What was the amount of any loan, grant, discount or subsidy given?
id,Log ID
status,Status of log
duplicate_set_id,ID of a set of duplicate logs
created_at,Time and date the log was created
updated_at,Time and date the log was last updated
old_form_id,The ID on the old service
collection_start_year,Year collection period opened
creation_method,Was the log submitted in-service or via bulk upload?
is_dpo,Is the user in the assigned_to column the data protection officer?
owning_organisation_name,Which organisation owned this property before the sale?
managing_organisation_name,Which organisation reported the sale?
assigned_to,User the log is assigned to
day,Day of sale completion date
month,Month of sale completion date
year,Year of sale completion date
purchid,What is the purchaser code?
ownershipsch,Was this purchase made through an ownership scheme?
type,What is the type of shared ownership/discounted ownership/outright sale?
othtype,If type = 'Other', what is the type of outright sale?
companybuy,Is the buyer a company?
buylivein,Will the buyer(s) live in the property?
jointpur,Is this a joint purchase?
jointmore,Are there more than 2 joint buyers of this property?
beds,How many bedrooms does the property have?
proptype,What type of unit is the property?
builtype,Which type of building is the property?
uprn,What is the UPRN of the property?
uprn_confirmed,We found an address that might be this property. Is this the property address?
address_line1_input,Address line 1 input from address matching feature
postcode_full_input,Postcode input from address matching feature
uprn_selection,UPRN of the address selected
address_line1,Address line 1
address_line2,Address line 2
town_or_city,Town/City
county,County
pcode1,Part 1 of the property's postcode
pcode2,Part 2 of the property's postcode
la,LA code
la_label,LA name
wchair,Is the property built or adapted to wheelchair-user standards?
noint,Did you interview the buyer to answer these questions?
privacynotice,Has the buyer seen the MHCLG privacy notice?
age1,What is buyer 1's age?
sex1,Which of these best describes buyer 1's gender identity?
ethnic_group,What is buyer 1's ethnic group?
ethnic,Which of the following best describes buyer 1's ethnic background?
nationality_all,What is buyer 1's nationality?
ecstat1,Which of these best describes buyer 1's working situation?
buy1livein,Will buyer 1 live in the property?
relat2,What is buyer 2 or person 2's relationship to buyer 1?
age2,What is buyer 2 or person 2's age?
sex2,Which of these best describes buyer 2 or person 2's gender identity?
ethnic_group2,What is buyer 2's ethnic group?
ethnicbuy2,Which of the following best describes buyer 2's ethnic background?
nationality_all_buyer2,What is buyer 2's nationality?
ecstat2,What is buyer 2 or person 2's working situation?
buy2livein,Will buyer 2 live in the property?
hholdcount,Besides the buyer(s), how many other people live or will live in the property?
relat3,What is person 3's relationship to buyer 1?
age3,What is person 3's age?
sex3,What is person 3's gender identity?
ecstat3,What is person 3's working situation?
relat4,What is person 4's relationship to buyer 1?
age4,What is person 4's age?
sex4,What is person 4's gender identity?
ecstat4,What is person 4's working situation?
relat5,What is person 5's relationship to buyer 1?
age5,What is person 5's age?
sex5,What is person 5's gender identity?
ecstat5,What is person 5's working situation?
relat6,What is person 6's relationship to buyer 1?
age6,What is person 6's age?
sex6,What is person 6's gender identity?
ecstat6,What is person 6's working situation?
prevten,What was buyer 1's previous tenure?
ppcodenk,Do you know the postcode of buyer 1's last settled accommodation?
ppostc1,Part 1 of postcode of buyer 1's last settled accommodation
ppostc2,Part 2 of postcode of buyer 1's last settled accommodation
previous_la_known,Do you know the local authority of buyer 1's last settled accommodation?
prevloc,The local authority code of buyer 1's last settled accommodation
prevloc_label,The local authority name of buyer 1's last settled accommodation
pregyrha,Was the buyer registered with their PRP (HA)?
pregother,Was the buyer registered with another PRP (HA)?
pregla,Was the buyer registered with the local authority?
pregghb,Was the buyer registered with a Help to Buy agent?
pregblank,Populated if pregyrha, pregother, pregla and pregghb are blank
buy2living,At the time of purchase, was buyer 2 living at the same address as buyer 1?
prevtenbuy2,What was buyer 2's previous tenure?
hhregres,Have any of the buyers ever served as a regular in the UK armed forces?
hhregresstill,Is the buyer still serving in the UK armed forces?
armedforcesspouse,Are any of the buyers a spouse or civil partner of a UK armed forces regular who died in service within the last 2 years?
disabled,Does anyone in the household consider themselves to have a disability?
wheel,Does anyone in the household use a wheelchair?
income1nk,Is buyer 1's annual income known?
income1,What is buyer 1's annual income?
inc1mort,Was buyer 1's income used for a mortgage application?
income2nk,Is buyer 2's annual income known?
income2,What is buyer 2's annual income?
inc2mort,Was buyer 2's income used for a mortgage application?
hb,Were the buyers receiving any of these housing-related benefits immediately before buying this property?
savingsnk,Is the the total amount the buyers had in savings known?
savings,What is the total amount the buyers had in savings before they paid any deposit for the property?
prevown,Have any of the buyers previously owned a property?
prevshared,Was the previous property under shared ownership?
proplen,How long did the buyer(s) live in the property before purchasing it?
staircase,Is this a staircasing transaction?
stairbought,What percentage of the property has been bought in this staircasing transaction?
stairowned,What percentage of the property do the buyers now own in total?
staircasesale,Was this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?
resale,Is this a resale?
exday,Day of the exchange of contracts
exmonth,Month of the exchange of contracts
exyear,Year of the exchange of contracts
hoday,Day of the practical completion or handover date
homonth,Month of the practical completion or handover date
hoyear,Year of the practical completion or handover date
lanomagr,Was the household rehoused under a local authority nominations agreement?
soctenant,Was the buyer a private registered provider, housing association or local authority tenant immediately before this sale?
frombeds,How many bedrooms did the buyer's previous property have?
fromprop,What was the previous property type?
socprevten,What was the rent type of buyer's previous tenure?
value,What is the full purchase price?
equity,What was the initial percentage equity stake purchased?
mortgageused,Was a mortgage used to buy this property?
mortgage,What is the mortgage amount?
mortgagelender,What is the name of the mortgage lender?
mortgagelenderother,If mortgagelender = 'Other', what is the name of the mortgage lender?
mortlen,What is the length of the mortgage in years?
extrabor,Does this include any extra borrowing?
deposit,How much was the cash deposit paid on the property?
cashdis,How much cash discount was given through Social Homebuy?
mrent,What is the basic monthly rent?
has_mscharge,Does the property have any monthly leasehold charges?
mscharge,What are the total monthly leasehold charges for the property?
discount,What was the percentage discount?
grant,What was the amount of any loan, grant, discount or subsidy given?
sexrab1,What was buyer 1's sex at birth?
sexrab2,What was buyer/person 2's sex at birth?
sexrab3,What was person 3's sex at birth?
sexrab4,What was person 4's sex at birth?
sexrab5,What was person 5's sex at birth?
sexrab6,What was person 6's sex at birth?
Can't render this file because it has a wrong number of fields in line 20.

5
spec/lib/tasks/log_variable_definitions_spec.rb

@ -6,6 +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) { 431 }
before do
Rake.application.rake_require("tasks/log_variable_definitions")
@ -14,7 +15,7 @@ RSpec.describe "log_variable_definitions" do
end
it "adds CsvVariableDefinition records from each file in the specified directory" do
expect { task.invoke(path) }.to change(CsvVariableDefinition, :count).by(417)
expect { task.invoke(path) }.to change(CsvVariableDefinition, :count).by(total_variable_definitions_count)
end
it "handles an empty directory without errors" do
@ -34,7 +35,7 @@ RSpec.describe "log_variable_definitions" do
task.invoke(path)
second_run_count = CsvVariableDefinition.count
expect(first_run_count).to eq(initial_count + 417)
expect(first_run_count).to eq(initial_count + total_variable_definitions_count)
expect(second_run_count).to eq(first_run_count)
end
end

29
spec/models/form/lettings/pages/lead_tenant_sex_registered_at_birth_spec.rb

@ -0,0 +1,29 @@
require "rails_helper"
RSpec.describe Form::Lettings::Pages::LeadTenantSexRegisteredAtBirth, 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[sexrab1])
end
it "has the correct id" do
expect(page.id).to eq("lead_tenant_sex_registered_at_birth")
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 eq([{ "declaration" => 1 }])
end
end

138
spec/models/form/lettings/pages/person_sex_registered_at_birth_spec.rb

@ -0,0 +1,138 @@
require "rails_helper"
RSpec.describe Form::Lettings::Pages::PersonSexRegisteredAtBirth, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection, person_index:) }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2026, 4, 1))) }
let(:person_index) { 1 }
let(:page_id) { "person_2_sex_registered_at_birth" }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has the correct description" do
expect(page.description).to be_nil
end
context "with person 2" do
let(:person_index) { 2 }
let(:page_id) { "person_2_sex_registered_at_birth" }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[sexrab2])
end
it "has the correct id" do
expect(page.id).to eq("person_2_sex_registered_at_birth")
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "details_known_2" => 0 }])
end
end
context "with person 3" do
let(:person_index) { 3 }
let(:page_id) { "person_3_sex_registered_at_birth" }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[sexrab3])
end
it "has the correct id" do
expect(page.id).to eq("person_3_sex_registered_at_birth")
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "details_known_3" => 0 }])
end
end
context "with person 4" do
let(:person_index) { 4 }
let(:page_id) { "person_4_sex_registered_at_birth" }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[sexrab4])
end
it "has the correct id" do
expect(page.id).to eq("person_4_sex_registered_at_birth")
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "details_known_4" => 0 }])
end
end
context "with person 5" do
let(:person_index) { 5 }
let(:page_id) { "person_5_sex_registered_at_birth" }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[sexrab5])
end
it "has the correct id" do
expect(page.id).to eq("person_5_sex_registered_at_birth")
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "details_known_5" => 0 }])
end
end
context "with person 6" do
let(:person_index) { 6 }
let(:page_id) { "person_6_sex_registered_at_birth" }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[sexrab6])
end
it "has the correct id" do
expect(page.id).to eq("person_6_sex_registered_at_birth")
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "details_known_6" => 0 }])
end
end
context "with person 7" do
let(:person_index) { 7 }
let(:page_id) { "person_7_sex_registered_at_birth" }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[sexrab7])
end
it "has the correct id" do
expect(page.id).to eq("person_7_sex_registered_at_birth")
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "details_known_7" => 0 }])
end
end
context "with person 8" do
let(:person_index) { 8 }
let(:page_id) { "person_8_sex_registered_at_birth" }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[sexrab8])
end
it "has the correct id" do
expect(page.id).to eq("person_8_sex_registered_at_birth")
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "details_known_8" => 0 }])
end
end
end

2
spec/models/form/lettings/pages/previous_housing_situation_spec.rb

@ -3,7 +3,7 @@ require "rails_helper"
RSpec.describe Form::Lettings::Pages::PreviousHousingSituation, type: :model do
subject(:page) { described_class.new(nil, nil, subsection) }
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2024, 4, 1), start_year_2025_or_later?: false)) }
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2024, 4, 1), start_year_2025_or_later?: false, start_year_2026_or_later?: false)) }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)

53
spec/models/form/lettings/questions/lead_tenant_sex_registered_at_birth_spec.rb

@ -0,0 +1,53 @@
require "rails_helper"
RSpec.describe Form::Lettings::Questions::LeadTenantSexRegisteredAtBirth, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
let(:subsection) { instance_double(Form::Subsection) }
let(:form) { instance_double(Form, start_date: Time.zone.local(2026, 4, 1)) }
before do
allow(page).to receive(:subsection).and_return(subsection)
allow(subsection).to receive(:form).and_return(form)
end
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("sexrab1")
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({
"F" => { "value" => "Female" },
"M" => { "value" => "Male" },
"divider" => { "value" => true },
"R" => { "value" => "Lead tenant prefers not to say" },
})
end
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(1)
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to be_nil
end
it "has the correct question number" do
expect(question.question_number).to eq(31)
end
end

185
spec/models/form/lettings/questions/person_sex_registered_at_birth_spec.rb

@ -0,0 +1,185 @@
require "rails_helper"
RSpec.describe Form::Lettings::Questions::PersonSexRegisteredAtBirth, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page, person_index:) }
let(:question_id) { "sexrab2" }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
let(:person_index) { 2 }
let(:subsection) { instance_double(Form::Subsection) }
let(:form) { instance_double(Form, start_date: Time.zone.local(2026, 4, 1)) }
before do
allow(page).to receive(:subsection).and_return(subsection)
allow(subsection).to receive(:form).and_return(form)
end
it "has correct page" do
expect(question.page).to eq(page)
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({
"F" => { "value" => "Female" },
"M" => { "value" => "Male" },
"divider" => { "value" => true },
"R" => { "value" => "Person prefers not to say" },
})
end
context "when person 2" do
let(:question_id) { "sexrab2" }
let(:person_index) { 2 }
it "has the correct id" do
expect(question.id).to eq("sexrab2")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(2)
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to be_nil
end
it "has the correct question number" do
expect(question.question_number).to eq(39)
end
end
context "when person 3" do
let(:question_id) { "sexrab3" }
let(:person_index) { 3 }
it "has the correct id" do
expect(question.id).to eq("sexrab3")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(3)
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to be_nil
end
it "has the correct question number" do
expect(question.question_number).to eq(44)
end
end
context "when person 4" do
let(:question_id) { "sexrab4" }
let(:person_index) { 4 }
it "has the correct id" do
expect(question.id).to eq("sexrab4")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(4)
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to be_nil
end
it "has the correct question number" do
expect(question.question_number).to eq(49)
end
end
context "when person 5" do
let(:question_id) { "sexrab5" }
let(:person_index) { 5 }
it "has the correct id" do
expect(question.id).to eq("sexrab5")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(5)
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to be_nil
end
it "has the correct question number" do
expect(question.question_number).to eq(54)
end
end
context "when person 6" do
let(:question_id) { "sexrab6" }
let(:person_index) { 6 }
it "has the correct id" do
expect(question.id).to eq("sexrab6")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(6)
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to be_nil
end
it "has the correct question number" do
expect(question.question_number).to eq(59)
end
end
context "when person 7" do
let(:question_id) { "sexrab7" }
let(:person_index) { 7 }
it "has the correct id" do
expect(question.id).to eq("sexrab7")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(7)
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to be_nil
end
it "has the correct question number" do
expect(question.question_number).to eq(64)
end
end
context "when person 8" do
let(:question_id) { "sexrab8" }
let(:person_index) { 8 }
it "has the correct id" do
expect(question.id).to eq("sexrab8")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(8)
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to be_nil
end
it "has the correct question number" do
expect(question.question_number).to eq(69)
end
end
end

52
spec/models/form/lettings/questions/previous_tenure_spec.rb

@ -1,9 +1,14 @@
require "rails_helper"
RSpec.describe Form::Lettings::Questions::PreviousTenure, type: :model do
include CollectionTimeHelper
subject(:question) { described_class.new(nil, nil, page) }
let(:form) { instance_double(Form, start_date: Time.zone.local(2023, 4, 1), start_year_2025_or_later?: false) }
let(:year) { current_collection_start_year }
let(:start_year_2025_or_later?) { false }
let(:start_year_2026_or_later?) { false }
let(:form) { instance_double(Form, start_date: collection_start_date_for_year(year), start_year_2025_or_later?: start_year_2025_or_later?, start_year_2026_or_later?: start_year_2026_or_later?) }
let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form:)) }
it "has the correct id" do
@ -22,7 +27,9 @@ RSpec.describe Form::Lettings::Questions::PreviousTenure, type: :model do
expect(question.derived?(nil)).to be false
end
context "with start year before 2025" do
context "with 2024 logs" do
let(:year) { 2024 }
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"30" => { "value" => "Fixed-term local authority general needs tenancy" },
@ -56,7 +63,45 @@ RSpec.describe Form::Lettings::Questions::PreviousTenure, type: :model do
end
context "with 2025 logs" do
let(:form) { instance_double(Form, start_date: Time.zone.local(2025, 4, 1), start_year_2025_or_later?: true) }
let(:year) { 2025 }
let(:start_year_2025_or_later?) { true }
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"30" => { "value" => "Fixed-term local authority general needs tenancy" },
"32" => { "value" => "Fixed-term private registered provider (PRP) general needs tenancy" },
"31" => { "value" => "Lifetime local authority general needs tenancy" },
"33" => { "value" => "Lifetime private registered provider (PRP) general needs tenancy" },
"35" => { "value" => "Extra care housing" },
"38" => { "value" => "Older people’s housing for tenants with low support needs" },
"6" => { "value" => "Other supported housing" },
"3" => { "value" => "Private sector tenancy" },
"27" => { "value" => "Owner occupation (low-cost home ownership)" },
"26" => { "value" => "Owner occupation (private)" },
"28" => { "value" => "Living with friends or family (long-term)" },
"39" => { "value" => "Sofa surfing (moving regularly between family or friends, no permanent bed)" },
"14" => { "value" => "Bed and breakfast" },
"7" => { "value" => "Direct access hostel" },
"10" => { "value" => "Hospital" },
"29" => { "value" => "Prison or approved probation hostel" },
"19" => { "value" => "Rough sleeping" },
"18" => { "value" => "Any other temporary accommodation" },
"13" => { "value" => "Children’s home or foster care" },
"24" => { "value" => "Home Office Asylum Support" },
"23" => { "value" => "Mobile home or caravan" },
"21" => { "value" => "Refuge" },
"9" => { "value" => "Residential care home" },
"4" => { "value" => "Tied housing or rented with job" },
"37" => { "value" => "Host family or similar refugee accommodation" },
"25" => { "value" => "Any other accommodation" },
})
end
end
context "with 2026 logs" do
let(:year) { 2026 }
let(:start_year_2025_or_later?) { true }
let(:start_year_2026_or_later?) { true }
it "has the correct answer_options" do
expect(question.answer_options).to eq({
@ -64,6 +109,7 @@ RSpec.describe Form::Lettings::Questions::PreviousTenure, type: :model do
"32" => { "value" => "Fixed-term private registered provider (PRP) general needs tenancy" },
"31" => { "value" => "Lifetime local authority general needs tenancy" },
"33" => { "value" => "Lifetime private registered provider (PRP) general needs tenancy" },
"40" => { "value" => "Other general needs" },
"35" => { "value" => "Extra care housing" },
"38" => { "value" => "Older people’s housing for tenants with low support needs" },
"6" => { "value" => "Other supported housing" },

8
spec/models/form/lettings/subsections/household_characteristics_spec.rb

@ -346,6 +346,7 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
females_in_soft_age_range_in_pregnant_household_lead_age_value_check
age_lead_tenant_under_retirement_value_check
age_lead_tenant_over_retirement_value_check
lead_tenant_sex_registered_at_birth
lead_tenant_gender_identity
no_females_pregnant_household_lead_value_check
females_in_soft_age_range_in_pregnant_household_lead_value_check
@ -369,6 +370,7 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
females_in_soft_age_range_in_pregnant_household_person_2_age_value_check
age_2_under_retirement_value_check
age_2_over_retirement_value_check
person_2_sex_registered_at_birth
person_2_gender_identity
no_females_pregnant_household_person_2_value_check
females_in_soft_age_range_in_pregnant_household_person_2_value_check
@ -385,6 +387,7 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
females_in_soft_age_range_in_pregnant_household_person_3_age_value_check
age_3_under_retirement_value_check
age_3_over_retirement_value_check
person_3_sex_registered_at_birth
person_3_gender_identity
no_females_pregnant_household_person_3_value_check
females_in_soft_age_range_in_pregnant_household_person_3_value_check
@ -401,6 +404,7 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
females_in_soft_age_range_in_pregnant_household_person_4_age_value_check
age_4_under_retirement_value_check
age_4_over_retirement_value_check
person_4_sex_registered_at_birth
person_4_gender_identity
no_females_pregnant_household_person_4_value_check
females_in_soft_age_range_in_pregnant_household_person_4_value_check
@ -417,6 +421,7 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
females_in_soft_age_range_in_pregnant_household_person_5_age_value_check
age_5_under_retirement_value_check
age_5_over_retirement_value_check
person_5_sex_registered_at_birth
person_5_gender_identity
no_females_pregnant_household_person_5_value_check
females_in_soft_age_range_in_pregnant_household_person_5_value_check
@ -433,6 +438,7 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
females_in_soft_age_range_in_pregnant_household_person_6_age_value_check
age_6_under_retirement_value_check
age_6_over_retirement_value_check
person_6_sex_registered_at_birth
person_6_gender_identity
no_females_pregnant_household_person_6_value_check
females_in_soft_age_range_in_pregnant_household_person_6_value_check
@ -449,6 +455,7 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
females_in_soft_age_range_in_pregnant_household_person_7_age_value_check
age_7_under_retirement_value_check
age_7_over_retirement_value_check
person_7_sex_registered_at_birth
person_7_gender_identity
no_females_pregnant_household_person_7_value_check
females_in_soft_age_range_in_pregnant_household_person_7_value_check
@ -465,6 +472,7 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
females_in_soft_age_range_in_pregnant_household_person_8_age_value_check
age_8_under_retirement_value_check
age_8_over_retirement_value_check
person_8_sex_registered_at_birth
person_8_gender_identity
no_females_pregnant_household_person_8_value_check
females_in_soft_age_range_in_pregnant_household_person_8_value_check

104
spec/models/form/sales/pages/person_sex_registered_at_birth_spec.rb

@ -0,0 +1,104 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::PersonSexRegisteredAtBirth, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection, person_index:) }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2026, 4, 1))) }
let(:person_index) { 1 }
let(:page_id) { "person_2_sex_registered_at_birth" }
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
it "has the correct description" do
expect(page.description).to be_nil
end
context "with person 2" do
let(:person_index) { 2 }
let(:page_id) { "person_2_sex_registered_at_birth" }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[sexrab2])
end
it "has the correct id" do
expect(page.id).to eq("person_2_sex_registered_at_birth")
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "details_known_2" => 1 }])
end
end
context "with person 3" do
let(:person_index) { 3 }
let(:page_id) { "person_3_sex_registered_at_birth" }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[sexrab3])
end
it "has the correct id" do
expect(page.id).to eq("person_3_sex_registered_at_birth")
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "details_known_3" => 1 }])
end
end
context "with person 4" do
let(:person_index) { 4 }
let(:page_id) { "person_4_sex_registered_at_birth" }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[sexrab4])
end
it "has the correct id" do
expect(page.id).to eq("person_4_sex_registered_at_birth")
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "details_known_4" => 1 }])
end
end
context "with person 5" do
let(:person_index) { 5 }
let(:page_id) { "person_5_sex_registered_at_birth" }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[sexrab5])
end
it "has the correct id" do
expect(page.id).to eq("person_5_sex_registered_at_birth")
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "details_known_5" => 1 }])
end
end
context "with person 6" do
let(:person_index) { 6 }
let(:page_id) { "person_6_sex_registered_at_birth" }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[sexrab6])
end
it "has the correct id" do
expect(page.id).to eq("person_6_sex_registered_at_birth")
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "details_known_6" => 1 }])
end
end
end

29
spec/models/form/sales/pages/sex_registered_at_birth1_spec.rb

@ -0,0 +1,29 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::SexRegisteredAtBirth1, 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[sexrab1])
end
it "has the correct id" do
expect(page.id).to eq("buyer_1_sex_registered_at_birth")
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 eq([{ "buyer_has_seen_privacy_notice?" => true }, { "buyer_not_interviewed?" => true }])
end
end

38
spec/models/form/sales/pages/sex_registered_at_birth2_spec.rb

@ -0,0 +1,38 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::SexRegisteredAtBirth2, 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[sexrab2])
end
it "has the correct id" do
expect(page.id).to eq("buyer_2_sex_registered_at_birth")
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 eq([
{
"joint_purchase?" => true,
"buyer_has_seen_privacy_notice?" => true,
},
{
"joint_purchase?" => true,
"buyer_not_interviewed?" => true,
},
])
end
end

123
spec/models/form/sales/questions/person_sex_registered_at_birth_spec.rb

@ -0,0 +1,123 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::PersonSexRegisteredAtBirth, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page, person_index:) }
let(:question_id) { "sexrab2" }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
let(:person_index) { 2 }
let(:subsection) { instance_double(Form::Subsection) }
let(:form) { instance_double(Form, start_date: Time.zone.local(2026, 4, 1)) }
before do
allow(page).to receive(:subsection).and_return(subsection)
allow(subsection).to receive(:form).and_return(form)
end
it "has correct page" do
expect(question.page).to eq(page)
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({
"F" => { "value" => "Female" },
"M" => { "value" => "Male" },
"divider" => { "value" => true },
"R" => { "value" => "Person prefers not to say" },
})
end
context "when person 2" do
let(:question_id) { "sexrab2" }
let(:person_index) { 2 }
it "has the correct id" do
expect(question.id).to eq("sexrab2")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(2)
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to be_nil
end
end
context "when person 3" do
let(:question_id) { "sexrab3" }
let(:person_index) { 3 }
it "has the correct id" do
expect(question.id).to eq("sexrab3")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(3)
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to be_nil
end
end
context "when person 4" do
let(:question_id) { "sexrab4" }
let(:person_index) { 4 }
it "has the correct id" do
expect(question.id).to eq("sexrab4")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(4)
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to be_nil
end
end
context "when person 5" do
let(:question_id) { "sexrab5" }
let(:person_index) { 5 }
it "has the correct id" do
expect(question.id).to eq("sexrab5")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(5)
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to be_nil
end
end
context "when person 6" do
let(:question_id) { "sexrab6" }
let(:person_index) { 6 }
it "has the correct id" do
expect(question.id).to eq("sexrab6")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(6)
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to be_nil
end
end
end

49
spec/models/form/sales/questions/sex_registered_at_birth1_spec.rb

@ -0,0 +1,49 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::SexRegisteredAtBirth1, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
let(:subsection) { instance_double(Form::Subsection) }
let(:form) { instance_double(Form, start_date: Time.zone.local(2026, 4, 1)) }
before do
allow(page).to receive(:subsection).and_return(subsection)
allow(subsection).to receive(:form).and_return(form)
end
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("sexrab1")
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({
"F" => { "value" => "Female" },
"M" => { "value" => "Male" },
"divider" => { "value" => true },
"R" => { "value" => "Buyer prefers not to say" },
})
end
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(1)
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to be_nil
end
end

49
spec/models/form/sales/questions/sex_registered_at_birth2_spec.rb

@ -0,0 +1,49 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::SexRegisteredAtBirth2, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page) }
let(:question_id) { nil }
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page) }
let(:subsection) { instance_double(Form::Subsection) }
let(:form) { instance_double(Form, start_date: Time.zone.local(2026, 4, 1)) }
before do
allow(page).to receive(:subsection).and_return(subsection)
allow(subsection).to receive(:form).and_return(form)
end
it "has correct page" do
expect(question.page).to eq(page)
end
it "has the correct id" do
expect(question.id).to eq("sexrab2")
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({
"F" => { "value" => "Female" },
"M" => { "value" => "Male" },
"divider" => { "value" => true },
"R" => { "value" => "Buyer prefers not to say" },
})
end
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(2)
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to be_nil
end
end

131
spec/models/form/sales/subsections/household_characteristics_spec.rb

@ -21,6 +21,7 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
allow(form).to receive(:start_date).and_return(Time.zone.local(2023, 4, 1))
allow(form).to receive(:start_year_2024_or_later?).and_return(false)
allow(form).to receive(:start_year_2025_or_later?).and_return(false)
allow(form).to receive(:start_year_2026_or_later?).and_return(false)
end
it "has correct pages" do
@ -130,6 +131,7 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
allow(form).to receive(:start_date).and_return(Time.zone.local(2024, 4, 1))
allow(form).to receive(:start_year_2024_or_later?).and_return(true)
allow(form).to receive(:start_year_2025_or_later?).and_return(false)
allow(form).to receive(:start_year_2026_or_later?).and_return(false)
end
it "has correct depends on" do
@ -284,6 +286,7 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
allow(form).to receive(:start_date).and_return(Time.zone.local(2025, 4, 1))
allow(form).to receive(:start_year_2024_or_later?).and_return(true)
allow(form).to receive(:start_year_2025_or_later?).and_return(true)
allow(form).to receive(:start_year_2026_or_later?).and_return(false)
end
it "has correct pages" do
@ -398,4 +401,132 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
expect(household_characteristics.depends_on).to eq([{ "setup_completed?" => true }])
end
end
context "with 2026/27 form" do
before do
allow(form).to receive(:start_date).and_return(Time.zone.local(2026, 4, 1))
allow(form).to receive(:start_year_2024_or_later?).and_return(true)
allow(form).to receive(:start_year_2025_or_later?).and_return(true)
allow(form).to receive(:start_year_2026_or_later?).and_return(true)
end
it "has correct pages" do
expect(household_characteristics.pages.map(&:id)).to eq(
%w[
buyer_1_age
age_1_retirement_value_check
age_1_not_retired_value_check
age_1_old_persons_shared_ownership_joint_purchase_value_check
age_1_old_persons_shared_ownership_value_check
buyer_1_sex_registered_at_birth
buyer_1_gender_identity
buyer_1_ethnic_group
buyer_1_ethnic_background_black
buyer_1_ethnic_background_asian
buyer_1_ethnic_background_arab
buyer_1_ethnic_background_mixed
buyer_1_ethnic_background_white
buyer_1_nationality
buyer_1_working_situation
working_situation_1_retirement_value_check
working_situation_1_not_retired_value_check
working_situation_buyer_1_income_value_check
buyer_1_live_in_property
buyer_1_live_in_property_value_check
buyer_2_relationship_to_buyer_1
buyer_2_age
age_2_old_persons_shared_ownership_joint_purchase_value_check
age_2_old_persons_shared_ownership_value_check
age_2_buyer_retirement_value_check
age_2_buyer_not_retired_value_check
buyer_2_sex_registered_at_birth
buyer_2_gender_identity
buyer_2_ethnic_group
buyer_2_ethnic_background_black
buyer_2_ethnic_background_asian
buyer_2_ethnic_background_arab
buyer_2_ethnic_background_mixed
buyer_2_ethnic_background_white
buyer_2_nationality
buyer_2_working_situation
working_situation_2_retirement_value_check_joint_purchase
working_situation_2_not_retired_value_check_joint_purchase
working_situation_buyer_2_income_value_check
buyer_2_live_in_property
buyer_2_live_in_property_value_check
number_of_others_in_property
number_of_others_in_property_joint_purchase
person_2_known
person_2_relationship_to_buyer_1
relationship_2_partner_under_16_value_check
relationship_2_multiple_partners_value_check
person_2_age
age_2_retirement_value_check
age_2_not_retired_value_check
age_2_partner_under_16_value_check
person_2_sex_registered_at_birth
person_2_gender_identity
person_2_working_situation
working_situation_2_retirement_value_check
working_situation_2_not_retired_value_check
person_3_known
person_3_relationship_to_buyer_1
relationship_3_partner_under_16_value_check
relationship_3_multiple_partners_value_check
person_3_age
age_3_retirement_value_check
age_3_not_retired_value_check
age_3_partner_under_16_value_check
person_3_sex_registered_at_birth
person_3_gender_identity
person_3_working_situation
working_situation_3_retirement_value_check
working_situation_3_not_retired_value_check
person_4_known
person_4_relationship_to_buyer_1
relationship_4_partner_under_16_value_check
relationship_4_multiple_partners_value_check
person_4_age
age_4_retirement_value_check
age_4_not_retired_value_check
age_4_partner_under_16_value_check
person_4_sex_registered_at_birth
person_4_gender_identity
person_4_working_situation
working_situation_4_retirement_value_check
working_situation_4_not_retired_value_check
person_5_known
person_5_relationship_to_buyer_1
relationship_5_partner_under_16_value_check
relationship_5_multiple_partners_value_check
person_5_age
age_5_retirement_value_check
age_5_not_retired_value_check
age_5_partner_under_16_value_check
person_5_sex_registered_at_birth
person_5_gender_identity
person_5_working_situation
working_situation_5_retirement_value_check
working_situation_5_not_retired_value_check
person_6_known
person_6_relationship_to_buyer_1
relationship_6_partner_under_16_value_check
relationship_6_multiple_partners_value_check
person_6_age
age_6_retirement_value_check
age_6_not_retired_value_check
age_6_partner_under_16_value_check
person_6_sex_registered_at_birth
person_6_gender_identity
person_6_working_situation
working_situation_6_retirement_value_check
working_situation_6_not_retired_value_check
],
)
end
it "has correct depends on" do
expect(household_characteristics.depends_on).to eq([{ "setup_completed?" => true }])
end
end
end

6
spec/services/bulk_upload/lettings/validator_spec.rb

@ -11,7 +11,7 @@ RSpec.describe BulkUpload::Lettings::Validator do
let(:user) { create(:user, organisation:) }
let(:log) { build(:lettings_log, :completed, period: 2, assigned_to: user) }
let(:log_to_csv) { BulkUpload::LettingsLogToCsv.new(log:) }
let(:bulk_upload) { create(:bulk_upload, user:, year: log.collection_start_year) }
let(:bulk_upload) { create(:bulk_upload, :lettings, user:, year:) }
let(:path) { file.path }
let(:file) { Tempfile.new }
@ -190,8 +190,8 @@ RSpec.describe BulkUpload::Lettings::Validator do
expect(error.tenant_code).to eql(log.tenancycode)
expect(error.property_ref).to eql(log.propcode)
expect(error.row).to eql("2")
expect(error.cell).to eql("CX2")
expect(error.col).to eql("CX")
expect(error.cell).to eql("DL2")
expect(error.col).to eql("DL")
end
end
end

8
spec/services/bulk_upload/lettings/year2026/csv_parser_spec.rb

@ -244,10 +244,10 @@ RSpec.describe BulkUpload::Lettings::Year2026::CsvParser do
end
it "returns correct column" do
expect(service.column_for_field("field_5")).to eql("B")
expect(service.column_for_field("field_22")).to eql("AS")
expect(service.column_for_field("field_26")).to eql("DG")
expect(service.column_for_field("field_25")).to eql("I")
expect(service.column_for_field("field_5")).to eql("F")
expect(service.column_for_field("field_22")).to eql("AW")
expect(service.column_for_field("field_26")).to eql("DO")
expect(service.column_for_field("field_25")).to eql("R")
end
end
end

11
spec/services/bulk_upload/lettings/year2026/row_parser_spec.rb

@ -248,6 +248,15 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
field_4: "1",
field_18: "12",
field_130: "F",
field_131: "M",
field_132: "R",
field_133: "F",
field_134: "M",
field_135: "R",
field_136: "F",
field_137: "M",
}
end
@ -1910,7 +1919,7 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
let(:attributes) { { bulk_upload: } }
before do
build(:lettings_log, owning_organisation: nil, startdate: nil, tenancycode: nil, location: nil, age1: nil, sex1: nil, ecstat1: nil, brent: nil, scharge: nil, pscharge: nil, supcharg: nil).save(validate: false)
build(:lettings_log, owning_organisation: nil, startdate: nil, tenancycode: nil, location: nil, age1: nil, sexrab1: nil, sex1: nil, ecstat1: nil, brent: nil, scharge: nil, pscharge: nil, supcharg: nil).save(validate: false)
end
it "does not add duplicate logs validation to the blank row" do

2
spec/services/bulk_upload/sales/validator_spec.rb

@ -11,7 +11,7 @@ RSpec.describe BulkUpload::Sales::Validator do
let(:organisation) { create(:organisation, old_visible_id: "123") }
let(:log) { build(:sales_log, :completed, assigned_to: user) }
let(:log_to_csv) { BulkUpload::SalesLogToCsv.new(log:) }
let(:bulk_upload) { create(:bulk_upload, user:, year: log.collection_start_year) }
let(:bulk_upload) { create(:bulk_upload, :sales, user:, year:) }
let(:path) { file.path }
let(:file) { Tempfile.new }

8
spec/services/bulk_upload/sales/year2026/csv_parser_spec.rb

@ -15,8 +15,8 @@ RSpec.describe BulkUpload::Sales::Year2026::CsvParser do
file.write("Can be empty?\n")
file.write("Type of letting the question applies to\n")
file.write("Duplicate check field?\n")
file.write(BulkUpload::SalesLogToCsv.new(log:).default_field_numbers_row_for_year(2025))
file.write(BulkUpload::SalesLogToCsv.new(log:).to_year_csv_row(2025))
file.write(BulkUpload::SalesLogToCsv.new(log:).default_field_numbers_row_for_year(2026))
file.write(BulkUpload::SalesLogToCsv.new(log:).to_year_csv_row(2026))
file.write("\n")
file.rewind
end
@ -81,8 +81,8 @@ RSpec.describe BulkUpload::Sales::Year2026::CsvParser do
file.write("Can be empty?\n")
file.write("Type of letting the question applies to\n")
file.write("Duplicate check field?\n")
file.write(BulkUpload::SalesLogToCsv.new(log:).default_field_numbers_row_for_year(2025, seed:))
file.write(BulkUpload::SalesLogToCsv.new(log:).to_year_csv_row(2025, seed:))
file.write(BulkUpload::SalesLogToCsv.new(log:).default_field_numbers_row_for_year(2026, seed:))
file.write(BulkUpload::SalesLogToCsv.new(log:).to_year_csv_row(2026, seed:))
file.rewind
end

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

@ -112,6 +112,12 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do
field_105: "07",
field_106: "2023",
field_110: "900",
field_122: "F",
field_123: "F",
field_124: "M",
field_125: "M",
field_126: "R",
field_127: "R",
}
end
@ -1432,7 +1438,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do
let(:attributes) { { bulk_upload: } }
before do
build(:sales_log, owning_organisation: nil, saledate: nil, purchid: nil, age1: nil, sex1: nil, ecstat1: nil).save(validate: false)
build(:sales_log, owning_organisation: nil, saledate: nil, purchid: nil, age1: nil, sexrab1: nil, sex1: nil, ecstat1: nil).save(validate: false)
end
it "does not add duplicate logs validation to the blank row" do

3
spec/services/csv/lettings_log_csv_service_spec.rb

@ -242,6 +242,7 @@ RSpec.describe Csv::LettingsLogCsvService do
age1_known: 0,
age1: 35,
sex1: "F",
sexrab1: "F",
ethnic_group: 0,
ethnic: 2,
nationality_all: 36,
@ -251,12 +252,14 @@ RSpec.describe Csv::LettingsLogCsvService do
age2_known: 0,
age2: 32,
sex2: "M",
sexrab2: "M",
ecstat2: 6,
details_known_3: 1,
details_known_4: 0,
relat4: "R",
age4_known: 1,
sex4: "R",
sexrab4: "R",
ecstat4: 10,
armedforces: 1,
leftreg: 4,

4
spec/services/exports/lettings_log_export_service_spec.rb

@ -497,7 +497,7 @@ RSpec.describe Exports::LettingsLogExportService do
end
context "and one lettings log is available for export" do
let!(:lettings_log) { FactoryBot.create(:lettings_log, :completed, startdate: Time.zone.local(2026, 4, 3), assigned_to: user, age1: 35, sex1: "F", age2: 32, sex2: "M", ppostcode_full: "A1 1AA", nationality_all_group: 13, propcode: "123", postcode_full: "SE2 6RT", tenancycode: "BZ737", voiddate: Time.zone.local(2021, 11, 3), mrcdate: Time.zone.local(2022, 5, 5, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4, creation_method: 2, bulk_upload_id: 1, address_line1_as_entered: "address line 1 as entered", address_line2_as_entered: "address line 2 as entered", town_or_city_as_entered: "town or city as entered", county_as_entered: "county as entered", postcode_full_as_entered: "AB1 2CD", la_as_entered: "la as entered", manual_address_entry_selected: false, uprn: "1", uprn_known: 1) }
let!(:lettings_log) { FactoryBot.create(:lettings_log, :completed, startdate: Time.zone.local(2026, 4, 3), assigned_to: user, age1: 35, sexrab1: "F", sex1: "F", age2: 32, sexrab2: "M", sex2: "M", ppostcode_full: "A1 1AA", nationality_all_group: 13, propcode: "123", postcode_full: "SE2 6RT", tenancycode: "BZ737", voiddate: Time.zone.local(2021, 11, 3), mrcdate: Time.zone.local(2022, 5, 5, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4, creation_method: 2, bulk_upload_id: 1, address_line1_as_entered: "address line 1 as entered", address_line2_as_entered: "address line 2 as entered", town_or_city_as_entered: "town or city as entered", county_as_entered: "county as entered", postcode_full_as_entered: "AB1 2CD", la_as_entered: "la as entered", manual_address_entry_selected: false, uprn: "1", uprn_known: 1) }
let(:expected_zip_filename) { "core_2026_2027_apr_mar_f0001_inc0001.zip" }
let(:expected_data_filename) { "core_2026_2027_apr_mar_f0001_inc0001_pt001.xml" }
let(:xml_export_file) { File.open("spec/fixtures/exports/general_needs_log_26_27.xml", "r:UTF-8") }
@ -510,7 +510,7 @@ RSpec.describe Exports::LettingsLogExportService do
expect(entry.get_input_stream.read).to have_same_xml_contents_as(expected_content)
end
export_service.export_xml_lettings_logs
export_service.export_xml_lettings_logs(collection_year: 2026)
end
end
end

4
spec/services/imports/variable_definitions_service_spec.rb

@ -13,13 +13,13 @@ RSpec.describe Imports::VariableDefinitionsService, type: :service do
describe "#call" do
before do
allow(Dir).to receive(:glob).and_return(%w[lettings_download_23_24.csv lettings_download_24_25.csv sales_download_23_24.csv sales_download_24_25.csv])
allow(Dir).to receive(:glob).and_return(%w[lettings_download_23_24.csv lettings_download_24_25.csv lettings_download_26_27.csv sales_download_23_24.csv sales_download_24_25.csv sales_download_26_27.csv])
allow(service).to receive(:process_file)
end
it "processes each file in the directory" do
service.call
%w[lettings_download_23_24.csv lettings_download_24_25.csv sales_download_23_24.csv sales_download_24_25.csv].each do |file|
%w[lettings_download_23_24.csv lettings_download_24_25.csv lettings_download_26_27.csv sales_download_23_24.csv sales_download_24_25.csv sales_download_26_27.csv].each do |file|
expect(service).to have_received(:process_file).with(file)
end
end

Loading…
Cancel
Save