From f327ee4af52f8315004c186fe2072f0b9a096f53 Mon Sep 17 00:00:00 2001 From: oscric Date: Tue, 11 Aug 2026 09:17:58 +0100 Subject: [PATCH] Add BU handling --- .../lettings_log_variables.rb | 2 +- app/models/lettings_log.rb | 8 ++- .../lettings/year2026/row_parser.rb | 53 ++++++++------- .../lettings_log_derived_fields_spec.rb | 30 +++++++++ .../lettings/year2026/row_parser_spec.rb | 65 +++++++++++++++++++ 5 files changed, 134 insertions(+), 24 deletions(-) diff --git a/app/models/derived_variables/lettings_log_variables.rb b/app/models/derived_variables/lettings_log_variables.rb index 33068b0a5..31e3937c7 100644 --- a/app/models/derived_variables/lettings_log_variables.rb +++ b/app/models/derived_variables/lettings_log_variables.rb @@ -188,7 +188,7 @@ module DerivedVariables::LettingsLogVariables self.uprn_selection = nil self.postcode_known = nil self.manual_address_entry_selected = nil - location_la = location&.linked_local_authorities&.active(form.start_date)&.first&.code || location&.location_code + location_la = location_derived_la if location_la.present? self.la = location_la self.is_la_inferred = true diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb index df7ee4581..5b054c457 100644 --- a/app/models/lettings_log.rb +++ b/app/models/lettings_log.rb @@ -228,7 +228,7 @@ class LettingsLog < Log return super unless location return super if form.start_year_2026_or_later? && super - location.linked_local_authorities.active(form.start_date).first&.code || location.location_code + location_derived_la end def postcode_full @@ -849,6 +849,12 @@ class LettingsLog < Log private + def location_derived_la + return unless location + + location.linked_local_authorities.active(form.start_date).first&.code || location.location_code + end + def reset_invalid_unresolved_log_fields! return unless unresolved? diff --git a/app/services/bulk_upload/lettings/year2026/row_parser.rb b/app/services/bulk_upload/lettings/year2026/row_parser.rb index 13baad405..b38d79616 100644 --- a/app/services/bulk_upload/lettings/year2026/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2026/row_parser.rb @@ -493,8 +493,8 @@ class BulkUpload::Lettings::Year2026::RowParser validate :validate_assigned_to_when_support, on: :after_log validate :validate_all_charges_given, on: :after_log - validate :validate_uprn_exists_if_any_key_address_fields_are_blank, on: :after_log - validate :validate_address_fields, on: :after_log + validate :validate_uprn_exists_if_any_key_address_fields_are_blank, on: :after_log, unless: :scheme_has_confidential_information? + validate :validate_address_fields, on: :after_log, unless: :scheme_has_confidential_information? validate :validate_nationality, on: :after_log validate :validate_reasonpref_reason_values, on: :after_log @@ -1491,26 +1491,31 @@ private attributes["first_time_property_let_as_social_housing"] = first_time_property_let_as_social_housing - attributes["uprn_known"] = field_18.present? ? 1 : 0 - attributes["uprn_confirmed"] = 1 if field_18.present? - attributes["skip_update_uprn_confirmed"] = true - attributes["uprn"] = field_18 - attributes["address_line1"] = field_19 - attributes["address_line1_as_entered"] = field_19 - attributes["address_line2"] = field_20 - attributes["address_line2_as_entered"] = field_20 - attributes["town_or_city"] = field_21 - attributes["town_or_city_as_entered"] = field_21 - attributes["county"] = field_22 - attributes["county_as_entered"] = field_22 - attributes["postcode_full"] = postcode_full - attributes["postcode_full_as_entered"] = postcode_full - attributes["postcode_known"] = postcode_known - attributes["la"] = field_25 - attributes["la_as_entered"] = field_25 - attributes["address_line1_input"] = address_line1_input - attributes["postcode_full_input"] = postcode_full - attributes["select_best_address_match"] = true if field_18.blank? + # For a confidential scheme, the address and UPRN fields in the BU are ignored: + # they are left null and the local authority is instead derived from the + # scheme's location in LettingsLog#set_derived_fields!. + unless scheme_has_confidential_information? + attributes["uprn_known"] = field_18.present? ? 1 : 0 + attributes["uprn_confirmed"] = 1 if field_18.present? + attributes["skip_update_uprn_confirmed"] = true + attributes["uprn"] = field_18 + attributes["address_line1"] = field_19 + attributes["address_line1_as_entered"] = field_19 + attributes["address_line2"] = field_20 + attributes["address_line2_as_entered"] = field_20 + attributes["town_or_city"] = field_21 + attributes["town_or_city_as_entered"] = field_21 + attributes["county"] = field_22 + attributes["county_as_entered"] = field_22 + attributes["postcode_full"] = postcode_full + attributes["postcode_full_as_entered"] = postcode_full + attributes["postcode_known"] = postcode_known + attributes["la"] = field_25 + attributes["la_as_entered"] = field_25 + attributes["address_line1_input"] = address_line1_input + attributes["postcode_full_input"] = postcode_full + attributes["select_best_address_match"] = true if field_18.blank? + end attributes["gender_same_as_sex1"] = field_43 attributes["gender_description1"] = field_44 @@ -1583,6 +1588,10 @@ private @location ||= scheme.locations.find_by_id_on_multiple_fields(field_6) end + def scheme_has_confidential_information? + scheme&.sensitive == "Yes" + end + def startdate year = field_10.to_s.strip.length.between?(1, 2) ? field_10 + 2000 : field_10 Date.new(year, field_9, field_8) if field_10.present? && field_9.present? && field_8.present? diff --git a/spec/models/lettings_log_derived_fields_spec.rb b/spec/models/lettings_log_derived_fields_spec.rb index fb9ad1369..49eecd260 100644 --- a/spec/models/lettings_log_derived_fields_spec.rb +++ b/spec/models/lettings_log_derived_fields_spec.rb @@ -1688,6 +1688,36 @@ RSpec.describe LettingsLog, type: :model do end end + describe "address behaviour for a non-confidential supported housing log continues as normal", metadata: { year: 26 } do + let(:startdate) { collection_start_date_for_year(2026) } + let(:non_confidential_scheme) { create(:scheme, sensitive: 0) } + let(:location) { create(:location, scheme: non_confidential_scheme) } + + around do |example| + Timecop.freeze(collection_start_date_for_year(2026)) do + Singleton.__init__(FormHandler) + example.run + end + end + + before do + log.needstype = 2 + log.scheme = non_confidential_scheme + log.location = location + end + + it "still asks for the address or UPRN" do + expect(log.is_address_asked?).to be true + end + + it "routes a new-build property down the manual address entry route" do + log.assign_attributes(manual_address_entry_selected: false, rsnvac: 15, uprn: nil) + + expect { log.set_derived_fields! } + .to change(log, :manual_address_entry_selected).from(false).to(true) + end + end + describe "#infer_at_most_one_relationship!" do context "when 2025", metadata: { year: 25 } do before do 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 665e3d683..7d44d3544 100644 --- a/spec/services/bulk_upload/lettings/year2026/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2026/row_parser_spec.rb @@ -1864,6 +1864,71 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do end end + describe "UPRN and address fields for a supported housing log in a confidential scheme" do + let(:scheme) { create(:scheme, :with_old_visible_id, owning_organisation: owning_org, sensitive: 1) } + let(:base_attributes) do + setup_section_params.merge({ + field_4: 2, + field_5: "S#{scheme.id}", + field_6: location.old_visible_id, + }) + end + + context "when no UPRN or address fields are provided" do + let(:attributes) { base_attributes.merge({ field_18: nil, field_19: nil, field_21: nil, field_23: nil, field_24: nil }) } + + it "does not require the address or UPRN (no not answered errors)" do + parser.valid? + %i[field_18 field_19 field_21 field_23 field_24].each do |field| + expect(parser.errors[field]).to be_empty + end + end + + it "does not import any address or UPRN fields" do + log = parser.log + log.valid? + expect(log.read_attribute(:uprn)).to be_nil + expect(log.read_attribute(:address_line1)).to be_nil + expect(log.read_attribute(:town_or_city)).to be_nil + expect(log.read_attribute(:postcode_full)).to be_nil + end + + 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 + end + end + + context "when address and UPRN fields are provided in the template" do + let(:attributes) do + base_attributes.merge({ + field_18: "123456789012", + field_19: "1 Test Street", + field_21: "Testville", + field_23: postcode_first_part, + field_24: postcode_second_part, + field_25: "E09000008", + }) + end + + it "ignores them and does not import the address or UPRN fields" do + log = parser.log + log.valid? + expect(log.read_attribute(:uprn)).to be_nil + expect(log.read_attribute(:address_line1)).to be_nil + expect(log.read_attribute(:postcode_full)).to be_nil + end + + 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) + end + end + end + describe "#field_17" do # unitletas context "when no longer a valid option from previous year" do let(:attributes) { setup_section_params.merge({ field_17: "4" }) }