Browse Source

Merge branch 'main' into CLDC-4215-4216-add-mortgage-used-dont-know-option

pull/3208/head
samyou-softwire 1 week ago
parent
commit
b1755f905f
  1. 15
      app/helpers/bulk_upload/sales_log_to_csv.rb
  2. 12
      app/models/derived_variables/lettings_log_variables.rb
  3. 2
      app/models/derived_variables/sales_log_variables.rb
  4. 15
      app/models/form/lettings/questions/layear.rb
  5. 2
      app/models/form/lettings/questions/waityear.rb
  6. 21
      app/models/form/sales/pages/buyer1_gender_same_as_sex.rb
  7. 23
      app/models/form/sales/pages/buyer2_gender_same_as_sex.rb
  8. 16
      app/models/form/sales/pages/person_gender_same_as_sex.rb
  9. 16
      app/models/form/sales/questions/gender_description.rb
  10. 33
      app/models/form/sales/questions/gender_same_as_sex.rb
  11. 3
      app/models/form/sales/subsections/household_characteristics.rb
  12. 21
      app/models/log.rb
  13. 4
      app/services/bulk_upload/sales/year2026/csv_parser.rb
  14. 61
      app/services/bulk_upload/sales/year2026/row_parser.rb
  15. 1
      app/services/csv/sales_log_csv_service.rb
  16. 2
      app/services/exports/sales_log_export_constants.rb
  17. 2
      app/views/form/guidance/_address_fallback.html.erb
  18. 16
      config/locales/forms/2026/lettings/household_characteristics.en.yml
  19. 91
      config/locales/forms/2026/sales/household_characteristics.en.yml
  20. 18
      db/migrate/20260220141000_add_gender_same_as_sex_fields_to_sales_logs.rb
  21. 12
      db/schema.rb
  22. 12
      spec/factories/sales_log.rb
  23. 12
      spec/fixtures/exports/sales_log_26_27.xml
  24. 18
      spec/fixtures/files/2026_27_sales_bulk_upload.csv
  25. 2
      spec/fixtures/files/lettings_log_csv_export_labels_25.csv
  26. 2
      spec/fixtures/files/lettings_log_csv_export_labels_26.csv
  27. 2
      spec/fixtures/files/lettings_log_csv_export_non_support_labels_25.csv
  28. 2
      spec/fixtures/files/lettings_log_csv_export_non_support_labels_26.csv
  29. 6
      spec/fixtures/files/sales_logs_csv_export_codes_26.csv
  30. 6
      spec/fixtures/files/sales_logs_csv_export_labels_26.csv
  31. 6
      spec/fixtures/files/sales_logs_csv_export_non_support_codes_26.csv
  32. 6
      spec/fixtures/files/sales_logs_csv_export_non_support_labels_26.csv
  33. 12
      spec/fixtures/variable_definitions/sales_download_26_27.csv
  34. 2
      spec/lib/tasks/log_variable_definitions_spec.rb
  35. 23
      spec/models/form/lettings/questions/layear_spec.rb
  36. 2
      spec/models/form/lettings/questions/waityear_spec.rb
  37. 31
      spec/models/form/sales/pages/buyer1_gender_same_as_sex_spec.rb
  38. 40
      spec/models/form/sales/pages/buyer2_gender_same_as_sex_spec.rb
  39. 105
      spec/models/form/sales/pages/person_gender_same_as_sex_spec.rb
  40. 163
      spec/models/form/sales/questions/gender_description_spec.rb
  41. 189
      spec/models/form/sales/questions/gender_same_as_sex_spec.rb
  42. 7
      spec/models/form/sales/subsections/household_characteristics_spec.rb
  43. 3
      spec/services/bulk_upload/sales/year2026/row_parser_spec.rb
  44. 8
      spec/services/csv/sales_log_csv_service_spec.rb
  45. 6
      yarn.lock

15
app/helpers/bulk_upload/sales_log_to_csv.rb

@ -664,8 +664,21 @@ class BulkUpload::SalesLogToCsv
log.mortlen,
log.extrabor,
log.deposit, # 120
log.mscharge,
log.buildheightclass, # 122
log.buildheightclass,
log.gender_same_as_sex1,
log.gender_description1,
log.gender_same_as_sex2,
log.gender_description2,
log.gender_same_as_sex3,
log.gender_description3,
log.gender_same_as_sex4,
log.gender_description4, # 130
log.gender_same_as_sex5,
log.gender_description5,
log.gender_same_as_sex6,
log.gender_description6, # 134
]
end

12
app/models/derived_variables/lettings_log_variables.rb

@ -450,18 +450,6 @@ private
3 if rent_type == 5
end
def clear_gender_description_unless_gender_not_same_as_sex!
# we do this as the gender same as sex page always contains the gender description box that's hidden
# default submit will send a "" for gender description. this ensure it's nil in this case
# as well as blanking it if the user writes it in mistakenly in bulk upload
(1..8).each do |person_index|
gender_same_as_sex = public_send("gender_same_as_sex#{person_index}")
if gender_same_as_sex.present? && gender_same_as_sex != 2
self["gender_description#{person_index}"] = nil
end
end
end
def set_checkbox_values!
form.questions.select { |q| q.type == "checkbox" }.each do |question|
options = question.answer_keys_without_dividers

2
app/models/derived_variables/sales_log_variables.rb

@ -102,6 +102,8 @@ module DerivedVariables::SalesLogVariables
self.mortlen_known = 0
end
clear_gender_description_unless_gender_not_same_as_sex! if form.start_year_2026_or_later?
set_encoded_derived_values!(DEPENDENCIES)
end

15
app/models/form/lettings/questions/layear.rb

@ -8,7 +8,20 @@ class Form::Lettings::Questions::Layear < ::Form::Question
end
def answer_options
if form.start_year_2024_or_later?
if form.start_year_2025_or_later?
{
"1" => { "value" => "Just moved to local authority area with this new let" },
"2" => { "value" => "Under 1 year" },
"7" => { "value" => "1 year but under 2 years" },
"8" => { "value" => "2 years but under 3 years" },
"9" => { "value" => "3 years but under 4 years" },
"10" => { "value" => "4 years but under 5 years" },
"11" => { "value" => "5 years but under 10 years" },
"12" => { "value" => "10 years or more" },
"divider" => { "value" => true },
"6" => { "value" => "Don’t know" },
}
elsif form.start_year_2024_or_later?
{
"1" => { "value" => "Just moved to local authority area with this new let" },
"2" => { "value" => "Less than 1 year" },

2
app/models/form/lettings/questions/waityear.rb

@ -11,7 +11,7 @@ class Form::Lettings::Questions::Waityear < ::Form::Question
if form.start_year_2025_or_later?
return {
"13" => { "value" => "Household not on the housing register (or waiting list) in this area" },
"2" => { "value" => "Less than 1 year" },
"2" => { "value" => "Under 1 year" },
"7" => { "value" => "1 year but under 2 years" },
"8" => { "value" => "2 years but under 3 years" },
"9" => { "value" => "3 years but under 4 years" },

21
app/models/form/sales/pages/buyer1_gender_same_as_sex.rb

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

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

@ -0,0 +1,23 @@
class Form::Sales::Pages::Buyer2GenderSameAsSex < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "buyer_2_gender_same_as_sex"
@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::GenderSameAsSex.new(nil, nil, self, person_index: 2, buyer: true),
Form::Sales::Questions::GenderDescription.new(nil, nil, self, person_index: 2),
]
end
end

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

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

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

@ -0,0 +1,16 @@
class Form::Sales::Questions::GenderDescription < ::Form::Question
def initialize(id, hsh, page, person_index:)
super(id, hsh, page)
@id = "gender_description#{person_index}"
@type = "text"
@check_answers_card_number = person_index
@person_index = person_index
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end
QUESTION_NUMBER_FROM_YEAR = { 2026 => 0 }.freeze
def derived?(log)
log.public_send("gender_same_as_sex#{@person_index}") != 2
end
end

33
app/models/form/sales/questions/gender_same_as_sex.rb

@ -0,0 +1,33 @@
class Form::Sales::Questions::GenderSameAsSex < ::Form::Question
def initialize(id, hsh, page, person_index:, buyer: false)
super(id, hsh, page)
@id = "gender_same_as_sex#{person_index}"
@type = "radio"
@check_answers_card_number = person_index
@conditional_for = { "gender_description#{person_index}" => [2] }
@inferred_check_answers_value = [{ "condition" => { "gender_same_as_sex#{person_index}" => 2 }, "value" => "No" }]
@person_index = person_index
@buyer = buyer
@copy_key = "sales.household_characteristics.gender_same_as_sex#{person_index}.#{buyer ? 'buyer' : 'person'}" if person_index == 2
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end
QUESTION_NUMBER_FROM_YEAR = { 2026 => 0 }.freeze
def answer_options
{
"1" => { "value" => "Yes" },
"2" => { "value" => "No, enter gender identity" },
"divider" => { "value" => true },
"3" => { "value" => "#{@buyer ? 'Buyer' : 'Person'} prefers not to say" },
}.freeze
end
def label_from_value(value, _log = nil, _user = nil)
return unless value
return "Prefers not to say" if value == 3
super
end
end

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

@ -25,6 +25,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection
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::Buyer1GenderSameAsSex.new(nil, nil, self) if form.start_year_2026_or_later?),
(Form::Sales::Pages::GenderIdentity1.new(nil, nil, self) unless form.start_year_2026_or_later?),
Form::Sales::Pages::Buyer1EthnicGroup.new(nil, nil, self),
Form::Sales::Pages::Buyer1EthnicBackgroundBlack.new(nil, nil, self),
@ -48,6 +49,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection
(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::Buyer2GenderSameAsSex.new(nil, nil, self) if form.start_year_2026_or_later?),
(Form::Sales::Pages::GenderIdentity2.new(nil, nil, self) unless form.start_year_2026_or_later?),
buyer_2_ethnicity_nationality_pages,
Form::Sales::Pages::Buyer2WorkingSituation.new(nil, nil, self),
@ -77,6 +79,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection
(Form::Sales::Pages::PartnerUnder16ValueCheck.new("age_#{person_index}_partner_under_16_value_check", nil, self, person_index:) if form.start_year_2024_or_later?),
(Form::Sales::Pages::PersonGenderIdentity.new("person_#{person_index}_gender_identity", nil, self, person_index:) unless form.start_year_2026_or_later?),
(Form::Sales::Pages::PersonSexRegisteredAtBirth.new("person_#{person_index}_sex_registered_at_birth", nil, self, person_index:) if form.start_year_2026_or_later?),
(Form::Sales::Pages::PersonGenderSameAsSex.new("person_#{person_index}_gender_same_as_sex", nil, self, person_index:) if form.start_year_2026_or_later?),
Form::Sales::Pages::PersonWorkingSituation.new("person_#{person_index}_working_situation", nil, self, person_index:),
Form::Sales::Pages::RetirementValueCheck.new("working_situation_#{person_index}_retirement_value_check", nil, self, person_index:),
(Form::Sales::Pages::NotRetiredValueCheck.new("working_situation_#{person_index}_not_retired_value_check", nil, self, person_index:) if form.start_year_2024_or_later?),

21
app/models/log.rb

@ -50,7 +50,13 @@ class Log < ApplicationRecord
scope :has_old_form_id, -> { where.not(old_form_id: nil) }
scope :imported_2023_with_old_form_id, -> { imported.filter_by_year(2023).has_old_form_id }
scope :imported_2023, -> { imported.filter_by_year(2023) }
scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org).or(where(managing_organisation: org)) }
# TODO: CLDC-4273: use .union in filter_by_organisation rather than raw SQL
scope :filter_by_organisation, lambda { |orgs, _user = nil|
owned = unscoped { where(owning_organisation: orgs).select(:id) }
managed = unscoped { where(managing_organisation: orgs).select(:id) }
where("#{table_name}.id = ANY(ARRAY(#{owned.to_sql} UNION #{managed.to_sql}))")
}
scope :filter_by_owning_organisation, ->(owning_organisation, _user = nil) { where(owning_organisation:) }
scope :filter_by_managing_organisation, ->(managing_organisation, _user = nil) { where(managing_organisation:) }
scope :filter_by_user_text_search, ->(param, user) { where(assigned_to: User.visible(user).search_by(param)) }
@ -339,6 +345,19 @@ class Log < ApplicationRecord
end
end
def clear_gender_description_unless_gender_not_same_as_sex!
# gender_description is always routed to (even when hidden on the page), so default submit will set it as ""
# This method ensures gender_description is cleared if gender is the same as sex
# This also has the benefit of clearing a mistakenly input gender_description in bulk upload if gender is the same as sex
max_person = lettings? ? 8 : 6
(1..max_person).each do |person_index|
gender_same_as_sex = public_send("gender_same_as_sex#{person_index}")
if gender_same_as_sex.present? && gender_same_as_sex != 2
self["gender_description#{person_index}"] = nil
end
end
end
private
# Handle logs that are older than previous collection start date

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

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

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

@ -137,6 +137,18 @@ class BulkUpload::Sales::Year2026::RowParser
field_121: "What are the total monthly leasehold charges for the property?",
field_122: "What is the building height classification?",
field_123: "Is the gender buyer 1 identifies with the same as their sex registered at birth?",
field_124: "If 'No', enter buyer 1's gender identity",
field_125: "Is the gender buyer/person 2 identifies with the same as their sex registered at birth?",
field_126: "If 'No', enter buyer/person 2's gender identity",
field_127: "Is the gender person 3 identifies with the same as their sex registered at birth?",
field_128: "If 'No', enter person 3's gender identity",
field_129: "Is the gender person 4 identifies with the same as their sex registered at birth?",
field_130: "If 'No', enter person 4's gender identity",
field_131: "Is the gender person 5 identifies with the same as their sex registered at birth?",
field_132: "If 'No', enter person 5's gender identity",
field_133: "Is the gender person 6 identifies with the same as their sex registered at birth?",
field_134: "If 'No', enter person 6's gender identity",
}.freeze
ERROR_BASE_KEY = "validations.sales.2026.bulk_upload".freeze
@ -303,6 +315,19 @@ class BulkUpload::Sales::Year2026::RowParser
attribute :field_121, :decimal
attribute :field_122, :integer
attribute :field_123, :integer
attribute :field_124, :string
attribute :field_125, :integer
attribute :field_126, :string
attribute :field_127, :integer
attribute :field_128, :string
attribute :field_129, :integer
attribute :field_130, :string
attribute :field_131, :integer
attribute :field_132, :string
attribute :field_133, :integer
attribute :field_134, :string
validates :field_1,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "sale completion date (day)."),
@ -839,6 +864,19 @@ private
sexrab5: %i[field_52],
sexrab6: %i[field_56],
buildheightclass: %i[field_122],
gender_same_as_sex1: %i[field_123],
gender_description1: %i[field_124],
gender_same_as_sex2: %i[field_125],
gender_description2: %i[field_126],
gender_same_as_sex3: %i[field_127],
gender_description3: %i[field_128],
gender_same_as_sex4: %i[field_129],
gender_description4: %i[field_130],
gender_same_as_sex5: %i[field_131],
gender_description5: %i[field_132],
gender_same_as_sex6: %i[field_133],
gender_description6: %i[field_134],
}
end
@ -875,6 +913,19 @@ private
attributes["sexrab6"] = field_56
attributes["buildheightclass"] = field_122
attributes["gender_same_as_sex1"] = field_123
attributes["gender_description1"] = field_124
attributes["gender_same_as_sex2"] = field_125
attributes["gender_description2"] = field_126
attributes["gender_same_as_sex3"] = field_127
attributes["gender_description3"] = field_128
attributes["gender_same_as_sex4"] = field_129
attributes["gender_description4"] = field_130
attributes["gender_same_as_sex5"] = field_131
attributes["gender_description5"] = field_132
attributes["gender_same_as_sex6"] = field_133
attributes["gender_description6"] = field_134
attributes["relat2"] = relationship_from_is_partner(field_34)
attributes["relat3"] = relationship_from_is_partner(field_42)
attributes["relat4"] = relationship_from_is_partner(field_46)
@ -1075,23 +1126,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_125.present? || field_126.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_127.present? || field_128.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_129.present? || field_130.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_131.present? || field_132.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_133.present? || field_134.present?
end
def relationship_from_is_partner(is_partner)

1
app/services/csv/sales_log_csv_service.rb

@ -115,6 +115,7 @@ module Csv
(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["gender_same_as_sex#{i}"] = { "refused_code" => "3", "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}" }

2
app/services/exports/sales_log_export_constants.rb

@ -161,5 +161,7 @@ module Exports::SalesLogExportConstants
(1..6).each do |index|
YEAR_2026_EXPORT_FIELDS << "SEXRAB#{index}"
YEAR_2026_EXPORT_FIELDS << "GENDER_SAME_AS_SEX#{index}"
YEAR_2026_EXPORT_FIELDS << "GENDER_DESCRIPTION#{index}"
end
end

2
app/views/form/guidance/_address_fallback.html.erb

@ -1,3 +1,3 @@
<div class="govuk-button-group">
<%= govuk_link_to "Clear address and search instead", address_search_input_path(@log.log_type, @log.id), class: "govuk-button govuk-button--secondary" %>
<%= govuk_link_to "Clear address and search by UPRN instead", address_search_input_path(@log.log_type, @log.id), class: "govuk-button govuk-button--secondary" %>
</div>

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

@ -32,7 +32,7 @@ en:
gender_same_as_sex1:
page_header: ""
check_answer_label: "Lead tenant’s gender identity same as registered at birth"
check_answer_label: "Lead tenant’s gender identity same as sex registered at birth"
check_answer_prompt: ""
hint_text: ""
question_text: "Is the gender the lead tenant identifies with the same as their sex registered at birth?"
@ -139,7 +139,7 @@ en:
gender_same_as_sex2:
page_header: ""
check_answer_label: "Person 2’s gender identity same as registered at birth"
check_answer_label: "Person 2’s gender identity same as sex registered at birth"
check_answer_prompt: ""
hint_text: ""
question_text: "Is the gender person 2 identifies with the same as their sex registered at birth?"
@ -194,7 +194,7 @@ en:
gender_same_as_sex3:
page_header: ""
check_answer_label: "Person 3’s gender identity same as registered at birth"
check_answer_label: "Person 3’s gender identity same as sex registered at birth"
check_answer_prompt: ""
hint_text: ""
question_text: "Is the gender person 3 identifies with the same as their sex registered at birth?"
@ -249,7 +249,7 @@ en:
gender_same_as_sex4:
page_header: ""
check_answer_label: "Person 4’s gender identity same as registered at birth"
check_answer_label: "Person 4’s gender identity same as sex registered at birth"
check_answer_prompt: ""
hint_text: ""
question_text: "Is the gender person 4 identifies with the same as their sex registered at birth?"
@ -304,7 +304,7 @@ en:
gender_same_as_sex5:
page_header: ""
check_answer_label: "Person 5’s gender identity same as registered at birth"
check_answer_label: "Person 5’s gender identity same as sex registered at birth"
check_answer_prompt: ""
hint_text: ""
question_text: "Is the gender person 5 identifies with the same as their sex registered at birth?"
@ -359,7 +359,7 @@ en:
gender_same_as_sex6:
page_header: ""
check_answer_label: "Person 6’s gender identity same as registered at birth"
check_answer_label: "Person 6’s gender identity same as sex registered at birth"
check_answer_prompt: ""
hint_text: ""
question_text: "Is the gender person 6 identifies with the same as their sex registered at birth?"
@ -414,7 +414,7 @@ en:
gender_same_as_sex7:
page_header: ""
check_answer_label: "Person 7’s gender identity same as registered at birth"
check_answer_label: "Person 7’s gender identity same as sex registered at birth"
check_answer_prompt: ""
hint_text: ""
question_text: "Is the gender person 7 identifies with the same as their sex registered at birth?"
@ -469,7 +469,7 @@ en:
gender_same_as_sex8:
page_header: ""
check_answer_label: "Person 8’s gender identity same as registered at birth"
check_answer_label: "Person 8’s gender identity same as sex registered at birth"
check_answer_prompt: ""
hint_text: ""
question_text: "Is the gender person 8 identifies with the same as their sex registered at birth?"

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

@ -23,6 +23,20 @@ en:
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?"
gender_same_as_sex1:
page_header: ""
check_answer_label: "Buyer 1's gender identity same as sex registered at birth"
check_answer_prompt: ""
hint_text: ""
question_text: "Is the gender buyer 1 identifies with the same as their sex registered at birth?"
gender_description1:
page_header: ""
check_answer_label: "Gender identity description"
check_answer_prompt: ""
hint_text: ""
question_text: "Enter gender identity"
ethnic_group:
page_header: ""
check_answer_label: "Buyer 1’s ethnic group"
@ -144,6 +158,27 @@ en:
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?"
gender_same_as_sex2:
buyer:
page_header: ""
check_answer_label: "Buyer 2's gender identity same as sex registered at birth"
check_answer_prompt: ""
hint_text: ""
question_text: "Is the gender buyer 2 identifies with the same as their sex registered at birth?"
person:
page_header: ""
check_answer_label: "Person 2's gender identity same as sex registered at birth"
check_answer_prompt: ""
hint_text: ""
question_text: "Is the gender person 2 identifies with the same as their sex registered at birth?"
gender_description2:
page_header: ""
check_answer_label: "Gender identity description"
check_answer_prompt: ""
hint_text: ""
question_text: "Enter gender identity"
ethnic_group2:
page_header: ""
check_answer_label: "Buyer 2’s ethnic group"
@ -273,6 +308,20 @@ en:
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?"
gender_same_as_sex3:
page_header: ""
check_answer_label: "Person 3's gender identity same as sex registered at birth"
check_answer_prompt: ""
hint_text: ""
question_text: "Is the gender person 3 identifies with the same as their sex registered at birth?"
gender_description3:
page_header: ""
check_answer_label: "Gender identity description"
check_answer_prompt: ""
hint_text: ""
question_text: "Enter gender identity"
ecstat3:
page_header: ""
check_answer_label: "Person 3’s working situation"
@ -314,6 +363,20 @@ en:
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?"
gender_same_as_sex4:
page_header: ""
check_answer_label: "Person 4's gender identity same as sex registered at birth"
check_answer_prompt: ""
hint_text: ""
question_text: "Is the gender person 4 identifies with the same as their sex registered at birth?"
gender_description4:
page_header: ""
check_answer_label: "Gender identity description"
check_answer_prompt: ""
hint_text: ""
question_text: "Enter gender identity"
ecstat4:
page_header: ""
check_answer_label: "Person 4’s working situation"
@ -355,6 +418,20 @@ en:
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?"
gender_same_as_sex5:
page_header: ""
check_answer_label: "Person 5's gender identity same as sex registered at birth"
check_answer_prompt: ""
hint_text: ""
question_text: "Is the gender person 5 identifies with the same as their sex registered at birth?"
gender_description5:
page_header: ""
check_answer_label: "Gender identity description"
check_answer_prompt: ""
hint_text: ""
question_text: "Enter gender identity"
ecstat5:
page_header: ""
check_answer_label: "Person 5’s working situation"
@ -396,6 +473,20 @@ en:
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?"
gender_same_as_sex6:
page_header: ""
check_answer_label: "Person 6's gender identity same as sex registered at birth"
check_answer_prompt: ""
hint_text: ""
question_text: "Is the gender person 6 identifies with the same as their sex registered at birth?"
gender_description6:
page_header: ""
check_answer_label: "Gender identity description"
check_answer_prompt: ""
hint_text: ""
question_text: "Enter gender identity"
ecstat6:
page_header: ""
check_answer_label: "Person 6’s working situation"

18
db/migrate/20260220141000_add_gender_same_as_sex_fields_to_sales_logs.rb

@ -0,0 +1,18 @@
class AddGenderSameAsSexFieldsToSalesLogs < ActiveRecord::Migration[7.0]
def change
change_table :sales_logs, bulk: true do |t|
t.integer :gender_same_as_sex1
t.integer :gender_same_as_sex2
t.integer :gender_same_as_sex3
t.integer :gender_same_as_sex4
t.integer :gender_same_as_sex5
t.integer :gender_same_as_sex6
t.string :gender_description1
t.string :gender_description2
t.string :gender_description3
t.string :gender_description4
t.string :gender_description5
t.string :gender_description6
end
end
end

12
db/schema.rb

@ -826,6 +826,18 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_25_135309) do
t.string "sexrab6"
t.integer "mortlen_known"
t.integer "buildheightclass"
t.integer "gender_same_as_sex1"
t.integer "gender_same_as_sex2"
t.integer "gender_same_as_sex3"
t.integer "gender_same_as_sex4"
t.integer "gender_same_as_sex5"
t.integer "gender_same_as_sex6"
t.string "gender_description1"
t.string "gender_description2"
t.string "gender_description3"
t.string "gender_description4"
t.string "gender_description5"
t.string "gender_description6"
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"

12
spec/factories/sales_log.rb

@ -86,6 +86,7 @@ FactoryBot.define do
age1 { Faker::Number.within(range: 27..45) }
sexrab1 { %w[F M R].sample }
sex1 { %w[F M X R].sample }
gender_same_as_sex1 { 1 }
national { 18 }
buy1livein { 1 }
relat2 { "P" }
@ -98,6 +99,8 @@ FactoryBot.define do
ethnic_group { 17 }
sexrab2 { %w[F M R].sample }
sex2 { "X" }
gender_same_as_sex2 { 2 }
gender_description2 { Faker::Gender.type }
buy2livein { "1" }
ecstat1 { "1" }
ecstat2 { "1" }
@ -131,12 +134,17 @@ FactoryBot.define do
prevshared { 2 }
sexrab3 { %w[F M R].sample }
sex3 { %w[F M X R].sample }
gender_same_as_sex3 { 1 }
sexrab4 { %w[F M R].sample }
sex4 { %w[F M X R].sample }
gender_same_as_sex4 { 2 }
gender_description4 { Faker::Gender.type }
sexrab5 { %w[F M R].sample }
sex5 { %w[F M X R].sample }
gender_same_as_sex5 { 3 }
sexrab6 { %w[F M R].sample }
sex6 { %w[F M X R].sample }
gender_same_as_sex6 { 3 }
mortgage { 20_000 }
ecstat3 { 9 }
ecstat4 { 3 }
@ -291,6 +299,7 @@ FactoryBot.define do
age1 { 27 }
sexrab1 { "F" }
sex1 { "F" }
gender_same_as_sex1 { 1 }
national { 18 }
buy1livein { 1 }
relat2 { "P" }
@ -303,6 +312,8 @@ FactoryBot.define do
ethnic_group { 17 }
sexrab2 { "R" }
sex2 { "X" }
gender_same_as_sex2 { 2 }
gender_description2 { "Non-binary" }
buy2livein { "1" }
ecstat1 { "1" }
ecstat2 { "1" }
@ -311,6 +322,7 @@ FactoryBot.define do
details_known_3 { 1 }
age3_known { 0 }
age3 { 14 }
gender_same_as_sex3 { 3 }
details_known_4 { 1 }
age4_known { 0 }
age4 { 18 }

12
spec/fixtures/exports/sales_log_26_27.xml vendored

@ -89,6 +89,18 @@
<NUMSTAIR/>
<MRENTPRESTAIRCASING/>
<BUILDHEIGHTCLASS>2</BUILDHEIGHTCLASS>
<GENDER_SAME_AS_SEX1>1</GENDER_SAME_AS_SEX1>
<GENDER_DESCRIPTION1/>
<GENDER_SAME_AS_SEX2>2</GENDER_SAME_AS_SEX2>
<GENDER_DESCRIPTION2>Non-binary</GENDER_DESCRIPTION2>
<GENDER_SAME_AS_SEX3>3</GENDER_SAME_AS_SEX3>
<GENDER_DESCRIPTION3/>
<GENDER_SAME_AS_SEX4/>
<GENDER_DESCRIPTION4/>
<GENDER_SAME_AS_SEX5/>
<GENDER_DESCRIPTION5/>
<GENDER_SAME_AS_SEX6/>
<GENDER_DESCRIPTION6/>
<DAY>1</DAY>
<MONTH>4</MONTH>
<YEAR>2026</YEAR>

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

File diff suppressed because one or more lines are too long

2
spec/fixtures/files/lettings_log_csv_export_labels_25.csv vendored

File diff suppressed because one or more lines are too long

2
spec/fixtures/files/lettings_log_csv_export_labels_26.csv vendored

File diff suppressed because one or more lines are too long

2
spec/fixtures/files/lettings_log_csv_export_non_support_labels_25.csv vendored

File diff suppressed because one or more lines are too long

2
spec/fixtures/files/lettings_log_csv_export_non_support_labels_26.csv vendored

File diff suppressed because one or more lines are too long

6
spec/fixtures/files/sales_logs_csv_export_codes_26.csv vendored

File diff suppressed because one or more lines are too long

6
spec/fixtures/files/sales_logs_csv_export_labels_26.csv vendored

File diff suppressed because one or more lines are too long

6
spec/fixtures/files/sales_logs_csv_export_non_support_codes_26.csv vendored

File diff suppressed because one or more lines are too long

6
spec/fixtures/files/sales_logs_csv_export_non_support_labels_26.csv vendored

File diff suppressed because one or more lines are too long

12
spec/fixtures/variable_definitions/sales_download_26_27.csv vendored

@ -5,3 +5,15 @@ 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?
buildheightclass, What is the building height classification?
gender_same_as_sex1,Is the gender buyer 1 identifies with the same as their sex registered at birth?
gender_description1,If 'No', enter buyer 1's gender identity
gender_same_as_sex2,Is the gender buyer/person 2 identifies with the same as their sex registered at birth?
gender_description2,If 'No', enter buyer/person 2's gender identity
gender_same_as_sex3,Is the gender person 3 identifies with the same as their sex registered at birth?
gender_description3,If 'No', enter person 3's gender identity
gender_same_as_sex4,Is the gender person 4 identifies with the same as their sex registered at birth?
gender_description4,If 'No', enter person 4's gender identity
gender_same_as_sex5,Is the gender person 5 identifies with the same as their sex registered at birth?
gender_description5,If 'No', enter person 5's gender identity
gender_same_as_sex6,Is the gender person 6 identifies with the same as their sex registered at birth?
gender_description6,If 'No', enter person 6's gender identity

1 sexrab1 sexrab1,What was buyer 1's sex at birth? What was buyer 1's sex at birth?
5 sexrab5 sexrab5,What was person 5's sex at birth? What was person 5's sex at birth?
6 sexrab6 sexrab6,What was person 6's sex at birth? What was person 6's sex at birth?
7 buildheightclass buildheightclass, What is the building height classification? What is the building height classification?
8 gender_same_as_sex1,Is the gender buyer 1 identifies with the same as their sex registered at birth?
9 gender_description1,If 'No', enter buyer 1's gender identity
10 gender_same_as_sex2,Is the gender buyer/person 2 identifies with the same as their sex registered at birth?
11 gender_description2,If 'No', enter buyer/person 2's gender identity
12 gender_same_as_sex3,Is the gender person 3 identifies with the same as their sex registered at birth?
13 gender_description3,If 'No', enter person 3's gender identity
14 gender_same_as_sex4,Is the gender person 4 identifies with the same as their sex registered at birth?
15 gender_description4,If 'No', enter person 4's gender identity
16 gender_same_as_sex5,Is the gender person 5 identifies with the same as their sex registered at birth?
17 gender_description5,If 'No', enter person 5's gender identity
18 gender_same_as_sex6,Is the gender person 6 identifies with the same as their sex registered at birth?
19 gender_description6,If 'No', enter person 6's gender identity

2
spec/lib/tasks/log_variable_definitions_spec.rb

@ -6,7 +6,7 @@ RSpec.describe "log_variable_definitions" do
subject(:task) { Rake::Task["data_import:add_variable_definitions"] }
let(:path) { "spec/fixtures/variable_definitions" }
let(:total_variable_definitions_count) { 451 }
let(:total_variable_definitions_count) { 463 }
before do
Rake.application.rake_require("tasks/log_variable_definitions")

23
spec/models/form/lettings/questions/layear_spec.rb

@ -10,7 +10,7 @@ RSpec.describe Form::Lettings::Questions::Layear, type: :model do
let(:form) { instance_double(Form, start_date: Time.zone.local(2023, 4, 1)) }
before do
allow(form).to receive(:start_year_2024_or_later?).and_return(false)
allow(form).to receive_messages(start_year_2024_or_later?: false, start_year_2025_or_later?: false)
allow(page).to receive(:subsection).and_return(subsection)
allow(subsection).to receive(:form).and_return(form)
end
@ -68,6 +68,27 @@ RSpec.describe Form::Lettings::Questions::Layear, type: :model do
end
end
context "with 2025/26 form" do
before do
allow(form).to receive(:start_year_2025_or_later?).and_return(true)
end
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"1" => { "value" => "Just moved to local authority area with this new let" },
"2" => { "value" => "Under 1 year" },
"7" => { "value" => "1 year but under 2 years" },
"8" => { "value" => "2 years but under 3 years" },
"9" => { "value" => "3 years but under 4 years" },
"10" => { "value" => "4 years but under 5 years" },
"11" => { "value" => "5 years but under 10 years" },
"12" => { "value" => "10 years or more" },
"divider" => { "value" => true },
"6" => { "value" => "Don’t know" },
})
end
end
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(0)
end

2
spec/models/form/lettings/questions/waityear_spec.rb

@ -85,7 +85,7 @@ RSpec.describe Form::Lettings::Questions::Waityear, type: :model do
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"13" => { "value" => "Household not on the housing register (or waiting list) in this area" },
"2" => { "value" => "Less than 1 year" },
"2" => { "value" => "Under 1 year" },
"7" => { "value" => "1 year but under 2 years" },
"8" => { "value" => "2 years but under 3 years" },
"9" => { "value" => "3 years but under 4 years" },

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

@ -0,0 +1,31 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::Buyer1GenderSameAsSex, type: :model do
include CollectionTimeHelper
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: current_collection_start_date)) }
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[gender_same_as_sex1 gender_description1])
end
it "has the correct id" do
expect(page.id).to eq("buyer_1_gender_same_as_sex")
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

40
spec/models/form/sales/pages/buyer2_gender_same_as_sex_spec.rb

@ -0,0 +1,40 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::Buyer2GenderSameAsSex, type: :model do
include CollectionTimeHelper
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: current_collection_start_date)) }
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[gender_same_as_sex2 gender_description2])
end
it "has the correct id" do
expect(page.id).to eq("buyer_2_gender_same_as_sex")
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

105
spec/models/form/sales/pages/person_gender_same_as_sex_spec.rb

@ -0,0 +1,105 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::PersonGenderSameAsSex, type: :model do
include CollectionTimeHelper
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: current_collection_start_date)) }
let(:person_index) { 2 }
let(:page_id) { "person_2_gender_same_as_sex" }
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_gender_same_as_sex" }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[gender_same_as_sex2 gender_description2])
end
it "has the correct id" do
expect(page.id).to eq("person_2_gender_same_as_sex")
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_gender_same_as_sex" }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[gender_same_as_sex3 gender_description3])
end
it "has the correct id" do
expect(page.id).to eq("person_3_gender_same_as_sex")
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_gender_same_as_sex" }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[gender_same_as_sex4 gender_description4])
end
it "has the correct id" do
expect(page.id).to eq("person_4_gender_same_as_sex")
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_gender_same_as_sex" }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[gender_same_as_sex5 gender_description5])
end
it "has the correct id" do
expect(page.id).to eq("person_5_gender_same_as_sex")
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_gender_same_as_sex" }
it "has correct questions" do
expect(page.questions.map(&:id)).to eq(%w[gender_same_as_sex6 gender_description6])
end
it "has the correct id" do
expect(page.id).to eq("person_6_gender_same_as_sex")
end
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "details_known_6" => 1 }])
end
end
end

163
spec/models/form/sales/questions/gender_description_spec.rb

@ -0,0 +1,163 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::GenderDescription, type: :model do
include CollectionTimeHelper
subject(:question) { described_class.new(question_id, question_definition, page, person_index:) }
let(:question_id) { nil }
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: current_collection_start_date) }
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("text")
end
context "when person 1" do
let(:person_index) { 1 }
it "has the correct id" do
expect(question.id).to eq("gender_description1")
end
it "has expected 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
context "when gender_same_as_sex1 is 'Yes'" do
let(:log) { build(:sales_log, gender_same_as_sex1: 1) }
it "is marked as derived" do
expect(question.derived?(log)).to be true
end
end
context "when gender_same_as_sex1 is 'No'" do
let(:log) { build(:sales_log, gender_same_as_sex1: 2) }
it "is not marked as derived" do
expect(question.derived?(log)).to be false
end
end
context "when gender_same_as_sex1 is 'Prefers not to say'" do
let(:log) { build(:sales_log, gender_same_as_sex1: 3) }
it "is marked as derived" do
expect(question.derived?(log)).to be true
end
end
end
context "when person 2" do
let(:person_index) { 2 }
it "has the correct id" do
expect(question.id).to eq("gender_description2")
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
context "when gender_same_as_sex2 is 'Yes'" do
let(:log) { build(:sales_log, gender_same_as_sex2: 1) }
it "is marked as derived" do
expect(question.derived?(log)).to be true
end
end
context "when gender_same_as_sex2 is 'No'" do
let(:log) { build(:sales_log, gender_same_as_sex2: 2) }
it "is not marked as derived" do
expect(question.derived?(log)).to be false
end
end
end
context "when person 3" do
let(:person_index) { 3 }
it "has the correct id" do
expect(question.id).to eq("gender_description3")
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(:person_index) { 4 }
it "has the correct id" do
expect(question.id).to eq("gender_description4")
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(:person_index) { 5 }
it "has the correct id" do
expect(question.id).to eq("gender_description5")
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(:person_index) { 6 }
it "has the correct id" do
expect(question.id).to eq("gender_description6")
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

189
spec/models/form/sales/questions/gender_same_as_sex_spec.rb

@ -0,0 +1,189 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::GenderSameAsSex, type: :model do
include CollectionTimeHelper
subject(:question) { described_class.new(question_id, question_definition, page, person_index:) }
let(:question_id) { nil }
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: current_collection_start_date) }
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
context "when buyer is false (default)" do
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"1" => { "value" => "Yes" },
"2" => { "value" => "No, enter gender identity" },
"divider" => { "value" => true },
"3" => { "value" => "Person prefers not to say" },
})
end
end
context "when buyer is true" do
subject(:question) { described_class.new(question_id, question_definition, page, person_index:, buyer: true) }
it "has the correct answer_options" do
expect(question.answer_options).to eq({
"1" => { "value" => "Yes" },
"2" => { "value" => "No, enter gender identity" },
"divider" => { "value" => true },
"3" => { "value" => "Buyer prefers not to say" },
})
end
end
it "returns correct label_from_value for 'Prefers not to say'" do
expect(question.label_from_value(3)).to eq("Prefers not to say")
end
it "returns nil label_from_value for nil" do
expect(question.label_from_value(nil)).to be_nil
end
context "when person 1 (buyer)" do
subject(:question) { described_class.new(question_id, question_definition, page, person_index: 1, buyer: true) }
let(:person_index) { 1 }
it "has the correct id" do
expect(question.id).to eq("gender_same_as_sex1")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(1)
end
it "has the correct conditional_for" do
expect(question.conditional_for).to eq({ "gender_description1" => [2] })
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to eq([{ "condition" => { "gender_same_as_sex1" => 2 }, "value" => "No" }])
end
it "has the correct answer_options with Buyer label" do
expect(question.answer_options["3"]).to eq({ "value" => "Buyer prefers not to say" })
end
end
context "when person 2" do
let(:person_index) { 2 }
it "has the correct id" do
expect(question.id).to eq("gender_same_as_sex2")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(2)
end
it "has the correct conditional_for" do
expect(question.conditional_for).to eq({ "gender_description2" => [2] })
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to eq([{ "condition" => { "gender_same_as_sex2" => 2 }, "value" => "No" }])
end
end
context "when person 3" do
let(:person_index) { 3 }
it "has the correct id" do
expect(question.id).to eq("gender_same_as_sex3")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(3)
end
it "has the correct conditional_for" do
expect(question.conditional_for).to eq({ "gender_description3" => [2] })
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to eq([{ "condition" => { "gender_same_as_sex3" => 2 }, "value" => "No" }])
end
end
context "when person 4" do
let(:person_index) { 4 }
it "has the correct id" do
expect(question.id).to eq("gender_same_as_sex4")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(4)
end
it "has the correct conditional_for" do
expect(question.conditional_for).to eq({ "gender_description4" => [2] })
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to eq([{ "condition" => { "gender_same_as_sex4" => 2 }, "value" => "No" }])
end
end
context "when person 5" do
let(:person_index) { 5 }
it "has the correct id" do
expect(question.id).to eq("gender_same_as_sex5")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(5)
end
it "has the correct conditional_for" do
expect(question.conditional_for).to eq({ "gender_description5" => [2] })
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to eq([{ "condition" => { "gender_same_as_sex5" => 2 }, "value" => "No" }])
end
end
context "when person 6" do
let(:person_index) { 6 }
it "has the correct id" do
expect(question.id).to eq("gender_same_as_sex6")
end
it "has expected check answers card number" do
expect(question.check_answers_card_number).to eq(6)
end
it "has the correct conditional_for" do
expect(question.conditional_for).to eq({ "gender_description6" => [2] })
end
it "has the correct inferred_check_answers_value" do
expect(question.inferred_check_answers_value).to eq([{ "condition" => { "gender_same_as_sex6" => 2 }, "value" => "No" }])
end
end
end

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

@ -407,6 +407,7 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
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_same_as_sex
buyer_1_ethnic_group
buyer_1_ethnic_background_black
buyer_1_ethnic_background_asian
@ -427,6 +428,7 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
age_2_buyer_retirement_value_check
age_2_buyer_not_retired_value_check
buyer_2_sex_registered_at_birth
buyer_2_gender_same_as_sex
buyer_2_ethnic_group
buyer_2_ethnic_background_black
buyer_2_ethnic_background_asian
@ -451,6 +453,7 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
age_2_not_retired_value_check
age_2_partner_under_16_value_check
person_2_sex_registered_at_birth
person_2_gender_same_as_sex
person_2_working_situation
working_situation_2_retirement_value_check
working_situation_2_not_retired_value_check
@ -463,6 +466,7 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
age_3_not_retired_value_check
age_3_partner_under_16_value_check
person_3_sex_registered_at_birth
person_3_gender_same_as_sex
person_3_working_situation
working_situation_3_retirement_value_check
working_situation_3_not_retired_value_check
@ -475,6 +479,7 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
age_4_not_retired_value_check
age_4_partner_under_16_value_check
person_4_sex_registered_at_birth
person_4_gender_same_as_sex
person_4_working_situation
working_situation_4_retirement_value_check
working_situation_4_not_retired_value_check
@ -487,6 +492,7 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
age_5_not_retired_value_check
age_5_partner_under_16_value_check
person_5_sex_registered_at_birth
person_5_gender_same_as_sex
person_5_working_situation
working_situation_5_retirement_value_check
working_situation_5_not_retired_value_check
@ -499,6 +505,7 @@ RSpec.describe Form::Sales::Subsections::HouseholdCharacteristics, type: :model
age_6_not_retired_value_check
age_6_partner_under_16_value_check
person_6_sex_registered_at_birth
person_6_gender_same_as_sex
person_6_working_situation
working_situation_6_retirement_value_check
working_situation_6_not_retired_value_check

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

@ -113,6 +113,9 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do
field_106: "2023",
field_110: "900",
field_122: "1",
field_123: "1",
field_125: "2",
field_126: "Non-binary",
}
end

8
spec/services/csv/sales_log_csv_service_spec.rb

@ -38,6 +38,14 @@ RSpec.describe Csv::SalesLogCsvService do
age6: nil,
ecstat6: nil,
relat6: nil,
gender_same_as_sex1: 1,
gender_same_as_sex2: 2,
gender_description2: "Nonbinary",
gender_same_as_sex3: 1,
gender_same_as_sex4: 2,
gender_description4: "Genderfluid",
gender_same_as_sex5: 3,
gender_same_as_sex6: nil,
sexrab6: nil,
sex6: nil,
town_or_city: "Town or city",

6
yarn.lock

@ -2880,9 +2880,9 @@ ignore@^5.1.9:
integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==
immutable@^4.0.0:
version "4.3.7"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.7.tgz#c70145fc90d89fb02021e65c84eb0226e4e5a381"
integrity sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==
version "4.3.8"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.8.tgz#02d183c7727fb2bb1d5d0380da0d779dce9296a7"
integrity sha512-d/Ld9aLbKpNwyl0KiM2CT1WYvkitQ1TSvmRtkcV8FKStiDoA7Slzgjmb/1G2yhKM1p0XeNOieaTbFZmU1d3Xuw==
import-fresh@^3.2.1, import-fresh@^3.3.0:
version "3.3.0"

Loading…
Cancel
Save