diff --git a/app/controllers/lettings_logs_controller.rb b/app/controllers/lettings_logs_controller.rb
index b7670cdcc..de5e40ca0 100644
--- a/app/controllers/lettings_logs_controller.rb
+++ b/app/controllers/lettings_logs_controller.rb
@@ -7,30 +7,23 @@ class LettingsLogsController < ApplicationController
before_action :authenticate, if: :json_api_request?
before_action :authenticate_user!, unless: :json_api_request?
before_action :find_resource, except: %i[create index edit]
+ before_action :session_filters, if: :current_user
+ before_action :set_session_filters, if: :current_user
def index
- set_session_filters
-
- all_logs = current_user.lettings_logs
- unpaginated_filtered_logs = filtered_lettings_logs(filtered_collection(all_logs, search_term))
-
respond_to do |format|
format.html do
+ all_logs = current_user.lettings_logs
+ unpaginated_filtered_logs = filtered_lettings_logs(all_logs, search_term, @session_filters)
+
+ @search_term = search_term
@pagy, @lettings_logs = pagy(unpaginated_filtered_logs)
@searched = search_term.presence
@total_count = all_logs.size
end
-
- format.csv do
- send_data byte_order_mark + unpaginated_filtered_logs.to_csv(current_user), filename: "logs-#{Time.zone.now}.csv"
- end
end
end
- def emailCsv
- EmailCsvJob.perform_later()
- end
-
def create
lettings_log = LettingsLog.new(lettings_log_params)
respond_to do |format|
@@ -95,6 +88,22 @@ class LettingsLogsController < ApplicationController
end
end
+ def download_csv
+ unpaginated_filtered_logs = filtered_lettings_logs(current_user.lettings_logs, search_term, @session_filters)
+
+ render "download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: email_csv_lettings_logs_path }
+ end
+
+ def email_csv
+ all_orgs = params["organisation_select"] == "all"
+ EmailCsvJob.perform_later(current_user, search_term, @session_filters, all_orgs)
+ redirect_to csv_confirmation_lettings_logs_path
+ end
+
+ def csv_confirmation
+ render "csv_confirmation"
+ end
+
private
API_ACTIONS = %w[create show update destroy].freeze
diff --git a/app/controllers/modules/lettings_logs_filter.rb b/app/controllers/modules/lettings_logs_filter.rb
index c074e48bb..d06f241c2 100644
--- a/app/controllers/modules/lettings_logs_filter.rb
+++ b/app/controllers/modules/lettings_logs_filter.rb
@@ -1,23 +1,21 @@
module Modules::LettingsLogsFilter
- def filtered_lettings_logs(logs)
- if session[:lettings_logs_filters].present?
- filters = JSON.parse(session[:lettings_logs_filters])
- filters.each do |category, values|
- next if Array(values).reject(&:empty?).blank?
- next if category == "organisation" && params["organisation_select"] == "all"
-
- logs = logs.public_send("filter_by_#{category}", values, current_user)
- end
- end
- logs = logs.order(created_at: :desc)
- current_user.support? ? logs.all.includes(:owning_organisation, :managing_organisation) : logs
+ def filtered_lettings_logs(logs, search_term, filters)
+ all_orgs = params["organisation_select"] == "all"
+ FilterService.filter_lettings_logs(logs, search_term, filters, all_orgs, current_user)
end
- def set_session_filters(specific_org: false)
- new_filters = session[:lettings_logs_filters].present? ? JSON.parse(session[:lettings_logs_filters]) : {}
+ def load_session_filters(specific_org: false)
+ current_filters = session[:lettings_logs_filters]
+ new_filters = current_filters.present? ? JSON.parse(current_filters) : {}
current_user.lettings_logs_filters(specific_org:).each { |filter| new_filters[filter] = params[filter] if params[filter].present? }
- new_filters = new_filters.except("organisation") if params["organisation_select"] == "all"
+ @session_filters = params["organisation_select"] == "all" ? new_filters.except("organisation") : new_filters
+ end
+
+ def session_filters(specific_org: false)
+ @session_filters ||= load_session_filters(specific_org:)
+ end
- session[:lettings_logs_filters] = new_filters.to_json
+ def set_session_filters
+ session[:lettings_logs_filters] = @session_filters.to_json
end
end
diff --git a/app/controllers/modules/search_filter.rb b/app/controllers/modules/search_filter.rb
index c32298987..82bf0d6c0 100644
--- a/app/controllers/modules/search_filter.rb
+++ b/app/controllers/modules/search_filter.rb
@@ -1,13 +1,9 @@
module Modules::SearchFilter
def filtered_collection(base_collection, search_term = nil)
- if search_term.present?
- base_collection.search_by(search_term)
- else
- base_collection
- end
+ FilterService.filter_by_search(base_collection, search_term)
end
def filtered_users(base_collection, search_term = nil)
- filtered_collection(base_collection, search_term).includes(:organisation)
+ FilterService.filter_by_search(base_collection, search_term).includes(:organisation)
end
end
diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb
index 1cf181788..7bbe85e6b 100644
--- a/app/controllers/organisations_controller.rb
+++ b/app/controllers/organisations_controller.rb
@@ -6,6 +6,8 @@ class OrganisationsController < ApplicationController
before_action :authenticate_user!
before_action :find_resource, except: %i[index new create]
before_action :authenticate_scope!, except: [:index]
+ before_action { session_filters(specific_org: true) if support_user? }
+ before_action :set_session_filters, if: :support_user?
def index
redirect_to organisation_path(current_user.organisation) unless current_user.support?
@@ -89,28 +91,39 @@ class OrganisationsController < ApplicationController
def logs
if current_user.support?
- set_session_filters(specific_org: true)
-
organisation_logs = LettingsLog.all.where(owning_organisation_id: @organisation.id)
- unpaginated_filtered_logs = filtered_lettings_logs(filtered_collection(organisation_logs, search_term))
+ unpaginated_filtered_logs = filtered_lettings_logs(organisation_logs, search_term, @session_filters)
respond_to do |format|
format.html do
+ @search_term = search_term
@pagy, @lettings_logs = pagy(unpaginated_filtered_logs)
@searched = search_term.presence
@total_count = organisation_logs.size
render "logs", layout: "application"
end
-
- format.csv do
- send_data byte_order_mark + unpaginated_filtered_logs.to_csv, filename: "logs-#{@organisation.name}-#{Time.zone.now}.csv"
- end
end
else
redirect_to(lettings_logs_path)
end
end
+ def download_csv
+ if current_user.support?
+ organisation_logs = LettingsLog.all.where(owning_organisation_id: @organisation.id)
+ unpaginated_filtered_logs = filtered_lettings_logs(organisation_logs, search_term, @session_filters)
+
+ render "lettings_logs/download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: logs_email_csv_organisation_path }
+ else
+ redirect_to(download_csv_lettings_logs_path)
+ end
+ end
+
+ def email_csv
+ EmailCsvJob.perform_later(current_user, search_term, @session_filters, false, @organisation)
+ redirect_to logs_csv_confirmation_organisation_path
+ end
+
private
def org_params
@@ -132,4 +145,8 @@ private
def find_resource
@organisation = Organisation.find(params[:id])
end
+
+ def support_user?
+ current_user.support?
+ end
end
diff --git a/app/jobs/email_csv_job.rb b/app/jobs/email_csv_job.rb
new file mode 100644
index 000000000..96a3aea48
--- /dev/null
+++ b/app/jobs/email_csv_job.rb
@@ -0,0 +1,20 @@
+class EmailCsvJob < ApplicationJob
+ queue_as :default
+
+ BYTE_ORDER_MARK = "\uFEFF".freeze # Required to ensure Excel always reads CSV as UTF-8
+
+ def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil) # rubocop:disable Style/OptionalBooleanParameter
+ unfiltered_logs = organisation.present? && user.support? ? LettingsLog.all.where(owning_organisation_id: organisation.id) : user.lettings_logs
+ filtered_logs = FilterService.filter_lettings_logs(unfiltered_logs, search_term, filters, all_orgs, user)
+
+ filename = organisation.present? ? "logs-#{organisation.name}-#{Time.zone.now}.csv" : "logs-#{Time.zone.now}.csv"
+
+ storage_service = Storage::S3Service.new(Configuration::PaasConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"])
+ storage_service.write_file(filename, BYTE_ORDER_MARK + filtered_logs.to_csv(user))
+
+ duration = 3.hours.to_i
+ url = storage_service.get_presigned_url(filename, duration)
+
+ CsvDownloadMailer.new.send_email(user, url, duration)
+ end
+end
diff --git a/app/mailers/csv_download_mailer.rb b/app/mailers/csv_download_mailer.rb
new file mode 100644
index 000000000..fa2168455
--- /dev/null
+++ b/app/mailers/csv_download_mailer.rb
@@ -0,0 +1,30 @@
+class CsvDownloadMailer
+ require "notifications/client"
+
+ CSV_DOWNLOAD_TEMPLATE_ID = "7890e3b9-8c0d-4d08-bafe-427fd7cd95bf".freeze
+
+ def notify_client
+ @notify_client ||= ::Notifications::Client.new(ENV["GOVUK_NOTIFY_API_KEY"])
+ end
+
+ def send_email(user, link, duration)
+ return true if intercept_send?(user.email)
+
+ notify_client.send_email(
+ email_address: user.email,
+ template_id: CSV_DOWNLOAD_TEMPLATE_ID,
+ personalisation: { name: user.name || user.email, link:, duration: ActiveSupport::Duration.build(duration).inspect },
+ )
+ end
+
+ def intercept_send?(email)
+ return false unless email_allowlist
+
+ email_domain = email.split("@").last.downcase
+ !(Rails.env.production? || Rails.env.test?) && email_allowlist.exclude?(email_domain)
+ end
+
+ def email_allowlist
+ Rails.application.credentials[:email_allowlist]
+ end
+end
diff --git a/app/services/filter_service.rb b/app/services/filter_service.rb
new file mode 100644
index 000000000..0986e7aca
--- /dev/null
+++ b/app/services/filter_service.rb
@@ -0,0 +1,22 @@
+class FilterService
+ def self.filter_by_search(base_collection, search_term = nil)
+ if search_term.present?
+ base_collection.search_by(search_term)
+ else
+ base_collection
+ end
+ end
+
+ def self.filter_lettings_logs(logs, search_term, filters, all_orgs, user)
+ logs = filter_by_search(logs, search_term)
+
+ filters.each do |category, values|
+ next if Array(values).reject(&:empty?).blank?
+ next if category == "organisation" && all_orgs
+
+ logs = logs.public_send("filter_by_#{category}", values, user)
+ end
+ logs = logs.order(created_at: :desc)
+ user.support? ? logs.all.includes(:owning_organisation, :managing_organisation) : logs
+ end
+end
diff --git a/app/services/storage/s3_service.rb b/app/services/storage/s3_service.rb
index aee76398d..c972225a1 100644
--- a/app/services/storage/s3_service.rb
+++ b/app/services/storage/s3_service.rb
@@ -20,6 +20,12 @@ module Storage
response.key_count == 1
end
+ def get_presigned_url(file_name, duration)
+ Aws::S3::Presigner
+ .new({ client: @client })
+ .presigned_url(:get_object, bucket: @configuration.bucket_name, key: file_name, expires_in: duration)
+ end
+
def get_file_io(file_name)
@client.get_object(bucket: @configuration.bucket_name, key: file_name)
.body
diff --git a/app/sidekiq/email_csv_job.rb b/app/sidekiq/email_csv_job.rb
deleted file mode 100644
index e9492f156..000000000
--- a/app/sidekiq/email_csv_job.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class EmailCsvJob
- include Sidekiq::Job
-
- def perform(*args)
- # Do something
- end
-end
diff --git a/app/views/lettings_logs/_log_list.html.erb b/app/views/lettings_logs/_log_list.html.erb
index 93d365c40..24f8fe73e 100644
--- a/app/views/lettings_logs/_log_list.html.erb
+++ b/app/views/lettings_logs/_log_list.html.erb
@@ -1,6 +1,6 @@
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "logs", path: request.path)) %>
- <%= govuk_link_to "Download (CSV)", "#{request.path}.csv", type: "text/csv" %>
+ <%= govuk_link_to "Download (CSV)", "#{request.path}/csv-download?search=#{@search_term}", type: "text/csv" %>
<% lettings_logs.map do |log| %>
diff --git a/app/views/lettings_logs/csv_confirmation.html.erb b/app/views/lettings_logs/csv_confirmation.html.erb
new file mode 100644
index 000000000..0c8165f33
--- /dev/null
+++ b/app/views/lettings_logs/csv_confirmation.html.erb
@@ -0,0 +1,15 @@
+<% content_for :title, "CSV Confirmation" %>
+
+
+ <%= govuk_panel(title_text: "We're sending you an email") %>
+
+
It should arrive in a few minutes, but it could take longer.
+
+
What happens next
+
Open your email inbox and click the link to download your CSV file.
+
+
+ <%= govuk_link_to "Return to logs", "#{request.path}/.." %>
+
+
+
diff --git a/app/views/lettings_logs/download_csv.html.erb b/app/views/lettings_logs/download_csv.html.erb
new file mode 100644
index 000000000..4e0da2704
--- /dev/null
+++ b/app/views/lettings_logs/download_csv.html.erb
@@ -0,0 +1,16 @@
+<% content_for :title, "Download CSV" %>
+
+<% content_for :before_content do %>
+ <%= govuk_back_link(href: :back) %>
+<% end %>
+
+
+
+
Download CSV
+
+
We'll send a secure download link to your email address <%= @current_user.email %>.
+
You've selected <%= count %> logs.
+
+ <%= govuk_button_to "Send email", post_path, method: :post, params: { search: search_term } %>
+
+
diff --git a/config/application.rb b/config/application.rb
index 7bdbfb93e..153782f54 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -33,5 +33,7 @@ module DataCollector
# config.eager_load_paths << Rails.root.join("extras")
config.exceptions_app = routes
+
+ config.active_job.queue_adapter = :sidekiq
end
end
diff --git a/config/routes.rb b/config/routes.rb
index 5a2fda475..82fec6493 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -69,6 +69,9 @@ Rails.application.routes.draw do
get "users", to: "organisations#users"
get "users/invite", to: "users/account#new"
get "logs", to: "organisations#logs"
+ get "logs/csv-download", to: "organisations#download_csv"
+ post "logs/email-csv", to: "organisations#email_csv"
+ get "logs/csv-confirmation", to: "lettings_logs#csv_confirmation"
get "schemes", to: "organisations#schemes"
end
end
@@ -77,6 +80,9 @@ Rails.application.routes.draw do
collection do
post "bulk-upload", to: "bulk_upload#bulk_upload"
get "bulk-upload", to: "bulk_upload#show"
+ get "csv-download", to: "lettings_logs#download_csv"
+ post "email-csv", to: "lettings_logs#email_csv"
+ get "csv-confirmation", to: "lettings_logs#csv_confirmation"
end
member do
diff --git a/spec/jobs/email_csv_job_spec.rb b/spec/jobs/email_csv_job_spec.rb
new file mode 100644
index 000000000..806341ca1
--- /dev/null
+++ b/spec/jobs/email_csv_job_spec.rb
@@ -0,0 +1,159 @@
+require "rails_helper"
+
+describe EmailCsvJob do
+ include Helpers
+
+ test_url = :test_url
+
+ let(:job) { described_class.new }
+ let(:user) { FactoryBot.create(:user) }
+ let(:organisation) { user.organisation }
+ let(:other_organisation) { FactoryBot.create(:organisation) }
+
+ context "when a log exists" do
+ let!(:lettings_log) do
+ FactoryBot.create(
+ :lettings_log,
+ owning_organisation: organisation,
+ managing_organisation: organisation,
+ ecstat1: 1,
+ )
+ end
+
+ let(:storage_service) { instance_double(Storage::S3Service) }
+ let(:mailer) { instance_double(CsvDownloadMailer) }
+
+ before do
+ FactoryBot.create(:lettings_log,
+ :completed,
+ owning_organisation: organisation,
+ managing_organisation: organisation,
+ created_by: user)
+
+ allow(Storage::S3Service).to receive(:new).and_return(storage_service)
+ allow(storage_service).to receive(:write_file)
+ allow(storage_service).to receive(:get_presigned_url).and_return(test_url)
+
+ allow(CsvDownloadMailer).to receive(:new).and_return(mailer)
+ allow(mailer).to receive(:send_email)
+ end
+
+ it "uses an appropriate filename in S3" do
+ expect(storage_service).to receive(:write_file).with(/logs-.*\.csv/, anything)
+ job.perform(user)
+ end
+
+ it "includes the organisation name in the filename when one is provided" do
+ expect(storage_service).to receive(:write_file).with(/logs-#{organisation.name}-.*\.csv/, anything)
+ job.perform(user, nil, {}, nil, organisation)
+ end
+
+ it "sends an E-mail with the presigned URL and duration" do
+ expect(mailer).to receive(:send_email).with(user, test_url, instance_of(Integer))
+ job.perform(user)
+ end
+
+ context "when writing to S3" do
+ before do
+ FactoryBot.create_list(:lettings_log, 4, owning_organisation: other_organisation)
+ end
+
+ def expect_csv
+ expect(storage_service).to receive(:write_file) do |_filename, data|
+ # Ignore BOM
+ csv = CSV.parse(data[1..])
+ yield(csv)
+ end
+ end
+
+ it "writes CSV data with headers" do
+ expect_csv do |csv|
+ expect(csv.first.first).to eq("id")
+ expect(csv.second.first).to eq(lettings_log.id.to_s)
+ end
+
+ job.perform(user)
+ end
+
+ context "when there is no organisation provided" do
+ it "only writes logs from the user's organisation" do
+ expect_csv do |csv|
+ # Headings + 2 rows
+ expect(csv.count).to eq(3)
+ end
+
+ job.perform(user)
+ end
+ end
+
+ context "when the user is support and an organisation is provided" do
+ let(:user) { FactoryBot.create(:user, :support) }
+
+ it "only writes logs from that organisation" do
+ expect_csv do |csv|
+ # other organisation => Headings + 4 rows
+ expect(csv.count).to eq(5)
+ end
+
+ job.perform(user, nil, {}, nil, other_organisation)
+ end
+ end
+
+ it "writes answer labels rather than values" do
+ expect_csv do |csv|
+ expect(csv.second[15]).to eq("Full-time – 30 hours or more")
+ end
+
+ job.perform(user)
+ end
+
+ it "writes filtered logs" do
+ expect_csv do |csv|
+ expect(csv.count).to eq(2)
+ end
+
+ job.perform(user, nil, { status: "completed" })
+ end
+
+ it "writes searched logs" do
+ expect_csv do |csv|
+ expect(csv.count).to eq(LettingsLog.search_by(lettings_log.id.to_s).count + 1)
+ end
+
+ job.perform(user, lettings_log.id.to_s)
+ end
+
+ context "when both filter and search applied" do
+ let(:postcode) { "XX1 1TG" }
+
+ before do
+ FactoryBot.create(:lettings_log, :in_progress, postcode_full: postcode, owning_organisation: organisation, created_by: user)
+ FactoryBot.create(:lettings_log, :completed, postcode_full: postcode, owning_organisation: organisation, created_by: user)
+ end
+
+ it "downloads logs matching both csv and filter logs" do
+ expect_csv do |csv|
+ expect(csv.count).to eq(2)
+ end
+
+ job.perform(user, postcode, { status: "completed" })
+ end
+ end
+
+ context "when there are more than 20 logs" do
+ before do
+ FactoryBot.create_list(:lettings_log, 26, owning_organisation: organisation)
+ end
+
+ it "does not paginate, it downloads all the user's logs" do
+ expect_csv do |csv|
+ # Heading + 2 + 26
+ expect(csv.count).to eq(29)
+ end
+
+ job.perform(user)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/mailers/csv_download_mailer_spec.rb b/spec/mailers/csv_download_mailer_spec.rb
new file mode 100644
index 000000000..1116f693e
--- /dev/null
+++ b/spec/mailers/csv_download_mailer_spec.rb
@@ -0,0 +1,30 @@
+require "rails_helper"
+
+RSpec.describe CsvDownloadMailer do
+ describe "Intercept mail" do
+ let(:notify_client) { instance_double(Notifications::Client) }
+ let(:user) { FactoryBot.create(:user, email: "user@example.com") }
+
+ before do
+ allow(Notifications::Client).to receive(:new).and_return(notify_client)
+ allow(notify_client).to receive(:send_email).and_return(true)
+ end
+
+ it "sends a CSV download E-mail via notify" do
+ link = :link
+ duration = 20.minutes.to_i
+
+ expect(notify_client).to receive(:send_email).with(
+ email_address: user.email,
+ template_id: described_class::CSV_DOWNLOAD_TEMPLATE_ID,
+ personalisation: {
+ name: user.name,
+ link:,
+ duration: "20 minutes",
+ },
+ )
+
+ described_class.new.send_email(user, link, duration)
+ end
+ end
+end
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
index 78867bad9..cdeb71092 100644
--- a/spec/rails_helper.rb
+++ b/spec/rails_helper.rb
@@ -84,4 +84,5 @@ RSpec.configure do |config|
config.include Devise::Test::IntegrationHelpers, type: :request
config.include ViewComponent::TestHelpers, type: :component
config.include Capybara::RSpecMatchers, type: :component
+ config.include ActiveJob::TestHelper
end
diff --git a/spec/requests/lettings_logs_controller_spec.rb b/spec/requests/lettings_logs_controller_spec.rb
index e70cace51..f6d159da4 100644
--- a/spec/requests/lettings_logs_controller_spec.rb
+++ b/spec/requests/lettings_logs_controller_spec.rb
@@ -383,6 +383,12 @@ RSpec.describe LettingsLogsController, type: :request do
end
end
+ it "includes the search on the CSV link" do
+ search_term = "test search term"
+ get "/logs?search=#{search_term}", headers: headers, params: {}
+ expect(page).to have_link("Download (CSV)", href: "/logs/csv-download?search=#{search_term}")
+ end
+
context "when more than one results with matching postcode" do
let!(:matching_postcode_log) { FactoryBot.create(:lettings_log, :completed, owning_organisation: user.organisation, postcode_full: log_to_search.postcode_full) }
@@ -491,8 +497,8 @@ RSpec.describe LettingsLogsController, type: :request do
expect(page).to have_title("Logs - Submit social housing lettings and sales data (CORE) - GOV.UK")
end
- it "shows the download csv link" do
- expect(page).to have_link("Download (CSV)", href: "/logs.csv")
+ it "shows the CSV download link" do
+ expect(page).to have_link("Download (CSV)", href: "/logs/csv-download?search=")
end
it "does not show the organisation filter" do
@@ -729,91 +735,43 @@ RSpec.describe LettingsLogsController, type: :request do
expect(CGI.unescape_html(response.body)).to include("You didn’t answer this question")
end
end
- end
- describe "CSV download" do
- let(:headers) { { "Accept" => "text/csv" } }
- let(:user) { FactoryBot.create(:user) }
- let(:organisation) { user.organisation }
- let(:other_organisation) { FactoryBot.create(:organisation) }
-
- context "when a log exists" do
- let!(:lettings_log) do
- FactoryBot.create(
- :lettings_log,
- owning_organisation: organisation,
- managing_organisation: organisation,
- ecstat1: 1,
- )
- end
+ context "when requesting CSV download" do
+ let(:headers) { { "Accept" => "text/html" } }
+ let(:search_term) { "test search term" }
before do
sign_in user
- FactoryBot.create(:lettings_log)
- FactoryBot.create(:lettings_log,
- :completed,
- owning_organisation: organisation,
- managing_organisation: organisation,
- created_by: user)
- get "/logs", headers:, params: {}
+ get "/logs/csv-download?search=#{search_term}", headers:
end
- it "downloads a CSV file with headers" do
- csv = CSV.parse(response.body)
- expect(csv.first.first).to eq("\uFEFFid")
- expect(csv.second.first).to eq(lettings_log.id.to_s)
- end
-
- it "does not download other orgs logs" do
- csv = CSV.parse(response.body)
- expect(csv.count).to eq(3)
- end
-
- it "downloads answer labels rather than values" do
- csv = CSV.parse(response.body)
- expect(csv.second[15]).to eq("Full-time – 30 hours or more")
+ it "returns http success" do
+ expect(response).to have_http_status(:success)
end
- it "downloads filtered logs" do
- get "/logs?status[]=completed", headers:, params: {}
- csv = CSV.parse(response.body)
- expect(csv.count).to eq(2)
+ it "shows a confirmation button" do
+ expect(page).to have_button("Send email")
end
- it "dowloads searched logs" do
- get "/logs?search=#{lettings_log.id}", headers:, params: {}
- csv = CSV.parse(response.body)
- expect(csv.count).to eq(LettingsLog.search_by(lettings_log.id.to_s).count + 1)
+ it "includes the search term" do
+ expect(page).to have_field("search", type: "hidden", with: search_term)
end
+ end
- context "when both filter and search applied" do
- let(:postcode) { "XX1 1TG" }
+ context "when confirming the CSV email" do
+ let(:headers) { { "Accept" => "text/html" } }
+ context "when a log exists" do
before do
- FactoryBot.create(:lettings_log, :in_progress, postcode_full: postcode, owning_organisation: organisation, created_by: user)
- FactoryBot.create(:lettings_log, :completed, postcode_full: postcode, owning_organisation: organisation, created_by: user)
+ sign_in user
end
- it "downloads logs matching both csv and filter logs" do
- get "/logs?status[]=completed&search=#{postcode}", headers:, params: {}
- csv = CSV.parse(response.body)
- expect(csv.count).to eq(2)
+ it "confirms that the user will receive an email with the requested CSV" do
+ get "/logs/csv-confirmation"
+ expect(CGI.unescape_html(response.body)).to include("We're sending you an email")
end
end
end
-
- context "when there are more than 20 logs" do
- before do
- sign_in user
- FactoryBot.create_list(:lettings_log, 26, owning_organisation: organisation)
- get "/logs", headers:, params: {}
- end
-
- it "does not paginate, it downloads all the user's logs" do
- csv = CSV.parse(response.body)
- expect(csv.count).to eq(27)
- end
- end
end
describe "PATCH" do
@@ -966,4 +924,60 @@ RSpec.describe LettingsLogsController, type: :request do
end
end
end
+
+ describe "POST #email-csv" do
+ let(:other_organisation) { FactoryBot.create(:organisation) }
+
+ context "when a log exists" do
+ let!(:lettings_log) do
+ FactoryBot.create(
+ :lettings_log,
+ owning_organisation:,
+ managing_organisation: owning_organisation,
+ ecstat1: 1,
+ )
+ end
+
+ before do
+ sign_in user
+ FactoryBot.create(:lettings_log)
+ FactoryBot.create(:lettings_log,
+ :completed,
+ owning_organisation:,
+ managing_organisation: owning_organisation,
+ created_by: user)
+ end
+
+ it "creates an E-mail job" do
+ expect {
+ post "/logs/email-csv", headers:, params: {}
+ }.to enqueue_job(EmailCsvJob).with(user, nil, {}, false)
+ end
+
+ it "redirects to the confirmation page" do
+ post "/logs/email-csv", headers:, params: {}
+ expect(response).to redirect_to(csv_confirmation_lettings_logs_path)
+ end
+
+ it "passes the search term" do
+ expect {
+ post "/logs/email-csv?search=#{lettings_log.id}", headers:, params: {}
+ }.to enqueue_job(EmailCsvJob).with(user, lettings_log.id.to_s, {}, false)
+ end
+
+ it "passes filter parameters" do
+ expect {
+ post "/logs/email-csv?status[]=completed", headers:, params: {}
+ }.to enqueue_job(EmailCsvJob).with(user, nil, { "status" => %w[completed] }, false)
+ end
+
+ it "passes a combination of search term and filter parameters" do
+ postcode = "XX1 1TG"
+
+ expect {
+ post "/logs/email-csv?status[]=completed&search=#{postcode}", headers:, params: {}
+ }.to enqueue_job(EmailCsvJob).with(user, postcode, { "status" => %w[completed] }, false)
+ end
+ end
+ end
end
diff --git a/spec/requests/organisations_controller_spec.rb b/spec/requests/organisations_controller_spec.rb
index 20197f468..469278b1e 100644
--- a/spec/requests/organisations_controller_spec.rb
+++ b/spec/requests/organisations_controller_spec.rb
@@ -1035,11 +1035,10 @@ RSpec.describe OrganisationsController, type: :request do
end
it "has a CSV download button with the correct path" do
- expect(page).to have_link("Download (CSV)", href: "/organisations/#{organisation.id}/logs.csv")
+ expect(page).to have_link("Download (CSV)", href: "/organisations/#{organisation.id}/logs/csv-download?search=")
end
context "when you download the CSV" do
- let(:headers) { { "Accept" => "text/csv" } }
let(:other_organisation) { FactoryBot.create(:organisation) }
before do
@@ -1048,9 +1047,15 @@ RSpec.describe OrganisationsController, type: :request do
end
it "only includes logs from that organisation" do
- get "/organisations/#{organisation.id}/logs", headers:, params: {}
- csv = CSV.parse(response.body)
- expect(csv.count).to eq(4)
+ get "/organisations/#{organisation.id}/logs/csv-download"
+
+ expect(page).to have_text("You've selected 3 logs.")
+ end
+
+ it "provides the organisation to the mail job" do
+ expect {
+ post "/organisations/#{organisation.id}/logs/email-csv?status[]=completed", headers:, params: {}
+ }.to enqueue_job(EmailCsvJob).with(user, nil, { "status" => %w[completed] }, false, organisation)
end
end
end
diff --git a/spec/services/filter_service_spec.rb b/spec/services/filter_service_spec.rb
new file mode 100644
index 000000000..1e892fd56
--- /dev/null
+++ b/spec/services/filter_service_spec.rb
@@ -0,0 +1,28 @@
+require "rails_helper"
+
+describe FilterService do
+ describe "filter_by_search" do
+ before do
+ FactoryBot.create_list(:organisation, 5)
+ FactoryBot.create(:organisation, name: "Acme LTD")
+ end
+
+ let(:organisation_list) { Organisation.all }
+
+ context "when given a search term" do
+ let(:search_term) { "Acme" }
+
+ it "filters the collection on search term" do
+ expect(described_class.filter_by_search(organisation_list, search_term).count).to eq(1)
+ end
+ end
+
+ context "when not given a search term" do
+ let(:search_term) { nil }
+
+ it "does not filter the given collection" do
+ expect(described_class.filter_by_search(organisation_list, search_term).count).to eq(6)
+ end
+ end
+ end
+end