diff --git a/app/jobs/email_missing_addresses_csv_job.rb b/app/jobs/email_missing_addresses_csv_job.rb index 02ccee8c2..83988d0b0 100644 --- a/app/jobs/email_missing_addresses_csv_job.rb +++ b/app/jobs/email_missing_addresses_csv_job.rb @@ -3,7 +3,7 @@ class EmailMissingAddressesCsvJob < ApplicationJob BYTE_ORDER_MARK = "\uFEFF".freeze # Required to ensure Excel always reads CSV as UTF-8 - def perform(users, organisation, log_type) + def perform(user_ids, organisation, log_type) case log_type when "lettings" csv_string = Csv::MissingAddressesCsvService.new(organisation:).create_missing_lettings_addresses_csv @@ -20,7 +20,10 @@ class EmailMissingAddressesCsvJob < ApplicationJob url = storage_service.get_presigned_url(filename, nil) - users.each do |user| + user_ids.each do |id| + user = User.find(id) + next if user.blank? + CsvDownloadMailer.new.send(email_method, user, url) end end diff --git a/app/services/csv/missing_addresses_csv_service.rb b/app/services/csv/missing_addresses_csv_service.rb index e49e7ea94..bac4dced9 100644 --- a/app/services/csv/missing_addresses_csv_service.rb +++ b/app/services/csv/missing_addresses_csv_service.rb @@ -5,12 +5,12 @@ module Csv end def create_missing_lettings_addresses_csv + logs_with_missing_addresses = @organisation.managed_lettings_logs.imported.filter_by_year(2023).where(needstype: 1, address_line1: nil, town_or_city: nil, uprn_known: [0, nil]).where.not(old_form_id: nil) + return if logs_with_missing_addresses.empty? + CSV.generate(headers: true) do |csv| csv << ["Tenancy start date", "Tenant code", "Property code", "Log owner", "Owning organisation name", "Managing organisation name", "Address line 1", "Address line 2", "Town or City", "County", "Postcode", "Local authority"] - logs_with_missing_addresses = @organisation.managed_lettings_logs.imported.filter_by_year(2023).where(needstype: 1, address_line1: nil, town_or_city: nil, uprn_known: [0, nil]) - return if logs_with_missing_addresses.empty? - logs_with_missing_addresses.each do |log| csv << [log.startdate&.to_date, log.tenancycode, log.propcode, log.created_by&.email, log.owning_organisation&.name, log.managing_organisation&.name, log.address_line1, log.address_line2, log.town_or_city, log.county, log.postcode_full, log.la] end @@ -18,12 +18,12 @@ module Csv end def create_missing_sales_addresses_csv + logs_with_missing_addresses = @organisation.sales_logs.imported.filter_by_year(2023).where(address_line1: nil, town_or_city: nil, uprn_known: [0, nil]).where.not(old_form_id: nil) + return if logs_with_missing_addresses.empty? + CSV.generate(headers: true) do |csv| csv << ["Sale completion date", "Purchaser code", "Log owner", "Owning organisation name", "Address line 1", "Address line 2", "Town or City", "County", "Postcode", "Local authority"] - logs_with_missing_addresses = @organisation.sales_logs.imported.filter_by_year(2023).where(address_line1: nil, town_or_city: nil, uprn_known: [0, nil]) - return if logs_with_missing_addresses.empty? - logs_with_missing_addresses.each do |log| csv << [log.saledate&.to_date, log.purchid, log.created_by&.email, log.owning_organisation&.name, log.address_line1, log.address_line2, log.town_or_city, log.county, log.postcode_full, log.la] end diff --git a/lib/tasks/send_missing_addresses_csv.rake b/lib/tasks/send_missing_addresses_csv.rake new file mode 100644 index 000000000..ea668a245 --- /dev/null +++ b/lib/tasks/send_missing_addresses_csv.rake @@ -0,0 +1,23 @@ +namespace :correct_addresses do + # rubocop:disable Lint/ConstantDefinitionInBlock + MISSING_ADDRESSES_THRESHOLD = 50 + # rubocop:enable Lint/ConstantDefinitionInBlock + + desc "Send missing addresses csv" + task :send_missing_addresses_csv, %i[] => :environment do |_task, _args| + Organisation.all.each do |organisation| + impacted_logs = organisation.managed_lettings_logs + .imported + .filter_by_year(2023) + .where(needstype: 1, address_line1: nil, town_or_city: nil, uprn_known: [0, nil]) + .where.not(old_form_id: nil) + + next unless impacted_logs.count >= MISSING_ADDRESSES_THRESHOLD + + data_coordinators = organisation.users.where(role: 2).filter_by_active + users_to_contact = data_coordinators.any? ? data_coordinators : organisation.users.filter_by_active + EmailMissingAddressesCsvJob.perform_later(users_to_contact.map(&:id), organisation, "lettings") + Rails.logger.info("Sending missing addresses CSV for #{organisation.name} to #{users_to_contact.map(&:email).join(', ')}") + end + end +end diff --git a/spec/factories/lettings_log.rb b/spec/factories/lettings_log.rb index 6cce1dfea..b71362647 100644 --- a/spec/factories/lettings_log.rb +++ b/spec/factories/lettings_log.rb @@ -190,6 +190,9 @@ FactoryBot.define do status { 4 } discarded_at { Time.zone.now } end + trait :imported do + old_id { rand(1...99_999) } + end created_at { Time.zone.today } updated_at { Time.zone.today } end diff --git a/spec/jobs/email_missing_addresses_csv_job_spec.rb b/spec/jobs/email_missing_addresses_csv_job_spec.rb index e5e344c97..6b22ce121 100644 --- a/spec/jobs/email_missing_addresses_csv_job_spec.rb +++ b/spec/jobs/email_missing_addresses_csv_job_spec.rb @@ -29,38 +29,38 @@ describe EmailMissingAddressesCsvJob do context "when sending missing lettings logs csv" do it "uses an appropriate filename in S3" do expect(storage_service).to receive(:write_file).with(/missing-lettings-logs-addresses-#{organisation.name}-.*\.csv/, anything) - job.perform(users, organisation, "lettings") + job.perform(users.map(&:id), organisation, "lettings") end it "creates a MissingAddressesCsvService with the correct organisation and calls create missing lettings logs adresses csv" do expect(Csv::MissingAddressesCsvService).to receive(:new).with(organisation:) expect(missing_addresses_csv_service).to receive(:create_missing_lettings_addresses_csv) - job.perform(users, organisation, "lettings") + job.perform(users.map(&:id), organisation, "lettings") end it "sends emails to all the provided users" do expect(mailer).to receive(:send_missing_lettings_addresses_csv_download_mail).with(users[0], test_url) expect(mailer).to receive(:send_missing_lettings_addresses_csv_download_mail).with(users[1], test_url) - job.perform(users, organisation, "lettings") + job.perform(users.map(&:id), organisation, "lettings") end end context "when sending missing sales logs csv" do it "uses an appropriate filename in S3" do expect(storage_service).to receive(:write_file).with(/missing-sales-logs-addresses-#{organisation.name}-.*\.csv/, anything) - job.perform(users, organisation, "sales") + job.perform(users.map(&:id), organisation, "sales") end it "creates a MissingAddressesCsvService with the correct organisation and calls create missing sales logs adresses csv" do expect(Csv::MissingAddressesCsvService).to receive(:new).with(organisation:) expect(missing_addresses_csv_service).to receive(:create_missing_sales_addresses_csv) - job.perform(users, organisation, "sales") + job.perform(users.map(&:id), organisation, "sales") end it "sends emails to all the provided users" do expect(mailer).to receive(:send_missing_sales_addresses_csv_download_mail).with(users[0], test_url) expect(mailer).to receive(:send_missing_sales_addresses_csv_download_mail).with(users[1], test_url) - job.perform(users, organisation, "sales") + job.perform(users.map(&:id), organisation, "sales") end end end diff --git a/spec/lib/tasks/send_missing_addresses_csv_spec.rb b/spec/lib/tasks/send_missing_addresses_csv_spec.rb new file mode 100644 index 000000000..4f28cdec7 --- /dev/null +++ b/spec/lib/tasks/send_missing_addresses_csv_spec.rb @@ -0,0 +1,73 @@ +require "rails_helper" +require "rake" + +RSpec.describe "emails" do + describe ":send_missing_addresses_csv", type: :task do + subject(:task) { Rake::Task["correct_addresses:send_missing_addresses_csv"] } + + before do + organisation.users.destroy_all + Rake.application.rake_require("tasks/send_missing_addresses_csv") + Rake::Task.define_task(:environment) + task.reenable + end + + context "when the rake task is run" do + let(:organisation) { create(:organisation, name: "test organisation") } + + before do + stub_const("MISSING_ADDRESSES_THRESHOLD", 5) + end + + context "when org has more than 5 missing addresses and data coordinators" do + let!(:data_coordinator) { create(:user, :data_coordinator, organisation:, email: "data_coordinator1@example.com") } + let!(:data_coordinator2) { create(:user, :data_coordinator, organisation:, email: "data_coordinator2@example.com") } + + before do + create(:user, :data_provider, organisation:, email: "data_provider1@example.com") + create_list(:lettings_log, 7, :imported, startdate: Time.zone.local(2023, 9, 9), address_line1: nil, town_or_city: nil, needstype: 1, old_form_id: "form_1", owning_organisation: organisation, managing_organisation: organisation, created_by: organisation.users.first) + end + + it "enqueues the job with correct organisations" do + expect { task.invoke }.to enqueue_job(EmailMissingAddressesCsvJob).with(include(data_coordinator.id, data_coordinator2.id), organisation, "lettings") + end + + it "prints out the jobs enqueued" do + expect(Rails.logger).to receive(:info).with(nil) + expect(Rails.logger).to receive(:info).with("Sending missing addresses CSV for test organisation to data_coordinator1@example.com, data_coordinator2@example.com") + task.invoke + end + end + + context "when org has 5 missing addresses and data providers only" do + let!(:data_provider) { create(:user, :data_provider, organisation:, email: "data_provider3@example.com") } + let!(:data_provider2) { create(:user, :data_provider, organisation:, email: "data_provider4@example.com") } + + before do + create_list(:lettings_log, 5, :imported, startdate: Time.zone.local(2023, 9, 9), address_line1: nil, town_or_city: nil, needstype: 1, old_form_id: "form_2", owning_organisation: organisation, managing_organisation: organisation, created_by: organisation.users.first) + end + + it "enqueues the job with correct organisations" do + expect { task.invoke }.to enqueue_job(EmailMissingAddressesCsvJob).with(include(data_provider.id, data_provider2.id), organisation, "lettings") + end + + it "prints out the jobs enqueued" do + expect(Rails.logger).to receive(:info).with(nil) + expect(Rails.logger).to receive(:info).with("Sending missing addresses CSV for test organisation to data_provider3@example.com, data_provider4@example.com") + task.invoke + end + end + + context "when org has less than 5 missing addresses" do + before do + create_list(:lettings_log, 3, :imported, startdate: Time.zone.local(2023, 9, 9), address_line1: nil, town_or_city: nil, needstype: 1, old_form_id: "form_2", owning_organisation: organisation, managing_organisation: organisation, created_by: organisation.users.first) + create_list(:lettings_log, 2, :imported, startdate: Time.zone.local(2023, 9, 9), address_line1: nil, needstype: 1, owning_organisation: organisation, managing_organisation: organisation, created_by: organisation.users.first) + end + + it "does not enqueue the job with organisations that is missing less addresses than threshold amount" do + expect { task.invoke }.not_to enqueue_job(EmailMissingAddressesCsvJob) + end + end + end + end +end diff --git a/spec/services/csv/missing_addresses_csv_service_spec.rb b/spec/services/csv/missing_addresses_csv_service_spec.rb index 10bae9d01..3269f4fcd 100644 --- a/spec/services/csv/missing_addresses_csv_service_spec.rb +++ b/spec/services/csv/missing_addresses_csv_service_spec.rb @@ -17,6 +17,7 @@ RSpec.describe Csv::MissingAddressesCsvService do address_line1: nil, town_or_city: nil, old_id: "old_id", + old_form_id: "old_form_id", needstype: 1, uprn_known: 0) end @@ -70,6 +71,7 @@ RSpec.describe Csv::MissingAddressesCsvService do address_line1: nil, town_or_city: nil, old_id: "old_id", + old_form_id: "old_form_id", uprn_known: 0) end