diff --git a/app/services/bulk_upload/lettings/year2026/row_parser.rb b/app/services/bulk_upload/lettings/year2026/row_parser.rb index 834c620bb..96b7a23d7 100644 --- a/app/services/bulk_upload/lettings/year2026/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2026/row_parser.rb @@ -688,9 +688,9 @@ private def validate_uprn_exists_if_any_key_address_fields_are_blank if field_18.blank? && !key_address_fields_provided? %i[field_19 field_21 field_23 field_24].each do |field| - errors.add(field, I18n.t("#{ERROR_BASE_KEY}.address.not_answered")) if send(field).blank? + errors.add(field, with_confidential_scheme_suffix(I18n.t("#{ERROR_BASE_KEY}.address.not_answered"))) if send(field).blank? end - errors.add(:field_18, I18n.t("#{ERROR_BASE_KEY}.address.not_answered", question: "UPRN.")) + errors.add(:field_18, with_confidential_scheme_suffix(I18n.t("#{ERROR_BASE_KEY}.address.not_answered", question: "UPRN."))) end end @@ -701,23 +701,27 @@ private def validate_address_fields if field_18.blank? || log.errors.attribute_names.include?(:uprn) if field_19.blank? && errors[:field_19].blank? - errors.add(:field_19, I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "address line 1.")) + errors.add(:field_19, with_confidential_scheme_suffix(I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "address line 1."))) end if field_21.blank? && errors[:field_21].blank? - errors.add(:field_21, I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "town or city.")) + errors.add(:field_21, with_confidential_scheme_suffix(I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "town or city."))) end if field_23.blank? && errors[:field_23].blank? - errors.add(:field_23, I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "part 1 of postcode.")) + errors.add(:field_23, with_confidential_scheme_suffix(I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "part 1 of postcode."))) end if field_24.blank? && errors[:field_24].blank? - errors.add(:field_24, I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "part 2 of postcode.")) + errors.add(:field_24, with_confidential_scheme_suffix(I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "part 2 of postcode."))) end end end + def with_confidential_scheme_suffix(message) + "#{message} #{I18n.t("#{ERROR_BASE_KEY}.address.confidential_scheme_suffix")}" + end + def validate_incomplete_soft_validations routed_to_soft_validation_questions = log.form.questions.filter { |q| q.type == "interruption_screen" && q.page.routed_to?(log, nil) }.compact routed_to_soft_validation_questions.each do |question| diff --git a/config/locales/validations/lettings/2026/bulk_upload.en.yml b/config/locales/validations/lettings/2026/bulk_upload.en.yml index 1206b7ad6..52bb3fa77 100644 --- a/config/locales/validations/lettings/2026/bulk_upload.en.yml +++ b/config/locales/validations/lettings/2026/bulk_upload.en.yml @@ -53,6 +53,7 @@ en: one: "There is a possible match for this address which doesn't look right. Check the address data in your CSV file is correct and complete, or confirm the address in the service." multiple: "There are multiple matches for this address. Check the address data in your CSV file is correct and complete, or select the correct address in the service." not_answered: "Enter either the UPRN or the full address." + confidential_scheme_suffix: "If your letting is in a confidential scheme, check the scheme you chose. A coordinator can edit a scheme to confidential on the ‘Schemes' page." nationality: invalid: "Select a valid nationality." charges: diff --git a/spec/models/form/lettings/questions/address_search_spec.rb b/spec/models/form/lettings/questions/address_search_spec.rb index c775839d5..64e82b744 100644 --- a/spec/models/form/lettings/questions/address_search_spec.rb +++ b/spec/models/form/lettings/questions/address_search_spec.rb @@ -65,36 +65,6 @@ RSpec.describe Form::Lettings::Questions::AddressSearch, type: :model do end end - describe "#unanswered_error_message" do - context "when the log is supported housing" do - let(:log) { build(:lettings_log, needstype: 2) } - - it "returns the confidential-scheme guidance message" do - expect(question.unanswered_error_message(log)).to eq( - "You must enter address. If your letting is in a confidential scheme, please check the scheme you chose in the ‘Set up this lettings log’ section. If a scheme needs updating to mark it as confidential, a CORE coordinator in your organisation can do this.", - ) - end - end - - context "when the log is general needs" do - let(:log) { build(:lettings_log, needstype: 1) } - - it "returns the default unanswered message" do - expect(question.unanswered_error_message(log)).to eq( - I18n.t("validations.not_answered", question: question.error_display_label.downcase), - ) - end - end - - context "when no log is given" do - it "returns the default unanswered message" do - expect(question.unanswered_error_message).to eq( - I18n.t("validations.not_answered", question: question.error_display_label.downcase), - ) - end - end - end - describe "get_extra_check_answer_value" do context "when address is not present" do let(:log) { build(:lettings_log, manual_address_entry_selected: false) } @@ -128,4 +98,34 @@ RSpec.describe Form::Lettings::Questions::AddressSearch, type: :model do end end end + + describe "#unanswered_error_message" do + context "when the log is supported housing" do + let(:log) { build(:lettings_log, needstype: 2) } + + it "returns the confidential-scheme guidance message" do + expect(question.unanswered_error_message(log)).to eq( + "You must enter address. If your letting is in a confidential scheme, please check the scheme you chose in the ‘Set up this lettings log’ section. If a scheme needs updating to mark it as confidential, a CORE coordinator in your organisation can do this.", + ) + end + end + + context "when the log is general needs" do + let(:log) { build(:lettings_log, needstype: 1) } + + it "returns the default unanswered message" do + expect(question.unanswered_error_message(log)).to eq( + I18n.t("validations.not_answered", question: question.error_display_label.downcase), + ) + end + end + + context "when no log is given" do + it "returns the default unanswered message" do + expect(question.unanswered_error_message).to eq( + I18n.t("validations.not_answered", question: question.error_display_label.downcase), + ) + end + end + end end diff --git a/spec/services/bulk_upload/lettings/year2026/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2026/row_parser_spec.rb index 7d44d3544..e7e552ca4 100644 --- a/spec/services/bulk_upload/lettings/year2026/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2026/row_parser_spec.rb @@ -16,6 +16,7 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do let(:postcode_second_part) { "1AA".freeze } let(:postcode) { "#{postcode_first_part} #{postcode_second_part}" } let(:location) { create(:location, :with_old_visible_id, scheme:, postcode:) } + let(:confidential_suffix) { I18n.t("validations.lettings.2026.bulk_upload.address.confidential_scheme_suffix") } let(:setup_section_params) do { @@ -1676,10 +1677,10 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do it "adds errors to missing key address fields" do parser.valid? - expect(parser.errors[:field_19]).to eql([I18n.t("validations.lettings.2026.bulk_upload.not_answered", question: "address line 1.")]) - expect(parser.errors[:field_21]).to eql([I18n.t("validations.lettings.2026.bulk_upload.not_answered", question: "town or city.")]) - expect(parser.errors[:field_23]).to eql([I18n.t("validations.lettings.2026.bulk_upload.not_answered", question: "part 1 of postcode.")]) - expect(parser.errors[:field_24]).to eql([I18n.t("validations.lettings.2026.bulk_upload.not_answered", question: "part 2 of postcode.")]) + expect(parser.errors[:field_19]).to eql(["#{I18n.t('validations.lettings.2026.bulk_upload.not_answered', question: 'address line 1.')} #{confidential_suffix}"]) + expect(parser.errors[:field_21]).to eql(["#{I18n.t('validations.lettings.2026.bulk_upload.not_answered', question: 'town or city.')} #{confidential_suffix}"]) + expect(parser.errors[:field_23]).to eql(["#{I18n.t('validations.lettings.2026.bulk_upload.not_answered', question: 'part 1 of postcode.')} #{confidential_suffix}"]) + expect(parser.errors[:field_24]).to eql(["#{I18n.t('validations.lettings.2026.bulk_upload.not_answered', question: 'part 2 of postcode.')} #{confidential_suffix}"]) end end @@ -1707,11 +1708,20 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do it "adds appropriate errors to UPRN and key address fields" do parser.valid? - expect(parser.errors[:field_18]).to eql([I18n.t("validations.lettings.2026.bulk_upload.address.not_answered")]) - expect(parser.errors[:field_19]).to eql([I18n.t("validations.lettings.2026.bulk_upload.address.not_answered")]) - expect(parser.errors[:field_21]).to eql([I18n.t("validations.lettings.2026.bulk_upload.address.not_answered")]) - expect(parser.errors[:field_23]).to eql([I18n.t("validations.lettings.2026.bulk_upload.address.not_answered")]) - expect(parser.errors[:field_24]).to eql([I18n.t("validations.lettings.2026.bulk_upload.address.not_answered")]) + expect(parser.errors[:field_18]).to eql(["#{I18n.t('validations.lettings.2026.bulk_upload.address.not_answered')} #{confidential_suffix}"]) + expect(parser.errors[:field_19]).to eql(["#{I18n.t('validations.lettings.2026.bulk_upload.address.not_answered')} #{confidential_suffix}"]) + expect(parser.errors[:field_21]).to eql(["#{I18n.t('validations.lettings.2026.bulk_upload.address.not_answered')} #{confidential_suffix}"]) + expect(parser.errors[:field_23]).to eql(["#{I18n.t('validations.lettings.2026.bulk_upload.address.not_answered')} #{confidential_suffix}"]) + expect(parser.errors[:field_24]).to eql(["#{I18n.t('validations.lettings.2026.bulk_upload.address.not_answered')} #{confidential_suffix}"]) + end + + it "appends the confidential scheme guidance to each address error" do + parser.valid? + %i[field_18 field_19 field_21 field_23 field_24].each do |field| + expect(parser.errors[field].first).to end_with( + "If your letting is in a confidential scheme, check the scheme you chose. A coordinator can edit a scheme to confidential on the ‘Schemes' page.", + ) + end end end @@ -1720,8 +1730,8 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do it "adds errors to UPRN and the missing key address field" do parser.valid? - expect(parser.errors[:field_18]).to eql([I18n.t("validations.lettings.2026.bulk_upload.address.not_answered")]) - expect(parser.errors[:field_19]).to eql([I18n.t("validations.lettings.2026.bulk_upload.address.not_answered")]) + expect(parser.errors[:field_18]).to eql(["#{I18n.t('validations.lettings.2026.bulk_upload.address.not_answered')} #{confidential_suffix}"]) + expect(parser.errors[:field_19]).to eql(["#{I18n.t('validations.lettings.2026.bulk_upload.address.not_answered')} #{confidential_suffix}"]) expect(parser.errors[:field_21]).to be_empty expect(parser.errors[:field_23]).to be_empty expect(parser.errors[:field_24]).to be_empty @@ -1896,8 +1906,7 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do it "derives the local authority from the scheme's location" do log = parser.log log.valid? - expect(log.read_attribute(:la)).to eq(location.location_code) - expect(log.is_la_inferred).to be true + expect(log.la).to eq(location.location_code) end end @@ -1924,7 +1933,7 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do it "still derives the local authority from the scheme's location, ignoring the template LA" do log = parser.log log.valid? - expect(log.read_attribute(:la)).to eq(location.location_code) + expect(log.la).to eq(location.location_code) end end end