From cd96d9556de03c7b8c9346c4f6dc241802f61a11 Mon Sep 17 00:00:00 2001 From: Samuel Young Date: Fri, 13 Feb 2026 14:55:52 +0000 Subject: [PATCH] CLDC-4119: Update main site duplicate logs check adds sexrab1 to the duplicate log attributes needs special care for the location and uprn attributes --- app/models/lettings_log.rb | 78 +++++++++++++++++++++-------- lib/tasks/handle_unpended_logs.rake | 6 +-- 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb index f2230c4e5..a8e494b5b 100644 --- a/app/models/lettings_log.rb +++ b/app/models/lettings_log.rb @@ -21,6 +21,7 @@ class LettingsLog < Log include Validations::DateValidations include Validations::FinancialValidations include MoneyFormattingHelper + include CollectionTimeHelper has_paper_trail @@ -41,6 +42,8 @@ class LettingsLog < Log belongs_to :managing_organisation, class_name: "Organisation", optional: true scope :filter_by_year, ->(year) { where(startdate: Time.zone.local(year.to_i, 4, 1)...Time.zone.local(year.to_i + 1, 4, 1)) } + scope :filter_by_year_or_later, ->(year) { where("lettings_logs.startdate >= ?", Time.zone.local(year.to_i, 4, 1)) } + scope :filter_by_year_or_earlier, ->(year) { where("lettings_logs.startdate < ?", Time.zone.local(year.to_i + 1, 4, 1)) } scope :filter_by_years_or_nil, lambda { |years, _user = nil| first_year = years.shift query = filter_by_year(first_year) @@ -85,9 +88,15 @@ class LettingsLog < Log scope :age1_answered, -> { where.not(age1: nil).or(where(age1_known: 1)) } scope :tcharge_answered, -> { where.not(tcharge: nil).or(where(household_charge: 1)).or(where(is_carehome: 1)) } scope :chcharge_answered, -> { where.not(chcharge: nil).or(where(is_carehome: [nil, 0])) } - scope :location_for_log_answered, ->(log) { where(location_id: log.location_id).or(where(needstype: 1)) } - scope :postcode_for_log_answered, ->(log) { where(postcode_full: log.postcode_full).or(where(needstype: 2)) } - scope :location_answered, -> { where.not(location_id: nil).or(where(needstype: 1)) } + # once 2025 logs are removed this logic can be simplified + # pre 2025, match on location if supported, or address if general needs + # post 2026, match on address only + scope :location_for_log_answered_as, ->(log) { where(location_id: log.location_id).or(where(needstype: 1)).or(filter_by_year_or_later(2026)) } + scope :address_for_log_answered_as, lambda { |log| + where(postcode_full: log.postcode_full).where(address_line1: log.address_line1).where(uprn: log.uprn) + .or(filter_by_year_or_earlier(2025).where(needstype: 2)) + } + scope :location_answered, -> { where.not(location_id: nil).or(where(needstype: 1)).or(filter_by_year_or_later(2026)) } scope :postcode_answered, -> { where.not(postcode_full: nil).or(where(needstype: 2)) } scope :duplicate_logs, lambda { |log| visible @@ -99,26 +108,27 @@ class LettingsLog < Log .age1_answered .tcharge_answered .chcharge_answered - .location_for_log_answered(log) - .postcode_for_log_answered(log) + .location_for_log_answered_as(log) + .address_for_log_answered_as(log) .where(log.slice(*DUPLICATE_LOG_ATTRIBUTES)) } - scope :duplicate_sets, lambda { |assigned_to_id = nil| + scope :duplicate_sets_2025_and_earlier, lambda { |assigned_to_id = nil| scope = visible - .group(*DUPLICATE_LOG_ATTRIBUTES, :postcode_full, :location_id) - .where.not(startdate: nil) - .where.not(sex1: nil) - .where.not(ecstat1: nil) - .where.not(needstype: nil) - .age1_answered - .tcharge_answered - .chcharge_answered - .location_answered - .postcode_answered - .having( - "COUNT(*) > 1", - ) + .filter_by_year_or_earlier(2025) + .group(*DUPLICATE_LOG_ATTRIBUTES, :postcode_full, :location_id, :uprn, :address_line1) + .where.not(startdate: nil) + .where.not(sex1: nil) + .where.not(ecstat1: nil) + .where.not(needstype: nil) + .age1_answered + .tcharge_answered + .chcharge_answered + .location_answered + .postcode_answered + .having( + "COUNT(*) > 1", + ) if assigned_to_id scope = scope.having("MAX(CASE WHEN assigned_to_id = ? THEN 1 ELSE 0 END) >= 1", assigned_to_id) @@ -126,6 +136,34 @@ class LettingsLog < Log scope.pluck("ARRAY_AGG(id)") } + scope :duplicate_sets_2026_and_later, lambda { |assigned_to_id = nil| + scope = visible + .filter_by_year_or_later(2026) + # separate function as location needs to be fully ignored in 2026 + .group(*DUPLICATE_LOG_ATTRIBUTES, :postcode_full, :uprn, :address_line1) + .where.not(startdate: nil) + .where.not(sex1: nil) + .where.not(ecstat1: nil) + .where.not(needstype: nil) + .age1_answered + .tcharge_answered + .chcharge_answered + .location_answered + .postcode_answered + .having( + "COUNT(*) > 1", + ) + + if assigned_to_id + scope = scope.having("MAX(CASE WHEN assigned_to_id = ? THEN 1 ELSE 0 END) >= 1", assigned_to_id) + end + scope.pluck("ARRAY_AGG(id)") + } + + scope :duplicate_sets, lambda { |assigned_to_id = nil| + duplicate_sets_2025_and_earlier(assigned_to_id) + duplicate_sets_2026_and_later(assigned_to_id) + } + scope :with_illness_without_type, lambda { where(illness: 1, illness_type_1: false, @@ -150,7 +188,7 @@ class LettingsLog < Log HAS_BENEFITS_OPTIONS = [1, 6, 8, 7].freeze NUM_OF_WEEKS_FROM_PERIOD = { 2 => 26, 3 => 13, 4 => 12, 5 => 50, 6 => 49, 7 => 48, 8 => 47, 9 => 46, 11 => 51, 1 => 52, 10 => 53 }.freeze SUFFIX_FROM_PERIOD = { 2 => "every 2 weeks", 3 => "every 4 weeks", 4 => "every month" }.freeze - DUPLICATE_LOG_ATTRIBUTES = %w[owning_organisation_id tenancycode startdate age1_known age1 sex1 ecstat1 tcharge household_charge chcharge].freeze + DUPLICATE_LOG_ATTRIBUTES = %w[owning_organisation_id tenancycode startdate age1_known age1 sex1 sexrab1 ecstat1 tcharge household_charge chcharge].freeze RENT_TYPE = { social_rent: 0, affordable_rent: 1, diff --git a/lib/tasks/handle_unpended_logs.rake b/lib/tasks/handle_unpended_logs.rake index d3837ee06..7e2c07862 100644 --- a/lib/tasks/handle_unpended_logs.rake +++ b/lib/tasks/handle_unpended_logs.rake @@ -6,7 +6,7 @@ task :handle_unpended_logs, %i[perform_updates] => :environment do |_task, args| query = "SELECT \"versions\".* FROM \"versions\" WHERE \"versions\".\"item_type\" = 'LettingsLog' AND whodunnit is null AND ((object_changes like '%status:\n- 3\n- 1%') OR (object_changes like '%status:\n- 3\n- 2%'))" results = pg.execute(query) - duplicate_log_attributes = %w[owning_organisation_id tenancycode startdate age1_known age1 sex1 ecstat1 tcharge household_charge chcharge] + duplicate_log_attributes = %w[owning_organisation_id tenancycode startdate age1_known age1 sex1 sexrab1 ecstat1 tcharge household_charge chcharge] seen = [].to_set @@ -47,8 +47,8 @@ task :handle_unpended_logs, %i[perform_updates] => :environment do |_task, args| .age1_answered .tcharge_answered .chcharge_answered - .location_for_log_answered(log) - .postcode_for_log_answered(log) + .location_for_log_answered_as(log) + .address_for_log_answered_as(log) .where(log.slice(*duplicate_log_attributes)) duplicate_count = duplicates.length