diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb index b2f3023a5..48baa7b8e 100644 --- a/app/models/lettings_log.rb +++ b/app/models/lettings_log.rb @@ -44,15 +44,21 @@ class LettingsLog < Log scope :filter_by_propcode, ->(propcode) { where("propcode ILIKE ?", "%#{propcode}%") } scope :filter_by_location_postcode, ->(postcode_full) { left_joins(:location).where("REPLACE(locations.postcode, ' ', '') ILIKE ?", "%#{postcode_full.delete(' ')}%") } scope :search_by, lambda { |param| - filter_by_location_postcode(param) - .or(filter_by_tenant_code(param)) - .or(filter_by_propcode(param)) - .or(filter_by_postcode(param)) - .or(filter_by_id(param)) - } + filter_by_location_postcode(param) + .or(filter_by_tenant_code(param)) + .or(filter_by_propcode(param)) + .or(filter_by_postcode(param)) + .or(filter_by_id(param)) + } scope :filter_by_before_startdate, ->(date) { where("lettings_logs.startdate >= ?", date) } scope :unresolved, -> { where(unresolved: true) } scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org).or(where(managing_organisation: org)) } + scope :duplicate_logs, lambda { |log| + visible.where(log.slice(*DUPLICATE_LOG_ATTRIBUTES)) + .where.not(id: log.id) + .where.not("startdate IS NULL OR age1 IS NULL OR sex1 IS NULL OR ecstat1 IS NULL OR tcharge IS NULL OR postcode_full IS NULL OR propcode IS NULL OR needstype IS NULL") + .where("location_id = ? OR needstype = 1", log.location_id) + } AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze OPTIONAL_FIELDS = %w[first_time_property_let_as_social_housing tenancycode propcode chcharge].freeze @@ -61,6 +67,7 @@ class LettingsLog < Log NUM_OF_WEEKS_FROM_PERIOD = { 2 => 26, 3 => 13, 4 => 12, 5 => 50, 6 => 49, 7 => 48, 8 => 47, 9 => 46, 1 => 52, 10 => 53 }.freeze SUFFIX_FROM_PERIOD = { 2 => "every 2 weeks", 3 => "every 4 weeks", 4 => "every month" }.freeze RETIREMENT_AGES = { "M" => 67, "F" => 60, "X" => 67 }.freeze + DUPLICATE_LOG_ATTRIBUTES = %w[tenancycode propcode startdate age1 sex1 ecstat1 tcharge postcode_full].freeze def form FormHandler.instance.get_form(form_name) || FormHandler.instance.current_lettings_form diff --git a/app/models/sales_log.rb b/app/models/sales_log.rb index ee29b6681..311ecc565 100644 --- a/app/models/sales_log.rb +++ b/app/models/sales_log.rb @@ -41,9 +41,15 @@ class SalesLog < Log .or(filter_by_id(param)) } scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org) } + scope :duplicate_logs, lambda { |log| + visible.where(log.slice(*DUPLICATE_LOG_ATTRIBUTES)) + .where.not(id: log.id) + .where.not("saledate is NULL OR age1 IS NULL OR sex1 IS NULL OR ecstat1 IS NULL OR postcode_full IS NULL") + } OPTIONAL_FIELDS = %w[purchid othtype].freeze RETIREMENT_AGES = { "M" => 65, "F" => 60, "X" => 65 }.freeze + DUPLICATE_LOG_ATTRIBUTES = %w[purchid saledate age1 sex1 ecstat1 postcode_full].freeze def lettings? false diff --git a/spec/factories/lettings_log.rb b/spec/factories/lettings_log.rb index 8cb459117..9cf6232d4 100644 --- a/spec/factories/lettings_log.rb +++ b/spec/factories/lettings_log.rb @@ -34,6 +34,25 @@ FactoryBot.define do ecstat1 { 2 } hhmemb { 1 } end + trait :duplicate do + status { 1 } + needstype { 1 } + tenancycode { "same tenancy code" } + postcode_full { "A1 1AA" } + age1 { 18 } + sex1 { "M" } + ecstat1 { 0 } + period { 2 } + brent { 200 } + scharge { 50 } + pscharge { 40 } + supcharg { 35 } + tcharge { 325 } + propcode { "same property code" } + renewal { 1 } + rent_type { 1 } + startdate { Time.zone.today } + end trait :completed do startdate { Time.zone.today } status { 2 } diff --git a/spec/factories/sales_log.rb b/spec/factories/sales_log.rb index fbb4f5c96..dbfc8e418 100644 --- a/spec/factories/sales_log.rb +++ b/spec/factories/sales_log.rb @@ -35,6 +35,18 @@ FactoryBot.define do buylivein { 1 } jointpur { 2 } end + trait :duplicate do + purchid { "PC123" } + ownershipsch { 2 } + type { 8 } + jointpur { 2 } + saledate { Time.zone.today } + age1 { 20 } + sex1 { "F" } + ecstat1 { 1 } + postcode_full { "A1 1AA" } + privacynotice { 1 } + end trait :completed do purchid { rand(999_999_999).to_s } ownershipsch { 2 } diff --git a/spec/models/lettings_log_spec.rb b/spec/models/lettings_log_spec.rb index 990ef2b03..b5b5e99cb 100644 --- a/spec/models/lettings_log_spec.rb +++ b/spec/models/lettings_log_spec.rb @@ -2797,6 +2797,130 @@ RSpec.describe LettingsLog do expect(described_class.filter_by_user(%w[all yours], created_by_user).count).to eq(3) end end + + context "when filtering duplicate logs" do + let(:log) { create(:lettings_log, :duplicate) } + let!(:duplicate_log) { create(:lettings_log, :duplicate) } + + it "returns all duplicate logs for given log" do + expect(described_class.duplicate_logs(log).count).to eq(1) + end + + it "returns duplicate log" do + expect(described_class.duplicate_logs(log)).to include(duplicate_log) + end + + it "does not return the given log" do + expect(described_class.duplicate_logs(log)).not_to include(log) + end + + context "when there is a deleted duplicate log" do + let!(:deleted_duplicate_log) { create(:lettings_log, :duplicate, discarded_at: Time.zone.now) } + + it "does not return the deleted log as a duplicate" do + expect(described_class.duplicate_logs(log)).not_to include(deleted_duplicate_log) + end + end + + context "when there is a log with a different start date" do + let!(:different_start_date_log) { create(:lettings_log, :duplicate, startdate: Time.zone.tomorrow) } + + it "does not return a log with a different start date as a duplicate" do + expect(described_class.duplicate_logs(log)).not_to include(different_start_date_log) + end + end + + context "when there is a log with a different age1" do + let!(:different_age1) { create(:lettings_log, :duplicate, age1: 50) } + + it "does not return a log with a different age1 as a duplicate" do + expect(described_class.duplicate_logs(log)).not_to include(different_age1) + end + end + + context "when there is a log with a different sex1" do + let!(:different_sex1) { create(:lettings_log, :duplicate, sex1: "F") } + + it "does not return a log with a different sex1 as a duplicate" do + expect(described_class.duplicate_logs(log)).not_to include(different_sex1) + end + end + + context "when there is a log with a different ecstat1" do + let!(:different_ecstat1) { create(:lettings_log, :duplicate, ecstat1: 1) } + + it "does not return a log with a different ecstat1 as a duplicate" do + expect(described_class.duplicate_logs(log)).not_to include(different_ecstat1) + end + end + + context "when there is a log with a different tcharge" do + let!(:different_tcharge) { create(:lettings_log, :duplicate, brent: 100) } + + it "does not return a log with a different tcharge as a duplicate" do + expect(described_class.duplicate_logs(log)).not_to include(different_tcharge) + end + end + + context "when there is a log with a different propcode" do + let!(:different_propcode) { create(:lettings_log, :duplicate, propcode: "different") } + + it "does not return a log with a different propcode as a duplicate" do + expect(described_class.duplicate_logs(log)).not_to include(different_propcode) + end + end + + context "when there is a log with a different tenancycode" do + let!(:different_tenancycode) { create(:lettings_log, :duplicate, tenancycode: "different") } + + it "does not return a log with a different tenancycode as a duplicate" do + expect(described_class.duplicate_logs(log)).not_to include(different_tenancycode) + end + end + + context "when there is a log with a different postcode_full" do + let!(:different_postcode_full) { create(:lettings_log, :duplicate, postcode_full: "B1 1AA") } + + it "does not return a log with a different postcode_full as a duplicate" do + expect(described_class.duplicate_logs(log)).not_to include(different_postcode_full) + end + end + + context "when there is a log with nil values for duplicate check fields" do + let!(:duplicate_check_fields_not_given) { create(:lettings_log, :duplicate, age1: nil, sex1: nil, ecstat1: nil, propcode: nil, postcode_known: 2, postcode_full: nil) } + + it "does not return a log with nil values as a duplicate" do + log.update!(age1: nil, sex1: nil, ecstat1: nil, propcode: nil, postcode_known: 2, postcode_full: nil) + expect(described_class.duplicate_logs(log)).not_to include(duplicate_check_fields_not_given) + end + end + + context "when there is a log with nil values for tenancycode" do + let!(:tenancycode_not_given) { create(:lettings_log, :duplicate, tenancycode: nil) } + + it "returns the log as a duplicate if tenancy code is nil" do + log.update!(tenancycode: nil) + expect(described_class.duplicate_logs(log)).to include(tenancycode_not_given) + end + end + + context "when there is a duplicate supported housing log" do + let(:scheme) { create(:scheme) } + let(:location) { create(:location, scheme:) } + let(:location_2) { create(:location, scheme:) } + let(:supported_housing_log) { create(:lettings_log, :duplicate, needstype: 2, location:, scheme:) } + let!(:duplicate_supported_housing_log) { create(:lettings_log, :duplicate, needstype: 2, location:, scheme:) } + + it "returns the log as a duplicate" do + expect(described_class.duplicate_logs(supported_housing_log)).to include(duplicate_supported_housing_log) + end + + it "does not return the log if the locations are different" do + duplicate_supported_housing_log.update!(location: location_2) + expect(described_class.duplicate_logs(supported_housing_log)).not_to include(duplicate_supported_housing_log) + end + end + end end describe "#retirement_age_for_person" do diff --git a/spec/models/sales_log_spec.rb b/spec/models/sales_log_spec.rb index ef197235f..c382c364b 100644 --- a/spec/models/sales_log_spec.rb +++ b/spec/models/sales_log_spec.rb @@ -160,6 +160,97 @@ RSpec.describe SalesLog, type: :model do end end + context "when filtering duplicate logs" do + let(:log) { create(:sales_log, :duplicate) } + let!(:duplicate_log) { create(:sales_log, :duplicate) } + + it "returns all duplicate logs for given log" do + expect(described_class.duplicate_logs(log).count).to eq(1) + end + + it "returns duplicate log" do + expect(described_class.duplicate_logs(log)).to include(duplicate_log) + end + + it "does not return the given log" do + expect(described_class.duplicate_logs(log)).not_to include(log) + end + + context "when there is a deleted duplicate log" do + let!(:deleted_duplicate_log) { create(:sales_log, :duplicate, discarded_at: Time.zone.now) } + + it "does not return the deleted log as a duplicate" do + expect(described_class.duplicate_logs(log)).not_to include(deleted_duplicate_log) + end + end + + context "when there is a log with a different sale date" do + let!(:different_sale_date_log) { create(:sales_log, :duplicate, saledate: Time.zone.tomorrow) } + + it "does not return a log with a different sale date as a duplicate" do + expect(described_class.duplicate_logs(log)).not_to include(different_sale_date_log) + end + end + + context "when there is a log with a different age1" do + let!(:different_age1) { create(:sales_log, :duplicate, age1: 50) } + + it "does not return a log with a different age1 as a duplicate" do + expect(described_class.duplicate_logs(log)).not_to include(different_age1) + end + end + + context "when there is a log with a different sex1" do + let!(:different_sex1) { create(:sales_log, :duplicate, sex1: "M") } + + it "does not return a log with a different sex1 as a duplicate" do + expect(described_class.duplicate_logs(log)).not_to include(different_sex1) + end + end + + context "when there is a log with a different ecstat1" do + let!(:different_ecstat1) { create(:sales_log, :duplicate, ecstat1: 0) } + + it "does not return a log with a different ecstat1 as a duplicate" do + expect(described_class.duplicate_logs(log)).not_to include(different_ecstat1) + end + end + + context "when there is a log with a different purchid" do + let!(:different_purchid) { create(:sales_log, :duplicate, purchid: "different") } + + it "does not return a log with a different purchid as a duplicate" do + expect(described_class.duplicate_logs(log)).not_to include(different_purchid) + end + end + + context "when there is a log with a different postcode_full" do + let!(:different_postcode_full) { create(:sales_log, :duplicate, postcode_full: "B1 1AA") } + + it "does not return a log with a different postcode_full as a duplicate" do + expect(described_class.duplicate_logs(log)).not_to include(different_postcode_full) + end + end + + context "when there is a log with nil values for duplicate check fields" do + let!(:duplicate_check_fields_not_given) { create(:sales_log, :duplicate, age1: nil, sex1: nil, ecstat1: nil, pcodenk: 1, postcode_full: nil) } + + it "does not return a log with nil values as a duplicate" do + log.update!(age1: nil, sex1: nil, ecstat1: nil, pcodenk: 1, postcode_full: nil) + expect(described_class.duplicate_logs(log)).not_to include(duplicate_check_fields_not_given) + end + end + + context "when there is a log with nil values for purchid" do + let!(:purchid_not_given) { create(:sales_log, :duplicate, purchid: nil) } + + it "returns the log as a duplicate if purchid is nil" do + log.update!(purchid: nil) + expect(described_class.duplicate_logs(log)).to include(purchid_not_given) + end + end + end + describe "derived variables" do let(:sales_log) { create(:sales_log, :completed) }