diff --git a/app/controllers/collection_resources_controller.rb b/app/controllers/collection_resources_controller.rb
index 23a1b3c79..3bec72371 100644
--- a/app/controllers/collection_resources_controller.rb
+++ b/app/controllers/collection_resources_controller.rb
@@ -23,7 +23,7 @@ class CollectionResourcesController < ApplicationController
download_resource(resource.download_filename)
end
- def update_mandatory_collection_resource
+ def edit
return render_not_found unless current_user.support?
year = params[:year].to_i
@@ -39,6 +39,31 @@ class CollectionResourcesController < ApplicationController
render "collection_resources/edit"
end
+ def update
+ return render_not_found unless current_user.support?
+
+ year = resource_params[:year].to_i
+ resource_type = resource_params[:resource_type]
+ log_type = resource_params[:log_type]
+ file = resource_params[:file]
+
+ return render_not_found unless resource_for_year_can_be_updated?(year)
+
+ @collection_resource = MandatoryCollectionResourcesService.generate_resource(log_type, year, resource_type)
+ render_not_found unless @collection_resource
+
+ filename = @collection_resource.download_filename
+ begin
+ UploadCollectionResourcesService.upload_collection_resource(filename, file)
+ rescue StandardError
+ @collection_resource.errors.add(:file, "There was an error uploading this file.")
+ return render "collection_resources/edit"
+ end
+
+ flash[:notice] = "The #{log_type} #{text_year_range_format(year)} #{@collection_resource.short_display_name.downcase} has been updated"
+ redirect_to collection_resources_path
+ end
+
private
def resource_params
diff --git a/app/services/upload_collection_resources_service.rb b/app/services/upload_collection_resources_service.rb
new file mode 100644
index 000000000..e137531df
--- /dev/null
+++ b/app/services/upload_collection_resources_service.rb
@@ -0,0 +1,6 @@
+class UploadCollectionResourcesService
+ def self.upload_collection_resource(filename, file)
+ storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["COLLECTION_RESOURCES_BUCKET"])
+ storage_service.write_file(filename, file)
+ end
+end
diff --git a/app/views/collection_resources/_collection_resource_summary_list.erb b/app/views/collection_resources/_collection_resource_summary_list.erb
index 4e3485e76..61c29865d 100644
--- a/app/views/collection_resources/_collection_resource_summary_list.erb
+++ b/app/views/collection_resources/_collection_resource_summary_list.erb
@@ -10,7 +10,7 @@
<% end %>
<% row.with_action(
text: "Change",
- href: update_mandatory_collection_resource_path(year: resource.year, log_type: resource.log_type, resource_type: resource.resource_type),
+ href: edit_mandatory_collection_resource_path(year: resource.year, log_type: resource.log_type, resource_type: resource.resource_type),
) %>
<% else %>
<% row.with_value do %>
diff --git a/app/views/collection_resources/edit.html.erb b/app/views/collection_resources/edit.html.erb
index 00bbf39cb..d9bfe5be9 100644
--- a/app/views/collection_resources/edit.html.erb
+++ b/app/views/collection_resources/edit.html.erb
@@ -4,7 +4,7 @@
- <%= form_with model: @collection_resource, scope: :form, url: update_mandatory_collection_resource_path, method: :patch do |f| %>
+ <%= form_with model: @collection_resource, url: update_mandatory_collection_resource_path, method: :patch do |f| %>
<%= f.hidden_field :year %>
<%= f.hidden_field :log_type %>
<%= f.hidden_field :resource_type %>
diff --git a/config/routes.rb b/config/routes.rb
index 1ac459b6b..295cb8545 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -42,7 +42,8 @@ Rails.application.routes.draw do
get "collection-resources", to: "collection_resources#index"
get "/collection-resources/:log_type/:year/:resource_type/download", to: "collection_resources#download_mandatory_collection_resource", as: :download_mandatory_collection_resource
- get "/collection-resources/:log_type/:year/:resource_type/edit", to: "collection_resources#update_mandatory_collection_resource", as: :update_mandatory_collection_resource
+ get "/collection-resources/:log_type/:year/:resource_type/edit", to: "collection_resources#edit", as: :edit_mandatory_collection_resource
+ patch "/collection-resources", to: "collection_resources#update", as: :update_mandatory_collection_resource
resources :collection_resources, path: "/collection-resources" do
get "/download", to: "collection_resources#download_additional_collection_resource" # when we get to adding them
diff --git a/spec/requests/collection_resources_controller_spec.rb b/spec/requests/collection_resources_controller_spec.rb
index 026b917c9..b725a2991 100644
--- a/spec/requests/collection_resources_controller_spec.rb
+++ b/spec/requests/collection_resources_controller_spec.rb
@@ -86,19 +86,19 @@ RSpec.describe CollectionResourcesController, type: :request do
it "displays change links" do
expect(page).to have_selector(:link_or_button, "Change", count: 12)
- expect(page).to have_link("Change", href: update_mandatory_collection_resource_path(year: 2024, log_type: "lettings", resource_type: "paper_form"))
- expect(page).to have_link("Change", href: update_mandatory_collection_resource_path(year: 2024, log_type: "lettings", resource_type: "bulk_upload_template"))
- expect(page).to have_link("Change", href: update_mandatory_collection_resource_path(year: 2024, log_type: "lettings", resource_type: "bulk_upload_specification"))
- expect(page).to have_link("Change", href: update_mandatory_collection_resource_path(year: 2024, log_type: "sales", resource_type: "paper_form"))
- expect(page).to have_link("Change", href: update_mandatory_collection_resource_path(year: 2024, log_type: "sales", resource_type: "bulk_upload_template"))
- expect(page).to have_link("Change", href: update_mandatory_collection_resource_path(year: 2024, log_type: "sales", resource_type: "bulk_upload_specification"))
-
- expect(page).to have_link("Change", href: update_mandatory_collection_resource_path(year: 2025, log_type: "lettings", resource_type: "paper_form"))
- expect(page).to have_link("Change", href: update_mandatory_collection_resource_path(year: 2025, log_type: "lettings", resource_type: "bulk_upload_template"))
- expect(page).to have_link("Change", href: update_mandatory_collection_resource_path(year: 2025, log_type: "lettings", resource_type: "bulk_upload_specification"))
- expect(page).to have_link("Change", href: update_mandatory_collection_resource_path(year: 2025, log_type: "sales", resource_type: "paper_form"))
- expect(page).to have_link("Change", href: update_mandatory_collection_resource_path(year: 2025, log_type: "sales", resource_type: "bulk_upload_template"))
- expect(page).to have_link("Change", href: update_mandatory_collection_resource_path(year: 2025, log_type: "sales", resource_type: "bulk_upload_specification"))
+ expect(page).to have_link("Change", href: edit_mandatory_collection_resource_path(year: 2024, log_type: "lettings", resource_type: "paper_form"))
+ expect(page).to have_link("Change", href: edit_mandatory_collection_resource_path(year: 2024, log_type: "lettings", resource_type: "bulk_upload_template"))
+ expect(page).to have_link("Change", href: edit_mandatory_collection_resource_path(year: 2024, log_type: "lettings", resource_type: "bulk_upload_specification"))
+ expect(page).to have_link("Change", href: edit_mandatory_collection_resource_path(year: 2024, log_type: "sales", resource_type: "paper_form"))
+ expect(page).to have_link("Change", href: edit_mandatory_collection_resource_path(year: 2024, log_type: "sales", resource_type: "bulk_upload_template"))
+ expect(page).to have_link("Change", href: edit_mandatory_collection_resource_path(year: 2024, log_type: "sales", resource_type: "bulk_upload_specification"))
+
+ expect(page).to have_link("Change", href: edit_mandatory_collection_resource_path(year: 2025, log_type: "lettings", resource_type: "paper_form"))
+ expect(page).to have_link("Change", href: edit_mandatory_collection_resource_path(year: 2025, log_type: "lettings", resource_type: "bulk_upload_template"))
+ expect(page).to have_link("Change", href: edit_mandatory_collection_resource_path(year: 2025, log_type: "lettings", resource_type: "bulk_upload_specification"))
+ expect(page).to have_link("Change", href: edit_mandatory_collection_resource_path(year: 2025, log_type: "sales", resource_type: "paper_form"))
+ expect(page).to have_link("Change", href: edit_mandatory_collection_resource_path(year: 2025, log_type: "sales", resource_type: "bulk_upload_template"))
+ expect(page).to have_link("Change", href: edit_mandatory_collection_resource_path(year: 2025, log_type: "sales", resource_type: "bulk_upload_specification"))
end
end
@@ -196,10 +196,10 @@ RSpec.describe CollectionResourcesController, type: :request do
end
end
- describe "GET #update_mandatory_collection_resource" do
+ describe "GET #edit_mandatory_collection_resource" do
context "when user is not signed in" do
it "redirects to the sign in page" do
- get update_mandatory_collection_resource_path(year: 2024, log_type: "sales", resource_type: "bulk_upload_template")
+ get edit_mandatory_collection_resource_path(year: 2024, log_type: "sales", resource_type: "bulk_upload_template")
expect(response).to redirect_to(new_user_session_path)
end
end
@@ -212,7 +212,7 @@ RSpec.describe CollectionResourcesController, type: :request do
end
it "returns page not found" do
- get update_mandatory_collection_resource_path(year: 2024, log_type: "sales", resource_type: "bulk_upload_template")
+ get edit_mandatory_collection_resource_path(year: 2024, log_type: "sales", resource_type: "bulk_upload_template")
expect(response).to have_http_status(:not_found)
end
end
@@ -225,7 +225,7 @@ RSpec.describe CollectionResourcesController, type: :request do
end
it "returns page not found" do
- get update_mandatory_collection_resource_path(year: 2024, log_type: "sales", resource_type: "bulk_upload_template")
+ get edit_mandatory_collection_resource_path(year: 2024, log_type: "sales", resource_type: "bulk_upload_template")
expect(response).to have_http_status(:not_found)
end
end
@@ -240,7 +240,7 @@ RSpec.describe CollectionResourcesController, type: :request do
end
it "displays update collection resources page content" do
- get update_mandatory_collection_resource_path(year: 2024, log_type: "sales", resource_type: "bulk_upload_template")
+ get edit_mandatory_collection_resource_path(year: 2024, log_type: "sales", resource_type: "bulk_upload_template")
expect(page).to have_content("Sales 2024 to 2025")
expect(page).to have_content("Change the bulk upload template")
@@ -252,4 +252,86 @@ RSpec.describe CollectionResourcesController, type: :request do
end
end
end
+
+ describe "PATCH #update_mandatory_collection_resource" do
+ let(:some_file) { File.open(file_fixture("blank_bulk_upload_sales.csv")) }
+ let(:params) { { collection_resource: { year: 2024, log_type: "sales", resource_type: "bulk_upload_template", file: some_file } } }
+
+ before do
+ allow(UploadCollectionResourcesService).to receive(:upload_collection_resource)
+ end
+
+ context "when user is not signed in" do
+ it "redirects to the sign in page" do
+ patch update_mandatory_collection_resource_path(year: 2024, log_type: "sales", resource_type: "bulk_upload_template", file: some_file)
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+
+ context "when user is signed in as a data coordinator" do
+ let(:user) { create(:user, :data_coordinator) }
+
+ before do
+ sign_in user
+ end
+
+ it "returns page not found" do
+ patch update_mandatory_collection_resource_path, params: params
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ context "when user is signed in as a data provider" do
+ let(:user) { create(:user, :data_provider) }
+
+ before do
+ sign_in user
+ end
+
+ it "returns page not found" do
+ patch update_mandatory_collection_resource_path, params: params
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ context "when user is signed in as a support user" do
+ let(:user) { create(:user, :support) }
+
+ before do
+ allow(Time.zone).to receive(:today).and_return(Time.zone.local(2025, 1, 8))
+ allow(user).to receive(:need_two_factor_authentication?).and_return(false)
+ # rubocop:disable RSpec/AnyInstance
+ allow_any_instance_of(CollectionResourcesHelper).to receive(:editable_collection_resource_years).and_return([2024, 2025])
+ # rubocop:enable RSpec/AnyInstance
+ sign_in user
+ end
+
+ it "correcty updates a sales file" do
+ params = { collection_resource: { year: 2024, log_type: "sales", resource_type: "bulk_upload_template", file: some_file } }
+ patch update_mandatory_collection_resource_path, params: params
+
+ expect(response).to redirect_to(collection_resources_path)
+ expect(UploadCollectionResourcesService).to have_received(:upload_collection_resource).with("bulk-upload-sales-template-2024-25.xlsx", anything)
+ expect(flash[:notice]).to eq("The sales 2024 to 2025 bulk upload template has been updated")
+ end
+
+ it "correcty updates a lettings file" do
+ params = { collection_resource: { year: 2025, log_type: "lettings", resource_type: "paper_form", file: some_file } }
+ patch update_mandatory_collection_resource_path, params: params
+
+ expect(response).to redirect_to(collection_resources_path)
+ expect(UploadCollectionResourcesService).to have_received(:upload_collection_resource).with("2025_26_lettings_paper_form.pdf", anything)
+ expect(flash[:notice]).to eq("The lettings 2025 to 2026 paper form has been updated")
+ end
+
+ it "displays error message if the upload fails" do
+ allow(UploadCollectionResourcesService).to receive(:upload_collection_resource).and_raise(StandardError)
+
+ params = { collection_resource: { year: 2025, log_type: "lettings", resource_type: "paper_form", file: some_file } }
+ patch update_mandatory_collection_resource_path, params: params
+
+ expect(page).to have_content("There was an error uploading this file.")
+ end
+ end
+ end
end
diff --git a/spec/services/upload_collection_resources_service_spec.rb b/spec/services/upload_collection_resources_service_spec.rb
new file mode 100644
index 000000000..bb5cabc46
--- /dev/null
+++ b/spec/services/upload_collection_resources_service_spec.rb
@@ -0,0 +1,19 @@
+require "rails_helper"
+
+describe UploadCollectionResourcesService do
+ let(:service) { described_class }
+ let(:some_file) { File.open(file_fixture("blank_bulk_upload_sales.csv")) }
+ let(:storage_service) { instance_double(Storage::S3Service) }
+
+ describe "#upload_collection_resource" do
+ before do
+ allow(Storage::S3Service).to receive(:new).and_return(storage_service)
+ allow(storage_service).to receive(:write_file)
+ end
+
+ it "calls write_file on S3 service" do
+ expect(storage_service).to receive(:write_file).with("2025_26_lettings_paper_form.pdf", some_file)
+ service.upload_collection_resource("2025_26_lettings_paper_form.pdf", some_file)
+ end
+ end
+end