diff --git a/app/controllers/delete_logs_controller.rb b/app/controllers/delete_logs_controller.rb index a0fa962ad..561fe7346 100644 --- a/app/controllers/delete_logs_controller.rb +++ b/app/controllers/delete_logs_controller.rb @@ -25,8 +25,14 @@ class DeleteLogsController < ApplicationController def discard_lettings_logs logs = LettingsLog.find(params.require(:ids)) + remove_lettings_duplicate_set_ids(logs) discard logs if request.referer&.include?("delete-duplicates") + logs.each do |log| + log.update!(duplicate_set_id: nil) + end + LettingsLog.find(params["remaining_log_id"]).update!(duplicate_set_id: nil) + redirect_to lettings_log_duplicate_logs_path(lettings_log_id: params["remaining_log_id"], original_log_id: params["original_log_id"], referrer: params[:referrer], organisation_id: params[:organisation_id]), notice: I18n.t("notification.duplicate_logs_deleted", count: logs.count, log_ids: duplicate_log_ids(logs)) else redirect_to lettings_logs_path, notice: I18n.t("notification.logs_deleted", count: logs.count) @@ -54,8 +60,14 @@ class DeleteLogsController < ApplicationController def discard_sales_logs logs = SalesLog.find(params.require(:ids)) + remove_sales_duplicate_set_ids(logs) discard logs if request.referer&.include?("delete-duplicates") + logs.each do |log| + log.update!(duplicate_set_id: nil) + end + SalesLog.find(params["remaining_log_id"]).update!(duplicate_set_id: nil) + redirect_to sales_log_duplicate_logs_path(sales_log_id: params["remaining_log_id"], original_log_id: params["original_log_id"], referrer: params[:referrer], organisation_id: params[:organisation_id]), notice: I18n.t("notification.duplicate_logs_deleted", count: logs.count, log_ids: duplicate_log_ids(logs)) else redirect_to sales_logs_path, notice: I18n.t("notification.logs_deleted", count: logs.count) @@ -203,4 +215,30 @@ private def duplicate_log_ids(logs) logs.map { |log| "Log #{log.id}" }.to_sentence(last_word_connector: " and ") end + + def remove_lettings_duplicate_set_ids(logs) + duplicate_set_ids = [] + logs.each do |log| + if log.duplicate_set_id.present? + duplicate_set_ids << log.duplicate_set_id + log.update!(duplicate_set_id: nil) + end + end + duplicate_set_ids.uniq.each do |duplicate_set_id| + LettingsLog.where(duplicate_set_id:).update!(duplicate_set_id: nil) if LettingsLog.where(duplicate_set_id:).count == 1 + end + end + + def remove_sales_duplicate_set_ids(logs) + duplicate_set_ids = [] + logs.each do |log| + if log.duplicate_set_id.present? + duplicate_set_ids << log.duplicate_set_id + log.update!(duplicate_set_id: nil) + end + end + duplicate_set_ids.uniq.each do |duplicate_set_id| + SalesLog.where(duplicate_set_id:).update!(duplicate_set_id: nil) if SalesLog.where(duplicate_set_id:).count == 1 + end + end end diff --git a/app/controllers/form_controller.rb b/app/controllers/form_controller.rb index 11f2aa9cf..17fd9af77 100644 --- a/app/controllers/form_controller.rb +++ b/app/controllers/form_controller.rb @@ -171,12 +171,15 @@ private return correcting_duplicate_logs_redirect_path end - if @log.lettings? && current_user.lettings_logs.duplicate_logs(@log).count.positive? - return send("lettings_log_duplicate_logs_path", @log, original_log_id: @log.id) - end - - if @log.sales? && current_user.sales_logs.duplicate_logs(@log).count.positive? - return send("sales_log_duplicate_logs_path", @log, original_log_id: @log.id) + dynamic_duplicates = @log.lettings? ? current_user.lettings_logs.duplicate_logs(@log) : current_user.sales_logs.duplicate_logs(@log) + if dynamic_duplicates.any? + saved_duplicates = @log.duplicates + if saved_duplicates.none? || duplicates_changed?(dynamic_duplicates, saved_duplicates) + duplicate_set_id = dynamic_duplicates.first.duplicate_set_id || new_duplicate_set_id(@log) + update_logs_with_duplicate_set_id(@log, dynamic_duplicates, duplicate_set_id) + saved_duplicates.first.update!(duplicate_set_id: nil) if saved_duplicates.count == 1 + end + return send("#{@log.class.name.underscore}_duplicate_logs_path", @log, original_log_id: @log.id) end end @@ -250,11 +253,26 @@ private class_name = @log.class.name.underscore original_log = current_user.send(class_name.pluralize).find_by(id: from_referrer_query("original_log_id")) + dynamic_duplicates = current_user.send(class_name.pluralize).duplicate_logs(@log) + + if dynamic_duplicates.any? + saved_duplicates = @log.duplicates + if duplicates_changed?(dynamic_duplicates, saved_duplicates) + duplicate_set_id = dynamic_duplicates.first.duplicate_set_id || new_duplicate_set_id(@log) + update_logs_with_duplicate_set_id(@log, dynamic_duplicates, duplicate_set_id) + saved_duplicates.first.update!(duplicate_set_id: nil) if saved_duplicates.count == 1 + end + else + remove_fixed_duplicate_set_ids(@log) + end - if original_log.present? && current_user.send(class_name.pluralize).duplicate_logs(original_log).count.positive? - flash[:notice] = deduplication_success_banner unless current_user.send(class_name.pluralize).duplicate_logs(@log).count.positive? + if original_log.present? && current_user.send(class_name.pluralize).duplicate_logs(original_log).any? + if dynamic_duplicates.none? + flash[:notice] = deduplication_success_banner + end send("#{class_name}_duplicate_logs_path", original_log, original_log_id: original_log.id, referrer: params[:referrer], organisation_id: params[:organisation_id]) else + remove_fixed_duplicate_set_ids(original_log) flash[:notice] = deduplication_success_banner send("#{class_name}_duplicate_logs_path", "#{class_name}_id".to_sym => from_referrer_query("first_remaining_duplicate_id"), original_log_id: from_referrer_query("original_log_id"), referrer: params[:referrer], organisation_id: params[:organisation_id]) end @@ -275,4 +293,32 @@ private I18n.t("notification.duplicate_logs.deduplication_success_banner", log_link: deduplicated_log_link, changed_question_label:).html_safe end + + def remove_fixed_duplicate_set_ids(log) + duplicate_set_id = log.duplicate_set_id + return unless duplicate_set_id + + log.update!(duplicate_set_id: nil) + LettingsLog.find_by(duplicate_set_id:)&.update!(duplicate_set_id: nil) if log.lettings? && LettingsLog.where(duplicate_set_id:).count == 1 + SalesLog.find_by(duplicate_set_id:)&.update!(duplicate_set_id: nil) if log.sales? && SalesLog.where(duplicate_set_id:).count == 1 + end + + def new_duplicate_set_id(log) + if log.lettings? + LettingsLog.maximum(:duplicate_set_id).to_i + 1 + else + SalesLog.maximum(:duplicate_set_id).to_i + 1 + end + end + + def duplicates_changed?(dynamic_duplicates, saved_duplicates) + dynamic_duplicates.present? && saved_duplicates.present? && dynamic_duplicates.order(:id).pluck(:id) != saved_duplicates.order(:id).pluck(:id) + end + + def update_logs_with_duplicate_set_id(log, dynamic_duplicates, duplicate_set_id) + log.update!(duplicate_set_id:) + dynamic_duplicates.each do |duplicate| + duplicate.update!(duplicate_set_id: log.duplicate_set_id) if duplicate.duplicate_set_id != log.duplicate_set_id + end + end end diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb index 8ebf8092f..b3ae36def 100644 --- a/app/models/lettings_log.rb +++ b/app/models/lettings_log.rb @@ -645,6 +645,10 @@ class LettingsLog < Log renttype == 1 || renttype == 2 end + def duplicates + LettingsLog.where.not(duplicate_set_id: nil).where(duplicate_set_id:).where.not(id:) + end + private def reset_invalid_unresolved_log_fields! diff --git a/app/models/sales_log.rb b/app/models/sales_log.rb index ed5b2424c..486dcc78c 100644 --- a/app/models/sales_log.rb +++ b/app/models/sales_log.rb @@ -456,4 +456,8 @@ class SalesLog < Log form.start_date.year < 2023 || uprn.blank? ? "postcode_full" : nil, form.start_date.year >= 2023 && uprn.present? ? "uprn" : nil].compact end + + def duplicates + SalesLog.where.not(duplicate_set_id: nil).where(duplicate_set_id:).where.not(id:) + end end diff --git a/app/services/csv/lettings_log_csv_service.rb b/app/services/csv/lettings_log_csv_service.rb index 882969169..3f25425b4 100644 --- a/app/services/csv/lettings_log_csv_service.rb +++ b/app/services/csv/lettings_log_csv_service.rb @@ -299,7 +299,7 @@ module Csv ATTRIBUTE_MAPPINGS.fetch(question.id, question.id) end end - non_question_fields = %w[id status created_by is_dpo created_at updated_by updated_at creation_method old_id old_form_id collection_start_year] + non_question_fields = %w[id status duplicate_set_id created_by is_dpo created_at updated_by updated_at creation_method old_id old_form_id collection_start_year] scheme_and_location_attributes = %w[scheme_code scheme_service_name scheme_sensitive SCHTYPE scheme_registered_under_care_act scheme_owning_organisation_name scheme_primary_client_group scheme_has_other_client_group scheme_secondary_client_group scheme_support_type scheme_intended_stay scheme_created_at location_code location_postcode location_name location_units location_type_of_unit location_mobility_type location_local_authority location_startdate] final_attributes = non_question_fields + attributes + scheme_and_location_attributes @user.support? ? final_attributes : final_attributes - SUPPORT_ONLY_ATTRIBUTES diff --git a/app/services/csv/sales_log_csv_service.rb b/app/services/csv/sales_log_csv_service.rb index 900d3fdb1..83c8e9cbf 100644 --- a/app/services/csv/sales_log_csv_service.rb +++ b/app/services/csv/sales_log_csv_service.rb @@ -144,7 +144,7 @@ module Csv question.id end end - non_question_fields = %w[id status created_at updated_at old_form_id collection_start_year creation_method is_dpo] + non_question_fields = %w[id status duplicate_set_id created_at updated_at old_form_id collection_start_year creation_method is_dpo] non_question_fields + attributes end diff --git a/app/services/exports/lettings_log_export_constants.rb b/app/services/exports/lettings_log_export_constants.rb index edfb58b17..0d460a19f 100644 --- a/app/services/exports/lettings_log_export_constants.rb +++ b/app/services/exports/lettings_log_export_constants.rb @@ -136,7 +136,8 @@ module Exports::LettingsLogExportConstants "scheme_status", "location_status", "created_by", - "amended_by" + "amended_by", + "duplicate_set_id" ] (1..8).each do |index| diff --git a/db/migrate/20240118183843_add_duplicate_set_id.rb b/db/migrate/20240118183843_add_duplicate_set_id.rb new file mode 100644 index 000000000..6d58ebd00 --- /dev/null +++ b/db/migrate/20240118183843_add_duplicate_set_id.rb @@ -0,0 +1,6 @@ +class AddDuplicateSetId < ActiveRecord::Migration[7.0] + def change + add_column :lettings_logs, :duplicate_set_id, :integer + add_column :sales_logs, :duplicate_set_id, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index e75f47e60..f12d36ced 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2024_01_08_152935) do +ActiveRecord::Schema[7.0].define(version: 2024_01_18_183843) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -302,6 +302,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_01_08_152935) do t.integer "supcharg_value_check" t.integer "scharge_value_check" t.integer "pscharge_value_check" + t.integer "duplicate_set_id" t.index ["bulk_upload_id"], name: "index_lettings_logs_on_bulk_upload_id" t.index ["created_by_id"], name: "index_lettings_logs_on_created_by_id" t.index ["location_id"], name: "index_lettings_logs_on_location_id" @@ -463,6 +464,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_01_08_152935) do t.string "reader_type", null: false t.bigint "reader_id" t.datetime "timestamp", precision: nil, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.index ["readable_type", "readable_id"], name: "index_read_marks_on_readable_type_and_readable_id" t.index ["reader_id", "reader_type", "readable_type", "readable_id"], name: "read_marks_reader_readable_index", unique: true t.index ["reader_type", "reader_id"], name: "index_read_marks_on_reader_type_and_reader_id" @@ -648,6 +651,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_01_08_152935) do t.integer "old_form_id" t.datetime "values_updated_at" t.bigint "managing_organisation_id" + t.integer "duplicate_set_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" t.index ["managing_organisation_id"], name: "index_sales_logs_on_managing_organisation_id" diff --git a/lib/tasks/set_duplicate_references.rake b/lib/tasks/set_duplicate_references.rake new file mode 100644 index 000000000..82f75f031 --- /dev/null +++ b/lib/tasks/set_duplicate_references.rake @@ -0,0 +1,24 @@ +desc "Set duplicate references for sales and lettings logs" +task set_duplicate_references: :environment do + SalesLog.filter_by_year(2023).duplicate_sets.each do |duplicate_set| + duplicate_set_id = SalesLog.maximum(:duplicate_set_id).to_i + 1 + next if duplicate_set.any? { |_log_id| SalesLog.exists?(duplicate_set_id:) } + + duplicate_set.each do |log_id| + log = SalesLog.find(log_id) + log.duplicate_set_id = duplicate_set_id + log.save!(touch: false, validate: false) + end + end + + LettingsLog.filter_by_year(2023).duplicate_sets.each do |duplicate_set| + duplicate_set_id = LettingsLog.maximum(:duplicate_set_id).to_i + 1 + next if duplicate_set.any? { |_log_id| LettingsLog.exists?(duplicate_set_id:) } + + duplicate_set.each do |log_id| + log = LettingsLog.find(log_id) + log.duplicate_set_id = duplicate_set_id + log.save!(touch: false, validate: false) + end + end +end diff --git a/spec/features/form/form_navigation_spec.rb b/spec/features/form/form_navigation_spec.rb index d248fbfcc..256b16d74 100644 --- a/spec/features/form/form_navigation_spec.rb +++ b/spec/features/form/form_navigation_spec.rb @@ -178,16 +178,25 @@ RSpec.describe "Form Navigation" do end describe "fixing duplicate logs" do + let!(:lettings_log) { create(:lettings_log, :duplicate, created_by: user, duplicate_set_id: 1) } + let!(:second_log) { create(:lettings_log, :duplicate, created_by: user, duplicate_set_id: 1) } + it "shows a correct cancel link" do - visit("lettings-logs/#{id}/tenant-code-test?first_remaining_duplicate_id=x&original_log_id=#{id}&referrer=duplicate_logs") + expect(lettings_log.duplicates.count).to eq(1) + visit("lettings-logs/#{id}/tenant-code-test?first_remaining_duplicate_id=#{second_log.id}&original_log_id=#{id}&referrer=duplicate_logs") click_link(text: "Cancel") expect(page).to have_current_path("/lettings-logs/#{id}/duplicate-logs?original_log_id=#{id}") + lettings_log.reload + expect(lettings_log.duplicates.count).to eq(1) end it "shows a correct Save Changes buttons" do + expect(lettings_log.duplicates.count).to eq(1) visit("lettings-logs/#{id}/tenant-code-test?first_remaining_duplicate_id=#{id}&original_log_id=#{id}&referrer=duplicate_logs") click_button(text: "Save changes") expect(page).to have_current_path("/lettings-logs/#{id}/duplicate-logs?original_log_id=#{id}&referrer=duplicate_logs") + lettings_log.reload + expect(lettings_log.duplicates.count).to eq(1) end end end diff --git a/spec/features/lettings_log_spec.rb b/spec/features/lettings_log_spec.rb index 7dabd3efa..f5aefb6e4 100644 --- a/spec/features/lettings_log_spec.rb +++ b/spec/features/lettings_log_spec.rb @@ -459,6 +459,13 @@ RSpec.describe "Lettings Log Features" do end it "allows keeping the original log and deleting duplicates" do + lettings_log.reload + duplicate_log.reload + expect(lettings_log.duplicates.count).to eq(1) + expect(lettings_log.duplicate_set_id).not_to be_nil + expect(duplicate_log.duplicate_set_id).not_to be_nil + expect(lettings_log.duplicates).to include(duplicate_log) + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/duplicate-logs?original_log_id=#{lettings_log.id}") click_link("Keep this log and delete duplicates", href: "/lettings-logs/#{lettings_log.id}/delete-duplicates?original_log_id=#{lettings_log.id}") expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/delete-duplicates?original_log_id=#{lettings_log.id}") @@ -471,6 +478,14 @@ RSpec.describe "Lettings Log Features" do expect(page).not_to have_content("These logs are duplicates") expect(page).not_to have_link("Keep this log and delete duplicates") expect(page).to have_link("Back to Log #{lettings_log.id}", href: "/lettings-logs/#{lettings_log.id}") + + lettings_log.reload + duplicate_log.reload + + expect(lettings_log.duplicates.count).to eq(0) + expect(duplicate_log.duplicates.count).to eq(0) + expect(lettings_log.duplicate_set_id).to be_nil + expect(duplicate_log.duplicate_set_id).to be_nil end it "allows changing answers on remaining original log" do @@ -483,6 +498,13 @@ RSpec.describe "Lettings Log Features" do end it "allows keeping the duplicate log and deleting the original one" do + lettings_log.reload + duplicate_log.reload + expect(lettings_log.duplicates.count).to eq(1) + expect(lettings_log.duplicate_set_id).not_to be_nil + expect(duplicate_log.duplicate_set_id).not_to be_nil + expect(duplicate_log.duplicates).to include(lettings_log) + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/duplicate-logs?original_log_id=#{lettings_log.id}") click_link("Keep this log and delete duplicates", href: "/lettings-logs/#{duplicate_log.id}/delete-duplicates?original_log_id=#{lettings_log.id}") expect(page).to have_current_path("/lettings-logs/#{duplicate_log.id}/delete-duplicates?original_log_id=#{lettings_log.id}") @@ -495,6 +517,14 @@ RSpec.describe "Lettings Log Features" do expect(page).not_to have_content("These logs are duplicates") expect(page).not_to have_link("Keep this log and delete duplicates") expect(page).to have_link("Back to lettings logs", href: "/lettings-logs") + + lettings_log.reload + duplicate_log.reload + + expect(lettings_log.duplicates.count).to eq(0) + expect(duplicate_log.duplicates.count).to eq(0) + expect(lettings_log.duplicate_set_id).to be_nil + expect(duplicate_log.duplicate_set_id).to be_nil end it "allows changing answers to remaining duplicate log" do @@ -507,6 +537,13 @@ RSpec.describe "Lettings Log Features" do end it "allows deduplicating logs by changing the answers on the duplicate log" do + lettings_log.reload + duplicate_log.reload + expect(lettings_log.duplicates.count).to eq(1) + expect(lettings_log.duplicate_set_id).not_to be_nil + expect(duplicate_log.duplicate_set_id).not_to be_nil + expect(lettings_log.duplicates).to include(duplicate_log) + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/duplicate-logs?original_log_id=#{lettings_log.id}") click_link("Change", href: "/lettings-logs/#{duplicate_log.id}/tenant-code?first_remaining_duplicate_id=#{lettings_log.id}&original_log_id=#{lettings_log.id}&referrer=duplicate_logs") fill_in("lettings-log-tenancycode-field", with: "something else") @@ -516,6 +553,14 @@ RSpec.describe "Lettings Log Features" do expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success") expect(page).to have_content("Log #{duplicate_log.id} is no longer a duplicate and has been removed from the list") expect(page).to have_content("You changed the tenant code.") + + lettings_log.reload + duplicate_log.reload + + expect(lettings_log.duplicates.count).to eq(0) + expect(duplicate_log.duplicates.count).to eq(0) + expect(lettings_log.duplicate_set_id).to be_nil + expect(duplicate_log.duplicate_set_id).to be_nil end it "allows deduplicating logs by changing the answers on the original log" do @@ -527,6 +572,8 @@ RSpec.describe "Lettings Log Features" do expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success") expect(page).to have_content("Log #{lettings_log.id} is no longer a duplicate and has been removed from the list") expect(page).to have_content("You changed the tenant code.") + expect(duplicate_log.duplicates.count).to eq(0) + expect(duplicate_log.duplicate_set_id).to be_nil end end end diff --git a/spec/features/sales_log_spec.rb b/spec/features/sales_log_spec.rb index bac9da761..83fd2f458 100644 --- a/spec/features/sales_log_spec.rb +++ b/spec/features/sales_log_spec.rb @@ -198,6 +198,14 @@ RSpec.describe "Sales Log Features" do end it "allows keeping the original log and deleting duplicates" do + sales_log.reload + duplicate_log.reload + expect(sales_log.duplicates.count).to eq(1) + expect(duplicate_log.duplicates.count).to eq(1) + expect(sales_log.duplicate_set_id).not_to be_nil + expect(duplicate_log.duplicate_set_id).not_to be_nil + expect(sales_log.duplicates).to include(duplicate_log) + expect(page).to have_current_path("/sales-logs/#{sales_log.id}/duplicate-logs?original_log_id=#{sales_log.id}") click_link("Keep this log and delete duplicates", href: "/sales-logs/#{sales_log.id}/delete-duplicates?original_log_id=#{sales_log.id}") expect(page).to have_current_path("/sales-logs/#{sales_log.id}/delete-duplicates?original_log_id=#{sales_log.id}") @@ -210,6 +218,14 @@ RSpec.describe "Sales Log Features" do expect(page).not_to have_content("These logs are duplicates") expect(page).not_to have_link("Keep this log and delete duplicates") expect(page).to have_link("Back to Log #{sales_log.id}", href: "/sales-logs/#{sales_log.id}") + + sales_log.reload + duplicate_log.reload + + expect(sales_log.duplicates.count).to eq(0) + expect(duplicate_log.duplicates.count).to eq(0) + expect(sales_log.duplicate_set_id).to be_nil + expect(duplicate_log.duplicate_set_id).to be_nil end it "allows changing answer on remaining original log" do @@ -222,6 +238,14 @@ RSpec.describe "Sales Log Features" do end it "allows keeping the duplicate log and deleting the original one" do + sales_log.reload + duplicate_log.reload + expect(sales_log.duplicates.count).to eq(1) + expect(duplicate_log.duplicates.count).to eq(1) + expect(sales_log.duplicate_set_id).not_to be_nil + expect(duplicate_log.duplicate_set_id).not_to be_nil + expect(duplicate_log.duplicates).to include(sales_log) + expect(page).to have_current_path("/sales-logs/#{sales_log.id}/duplicate-logs?original_log_id=#{sales_log.id}") click_link("Keep this log and delete duplicates", href: "/sales-logs/#{duplicate_log.id}/delete-duplicates?original_log_id=#{sales_log.id}") expect(page).to have_current_path("/sales-logs/#{duplicate_log.id}/delete-duplicates?original_log_id=#{sales_log.id}") @@ -234,6 +258,14 @@ RSpec.describe "Sales Log Features" do expect(page).not_to have_content("These logs are duplicates") expect(page).not_to have_link("Keep this log and delete duplicates") expect(page).to have_link("Back to sales logs", href: "/sales-logs") + + sales_log.reload + duplicate_log.reload + + expect(sales_log.duplicates.count).to eq(0) + expect(duplicate_log.duplicates.count).to eq(0) + expect(sales_log.duplicate_set_id).to be_nil + expect(duplicate_log.duplicate_set_id).to be_nil end it "allows changing answers on remaining duplicate log" do @@ -246,6 +278,14 @@ RSpec.describe "Sales Log Features" do end it "allows deduplicating logs by changing the answers on the duplicate log" do + sales_log.reload + duplicate_log.reload + expect(sales_log.duplicates.count).to eq(1) + expect(duplicate_log.duplicates.count).to eq(1) + expect(sales_log.duplicate_set_id).not_to be_nil + expect(duplicate_log.duplicate_set_id).not_to be_nil + expect(sales_log.duplicates).to include(duplicate_log) + expect(page).to have_current_path("/sales-logs/#{sales_log.id}/duplicate-logs?original_log_id=#{sales_log.id}") click_link("Change", href: "/sales-logs/#{duplicate_log.id}/purchaser-code?first_remaining_duplicate_id=#{sales_log.id}&original_log_id=#{sales_log.id}&referrer=duplicate_logs") fill_in("sales-log-purchid-field", with: "something else") @@ -255,6 +295,14 @@ RSpec.describe "Sales Log Features" do expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success") expect(page).to have_content("Log #{duplicate_log.id} is no longer a duplicate and has been removed from the list") expect(page).to have_content("You changed the purchaser code.") + + sales_log.reload + duplicate_log.reload + + expect(sales_log.duplicates.count).to eq(0) + expect(duplicate_log.duplicates.count).to eq(0) + expect(sales_log.duplicate_set_id).to be_nil + expect(duplicate_log.duplicate_set_id).to be_nil end it "allows deduplicating logs by changing the answers on the original log" do @@ -266,6 +314,11 @@ RSpec.describe "Sales Log Features" do expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success") expect(page).to have_content("Log #{sales_log.id} is no longer a duplicate and has been removed from the list") expect(page).to have_content("You changed the purchaser code.") + + expect(sales_log.duplicates.count).to eq(0) + expect(duplicate_log.duplicates.count).to eq(0) + expect(sales_log.duplicate_set_id).to be_nil + expect(duplicate_log.duplicate_set_id).to be_nil end end end diff --git a/spec/fixtures/exports/general_needs_log.xml b/spec/fixtures/exports/general_needs_log.xml index 79e904dcf..f1f6a2723 100644 --- a/spec/fixtures/exports/general_needs_log.xml +++ b/spec/fixtures/exports/general_needs_log.xml @@ -144,6 +144,7 @@ 1 + {id} {owning_org_id} DLUHC diff --git a/spec/fixtures/exports/general_needs_log_23_24.xml b/spec/fixtures/exports/general_needs_log_23_24.xml index a1b53162d..19f6c8c79 100644 --- a/spec/fixtures/exports/general_needs_log_23_24.xml +++ b/spec/fixtures/exports/general_needs_log_23_24.xml @@ -145,6 +145,7 @@ 1 + {id} {owning_org_id} DLUHC diff --git a/spec/fixtures/exports/supported_housing_logs.xml b/spec/fixtures/exports/supported_housing_logs.xml index bc80fd49b..018c158b0 100644 --- a/spec/fixtures/exports/supported_housing_logs.xml +++ b/spec/fixtures/exports/supported_housing_logs.xml @@ -143,6 +143,7 @@ 1 + {id} {owning_org_id} DLUHC diff --git a/spec/fixtures/files/lettings_log_csv_export_codes.csv b/spec/fixtures/files/lettings_log_csv_export_codes.csv index cdd43809f..28e3832c6 100644 --- a/spec/fixtures/files/lettings_log_csv_export_codes.csv +++ b/spec/fixtures/files/lettings_log_csv_export_codes.csv @@ -1,2 +1,2 @@ -id,status,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,rent_type_detail,irproduct,irproduct_other,lar,tenancycode,propcode,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,national,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate -,completed,s.port@jeemayle.com,false,2023-06-26T00:00:00+01:00,,2023-06-26T00:00:00+01:00,1,,,2023,DLUHC,DLUHC,1,7,0,2023-06-26,2,1,1,,2,HIJKLMN,ABCDEFG,0,,,fake address,,London,,NW9 5LL,false,Barnet,E09000003,0,2,6,2,2,7,1,1,3,2023-06-24,,,1,2023-06-25,,3,1,4,,2,,1,4,,1,4,0,0,2,35,,F,0,2,13,0,0,P,32,M,6,1,R,-9,R,10,0,R,-9,R,10,,,,,,,,,,,,,,,,,,,,,1,4,1,2,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,7,4,,6,2,1,0,TN23 6LZ,1,false,Ashford,E07000105,1,0,1,0,0,0,0,0,1,,2,,0,0,268,1,,6,1,1,,0,2,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,1,0,12.0,6.0,,,,,,,,,,,,,,,,,,,, +id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,rent_type_detail,irproduct,irproduct_other,lar,tenancycode,propcode,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,national,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate +,completed,,s.port@jeemayle.com,false,2023-06-26T00:00:00+01:00,,2023-06-26T00:00:00+01:00,1,,,2023,DLUHC,DLUHC,1,7,0,2023-06-26,2,1,1,,2,HIJKLMN,ABCDEFG,0,,,fake address,,London,,NW9 5LL,false,Barnet,E09000003,0,2,6,2,2,7,1,1,3,2023-06-24,,,1,2023-06-25,,3,1,4,,2,,1,4,,1,4,0,0,2,35,,F,0,2,13,0,0,P,32,M,6,1,R,-9,R,10,0,R,-9,R,10,,,,,,,,,,,,,,,,,,,,,1,4,1,2,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,7,4,,6,2,1,0,TN23 6LZ,1,false,Ashford,E07000105,1,0,1,0,0,0,0,0,1,,2,,0,0,268,1,,6,1,1,,0,2,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,1,0,12.0,6.0,,,,,,,,,,,,,,,,,,,, diff --git a/spec/fixtures/files/lettings_log_csv_export_labels.csv b/spec/fixtures/files/lettings_log_csv_export_labels.csv index 7d76b9a36..088e85d22 100644 --- a/spec/fixtures/files/lettings_log_csv_export_labels.csv +++ b/spec/fixtures/files/lettings_log_csv_export_labels.csv @@ -1,2 +1,2 @@ -id,status,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,rent_type_detail,irproduct,irproduct_other,lar,tenancycode,propcode,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,national,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate -,completed,s.port@jeemayle.com,false,2023-06-26T00:00:00+01:00,,2023-06-26T00:00:00+01:00,single log,,,2023,DLUHC,DLUHC,General needs,Affordable rent general needs local authority,No,2023-06-26,2,Affordable Rent,Rent to Buy,,No,HIJKLMN,ABCDEFG,No,,,fake address,,London,,NW9 5LL,No,Barnet,E09000003,No,Affordable rent basis,Tenant abandoned property,No,2,House,Purpose built,Yes,3,2023-06-24,,,Yes,2023-06-25,,Don’t know,Yes,Assured Shorthold Tenancy (AST) – Fixed term,,2,,Yes,4,,Yes,4,0,0,2,35,,Female,White,Irish,Tenant prefers not to say,Other,Yes,Partner,32,Male,Not seeking work,No,Prefers not to say,Not known,Prefers not to say,Prefers not to say,Yes,Person prefers not to say,Not known,Person prefers not to say,Person prefers not to say,,,,,,,,,,,,,,,,,,,,,Yes – the person is a current or former regular,No – they left up to and including 5 years ago,Yes,No,Yes,Fully wheelchair accessible housing,Yes,No,No,No,No,No,No,Yes,No,No,Yes,No,No,No,No,No,No,No,Less than 1 year,1 year but under 2 years,Loss of tied accommodation,,Other supported housing,2,No,Yes,TN23 6LZ,Yes,No,Ashford,E07000105,Yes,,Yes,,,,No,No,Yes,,Tenant applied directly (no referral or nomination),,Yes,No,268,Weekly,,Universal Credit housing element,Yes,All,,No,Every 2 weeks,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,Yes,Yes,12.0,6.0,,,,,,,,,,,,,,,,,,,, +id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,rent_type_detail,irproduct,irproduct_other,lar,tenancycode,propcode,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,pregnancy_value_check,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,national,ecstat1,details_known_2,relat2,age2,sex2,ecstat2,details_known_3,relat3,age3,sex3,ecstat3,details_known_4,relat4,age4,sex4,ecstat4,details_known_5,relat5,age5,sex5,ecstat5,details_known_6,relat6,age6,sex6,ecstat6,details_known_7,relat7,age7,sex7,ecstat7,details_known_8,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate +,completed,,s.port@jeemayle.com,false,2023-06-26T00:00:00+01:00,,2023-06-26T00:00:00+01:00,single log,,,2023,DLUHC,DLUHC,General needs,Affordable rent general needs local authority,No,2023-06-26,2,Affordable Rent,Rent to Buy,,No,HIJKLMN,ABCDEFG,No,,,fake address,,London,,NW9 5LL,No,Barnet,E09000003,No,Affordable rent basis,Tenant abandoned property,No,2,House,Purpose built,Yes,3,2023-06-24,,,Yes,2023-06-25,,Don’t know,Yes,Assured Shorthold Tenancy (AST) – Fixed term,,2,,Yes,4,,Yes,4,0,0,2,35,,Female,White,Irish,Tenant prefers not to say,Other,Yes,Partner,32,Male,Not seeking work,No,Prefers not to say,Not known,Prefers not to say,Prefers not to say,Yes,Person prefers not to say,Not known,Person prefers not to say,Person prefers not to say,,,,,,,,,,,,,,,,,,,,,Yes – the person is a current or former regular,No – they left up to and including 5 years ago,Yes,No,Yes,Fully wheelchair accessible housing,Yes,No,No,No,No,No,No,Yes,No,No,Yes,No,No,No,No,No,No,No,Less than 1 year,1 year but under 2 years,Loss of tied accommodation,,Other supported housing,2,No,Yes,TN23 6LZ,Yes,No,Ashford,E07000105,Yes,,Yes,,,,No,No,Yes,,Tenant applied directly (no referral or nomination),,Yes,No,268,Weekly,,Universal Credit housing element,Yes,All,,No,Every 2 weeks,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,,,,Yes,Yes,12.0,6.0,,,,,,,,,,,,,,,,,,,, diff --git a/spec/fixtures/files/lettings_log_csv_export_non_support_codes.csv b/spec/fixtures/files/lettings_log_csv_export_non_support_codes.csv index 9fedc109d..e985d96b7 100644 --- a/spec/fixtures/files/lettings_log_csv_export_non_support_codes.csv +++ b/spec/fixtures/files/lettings_log_csv_export_non_support_codes.csv @@ -1,2 +1,2 @@ -id,status,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,rent_type_detail,irproduct,irproduct_other,lar,tenancycode,propcode,uprn_known,uprn,address_line1,address_line2,town_or_city,county,postcode_full,la_label,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,refused,age1,sex1,ethnic_group,ethnic,national,ecstat1,relat2,age2,sex2,ecstat2,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,relat7,age7,sex7,ecstat7,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,homeless,ppcodenk,ppostcode_full,prevloc_label,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,incref,earnings,incfreq,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,scharge,pscharge,supcharg,tcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate -,completed,choreographer@owtluk.com,false,2023-06-26T00:00:00+01:00,,2023-06-26T00:00:00+01:00,1,2023,DLUHC,DLUHC,1,7,0,2023-06-26,2,1,1,,2,HIJKLMN,ABCDEFG,0,,fake address,,London,,NW9 5LL,Barnet,2,6,2,2,7,1,1,3,2023-06-24,1,,1,2023-06-25,,3,1,4,,2,,1,4,1,35,F,0,2,13,0,P,32,M,6,R,-9,R,10,R,-9,R,10,,,,,,,,,,,,,,,,,1,4,1,2,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,7,4,,6,1,0,TN23 6LZ,Ashford,1,0,1,0,0,0,0,0,1,,2,,0,268,1,6,1,1,,0,2,,,,,200.0,50.0,40.0,35.0,325.0,,,,1,12.0,,,,,,,,,,,,,,,,,,,, +id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,rent_type_detail,irproduct,irproduct_other,lar,tenancycode,propcode,uprn_known,uprn,address_line1,address_line2,town_or_city,county,postcode_full,la_label,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,refused,age1,sex1,ethnic_group,ethnic,national,ecstat1,relat2,age2,sex2,ecstat2,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,relat7,age7,sex7,ecstat7,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,homeless,ppcodenk,ppostcode_full,prevloc_label,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,incref,earnings,incfreq,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,scharge,pscharge,supcharg,tcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate +,completed,,choreographer@owtluk.com,false,2023-06-26T00:00:00+01:00,,2023-06-26T00:00:00+01:00,1,2023,DLUHC,DLUHC,1,7,0,2023-06-26,2,1,1,,2,HIJKLMN,ABCDEFG,0,,fake address,,London,,NW9 5LL,Barnet,2,6,2,2,7,1,1,3,2023-06-24,1,,1,2023-06-25,,3,1,4,,2,,1,4,1,35,F,0,2,13,0,P,32,M,6,R,-9,R,10,R,-9,R,10,,,,,,,,,,,,,,,,,1,4,1,2,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,7,4,,6,1,0,TN23 6LZ,Ashford,1,0,1,0,0,0,0,0,1,,2,,0,268,1,6,1,1,,0,2,,,,,200.0,50.0,40.0,35.0,325.0,,,,1,12.0,,,,,,,,,,,,,,,,,,,, diff --git a/spec/fixtures/files/lettings_log_csv_export_non_support_labels.csv b/spec/fixtures/files/lettings_log_csv_export_non_support_labels.csv index 563a231b6..8424dbccd 100644 --- a/spec/fixtures/files/lettings_log_csv_export_non_support_labels.csv +++ b/spec/fixtures/files/lettings_log_csv_export_non_support_labels.csv @@ -1,2 +1,2 @@ -id,status,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,rent_type_detail,irproduct,irproduct_other,lar,tenancycode,propcode,uprn_known,uprn,address_line1,address_line2,town_or_city,county,postcode_full,la_label,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,refused,age1,sex1,ethnic_group,ethnic,national,ecstat1,relat2,age2,sex2,ecstat2,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,relat7,age7,sex7,ecstat7,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,homeless,ppcodenk,ppostcode_full,prevloc_label,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,incref,earnings,incfreq,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,scharge,pscharge,supcharg,tcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate -,completed,choreographer@owtluk.com,false,2023-06-26T00:00:00+01:00,,2023-06-26T00:00:00+01:00,single log,2023,DLUHC,DLUHC,General needs,Affordable rent general needs local authority,No,2023-06-26,2,Affordable Rent,Rent to Buy,,No,HIJKLMN,ABCDEFG,No,,fake address,,London,,NW9 5LL,Barnet,Affordable rent basis,Tenant abandoned property,No,2,House,Purpose built,Yes,3,2023-06-24,1,,Yes,2023-06-25,,Don’t know,Yes,Assured Shorthold Tenancy (AST) – Fixed term,,2,,Yes,4,Yes,35,Female,White,Irish,Tenant prefers not to say,Other,Partner,32,Male,Not seeking work,Prefers not to say,Not known,Prefers not to say,Prefers not to say,Person prefers not to say,Not known,Person prefers not to say,Person prefers not to say,,,,,,,,,,,,,,,,,Yes – the person is a current or former regular,No – they left up to and including 5 years ago,Yes,No,Yes,Fully wheelchair accessible housing,Yes,No,No,No,No,No,No,Yes,No,No,Yes,No,No,No,No,No,No,No,Less than 1 year,1 year but under 2 years,Loss of tied accommodation,,Other supported housing,No,Yes,TN23 6LZ,Ashford,Yes,,Yes,,,,No,No,Yes,,Tenant applied directly (no referral or nomination),,No,268,Weekly,Universal Credit housing element,Yes,All,,No,Every 2 weeks,,,,,200.0,50.0,40.0,35.0,325.0,,,,Yes,12.0,,,,,,,,,,,,,,,,,,,, +id,status,duplicate_set_id,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,rent_type_detail,irproduct,irproduct_other,lar,tenancycode,propcode,uprn_known,uprn,address_line1,address_line2,town_or_city,county,postcode_full,la_label,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,refused,age1,sex1,ethnic_group,ethnic,national,ecstat1,relat2,age2,sex2,ecstat2,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,relat7,age7,sex7,ecstat7,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,homeless,ppcodenk,ppostcode_full,prevloc_label,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_none,referral,referral_value_check,incref,earnings,incfreq,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,scharge,pscharge,supcharg,tcharge,scharge_value_check,pscharge_value_check,supcharg_value_check,hbrentshortfall,tshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_local_authority,location_startdate +,completed,,choreographer@owtluk.com,false,2023-06-26T00:00:00+01:00,,2023-06-26T00:00:00+01:00,single log,2023,DLUHC,DLUHC,General needs,Affordable rent general needs local authority,No,2023-06-26,2,Affordable Rent,Rent to Buy,,No,HIJKLMN,ABCDEFG,No,,fake address,,London,,NW9 5LL,Barnet,Affordable rent basis,Tenant abandoned property,No,2,House,Purpose built,Yes,3,2023-06-24,1,,Yes,2023-06-25,,Don’t know,Yes,Assured Shorthold Tenancy (AST) – Fixed term,,2,,Yes,4,Yes,35,Female,White,Irish,Tenant prefers not to say,Other,Partner,32,Male,Not seeking work,Prefers not to say,Not known,Prefers not to say,Prefers not to say,Person prefers not to say,Not known,Person prefers not to say,Person prefers not to say,,,,,,,,,,,,,,,,,Yes – the person is a current or former regular,No – they left up to and including 5 years ago,Yes,No,Yes,Fully wheelchair accessible housing,Yes,No,No,No,No,No,No,Yes,No,No,Yes,No,No,No,No,No,No,No,Less than 1 year,1 year but under 2 years,Loss of tied accommodation,,Other supported housing,No,Yes,TN23 6LZ,Ashford,Yes,,Yes,,,,No,No,Yes,,Tenant applied directly (no referral or nomination),,No,268,Weekly,Universal Credit housing element,Yes,All,,No,Every 2 weeks,,,,,200.0,50.0,40.0,35.0,325.0,,,,Yes,12.0,,,,,,,,,,,,,,,,,,,, diff --git a/spec/fixtures/files/sales_logs_csv_export_codes.csv b/spec/fixtures/files/sales_logs_csv_export_codes.csv index a82240317..46e63c3db 100644 --- a/spec/fixtures/files/sales_logs_csv_export_codes.csv +++ b/spec/fixtures/files/sales_logs_csv_export_codes.csv @@ -1,2 +1,2 @@ -id,status,created_at,updated_at,old_form_id,collection_start_year,creation_method,is_dpo,owning_organisation_name,managing_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,beds,proptype,builtype,pcodenk,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,wchair,noint,privacynotice,age1,sex1,ethnic_group,ethnic,national,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant -,completed,2023-02-08T00:00:00+00:00,2023-02-08T00:00:00+00:00,,2022,1,false,DLUHC,DLUHC,billyboy@eyeklaud.com,8,2,2023,,2,8,,,,1,1,2,1,1,0,,,Address line 1,,Town or city,,SW1A,1AA,1,E09000003,Barnet,1,2,1,30,X,17,17,18,1,1,P,35,X,17,,13,1,1,3,C,14,X,9,X,-9,X,3,R,-9,R,10,,,,,1,1,,,0,,,1,1,1,1,,3,,1,4,5,1,1,0,10000,1,0,10000,1,4,1,,1,2,10,,,,,,,,,,,,,,,,,110000.0,,1,20000.0,5,,10,1,80000.0,,,1,100.0,,10000.0 +id,status,duplicate_set_id,created_at,updated_at,old_form_id,collection_start_year,creation_method,is_dpo,owning_organisation_name,managing_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,beds,proptype,builtype,pcodenk,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,wchair,noint,privacynotice,age1,sex1,ethnic_group,ethnic,national,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant +,completed,,2023-02-08T00:00:00+00:00,2023-02-08T00:00:00+00:00,,2022,1,false,DLUHC,DLUHC,billyboy@eyeklaud.com,8,2,2023,,2,8,,,,1,1,2,1,1,0,,,Address line 1,,Town or city,,SW1A,1AA,1,E09000003,Barnet,1,2,1,30,X,17,17,18,1,1,P,35,X,17,,13,1,1,3,C,14,X,9,X,-9,X,3,R,-9,R,10,,,,,1,1,,,0,,,1,1,1,1,,3,,1,4,5,1,1,0,10000,1,0,10000,1,4,1,,1,2,10,,,,,,,,,,,,,,,,,110000.0,,1,20000.0,5,,10,1,80000.0,,,1,100.0,,10000.0 diff --git a/spec/fixtures/files/sales_logs_csv_export_labels.csv b/spec/fixtures/files/sales_logs_csv_export_labels.csv index 6c38f5e58..e65065f07 100644 --- a/spec/fixtures/files/sales_logs_csv_export_labels.csv +++ b/spec/fixtures/files/sales_logs_csv_export_labels.csv @@ -1,2 +1,2 @@ -id,status,created_at,updated_at,old_form_id,collection_start_year,creation_method,is_dpo,owning_organisation_name,managing_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,beds,proptype,builtype,pcodenk,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,wchair,noint,privacynotice,age1,sex1,ethnic_group,ethnic,national,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant -,completed,2023-02-08T00:00:00+00:00,2023-02-08T00:00:00+00:00,,2022,single log,false,DLUHC,DLUHC,billyboy@eyeklaud.com,8,2,2023,,Yes - a discounted ownership scheme,Right to Acquire (RTA),,,,Yes,Yes,2,Flat or maisonette,Purpose built,Yes,,,Address line 1,,Town or city,,SW1A,1AA,Yes,E09000003,Barnet,Yes,Yes,1,30,Non-binary,Buyer 1 prefers not to say,17,United Kingdom,Full-time - 30 hours or more,Yes,Partner,35,Non-binary,17,,13,Full-time - 30 hours or more,Yes,3,Child,14,Non-binary,Child under 16,Other,Not known,Non-binary,"In government training into work, such as New Deal",Prefers not to say,Not known,Prefers not to say,Prefers not to say,,,,,Local authority tenant,No,,,No,,,1,1,1,1,,3,,Yes,Yes,No,Yes,Yes,Yes,10000,Yes,Yes,10000,Yes,"Don’t know ",No,,Yes,2,10,,,,,,,,,,,,,,,,,110000.0,,Yes,20000.0,Cambridge Building Society,,10,Yes,80000.0,,,Yes,100.0,,10000.0 +id,status,duplicate_set_id,created_at,updated_at,old_form_id,collection_start_year,creation_method,is_dpo,owning_organisation_name,managing_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,beds,proptype,builtype,pcodenk,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,wchair,noint,privacynotice,age1,sex1,ethnic_group,ethnic,national,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant +,completed,,2023-02-08T00:00:00+00:00,2023-02-08T00:00:00+00:00,,2022,single log,false,DLUHC,DLUHC,billyboy@eyeklaud.com,8,2,2023,,Yes - a discounted ownership scheme,Right to Acquire (RTA),,,,Yes,Yes,2,Flat or maisonette,Purpose built,Yes,,,Address line 1,,Town or city,,SW1A,1AA,Yes,E09000003,Barnet,Yes,Yes,1,30,Non-binary,Buyer 1 prefers not to say,17,United Kingdom,Full-time - 30 hours or more,Yes,Partner,35,Non-binary,17,,13,Full-time - 30 hours or more,Yes,3,Child,14,Non-binary,Child under 16,Other,Not known,Non-binary,"In government training into work, such as New Deal",Prefers not to say,Not known,Prefers not to say,Prefers not to say,,,,,Local authority tenant,No,,,No,,,1,1,1,1,,3,,Yes,Yes,No,Yes,Yes,Yes,10000,Yes,Yes,10000,Yes,"Don’t know ",No,,Yes,2,10,,,,,,,,,,,,,,,,,110000.0,,Yes,20000.0,Cambridge Building Society,,10,Yes,80000.0,,,Yes,100.0,,10000.0 diff --git a/spec/lib/tasks/set_duplicate_references_spec.rb b/spec/lib/tasks/set_duplicate_references_spec.rb new file mode 100644 index 000000000..32918b974 --- /dev/null +++ b/spec/lib/tasks/set_duplicate_references_spec.rb @@ -0,0 +1,235 @@ +require "rails_helper" +require "rake" + +RSpec.describe "set_duplicate_references" do + describe ":set_duplicate_references", type: :task do + subject(:task) { Rake::Task["set_duplicate_references"] } + + before do + Rake.application.rake_require("tasks/set_duplicate_references") + Rake::Task.define_task(:environment) + task.reenable + end + + context "when the rake task is run" do + context "and there are sales duplicates in 1 organisation" do + let(:user) { create(:user) } + let!(:sales_log) { create(:sales_log, :duplicate, created_by: user) } + let!(:duplicate_sales_log) { create(:sales_log, :duplicate, created_by: user) } + let!(:second_duplicate_sales_log) { create(:sales_log, :duplicate, created_by: user) } + let!(:sales_log_without_duplicates) { create(:sales_log, created_by: user) } + + it "creates duplicate references for sales logs" do + initial_sales_log_updated_at = sales_log.updated_at + expect(sales_log.duplicates.count).to eq(0) + expect(sales_log.duplicate_set_id).to be_nil + expect(duplicate_sales_log.duplicates.count).to eq(0) + expect(duplicate_sales_log.duplicate_set_id).to be_nil + expect(second_duplicate_sales_log.duplicates.count).to eq(0) + expect(second_duplicate_sales_log.duplicate_set_id).to be_nil + expect(sales_log_without_duplicates.duplicates.count).to eq(0) + expect(sales_log_without_duplicates.duplicate_set_id).to be_nil + + task.invoke + sales_log.reload + duplicate_sales_log.reload + second_duplicate_sales_log.reload + sales_log_without_duplicates.reload + + expect(sales_log.duplicates.count).to eq(2) + expect(duplicate_sales_log.duplicates.count).to eq(2) + expect(second_duplicate_sales_log.duplicates.count).to eq(2) + expect(sales_log_without_duplicates.duplicates.count).to eq(0) + expect(sales_log.duplicate_set_id).to eq(duplicate_sales_log.duplicate_set_id) + expect(sales_log.duplicate_set_id).to eq(second_duplicate_sales_log.duplicate_set_id) + expect(sales_log.updated_at).to eq(initial_sales_log_updated_at) + end + end + + context "and there are sales duplicates in multiple organisations" do + let(:user) { create(:user) } + let!(:sales_log) { create(:sales_log, :duplicate, created_by: user) } + let!(:duplicate_sales_log) { create(:sales_log, :duplicate, created_by: user) } + let!(:sales_log_without_duplicates) { create(:sales_log, created_by: user) } + let(:other_user) { create(:user) } + let!(:other_sales_log) { create(:sales_log, :duplicate, created_by: other_user) } + let!(:other_duplicate_sales_log) { create(:sales_log, :duplicate, created_by: other_user) } + let!(:other_sales_log_without_duplicates) { create(:sales_log, created_by: other_user) } + + it "creates separate duplicate references for sales logs" do + expect(sales_log.duplicates.count).to eq(0) + expect(sales_log.duplicate_set_id).to be_nil + expect(duplicate_sales_log.duplicates.count).to eq(0) + expect(duplicate_sales_log.duplicate_set_id).to be_nil + expect(sales_log_without_duplicates.duplicates.count).to eq(0) + expect(sales_log_without_duplicates.duplicate_set_id).to be_nil + + expect(other_sales_log.duplicates.count).to eq(0) + expect(other_sales_log.duplicate_set_id).to be_nil + expect(other_duplicate_sales_log.duplicates.count).to eq(0) + expect(other_duplicate_sales_log.duplicate_set_id).to be_nil + expect(other_sales_log_without_duplicates.duplicates.count).to eq(0) + expect(other_sales_log_without_duplicates.duplicate_set_id).to be_nil + + task.invoke + sales_log.reload + duplicate_sales_log.reload + sales_log_without_duplicates.reload + other_sales_log.reload + other_duplicate_sales_log.reload + + expect(sales_log.duplicates.count).to eq(1) + expect(duplicate_sales_log.duplicates.count).to eq(1) + expect(sales_log_without_duplicates.duplicates.count).to eq(0) + expect(sales_log.duplicate_set_id).to eq(duplicate_sales_log.duplicate_set_id) + + expect(other_sales_log.duplicates.count).to eq(1) + expect(other_duplicate_sales_log.duplicates.count).to eq(1) + expect(other_sales_log_without_duplicates.duplicates.count).to eq(0) + expect(other_sales_log.duplicate_set_id).to eq(other_duplicate_sales_log.duplicate_set_id) + expect(other_sales_log.duplicate_set_id).not_to eq(sales_log.duplicate_set_id) + end + end + + context "and there are sales duplicates for non 2023/24 collection period" do + let(:user) { create(:user) } + let!(:sales_log) { create(:sales_log, :duplicate, created_by: user) } + let!(:duplicate_sales_log) { create(:sales_log, :duplicate, created_by: user) } + + before do + sales_log.saledate = Time.zone.local(2022, 4, 4) + sales_log.save!(validate: false) + duplicate_sales_log.saledate = Time.zone.local(2022, 4, 4) + duplicate_sales_log.save!(validate: false) + end + + it "does not create duplicate references for sales logs" do + expect(sales_log.duplicates.count).to eq(0) + expect(sales_log.duplicate_set_id).to be_nil + expect(duplicate_sales_log.duplicates.count).to eq(0) + expect(duplicate_sales_log.duplicate_set_id).to be_nil + + task.invoke + sales_log.reload + duplicate_sales_log.reload + + expect(sales_log.duplicates.count).to eq(0) + expect(sales_log.duplicate_set_id).to be_nil + expect(duplicate_sales_log.duplicates.count).to eq(0) + expect(duplicate_sales_log.duplicate_set_id).to be_nil + end + end + + context "and there are lettings duplicates in 1 organisation" do + let(:user) { create(:user) } + let!(:lettings_log) { create(:lettings_log, :duplicate, created_by: user) } + let!(:duplicate_lettings_log) { create(:lettings_log, :duplicate, created_by: user) } + let!(:second_duplicate_lettings_log) { create(:lettings_log, :duplicate, created_by: user) } + let!(:lettings_log_without_duplicates) { create(:lettings_log, created_by: user) } + + it "creates duplicate references for lettings logs" do + initial_lettings_log_updated_at = lettings_log.updated_at + expect(lettings_log.duplicates.count).to eq(0) + expect(lettings_log.duplicate_set_id).to be_nil + expect(duplicate_lettings_log.duplicates.count).to eq(0) + expect(duplicate_lettings_log.duplicate_set_id).to be_nil + expect(second_duplicate_lettings_log.duplicates.count).to eq(0) + expect(second_duplicate_lettings_log.duplicate_set_id).to be_nil + expect(lettings_log_without_duplicates.duplicates.count).to eq(0) + expect(lettings_log_without_duplicates.duplicate_set_id).to be_nil + + task.invoke + lettings_log.reload + duplicate_lettings_log.reload + second_duplicate_lettings_log.reload + lettings_log_without_duplicates.reload + + expect(lettings_log.duplicates.count).to eq(2) + expect(duplicate_lettings_log.duplicates.count).to eq(2) + expect(second_duplicate_lettings_log.duplicates.count).to eq(2) + expect(lettings_log_without_duplicates.duplicates.count).to eq(0) + expect(lettings_log_without_duplicates.duplicate_set_id).to be_nil + expect(lettings_log.duplicate_set_id).to eq(duplicate_lettings_log.duplicate_set_id) + expect(lettings_log.duplicate_set_id).to eq(second_duplicate_lettings_log.duplicate_set_id) + expect(lettings_log.updated_at).to eq(initial_lettings_log_updated_at) + end + end + + context "and there are lettings duplicates in multiple organisations" do + let(:user) { create(:user) } + let!(:lettings_log) { create(:lettings_log, :duplicate, created_by: user) } + let!(:duplicate_lettings_log) { create(:lettings_log, :duplicate, created_by: user) } + let!(:lettings_log_without_duplicates) { create(:lettings_log, created_by: user) } + let(:other_user) { create(:user) } + let!(:other_lettings_log) { create(:lettings_log, :duplicate, created_by: other_user) } + let!(:other_duplicate_lettings_log) { create(:lettings_log, :duplicate, created_by: other_user) } + let!(:other_lettings_log_without_duplicates) { create(:lettings_log, created_by: other_user) } + + it "creates separate duplicate references for lettings logs" do + expect(lettings_log.duplicates.count).to eq(0) + expect(lettings_log.duplicate_set_id).to be_nil + expect(duplicate_lettings_log.duplicates.count).to eq(0) + expect(duplicate_lettings_log.duplicate_set_id).to be_nil + expect(lettings_log_without_duplicates.duplicates.count).to eq(0) + expect(lettings_log_without_duplicates.duplicate_set_id).to be_nil + + expect(other_lettings_log.duplicates.count).to eq(0) + expect(other_lettings_log.duplicate_set_id).to be_nil + expect(other_duplicate_lettings_log.duplicates.count).to eq(0) + expect(other_duplicate_lettings_log.duplicate_set_id).to be_nil + expect(other_lettings_log_without_duplicates.duplicates.count).to eq(0) + expect(other_lettings_log_without_duplicates.duplicate_set_id).to be_nil + + task.invoke + lettings_log.reload + duplicate_lettings_log.reload + lettings_log_without_duplicates.reload + other_lettings_log.reload + other_duplicate_lettings_log.reload + + expect(lettings_log.duplicates.count).to eq(1) + expect(duplicate_lettings_log.duplicates.count).to eq(1) + expect(lettings_log_without_duplicates.duplicates.count).to eq(0) + expect(lettings_log_without_duplicates.duplicate_set_id).to be_nil + expect(lettings_log.duplicate_set_id).to eq(duplicate_lettings_log.duplicate_set_id) + + expect(other_lettings_log.duplicates.count).to eq(1) + expect(other_duplicate_lettings_log.duplicates.count).to eq(1) + expect(other_lettings_log_without_duplicates.duplicates.count).to eq(0) + expect(other_lettings_log_without_duplicates.duplicate_set_id).to be_nil + expect(other_lettings_log.duplicate_set_id).to eq(other_duplicate_lettings_log.duplicate_set_id) + expect(other_lettings_log.duplicate_set_id).not_to eq(lettings_log.duplicate_set_id) + end + end + + context "and there are lettings duplicates for non 2023/24 collection period" do + let(:user) { create(:user) } + let!(:lettings_log) { create(:lettings_log, :duplicate, created_by: user) } + let!(:duplicate_lettings_log) { create(:lettings_log, :duplicate, created_by: user) } + + before do + lettings_log.startdate = Time.zone.local(2022, 4, 4) + lettings_log.save!(validate: false) + duplicate_lettings_log.startdate = Time.zone.local(2022, 4, 4) + duplicate_lettings_log.save!(validate: false) + end + + it "does not create duplicate references for lettings logs" do + expect(lettings_log.duplicates.count).to eq(0) + expect(lettings_log.duplicate_set_id).to be_nil + expect(duplicate_lettings_log.duplicates.count).to eq(0) + expect(duplicate_lettings_log.duplicate_set_id).to be_nil + + task.invoke + lettings_log.reload + duplicate_lettings_log.reload + + expect(lettings_log.duplicates.count).to eq(0) + expect(lettings_log.duplicate_set_id).to be_nil + expect(duplicate_lettings_log.duplicates.count).to eq(0) + expect(duplicate_lettings_log.duplicate_set_id).to be_nil + end + end + end + end +end diff --git a/spec/requests/delete_logs_controller_spec.rb b/spec/requests/delete_logs_controller_spec.rb index cfb8bb069..58ccae200 100644 --- a/spec/requests/delete_logs_controller_spec.rb +++ b/spec/requests/delete_logs_controller_spec.rb @@ -247,6 +247,53 @@ RSpec.describe "DeleteLogs", type: :request do expect(log_2.discarded_at).to be nil end end + + context "when an authorized user deletes a log that had duplicates" do + context "and only 1 log remains in the duplicate set" do + let!(:log_1) { create(:lettings_log, :duplicate, duplicate_set_id: 5, created_by: user) } + let!(:log_2) { create(:lettings_log, :duplicate, duplicate_set_id: 5, created_by: user) } + let!(:log_3) { create(:lettings_log, :duplicate, duplicate_set_id: 5, created_by: user) } + + it "deletes the log and marks related logs deduplicated" do + delete delete_logs_lettings_logs_path, params: params + log_1.reload + expect(log_1.status).to eq "deleted" + expect(log_1.discarded_at).not_to be nil + expect(log_1.duplicates.count).to eq(0) + expect(log_1.duplicate_set_id).to be nil + log_2.reload + expect(log_2.status).to eq "deleted" + expect(log_2.discarded_at).not_to be nil + expect(log_2.duplicates.count).to eq(0) + expect(log_2.duplicate_set_id).to be nil + log_3.reload + expect(log_3.duplicates.count).to eq(0) + expect(log_3.duplicate_set_id).to be nil + end + end + + context "and multiple logs remains in the duplicate set" do + let!(:log_1) { create(:lettings_log, :duplicate, duplicate_set_id: 5, created_by: user) } + let!(:log_2) { create(:lettings_log, :duplicate, duplicate_set_id: 5, created_by: user) } + let!(:log_3) { create(:lettings_log, :duplicate, duplicate_set_id: 5, created_by: user) } + let(:params) { { ids: [log_1.id] } } + + it "deletes the log and marks related logs deduplicated" do + delete delete_logs_lettings_logs_path, params: params + log_1.reload + expect(log_1.status).to eq "deleted" + expect(log_1.discarded_at).not_to be nil + expect(log_1.duplicates.count).to eq(0) + expect(log_1.duplicate_set_id).to be nil + log_2.reload + log_3.reload + expect(log_2.duplicates.count).to eq(1) + expect(log_3.duplicates.count).to eq(1) + expect(log_3.duplicate_set_id).not_to be nil + expect(log_3.duplicate_set_id).to eq(log_2.duplicate_set_id) + end + end + end end describe "GET sales-logs/delete-logs" do @@ -487,6 +534,53 @@ RSpec.describe "DeleteLogs", type: :request do expect(log_2.discarded_at).to be nil end end + + context "when an authorized user deletes a log that had duplicates" do + context "and only 1 log remains in the duplicate set" do + let!(:log_1) { create(:sales_log, :duplicate, duplicate_set_id: 5, created_by: user) } + let!(:log_2) { create(:sales_log, :duplicate, duplicate_set_id: 5, created_by: user) } + let!(:log_3) { create(:sales_log, :duplicate, duplicate_set_id: 5, created_by: user) } + + it "deletes the log and marks related logs deduplicated" do + delete delete_logs_sales_logs_path, params: params + log_1.reload + expect(log_1.status).to eq "deleted" + expect(log_1.discarded_at).not_to be nil + expect(log_1.duplicates.count).to eq(0) + expect(log_1.duplicate_set_id).to be nil + log_2.reload + expect(log_2.status).to eq "deleted" + expect(log_2.discarded_at).not_to be nil + expect(log_2.duplicates.count).to eq(0) + expect(log_2.duplicate_set_id).to be nil + log_3.reload + expect(log_3.duplicates.count).to eq(0) + expect(log_3.duplicate_set_id).to be nil + end + end + + context "and multiple logs remains in the duplicate set" do + let!(:log_1) { create(:sales_log, :duplicate, duplicate_set_id: 5, created_by: user) } + let!(:log_2) { create(:sales_log, :duplicate, duplicate_set_id: 5, created_by: user) } + let!(:log_3) { create(:sales_log, :duplicate, duplicate_set_id: 5, created_by: user) } + let(:params) { { ids: [log_1.id] } } + + it "deletes the log and marks related logs deduplicated" do + delete delete_logs_sales_logs_path, params: params + log_1.reload + expect(log_1.status).to eq "deleted" + expect(log_1.discarded_at).not_to be nil + expect(log_1.duplicates.count).to eq(0) + expect(log_1.duplicate_set_id).to be nil + log_2.reload + log_3.reload + expect(log_2.duplicates.count).to eq(1) + expect(log_3.duplicates.count).to eq(1) + expect(log_3.duplicate_set_id).not_to be nil + expect(log_3.duplicate_set_id).to eq(log_2.duplicate_set_id) + end + end + end end context "when a support user navigates to the organisations tab" do diff --git a/spec/requests/form_controller_spec.rb b/spec/requests/form_controller_spec.rb index ba27077a5..4fe03baf3 100644 --- a/spec/requests/form_controller_spec.rb +++ b/spec/requests/form_controller_spec.rb @@ -685,7 +685,7 @@ RSpec.describe FormController, type: :request do end context "and duplicate logs" do - let(:duplicate_logs) { create_list(:sales_log, 2) } + let!(:duplicate_logs) { create_list(:sales_log, 2) } before do allow(SalesLog).to receive(:duplicate_logs).and_return(duplicate_logs) @@ -882,8 +882,8 @@ RSpec.describe FormController, type: :request do end context "when the question was accessed from a duplicate logs screen" do - let(:lettings_log) { create(:lettings_log, :duplicate, created_by: user) } - let(:duplicate_log) { create(:lettings_log, :duplicate, created_by: user) } + let(:lettings_log) { create(:lettings_log, :duplicate, created_by: user, duplicate_set_id: 1) } + let(:duplicate_log) { create(:lettings_log, :duplicate, created_by: user, duplicate_set_id: 1) } let(:referrer) { "/lettings-logs/#{lettings_log.id}/lead-tenant-age?referrer=duplicate_logs&first_remaining_duplicate_id=#{duplicate_log.id}&original_log_id=#{lettings_log.id}" } let(:params) do { @@ -896,12 +896,30 @@ RSpec.describe FormController, type: :request do } end - before do - post "/lettings-logs/#{lettings_log.id}/lead-tenant-age", params:, headers: headers.merge({ "HTTP_REFERER" => referrer }) + context "and the answer changes" do + before do + post "/lettings-logs/#{lettings_log.id}/lead-tenant-age", params:, headers: headers.merge({ "HTTP_REFERER" => referrer }) + end + + it "redirects back to the duplicates page for remaining duplicates" do + expect(response).to redirect_to("/lettings-logs/#{duplicate_log.id}/duplicate-logs?original_log_id=#{lettings_log.id}") + expect(lettings_log.duplicates.count).to eq(0) + expect(duplicate_log.duplicates.count).to eq(0) + end end - it "redirects back to the duplicates page for remaining duplicates" do - expect(response).to redirect_to("/lettings-logs/#{duplicate_log.id}/duplicate-logs?original_log_id=#{lettings_log.id}") + context "and updating the answer creates a different set of duplicates" do + let!(:another_duplicate_log) { create(:lettings_log, :duplicate, created_by: user, age1_known: 1, age1: 20) } + + before do + post "/lettings-logs/#{lettings_log.id}/lead-tenant-age", params:, headers: headers.merge({ "HTTP_REFERER" => referrer }) + end + + it "correctly assigs duplicate set IDs" do + expect(lettings_log.reload.duplicates.count).to eq(1) + expect(lettings_log.duplicate_set_id).to eq(another_duplicate_log.reload.duplicate_set_id) + expect(duplicate_log.reload.duplicates.count).to eq(0) + end end context "and the answer didn't change" do @@ -916,15 +934,21 @@ RSpec.describe FormController, type: :request do } end + before do + post "/lettings-logs/#{lettings_log.id}/lead-tenant-age", params:, headers: headers.merge({ "HTTP_REFERER" => referrer }) + end + it "redirects back to the duplicates page for remaining duplicates" do expect(response).to redirect_to("/lettings-logs/#{lettings_log.id}/duplicate-logs?original_log_id=#{lettings_log.id}") + expect(lettings_log.duplicates.count).to eq(1) + expect(duplicate_log.duplicates.count).to eq(1) end end end context "when the sales question was accessed from a duplicate logs screen" do - let(:sales_log) { create(:sales_log, :duplicate, created_by: user) } - let(:duplicate_log) { create(:sales_log, :duplicate, created_by: user) } + let!(:sales_log) { create(:sales_log, :duplicate, created_by: user, duplicate_set_id: 1) } + let!(:duplicate_log) { create(:sales_log, :duplicate, created_by: user, duplicate_set_id: 1) } let(:referrer) { "/sales-logs/#{sales_log.id}/buyer-1-age?referrer=duplicate_logs&first_remaining_duplicate_id=#{duplicate_log.id}&original_log_id=#{sales_log.id}&referrer=duplicate_logs" } let(:params) do { @@ -943,6 +967,10 @@ RSpec.describe FormController, type: :request do it "redirects back to the duplicates page for remaining duplicates" do expect(response).to redirect_to("/sales-logs/#{duplicate_log.id}/duplicate-logs?original_log_id=#{sales_log.id}") + sales_log.reload + duplicate_log.reload + expect(sales_log.duplicates.count).to eq(0) + expect(duplicate_log.duplicates.count).to eq(0) end context "and the answer didn't change" do @@ -959,6 +987,8 @@ RSpec.describe FormController, type: :request do it "redirects back to the duplicates page for remaining duplicates" do expect(response).to redirect_to("/sales-logs/#{sales_log.id}/duplicate-logs?original_log_id=#{sales_log.id}") + expect(sales_log.duplicates.count).to eq(1) + expect(duplicate_log.duplicates.count).to eq(1) end end end diff --git a/spec/services/csv/lettings_log_csv_service_spec.rb b/spec/services/csv/lettings_log_csv_service_spec.rb index 4e6e21621..f3e180d51 100644 --- a/spec/services/csv/lettings_log_csv_service_spec.rb +++ b/spec/services/csv/lettings_log_csv_service_spec.rb @@ -114,7 +114,7 @@ RSpec.describe Csv::LettingsLogCsvService do end it "adds log attributes not related to questions to the headers" do - expect(headers.first(5)).to eq %w[id status created_by is_dpo created_at] + expect(headers.first(5)).to eq %w[id status duplicate_set_id created_by is_dpo] end it "adds attributes related to associated schemes and locations to the headers" do @@ -168,6 +168,18 @@ RSpec.describe Csv::LettingsLogCsvService do end expect(csv).to eq expected_content end + + context "when the log has a duplicate log reference" do + before do + log.update!(duplicate_set_id: 12_312) + end + + it "exports the id for under the heading 'duplicate_set_id'" do + duplicate_set_id_column_index = csv.first.index("duplicate_set_id") + duplicate_set_id_value = csv.second[duplicate_set_id_column_index] + expect(duplicate_set_id_value).to eq "12312" + end + end end context "when exporting as codes" do @@ -241,6 +253,18 @@ RSpec.describe Csv::LettingsLogCsvService do end expect(csv).to eq expected_content end + + context "when the log has a duplicate log reference" do + before do + log.update!(duplicate_set_id: 12_312) + end + + it "exports the id for under the heading 'duplicate_set_id'" do + duplicate_set_id_column_index = csv.first.index("duplicate_set_id") + duplicate_set_id_value = csv.second[duplicate_set_id_column_index] + expect(duplicate_set_id_value).to eq "12312" + end + end end end end diff --git a/spec/services/csv/sales_log_csv_service_spec.rb b/spec/services/csv/sales_log_csv_service_spec.rb index 28ce1dd5c..f9f545c35 100644 --- a/spec/services/csv/sales_log_csv_service_spec.rb +++ b/spec/services/csv/sales_log_csv_service_spec.rb @@ -163,6 +163,18 @@ RSpec.describe Csv::SalesLogCsvService do end expect(csv).to eq expected_content end + + context "when the log has a duplicate log reference" do + before do + log.update!(duplicate_set_id: 12_312) + end + + it "exports the id for under the heading 'duplicate_set_id'" do + duplicate_set_id_column_index = csv.first.index("duplicate_set_id") + duplicate_set_id_value = csv.second[duplicate_set_id_column_index] + expect(duplicate_set_id_value).to eq "12312" + end + end end context "when exporting values as codes" do @@ -211,5 +223,17 @@ RSpec.describe Csv::SalesLogCsvService do end expect(csv).to eq expected_content end + + context "when the log has a duplicate log reference" do + before do + log.update!(duplicate_set_id: 12_312) + end + + it "exports the id for under the heading 'duplicate_set_id'" do + duplicate_set_id_column_index = csv.first.index("duplicate_set_id") + duplicate_set_id_value = csv.second[duplicate_set_id_column_index] + expect(duplicate_set_id_value).to eq "12312" + end + end end end diff --git a/spec/services/exports/lettings_log_export_service_spec.rb b/spec/services/exports/lettings_log_export_service_spec.rb index 790995c9e..4e76364c4 100644 --- a/spec/services/exports/lettings_log_export_service_spec.rb +++ b/spec/services/exports/lettings_log_export_service_spec.rb @@ -413,6 +413,26 @@ RSpec.describe Exports::LettingsLogExportService do export_service.export_xml_lettings_logs end end + + context "and one lettings log with duplicate reference is available for export" do + let!(:lettings_log) { FactoryBot.create(:lettings_log, :completed, created_by: user, propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", tenancycode: "BZ737", startdate: Time.zone.local(2022, 2, 2, 10, 36, 49), voiddate: Time.zone.local(2019, 11, 3), mrcdate: Time.zone.local(2020, 5, 5, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4, duplicate_set_id: 123) } + + def replace_duplicate_set_id(export_file) + export_file.sub!("", "123") + end + + it "generates an XML export file with the expected content within the ZIP file" do + expected_content = replace_entity_ids(lettings_log, xml_export_file.read) + expected_content = replace_duplicate_set_id(expected_content) + expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) do |_, content| + entry = Zip::File.open_buffer(content).find_entry(expected_data_filename) + expect(entry).not_to be_nil + expect(entry.get_input_stream.read).to eq(expected_content) + end + + export_service.export_xml_lettings_logs + end + end end context "when exporting a supported housing lettings logs in XML" do