diff --git a/app/controllers/collection_resources_controller.rb b/app/controllers/collection_resources_controller.rb index ff54f875f..8de14e6fb 100644 --- a/app/controllers/collection_resources_controller.rb +++ b/app/controllers/collection_resources_controller.rb @@ -23,8 +23,53 @@ class CollectionResourcesController < ApplicationController download_resource(resource.download_filename) end + def edit + return render_not_found unless current_user.support? + + year = params[:year].to_i + resource_type = params[:resource_type] + log_type = params[:log_type] + + return render_not_found unless resource_for_year_can_be_updated?(year) + + @collection_resource = MandatoryCollectionResourcesService.generate_resource(log_type, year, resource_type) + + return render_not_found unless @collection_resource + + 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 + params.require(:collection_resource).permit(:year, :log_type, :resource_type, :file) + end + def download_resource(filename, download_filename) file = CollectionResourcesService.new.get_file(filename) return render_not_found unless file @@ -37,4 +82,8 @@ private displayed_collection_resource_years.include?(year) end + + def resource_for_year_can_be_updated?(year) + editable_collection_resource_years.include?(year) + end end diff --git a/app/services/mandatory_collection_resources_service.rb b/app/services/mandatory_collection_resources_service.rb index 74cb92518..82a6bd41e 100644 --- a/app/services/mandatory_collection_resources_service.rb +++ b/app/services/mandatory_collection_resources_service.rb @@ -16,6 +16,8 @@ class MandatoryCollectionResourcesService end def self.generate_resource(log_type, year, resource_type) + return unless log_type && year && resource_type + return unless %w[lettings sales].include?(log_type) return unless MANDATORY_RESOURCES.include?(resource_type) CollectionResource.new( 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 148c52500..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: "/", + 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 new file mode 100644 index 000000000..d9bfe5be9 --- /dev/null +++ b/app/views/collection_resources/edit.html.erb @@ -0,0 +1,28 @@ +<% content_for :before_content do %> + <%= govuk_back_link href: collection_resources_path %> +<% end %> + +
+
+ <%= 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 %> + + <%= f.govuk_error_summary %> + + <%= "#{@collection_resource.log_type.humanize} #{text_year_range_format(@collection_resource.year)}" %> +

Change the <%= @collection_resource.resource_type.humanize.downcase %>

+ +

+ This file will be available for all users to download. +

+ + <%= f.govuk_file_field :file, + label: { text: "Upload file", size: "m" } %> + + <%= f.govuk_submit "Save changes" %> + <%= govuk_button_link_to "Cancel", collection_resources_path, secondary: true %> + <% end %> +
+
diff --git a/config/routes.rb b/config/routes.rb index 7fd495af6..3c4a8d90c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -42,6 +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#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 4a066ef13..b725a2991 100644 --- a/spec/requests/collection_resources_controller_spec.rb +++ b/spec/requests/collection_resources_controller_spec.rb @@ -86,6 +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: 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 @@ -182,4 +195,143 @@ RSpec.describe CollectionResourcesController, type: :request do end end end + + describe "GET #edit_mandatory_collection_resource" do + context "when user is not signed in" do + it "redirects to the sign in page" do + 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 + + 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 + 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 + + 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 + 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 + + 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) + sign_in user + end + + it "displays update collection resources page content" do + 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") + expect(page).to have_content("This file will be available for all users to download.") + expect(page).to have_content("Upload file") + expect(page).to have_button("Save changes") + expect(page).to have_link("Back", href: collection_resources_path) + expect(page).to have_link("Cancel", href: collection_resources_path) + 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