Browse Source

CLDC-4140: remove old gender question lettings (#3177)

* CLDC-4141: copy person questions refactor from CLDC-4142

* CLDC-4141: remove gender same as sex and replace gender identity q

* CLDC-4141: replace sex with sexrab where relevant for 2026

* CLDC-4141: update download order

* CLDC-4140: remove sex from rowparser entirely

* CLDC-4140: keep sex in factory for now

* CLDC-4140: update household characteristics test

* CLDC-4140: update log variable spec

* CLDC-4140: update csv export tests

* CLDC-4140: update validator tests

* CLDC-4140: update row-parser spec

* CLDC-4140: update csv-parser spec

* CLDC-4140: update export spec

* CLDC-4140: update log var defs spec

* CLDC-4140: update export spec

* CLDC-4140: update row parser spec

* CLDC-4140: update validator spec

* CLDC-4140: update csv parser spec

* CLDC-4143: Update gender retirement validations

rename functions as the new wording is more related to those that identify as male and any other option

* CLDC-4143: Add 2026 tests

* CLDC-4143: Update legacy json files

causes issues with some old tests

* CLDC-4143: Add error mappings for gender fields

* CLDC-4143: Add new gender age retirement soft validation

replaces the many check pages from previous years with a single one

shows if either of the previous ones would show

has agreed wording on ticket

* fixup! CLDC-4143: Update gender retirement validations

improve gender comment wording

* CLDC-4143: Update non males validation name

reference to 'soft validation' we found confusing

* fixup! CLDC-4143: Add new gender age retirement soft validation

update subsection specs

* CLDC-4140: update parsers post merge

* CLDC-4140: update tests after field renumbering

* CLDC-4140: update tests after field renumbering

* CLDC-4140: update spacing in to_2026_row

* CLDC-4140: update row parser spec field numbers

* CLDC-4140: update row parser field types

---------

Co-authored-by: Samuel Young <samuel.young@softwire.com>
pull/3169/head^2
Nat Dean-Lewis 1 month ago committed by GitHub
parent
commit
02f319396b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 35
      app/helpers/bulk_upload/lettings_log_to_csv.rb
  2. 2
      app/models/derived_variables/lettings_log_variables.rb
  3. 4
      app/models/form/lettings/subsections/household_characteristics.rb
  4. 8
      app/models/form/question.rb
  5. 6
      app/models/lettings_log.rb
  6. 2
      app/services/bulk_upload/lettings/year2026/csv_parser.rb
  7. 251
      app/services/bulk_upload/lettings/year2026/row_parser.rb
  8. 17
      app/services/exports/lettings_log_export_constants.rb
  9. 8
      spec/fixtures/exports/general_needs_log_26_27.xml
  10. 6
      spec/fixtures/files/lettings_log_csv_export_codes_26.csv
  11. 6
      spec/fixtures/files/lettings_log_csv_export_labels_26.csv
  12. 6
      spec/fixtures/files/lettings_log_csv_export_non_support_codes_26.csv
  13. 6
      spec/fixtures/files/lettings_log_csv_export_non_support_labels_26.csv
  14. 8
      spec/models/form/lettings/subsections/household_characteristics_spec.rb
  15. 4
      spec/services/bulk_upload/lettings/validator_spec.rb
  16. 8
      spec/services/bulk_upload/lettings/year2026/csv_parser_spec.rb
  17. 244
      spec/services/bulk_upload/lettings/year2026/row_parser_spec.rb
  18. 3
      spec/services/csv/lettings_log_csv_service_spec.rb
  19. 18
      spec/services/exports/lettings_log_export_service_spec.rb

35
app/helpers/bulk_upload/lettings_log_to_csv.rb

@ -144,40 +144,40 @@ class BulkUpload::LettingsLogToCsv
log.tenancylength,
log.age1 || overrides[:age1],
log.sex1,
log.sexrab1,
log.ethnic,
log.nationality_all_group,
log.ecstat1,
relat_number(log.relat2),
log.age2 || overrides[:age2],
log.sex2,
log.sexrab2,
log.ecstat2, # 50
relat_number(log.relat3),
log.age3 || overrides[:age3],
log.sex3,
log.sexrab3,
log.ecstat3,
relat_number(log.relat4),
log.age4 || overrides[:age4],
log.sex4,
log.sexrab4,
log.ecstat4,
relat_number(log.relat5),
log.age5 || overrides[:age5], # 60
log.sex5,
log.sexrab5,
log.ecstat5,
relat_number(log.relat6),
log.age6 || overrides[:age6],
log.sex6,
log.sexrab6,
log.ecstat6,
relat_number(log.relat7),
log.age7 || overrides[:age7],
log.sex7,
log.sexrab7,
log.ecstat7, # 70
relat_number(log.relat8),
log.age8 || overrides[:age8],
log.sex8,
log.sexrab8,
log.ecstat8,
log.armedforces,
log.leftreg,
@ -239,18 +239,10 @@ class BulkUpload::LettingsLogToCsv
log.supcharg,
log.hbrentshortfall,
log.tshortfall,
log.gender_same_as_sex1, # 130
log.sexrab1, # 130
log.sexrab2,
log.sexrab3,
log.sexrab4,
log.sexrab5,
log.sexrab6,
log.sexrab7,
log.sexrab8,
log.gender_same_as_sex1,
log.gender_description1,
log.gender_same_as_sex2, # 140
log.gender_same_as_sex2,
log.gender_description2,
log.gender_same_as_sex3,
log.gender_description3,
@ -258,15 +250,16 @@ class BulkUpload::LettingsLogToCsv
log.gender_description4,
log.gender_same_as_sex5,
log.gender_description5,
log.gender_same_as_sex6,
log.gender_same_as_sex6, # 140
log.gender_description6,
log.gender_same_as_sex7, # 150
log.gender_same_as_sex7,
log.gender_description7,
log.gender_same_as_sex8,
log.gender_description8,
log.owning_organisation.prp? ? log.referral_register : nil,
log.referral_noms,
log.referral_org, # 156
log.referral_org, # 148
]
end

2
app/models/derived_variables/lettings_log_variables.rb

@ -300,7 +300,7 @@ private
end
def get_refused
return 1 if details_unknown? || age_refused? || sex_refused? || relat_refused? || ecstat_refused?
return 1 if details_unknown? || age_refused? || sex_refused? || sexrab_refused? || relat_refused? || ecstat_refused?
0
end

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

@ -21,7 +21,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
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::LeadTenantGenderSameAsSex.new(nil, nil, self) if form.start_year_2026_or_later?),
Form::Lettings::Pages::LeadTenantGenderIdentity.new(nil, nil, self),
(Form::Lettings::Pages::LeadTenantGenderIdentity.new(nil, nil, self) unless form.start_year_2026_or_later?),
(Form::Lettings::Pages::NoFemalesPregnantHouseholdLeadValueCheck.new(nil, nil, self) unless form.start_year_2026_or_later?),
(Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdLeadValueCheck.new(nil, nil, self) unless form.start_year_2026_or_later?),
(Form::Lettings::Pages::NoHouseholdMemberLikelyToBePregnantCheck.new("no_household_member_likely_to_be_pregnant_lead_check", nil, self, person_index: 1) if form.start_year_2026_or_later?),
@ -63,7 +63,7 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
(Form::Lettings::Pages::PartnerUnder16ValueCheck.new("age_#{person_index}_partner_under_16_value_check", nil, self, person_index:) if form.start_year_2024_or_later? && !form.start_year_2026_or_later?),
(Form::Lettings::Pages::PersonSexRegisteredAtBirth.new(nil, nil, self, person_index:) if form.start_year_2026_or_later?),
(Form::Lettings::Pages::PersonGenderSameAsSex.new(nil, nil, self, person_index:) if form.start_year_2026_or_later?),
Form::Lettings::Pages::PersonGenderIdentity.new(nil, nil, self, person_index:),
(Form::Lettings::Pages::PersonGenderIdentity.new(nil, nil, self, person_index:) unless form.start_year_2026_or_later?),
(Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.new(nil, nil, self, person_index:) unless form.start_year_2026_or_later?),
(Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self, person_index:) unless form.start_year_2026_or_later?),
(Form::Lettings::Pages::NoHouseholdMemberLikelyToBePregnantCheck.new("no_household_member_likely_to_be_pregnant_person_#{person_index}_check", nil, self, person_index:) if form.start_year_2026_or_later?),

8
app/models/form/question.rb

@ -446,6 +446,14 @@ private
sex6: %w[R],
sex7: %w[R],
sex8: %w[R],
sexrab1: %w[R],
sexrab2: %w[R],
sexrab3: %w[R],
sexrab4: %w[R],
sexrab5: %w[R],
sexrab6: %w[R],
sexrab7: %w[R],
sexrab8: %w[R],
relat2: [3],
relat3: [3],
relat4: [3],

6
app/models/lettings_log.rb

@ -687,7 +687,7 @@ class LettingsLog < Log
end
def has_any_person_details?(person_index)
["sex#{person_index}", "relat#{person_index}", "ecstat#{person_index}"].any? { |field| public_send(field).present? } || public_send("age#{person_index}_known") == 1
["sex#{person_index}", "sexrab#{person_index}", "relat#{person_index}", "ecstat#{person_index}"].any? { |field| public_send(field).present? } || public_send("age#{person_index}_known") == 1
end
def details_not_known_for_person?(person_index)
@ -911,6 +911,10 @@ private
[sex1, sex2, sex3, sex4, sex5, sex6, sex7, sex8].any?("R")
end
def sexrab_refused?
[sexrab1, sexrab2, sexrab3, sexrab4, sexrab5, sexrab6, sexrab7, sexrab8].any?("R")
end
def relat_refused?
[relat2, relat3, relat4, relat5, relat6, relat7, relat8].any?("R")
end

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

@ -4,7 +4,7 @@ class BulkUpload::Lettings::Year2026::CsvParser
include CollectionTimeHelper
# TODO: CLDC-4162: Update when 2026 format is known
FIELDS = 156
FIELDS = 148
FORM_YEAR = 2026
attr_reader :path

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

@ -47,37 +47,37 @@ class BulkUpload::Lettings::Year2026::RowParser
field_40: "If 'Other', what is the type of tenancy?",
field_41: "What is the length of the fixed-term tenancy to the nearest year?",
field_42: "What is the lead tenant’s age?",
field_43: "Which of these best describes the lead tenant’s gender identity?",
field_43: "Lead tenant's sex, as registered at birth",
field_44: "Which of these best describes the lead tenant’s ethnic background?",
field_45: "What is the lead tenant’s nationality?",
field_46: "Which of these best describes the lead tenant’s working situation?",
field_47: "Is person 2 the partner of the lead tenant?",
field_48: "What is person 2’s age?",
field_49: "Which of these best describes person 2’s gender identity?",
field_49: "Person 2's sex, as registered at birth",
field_50: "Which of these best describes person 2’s working situation?",
field_51: "Is person 3 the partner of the lead tenant?",
field_52: "What is person 3’s age?",
field_53: "Which of these best describes person 3’s gender identity?",
field_53: "Person 3's sex, as registered at birth",
field_54: "Which of these best describes person 3’s working situation?",
field_55: "Is person 4 the partner of the lead tenant?",
field_56: "What is person 4’s age?",
field_57: "Which of these best describes person 4’s gender identity?",
field_57: "Person 4's sex, as registered at birth",
field_58: "Which of these best describes person 4’s working situation?",
field_59: "Is person 5 the partner of the lead tenant?",
field_60: "What is person 5’s age?",
field_61: "Which of these best describes person 5’s gender identity?",
field_61: "Person 5's sex, as registered at birth",
field_62: "Which of these best describes person 5’s working situation?",
field_63: "Is person 6 the partner of the lead tenant?",
field_64: "What is person 6’s age?",
field_65: "Which of these best describes person 6’s gender identity?",
field_65: "Person 6's sex, as registered at birth",
field_66: "Which of these best describes person 6’s working situation?",
field_67: "Is person 7 the partner of the lead tenant?",
field_68: "What is person 7’s age?",
field_69: "Which of these best describes person 7’s gender identity?",
field_69: "Person 7's sex, as registered at birth",
field_70: "Which of these best describes person 7’s working situation?",
field_71: "Is person 8 the partner of the lead tenant?",
field_72: "What is person 8’s age?",
field_73: "Which of these best describes person 8’s gender identity?",
field_73: "Person 8's sex, as registered at birth",
field_74: "Which of these best describes person 8’s working situation?",
field_75: "Does anybody in the household have links to the UK armed forces?",
field_76: "Is this person still serving in the UK armed forces?",
@ -135,34 +135,26 @@ class BulkUpload::Lettings::Year2026::RowParser
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",
field_138: "Is the gender the lead tenant identifies with the same as their sex registered at birth?",
field_139: "If 'No', enter the lead tenant's gender identity",
field_140: "Is the gender person 2 identifies with the same as their sex registered at birth?",
field_141: "If 'No', enter person 2's gender identity",
field_142: "Is the gender person 3 identifies with the same as their sex registered at birth?",
field_143: "If 'No', enter person 3's gender identity",
field_144: "Is the gender person 4 identifies with the same as their sex registered at birth?",
field_145: "If 'No', enter person 4's gender identity",
field_146: "Is the gender person 5 identifies with the same as their sex registered at birth?",
field_147: "If 'No', enter person 5's gender identity",
field_148: "Is the gender person 6 identifies with the same as their sex registered at birth?",
field_149: "If 'No', enter person 6's gender identity",
field_150: "Is the gender person 7 identifies with the same as their sex registered at birth?",
field_151: "If 'No', enter person 7's gender identity",
field_152: "Is the gender person 8 identifies with the same as their sex registered at birth?",
field_153: "If 'No', enter person 8's gender identity",
field_154: "What was the source of referral for this letting? - PRP properties part 1",
field_155: "What was the source of referral for this letting? - PRP properties part 2",
field_156: "What was the source of referral for this letting? - PRP properties part 3",
field_130: "Is the gender the lead tenant identifies with the same as their sex registered at birth?",
field_131: "If 'No', enter the lead tenant's gender identity",
field_132: "Is the gender person 2 identifies with the same as their sex registered at birth?",
field_133: "If 'No', enter person 2's gender identity",
field_134: "Is the gender person 3 identifies with the same as their sex registered at birth?",
field_135: "If 'No', enter person 3's gender identity",
field_136: "Is the gender person 4 identifies with the same as their sex registered at birth?",
field_137: "If 'No', enter person 4's gender identity",
field_138: "Is the gender person 5 identifies with the same as their sex registered at birth?",
field_139: "If 'No', enter person 5's gender identity",
field_140: "Is the gender person 6 identifies with the same as their sex registered at birth?",
field_141: "If 'No', enter person 6's gender identity",
field_142: "Is the gender person 7 identifies with the same as their sex registered at birth?",
field_143: "If 'No', enter person 7's gender identity",
field_144: "Is the gender person 8 identifies with the same as their sex registered at birth?",
field_145: "If 'No', enter person 8's gender identity",
field_146: "What was the source of referral for this letting? - PRP properties part 1",
field_147: "What was the source of referral for this letting? - PRP properties part 2",
field_148: "What was the source of referral for this letting? - PRP properties part 3",
}.freeze
RENT_TYPE_BU_MAPPING = {
@ -312,13 +304,13 @@ class BulkUpload::Lettings::Year2026::RowParser
attribute :field_128, :integer
attribute :field_129, :decimal
attribute :field_130, :string
attribute :field_130, :integer
attribute :field_131, :string
attribute :field_132, :string
attribute :field_132, :integer
attribute :field_133, :string
attribute :field_134, :string
attribute :field_134, :integer
attribute :field_135, :string
attribute :field_136, :string
attribute :field_136, :integer
attribute :field_137, :string
attribute :field_138, :integer
attribute :field_139, :string
@ -328,18 +320,10 @@ class BulkUpload::Lettings::Year2026::RowParser
attribute :field_143, :string
attribute :field_144, :integer
attribute :field_145, :string
attribute :field_146, :integer
attribute :field_147, :string
attribute :field_147, :integer
attribute :field_148, :integer
attribute :field_149, :string
attribute :field_150, :integer
attribute :field_151, :string
attribute :field_152, :integer
attribute :field_153, :string
attribute :field_154, :integer
attribute :field_155, :integer
attribute :field_156, :integer
validate :validate_valid_radio_option, on: :before_log
@ -597,8 +581,7 @@ class BulkUpload::Lettings::Year2026::RowParser
!supported_housing? ? "field_23" : nil, # postcode # TODO: CLDC-4119: add postcode to hash for supported housing
!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_43", # sexrab1
"field_46", # ecstat1
)
if [field_124, field_125, field_126, field_127].all?(&:present?)
@ -748,7 +731,6 @@ private
"startdate",
"age1",
"sexrab1",
"sex1",
"ecstat1",
"owning_organisation",
"tcharge",
@ -1030,8 +1012,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_43, error_message) # sexrab1
errors.add(:field_46, error_message) # ecstat1
errors.add(:field_122, error_message) unless general_needs? # household_charge
errors.add(:field_124, error_message) # brent
@ -1051,31 +1032,31 @@ private
def field_referral_register_prp_valid?
if owning_organisation&.prp?
[5, 6, 7, 8, 9].include?(field_154)
[5, 6, 7, 8, 9].include?(field_146)
else
field_154.blank?
field_146.blank?
end
end
def field_referral_noms_valid?
case field_154
case field_146
when 6
[1, 2, 3, 4].include?(field_155)
[1, 2, 3, 4].include?(field_147)
when 7
[5, 6, 7, 8].include?(field_155)
[5, 6, 7, 8].include?(field_147)
else
field_155.blank?
field_147.blank?
end
end
def field_referral_org_valid?
case field_155
case field_147
when 1
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].include?(field_156)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].include?(field_148)
when 7
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20].include?(field_156)
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20].include?(field_148)
else
field_156.blank?
field_148.blank?
end
end
@ -1087,7 +1068,7 @@ private
return if renewal?
return if referral_fields_valid?
%i[field_116 field_154 field_155 field_156].each do |field|
%i[field_116 field_146 field_147 field_148].each do |field|
errors.add(field, I18n.t("#{ERROR_BASE_KEY}.referral.invalid_option"))
end
end
@ -1140,14 +1121,14 @@ private
age8_known: %i[field_72],
age8: %i[field_72],
sex1: %i[field_43],
sex2: %i[field_49],
sex3: %i[field_53],
sex4: %i[field_57],
sex5: %i[field_61],
sex6: %i[field_65],
sex7: %i[field_69],
sex8: %i[field_73],
sexrab1: %i[field_43],
sexrab2: %i[field_49],
sexrab3: %i[field_53],
sexrab4: %i[field_57],
sexrab5: %i[field_61],
sexrab6: %i[field_65],
sexrab7: %i[field_69],
sexrab8: %i[field_73],
ethnic_group: %i[field_44],
ethnic: %i[field_44],
@ -1204,9 +1185,9 @@ private
accessible_register: %i[field_115],
letting_allocation: %i[field_112 field_113 field_114 field_115],
referral_register: %i[field_116 field_154],
referral_noms: %i[field_155],
referral_org: %i[field_156],
referral_register: %i[field_116 field_146],
referral_noms: %i[field_147],
referral_org: %i[field_148],
net_income_known: %i[field_117],
incfreq: %i[field_118],
@ -1255,30 +1236,22 @@ private
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],
gender_same_as_sex1: %i[field_138],
gender_same_as_sex2: %i[field_140],
gender_same_as_sex3: %i[field_142],
gender_same_as_sex4: %i[field_144],
gender_same_as_sex5: %i[field_146],
gender_same_as_sex6: %i[field_148],
gender_same_as_sex7: %i[field_150],
gender_same_as_sex8: %i[field_152],
gender_description1: %i[field_139],
gender_description2: %i[field_141],
gender_description3: %i[field_143],
gender_description4: %i[field_145],
gender_description5: %i[field_147],
gender_description6: %i[field_149],
gender_description7: %i[field_151],
gender_description8: %i[field_153],
gender_same_as_sex1: %i[field_130],
gender_same_as_sex2: %i[field_132],
gender_same_as_sex3: %i[field_134],
gender_same_as_sex4: %i[field_136],
gender_same_as_sex5: %i[field_138],
gender_same_as_sex6: %i[field_140],
gender_same_as_sex7: %i[field_142],
gender_same_as_sex8: %i[field_144],
gender_description1: %i[field_131],
gender_description2: %i[field_133],
gender_description3: %i[field_135],
gender_description4: %i[field_137],
gender_description5: %i[field_139],
gender_description6: %i[field_141],
gender_description7: %i[field_143],
gender_description8: %i[field_145],
}.compact
end
@ -1340,14 +1313,14 @@ private
attributes["age8_known"] = age8_known?
attributes["age8"] = field_72 if attributes["age8_known"]&.zero? && field_72&.match(/\A\d{1,3}\z|\AR\z/)
attributes["sex1"] = field_43
attributes["sex2"] = field_49
attributes["sex3"] = field_53
attributes["sex4"] = field_57
attributes["sex5"] = field_61
attributes["sex6"] = field_65
attributes["sex7"] = field_69
attributes["sex8"] = field_73
attributes["sexrab1"] = field_43
attributes["sexrab2"] = field_49
attributes["sexrab3"] = field_53
attributes["sexrab4"] = field_57
attributes["sexrab5"] = field_61
attributes["sexrab6"] = field_65
attributes["sexrab7"] = field_69
attributes["sexrab8"] = field_73
attributes["ethnic_group"] = ethnic_group_from_ethnic
attributes["ethnic"] = field_44
@ -1486,30 +1459,22 @@ 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["gender_same_as_sex1"] = field_138
attributes["gender_description1"] = field_139
attributes["gender_same_as_sex2"] = field_140
attributes["gender_description2"] = field_141
attributes["gender_same_as_sex3"] = field_142
attributes["gender_description3"] = field_143
attributes["gender_same_as_sex4"] = field_144
attributes["gender_description4"] = field_145
attributes["gender_same_as_sex5"] = field_146
attributes["gender_description5"] = field_147
attributes["gender_same_as_sex6"] = field_148
attributes["gender_description6"] = field_149
attributes["gender_same_as_sex7"] = field_150
attributes["gender_description7"] = field_151
attributes["gender_same_as_sex8"] = field_152
attributes["gender_description8"] = field_153
attributes["gender_same_as_sex1"] = field_130
attributes["gender_description1"] = field_131
attributes["gender_same_as_sex2"] = field_132
attributes["gender_description2"] = field_133
attributes["gender_same_as_sex3"] = field_134
attributes["gender_description3"] = field_135
attributes["gender_same_as_sex4"] = field_136
attributes["gender_description4"] = field_137
attributes["gender_same_as_sex5"] = field_138
attributes["gender_description5"] = field_139
attributes["gender_same_as_sex6"] = field_140
attributes["gender_description6"] = field_141
attributes["gender_same_as_sex7"] = field_142
attributes["gender_description7"] = field_143
attributes["gender_same_as_sex8"] = field_144
attributes["gender_description8"] = field_145
attributes
end
@ -1617,31 +1582,31 @@ private
end
def person_2_present?
field_47.present? || field_48.present? || field_49.present? || field_131.present? || field_140.present? || field_141.present?
field_47.present? || field_48.present? || field_49.present? || field_132.present? || field_133.present?
end
def person_3_present?
field_51.present? || field_52.present? || field_53.present? || field_132.present? || field_142.present? || field_143.present?
field_51.present? || field_52.present? || field_53.present? || field_134.present? || field_135.present?
end
def person_4_present?
field_55.present? || field_56.present? || field_57.present? || field_133.present? || field_144.present? || field_145.present?
field_55.present? || field_56.present? || field_57.present? || field_136.present? || field_137.present?
end
def person_5_present?
field_59.present? || field_60.present? || field_61.present? || field_134.present? || field_146.present? || field_147.present?
field_59.present? || field_60.present? || field_61.present? || field_138.present? || field_139.present?
end
def person_6_present?
field_63.present? || field_64.present? || field_65.present? || field_135.present? || field_148.present? || field_149.present?
field_63.present? || field_64.present? || field_65.present? || field_140.present? || field_141.present?
end
def person_7_present?
field_67.present? || field_68.present? || field_69.present? || field_136.present? || field_150.present? || field_151.present?
field_67.present? || field_68.present? || field_69.present? || field_142.present? || field_143.present?
end
def person_8_present?
field_71.present? || field_72.present? || field_73.present? || field_137.present? || field_152.present? || field_153.present?
field_71.present? || field_72.present? || field_73.present? || field_144.present? || field_145.present?
end
def leftreg
@ -1847,7 +1812,7 @@ private
if owning_organisation.la?
field_116
else
field_154
field_146
end
end
@ -1856,7 +1821,7 @@ private
return unless referral_fields_valid?
if owning_organisation.prp?
field_155
field_147
end
end
@ -1865,7 +1830,7 @@ private
return unless referral_fields_valid?
if owning_organisation.prp?
field_156
field_148
end
end
end

17
app/services/exports/lettings_log_export_constants.rb

@ -140,7 +140,6 @@ module Exports::LettingsLogExportConstants
(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}"
@ -159,6 +158,9 @@ module Exports::LettingsLogExportConstants
"offered",
"referral",
]
(1..8).each do |index|
YEAR_2021_EXPORT_FIELDS << "sex#{index}"
end
YEAR_2022_EXPORT_FIELDS = Set[
"builtype",
@ -167,6 +169,9 @@ module Exports::LettingsLogExportConstants
"offered",
"referral",
]
(1..8).each do |index|
YEAR_2022_EXPORT_FIELDS << "sex#{index}"
end
YEAR_2023_EXPORT_FIELDS = Set[
"builtype",
@ -175,6 +180,9 @@ module Exports::LettingsLogExportConstants
"offered",
"referral",
]
(1..8).each do |index|
YEAR_2023_EXPORT_FIELDS << "sex#{index}"
end
YEAR_2024_EXPORT_FIELDS = Set[
"builtype",
@ -196,6 +204,9 @@ module Exports::LettingsLogExportConstants
"carehome_charges_value_check",
"referral",
]
(1..8).each do |index|
YEAR_2024_EXPORT_FIELDS << "sex#{index}"
end
YEAR_2025_EXPORT_FIELDS = Set[
"builtype",
@ -215,6 +226,9 @@ module Exports::LettingsLogExportConstants
"supcharg_value_check",
"referral",
]
(1..8).each do |index|
YEAR_2025_EXPORT_FIELDS << "sex#{index}"
end
YEAR_2026_EXPORT_FIELDS = Set[
"accessible_register",
@ -235,7 +249,6 @@ module Exports::LettingsLogExportConstants
"referral_noms",
"referral_org",
]
(1..8).each do |index|
YEAR_2026_EXPORT_FIELDS << "sexrab#{index}"
YEAR_2026_EXPORT_FIELDS << "gender_same_as_sex#{index}"

8
spec/fixtures/exports/general_needs_log_26_27.xml vendored

@ -5,7 +5,6 @@
<tenancycode>BZ737</tenancycode>
<age1>35</age1>
<sexrab1>F</sexrab1>
<sex1>F</sex1>
<gender_same_as_sex1>1</gender_same_as_sex1>
<gender_description1/>
<ethnic>2</ethnic>
@ -14,43 +13,36 @@
<hhmemb>2</hhmemb>
<age2>32</age2>
<sexrab2>M</sexrab2>
<sex2>M</sex2>
<gender_same_as_sex2>2</gender_same_as_sex2>
<gender_description2>Non-binary</gender_description2>
<ecstat2>6</ecstat2>
<age3/>
<sexrab3/>
<sex3/>
<gender_same_as_sex3/>
<gender_description3/>
<ecstat3/>
<age4/>
<sexrab4/>
<sex4/>
<gender_same_as_sex4/>
<gender_description4/>
<ecstat4/>
<age5/>
<sexrab5/>
<sex5/>
<gender_same_as_sex5/>
<gender_description5/>
<ecstat5/>
<age6/>
<sexrab6/>
<sex6/>
<gender_same_as_sex6/>
<gender_description6/>
<ecstat6/>
<age7/>
<sexrab7/>
<sex7/>
<gender_same_as_sex7/>
<gender_description7/>
<ecstat7/>
<age8/>
<sexrab8/>
<sex8/>
<gender_same_as_sex8/>
<gender_description8/>
<ecstat8/>

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

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

@ -346,7 +346,6 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
age_lead_tenant_over_retirement_value_check
lead_tenant_sex_registered_at_birth
lead_tenant_gender_same_as_sex
lead_tenant_gender_identity
no_household_member_likely_to_be_pregnant_lead_check
gender_lead_tenant_over_retirement_value_check
lead_tenant_ethnic_group
@ -369,7 +368,6 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
age_2_over_retirement_value_check
person_2_sex_registered_at_birth
person_2_gender_same_as_sex
person_2_gender_identity
no_household_member_likely_to_be_pregnant_person_2_check
gender_2_over_retirement_value_check
person_2_working_situation
@ -385,7 +383,6 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
age_3_over_retirement_value_check
person_3_sex_registered_at_birth
person_3_gender_same_as_sex
person_3_gender_identity
no_household_member_likely_to_be_pregnant_person_3_check
gender_3_over_retirement_value_check
person_3_working_situation
@ -401,7 +398,6 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
age_4_over_retirement_value_check
person_4_sex_registered_at_birth
person_4_gender_same_as_sex
person_4_gender_identity
no_household_member_likely_to_be_pregnant_person_4_check
gender_4_over_retirement_value_check
person_4_working_situation
@ -417,7 +413,6 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
age_5_over_retirement_value_check
person_5_sex_registered_at_birth
person_5_gender_same_as_sex
person_5_gender_identity
no_household_member_likely_to_be_pregnant_person_5_check
gender_5_over_retirement_value_check
person_5_working_situation
@ -433,7 +428,6 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
age_6_over_retirement_value_check
person_6_sex_registered_at_birth
person_6_gender_same_as_sex
person_6_gender_identity
no_household_member_likely_to_be_pregnant_person_6_check
gender_6_over_retirement_value_check
person_6_working_situation
@ -449,7 +443,6 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
age_7_over_retirement_value_check
person_7_sex_registered_at_birth
person_7_gender_same_as_sex
person_7_gender_identity
no_household_member_likely_to_be_pregnant_person_7_check
gender_7_over_retirement_value_check
person_7_working_situation
@ -465,7 +458,6 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
age_8_over_retirement_value_check
person_8_sex_registered_at_birth
person_8_gender_same_as_sex
person_8_gender_identity
no_household_member_likely_to_be_pregnant_person_8_check
gender_8_over_retirement_value_check
person_8_working_situation

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

@ -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("EH2") # this may change when adding a new field as the cols are in a random order
expect(error.col).to eql("EH") # this may change when adding a new field as the cols are in a random order
expect(error.cell).to eql("EB2") # this may change when adding a new field as the cols are in a random order
expect(error.col).to eql("EB") # this may change when adding a new field as the cols are in a random order
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("AI")
expect(service.column_for_field("field_22")).to eql("BR")
expect(service.column_for_field("field_26")).to eql("CG")
expect(service.column_for_field("field_25")).to eql("E")
expect(service.column_for_field("field_5")).to eql("AA")
expect(service.column_for_field("field_22")).to eql("BK")
expect(service.column_for_field("field_26")).to eql("BZ")
expect(service.column_for_field("field_25")).to eql("S")
end
end
end

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

@ -249,30 +249,22 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
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",
field_138: "1",
field_139: "",
field_140: "2",
field_141: "identity",
field_142: "3",
field_130: "1",
field_131: "",
field_132: "2",
field_133: "identity",
field_134: "3",
field_135: "",
field_136: "1",
field_137: "",
field_138: "2",
field_139: "identity",
field_140: "3",
field_141: "",
field_142: "1",
field_143: "",
field_144: "1",
field_145: "",
field_146: "2",
field_147: "identity",
field_148: "3",
field_149: "",
field_150: "1",
field_151: "",
field_152: "2",
field_153: "identity",
field_144: "2",
field_145: "identity",
}
end
@ -323,7 +315,7 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
:field_24, # postcode_full
:field_25, # LA
:field_42, # age1
:field_43, # sex1
:field_43, # sexrab1
:field_46, # ecstat1
:field_124, # brent
:field_125, # scharge
@ -377,7 +369,7 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
:field_13, # tenancycode
:field_6, # location
:field_42, # age1
:field_43, # sex1
:field_43, # sexrab1
:field_46, # ecstat1
:field_124, # brent
:field_125, # scharge
@ -419,7 +411,7 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
:field_13, # tenancycode
:field_6, # location
:field_42, # age1
:field_43, # sex1
:field_43, # sexrab1
:field_46, # ecstat1
:field_124, # brent
:field_125, # scharge
@ -462,7 +454,7 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
:field_13, # tenancycode
:field_6, # location
:field_42, # age1
:field_43, # sex1
:field_43, # sexrab1
:field_46, # ecstat1
].each do |field|
expect(parser.errors[field]).to include(error_message)
@ -508,7 +500,7 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
:field_13, # tenancycode
:field_6, # location
:field_42, # age1
:field_43, # sex1
:field_43, # sexrab1
:field_46, # ecstat1
:field_122, # household_charge
].each do |field|
@ -562,7 +554,7 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
:field_24, # postcode_full
:field_25, # LA
:field_42, # age1
:field_43, # sex1
:field_43, # sexrab1
:field_46, # ecstat1
].each do |field|
expect(parser.errors[field]).to be_blank
@ -634,7 +626,7 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
it "fetches the question's check_answer_label if it exists" do
parser.valid?
expect(parser.errors[:field_43]).to eql([I18n.t("validations.lettings.2026.bulk_upload.not_answered", question: "lead tenant’s gender identity.")])
expect(parser.errors[:field_43]).to eql([I18n.t("validations.lettings.2026.bulk_upload.not_answered", question: "lead tenant’s sex registered at birth.")])
end
end
@ -1160,7 +1152,7 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
end
end
describe "#field_116, field_154, field_155, field_156" do # referral
describe "#field_116, field_146, field_147, field_148" do # referral
context "when org is LA" do
let(:owning_org) { create(:organisation, :la, :with_old_visible_id) }
@ -1175,9 +1167,9 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
it "does not add an error" do
parser.valid?
expect(parser.errors[:field_116]).to be_blank
expect(parser.errors[:field_154]).to be_blank
expect(parser.errors[:field_155]).to be_blank
expect(parser.errors[:field_156]).to be_blank
expect(parser.errors[:field_146]).to be_blank
expect(parser.errors[:field_147]).to be_blank
expect(parser.errors[:field_148]).to be_blank
end
end
@ -1187,9 +1179,9 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
it "adds errors to all referral fields" do
parser.valid?
expect(parser.errors[:field_116]).to be_present
expect(parser.errors[:field_154]).to be_present
expect(parser.errors[:field_155]).to be_present
expect(parser.errors[:field_156]).to be_present
expect(parser.errors[:field_146]).to be_present
expect(parser.errors[:field_147]).to be_present
expect(parser.errors[:field_148]).to be_present
end
end
@ -1199,34 +1191,34 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
it "adds errors to all referral fields" do
parser.valid?
expect(parser.errors[:field_116]).to be_present
expect(parser.errors[:field_154]).to be_present
expect(parser.errors[:field_155]).to be_present
expect(parser.errors[:field_156]).to be_present
expect(parser.errors[:field_146]).to be_present
expect(parser.errors[:field_147]).to be_present
expect(parser.errors[:field_148]).to be_present
end
end
context "and other fields are given" do
let(:attributes) { renewal_attributes.merge({ field_116: 1, field_154: 5, field_155: 1, field_152: 1 }) }
let(:attributes) { renewal_attributes.merge({ field_116: 1, field_146: 5, field_147: 1, field_144: 1 }) }
it "adds errors to all referral fields" do
parser.valid?
expect(parser.errors[:field_116]).to be_present
expect(parser.errors[:field_154]).to be_present
expect(parser.errors[:field_155]).to be_present
expect(parser.errors[:field_156]).to be_present
expect(parser.errors[:field_146]).to be_present
expect(parser.errors[:field_147]).to be_present
expect(parser.errors[:field_148]).to be_present
end
end
end
context "and is renewal" do
let(:attributes) { org_attributes.merge({ field_7: 1, field_116: 1, field_154: 5, field_155: 1, field_156: 1 }) }
let(:attributes) { org_attributes.merge({ field_7: 1, field_116: 1, field_146: 5, field_147: 1, field_148: 1 }) }
it "does not add an error for referral fields" do
parser.valid?
expect(parser.errors[:field_116]).to be_blank
expect(parser.errors[:field_154]).to be_blank
expect(parser.errors[:field_155]).to be_blank
expect(parser.errors[:field_156]).to be_blank
expect(parser.errors[:field_146]).to be_blank
expect(parser.errors[:field_147]).to be_blank
expect(parser.errors[:field_148]).to be_blank
end
end
end
@ -1239,141 +1231,141 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
context "and not renewal" do
let(:renewal_attributes) { org_attributes.merge({ field_7: nil }) }
context "and field_154 is valid and does not expect an answer for field_155" do
let(:attributes) { renewal_attributes.merge({ field_154: 5 }) }
context "and field_146 is valid and does not expect an answer for field_147" do
let(:attributes) { renewal_attributes.merge({ field_146: 5 }) }
it "does not add an error" do
parser.valid?
expect(parser.errors[:field_116]).to be_blank
expect(parser.errors[:field_154]).to be_blank
expect(parser.errors[:field_155]).to be_blank
expect(parser.errors[:field_156]).to be_blank
expect(parser.errors[:field_146]).to be_blank
expect(parser.errors[:field_147]).to be_blank
expect(parser.errors[:field_148]).to be_blank
end
context "and later fields are given" do
let(:attributes) { renewal_attributes.merge({ field_154: 5, field_155: 1, field_156: 1 }) }
let(:attributes) { renewal_attributes.merge({ field_146: 5, field_147: 1, field_148: 1 }) }
it "adds errors to all referral fields" do
parser.valid?
expect(parser.errors[:field_116]).to be_present
expect(parser.errors[:field_154]).to be_present
expect(parser.errors[:field_155]).to be_present
expect(parser.errors[:field_156]).to be_present
expect(parser.errors[:field_146]).to be_present
expect(parser.errors[:field_147]).to be_present
expect(parser.errors[:field_148]).to be_present
end
end
end
context "and field_154 is invalid" do
let(:attributes) { renewal_attributes.merge({ field_154: 1 }) } # LA option
context "and field_146 is invalid" do
let(:attributes) { renewal_attributes.merge({ field_146: 1 }) } # LA option
it "adds errors to all referral fields" do
parser.valid?
expect(parser.errors[:field_116]).to be_present
expect(parser.errors[:field_154]).to be_present
expect(parser.errors[:field_155]).to be_present
expect(parser.errors[:field_156]).to be_present
expect(parser.errors[:field_146]).to be_present
expect(parser.errors[:field_147]).to be_present
expect(parser.errors[:field_148]).to be_present
end
end
context "and field_154 is blank" do
let(:attributes) { renewal_attributes.merge({ field_154: nil }) }
context "and field_146 is blank" do
let(:attributes) { renewal_attributes.merge({ field_146: nil }) }
it "adds errors to all referral fields" do
parser.valid?
expect(parser.errors[:field_116]).to be_present
expect(parser.errors[:field_154]).to be_present
expect(parser.errors[:field_155]).to be_present
expect(parser.errors[:field_156]).to be_present
expect(parser.errors[:field_146]).to be_present
expect(parser.errors[:field_147]).to be_present
expect(parser.errors[:field_148]).to be_present
end
end
context "and field_154 is valid and expects an answer for field_155" do
let(:field_154_attributes) { renewal_attributes.merge({ field_154: 6 }) }
context "and field_146 is valid and expects an answer for field_147" do
let(:field_146_attributes) { renewal_attributes.merge({ field_146: 6 }) }
context "and field_155 is valid and does not expect an answer for field_156" do
let(:attributes) { field_154_attributes.merge({ field_155: 2 }) }
context "and field_147 is valid and does not expect an answer for field_148" do
let(:attributes) { field_146_attributes.merge({ field_147: 2 }) }
it "does not add an error" do
parser.valid?
expect(parser.errors[:field_116]).to be_blank
expect(parser.errors[:field_154]).to be_blank
expect(parser.errors[:field_155]).to be_blank
expect(parser.errors[:field_156]).to be_blank
expect(parser.errors[:field_146]).to be_blank
expect(parser.errors[:field_147]).to be_blank
expect(parser.errors[:field_148]).to be_blank
end
context "and later fields are given" do
let(:attributes) { field_154_attributes.merge({ field_155: 2, field_156: 1 }) }
let(:attributes) { field_146_attributes.merge({ field_147: 2, field_148: 1 }) }
it "adds errors to all referral fields" do
parser.valid?
expect(parser.errors[:field_116]).to be_present
expect(parser.errors[:field_154]).to be_present
expect(parser.errors[:field_155]).to be_present
expect(parser.errors[:field_156]).to be_present
expect(parser.errors[:field_146]).to be_present
expect(parser.errors[:field_147]).to be_present
expect(parser.errors[:field_148]).to be_present
end
end
end
context "and field_155 is invalid" do
let(:attributes) { field_154_attributes.merge({ field_155: 5 }) } # needs field_154 to be 7
context "and field_147 is invalid" do
let(:attributes) { field_146_attributes.merge({ field_147: 5 }) } # needs field_146 to be 7
it "adds errors to all referral fields" do
parser.valid?
expect(parser.errors[:field_116]).to be_present
expect(parser.errors[:field_154]).to be_present
expect(parser.errors[:field_155]).to be_present
expect(parser.errors[:field_156]).to be_present
expect(parser.errors[:field_146]).to be_present
expect(parser.errors[:field_147]).to be_present
expect(parser.errors[:field_148]).to be_present
end
end
context "and field_155 is blank" do
let(:attributes) { field_154_attributes.merge({ field_155: nil }) }
context "and field_147 is blank" do
let(:attributes) { field_146_attributes.merge({ field_147: nil }) }
it "adds errors to all referral fields" do
parser.valid?
expect(parser.errors[:field_116]).to be_present
expect(parser.errors[:field_154]).to be_present
expect(parser.errors[:field_155]).to be_present
expect(parser.errors[:field_156]).to be_present
expect(parser.errors[:field_146]).to be_present
expect(parser.errors[:field_147]).to be_present
expect(parser.errors[:field_148]).to be_present
end
end
context "and field_155 is valid and expects an answer for field_156" do
let(:field_155_attributes) { field_154_attributes.merge({ field_155: 1 }) }
context "and field_147 is valid and expects an answer for field_148" do
let(:field_147_attributes) { field_146_attributes.merge({ field_147: 1 }) }
context "and field_156 is valid" do
let(:attributes) { field_155_attributes.merge({ field_156: 1 }) }
context "and field_148 is valid" do
let(:attributes) { field_147_attributes.merge({ field_148: 1 }) }
it "does not add an error" do
parser.valid?
expect(parser.errors[:field_116]).to be_blank
expect(parser.errors[:field_154]).to be_blank
expect(parser.errors[:field_155]).to be_blank
expect(parser.errors[:field_156]).to be_blank
expect(parser.errors[:field_146]).to be_blank
expect(parser.errors[:field_147]).to be_blank
expect(parser.errors[:field_148]).to be_blank
end
end
context "and field_156 is invalid" do
let(:attributes) { field_155_attributes.merge({ field_156: 11 }) } # needs field_155 to be 7
context "and field_148 is invalid" do
let(:attributes) { field_147_attributes.merge({ field_148: 11 }) } # needs field_147 to be 7
it "adds errors to all referral fields" do
parser.valid?
expect(parser.errors[:field_116]).to be_present
expect(parser.errors[:field_154]).to be_present
expect(parser.errors[:field_155]).to be_present
expect(parser.errors[:field_156]).to be_present
expect(parser.errors[:field_146]).to be_present
expect(parser.errors[:field_147]).to be_present
expect(parser.errors[:field_148]).to be_present
end
end
context "and field_156 is blank" do
let(:attributes) { field_155_attributes.merge({ field_156: nil }) }
context "and field_148 is blank" do
let(:attributes) { field_147_attributes.merge({ field_148: nil }) }
it "adds errors to all referral fields" do
parser.valid?
expect(parser.errors[:field_116]).to be_present
expect(parser.errors[:field_154]).to be_present
expect(parser.errors[:field_155]).to be_present
expect(parser.errors[:field_156]).to be_present
expect(parser.errors[:field_146]).to be_present
expect(parser.errors[:field_147]).to be_present
expect(parser.errors[:field_148]).to be_present
end
end
end
@ -1381,14 +1373,14 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
end
context "and is renewal" do
let(:attributes) { org_attributes.merge({ field_7: 1, field_116: 1, field_154: 5, field_155: 1, field_156: 1 }) }
let(:attributes) { org_attributes.merge({ field_7: 1, field_116: 1, field_146: 5, field_147: 1, field_148: 1 }) }
it "does not add an error for referral fields" do
parser.valid?
expect(parser.errors[:field_116]).to be_blank
expect(parser.errors[:field_154]).to be_blank
expect(parser.errors[:field_155]).to be_blank
expect(parser.errors[:field_156]).to be_blank
expect(parser.errors[:field_146]).to be_blank
expect(parser.errors[:field_147]).to be_blank
expect(parser.errors[:field_148]).to be_blank
end
end
end
@ -2084,22 +2076,22 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
end
context "when a soft validation is triggered that relates both to fields that are and are not routed to" do
let(:attributes) { setup_section_params.merge({ field_78: "1", field_130: "M", field_131: "M", field_132: "M", field_138: 1, field_140: 1, field_142: 1 }) }
let(:attributes) { setup_section_params.merge({ field_78: "1", field_43: "M", field_49: "M", field_53: "M", field_130: 1, field_132: 1, field_134: 1 }) }
it "adds errors to fields that are routed to" do
parser.valid?
expect(parser.errors.where(:field_43, category: :soft_validation)).to be_present
expect(parser.errors.where(:field_130, category: :soft_validation)).to be_present
expect(parser.errors.where(:field_138, category: :soft_validation)).to be_present
expect(parser.errors.where(:field_131, category: :soft_validation)).to be_present
expect(parser.errors.where(:field_140, category: :soft_validation)).to be_present
expect(parser.errors.where(:field_49, category: :soft_validation)).to be_present
expect(parser.errors.where(:field_132, category: :soft_validation)).to be_present
end
it "does not add errors to fields that are not routed to" do
parser.valid?
expect(parser.errors.where(:field_133, category: :soft_validation)).not_to be_present
expect(parser.errors.where(:field_144, category: :soft_validation)).not_to be_present
expect(parser.errors.where(:field_134, category: :soft_validation)).not_to be_present
expect(parser.errors.where(:field_146, category: :soft_validation)).not_to be_present
expect(parser.errors.where(:field_57, category: :soft_validation)).not_to be_present
expect(parser.errors.where(:field_136, category: :soft_validation)).not_to be_present
expect(parser.errors.where(:field_61, category: :soft_validation)).not_to be_present
expect(parser.errors.where(:field_138, category: :soft_validation)).not_to be_present
end
end
@ -2501,7 +2493,7 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
end
end
describe "#sexN fields" do
describe "#sexrabN fields" do
let(:attributes) do
{
bulk_upload:,
@ -2517,14 +2509,14 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
end
it "sets value from correct mapping" do
expect(parser.log.sex1).to eql("F")
expect(parser.log.sex2).to eql("M")
expect(parser.log.sex3).to eql("X")
expect(parser.log.sex4).to eql("R")
expect(parser.log.sex5).to eql("F")
expect(parser.log.sex6).to eql("M")
expect(parser.log.sex7).to eql("X")
expect(parser.log.sex8).to eql("R")
expect(parser.log.sexrab1).to eql("F")
expect(parser.log.sexrab2).to eql("M")
expect(parser.log.sexrab3).to eql("X")
expect(parser.log.sexrab4).to eql("R")
expect(parser.log.sexrab5).to eql("F")
expect(parser.log.sexrab6).to eql("M")
expect(parser.log.sexrab7).to eql("X")
expect(parser.log.sexrab8).to eql("R")
end
end

3
spec/services/csv/lettings_log_csv_service_spec.rb

@ -241,7 +241,6 @@ RSpec.describe Csv::LettingsLogCsvService do
hhmemb: 4,
age1_known: 0,
age1: 35,
sex1: "F",
sexrab1: "F",
gender_same_as_sex1: 1,
ethnic_group: 0,
@ -252,7 +251,6 @@ RSpec.describe Csv::LettingsLogCsvService do
relat2: "P",
age2_known: 0,
age2: 32,
sex2: "M",
sexrab2: "M",
gender_same_as_sex2: 2,
gender_description2: "Non-binary",
@ -261,7 +259,6 @@ RSpec.describe Csv::LettingsLogCsvService do
details_known_4: 0,
relat4: "R",
age4_known: 1,
sex4: "R",
sexrab4: "R",
gender_same_as_sex4: 3,
ecstat4: 10,

18
spec/services/exports/lettings_log_export_service_spec.rb

@ -83,7 +83,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, assigned_to: user, age1: 35, sex1: "F", age2: 32, sex2: "M", propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", town_or_city: "London", tenancycode: "BZ737", startdate: Time.zone.local(2022, 2, 2, 10, 36, 49), voiddate: Time.zone.local(2019, 11, 3), mrcdate: Time.zone.local(2020, 5, 5, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4) }
let!(:lettings_log) { FactoryBot.create(:lettings_log, :completed, assigned_to: user, age1: 35, sex1: "F", sexrab1: nil, age2: 32, sex2: "M", sexrab2: nil, propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", town_or_city: "London", tenancycode: "BZ737", startdate: Time.zone.local(2022, 2, 2, 10, 36, 49), voiddate: Time.zone.local(2019, 11, 3), mrcdate: Time.zone.local(2020, 5, 5, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4) }
it "generates a ZIP export file with the expected filename" do
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args)
@ -127,7 +127,7 @@ RSpec.describe Exports::LettingsLogExportService do
end
context "and one lettings log with unknown user details is available for export" do
let!(:lettings_log) { FactoryBot.create(:lettings_log, :completed, details_known_2: 1, assigned_to: user, age1: 35, sex1: "F", propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", town_or_city: "London", tenancycode: "BZ737", startdate: Time.zone.local(2022, 2, 2, 10, 36, 49), voiddate: Time.zone.local(2019, 11, 3), mrcdate: Time.zone.local(2020, 5, 5, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4) }
let!(:lettings_log) { FactoryBot.create(:lettings_log, :completed, details_known_2: 1, assigned_to: user, age1: 35, sex1: "F", sexrab1: nil, propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", town_or_city: "London", tenancycode: "BZ737", startdate: Time.zone.local(2022, 2, 2, 10, 36, 49), voiddate: Time.zone.local(2019, 11, 3), mrcdate: Time.zone.local(2020, 5, 5, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4) }
def replace_person_details(export_file)
export_file.sub!("<age2>32</age2>", "<age2>-9</age2>")
@ -180,7 +180,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, assigned_to: user, age1: 35, sex1: "F", age2: 32, sex2: "M", uprn_known: 1, uprn: "100023336956", propcode: "123", postcode_full: "SE2 6RT", ppostcode_full: "SE2 6RT", tenancycode: "BZ737", startdate: Time.zone.local(2023, 4, 2, 10, 36, 49), voiddate: Time.zone.local(2021, 11, 3), mrcdate: Time.zone.local(2022, 5, 5, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4) }
let!(:lettings_log) { FactoryBot.create(:lettings_log, :completed, assigned_to: user, age1: 35, sex1: "F", sexrab1: nil, age2: 32, sex2: "M", sexrab2: nil, uprn_known: 1, uprn: "100023336956", propcode: "123", postcode_full: "SE2 6RT", ppostcode_full: "SE2 6RT", tenancycode: "BZ737", startdate: Time.zone.local(2023, 4, 2, 10, 36, 49), voiddate: Time.zone.local(2021, 11, 3), mrcdate: Time.zone.local(2022, 5, 5, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4) }
let(:expected_zip_filename) { "core_2023_2024_apr_mar_f0001_inc0001.zip" }
let(:expected_data_filename) { "core_2023_2024_apr_mar_f0001_inc0001_pt001.xml" }
let(:xml_export_file) { File.open("spec/fixtures/exports/general_needs_log_23_24.xml", "r:UTF-8") }
@ -400,7 +400,7 @@ RSpec.describe Exports::LettingsLogExportService do
end
context "and one lettings log with duplicate reference is available for export" do
let!(:lettings_log) { FactoryBot.create(:lettings_log, :completed, assigned_to: user, age1: 35, sex1: "F", age2: 32, sex2: "M", propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", town_or_city: "London", tenancycode: "BZ737", startdate: Time.zone.local(2022, 2, 2, 10, 36, 49), voiddate: Time.zone.local(2019, 11, 3), mrcdate: Time.zone.local(2020, 5, 5, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4, duplicate_set_id: 123) }
let!(:lettings_log) { FactoryBot.create(:lettings_log, :completed, assigned_to: user, age1: 35, sex1: "F", sexrab1: nil, age2: 32, sex2: "M", sexrab2: nil, propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", town_or_city: "London", tenancycode: "BZ737", startdate: Time.zone.local(2022, 2, 2, 10, 36, 49), voiddate: Time.zone.local(2019, 11, 3), mrcdate: Time.zone.local(2020, 5, 5, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4, duplicate_set_id: 123) }
def replace_duplicate_set_id(export_file)
export_file.sub!("<duplicate_set_id/>", "<duplicate_set_id>123</duplicate_set_id>")
@ -433,7 +433,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, 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", startdate: Time.zone.local(2024, 4, 2, 10, 36, 49), 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, assigned_to: user, age1: 35, sex1: "F", sexrab1: nil, age2: 32, sex2: "M", sexrab2: nil, ppostcode_full: "A1 1AA", nationality_all_group: 13, propcode: "123", postcode_full: "SE2 6RT", tenancycode: "BZ737", startdate: Time.zone.local(2024, 4, 2, 10, 36, 49), 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_2024_2025_apr_mar_f0001_inc0001.zip" }
let(:expected_data_filename) { "core_2024_2025_apr_mar_f0001_inc0001_pt001.xml" }
let(:xml_export_file) { File.open("spec/fixtures/exports/general_needs_log_24_25.xml", "r:UTF-8") }
@ -465,7 +465,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(2025, 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(2025, 4, 3), assigned_to: user, age1: 35, sex1: "F", sexrab1: nil, age2: 32, sex2: "M", sexrab2: nil, 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_2025_2026_apr_mar_f0001_inc0001.zip" }
let(:expected_data_filename) { "core_2025_2026_apr_mar_f0001_inc0001_pt001.xml" }
let(:xml_export_file) { File.open("spec/fixtures/exports/general_needs_log_25_26.xml", "r:UTF-8") }
@ -505,10 +505,10 @@ RSpec.describe Exports::LettingsLogExportService do
assigned_to: user,
age1: 35,
sexrab1: "F",
sex1: "F",
sex1: nil,
age2: 32,
sexrab2: "M",
sex2: "M",
sex2: nil,
ppostcode_full: "A1 1AA",
nationality_all_group: 13,
propcode: "123",
@ -643,7 +643,7 @@ RSpec.describe Exports::LettingsLogExportService do
let(:scheme) { FactoryBot.create(:scheme, :export, owning_organisation: organisation) }
let(:location) { FactoryBot.create(:location, :export, scheme:, startdate: Time.zone.local(2021, 4, 1), old_id: "1a") }
let(:lettings_log) { FactoryBot.create(:lettings_log, :completed, :export, :sh, scheme:, location:, assigned_to: user, updated_by: other_user, owning_organisation: organisation, age1: 35, sex1: "F", age2: 32, sex2: "M", startdate: Time.zone.local(2022, 2, 2, 10, 36, 49), voiddate: Time.zone.local(2019, 11, 3), mrcdate: Time.zone.local(2020, 5, 5, 10, 36, 49), underoccupation_benefitcap: 4, sheltered: 1) }
let(:lettings_log) { FactoryBot.create(:lettings_log, :completed, :export, :sh, scheme:, location:, assigned_to: user, updated_by: other_user, owning_organisation: organisation, age1: 35, sex1: "F", sexrab1: nil, age2: 32, sex2: "M", sexrab2: nil, startdate: Time.zone.local(2022, 2, 2, 10, 36, 49), voiddate: Time.zone.local(2019, 11, 3), mrcdate: Time.zone.local(2020, 5, 5, 10, 36, 49), underoccupation_benefitcap: 4, sheltered: 1) }
before do
lettings_log.postcode_full = nil

Loading…
Cancel
Save