diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb index db31fb12a..c6799bd55 100644 --- a/app/controllers/organisations_controller.rb +++ b/app/controllers/organisations_controller.rb @@ -155,6 +155,38 @@ 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, + organisation_name: @organisation.name, + organisation_address: @organisation.address_row, + organisation_phone_number: @organisation.phone, + dpo_email: current_user.email, + dpo_name: current_user.name, + ) + + 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/helpers/data_sharing_agreement_helper.rb b/app/helpers/data_sharing_agreement_helper.rb new file mode 100644 index 000000000..d8949de4f --- /dev/null +++ b/app/helpers/data_sharing_agreement_helper.rb @@ -0,0 +1,87 @@ +module DataSharingAgreementHelper + def data_sharing_agreement_row(user:, organisation:, summary_list:) + summary_list.row do |row| + row.key { "Data Sharing Agreement" } + row.action( + href: data_sharing_agreement_organisation_path(organisation), + text: "View agreement", + ) + + row.value do + simple_format( + data_sharing_agreement_first_line(organisation:, user:), + wrapper_tag: "span", + class: "govuk-!-margin-right-4", + ) + simple_format( + data_sharing_agreement_second_line(organisation:, user:), + wrapper_tag: "span", + class: "govuk-!-font-weight-regular app-!-colour-muted", + ) + end + end + end + + def name_for_data_sharing_agreement(data_sharing_agreement, user) + if data_sharing_agreement.present? + data_sharing_agreement.data_protection_officer.name + elsif user.is_dpo? + user.name + else + "[DPO name]" + end + end + + def org_name_for_data_sharing_agreement(data_sharing_agreement, user) + if data_sharing_agreement.present? + data_sharing_agreement.organisation_name + else + user.organisation.name + end + end + + # rubocop:disable Rails/HelperInstanceVariable + def section_12_2(data_sharing_agreement:, user:, organisation:) + if data_sharing_agreement + @org_address = data_sharing_agreement.organisation_address + @org_name = data_sharing_agreement.organisation_name + @org_phone = data_sharing_agreement.organisation_phone_number + @dpo_name = data_sharing_agreement.dpo_name + @dpo_email = data_sharing_agreement.dpo_email + else + @org_name = organisation.name + @org_address = organisation.address_row + @org_phone = organisation.phone + + if user.is_dpo? + @dpo_name = user.name + @dpo_email = user.email + else + @dpo_name = "[DPO name]" + @dpo_email = "[DPO email]" + end + end + + "12.2. For #{@org_name}: Name: #{@dpo_name}, Postal Address: #{@org_address}, E-mail address: #{@dpo_email}, Telephone number: #{@org_phone}" + end +# rubocop:enable Rails/HelperInstanceVariable + +private + + def data_sharing_agreement_first_line(organisation:, user:) + return "Not accepted" if organisation.data_sharing_agreement.blank? + + if user.support? + "Accepted #{organisation.data_sharing_agreement.signed_at.strftime('%d/%m/%Y')}" + else + "Accepted" + end + end + + def data_sharing_agreement_second_line(organisation:, user:) + if organisation.data_sharing_agreement.present? + organisation.data_sharing_agreement.data_protection_officer.name if user.support? + else + "Data protection officer must sign" unless user.is_dpo? + end + end +end diff --git a/app/models/data_sharing_agreement.rb b/app/models/data_sharing_agreement.rb new file mode 100644 index 000000000..7b366c9fe --- /dev/null +++ b/app/models/data_sharing_agreement.rb @@ -0,0 +1,4 @@ +class DataSharingAgreement < ApplicationRecord + belongs_to :organisation + belongs_to :data_protection_officer, class_name: "User" +end diff --git a/app/models/organisation.rb b/app/models/organisation.rb index 7df43c429..cd2fd8c38 100644 --- a/app/models/organisation.rb +++ b/app/models/organisation.rb @@ -4,6 +4,7 @@ class Organisation < ApplicationRecord has_many :managed_lettings_logs, class_name: "LettingsLog", foreign_key: "managing_organisation_id" has_many :owned_sales_logs, class_name: "SalesLog", foreign_key: "owning_organisation_id", dependent: :delete_all has_many :data_protection_confirmations + has_one :data_sharing_agreement has_many :organisation_rent_periods has_many :owned_schemes, class_name: "Scheme", foreign_key: "owning_organisation_id", dependent: :delete_all has_many :parent_organisation_relationships, foreign_key: :child_organisation_id, class_name: "OrganisationRelationship" @@ -74,6 +75,10 @@ class Organisation < ApplicationRecord %i[address_line1 address_line2 postcode].map { |field| public_send(field) }.join("\n") end + def address_row + %i[address_line1 address_line2 postcode].map { |field| public_send(field) }.join(", ") + end + def rent_periods organisation_rent_periods.pluck(:rent_period) end 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/app/views/organisations/data_sharing_agreement.html.erb b/app/views/organisations/data_sharing_agreement.html.erb new file mode 100644 index 000000000..9e1c020d7 --- /dev/null +++ b/app/views/organisations/data_sharing_agreement.html.erb @@ -0,0 +1,144 @@ +<%= content_for :title, "Data sharing agreement" %> + +
+ <% if @data_sharing_agreement %> + This agreement is made the <%= @data_sharing_agreement.signed_at.day.ordinalize %> day of <%= @data_sharing_agreement.signed_at.strftime("%B") %> <%= @data_sharing_agreement.signed_at.year %> + <% elsif current_user.is_dpo? %> + This agreement is made the <%= Time.zone.now.day.ordinalize %> day of <%= Time.zone.now.strftime("%B") %> <%= Time.zone.now.year %> + <% else %> + This agreement is made the [XX] day of [XX] 20[XX] + <% end %> +
+between
+ <% if @data_sharing_agreement %> +1) <%= @data_sharing_agreement.organisation_name %> of <%= @data_sharing_agreement.organisation_address %> (“CORE Data Provider”)
+ <% else %> +1) <%= @organisation.name %> of <%= @organisation.address_row %> (“CORE Data Provider”)
+ <% end %> +and
+2) The Department for Levelling Up, Housing and Communities of 2 Marsham Street, London, SW1P 4DF (“DLUHC”)
+1.1. The Department for Levelling Up, Housing and Communities (DLUHC) collect data on social housing lettings and sales via CORE (COntinuous REcording of social housing lettings and sales) for statistical purposes. They are the data controller for all data within the CORE system.
+1.2. The purpose of this Agreement is to describe the duties of CORE data providers to DLUHC and to formalise the arrangement established to share the data between DLUHC and data providers. This agreement covers all data in the CORE system. This agreement does not cover data providers’ own versions of these data.
+1.3. Data providers and DLUHC will each be responsible for compliance with the Data Protection legislation including the Data Protection Act 2018 and the EU General Data Protection Regulation (GDPR).
+1.4. DLUHC shall only collect data from and share data with CORE data providers that have signed this data sharing agreement.
+1.5. There are occasions when DLUHC may instruct a third party to carry out analysis on the CORE dataset on their behalf. This third party will be a data processor for DLUHC and work under a contract ensuring data protection compliance.
+It is now agreed as follows:
+2.1. In this Agreement the following words and phrases shall have the following meanings, unless expressly stated to the contrary:
+2.2. Headings are included in this Agreement for ease of reference only and shall not affect the interpretation or construction of this Agreement.
+2.3. References in this Agreement to Clauses, Paragraphs and Annexes are, unless otherwise provided, references to the Clauses, Paragraphs and Annexes of this Agreement.
+2.4. In the event and to the extent only of any conflict or inconsistency between the provisions of this Agreement and the provisions of any document referred to or referenced herein, the provisions of this Agreement shall prevail.
+3.1. This Agreement shall commence upon signature by the Parties and shall continue in effect whilst the CORE data collection remains live, in accordance with the requirements of this Agreement unless otherwise subject to earlier termination in accordance with Clause 15.
+3.2. The Parties may, by mutual consent in writing, agree to amend this agreement.
+4.1. CORE (COntinuous Recording of social housing lettings and sales) is a national information source funded by the Department for Levelling Up, Housing and Communities that records information on the characteristics of Private Registered Providers’ and Local Authorities’ new social housing tenants and the homes they rent and buy.
+4.2. The CORE dataset includes information on the letting or sale, type of tenancy or sale, rents and charges, demographic information about the tenant/buyer and other information related to the tenants/buyers themselves (e.g. source of referral, route into housing, whether they are on benefits, income).
+4.3. The data in the CORE dataset is considered personal data because individuals could be easily identified. The dataset does not contain direct personal identifiers but when taken as a whole the data allows social housing tenants to be identified. This is because the dataset contains information such as UPRN (Unique Property Reference Number), which allows properties to be uniquely identified or full postcode data.
+4.4. The dataset also contains information which is very sensitive, and which in some cases is “special category” personal data for the purposes of the GDPR, and if disclosed could cause considerable distress to the data subject, for example it indicates whether the social housing tenant has been in prison or probation or referred by a mental health institution; or whether anyone in the household has suffered from domestic abuse or hate crime.
+5.1. DLUHC shall be the ‘Data controller’ for all personal data held within the CORE database.
+5.2. CORE data providers are data controllers for personal data that they hold within their own systems. It is recognised that while much of the CORE data will be replicated in data collections held by CORE data providers, each organisation accepts full data controller responsibility for the data it holds.
+5.3. CORE data providers need to submit information for the tenancy, the tenants and the property each time there is a new social housing letting or sale. The data collection covers general needs and supported housing lettings. Since April 2012, local authorities and private registered providers report their affordable rent lettings as well as their social rent lettings and, from April 2017, rent-to-buy lettings are also included.
+5.4. In order to be compliant with the data protection legislation all data subjects (social housing tenants and buyers) from the CORE dataset need to be informed of how their data will be processed and used.
+5.5. DLUHC has set the information that needs to be provided to new CORE data subjects in the privacy notice in Annex 1 of this agreement. CORE data providers must either share the DLUHC privacy notice with tenants or if using their own privacy notice, state within that they share the data with DLUHC and provide a link to the DLUHC privacy notice.
+6.1. CORE provides DLUHC with an essential evidence base for monitoring and developing government policy, in particular to assess who is accessing social housing and their associated tenancy and property details. It is necessary therefore that the personal data that forms the CORE data is processed for that purpose. The legal basis for processing this personal data is s(8)(d) of the Data Protection Act 2018 which states:
+ +++In Article 6(1) of the GDPR (lawfulness of processing), the reference in point (e) to processing of personal data that is necessary for the performance of a task carried out in the public interest or in the exercise of the controller’s official authority includes processing of personal data that is necessary for— (d) the exercise of a function of the Crown, a Minister of the Crown or a government department.
+
6.2. In addition to the previous paragraph, processing of ‘special category’ personal data is prohibited unless a condition at Article 9 of the GDPR is satisfied. In this case the relevant condition is Article 9(2)(g) “the processing is necessary for reasons of substantial public interest”. This requires the processing to have a basis in law. In this case section 10(3) of the Act provides that the requirement is met by the processing being necessary by virtue of the conditions at paragraphs 5 and 6 of Schedule 1, Part 2.
+6.3. Article 10 of the GDPR requires that the processing of any criminal convictions and offences data shall be carried out only under control of official authority or when the processing is authorised in law. In this case section 10(5) of the Act provides that this requirement is met by the processing being necessary by virtue of the same condition at Schedule 1, Part 2.
+6.4. The data submitted to CORE and processed is made available to the CORE data providers registered in the system for further use to encourage use of available evidence to assess housing requirements. CORE data providers can only access CORE personal data that has been submitted by their organisation.
+7.1. The Parties understand that the CORE data submitted via the CORE data collection and accessed via the same system is being used for research and analytical purposes only and cannot be used for any other purpose, such as making decisions in relation to specific individuals.
+8.1. The security of the CORE data collection system is compliant with Government security standards (https://core.communities.gov.uk/public/index.html).
+8.2. All parts of the CORE website where an individual’s letting/sale data is submitted or downloaded are only accessible via login and passwords. The CORE system has hierarchies in place to ensure that data providers and users can only submit, view or download data for the organisations they are associated with.
+8.3. Data providers will be able to access the CORE personal data they submitted right after it is validated by the system; but can only access processed data from MHCLG after MHCLG has published the data. MHCLG will make the processed data available to data providers as soon as possible via the system.
+8.4.CORE team members in DLUHC and third-party data processors as developers/maintenance contractors have access to all parts of the website, including data. The handling of CORE data by contractors is covered in the contracts with these organisations. All CORE staff that have access to the data have had training on how to handle personal data.
+8.5. All work carried out by DLUHC will follow appropriate security measures and procedures to ensure the protection of the data.
+9.1. CORE data providers and DLUHC agree that they shall:
+9.2. The data providers and DLUHC shall comply at all times with the Data Protection Legislation and shall ensure that they each perform their obligations under this agreement in full compliance with the Data Protection Legislation and any other applicable law, in particular the Human Rights Act 1998 and the common law duty of confidentiality.
+9.3. CORE data providers should limit access to CORE to a small number of individuals who can be named on request. CORE access is limited to registered users only via password, but it is the responsibility of the CORE data providers to ensure that all individuals granted access to the datasets should be briefed on the legal requirements around handling and storing the Data from CORE.
+10.1. DLUHC acknowledges that CORE data providers that are or act on behalf of local authorities may be subject to the requirements of the FOIA and the Environmental Information Regulations and shall assist and cooperate with them to enable them to comply with their Information disclosure requirements.
+11.1. CORE data providers will report to DLUHC any loss or unauthorised release of the Data as soon as possible and no later than 24 hours after the loss or unauthorised release is identified. DLUHC will report to CORE data providers any loss or unauthorised release of the Data as soon as possible and no later than 24 hours after the loss or unauthorised release is identified.
+11.2. CORE data providers and DLUHC acknowledge that any loss or unauthorised release of the Data can be treated as valid grounds for immediately terminating this agreement by DLUHC.
+12.1. CORE data providers and DLUHC will each appoint an Authorised Representative to be the primary point of contact in all day-to-day matters relating to this Agreement:
++ <%= section_12_2(data_sharing_agreement: @data_sharing_agreement, user: current_user, organisation: @organisation) %> +
+12.3. For DLUHC: Name: Rachel Worledge, + Postal Address: South-west section, 4th Floor, Fry Building, 2 Marsham Street, London, SW1P 4DF, + E-mail address: Rachel.Worledge@levellingup.gov.uk
+13.1. The Data potentially allows for persons to be identified, although the risk of this happening should be minimised by the steps taken in clause 9. CORE data providers should agree to carry out a thorough check of the Data and ensure that all steps are taken within its powers to minimise the risk that any outputs lead to identification of a person by a third party.
+14.1. Any disputes arising concerning this Agreement will be resolved initially by discussions between the Authorised Representatives of the CORE data providers and DLUHC.
+14.2. If the dispute cannot be resolved amicably between the Authorised Representatives then the matter will be escalated to: for the CORE data providers: the Chief Executive; and for DLUHC: the Deputy Director of the Data, Analytics and Statistics Division.
+15.1. Any Party may terminate this Agreement upon one month’s written notice to the other.
+15.2. Any Party may terminate this Agreement with immediate effect in the event of a + material breach of its obligations by the other Party to this Agreement.
+16.1. The Parties shall comply with all relevant legislation, regulations, orders, statutory instruments and any amendments or re-enactments thereof from the commencement of this agreement.
+As witness of which the parties have set their hands on the day and year first above written + signed for and on behalf of the Data Protection Officer for <%= org_name_for_data_sharing_agreement(@data_sharing_agreement, current_user) %>, by:
+SIGNED for and on behalf of the deputy director of the data, analytics & statistics in the Department for Levelling Up, Housing and Communities, by:
+Is your organisation merging with another? <%= govuk_link_to "Let us know using this form", merge_request_organisation_path %>
+Is your organisation merging with another? <%= govuk_link_to "Let us know using this form", merge_request_organisation_path(@organisation) %>
<% end %> 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/db/migrate/20230530094653_add_data_sharing_agreement.rb b/db/migrate/20230530094653_add_data_sharing_agreement.rb new file mode 100644 index 000000000..a148b32cc --- /dev/null +++ b/db/migrate/20230530094653_add_data_sharing_agreement.rb @@ -0,0 +1,22 @@ +class AddDataSharingAgreement < ActiveRecord::Migration[7.0] + def change + create_table :data_sharing_agreements do |t| + t.belongs_to :organisation + t.belongs_to :data_protection_officer, class_name: "User" + + t.datetime :signed_at, null: false + t.string :organisation_name, null: false + t.string :organisation_address, null: false + t.string :organisation_phone_number + t.string :dpo_email, null: false + t.string :dpo_name, null: false + + t.timestamps + end + + add_index :data_sharing_agreements, + %i[organisation_id data_protection_officer_id], + unique: true, + name: "data_sharing_agreements_unique" + end +end diff --git a/db/schema.rb b/db/schema.rb index 6c33dd973..d01fa5935 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_05_15_114101) do +ActiveRecord::Schema[7.0].define(version: 2023_05_30_094653) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -56,6 +56,22 @@ ActiveRecord::Schema[7.0].define(version: 2023_05_15_114101) do t.index ["organisation_id"], name: "index_data_protection_confirmations_on_organisation_id" end + create_table "data_sharing_agreements", force: :cascade do |t| + t.bigint "organisation_id" + t.bigint "data_protection_officer_id" + t.datetime "signed_at", null: false + t.string "organisation_name", null: false + t.string "organisation_address", null: false + t.string "organisation_phone_number" + t.string "dpo_email", null: false + t.string "dpo_name", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["data_protection_officer_id"], name: "index_data_sharing_agreements_on_data_protection_officer_id" + t.index ["organisation_id", "data_protection_officer_id"], name: "data_sharing_agreements_unique", unique: true + t.index ["organisation_id"], name: "index_data_sharing_agreements_on_organisation_id" + end + create_table "la_rent_ranges", force: :cascade do |t| t.integer "ranges_rent_id" t.integer "lettype" diff --git a/spec/factories/data_sharing_agreement.rb b/spec/factories/data_sharing_agreement.rb new file mode 100644 index 000000000..11e9eef02 --- /dev/null +++ b/spec/factories/data_sharing_agreement.rb @@ -0,0 +1,15 @@ +FactoryBot.define do + factory :data_sharing_agreement do + organisation + data_protection_officer { create(:user, is_dpo: true) } + signed_at { Time.zone.now } + created_at { Time.zone.now } + updated_at { Time.zone.now } + + dpo_name { data_protection_officer.name } + dpo_email { data_protection_officer.email } + organisation_address { organisation.address_string } + organisation_phone_number { organisation.phone } + organisation_name { organisation.name } + end +end diff --git a/spec/requests/organisations_controller_spec.rb b/spec/requests/organisations_controller_spec.rb index 6013a5813..e8580d219 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,122 @@ 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 + + it "creates a data sharing agreement" do + expect(organisation.reload.data_sharing_agreement).to be_nil + + post("/organisations/#{organisation.id}/data-sharing-agreement", headers:) + + data_sharing_agreement = organisation.reload.data_sharing_agreement + + expect(data_sharing_agreement.organisation_address).to eq(organisation.address_row) + expect(data_sharing_agreement.organisation_name).to eq(organisation.name) + expect(data_sharing_agreement.organisation_phone_number).to eq(organisation.phone) + expect(data_sharing_agreement.data_protection_officer).to eq(user) + expect(data_sharing_agreement.dpo_name).to eq(user.name) + expect(data_sharing_agreement.dpo_email).to eq(user.email) + end + + context "when the user has already accepted the agreement" do + before do + create(:data_sharing_agreement, data_protection_officer: user, organisation: user.organisation) + 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 diff --git a/spec/views/organisations/data_sharing_agreement.html.erb_spec.rb b/spec/views/organisations/data_sharing_agreement.html.erb_spec.rb new file mode 100644 index 000000000..430d6f459 --- /dev/null +++ b/spec/views/organisations/data_sharing_agreement.html.erb_spec.rb @@ -0,0 +1,116 @@ +require "rails_helper" + +RSpec.describe "organisations/data_sharing_agreement.html.erb", :aggregate_failures do + before do + Timecop.freeze(Time.zone.local(2023, 1, 10)) + allow(view).to receive(:current_user).and_return(user) + assign(:organisation, organisation) + assign(:data_sharing_agreement, data_sharing_agreement) + end + + after do + Timecop.return + end + + let(:fragment) { Capybara::Node::Simple.new(rendered) } + let(:organisation) { user.organisation } + let(:data_sharing_agreement) { nil } + + context "when dpo" do + let(:user) { create(:user, is_dpo: true) } + + it "renders dynamic content" do + render + # current date + expect(fragment).to have_content("10th day of January 2023") + # dpo name + expect(fragment).to have_content("Name: #{user.name}") + # org details + expect(fragment).to have_content("#{organisation.name} of #{organisation.address_row} (“CORE Data Provider”)") + # header + expect(fragment).to have_css("h2", text: "#{organisation.name} and Department for Levelling Up, Housing and Communities") + # action buttons + expect(fragment).to have_button(text: "Accept this agreement") + expect(fragment).to have_link(text: "Cancel", href: "/organisations/#{organisation.id}/details") + # Shows DPO and org details in 12.2 + expect(fragment).to have_content("12.2. For #{organisation.name}: Name: #{user.name}, Postal Address: #{organisation.address_row}, E-mail address: #{user.email}, Telephone number: #{organisation.phone}") + end + + context "when accepted" do + let(:data_sharing_agreement) do + create( + :data_sharing_agreement, + organisation:, + signed_at: Time.zone.now - 1.day, + ) + end + + it "renders dynamic content" do + render + + # dpo name + expect(fragment).to have_content("Name: #{data_sharing_agreement.dpo_name}") + + # org details + expect(fragment).to have_content("#{data_sharing_agreement.organisation_name} of #{data_sharing_agreement.organisation_address} (“CORE Data Provider”)") + # header + expect(fragment).to have_css("h2", text: "#{data_sharing_agreement.organisation_name} and Department for Levelling Up, Housing and Communities") + # does not show action buttons + expect(fragment).not_to have_button(text: "Accept this agreement") + expect(fragment).not_to have_link(text: "Cancel", href: "/organisations/#{organisation.id}/details") + # sees signed_at date + expect(fragment).to have_content("9th day of January 2023") + # Shows DPO and org details in 12.2 + expect(fragment).to have_content("12.2. For #{data_sharing_agreement.organisation_name}: Name: #{data_sharing_agreement.dpo_name}, Postal Address: #{data_sharing_agreement.organisation_address}, E-mail address: #{data_sharing_agreement.dpo_email}, Telephone number: #{data_sharing_agreement.organisation_phone_number}") + end + end + end + + context "when not dpo" do + let(:user) { create(:user) } + + it "renders dynamic content" do + render + # placeholder date + expect(fragment).to have_content("This agreement is made the [XX] day of [XX] 20[XX]") + # dpo name placedholder + expect(fragment).to have_content("Name: [DPO name]") + # org details + expect(fragment).to have_content("#{organisation.name} of #{organisation.address_row} (“CORE Data Provider”)") + # header + expect(fragment).to have_css("h2", text: "#{organisation.name} and Department for Levelling Up, Housing and Communities") + # does not show action buttons + expect(fragment).not_to have_button(text: "Accept this agreement") + expect(fragment).not_to have_link(text: "Cancel", href: "/organisations/#{organisation.id}/details") + # Shows placeholder details in 12.2 + expect(fragment).to have_content("12.2. For #{organisation.name}: Name: [DPO name], Postal Address: #{organisation.address_row}, E-mail address: [DPO email], Telephone number: #{organisation.phone}") + end + + context "when accepted" do + let(:data_sharing_agreement) do + create( + :data_sharing_agreement, + organisation:, + signed_at: Time.zone.now - 1.day, + ) + end + + it "renders dynamic content" do + render + # sees signed_at date + expect(fragment).to have_content("9th day of January 2023") + # dpo name placedholder + expect(fragment).to have_content("Name: #{data_sharing_agreement.dpo_name}") + # org details + expect(fragment).to have_content("#{data_sharing_agreement.organisation_name} of #{data_sharing_agreement.organisation_address} (“CORE Data Provider”)") + # header + expect(fragment).to have_css("h2", text: "#{data_sharing_agreement.organisation_name} and Department for Levelling Up, Housing and Communities") + # does not show action buttons + expect(fragment).not_to have_button(text: "Accept this agreement") + expect(fragment).not_to have_link(text: "Cancel", href: "/organisations/#{organisation.id}/details") + # Shows filled in details in 12.2 + expect(fragment).to have_content("12.2. For #{data_sharing_agreement.organisation_name}: Name: #{data_sharing_agreement.dpo_name}, Postal Address: #{data_sharing_agreement.organisation_address}, E-mail address: #{data_sharing_agreement.dpo_email}, Telephone number: #{data_sharing_agreement.organisation_phone_number}") + end + end + end +end diff --git a/spec/views/organisations/show.html.erb_spec.rb b/spec/views/organisations/show.html.erb_spec.rb new file mode 100644 index 000000000..ea392d76d --- /dev/null +++ b/spec/views/organisations/show.html.erb_spec.rb @@ -0,0 +1,179 @@ +require "rails_helper" + +RSpec.describe "organisations/show.html.erb" do + before do + Timecop.freeze(Time.zone.local(2023, 1, 10)) + allow(view).to receive(:current_user).and_return(user) + assign(:organisation, organisation) + organisation.update!(data_sharing_agreement:) + end + + after do + Timecop.return + end + + let(:fragment) { Capybara::Node::Simple.new(rendered) } + let(:organisation) { user.organisation } + let(:data_sharing_agreement) { nil } + + context "when flag disabled" do + let(:user) { create(:user) } + + before do + allow(FeatureToggle).to receive(:new_data_sharing_agreement?).and_return(false) + end + + it "does not include data sharing agreement row" do + render + + expect(fragment).not_to have_content("Data Sharing Agreement") + end + end + + context "when dpo" do + let(:user) { create(:user, is_dpo: true) } + + it "includes data sharing agreement row" do + render + + expect(fragment).to have_content("Data Sharing Agreement") + end + + it "shows data sharing agreement not accepted" do + render + + expect(fragment).to have_content("Not accepted") + end + + it "shows link to view data sharing agreement" do + render + + expect(fragment).to have_link(text: "View agreement", href: "/organisations/#{organisation.id}/data-sharing-agreement") + end + + context "when accepted" do + let(:data_sharing_agreement) { create(:data_sharing_agreement, organisation:, signed_at: Time.zone.now - 1.day) } + + it "includes data sharing agreement row" do + render + + expect(fragment).to have_content("Data Sharing Agreement") + end + + it "shows data sharing agreement accepted" do + render + + expect(fragment).to have_content("Accepted") + end + + it "shows link to view data sharing agreement" do + render + + expect(fragment).to have_link(text: "View agreement", href: "/organisations/#{organisation.id}/data-sharing-agreement") + end + end + end + + context "when support user" do + let(:user) { create(:user, :support) } + + it "includes data sharing agreement row" do + render + + expect(fragment).to have_content("Data Sharing Agreement") + end + + it "shows data sharing agreement not accepted" do + render + + expect(fragment).to have_content("Not accepted") + end + + it "tells DPO must sign" do + render + + expect(fragment).to have_content("Data protection officer must sign") + end + + it "shows link to view data sharing agreement" do + render + + expect(fragment).to have_link(text: "View agreement", href: "/organisations/#{organisation.id}/data-sharing-agreement") + end + + context "when accepted" do + let(:data_sharing_agreement) { create(:data_sharing_agreement, organisation:, signed_at: Time.zone.now - 1.day) } + + it "includes data sharing agreement row" do + render + + expect(fragment).to have_content("Data Sharing Agreement") + end + + it "shows data sharing agreement accepted with date" do + render + + expect(fragment).to have_content("Accepted 09/01/2023") + end + + it "shows show name of who signed the agreement" do + render + + expect(fragment).to have_content(data_sharing_agreement.dpo_name) + end + + it "shows link to view data sharing agreement" do + render + + expect(fragment).to have_link(text: "View agreement", href: "/organisations/#{organisation.id}/data-sharing-agreement") + end + end + end + + context "when not dpo" do + let(:user) { create(:user) } + + it "includes data sharing agreement row" do + render + + expect(fragment).to have_content("Data Sharing Agreement") + end + + it "shows data sharing agreement not accepted" do + render + expect(fragment).to have_content("Not accepted") + end + + it "tells DPO must sign" do + render + expect(fragment).to have_content("Data protection officer must sign") + end + + it "shows link to view data sharing agreement" do + render + expect(fragment).to have_link(text: "View agreement", href: "/organisations/#{organisation.id}/data-sharing-agreement") + end + + context "when accepted" do + let(:data_sharing_agreement) do + create(:data_sharing_agreement, organisation:, signed_at: Time.zone.now - 1.day) + end + + it "includes data sharing agreement row" do + render + + expect(fragment).to have_content("Data Sharing Agreement") + end + + it "shows data sharing agreement accepted" do + render + expect(fragment).to have_content("Accepted") + end + + it "shows link to view data sharing agreement" do + render + expect(fragment).to have_link(text: "View agreement", href: "/organisations/#{organisation.id}/data-sharing-agreement") + end + end + end +end