Browse Source

CLDC-2322 read and sign data sharing agreement (#1686)

* Add data sharing agreement

* Add data sharing agreement view

* Add controller actions

* Update details page

* Rubocop fix

* Fill in placeholders and persist data in table

* Update agreement template
pull/1688/head
Jack 2 years ago committed by GitHub
parent
commit
a3b945d8e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 32
      app/controllers/organisations_controller.rb
  2. 87
      app/helpers/data_sharing_agreement_helper.rb
  3. 4
      app/models/data_sharing_agreement.rb
  4. 5
      app/models/organisation.rb
  5. 4
      app/services/feature_toggle.rb
  6. 3
      app/views/layouts/application.html.erb
  7. 144
      app/views/organisations/data_sharing_agreement.html.erb
  8. 8
      app/views/organisations/show.html.erb
  9. 3
      config/routes.rb
  10. 22
      db/migrate/20230530094653_add_data_sharing_agreement.rb
  11. 18
      db/schema.rb
  12. 15
      spec/factories/data_sharing_agreement.rb
  13. 224
      spec/requests/organisations_controller_spec.rb
  14. 116
      spec/views/organisations/data_sharing_agreement.html.erb_spec.rb
  15. 179
      spec/views/organisations/show.html.erb_spec.rb

32
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

87
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

4
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

5
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

4
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

3
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 %>

144
app/views/organisations/data_sharing_agreement.html.erb

@ -0,0 +1,144 @@
<%= content_for :title, "Data sharing agreement" %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<h2 id="data-provider-organisation-and-department-for-levelling-up-housing-and-communities" class="govuk-heading-l">
<%= org_name_for_data_sharing_agreement(@data_sharing_agreement, current_user) %> and Department for Levelling Up, Housing and Communities
</h2>
<p class="govuk-body-m">
<% 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 %>
</p>
<p class="govuk-body-m"><strong>between</strong></p>
<% if @data_sharing_agreement %>
<p class="govuk-body-m">1) <%= @data_sharing_agreement.organisation_name %> of <%= @data_sharing_agreement.organisation_address %> (“CORE Data Provider”)</p>
<% else %>
<p class="govuk-body-m">1) <%= @organisation.name %> of <%= @organisation.address_row %> (“CORE Data Provider”)</p>
<% end %>
<p class="govuk-body-m">and</p>
<p class="govuk-body-m">2) The Department for Levelling Up, Housing and Communities of 2 Marsham Street, London, SW1P 4DF (“DLUHC”)</p>
<h3 id="1-background" class="govuk-heading-m">1. Background</h3>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">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).</p>
<p class="govuk-body-m">1.4. DLUHC shall only collect data from and share data with CORE data providers that have signed this data sharing agreement.</p>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m"><strong>It is now agreed</strong> as follows:</p>
<h3 id="2-definitions-and-interpretation" class="govuk-heading-m">2. Definitions and interpretation</h3>
<p class="govuk-body-m">2.1. In this Agreement the following words and phrases shall have the following meanings, unless expressly stated to the contrary:</p>
<ul class="govuk-list govuk-list--bullet">
<li>“Act” means the Data Protection Act 2018;</li>
<li>“Authorised Representatives” means the nominated lead officer representing each of the
parties with delegated authority to handle the day-to-day matters arising from this Agreement;</li>
<li>“Data Subject” means social housing lettings tenants and participants in discounted sales where their data is reported via the CORE system.</li>
<li>“Data Controller” has the meaning in Article 4(7) of the GDPR and section 5(2) of the Act.</li>
<li>“Data Processor” has the meaning in Article 4(8) of the GDPR.</li>
<li>“Data Protection Legislation” means the Data Protection Act 2018 and all applicable laws and regulations relating to the processing of personal data and privacy, including where applicable the guidance and codes of practice issued by the Information Commissioner; it includes the General Data Protection Regulation (GDPR).</li>
<li>“Data” means the data supplied by the CORE data providers via the CORE system and the data that is calculated or derived via the CORE system based on that initial data;</li>
<li>“GDPR” means the General Data Protection Regulation.</li>
<li>“Parties” means the parties to this Agreement, namely DLUHC and the CORE data providers. CORE data providers include social housing providers and managing organisations that provide data on behalf of the social housing providers.</li>
<li>“Personal Data” has the meaning in Article 4(1) of the GDPR. “Processing” has the meaning in Article 4(2) of the GDPR.</li>
<li>“Request for Information” means a request for information or a request under the Freedom of Information Act 2000.</li>
<li>“Special category personal data” has the meaning in Article 9(1) of the GDPR. In this Agreement:
<ul class="govuk-list govuk-list--bullet">
<li>A. The masculine includes the feminine and neuter;</li>
<li>B. Person means a natural person;</li>
<li>C. The singular includes the plural and vice versa;</li>
<li>D. A reference to any statute, enactment, order, regulation or other similar instrument
shall be construed as a reference to the statute, enactment, order, regulation or instrument as amended by any subsequent statute, enactment, order, regulation or instrument or as contained in any subsequent re-enactment.</li>
</ul></li>
</ul>
<p class="govuk-body-m">2.2. Headings are included in this Agreement for ease of reference only and shall not affect the interpretation or construction of this Agreement.</p>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">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.</p>
<h3 id="3-commencement-and-term" class="govuk-heading-m">3. Commencement and term</h3>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">3.2. The Parties may, by mutual consent in writing, agree to amend this agreement.</p>
<h3 id="4-purpose-and-scope-of-data-collection-via-core" class="govuk-heading-m">4. Purpose and scope of data collection via CORE</h3>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">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).</p>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">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.</p>
<h3 id="5-roles-and-responsibilities" class="govuk-heading-m">5. Roles and responsibilities</h3>
<p class="govuk-body-m">5.1. DLUHC shall be the ‘Data controller’ for all personal data held within the CORE database.</p>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">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.</p>
<h3 id="6-legal-basis-for-data-sharing" class="govuk-heading-m">6. Legal basis for data sharing</h3>
<p class="govuk-body-m">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:</p>
<blockquote>
<p class="govuk-body-m">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.</p>
</blockquote>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">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.</p>
<h3 id="7-use-of-data" class="govuk-heading-m">7. Use of data</h3>
<p class="govuk-body-m">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.</p>
<h3 id="8-security-of-data-transfer" class="govuk-heading-m">8. SECURITY OF DATA TRANSFER</h3>
<p class="govuk-body-m">8.1. The security of the CORE data collection system is compliant with Government security standards (https://core.communities.gov.uk/public/index.html).</p>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">8.5. All work carried out by DLUHC will follow appropriate security measures and procedures to ensure the protection of the data.</p>
<h3 id="9-protection-of-personal-data" class="govuk-heading-m">9. Protection of personal data</h3>
<p class="govuk-body-m">9.1. CORE data providers and DLUHC agree that they shall:</p>
<ul class="govuk-list govuk-list--bullet">
<li>A. Implement appropriate technical and organisational measures to protect the Personal Data against unauthorised or unlawful Processing and against accidental loss, destruction, damage, alteration or disclosure. These measures shall ensure a level of security appropriate to the harm which might result from any unauthorised or unlawful Processing, accidental loss, destruction or damage to the Personal Data and having regard to the nature of the Personal Data which is to be protected;</li>
<li>B. Take reasonable steps to ensure the reliability of any personnel who have access to the Personal Data. DLUHC and Data providers will ensure such personnel will be a limited number of analysts assigned to the data collection.</li>
</ul>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">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.</p>
<h3 id="10-freedom-of-information" class="govuk-heading-m">10. Freedom of information</h3>
<p class="govuk-body-m">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.</p>
<h3 id="11-loss-or-unauthorised-release" class="govuk-heading-m">11. Loss or unauthorised release</h3>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">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.</p>
<h3 id="12-authorised-representatives" class="govuk-heading-m">12. Authorised representatives</h3>
<p class="govuk-body-m">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:</p>
<p class="govuk-body-m">
<%= section_12_2(data_sharing_agreement: @data_sharing_agreement, user: current_user, organisation: @organisation) %>
</p>
<p class="govuk-body-m">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 </p>
<h3 id="13-products-and-publications" class="govuk-heading-m">13. Products and publications</h3>
<p class="govuk-body-m">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.</p>
<h3 id="14-dispute-resolution" class="govuk-heading-m">14. Dispute resolution</h3>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">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.</p>
<h3 id="15-termination" class="govuk-heading-m">15. Termination</h3>
<p class="govuk-body-m">15.1. Any Party may terminate this Agreement upon one month’s written notice to the other. </p>
<p class="govuk-body-m">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.</p>
<h3 id="16-statutory-compliance" class="govuk-heading-m">16. Statutory compliance</h3>
<p class="govuk-body-m">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.</p>
<p class="govuk-body-m">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:</p>
<ul class="govuk-list govuk-list--bullet">
<li>Name: <%= name_for_data_sharing_agreement(@data_sharing_agreement, current_user) %></li>
<li>Title: Data Protection Officer</li>
</ul>
<p class="govuk-body-m">SIGNED for and on behalf of the deputy director of the data, analytics &amp; statistics in the Department for Levelling Up, Housing and Communities, by:</p>
<ul class="govuk-list govuk-list--bullet">
<li>Name: Sandra Tudor</li>
<li>Title: Deputy Director</li>
</ul>
<% if current_user.is_dpo? && @organisation.data_sharing_agreement.nil? %>
<div class="govuk-button-group">
<%= govuk_button_to("Accept this agreement", data_sharing_agreement_organisation_path(@organisation), method: :post) %>
<%= govuk_button_link_to("Cancel", details_organisation_path(@organisation), secondary: true) %>
</div>
<% end %>
</div>
</div>

8
app/views/organisations/show.html.erb

@ -21,7 +21,7 @@
<% row.value { details_html(attr) } %>
<% row.action(
visually_hidden_text: attr[:name].to_s.humanize.downcase,
href: edit_organisation_path,
href: edit_organisation_path(@organisation),
html_attributes: { "data-qa": "change-#{attr[:name].downcase}" },
) %>
<% end %>
@ -32,11 +32,13 @@
<% row.action %>
<% end %>
<% end %>
<% end %>
<% if FeatureToggle.new_data_sharing_agreement? %>
<%= data_sharing_agreement_row(organisation: @organisation, user: current_user, summary_list:) %>
<% end %>
<% end %>
<% if FeatureToggle.merge_organisations_enabled? %>
<p>Is your organisation merging with another? <%= govuk_link_to "Let us know using this form", merge_request_organisation_path %></p>
<p>Is your organisation merging with another? <%= govuk_link_to "Let us know using this form", merge_request_organisation_path(@organisation) %></p>
<% end %>
</div>

3
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"

22
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

18
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"

15
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

224
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

116
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

179
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
Loading…
Cancel
Save