diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb index db31fb12a..dfc6d6909 100644 --- a/app/controllers/organisations_controller.rb +++ b/app/controllers/organisations_controller.rb @@ -155,6 +155,33 @@ class OrganisationsController < ApplicationController @merge_request = MergeRequest.new end + def data_sharing_agreement + return render_not_found unless FeatureToggle.new_data_sharing_agreement? + + @data_sharing_agreement = current_user.organisation.data_sharing_agreement + end + + def confirm_data_sharing_agreement + return render_not_found unless FeatureToggle.new_data_sharing_agreement? + return render_not_found unless current_user.is_dpo? + return render_not_found if @organisation.data_sharing_agreement.present? + + data_sharing_agreement = DataSharingAgreement.new( + organisation: current_user.organisation, + signed_at: Time.zone.now, + data_protection_officer: current_user, + ) + + if data_sharing_agreement.save + flash[:notice] = "You have accepted the Data Sharing Agreement" + flash[:notification_banner_body] = "Your organisation can now submit logs." + + redirect_to details_organisation_path(@organisation) + else + render :data_sharing_agreement + end + end + private def org_params diff --git a/app/services/feature_toggle.rb b/app/services/feature_toggle.rb index a09d91f1d..1b6ae4f42 100644 --- a/app/services/feature_toggle.rb +++ b/app/services/feature_toggle.rb @@ -45,4 +45,8 @@ class FeatureToggle def self.merge_organisations_enabled? !Rails.env.production? end + + def self.new_data_sharing_agreement? + !Rails.env.production? + end end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 48a790802..d86a622f6 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -125,6 +125,9 @@ title_id: "swanky-notifications" ) do |notification_banner| notification_banner.heading(text: flash.notice.html_safe) + if flash[:notification_banner_body] + tag.p flash[:notification_banner_body] + end end %> <% end %> <%= content_for?(:content) ? yield(:content) : yield %> diff --git a/config/routes.rb b/config/routes.rb index 70cda158d..f007317dc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -109,6 +109,9 @@ Rails.application.routes.draw do resources :organisations do member do get "details", to: "organisations#details" + get "data-sharing-agreement", to: "organisations#data_sharing_agreement" + post "data-sharing-agreement", to: "organisations#confirm_data_sharing_agreement" + get "users", to: "organisations#users" get "users/invite", to: "users/account#new" get "lettings-logs", to: "organisations#lettings_logs" diff --git a/spec/requests/organisations_controller_spec.rb b/spec/requests/organisations_controller_spec.rb index 6013a5813..374503106 100644 --- a/spec/requests/organisations_controller_spec.rb +++ b/spec/requests/organisations_controller_spec.rb @@ -2,10 +2,10 @@ require "rails_helper" RSpec.describe OrganisationsController, type: :request do let(:organisation) { user.organisation } - let!(:unauthorised_organisation) { FactoryBot.create(:organisation) } + let!(:unauthorised_organisation) { create(:organisation) } let(:headers) { { "Accept" => "text/html" } } let(:page) { Capybara::Node::Simple.new(response.body) } - let(:user) { FactoryBot.create(:user, :data_coordinator) } + let(:user) { create(:user, :data_coordinator) } let(:new_value) { "Test Name 35" } let(:params) { { id: organisation.id, organisation: { name: new_value } } } @@ -41,9 +41,9 @@ RSpec.describe OrganisationsController, type: :request do context "when user is signed in" do describe "#schemes" do context "when support user" do - let(:user) { FactoryBot.create(:user, :support) } - let!(:schemes) { FactoryBot.create_list(:scheme, 5) } - let!(:same_org_scheme) { FactoryBot.create(:scheme, owning_organisation: user.organisation) } + let(:user) { create(:user, :support) } + let!(:schemes) { create_list(:scheme, 5) } + let!(:same_org_scheme) { create(:scheme, owning_organisation: user.organisation) } before do allow(user).to receive(:need_two_factor_authentication?).and_return(false) @@ -72,11 +72,11 @@ RSpec.describe OrganisationsController, type: :request do end context "when searching" do - let!(:searched_scheme) { FactoryBot.create(:scheme, owning_organisation: user.organisation) } + let!(:searched_scheme) { create(:scheme, owning_organisation: user.organisation) } let(:search_param) { searched_scheme.id } before do - FactoryBot.create(:location, scheme: searched_scheme) + create(:location, scheme: searched_scheme) allow(user).to receive(:need_two_factor_authentication?).and_return(false) get "/organisations/#{organisation.id}/schemes?search=#{search_param}" end @@ -99,9 +99,9 @@ RSpec.describe OrganisationsController, type: :request do end context "when data coordinator user" do - let(:user) { FactoryBot.create(:user, :data_coordinator) } - let!(:schemes) { FactoryBot.create_list(:scheme, 5) } - let!(:same_org_scheme) { FactoryBot.create(:scheme, owning_organisation: user.organisation) } + let(:user) { create(:user, :data_coordinator) } + let!(:schemes) { create_list(:scheme, 5) } + let!(:same_org_scheme) { create(:scheme, owning_organisation: user.organisation) } before do sign_in user @@ -135,7 +135,7 @@ RSpec.describe OrganisationsController, type: :request do end context "with schemes that are not in scope for the user, i.e. that they do not belong to" do - let!(:unauthorised_organisation) { FactoryBot.create(:organisation) } + let!(:unauthorised_organisation) { create(:organisation) } before do get "/organisations/#{unauthorised_organisation.id}/schemes", headers:, params: {} @@ -147,11 +147,11 @@ RSpec.describe OrganisationsController, type: :request do end context "when searching" do - let!(:searched_scheme) { FactoryBot.create(:scheme, owning_organisation: user.organisation) } + let!(:searched_scheme) { create(:scheme, owning_organisation: user.organisation) } let(:search_param) { searched_scheme.id_to_display } before do - FactoryBot.create(:location, scheme: searched_scheme) + create(:location, scheme: searched_scheme) get "/organisations/#{organisation.id}/schemes?search=#{search_param}" end @@ -247,9 +247,9 @@ RSpec.describe OrganisationsController, type: :request do context "when accessing the users tab" do context "with an organisation that the user belongs to" do - let!(:other_user) { FactoryBot.create(:user, organisation: user.organisation, name: "User 2") } - let!(:inactive_user) { FactoryBot.create(:user, organisation: user.organisation, active: false, name: "User 3") } - let!(:other_org_user) { FactoryBot.create(:user, name: "User 4") } + let!(:other_user) { create(:user, organisation: user.organisation, name: "User 2") } + let!(:inactive_user) { create(:user, organisation: user.organisation, active: false, name: "User 3") } + let!(:other_org_user) { create(:user, name: "User 4") } before do get "/organisations/#{organisation.id}/users", headers:, params: {} @@ -501,7 +501,7 @@ RSpec.describe OrganisationsController, type: :request do end context "with a data provider user" do - let(:user) { FactoryBot.create(:user) } + let(:user) { create(:user) } before do sign_in user @@ -630,7 +630,7 @@ RSpec.describe OrganisationsController, type: :request do end context "with a support user" do - let(:user) { FactoryBot.create(:user, :support) } + let(:user) { create(:user, :support) } before do allow(user).to receive(:need_two_factor_authentication?).and_return(false) @@ -681,9 +681,9 @@ RSpec.describe OrganisationsController, type: :request do let(:number_of_org2_lettings_logs) { 4 } before do - FactoryBot.create_list(:lettings_log, number_of_org1_lettings_logs, created_by: user) - FactoryBot.create(:lettings_log, created_by: user, status: "pending", skip_update_status: true) - FactoryBot.create_list(:lettings_log, number_of_org2_lettings_logs, created_by: nil, owning_organisation_id: unauthorised_organisation.id, managing_organisation_id: unauthorised_organisation.id) + create_list(:lettings_log, number_of_org1_lettings_logs, created_by: user) + create(:lettings_log, created_by: user, status: "pending", skip_update_status: true) + create_list(:lettings_log, number_of_org2_lettings_logs, created_by: nil, owning_organisation_id: unauthorised_organisation.id, managing_organisation_id: unauthorised_organisation.id) get "/organisations/#{organisation.id}/lettings-logs", headers:, params: {} end @@ -715,8 +715,8 @@ RSpec.describe OrganisationsController, type: :request do end context "when using a search query" do - let(:logs) { FactoryBot.create_list(:lettings_log, 3, :completed, owning_organisation: user.organisation, created_by: user) } - let(:log_to_search) { FactoryBot.create(:lettings_log, :completed, owning_organisation: user.organisation, created_by: user) } + let(:logs) { create_list(:lettings_log, 3, :completed, owning_organisation: user.organisation, created_by: user) } + let(:log_to_search) { create(:lettings_log, :completed, owning_organisation: user.organisation, created_by: user) } let(:log_total_count) { LettingsLog.where(owning_organisation: user.organisation).count } it "has search results in the title" do @@ -757,7 +757,7 @@ RSpec.describe OrganisationsController, type: :request do 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) } + let!(:matching_postcode_log) { create(:lettings_log, :completed, owning_organisation: user.organisation, postcode_full: log_to_search.postcode_full) } it "displays all matching logs" do get "/organisations/#{organisation.id}/lettings-logs?search=#{log_to_search.postcode_full}", headers: headers, params: {} @@ -771,7 +771,7 @@ RSpec.describe OrganisationsController, type: :request do context "when there are more than 1 page of search results" do let(:postcode) { "XX11YY" } - let(:logs) { FactoryBot.create_list(:lettings_log, 30, :completed, owning_organisation: user.organisation, postcode_full: postcode) } + let(:logs) { create_list(:lettings_log, 30, :completed, owning_organisation: user.organisation, postcode_full: postcode) } let(:log_total_count) { LettingsLog.where(owning_organisation: user.organisation).count } it "has title with pagination details for page 1" do @@ -808,7 +808,7 @@ RSpec.describe OrganisationsController, type: :request do context "when search and filter is present" do let(:matching_postcode) { log_to_search.postcode_full } let(:matching_status) { "in_progress" } - let!(:log_matching_filter_and_search) { FactoryBot.create(:lettings_log, :in_progress, owning_organisation: user.organisation, postcode_full: matching_postcode, created_by: user) } + let!(:log_matching_filter_and_search) { create(:lettings_log, :in_progress, owning_organisation: user.organisation, postcode_full: matching_postcode, created_by: user) } it "shows only logs matching both search and filters" do get "/organisations/#{organisation.id}/lettings-logs?search=#{matching_postcode}&status[]=#{matching_status}", headers: headers, params: {} @@ -827,8 +827,8 @@ RSpec.describe OrganisationsController, type: :request do let(:number_of_org2_sales_logs) { 4 } before do - FactoryBot.create_list(:sales_log, number_of_org1_sales_logs, owning_organisation_id: organisation.id) - FactoryBot.create_list(:sales_log, number_of_org2_sales_logs, owning_organisation_id: unauthorised_organisation.id) + create_list(:sales_log, number_of_org1_sales_logs, owning_organisation_id: organisation.id) + create_list(:sales_log, number_of_org2_sales_logs, owning_organisation_id: unauthorised_organisation.id) get "/organisations/#{organisation.id}/sales-logs", headers:, params: {} end @@ -859,8 +859,8 @@ RSpec.describe OrganisationsController, type: :request do end context "when using a search query" do - let(:logs) { FactoryBot.create_list(:sales_log, 3, :completed, owning_organisation: user.organisation, created_by: user) } - let(:log_to_search) { FactoryBot.create(:sales_log, :completed, owning_organisation: user.organisation, created_by: user) } + let(:logs) { create_list(:sales_log, 3, :completed, owning_organisation: user.organisation, created_by: user) } + let(:log_to_search) { create(:sales_log, :completed, owning_organisation: user.organisation, created_by: user) } let(:log_total_count) { LettingsLog.where(owning_organisation: user.organisation).count } it "has search results in the title" do @@ -898,7 +898,7 @@ RSpec.describe OrganisationsController, type: :request do context "when search and filter is present" do let(:matching_status) { "completed" } - let!(:log_matching_filter_and_search) { FactoryBot.create(:sales_log, :completed, owning_organisation: user.organisation, created_by: user) } + let!(:log_matching_filter_and_search) { create(:sales_log, :completed, owning_organisation: user.organisation, created_by: user) } let(:matching_id) { log_matching_filter_and_search.id } it "shows only logs matching both search and filters" do @@ -914,8 +914,8 @@ RSpec.describe OrganisationsController, type: :request do end context "when viewing a specific organisation's users" do - let!(:users) { FactoryBot.create_list(:user, 5, organisation:) } - let!(:different_org_users) { FactoryBot.create_list(:user, 5) } + let!(:users) { create_list(:user, 5, organisation:) } + let!(:different_org_users) { create_list(:user, 5) } before do get "/organisations/#{organisation.id}/users", headers:, params: {} @@ -944,7 +944,7 @@ RSpec.describe OrganisationsController, type: :request do end context "when a search parameter is passed" do - let!(:matching_user) { FactoryBot.create(:user, organisation:, name: "joe", email: "matching@example.com") } + let!(:matching_user) { create(:user, organisation:, name: "joe", email: "matching@example.com") } let(:org_user_count) { User.where(organisation:).count } before do @@ -1014,8 +1014,8 @@ RSpec.describe OrganisationsController, type: :request do end context "when our search term matches an email and a name" do - let!(:matching_user) { FactoryBot.create(:user, organisation:, name: "Foobar", email: "some@example.com") } - let!(:another_matching_user) { FactoryBot.create(:user, organisation:, name: "Joe", email: "foobar@example.com") } + let!(:matching_user) { create(:user, organisation:, name: "Foobar", email: "some@example.com") } + let!(:another_matching_user) { create(:user, organisation:, name: "Joe", email: "foobar@example.com") } let!(:org_user_count) { User.where(organisation:).count } let(:search_param) { "Foobar" } @@ -1068,7 +1068,7 @@ RSpec.describe OrganisationsController, type: :request do let(:total_organisations_count) { Organisation.all.count } before do - FactoryBot.create_list(:organisation, 25) + create_list(:organisation, 25) get "/organisations" end @@ -1115,8 +1115,8 @@ RSpec.describe OrganisationsController, type: :request do end context "when searching" do - let!(:searched_organisation) { FactoryBot.create(:organisation, name: "Unusual name") } - let!(:other_organisation) { FactoryBot.create(:organisation, name: "Some other name") } + let!(:searched_organisation) { create(:organisation, name: "Unusual name") } + let!(:other_organisation) { create(:organisation, name: "Some other name") } let(:search_param) { "Unusual" } before do @@ -1225,7 +1225,7 @@ RSpec.describe OrganisationsController, type: :request do end context "when the user is a support user" do - let(:user) { FactoryBot.create(:user, :support) } + let(:user) { create(:user, :support) } before do allow(user).to receive(:need_two_factor_authentication?).and_return(false) @@ -1234,7 +1234,7 @@ RSpec.describe OrganisationsController, type: :request do context "when they view the lettings logs tab" do before do - FactoryBot.create(:lettings_log, owning_organisation: organisation) + create(:lettings_log, owning_organisation: organisation) end it "has CSV download buttons with the correct paths if at least 1 log exists" do @@ -1244,12 +1244,12 @@ RSpec.describe OrganisationsController, type: :request do end context "when you download the CSV" do - let(:other_organisation) { FactoryBot.create(:organisation) } + let(:other_organisation) { create(:organisation) } before do - FactoryBot.create_list(:lettings_log, 2, owning_organisation: organisation) - FactoryBot.create(:lettings_log, owning_organisation: organisation, status: "pending", skip_update_status: true) - FactoryBot.create_list(:lettings_log, 2, owning_organisation: other_organisation) + create_list(:lettings_log, 2, owning_organisation: organisation) + create(:lettings_log, owning_organisation: organisation, status: "pending", skip_update_status: true) + create_list(:lettings_log, 2, owning_organisation: other_organisation) end it "only includes logs from that organisation" do @@ -1279,7 +1279,7 @@ RSpec.describe OrganisationsController, type: :request do context "when they view the sales logs tab" do before do - FactoryBot.create(:sales_log, owning_organisation: organisation) + create(:sales_log, owning_organisation: organisation) end it "has CSV download buttons with the correct paths if at least 1 log exists" do @@ -1289,12 +1289,12 @@ RSpec.describe OrganisationsController, type: :request do end context "when you download the CSV" do - let(:other_organisation) { FactoryBot.create(:organisation) } + let(:other_organisation) { create(:organisation) } before do - FactoryBot.create_list(:sales_log, 2, owning_organisation: organisation) - FactoryBot.create(:sales_log, owning_organisation: organisation, status: "pending", skip_update_status: true) - FactoryBot.create_list(:sales_log, 2, owning_organisation: other_organisation) + create_list(:sales_log, 2, owning_organisation: organisation) + create(:sales_log, owning_organisation: organisation, status: "pending", skip_update_status: true) + create_list(:sales_log, 2, owning_organisation: other_organisation) end it "only includes logs from that organisation" do @@ -1414,11 +1414,11 @@ RSpec.describe OrganisationsController, type: :request do context "when you download the CSV" do let(:headers) { { "Accept" => "text/csv" } } - let(:other_organisation) { FactoryBot.create(:organisation) } + let(:other_organisation) { create(:organisation) } before do - FactoryBot.create_list(:user, 3, organisation:) - FactoryBot.create_list(:user, 2, organisation: other_organisation) + create_list(:user, 3, organisation:) + create_list(:user, 2, organisation: other_organisation) end it "only includes users from that organisation" do @@ -1429,4 +1429,111 @@ RSpec.describe OrganisationsController, type: :request do end end end + + describe "GET #data_sharing_agreement" do + context "when not signed in" do + it "redirects to sign in" do + get "/organisations/#{organisation.id}/data-sharing-agreement", headers: headers + expect(response).to redirect_to("/account/sign-in") + end + end + + context "when signed in" do + before do + allow(user).to receive(:need_two_factor_authentication?).and_return(false) + sign_in user + end + + context "when flag not enabled" do + before do + allow(FeatureToggle).to receive(:new_data_sharing_agreement?).and_return(false) + end + + it "returns not found" do + get "/organisations/#{organisation.id}/data-sharing-agreement", headers: headers + expect(response).to have_http_status(:not_found) + end + end + + context "when flag enabled" do + before do + allow(FeatureToggle).to receive(:new_data_sharing_agreement?).and_return(true) + end + + it "returns ok" do + get "/organisations/#{organisation.id}/data-sharing-agreement", headers: headers + expect(response).to have_http_status(:ok) + end + end + end + end + + describe "POST #data_sharing_agreement" do + context "when not signed in" do + it "redirects to sign in" do + post "/organisations/#{organisation.id}/data-sharing-agreement", headers: headers + expect(response).to redirect_to("/account/sign-in") + end + end + + context "when signed in" do + before do + allow(user).to receive(:need_two_factor_authentication?).and_return(false) + sign_in user + end + + context "when flag not enabled" do + before do + allow(FeatureToggle).to receive(:new_data_sharing_agreement?).and_return(false) + end + + it "returns not found" do + post "/organisations/#{organisation.id}/data-sharing-agreement", headers: headers + expect(response).to have_http_status(:not_found) + end + end + + context "when flag enabled" do + before do + allow(FeatureToggle).to receive(:new_data_sharing_agreement?).and_return(true) + end + + context "when user not dpo" do + let(:user) { create(:user, is_dpo: false) } + + it "returns not found" do + post "/organisations/#{organisation.id}/data-sharing-agreement", headers: headers + expect(response).to have_http_status(:not_found) + end + end + + context "when user is dpo" do + let(:user) { create(:user, is_dpo: true) } + + it "returns redirects to details page" do + post "/organisations/#{organisation.id}/data-sharing-agreement", headers: headers + + expect(response).to redirect_to("/organisations/#{organisation.id}/details") + expect(flash[:notice]).to eq("You have accepted the Data Sharing Agreement") + expect(flash[:notification_banner_body]).to eq("Your organisation can now submit logs.") + end + + context "when the user has already accepted the agreement" do + before do + DataSharingAgreement.create!( + organisation: user.organisation, + signed_at: Time.zone.now - 1.day, + data_protection_officer: user, + ) + end + + it "returns not found" do + post "/organisations/#{organisation.id}/data-sharing-agreement", headers: headers + expect(response).to have_http_status(:not_found) + end + end + end + end + end + end end