From 5b98a2190709cf7c0bd3766184e50b0352b9dbe9 Mon Sep 17 00:00:00 2001
From: Arthur Campbell <51094020+arfacamble@users.noreply.github.com>
Date: Thu, 15 Jun 2023 15:01:24 +0100
Subject: [PATCH 01/19] minor change in the reset confirmation method to avoid
errors if email in params is nil (#1676)
---
app/controllers/auth/passwords_controller.rb | 2 +-
spec/requests/auth/passwords_controller_spec.rb | 8 ++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/app/controllers/auth/passwords_controller.rb b/app/controllers/auth/passwords_controller.rb
index c76ebaf6a..a990325f9 100644
--- a/app/controllers/auth/passwords_controller.rb
+++ b/app/controllers/auth/passwords_controller.rb
@@ -4,7 +4,7 @@ class Auth::PasswordsController < Devise::PasswordsController
def reset_confirmation
self.resource = resource_class.new
@email = params["email"]
- if @email.empty?
+ if @email.blank?
resource.errors.add :email, I18n.t("validations.email.blank")
render "devise/passwords/new", status: :unprocessable_entity
elsif !email_valid?(@email)
diff --git a/spec/requests/auth/passwords_controller_spec.rb b/spec/requests/auth/passwords_controller_spec.rb
index 70357062e..29a032395 100644
--- a/spec/requests/auth/passwords_controller_spec.rb
+++ b/spec/requests/auth/passwords_controller_spec.rb
@@ -145,4 +145,12 @@ RSpec.describe Auth::PasswordsController, type: :request do
end
end
end
+
+ context "when a password is reset" do
+ let(:email) { nil }
+
+ it "does not error if the email is nil or not in the params" do
+ expect { get account_password_reset_confirmation_path(email:) }.not_to raise_error
+ end
+ end
end
From cec1208b35e7aa4e9f3dfbcfdce072cff2444f19 Mon Sep 17 00:00:00 2001
From: Arthur Campbell <51094020+arfacamble@users.noreply.github.com>
Date: Thu, 15 Jun 2023 15:02:06 +0100
Subject: [PATCH 02/19] CLDC-2290 implement delete multiple logs story (#1657)
* add a button to the logs list to delete multiple logs
style and position of button
helpers for displaying the button conditionally depending on user role and what filters and search are active
* correct indentation from 4 spaces to 2 in view file
* test appearance of delete logs button on index page for lettings logs
* write a happy path feature test for the entire journey
* create basic tests for the view component for listing logs to delete
* create request tests for the GET delete-logs path
* create request tests for the GET delete-logs-confirmation path
* create request tests for the DELETE delete-logs path
* comprehensive reworking after code review
ensure that we are not passing lists of ids through params in the query string, risking overflowing the maximum URL length,
adjust tests accordingly,
do not attempt to reuse the same table for sales and lettings
* alter config to allow creating controllers from the command line with associated spec files that matches how we test
* extract controller methods and associated tests to do with the delete logs feature into their own controller,
amend routes accordingly
* implement same work for sales as for lettings
* implement the story for lettings and sales logs under the organisation tab
routing and controller methods
testing for deleting sales logs, lettings or sales logs for an organisation
move storage of relevant routes inside the form object as a comprehensive view model
* merge the delete pages for lettings logs and sales logs, add to the tests for the lettings page to test sales specific content
* minor refactor to delete logs controller: ensure session filters are only fetched from teh session when needed and extract discard logs method to private method
* extract tables for lettings and sales to own partials
* refactor delete logs controller after tech review
improve the private method that builds the form object so that it has the flexibility to do so for all controller methods
ensure that the search term is passed to the delete logs controller when navigating through the organisations tab
ensure that noly logs for that organisation are displayed when navigating to delete logs through the organisations tab
* remove unnecessary untested arguments
* test new helper methods
* implement dirty fiddle to get the checkboxes smaller and also not misaligned
* ensure delete logs button is always visible on log lists when in the organisations tab
* minor linting corrections
* revert change, causing errors and outside the scope of this ticket
* simplify tests for whether delete logs button appears on index page
* replicate request specs from lettings for sales and organisations controllers
* minor refactor of lettings log feature spec setup, replicate happy path for sales
* minor refactors after rebasing onto Nat's work
* temp
* write tests for the delete logs form object
* lint: add new line at end of file
* respond to PO feedback
the log id in the delte logs table should be a link to the log
the delete logs button should be visible when the user is in a bulk upload journey
updated associated tests
---
app/controllers/delete_logs_controller.rb | 196 ++++
app/controllers/lettings_logs_controller.rb | 2 +-
app/controllers/organisations_controller.rb | 4 +-
app/controllers/sales_logs_controller.rb | 1 +
app/frontend/styles/_delete-logs-table.scss | 5 +
app/frontend/styles/application.scss | 3 +-
app/helpers/filters_helper.rb | 12 +
app/helpers/log_list_helper.rb | 13 +
app/models/forms/delete_logs_form.rb | 44 +
.../logs/_delete_logs_table_lettings.html.erb | 30 +
.../logs/_delete_logs_table_sales.html.erb | 30 +
app/views/logs/_log_list.html.erb | 12 +-
app/views/logs/delete_logs.html.erb | 22 +
.../logs/delete_logs_confirmation.html.erb | 25 +
app/views/logs/index.html.erb | 6 +-
app/views/logs/update_logs.html.erb | 65 +-
app/views/organisations/logs.html.erb | 2 +
config/application.rb | 9 +
config/locales/en.yml | 5 +
config/routes.rb | 18 +
spec/features/lettings_log_spec.rb | 40 +
spec/features/sales_log_spec.rb | 73 +-
spec/helpers/filters_helper_spec.rb | 78 ++
spec/helpers/log_list_helper_spec.rb | 72 ++
spec/models/forms/delete_logs_form_spec.rb | 89 ++
spec/requests/delete_logs_controller_spec.rb | 946 ++++++++++++++++++
.../requests/lettings_logs_controller_spec.rb | 40 +-
.../requests/organisations_controller_spec.rb | 344 ++++---
spec/requests/sales_logs_controller_spec.rb | 32 +
spec/views/form/page_view_spec.rb | 4 +-
spec/views/logs/delete_logs_spec.rb | 121 +++
31 files changed, 2115 insertions(+), 228 deletions(-)
create mode 100644 app/controllers/delete_logs_controller.rb
create mode 100644 app/frontend/styles/_delete-logs-table.scss
create mode 100644 app/helpers/log_list_helper.rb
create mode 100644 app/models/forms/delete_logs_form.rb
create mode 100644 app/views/logs/_delete_logs_table_lettings.html.erb
create mode 100644 app/views/logs/_delete_logs_table_sales.html.erb
create mode 100644 app/views/logs/delete_logs.html.erb
create mode 100644 app/views/logs/delete_logs_confirmation.html.erb
create mode 100644 spec/helpers/log_list_helper_spec.rb
create mode 100644 spec/models/forms/delete_logs_form_spec.rb
create mode 100644 spec/requests/delete_logs_controller_spec.rb
create mode 100644 spec/views/logs/delete_logs_spec.rb
diff --git a/app/controllers/delete_logs_controller.rb b/app/controllers/delete_logs_controller.rb
new file mode 100644
index 000000000..0b4dc3ba5
--- /dev/null
+++ b/app/controllers/delete_logs_controller.rb
@@ -0,0 +1,196 @@
+class DeleteLogsController < ApplicationController
+ rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
+
+ before_action :session_filters, if: :current_user, except: %i[discard_lettings_logs discard_sales_logs discard_lettings_logs_for_organisation discard_sales_logs_for_organisation]
+ before_action :add_organisation_to_filters, only: %i[delete_lettings_logs_for_organisation delete_lettings_logs_for_organisation_with_selected_ids delete_lettings_logs_for_organisation_confirmation delete_sales_logs_for_organisation delete_sales_logs_for_organisation_with_selected_ids delete_sales_logs_for_organisation_confirmation]
+
+ def delete_lettings_logs
+ @delete_logs_form = delete_logs_form(log_type: :lettings)
+ render "logs/delete_logs"
+ end
+
+ def delete_lettings_logs_with_selected_ids
+ @delete_logs_form = delete_logs_form(log_type: :lettings, selected_ids:)
+ render "logs/delete_logs"
+ end
+
+ def delete_lettings_logs_confirmation
+ @delete_logs_form = delete_logs_form(log_type: :lettings, form_params:)
+ if @delete_logs_form.valid?
+ render "logs/delete_logs_confirmation"
+ else
+ render "logs/delete_logs"
+ end
+ end
+
+ def discard_lettings_logs
+ logs = LettingsLog.find(params.require(:ids))
+ discard logs
+
+ redirect_to lettings_logs_path, notice: I18n.t("notification.logs_deleted", count: logs.count)
+ end
+
+ def delete_sales_logs
+ @delete_logs_form = delete_logs_form(log_type: :sales)
+ render "logs/delete_logs"
+ end
+
+ def delete_sales_logs_with_selected_ids
+ @delete_logs_form = delete_logs_form(log_type: :sales, selected_ids:)
+ render "logs/delete_logs"
+ end
+
+ def delete_sales_logs_confirmation
+ @delete_logs_form = delete_logs_form(log_type: :sales, form_params:)
+ if @delete_logs_form.valid?
+ render "logs/delete_logs_confirmation"
+ else
+ render "logs/delete_logs"
+ end
+ end
+
+ def discard_sales_logs
+ logs = SalesLog.find(params.require(:ids))
+ discard logs
+
+ redirect_to sales_logs_path, notice: I18n.t("notification.logs_deleted", count: logs.count)
+ end
+
+ def delete_lettings_logs_for_organisation
+ @delete_logs_form = delete_logs_form(log_type: :lettings, organisation: true)
+ render "logs/delete_logs"
+ end
+
+ def delete_lettings_logs_for_organisation_with_selected_ids
+ @delete_logs_form = delete_logs_form(log_type: :lettings, organisation: true, selected_ids:)
+ render "logs/delete_logs"
+ end
+
+ def delete_lettings_logs_for_organisation_confirmation
+ @delete_logs_form = delete_logs_form(log_type: :lettings, organisation: true, form_params:)
+ if @delete_logs_form.valid?
+ render "logs/delete_logs_confirmation"
+ else
+ render "logs/delete_logs"
+ end
+ end
+
+ def discard_lettings_logs_for_organisation
+ logs = LettingsLog.where(owning_organisation: params[:id]).find(params.require(:ids))
+ discard logs
+
+ redirect_to lettings_logs_organisation_path, notice: I18n.t("notification.logs_deleted", count: logs.count)
+ end
+
+ def delete_sales_logs_for_organisation
+ @delete_logs_form = delete_logs_form(log_type: :sales, organisation: true)
+ render "logs/delete_logs"
+ end
+
+ def delete_sales_logs_for_organisation_with_selected_ids
+ @delete_logs_form = delete_logs_form(log_type: :sales, organisation: true, selected_ids:)
+ render "logs/delete_logs"
+ end
+
+ def delete_sales_logs_for_organisation_confirmation
+ @delete_logs_form = delete_logs_form(log_type: :sales, organisation: true, form_params:)
+ if @delete_logs_form.valid?
+ render "logs/delete_logs_confirmation"
+ else
+ render "logs/delete_logs"
+ end
+ end
+
+ def discard_sales_logs_for_organisation
+ logs = SalesLog.where(owning_organisation: params[:id]).find(params.require(:ids))
+ discard logs
+
+ redirect_to sales_logs_organisation_path, notice: I18n.t("notification.logs_deleted", count: logs.count)
+ end
+
+private
+
+ def session_filters
+ @session_filters ||= filter_manager.session_filters
+ end
+
+ def filter_manager
+ log_type = action_name.include?("lettings") ? "lettings_logs" : "sales_logs"
+ FilterManager.new(current_user:, session:, params:, filter_type: log_type)
+ end
+
+ def delete_logs_form(log_type:, organisation: false, selected_ids: nil, form_params: {})
+ paths = case log_type
+ when :lettings
+ organisation ? lettings_logs_for_organisation_paths : lettings_logs_paths
+ when :sales
+ organisation ? sales_logs_for_organisation_paths : sales_logs_paths
+ end
+ attributes = {
+ log_type:,
+ current_user:,
+ log_filters: @session_filters,
+ search_term:,
+ selected_ids:,
+ **paths,
+ }.merge(form_params).transform_keys(&:to_sym)
+ Forms::DeleteLogsForm.new(attributes)
+ end
+
+ def form_params
+ form_attributes = params.require(:forms_delete_logs_form).permit(:search_term, selected_ids: [])
+ form_attributes[:selected_ids] = [] unless form_attributes.key? :selected_ids
+ form_attributes
+ end
+
+ def lettings_logs_paths
+ {
+ delete_confirmation_path: delete_logs_confirmation_lettings_logs_path,
+ back_to_logs_path: lettings_logs_path(search: search_term),
+ delete_path: delete_logs_lettings_logs_path,
+ }
+ end
+
+ def sales_logs_paths
+ {
+ delete_confirmation_path: delete_logs_confirmation_sales_logs_path,
+ back_to_logs_path: sales_logs_path(search: search_term),
+ delete_path: delete_logs_sales_logs_path,
+ }
+ end
+
+ def lettings_logs_for_organisation_paths
+ {
+ delete_confirmation_path: delete_lettings_logs_confirmation_organisation_path,
+ back_to_logs_path: lettings_logs_organisation_path(search: search_term),
+ delete_path: delete_lettings_logs_organisation_path,
+ }
+ end
+
+ def sales_logs_for_organisation_paths
+ {
+ delete_confirmation_path: delete_sales_logs_confirmation_organisation_path,
+ back_to_logs_path: sales_logs_organisation_path(search: search_term),
+ delete_path: delete_sales_logs_organisation_path,
+ }
+ end
+
+ def add_organisation_to_filters
+ @session_filters[:organisation] = params[:id]
+ end
+
+ def search_term
+ params["search"]
+ end
+
+ def selected_ids
+ params.require(:selected_ids).split.map(&:to_i)
+ end
+
+ def discard(logs)
+ logs.each do |log|
+ authorize log, :destroy?
+ log.discard!
+ end
+ end
+end
diff --git a/app/controllers/lettings_logs_controller.rb b/app/controllers/lettings_logs_controller.rb
index eb1caa47c..f67e94acc 100644
--- a/app/controllers/lettings_logs_controller.rb
+++ b/app/controllers/lettings_logs_controller.rb
@@ -16,7 +16,7 @@ class LettingsLogsController < LogsController
all_logs = current_user.lettings_logs.visible
unpaginated_filtered_logs = filter_manager.filtered_logs(all_logs, search_term, session_filters)
- @search_term = search_term
+ @delete_logs_path = delete_logs_lettings_logs_path(search: search_term)
@pagy, @logs = pagy(unpaginated_filtered_logs)
@searched = search_term.presence
@total_count = all_logs.size
diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb
index 7fdcd1d94..98082bea9 100644
--- a/app/controllers/organisations_controller.rb
+++ b/app/controllers/organisations_controller.rb
@@ -96,6 +96,7 @@ class OrganisationsController < ApplicationController
format.html do
@search_term = search_term
@pagy, @logs = pagy(unpaginated_filtered_logs)
+ @delete_logs_path = delete_lettings_logs_organisation_path(search: @search_term)
@searched = search_term.presence
@total_count = organisation_logs.size
@log_type = :lettings
@@ -119,13 +120,14 @@ class OrganisationsController < ApplicationController
end
def sales_logs
- organisation_logs = SalesLog.where(owning_organisation_id: @organisation.id)
+ organisation_logs = SalesLog.visible.where(owning_organisation_id: @organisation.id)
unpaginated_filtered_logs = filter_manager.filtered_logs(organisation_logs, search_term, session_filters)
respond_to do |format|
format.html do
@search_term = search_term
@pagy, @logs = pagy(unpaginated_filtered_logs)
+ @delete_logs_path = delete_sales_logs_organisation_path(search: @search_term)
@searched = search_term.presence
@total_count = organisation_logs.size
@log_type = :sales
diff --git a/app/controllers/sales_logs_controller.rb b/app/controllers/sales_logs_controller.rb
index 27f3d4f05..4a06fa6c4 100644
--- a/app/controllers/sales_logs_controller.rb
+++ b/app/controllers/sales_logs_controller.rb
@@ -18,6 +18,7 @@ class SalesLogsController < LogsController
all_logs = current_user.sales_logs.visible
unpaginated_filtered_logs = filter_manager.filtered_logs(all_logs, search_term, session_filters)
+ @delete_logs_path = delete_logs_sales_logs_path(search: search_term)
@search_term = search_term
@pagy, @logs = pagy(unpaginated_filtered_logs)
@searched = search_term.presence
diff --git a/app/frontend/styles/_delete-logs-table.scss b/app/frontend/styles/_delete-logs-table.scss
new file mode 100644
index 000000000..00bb3ed28
--- /dev/null
+++ b/app/frontend/styles/_delete-logs-table.scss
@@ -0,0 +1,5 @@
+.checkbox-cell {
+ .govuk-checkboxes__item {
+ margin-top: -7px;
+ }
+}
diff --git a/app/frontend/styles/application.scss b/app/frontend/styles/application.scss
index 03a6ab6f2..ddf368807 100644
--- a/app/frontend/styles/application.scss
+++ b/app/frontend/styles/application.scss
@@ -44,6 +44,7 @@ $govuk-breakpoints: (
@import "search";
@import "sub-navigation";
@import "errors";
+@import "delete-logs-table";
// App utilities
.app-\!-colour-muted {
@@ -52,7 +53,7 @@ $govuk-breakpoints: (
}
.app-\!-colour-red {
- color: govuk-colour("red");
+ color: govuk-colour("red") !important;
}
.app-\!-font-tabular {
diff --git a/app/helpers/filters_helper.rb b/app/helpers/filters_helper.rb
index 51472b299..34e768603 100644
--- a/app/helpers/filters_helper.rb
+++ b/app/helpers/filters_helper.rb
@@ -11,6 +11,18 @@ module FiltersHelper
selected_filters[filter].include?(value.to_s)
end
+ def any_filter_selected?(filter_type)
+ filters_json = session[session_name_for(filter_type)]
+ return false unless filters_json
+
+ filters = JSON.parse(filters_json)
+ filters["user"] == "yours" ||
+ filters["organisation"].present? ||
+ filters["status"]&.compact_blank&.any? ||
+ filters["years"]&.compact_blank&.any? ||
+ filters["bulk_upload_id"].present?
+ end
+
def status_filters
{
"not_started" => "Not started",
diff --git a/app/helpers/log_list_helper.rb b/app/helpers/log_list_helper.rb
new file mode 100644
index 000000000..b3e967358
--- /dev/null
+++ b/app/helpers/log_list_helper.rb
@@ -0,0 +1,13 @@
+module LogListHelper
+ def display_delete_logs?(current_user, search_term, filter_type)
+ if current_user.data_provider?
+ filter_selected?("user", "yours", filter_type)
+ else
+ any_filter_selected?(filter_type) || search_term.present?
+ end
+ end
+
+ def in_organisations_tab?
+ controller.class.name.start_with? "Organisation"
+ end
+end
diff --git a/app/models/forms/delete_logs_form.rb b/app/models/forms/delete_logs_form.rb
new file mode 100644
index 000000000..6c4d8eba6
--- /dev/null
+++ b/app/models/forms/delete_logs_form.rb
@@ -0,0 +1,44 @@
+module Forms
+ class DeleteLogsForm
+ include ActiveModel::Model
+ include ActiveModel::Validations
+
+ attr_reader :logs, :log_type, :selected_ids, :search_term, :delete_confirmation_path, :back_to_logs_path, :delete_path
+
+ validate :at_least_one_log_selected
+
+ def initialize(attributes)
+ @log_type = attributes[:log_type]
+ @search_term = attributes[:search_term]
+ @current_user = attributes[:current_user]
+ @logs = FilterManager.filter_logs(visible_logs, @search_term, attributes[:log_filters], nil, @current_user)
+ @selected_ids = attributes[:selected_ids] || @logs.map(&:id)
+ @delete_confirmation_path = attributes[:delete_confirmation_path]
+ @back_to_logs_path = attributes[:back_to_logs_path]
+ @delete_path = attributes[:delete_path]
+ end
+
+ def log_count
+ @logs.count
+ end
+
+ def table_partial_name
+ "logs/delete_logs_table_#{@log_type}"
+ end
+
+ private
+
+ def at_least_one_log_selected
+ if selected_ids.blank? || selected_ids.reject(&:blank?).blank?
+ errors.add(:log_ids, "Select at least one log to delete or press cancel to return")
+ end
+ end
+
+ def visible_logs
+ case @log_type
+ when :lettings then @current_user.lettings_logs.visible
+ when :sales then @current_user.sales_logs.visible
+ end
+ end
+ end
+end
diff --git a/app/views/logs/_delete_logs_table_lettings.html.erb b/app/views/logs/_delete_logs_table_lettings.html.erb
new file mode 100644
index 000000000..bd5952170
--- /dev/null
+++ b/app/views/logs/_delete_logs_table_lettings.html.erb
@@ -0,0 +1,30 @@
+<%= govuk_table do |table| %>
+ <% table.head do |head| %>
+ <% head.row do |row| %>
+ <% row.cell header: true, text: "Log ID" %>
+ <% row.cell header: true, text: "Tenancy code" %>
+ <% row.cell header: true, text: "Property reference" %>
+ <% row.cell header: true, text: "Status" %>
+ <% row.cell header: true, text: "Delete?" %>
+ <% end %>
+ <% end %>
+ <% table.body do |body| %>
+ <% f.govuk_check_boxes_fieldset :selected_ids, small: true do %>
+ <% delete_logs_form.logs.each do |log| %>
+ <% body.row do |row| %>
+ <% row.cell do %>
+ <%= govuk_link_to log.id, url_for(log) %>
+ <% end %>
+ <% row.cell text: log.tenancycode %>
+ <% row.cell text: log.propcode %>
+ <% row.cell text: status_tag(log.status) %>
+ <% row.cell html_attributes: { class: "checkbox-cell" } do %>
+ <% f.govuk_check_box :selected_ids, log.id,
+ label: { text: log.id, hidden: true },
+ checked: delete_logs_form.selected_ids.include?(log.id) %>
+ <% end %>
+ <% end %>
+ <% end %>
+ <% end %>
+ <% end %>
+<% end %>
diff --git a/app/views/logs/_delete_logs_table_sales.html.erb b/app/views/logs/_delete_logs_table_sales.html.erb
new file mode 100644
index 000000000..8659f12bb
--- /dev/null
+++ b/app/views/logs/_delete_logs_table_sales.html.erb
@@ -0,0 +1,30 @@
+<%= govuk_table do |table| %>
+ <% table.head do |head| %>
+ <% head.row do |row| %>
+ <% row.cell header: true, text: "Log ID" %>
+ <% row.cell header: true, text: "Purchaser code" %>
+ <% row.cell header: true, text: "Sale completion date" %>
+ <% row.cell header: true, text: "Status" %>
+ <% row.cell header: true, text: "Delete?" %>
+ <% end %>
+ <% end %>
+ <% table.body do |body| %>
+ <% f.govuk_check_boxes_fieldset :selected_ids, small: true do %>
+ <% delete_logs_form.logs.each do |log| %>
+ <% body.row do |row| %>
+ <% row.cell do %>
+ <%= govuk_link_to log.id, url_for(log) %>
+ <% end %>
+ <% row.cell text: log.purchid %>
+ <% row.cell text: log.saledate&.to_formatted_s(:govuk_date) %>
+ <% row.cell text: status_tag(log.status) %>
+ <% row.cell html_attributes: { class: "checkbox-cell" } do %>
+ <% f.govuk_check_box :selected_ids, log.id,
+ label: { text: log.id, hidden: true },
+ checked: delete_logs_form.selected_ids.include?(log.id) %>
+ <% end %>
+ <% end %>
+ <% end %>
+ <% end %>
+ <% end %>
+<% end %>
diff --git a/app/views/logs/_log_list.html.erb b/app/views/logs/_log_list.html.erb
index e92186d4d..d41bdc9a8 100644
--- a/app/views/logs/_log_list.html.erb
+++ b/app/views/logs/_log_list.html.erb
@@ -1,11 +1,21 @@
+
+
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "logs", path: request.path)) %>
<% if logs&.any? %>
<%= govuk_link_to "Download (CSV)", csv_download_url, type: "text/csv", class: "govuk-!-margin-right-4" %>
<% if @current_user.support? %>
- <%= govuk_link_to "Download (CSV, codes only)", csv_codes_only_download_url, type: "text/csv" %>
+ <%= govuk_link_to "Download (CSV, codes only)", csv_codes_only_download_url, type: "text/csv", class: "govuk-!-margin-right-4" %>
<% end %>
<% end %>
+
+
+ <% if logs&.any? && (display_delete_logs?(@current_user, searched, filter_type) || in_organisations_tab?) %>
+ <%# remove @ on current user %>
+ <%= govuk_link_to "Delete logs", delete_logs_path, class: "app-!-colour-red" %>
+ <% end %>
+
+
<% logs.map do |log| %>
<%= render(LogSummaryComponent.new(current_user:, log:)) %>
diff --git a/app/views/logs/delete_logs.html.erb b/app/views/logs/delete_logs.html.erb
new file mode 100644
index 000000000..8f9338ff2
--- /dev/null
+++ b/app/views/logs/delete_logs.html.erb
@@ -0,0 +1,22 @@
+<% title = "Delete logs" %>
+<% content_for :title, title %>
+<% content_for :before_content do %>
+ <%= govuk_back_link(href: :back) %>
+<% end %>
+
+
+ <%= title %>
+ Review the logs you want to delete
+
+You've selected <%= @delete_logs_form.log_count %> <%= "log".pluralize(@delete_logs_form.log_count) %> to delete
+
+
+ <%= form_with model: @delete_logs_form, url: @delete_logs_form.delete_confirmation_path do |f| %>
+ <%= f.hidden_field :search_term, value: @delete_logs_form.search_term %>
+ <%= f.govuk_error_summary %>
+ <%= render partial: @delete_logs_form.table_partial_name, locals: { f:, delete_logs_form: @delete_logs_form } %>
+ <%= f.govuk_submit "Continue" do %>
+ <%= govuk_button_link_to "Cancel", @delete_logs_form.back_to_logs_path, secondary: true %>
+ <% end %>
+ <% end %>
+
diff --git a/app/views/logs/delete_logs_confirmation.html.erb b/app/views/logs/delete_logs_confirmation.html.erb
new file mode 100644
index 000000000..a9b4252d4
--- /dev/null
+++ b/app/views/logs/delete_logs_confirmation.html.erb
@@ -0,0 +1,25 @@
+<% title = "Delete logs" %>
+<% content_for :title, title %>
+<% content_for :before_content do %>
+ <%= govuk_back_link(href: :back) %>
+<% end %>
+
+
+ <%= title %>
+ Are you sure you want to delete these logs?
+
+<% log_count = @delete_logs_form.selected_ids.count %>
+You've selected <%= log_count %> <%= "log".pluralize(log_count) %> to delete
+
+<%= govuk_warning_text(icon_fallback_text: "Danger") do %>
+ You will not be able to undo this action
+<% end %>
+
+
+ <%= govuk_button_to "Delete logs", @delete_logs_form.delete_path, method: "delete", params: { ids: @delete_logs_form.selected_ids } %>
+ <%= form_with url: @delete_logs_form.delete_path do |f| %>
+ <%= f.hidden_field :selected_ids, value: @delete_logs_form.selected_ids %>
+ <%= f.hidden_field :search, value: @delete_logs_form.search_term %>
+ <%= f.govuk_submit "Cancel", secondary: true %>
+ <% end %>
+
diff --git a/app/views/logs/index.html.erb b/app/views/logs/index.html.erb
index e2cc2e0d1..eac53ed7c 100644
--- a/app/views/logs/index.html.erb
+++ b/app/views/logs/index.html.erb
@@ -68,8 +68,10 @@
searched: @searched,
item_label:,
total_count: @total_count,
- csv_download_url: csv_download_url_for_controller(controller:, search: @search_term, codes_only: false),
- csv_codes_only_download_url: csv_download_url_for_controller(controller:, search: @search_term, codes_only: true),
+ csv_download_url: csv_download_url_for_controller(controller:, search: @searched, codes_only: false),
+ csv_codes_only_download_url: csv_download_url_for_controller(controller:, search: @searched, codes_only: true),
+ delete_logs_path: @delete_logs_path,
+ filter_type: @filter_type,
} %>
<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %>
diff --git a/app/views/logs/update_logs.html.erb b/app/views/logs/update_logs.html.erb
index 1b744beb6..d89ecdc4b 100644
--- a/app/views/logs/update_logs.html.erb
+++ b/app/views/logs/update_logs.html.erb
@@ -4,43 +4,42 @@
<% content_for :title, title %>
<% if @total_count < 1 %>
- <%= render partial: "organisations/headings", locals: { main: "There are no more logs that need updating", sub: "" } %>
-
- You’ve completed all the logs that were affected by scheme changes.
-
-
- <%= govuk_button_link_to "Back to all logs", lettings_logs_path %>
-
+ <%= render partial: "organisations/headings", locals: { main: "There are no more logs that need updating", sub: "" } %>
+
+ You’ve completed all the logs that were affected by scheme changes.
+
+
+ <%= govuk_button_link_to "Back to all logs", lettings_logs_path %>
+
<% else %>
- <%= render partial: "organisations/headings", locals: { main: "You need to update #{@total_count} logs", sub: "" } %>
-
- <%= govuk_table do |table| %>
- <% table.head do |head| %>
- <% head.row do |row| %>
- <% row.cell(header: true, text: "Log ID") %>
- <% row.cell(header: true, text: "Tenancy code") %>
- <% row.cell(header: true, text: "Property reference") %>
- <% row.cell(header: true, text: "Status") %>
- <% row.cell(header: true, text: "") %>
- <% end %>
- <% end %>
+ <%= render partial: "organisations/headings", locals: { main: "You need to update #{@total_count} logs", sub: "" } %>
+ <%= govuk_table do |table| %>
+ <% table.head do |head| %>
+ <% head.row do |row| %>
+ <% row.cell(header: true, text: "Log ID") %>
+ <% row.cell(header: true, text: "Tenancy code") %>
+ <% row.cell(header: true, text: "Property reference") %>
+ <% row.cell(header: true, text: "Status") %>
+ <% row.cell(header: true, text: "") %>
+ <% end %>
+ <% end %>
<% @logs.each do |log| %>
- <% table.body do |body| %>
- <% body.row do |row| %>
- <% row.cell(text: log.id) %>
- <% row.cell(text: log.tenancycode) %>
- <% row.cell(text: log.propcode) %>
- <% row.cell(text: status_tag(log.status)) %>
- <% row.cell(html_attributes: {
- scope: "row",
- class: "govuk-!-text-align-right",
- }) do %>
- <%= govuk_link_to("Update now", send(log.form.unresolved_log_path, log)) %>
- <% end %>
- <% end %>
+ <% table.body do |body| %>
+ <% body.row do |row| %>
+ <% row.cell(text: log.id) %>
+ <% row.cell(text: log.tenancycode) %>
+ <% row.cell(text: log.propcode) %>
+ <% row.cell(text: status_tag(log.status)) %>
+ <% row.cell(html_attributes: {
+ scope: "row",
+ class: "govuk-!-text-align-right",
+ }) do %>
+ <%= govuk_link_to "Update now", send(log.form.unresolved_log_path, log) %>
+ <% end %>
<% end %>
+ <% end %>
<% end %>
- <% end %>
+ <% end %>
<% end %>
<%= render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %>
diff --git a/app/views/organisations/logs.html.erb b/app/views/organisations/logs.html.erb
index 6b269590c..89f54fb1d 100644
--- a/app/views/organisations/logs.html.erb
+++ b/app/views/organisations/logs.html.erb
@@ -34,6 +34,8 @@
total_count: @total_count,
csv_download_url: csv_download_url_by_log_type(@log_type, @organisation, search: @search_term, codes_only: false),
csv_codes_only_download_url: csv_download_url_by_log_type(@log_type, @organisation, search: @search_term, codes_only: true),
+ delete_logs_path: @delete_logs_path,
+ filter_type: @filter_type,
} %>
<%= render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %>
diff --git a/config/application.rb b/config/application.rb
index 49a236636..4fd65c695 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -41,5 +41,14 @@ module DataCollector
key: "_data_collector_session",
secure: (Rails.env.production? || Rails.env.staging? || Rails.env.review?)
)
+
+ config.generators do |g|
+ g.test_framework :rspec,
+ request_specs: true,
+ view_specs: false,
+ routing_specs: false,
+ helper_specs: false,
+ controller_specs: false
+ end
end
end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index f5d8a56b7..e1635e748 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -175,6 +175,11 @@ en:
new_organisation_telephone_number:
blank: "Enter a valid telephone number"
+ notification:
+ logs_deleted:
+ one: "%{count} log has been deleted"
+ other: "%{count} logs have been deleted"
+
validations:
organisation:
data_sharing_agreement_not_signed: Your organisation must accept the Data Sharing Agreement before you can create any logs.
diff --git a/config/routes.rb b/config/routes.rb
index 9dd9b7325..fa0cb598e 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -122,7 +122,15 @@ Rails.application.routes.draw do
get "users", to: "organisations#users"
get "users/invite", to: "users/account#new"
get "lettings-logs", to: "organisations#lettings_logs"
+ get "delete-lettings-logs", to: "delete_logs#delete_lettings_logs_for_organisation"
+ post "delete-lettings-logs", to: "delete_logs#delete_lettings_logs_for_organisation_with_selected_ids"
+ post "delete-lettings-logs-confirmation", to: "delete_logs#delete_lettings_logs_for_organisation_confirmation"
+ delete "delete-lettings-logs", to: "delete_logs#discard_lettings_logs_for_organisation"
get "sales-logs", to: "organisations#sales_logs"
+ get "delete-sales-logs", to: "delete_logs#delete_sales_logs_for_organisation"
+ post "delete-sales-logs", to: "delete_logs#delete_sales_logs_for_organisation_with_selected_ids"
+ post "delete-sales-logs-confirmation", to: "delete_logs#delete_sales_logs_for_organisation_confirmation"
+ delete "delete-sales-logs", to: "delete_logs#discard_sales_logs_for_organisation"
get "lettings-logs/csv-download", to: "organisations#download_lettings_csv"
post "lettings-logs/email-csv", to: "organisations#email_lettings_csv"
get "lettings-logs/csv-confirmation", to: "lettings_logs#csv_confirmation"
@@ -170,6 +178,11 @@ Rails.application.routes.draw do
post "email-csv", to: "lettings_logs#email_csv"
get "csv-confirmation", to: "lettings_logs#csv_confirmation"
+ get "delete-logs", to: "delete_logs#delete_lettings_logs"
+ post "delete-logs", to: "delete_logs#delete_lettings_logs_with_selected_ids"
+ post "delete-logs-confirmation", to: "delete_logs#delete_lettings_logs_confirmation"
+ delete "delete-logs", to: "delete_logs#discard_lettings_logs"
+
resources :bulk_upload_lettings_logs, path: "bulk-upload-logs", only: %i[show update] do
collection do
get :start
@@ -227,6 +240,11 @@ Rails.application.routes.draw do
post "email-csv", to: "sales_logs#email_csv"
get "csv-confirmation", to: "sales_logs#csv_confirmation"
+ get "delete-logs", to: "delete_logs#delete_sales_logs"
+ post "delete-logs", to: "delete_logs#delete_sales_logs_with_selected_ids"
+ post "delete-logs-confirmation", to: "delete_logs#delete_sales_logs_confirmation"
+ delete "delete-logs", to: "delete_logs#discard_sales_logs"
+
resources :bulk_upload_sales_logs, path: "bulk-upload-logs" do
collection do
get :start
diff --git a/spec/features/lettings_log_spec.rb b/spec/features/lettings_log_spec.rb
index 7f1e77d2a..2e83b669c 100644
--- a/spec/features/lettings_log_spec.rb
+++ b/spec/features/lettings_log_spec.rb
@@ -220,6 +220,46 @@ RSpec.describe "Lettings Log Features" do
end
end
end
+
+ it "is possible to delete multiple logs" do
+ postcode = "SW1A 1AA"
+ lettings_log_1 = create(:lettings_log, :setup_completed, created_by: support_user, postcode_full: postcode)
+ lettings_log_2 = create(:lettings_log, :in_progress, created_by: support_user, postcode_full: postcode)
+ create_list(:lettings_log, 5, :in_progress)
+
+ visit lettings_logs_path
+ expect(page).to have_selector "article.app-log-summary", count: 7
+ expect(page).not_to have_link "Delete logs"
+ within ".app-filter" do
+ check "status-in-progress-field"
+ choose "user-yours-field"
+ click_button
+ end
+ expect(page).to have_selector "article.app-log-summary", count: 2
+ expect(page).to have_link "Delete logs"
+ click_link "Delete logs"
+
+ expect(page).to have_current_path delete_logs_lettings_logs_path
+ rows = page.find_all "tbody tr"
+ expect(rows.count).to be 2
+ id_to_delete, id_to_keep = rows.map { |row| row.first("td").text.to_i }
+ expect([id_to_delete, id_to_keep]).to match_array [lettings_log_1.id, lettings_log_2.id]
+ check "forms-delete-logs-form-selected-ids-#{id_to_delete}-field"
+ uncheck "forms-delete-logs-form-selected-ids-#{id_to_keep}-field"
+ click_button "Continue"
+
+ expect(page).to have_current_path delete_logs_confirmation_lettings_logs_path
+ expect(page.text).to include "You've selected 1 log to delete"
+ expect(page.find("form.button_to")[:action]).to eq delete_logs_lettings_logs_path
+ click_button "Delete logs"
+
+ expect(page).to have_current_path lettings_logs_path
+ expect(page).to have_selector "article.app-log-summary", count: 1
+ expect(page.find("article.app-log-summary h2").text).to eq "Log #{id_to_keep}"
+ deleted_log = LettingsLog.find(id_to_delete)
+ expect(deleted_log.status).to eq "deleted"
+ expect(deleted_log.discarded_at).not_to be nil
+ end
end
context "when the signed is user is not a Support user" do
diff --git a/spec/features/sales_log_spec.rb b/spec/features/sales_log_spec.rb
index 8093f85dd..890c82786 100644
--- a/spec/features/sales_log_spec.rb
+++ b/spec/features/sales_log_spec.rb
@@ -3,16 +3,14 @@ require "rails_helper"
RSpec.describe "Sales Log Features" do
context "when searching for specific sales logs" do
context "when I am signed in and there are sales logs in the database" do
- let(:user) { FactoryBot.create(:user, last_sign_in_at: Time.zone.now) }
+ let(:user) { FactoryBot.create(:user, last_sign_in_at: Time.zone.now, name: "Jimbo") }
let!(:log_to_search) { FactoryBot.create(:sales_log, owning_organisation: user.organisation) }
let!(:same_organisation_log) { FactoryBot.create(:sales_log, owning_organisation: user.organisation) }
let!(:another_organisation_log) { FactoryBot.create(:sales_log) }
before do
- visit("/sales-logs")
- fill_in("user[email]", with: user.email)
- fill_in("user[password]", with: user.password)
- click_button("Sign in")
+ sign_in user
+ visit sales_logs_path
end
it "displays the logs belonging to the same organisation" do
@@ -23,34 +21,75 @@ RSpec.describe "Sales Log Features" do
context "when returning to the list of logs via breadcrumbs link" do
before do
- visit("/sales-logs")
click_button("Create a new sales log")
click_link("Logs")
end
it "navigates you to the sales logs page" do
- expect(page).to have_current_path("/sales-logs")
+ expect(page).to have_current_path sales_logs_path
end
end
context "when completing the setup sales log section" do
it "includes the purchaser code and sale completion date questions" do
- visit("/sales-logs")
- click_button("Create a new sales log")
- click_link("Set up this sales log")
+ click_button "Create a new sales log"
+ click_link "Set up this sales log"
fill_in("sales_log[saledate(1i)]", with: Time.zone.today.year)
fill_in("sales_log[saledate(2i)]", with: Time.zone.today.month)
fill_in("sales_log[saledate(3i)]", with: Time.zone.today.day)
- click_button("Save and continue")
- fill_in("sales_log[purchid]", with: "PC123")
- click_button("Save and continue")
+ click_button "Save and continue"
+ fill_in "sales_log[purchid]", with: "PC123"
+ click_button "Save and continue"
log_id = page.current_path.scan(/\d/).join
- visit("sales-logs/#{log_id}/setup/check-answers")
- expect(page).to have_content("Sale completion date")
+ visit sales_log_setup_check_answers_path(log_id)
+ expect(page).to have_content "Sale completion date"
expect(page).to have_content(Time.zone.today.year)
- expect(page).to have_content("Purchaser code")
- expect(page).to have_content("PC123")
+ expect(page).to have_content "Purchaser code"
+ expect(page).to have_content "PC123"
+ end
+ end
+
+ it "is possible to delete multiple logs" do
+ log_card_selector = "article.app-log-summary"
+ logs_by_user = create_list(:sales_log, 2, created_by: user)
+
+ visit sales_logs_path
+ expect(page).to have_selector log_card_selector, count: 4
+ expect(page).not_to have_link "Delete logs"
+
+ within ".app-filter" do
+ choose "user-yours-field"
+ click_button
end
+
+ expect(page).to have_selector log_card_selector, count: 2
+ expect(page).to have_link "Delete logs"
+
+ click_link "Delete logs"
+
+ expect(page).to have_current_path delete_logs_sales_logs_path
+
+ rows = page.find_all "tbody tr"
+ expect(rows.count).to be 2
+ id_to_delete, id_to_keep = rows.map { |row| row.first("td").text.to_i }
+ expect([id_to_delete, id_to_keep]).to match_array logs_by_user.map(&:id)
+ check "forms-delete-logs-form-selected-ids-#{id_to_delete}-field"
+ uncheck "forms-delete-logs-form-selected-ids-#{id_to_keep}-field"
+ click_button "Continue"
+
+ expect(page).to have_current_path delete_logs_confirmation_sales_logs_path
+ expect(page.text).to include "You've selected 1 log to delete"
+ button = page.find("form.button_to")
+ expect(button[:action]).to eq delete_logs_sales_logs_path
+ expect(button.text).to eq "Delete logs"
+ click_button "Delete logs"
+
+ expect(page).to have_current_path sales_logs_path
+ expect(page).to have_selector "article.app-log-summary", count: 1
+ expect(page.find("article.app-log-summary h2").text).to eq "Log #{id_to_keep}"
+ deleted_log = SalesLog.find(id_to_delete)
+ expect(deleted_log.status).to eq "deleted"
+ expect(deleted_log.discarded_at).not_to be nil
end
end
end
diff --git a/spec/helpers/filters_helper_spec.rb b/spec/helpers/filters_helper_spec.rb
index dfc6b51fb..622fd71f8 100644
--- a/spec/helpers/filters_helper_spec.rb
+++ b/spec/helpers/filters_helper_spec.rb
@@ -70,6 +70,84 @@ RSpec.describe FiltersHelper do
end
end
+ describe "#any_filter_selected?" do
+ let(:filter_type) { "lettings_logs" }
+ let(:result) { any_filter_selected?(filter_type) }
+ let(:serialised_filters) { filters&.to_json }
+ let(:filters) { nil }
+
+ before do
+ session[:lettings_logs_filters] = serialised_filters if serialised_filters
+ end
+
+ it "returns false if the session contains no filters" do
+ expect(result).to be_falsey
+ end
+
+ context "when organisation and user are set to all" do
+ let(:filters) { { "organisation_select" => "all", "user" => "all" } }
+
+ it "returns false" do
+ expect(result).to be_falsey
+ end
+ end
+
+ context "when user is set to 'yours'" do
+ let(:filters) { { "user" => "yours" } }
+
+ it "returns true" do
+ expect(result).to be true
+ end
+ end
+
+ context "when organisation is filtered" do
+ let(:filters) { { "organisation" => 2 } }
+
+ it "returns true" do
+ expect(result).to be true
+ end
+ end
+
+ context "when status is filtered" do
+ let(:filters) { { "status" => %w[in_progress] } }
+
+ it "returns true" do
+ expect(result).to be true
+ end
+ end
+
+ context "when collection year is filtered" do
+ let(:filters) { { "years" => %w[2023] } }
+
+ it "returns true" do
+ expect(result).to be true
+ end
+ end
+
+ context "when the user is currently in a bulk upload journey" do
+ let(:filters) { { "bulk_upload_id" => "3456" } }
+
+ it "returns true" do
+ expect(result).to be true
+ end
+ end
+
+ context "when a range of filters are applied" do
+ let(:filters) do
+ {
+ "user" => "all",
+ "status" => %w[in_progress completed],
+ "years" => [""],
+ "organisation" => 2,
+ }
+ end
+
+ it "returns true" do
+ expect(result).to be true
+ end
+ end
+ end
+
describe "#selected_option" do
before do
session[:lettings_logs_filters] = {}.to_json
diff --git a/spec/helpers/log_list_helper_spec.rb b/spec/helpers/log_list_helper_spec.rb
new file mode 100644
index 000000000..22836ec45
--- /dev/null
+++ b/spec/helpers/log_list_helper_spec.rb
@@ -0,0 +1,72 @@
+require "rails_helper"
+
+RSpec.describe LogListHelper, type: :helper do
+ include FiltersHelper
+
+ describe "#display_delete_logs?" do
+ let(:search_term) { nil }
+ let(:filter_type) { "lettings_logs" }
+
+ context "when logged in as a data provider" do
+ let(:result) { display_delete_logs?(user, search_term, filter_type) }
+ let(:user) { create(:user) }
+
+ it "returns false if no filters or search are set" do
+ allow(self).to receive(:filter_selected?).and_return false
+ expect(result).to be false
+ end
+
+ it "returns true if the user filter is set to 'yours'" do
+ allow(self).to receive(:filter_selected?).with("user", "yours", filter_type).and_return true
+ expect(result).to be true
+ end
+
+ it "returns false if any filters other than the user filter are set" do
+ allow(self).to receive(:filter_selected?).and_return true
+ allow(self).to receive(:filter_selected?).with("user", "yours", filter_type).and_return false
+ expect(result).to be false
+ end
+
+ context "when there is a search term present" do
+ let(:search_term) { "word" }
+
+ it "still returns false as long as the user filter is not set to yours" do
+ allow(self).to receive(:filter_selected?).with("user", "yours", filter_type).and_return false
+ expect(result).to be false
+ end
+ end
+ end
+
+ context "when logged in as a support user or data coordinator" do
+ let(:support_user) { create(:user, :support) }
+ let(:data_coordinator) { create(:user, :data_coordinator) }
+ let(:support_result) { display_delete_logs?(support_user, search_term, filter_type) }
+ let(:coordinator_result) { display_delete_logs?(data_coordinator, search_term, filter_type) }
+ let(:results) { [support_result, coordinator_result] }
+
+ it "returns false if no filters or search are set" do
+ allow(self).to receive(:any_filter_selected?).and_return false
+ expect(results).to all be false
+ end
+
+ it "returns true if any filter is set" do
+ allow(self).to receive(:any_filter_selected?).and_return true
+ expect(results).to all be true
+ end
+
+ context "when there is a search term present" do
+ let(:search_term) { "word" }
+
+ it "returns true if no filter is selected" do
+ allow(self).to receive(:any_filter_selected?).and_return false
+ expect(results).to all be true
+ end
+
+ it "returns true if any filter is selected" do
+ allow(self).to receive(:any_filter_selected?).and_return true
+ expect(results).to all be true
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/forms/delete_logs_form_spec.rb b/spec/models/forms/delete_logs_form_spec.rb
new file mode 100644
index 000000000..adad36d90
--- /dev/null
+++ b/spec/models/forms/delete_logs_form_spec.rb
@@ -0,0 +1,89 @@
+require "rails_helper"
+
+RSpec.describe Forms::DeleteLogsForm do
+ let(:delete_logs_form) { described_class.new(attributes) }
+
+ let(:attributes) do
+ {
+ log_type:,
+ search_term:,
+ current_user:,
+ log_filters:,
+ selected_ids:,
+ delete_confirmation_path:,
+ back_to_logs_path:,
+ delete_path:,
+ }
+ end
+ let(:log_type) { :lettings }
+ let(:search_term) { "meaning" }
+ let(:current_user) { create(:user) }
+ let(:log_filters) do
+ {
+ "years" => [""],
+ "status" => ["", "completed"],
+ "user" => "yours",
+ }
+ end
+ let(:selected_ids) { [visible_logs.first.id] }
+ let(:delete_confirmation_path) { "/lettings-logs/delete-logs-confirmation" }
+ let(:back_to_logs_path) { "/lettings-logs?search=meaning" }
+ let(:delete_path) { "/lettings-logs/delete-logs" }
+
+ let(:visible_logs) { create_list(:lettings_log, 3, created_by: current_user) }
+
+ before do
+ allow(FilterManager).to receive(:filter_logs).and_return visible_logs
+ end
+
+ it "exposes the log type" do
+ expect(delete_logs_form.log_type).to be log_type
+ end
+
+ it "exposes the search term" do
+ expect(delete_logs_form.search_term).to be search_term
+ end
+
+ it "exposes the paths" do
+ expect(delete_logs_form.delete_confirmation_path).to be delete_confirmation_path
+ expect(delete_logs_form.back_to_logs_path).to be back_to_logs_path
+ expect(delete_logs_form.delete_path).to be delete_path
+ end
+
+ it "exposes the logs returned by the filter manager" do
+ expect(delete_logs_form.logs).to be visible_logs
+ end
+
+ it "exposes the selected ids" do
+ expect(delete_logs_form.selected_ids).to be selected_ids
+ end
+
+ context "when selected ids are not provided to the initializer" do
+ let(:selected_ids) { nil }
+
+ it "sets the selected ids to be all logs" do
+ expect(delete_logs_form.selected_ids).to match_array visible_logs.map(&:id)
+ end
+ end
+
+ it "calls the filter manager with the correct arguments" do
+ create(:lettings_log)
+
+ expect(FilterManager).to receive(:filter_logs) { |arg1, arg2, arg3, arg4, arg5|
+ expect(arg1).to contain_exactly(*visible_logs)
+ expect(arg2).to eq search_term
+ expect(arg3).to eq log_filters
+ expect(arg4).to be nil
+ expect(arg5).to be current_user
+ }.and_return visible_logs
+ delete_logs_form
+ end
+
+ it "exposes the number of logs" do
+ expect(delete_logs_form.log_count).to be visible_logs.count
+ end
+
+ it "provides the name of the table partial relevant to the log type" do
+ expect(delete_logs_form.table_partial_name).to eq "logs/delete_logs_table_lettings"
+ end
+end
diff --git a/spec/requests/delete_logs_controller_spec.rb b/spec/requests/delete_logs_controller_spec.rb
new file mode 100644
index 000000000..fd509d699
--- /dev/null
+++ b/spec/requests/delete_logs_controller_spec.rb
@@ -0,0 +1,946 @@
+require "rails_helper"
+
+RSpec.describe "DeleteLogs", type: :request do
+ let(:page) { Capybara::Node::Simple.new(response.body) }
+ let(:user) { create(:user, name: "Richard MacDuff") }
+
+ before do
+ allow(user).to receive(:need_two_factor_authentication?).and_return(false)
+ sign_in user
+ end
+
+ describe "GET lettings-logs/delete-logs" do
+ let!(:log_1) { create(:lettings_log, :in_progress, created_by: user) }
+ let!(:log_2) { create(:lettings_log, :completed, created_by: user) }
+
+ before do
+ allow(FilterManager).to receive(:filter_logs).and_return LettingsLog.all
+ end
+
+ it "calls the filter service with the filters in the session and the search term from the query params" do
+ search = "Schrödinger's cat"
+ logs_filters = {
+ "years" => [""],
+ "status" => ["", "in_progress"],
+ "user" => "all",
+ }
+ get lettings_logs_path(logs_filters) # adds the filters to the session
+
+ expect(FilterManager).to receive(:filter_logs) { |arg1, arg2, arg3|
+ expect(arg1).to contain_exactly(log_1, log_2)
+ expect(arg2).to eq search
+ expect(arg3).to eq logs_filters
+ }.and_return LettingsLog.all
+
+ get delete_logs_lettings_logs_path(search:)
+ end
+
+ it "displays the logs returned by the filter service" do
+ get delete_logs_lettings_logs_path
+
+ table_body_rows = page.find_all("tbody tr")
+ expect(table_body_rows.count).to be 2
+ ids_in_table = table_body_rows.map { |row| row.first("td").text.strip }
+ expect(ids_in_table).to match_array [log_1.id.to_s, log_2.id.to_s]
+ end
+
+ it "checks all checkboxes by default" do
+ get delete_logs_lettings_logs_path
+
+ checkboxes = page.find_all("tbody tr").map { |row| row.find("input") }
+ expect(checkboxes.count).to be 2
+ expect(checkboxes).to all be_checked
+ end
+ end
+
+ describe "POST lettings-logs/delete-logs" do
+ let!(:log_1) { create(:lettings_log, :in_progress, created_by: user) }
+ let!(:log_2) { create(:lettings_log, :completed, created_by: user) }
+ let(:selected_ids) { log_1.id }
+
+ before do
+ allow(FilterManager).to receive(:filter_logs).and_return LettingsLog.all
+ end
+
+ it "throws an error if selected ids are not provided" do
+ expect { post delete_logs_lettings_logs_path }.to raise_error ActionController::ParameterMissing
+ end
+
+ it "calls the filter service with the filters in the session and the search term from the query params" do
+ search = "Schrödinger's cat"
+ logs_filters = {
+ "years" => [""],
+ "status" => ["", "in_progress"],
+ "user" => "all",
+ }
+ get lettings_logs_path(logs_filters) # adds the filters to the session
+
+ expect(FilterManager).to receive(:filter_logs) { |arg1, arg2, arg3|
+ expect(arg1).to contain_exactly(log_1, log_2)
+ expect(arg2).to eq search
+ expect(arg3).to eq logs_filters
+ }.and_return LettingsLog.all
+
+ post delete_logs_lettings_logs_path(search:, selected_ids:)
+ end
+
+ it "displays the logs returned by the filter service" do
+ post delete_logs_lettings_logs_path(selected_ids:)
+
+ table_body_rows = page.find_all("tbody tr")
+ expect(table_body_rows.count).to be 2
+ ids_in_table = table_body_rows.map { |row| row.first("td").text.strip }
+ expect(ids_in_table).to match_array [log_1.id.to_s, log_2.id.to_s]
+ end
+
+ it "only checks the selected checkboxes when selected_ids provided" do
+ post delete_logs_lettings_logs_path(selected_ids:)
+
+ checkboxes = page.find_all("tbody tr").map { |row| row.find("input") }
+ checkbox_expected_checked = checkboxes.find { |cb| cb.value == log_1.id.to_s }
+ checkbox_expected_unchecked = checkboxes.find { |cb| cb.value == log_2.id.to_s }
+ expect(checkbox_expected_checked).to be_checked
+ expect(checkbox_expected_unchecked).not_to be_checked
+ end
+ end
+
+ describe "POST lettings-logs/delete-logs-confirmation" do
+ let(:log_1) { create(:lettings_log, :in_progress) }
+ let(:log_2) { create(:lettings_log, :completed) }
+ let(:log_3) { create(:lettings_log, :in_progress) }
+ let(:params) do
+ {
+ forms_delete_logs_form: {
+ search_term: "milk",
+ selected_ids: [log_1, log_2].map(&:id),
+ },
+ }
+ end
+
+ before do
+ post delete_logs_confirmation_lettings_logs_path, params: params
+ end
+
+ it "requires delete logs form data to be provided" do
+ expect { post delete_logs_confirmation_lettings_logs_path }.to raise_error(ActionController::ParameterMissing)
+ end
+
+ it "shows the correct title" do
+ expect(page.find("h1").text).to include "Are you sure you want to delete these logs?"
+ end
+
+ it "shows the correct information text to the user" do
+ expect(page).to have_selector("p", text: "You've selected 2 logs to delete")
+ end
+
+ context "when only one log is selected" do
+ let(:params) do
+ {
+ forms_delete_logs_form: {
+ search_term: "milk",
+ selected_ids: [log_1].map(&:id),
+ },
+ }
+ end
+
+ it "shows the correct information text to the user in the singular" do
+ expect(page).to have_selector("p", text: "You've selected 1 log to delete")
+ end
+ end
+
+ it "shows a warning to the user" do
+ expect(page).to have_selector(".govuk-warning-text", text: "You will not be able to undo this action")
+ end
+
+ it "shows a button to delete the selected logs" do
+ expect(page).to have_selector("form.button_to button", text: "Delete logs")
+ end
+
+ it "the delete logs button submits the correct data to the correct path" do
+ form_containing_button = page.find("form.button_to")
+
+ expect(form_containing_button[:action]).to eq delete_logs_lettings_logs_path
+ expect(form_containing_button).to have_field "_method", type: :hidden, with: "delete"
+ expect(form_containing_button).to have_field "ids[]", type: :hidden, with: log_1.id
+ expect(form_containing_button).to have_field "ids[]", type: :hidden, with: log_2.id
+ end
+
+ it "shows a cancel button with the correct style" do
+ expect(page).to have_selector("button.govuk-button--secondary", text: "Cancel")
+ end
+
+ it "the cancel button submits the correct data to the correct path" do
+ form_containing_cancel = page.find_all("form").find { |form| form.has_selector?("button.govuk-button--secondary") }
+ expect(form_containing_cancel).to have_field("selected_ids", type: :hidden, with: [log_1, log_2].map(&:id).join(" "))
+ expect(form_containing_cancel).to have_field("search", type: :hidden, with: "milk")
+ expect(form_containing_cancel[:method]).to eq "post"
+ expect(form_containing_cancel[:action]).to eq delete_logs_lettings_logs_path
+ end
+
+ context "when no logs are selected" do
+ let(:params) do
+ {
+ forms_delete_logs_form: {
+ log_type: :lettings,
+ log_ids: [log_1, log_2, log_3].map(&:id).join(" "),
+ },
+ }
+ end
+
+ before do
+ post delete_logs_confirmation_lettings_logs_path, params: params
+ end
+
+ it "renders the list of logs table again" do
+ expect(page.find("h1").text).to include "Review the logs you want to delete"
+ end
+
+ it "displays an error message" do
+ expect(page).to have_selector(".govuk-error-summary", text: "Select at least one log to delete or press cancel to return")
+ end
+
+ it "renders the table with all checkboxes unchecked" do
+ checkboxes = page.find_all("tbody tr").map { |row| row.find("input") }
+ checkboxes.each do |checkbox|
+ expect(checkbox).not_to be_checked
+ end
+ end
+ end
+ end
+
+ describe "DELETE lettings-logs/delete-logs" do
+ let(:log_1) { create(:lettings_log, :in_progress, created_by: user) }
+ let(:params) { { ids: [log_1.id, log_2.id] } }
+
+ context "when the user is authorized to delete the logs provided" do
+ let(:log_2) { create(:lettings_log, :completed, created_by: user) }
+
+ it "deletes the logs provided" do
+ delete delete_logs_lettings_logs_path, params: params
+ log_1.reload
+ expect(log_1.status).to eq "deleted"
+ expect(log_1.discarded_at).not_to be nil
+ log_2.reload
+ expect(log_2.status).to eq "deleted"
+ expect(log_2.discarded_at).not_to be nil
+ end
+
+ it "redirects to the lettings log index and displays a notice that the logs have been deleted" do
+ delete delete_logs_lettings_logs_path, params: params
+ expect(response).to redirect_to lettings_logs_path
+ follow_redirect!
+ expect(page).to have_selector(".govuk-notification-banner--success")
+ expect(page).to have_selector(".govuk-notification-banner--success", text: "2 logs have been deleted")
+ end
+ end
+
+ context "when the user is not authorized to delete all the logs provided" do
+ let(:log_2) { create(:lettings_log, :completed) }
+
+ it "returns unauthorised and only deletes logs for which the user is authorised" do
+ delete delete_logs_lettings_logs_path, params: params
+ expect(response).to have_http_status(:unauthorized)
+ log_1.reload
+ expect(log_1.status).to eq "deleted"
+ expect(log_1.discarded_at).not_to be nil
+ log_2.reload
+ expect(log_2.discarded_at).to be nil
+ end
+ end
+ end
+
+ describe "GET sales-logs/delete-logs" do
+ let!(:log_1) { create(:sales_log, :in_progress, created_by: user) }
+ let!(:log_2) { create(:sales_log, :completed, created_by: user) }
+
+ before do
+ allow(FilterManager).to receive(:filter_logs).and_return SalesLog.all
+ end
+
+ it "calls the filter service with the filters in the session and the search term from the query params" do
+ search = "Schrödinger's cat"
+ logs_filters = {
+ "years" => [""],
+ "status" => ["", "in_progress"],
+ "user" => "all",
+ }
+ get sales_logs_path(logs_filters) # adds the filters to the session
+
+ expect(FilterManager).to receive(:filter_logs) { |arg1, arg2, arg3|
+ expect(arg1).to contain_exactly(log_1, log_2)
+ expect(arg2).to eq search
+ expect(arg3).to eq logs_filters
+ }.and_return SalesLog.all
+
+ get delete_logs_sales_logs_path(search:)
+ end
+
+ it "displays the logs returned by the filter service" do
+ get delete_logs_sales_logs_path
+
+ table_body_rows = page.find_all("tbody tr")
+ expect(table_body_rows.count).to be 2
+ ids_in_table = table_body_rows.map { |row| row.first("td").text.strip }
+ expect(ids_in_table).to match_array [log_1.id.to_s, log_2.id.to_s]
+ end
+
+ it "checks all checkboxes by default" do
+ get delete_logs_sales_logs_path
+
+ checkboxes = page.find_all("tbody tr").map { |row| row.find("input") }
+ expect(checkboxes.count).to be 2
+ expect(checkboxes).to all be_checked
+ end
+ end
+
+ describe "POST sales-logs/delete-logs" do
+ let!(:log_1) { create(:sales_log, :in_progress, created_by: user) }
+ let!(:log_2) { create(:sales_log, :completed, created_by: user) }
+ let(:selected_ids) { log_1.id }
+
+ before do
+ allow(FilterManager).to receive(:filter_logs).and_return SalesLog.all
+ end
+
+ it "throws an error if selected ids are not provided" do
+ expect { post delete_logs_sales_logs_path }.to raise_error ActionController::ParameterMissing
+ end
+
+ it "calls the filter service with the filters in the session and the search term from the query params" do
+ search = "Schrödinger's cat"
+ logs_filters = {
+ "years" => [""],
+ "status" => ["", "in_progress"],
+ "user" => "all",
+ }
+ get sales_logs_path(logs_filters) # adds the filters to the session
+
+ expect(FilterManager).to receive(:filter_logs) { |arg1, arg2, arg3|
+ expect(arg1).to contain_exactly(log_1, log_2)
+ expect(arg2).to eq search
+ expect(arg3).to eq logs_filters
+ }.and_return SalesLog.all
+
+ post delete_logs_sales_logs_path(search:, selected_ids:)
+ end
+
+ it "displays the logs returned by the filter service" do
+ post delete_logs_sales_logs_path(selected_ids:)
+
+ table_body_rows = page.find_all("tbody tr")
+ expect(table_body_rows.count).to be 2
+ ids_in_table = table_body_rows.map { |row| row.first("td").text.strip }
+ expect(ids_in_table).to match_array [log_1.id.to_s, log_2.id.to_s]
+ end
+
+ it "only checks the selected checkboxes when selected_ids provided" do
+ post delete_logs_sales_logs_path(selected_ids:)
+
+ checkboxes = page.find_all("tbody tr").map { |row| row.find("input") }
+ checkbox_expected_checked = checkboxes.find { |cb| cb.value == log_1.id.to_s }
+ checkbox_expected_unchecked = checkboxes.find { |cb| cb.value == log_2.id.to_s }
+ expect(checkbox_expected_checked).to be_checked
+ expect(checkbox_expected_unchecked).not_to be_checked
+ end
+ end
+
+ describe "POST sales-logs/delete-logs-confirmation" do
+ let(:log_1) { create(:sales_log, :in_progress) }
+ let(:log_2) { create(:sales_log, :completed) }
+ let(:log_3) { create(:sales_log, :in_progress) }
+ let(:params) do
+ {
+ forms_delete_logs_form: {
+ search_term: "milk",
+ selected_ids: [log_1, log_2].map(&:id),
+ },
+ }
+ end
+
+ before do
+ post delete_logs_confirmation_sales_logs_path, params: params
+ end
+
+ it "requires delete logs form data to be provided" do
+ expect { post delete_logs_confirmation_sales_logs_path }.to raise_error(ActionController::ParameterMissing)
+ end
+
+ it "shows the correct title" do
+ expect(page.find("h1").text).to include "Are you sure you want to delete these logs?"
+ end
+
+ it "shows the correct information text to the user" do
+ expect(page).to have_selector("p", text: "You've selected 2 logs to delete")
+ end
+
+ context "when only one log is selected" do
+ let(:params) do
+ {
+ forms_delete_logs_form: {
+ search_term: "milk",
+ selected_ids: [log_1].map(&:id),
+ },
+ }
+ end
+
+ it "shows the correct information text to the user in the singular" do
+ expect(page).to have_selector("p", text: "You've selected 1 log to delete")
+ end
+ end
+
+ it "shows a warning to the user" do
+ expect(page).to have_selector(".govuk-warning-text", text: "You will not be able to undo this action")
+ end
+
+ it "shows a button to delete the selected logs" do
+ expect(page).to have_selector("form.button_to button", text: "Delete logs")
+ end
+
+ it "the delete logs button submits the correct data to the correct path" do
+ form_containing_button = page.find("form.button_to")
+
+ expect(form_containing_button[:action]).to eq delete_logs_sales_logs_path
+ expect(form_containing_button).to have_field "_method", type: :hidden, with: "delete"
+ expect(form_containing_button).to have_field "ids[]", type: :hidden, with: log_1.id
+ expect(form_containing_button).to have_field "ids[]", type: :hidden, with: log_2.id
+ end
+
+ it "shows a cancel button with the correct style" do
+ expect(page).to have_selector("button.govuk-button--secondary", text: "Cancel")
+ end
+
+ it "the cancel button submits the correct data to the correct path" do
+ form_containing_cancel = page.find_all("form").find { |form| form.has_selector?("button.govuk-button--secondary") }
+ expect(form_containing_cancel).to have_field("selected_ids", type: :hidden, with: [log_1, log_2].map(&:id).join(" "))
+ expect(form_containing_cancel).to have_field("search", type: :hidden, with: "milk")
+ expect(form_containing_cancel[:method]).to eq "post"
+ expect(form_containing_cancel[:action]).to eq delete_logs_sales_logs_path
+ end
+
+ context "when no logs are selected" do
+ let(:params) do
+ {
+ forms_delete_logs_form: {
+ log_type: :sales,
+ log_ids: [log_1, log_2, log_3].map(&:id).join(" "),
+ },
+ }
+ end
+
+ before do
+ post delete_logs_confirmation_sales_logs_path, params: params
+ end
+
+ it "renders the list of logs table again" do
+ expect(page.find("h1").text).to include "Review the logs you want to delete"
+ end
+
+ it "displays an error message" do
+ expect(page).to have_selector(".govuk-error-summary", text: "Select at least one log to delete or press cancel to return")
+ end
+
+ it "renders the table with all checkboxes unchecked" do
+ checkboxes = page.find_all("tbody tr").map { |row| row.find("input") }
+ checkboxes.each do |checkbox|
+ expect(checkbox).not_to be_checked
+ end
+ end
+ end
+ end
+
+ describe "DELETE sales-logs/delete-logs" do
+ let(:log_1) { create(:sales_log, :in_progress, created_by: user) }
+ let(:params) { { ids: [log_1.id, log_2.id] } }
+
+ context "when the user is authorized to delete the logs provided" do
+ let(:log_2) { create(:sales_log, :completed, created_by: user) }
+
+ it "deletes the logs provided" do
+ delete delete_logs_sales_logs_path, params: params
+ log_1.reload
+ expect(log_1.status).to eq "deleted"
+ expect(log_1.discarded_at).not_to be nil
+ log_2.reload
+ expect(log_2.status).to eq "deleted"
+ expect(log_2.discarded_at).not_to be nil
+ end
+
+ it "redirects to the sales log index and displays a notice that the logs have been deleted" do
+ delete delete_logs_sales_logs_path, params: params
+ expect(response).to redirect_to sales_logs_path
+ follow_redirect!
+ expect(page).to have_selector(".govuk-notification-banner--success")
+ expect(page).to have_selector(".govuk-notification-banner--success", text: "2 logs have been deleted")
+ end
+ end
+
+ context "when the user is not authorized to delete all the logs provided" do
+ let(:log_2) { create(:sales_log, :completed) }
+
+ it "returns unauthorised and only deletes logs for which the user is authorised" do
+ delete delete_logs_sales_logs_path, params: params
+ expect(response).to have_http_status(:unauthorized)
+ log_1.reload
+ expect(log_1.status).to eq "deleted"
+ expect(log_1.discarded_at).not_to be nil
+ log_2.reload
+ expect(log_2.discarded_at).to be nil
+ end
+ end
+ end
+
+ context "when a support user navigates to the organisations tab" do
+ let(:organisation) { create(:organisation, name: "Schmorganisation") }
+ let(:user) { create(:user, :support, name: "Urban Chronotis") }
+
+ describe "GET organisations/delete-lettings-logs" do
+ let!(:log_1) { create(:lettings_log, :in_progress, owning_organisation: organisation) }
+ let!(:log_2) { create(:lettings_log, :completed, owning_organisation: organisation) }
+
+ before do
+ allow(FilterManager).to receive(:filter_logs).and_return LettingsLog.all
+ end
+
+ it "calls the filter service with the filters in the session and the search term from the query params" do
+ search = "Schrödinger's cat"
+ logs_filters = {
+ "years" => [""],
+ "status" => ["", "in_progress"],
+ "user" => "all",
+ }
+ get lettings_logs_path(logs_filters) # adds the filters to the session
+
+ expect(FilterManager).to receive(:filter_logs) { |arg1, arg2, arg3|
+ expect(arg1).to contain_exactly(log_1, log_2)
+ expect(arg2).to eq search
+ expect(arg3).to eq logs_filters.merge(organisation: organisation.id.to_s)
+ }.and_return LettingsLog.all
+
+ get delete_lettings_logs_organisation_path(id: organisation, search:)
+ end
+
+ it "displays the logs returned by the filter service" do
+ get delete_lettings_logs_organisation_path(id: organisation)
+
+ table_body_rows = page.find_all("tbody tr")
+ expect(table_body_rows.count).to be 2
+ ids_in_table = table_body_rows.map { |row| row.first("td").text.strip }
+ expect(ids_in_table).to match_array [log_1.id.to_s, log_2.id.to_s]
+ end
+
+ it "checks all checkboxes by default" do
+ get delete_lettings_logs_organisation_path(id: organisation)
+
+ checkboxes = page.find_all("tbody tr").map { |row| row.find("input") }
+ expect(checkboxes.count).to be 2
+ expect(checkboxes).to all be_checked
+ end
+ end
+
+ describe "POST organisations/delete-lettings-logs" do
+ let!(:log_1) { create(:lettings_log, :in_progress, owning_organisation: organisation) }
+ let!(:log_2) { create(:lettings_log, :completed, owning_organisation: organisation) }
+ let(:selected_ids) { log_1.id }
+
+ before do
+ allow(FilterManager).to receive(:filter_logs).and_return LettingsLog.all
+ end
+
+ it "throws an error if selected ids are not provided" do
+ expect { post delete_lettings_logs_organisation_path(id: organisation) }.to raise_error ActionController::ParameterMissing
+ end
+
+ it "calls the filter service with the filters in the session and the search term from the query params" do
+ search = "Schrödinger's cat"
+ logs_filters = {
+ "years" => [""],
+ "status" => ["", "in_progress"],
+ "user" => "all",
+ }
+ get lettings_logs_path(logs_filters) # adds the filters to the session
+
+ expect(FilterManager).to receive(:filter_logs) { |arg1, arg2, arg3|
+ expect(arg1).to contain_exactly(log_1, log_2)
+ expect(arg2).to eq search
+ expect(arg3).to eq logs_filters.merge(organisation: organisation.id.to_s)
+ }.and_return LettingsLog.all
+
+ post delete_lettings_logs_organisation_path(id: organisation, search:, selected_ids:)
+ end
+
+ it "displays the logs returned by the filter service" do
+ post delete_lettings_logs_organisation_path(id: organisation, selected_ids:)
+
+ table_body_rows = page.find_all("tbody tr")
+ expect(table_body_rows.count).to be 2
+ ids_in_table = table_body_rows.map { |row| row.first("td").text.strip }
+ expect(ids_in_table).to match_array [log_1.id.to_s, log_2.id.to_s]
+ end
+
+ it "only checks the selected checkboxes when selected_ids provided" do
+ post delete_lettings_logs_organisation_path(id: organisation, selected_ids:)
+
+ checkboxes = page.find_all("tbody tr").map { |row| row.find("input") }
+ checkbox_expected_checked = checkboxes.find { |cb| cb.value == log_1.id.to_s }
+ checkbox_expected_unchecked = checkboxes.find { |cb| cb.value == log_2.id.to_s }
+ expect(checkbox_expected_checked).to be_checked
+ expect(checkbox_expected_unchecked).not_to be_checked
+ end
+ end
+
+ describe "POST organisations/delete-lettings-logs-confirmation" do
+ let(:log_1) { create(:lettings_log, :in_progress, owning_organisation: organisation) }
+ let(:log_2) { create(:lettings_log, :completed, owning_organisation: organisation) }
+ let(:log_3) { create(:lettings_log, :in_progress, owning_organisation: organisation) }
+ let(:params) do
+ {
+ forms_delete_logs_form: {
+ search_term: "milk",
+ selected_ids: [log_1, log_2].map(&:id),
+ },
+ }
+ end
+
+ before do
+ post delete_lettings_logs_confirmation_organisation_path(id: organisation), params: params
+ end
+
+ it "requires delete logs form data to be provided" do
+ expect { post delete_lettings_logs_confirmation_organisation_path(id: organisation) }.to raise_error(ActionController::ParameterMissing)
+ end
+
+ it "shows the correct title" do
+ expect(page.find("h1").text).to include "Are you sure you want to delete these logs?"
+ end
+
+ it "shows the correct information text to the user" do
+ expect(page).to have_selector("p", text: "You've selected 2 logs to delete")
+ end
+
+ context "when only one log is selected" do
+ let(:params) do
+ {
+ forms_delete_logs_form: {
+ search_term: "milk",
+ selected_ids: [log_1].map(&:id),
+ },
+ }
+ end
+
+ it "shows the correct information text to the user in the singular" do
+ expect(page).to have_selector("p", text: "You've selected 1 log to delete")
+ end
+ end
+
+ it "shows a warning to the user" do
+ expect(page).to have_selector(".govuk-warning-text", text: "You will not be able to undo this action")
+ end
+
+ it "shows a button to delete the selected logs" do
+ expect(page).to have_selector("form.button_to button", text: "Delete logs")
+ end
+
+ it "the delete logs button submits the correct data to the correct path" do
+ form_containing_button = page.find("form.button_to")
+
+ expect(form_containing_button[:action]).to eq delete_lettings_logs_organisation_path(id: organisation)
+ expect(form_containing_button).to have_field "_method", type: :hidden, with: "delete"
+ expect(form_containing_button).to have_field "ids[]", type: :hidden, with: log_1.id
+ expect(form_containing_button).to have_field "ids[]", type: :hidden, with: log_2.id
+ end
+
+ it "shows a cancel button with the correct style" do
+ expect(page).to have_selector("button.govuk-button--secondary", text: "Cancel")
+ end
+
+ it "the cancel button submits the correct data to the correct path" do
+ form_containing_cancel = page.find_all("form").find { |form| form.has_selector?("button.govuk-button--secondary") }
+ expect(form_containing_cancel).to have_field("selected_ids", type: :hidden, with: [log_1, log_2].map(&:id).join(" "))
+ expect(form_containing_cancel).to have_field("search", type: :hidden, with: "milk")
+ expect(form_containing_cancel[:method]).to eq "post"
+ expect(form_containing_cancel[:action]).to eq delete_lettings_logs_organisation_path(id: organisation)
+ end
+
+ context "when no logs are selected" do
+ let(:params) do
+ {
+ forms_delete_logs_form: {
+ log_type: :lettings,
+ log_ids: [log_1, log_2, log_3].map(&:id).join(" "),
+ },
+ }
+ end
+
+ before do
+ post delete_lettings_logs_confirmation_organisation_path(id: organisation, params:)
+ end
+
+ it "renders the list of logs table again" do
+ expect(page.find("h1").text).to include "Review the logs you want to delete"
+ end
+
+ it "displays an error message" do
+ expect(page).to have_selector(".govuk-error-summary", text: "Select at least one log to delete or press cancel to return")
+ end
+
+ it "renders the table with all checkboxes unchecked" do
+ checkboxes = page.find_all("tbody tr").map { |row| row.find("input") }
+ checkboxes.each do |checkbox|
+ expect(checkbox).not_to be_checked
+ end
+ end
+ end
+ end
+
+ describe "DELETE organisations/delete-lettings-logs" do
+ let(:log_1) { create(:lettings_log, :in_progress, owning_organisation: organisation) }
+ let(:log_2) { create(:lettings_log, :completed, owning_organisation: organisation) }
+ let(:params) { { ids: [log_1.id, log_2.id] } }
+
+ before do
+ delete delete_lettings_logs_organisation_path(id: organisation, params:)
+ end
+
+ it "deletes the logs provided" do
+ log_1.reload
+ expect(log_1.status).to eq "deleted"
+ expect(log_1.discarded_at).not_to be nil
+ log_2.reload
+ expect(log_2.status).to eq "deleted"
+ expect(log_2.discarded_at).not_to be nil
+ end
+
+ it "redirects to the lettings log index for that organisation and displays a notice that the logs have been deleted" do
+ expect(response).to redirect_to lettings_logs_organisation_path(id: organisation)
+ follow_redirect!
+ expect(page).to have_selector(".govuk-notification-banner--success")
+ expect(page).to have_selector(".govuk-notification-banner--success", text: "2 logs have been deleted")
+ end
+ end
+
+ describe "GET organisations/delete-sales-logs" do
+ let!(:log_1) { create(:sales_log, :in_progress, owning_organisation: organisation) }
+ let!(:log_2) { create(:sales_log, :completed, owning_organisation: organisation) }
+
+ before do
+ allow(FilterManager).to receive(:filter_logs).and_return SalesLog.all
+ end
+
+ it "calls the filter service with the filters in the session and the search term from the query params" do
+ search = "Schrödinger's cat"
+ logs_filters = {
+ "years" => [""],
+ "status" => ["", "in_progress"],
+ "user" => "all",
+ }
+ get sales_logs_path(logs_filters) # adds the filters to the session
+
+ expect(FilterManager).to receive(:filter_logs) { |arg1, arg2, arg3|
+ expect(arg1).to contain_exactly(log_1, log_2)
+ expect(arg2).to eq search
+ expect(arg3).to eq logs_filters.merge(organisation: organisation.id.to_s)
+ }.and_return SalesLog.all
+
+ get delete_sales_logs_organisation_path(id: organisation, search:)
+ end
+
+ it "displays the logs returned by the filter service" do
+ get delete_sales_logs_organisation_path(id: organisation)
+
+ table_body_rows = page.find_all("tbody tr")
+ expect(table_body_rows.count).to be 2
+ ids_in_table = table_body_rows.map { |row| row.first("td").text.strip }
+ expect(ids_in_table).to match_array [log_1.id.to_s, log_2.id.to_s]
+ end
+
+ it "checks all checkboxes by default" do
+ get delete_sales_logs_organisation_path(id: organisation)
+
+ checkboxes = page.find_all("tbody tr").map { |row| row.find("input") }
+ expect(checkboxes.count).to be 2
+ expect(checkboxes).to all be_checked
+ end
+ end
+
+ describe "POST organisations/delete-sales-logs" do
+ let!(:log_1) { create(:sales_log, :in_progress, owning_organisation: organisation) }
+ let!(:log_2) { create(:sales_log, :completed, owning_organisation: organisation) }
+ let(:selected_ids) { log_1.id }
+
+ before do
+ allow(FilterManager).to receive(:filter_logs).and_return SalesLog.all
+ end
+
+ it "throws an error if selected ids are not provided" do
+ expect { post delete_sales_logs_organisation_path(id: organisation) }.to raise_error ActionController::ParameterMissing
+ end
+
+ it "calls the filter service with the filters in the session and the search term from the query params" do
+ search = "Schrödinger's cat"
+ logs_filters = {
+ "years" => [""],
+ "status" => ["", "in_progress"],
+ "user" => "all",
+ }
+ get sales_logs_path(logs_filters) # adds the filters to the session
+
+ expect(FilterManager).to receive(:filter_logs) { |arg1, arg2, arg3|
+ expect(arg1).to contain_exactly(log_1, log_2)
+ expect(arg2).to eq search
+ expect(arg3).to eq logs_filters.merge(organisation: organisation.id.to_s)
+ }.and_return SalesLog.all
+
+ post delete_sales_logs_organisation_path(id: organisation, search:, selected_ids:)
+ end
+
+ it "displays the logs returned by the filter service" do
+ post delete_sales_logs_organisation_path(id: organisation, selected_ids:)
+
+ table_body_rows = page.find_all("tbody tr")
+ expect(table_body_rows.count).to be 2
+ ids_in_table = table_body_rows.map { |row| row.first("td").text.strip }
+ expect(ids_in_table).to match_array [log_1.id.to_s, log_2.id.to_s]
+ end
+
+ it "only checks the selected checkboxes when selected_ids provided" do
+ post delete_sales_logs_organisation_path(id: organisation, selected_ids:)
+
+ checkboxes = page.find_all("tbody tr").map { |row| row.find("input") }
+ checkbox_expected_checked = checkboxes.find { |cb| cb.value == log_1.id.to_s }
+ checkbox_expected_unchecked = checkboxes.find { |cb| cb.value == log_2.id.to_s }
+ expect(checkbox_expected_checked).to be_checked
+ expect(checkbox_expected_unchecked).not_to be_checked
+ end
+ end
+
+ describe "POST organisations/delete-sales-logs-confirmation" do
+ let(:log_1) { create(:sales_log, :in_progress, owning_organisation: organisation) }
+ let(:log_2) { create(:sales_log, :completed, owning_organisation: organisation) }
+ let(:log_3) { create(:sales_log, :in_progress, owning_organisation: organisation) }
+ let(:params) do
+ {
+ forms_delete_logs_form: {
+ search_term: "milk",
+ selected_ids: [log_1, log_2].map(&:id),
+ },
+ }
+ end
+
+ before do
+ post delete_sales_logs_confirmation_organisation_path(id: organisation), params: params
+ end
+
+ it "requires delete logs form data to be provided" do
+ expect { post delete_sales_logs_confirmation_organisation_path(id: organisation) }.to raise_error(ActionController::ParameterMissing)
+ end
+
+ it "shows the correct title" do
+ expect(page.find("h1").text).to include "Are you sure you want to delete these logs?"
+ end
+
+ it "shows the correct information text to the user" do
+ expect(page).to have_selector("p", text: "You've selected 2 logs to delete")
+ end
+
+ context "when only one log is selected" do
+ let(:params) do
+ {
+ forms_delete_logs_form: {
+ search_term: "milk",
+ selected_ids: [log_1].map(&:id),
+ },
+ }
+ end
+
+ it "shows the correct information text to the user in the singular" do
+ expect(page).to have_selector("p", text: "You've selected 1 log to delete")
+ end
+ end
+
+ it "shows a warning to the user" do
+ expect(page).to have_selector(".govuk-warning-text", text: "You will not be able to undo this action")
+ end
+
+ it "shows a button to delete the selected logs" do
+ expect(page).to have_selector("form.button_to button", text: "Delete logs")
+ end
+
+ it "the delete logs button submits the correct data to the correct path" do
+ form_containing_button = page.find("form.button_to")
+
+ expect(form_containing_button[:action]).to eq delete_sales_logs_organisation_path(id: organisation)
+ expect(form_containing_button).to have_field "_method", type: :hidden, with: "delete"
+ expect(form_containing_button).to have_field "ids[]", type: :hidden, with: log_1.id
+ expect(form_containing_button).to have_field "ids[]", type: :hidden, with: log_2.id
+ end
+
+ it "shows a cancel button with the correct style" do
+ expect(page).to have_selector("button.govuk-button--secondary", text: "Cancel")
+ end
+
+ it "the cancel button submits the correct data to the correct path" do
+ form_containing_cancel = page.find_all("form").find { |form| form.has_selector?("button.govuk-button--secondary") }
+ expect(form_containing_cancel).to have_field("selected_ids", type: :hidden, with: [log_1, log_2].map(&:id).join(" "))
+ expect(form_containing_cancel).to have_field("search", type: :hidden, with: "milk")
+ expect(form_containing_cancel[:method]).to eq "post"
+ expect(form_containing_cancel[:action]).to eq delete_sales_logs_organisation_path(id: organisation)
+ end
+
+ context "when no logs are selected" do
+ let(:params) do
+ {
+ forms_delete_logs_form: {
+ log_type: :sales,
+ log_ids: [log_1, log_2, log_3].map(&:id).join(" "),
+ },
+ }
+ end
+
+ before do
+ post delete_sales_logs_confirmation_organisation_path(id: organisation, params:)
+ end
+
+ it "renders the list of logs table again" do
+ expect(page.find("h1").text).to include "Review the logs you want to delete"
+ end
+
+ it "displays an error message" do
+ expect(page).to have_selector(".govuk-error-summary", text: "Select at least one log to delete or press cancel to return")
+ end
+
+ it "renders the table with all checkboxes unchecked" do
+ checkboxes = page.find_all("tbody tr").map { |row| row.find("input") }
+ checkboxes.each do |checkbox|
+ expect(checkbox).not_to be_checked
+ end
+ end
+ end
+ end
+
+ describe "DELETE organisations/delete-sales-logs" do
+ let(:log_1) { create(:sales_log, :in_progress, owning_organisation: organisation) }
+ let(:log_2) { create(:sales_log, :completed, owning_organisation: organisation) }
+ let(:params) { { ids: [log_1.id, log_2.id] } }
+
+ before do
+ delete delete_sales_logs_organisation_path(id: organisation, params:)
+ end
+
+ it "deletes the logs provided" do
+ log_1.reload
+ expect(log_1.status).to eq "deleted"
+ expect(log_1.discarded_at).not_to be nil
+ log_2.reload
+ expect(log_2.status).to eq "deleted"
+ expect(log_2.discarded_at).not_to be nil
+ end
+
+ it "redirects to the sales log index for that organisation and displays a notice that the logs have been deleted" do
+ expect(response).to redirect_to sales_logs_organisation_path(id: organisation)
+ follow_redirect!
+ expect(page).to have_selector(".govuk-notification-banner--success")
+ expect(page).to have_selector(".govuk-notification-banner--success", text: "2 logs have been deleted")
+ end
+ end
+ end
+end
diff --git a/spec/requests/lettings_logs_controller_spec.rb b/spec/requests/lettings_logs_controller_spec.rb
index 33108e465..50c4d6763 100644
--- a/spec/requests/lettings_logs_controller_spec.rb
+++ b/spec/requests/lettings_logs_controller_spec.rb
@@ -246,10 +246,39 @@ RSpec.describe LettingsLogsController, type: :request do
end
it "does not have a button for creating sales logs" do
- get "/lettings-logs", headers:, params: {}
+ get lettings_logs_path, headers:, params: {}
page.assert_selector(".govuk-button", text: "Create a new sales log", count: 0)
page.assert_selector(".govuk-button", text: "Create a new lettings log", count: 1)
end
+
+ context "and the state of filters and search is such that display_delete_logs returns true" do
+ before do
+ allow_any_instance_of(LogListHelper).to receive(:display_delete_logs?).and_return(true) # rubocop:disable RSpec/AnyInstance
+ end
+
+ it "displays the delete logs button with the correct path if there are logs visibile" do
+ get lettings_logs_path(search: "LC783")
+ expect(page).to have_link "Delete logs", href: delete_logs_lettings_logs_path(search: "LC783")
+ end
+
+ it "does not display the delete logs button if there are no logs displayed" do
+ LettingsLog.destroy_all
+ get lettings_logs_path
+ expect(page).not_to have_link "Delete logs"
+ end
+ end
+
+ context "and the state of filters and search is such that display_delete_logs returns false" do
+ before do
+ allow_any_instance_of(LogListHelper).to receive(:display_delete_logs?).and_return(false) # rubocop:disable RSpec/AnyInstance
+ end
+
+ it "does not display the delete logs button even if there are logs displayed" do
+ get lettings_logs_path
+ expect(page).to have_selector "article.app-log-summary"
+ expect(page).not_to have_link "Delete logs"
+ end
+ end
end
context "when the user is a customer support user" do
@@ -347,7 +376,6 @@ RSpec.describe LettingsLogsController, type: :request do
Timecop.freeze(2022, 3, 1) do
example.run
end
- Timecop.return
end
let!(:lettings_log_2021) do
@@ -538,7 +566,7 @@ RSpec.describe LettingsLogsController, type: :request do
end
end
- context "when the user is not a customer support user" do
+ context "when the user is a data provider" do
before do
sign_in user
end
@@ -1348,7 +1376,7 @@ RSpec.describe LettingsLogsController, type: :request do
context "when user not authorised" do
let(:user) { create(:user) }
- it "returns 404" do
+ it "returns 401" do
delete_request
expect(response).to have_http_status(:unauthorized)
end
@@ -1400,7 +1428,7 @@ RSpec.describe LettingsLogsController, type: :request do
end
end
- describe "GET #csv-download" do
+ describe "GET csv-download" do
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:user) { FactoryBot.create(:user) }
let(:headers) { { "Accept" => "text/html" } }
@@ -1482,7 +1510,7 @@ RSpec.describe LettingsLogsController, type: :request do
end
end
- describe "POST #email-csv" do
+ describe "POST email-csv" do
let(:other_organisation) { FactoryBot.create(:organisation) }
let(:user) { FactoryBot.create(:user, :support) }
diff --git a/spec/requests/organisations_controller_spec.rb b/spec/requests/organisations_controller_spec.rb
index 466e7522e..a3d5ac41e 100644
--- a/spec/requests/organisations_controller_spec.rb
+++ b/spec/requests/organisations_controller_spec.rb
@@ -1221,210 +1221,228 @@ RSpec.describe OrganisationsController, type: :request do
end
end
end
- end
- end
-
- context "when the user is a support user" do
- let(:user) { create(:user, :support) }
- before do
- allow(user).to receive(:need_two_factor_authentication?).and_return(false)
- sign_in user
- end
-
- context "when they view the lettings logs tab" do
- before do
- create(:lettings_log, owning_organisation: organisation)
- end
-
- it "has CSV download buttons with the correct paths if at least 1 log exists" do
- get "/organisations/#{organisation.id}/lettings-logs"
- expect(page).to have_link("Download (CSV)", href: "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=false")
- expect(page).to have_link("Download (CSV, codes only)", href: "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=true")
- end
-
- context "when you download the CSV" do
- let(:other_organisation) { create(:organisation) }
+ context "when they view the lettings logs tab" do
+ let(:tenancycode) { "42" }
before do
- 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)
+ create(:lettings_log, owning_organisation: organisation, tenancycode:)
end
- it "only includes logs from that organisation" do
- get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=false"
+ context "when there is at least one log visible" do
+ before do
+ get lettings_logs_organisation_path(organisation, search: tenancycode)
+ end
+
+ it "shows the delete logs button with the correct path" do
+ expect(page).to have_link "Delete logs", href: delete_lettings_logs_organisation_path(search: tenancycode)
+ end
- expect(page).to have_text("You've selected 3 logs.")
+ it "has CSV download buttons with the correct paths" do
+ expect(page).to have_link "Download (CSV)", href: lettings_logs_csv_download_organisation_path(organisation, codes_only: false, search: tenancycode)
+ expect(page).to have_link "Download (CSV, codes only)", href: lettings_logs_csv_download_organisation_path(organisation, codes_only: true, search: tenancycode)
+ end
end
- it "provides the organisation to the mail job" do
- expect {
- post "/organisations/#{organisation.id}/lettings-logs/email-csv?status[]=completed&codes_only=false", headers:, params: {}
- }.to enqueue_job(EmailCsvJob).with(user, nil, { "status" => %w[completed] }, false, organisation, false)
- end
+ context "when there are no visible logs" do
+ before do
+ LettingsLog.destroy_all
+ get lettings_logs_organisation_path(organisation)
+ end
- it "provides the export type to the mail job" do
- codes_only_export_type = false
- expect {
- post "/organisations/#{organisation.id}/lettings-logs/email-csv?codes_only=#{codes_only_export_type}", headers:, params: {}
- }.to enqueue_job(EmailCsvJob).with(user, nil, {}, false, organisation, codes_only_export_type)
- codes_only_export_type = true
- expect {
- post "/organisations/#{organisation.id}/lettings-logs/email-csv?codes_only=#{codes_only_export_type}", headers:, params: {}
- }.to enqueue_job(EmailCsvJob).with(user, nil, {}, false, organisation, codes_only_export_type)
+ it "does not show the delete logs button " do
+ expect(page).not_to have_link "Delete logs"
+ end
+
+ it "does not show the csv download buttons" do
+ expect(page).not_to have_link "Download (CSV)"
+ expect(page).not_to have_link "Download (CSV, codes only)"
+ end
end
- end
- end
- context "when they view the sales logs tab" do
- before do
- create(:sales_log, owning_organisation: organisation)
- end
+ context "when you download the CSV" do
+ let(:other_organisation) { create(:organisation) }
- it "has CSV download buttons with the correct paths if at least 1 log exists" do
- get "/organisations/#{organisation.id}/sales-logs"
- expect(page).to have_link("Download (CSV)", href: "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=false")
- expect(page).to have_link("Download (CSV, codes only)", href: "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=true")
- end
+ before do
+ 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
- context "when you download the CSV" do
- let(:other_organisation) { create(:organisation) }
+ it "only includes logs from that organisation" do
+ get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=false"
- before do
- 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
+ expect(page).to have_text("You've selected 3 logs.")
+ end
- it "only includes logs from that organisation" do
- get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=false"
+ it "provides the organisation to the mail job" do
+ expect {
+ post "/organisations/#{organisation.id}/lettings-logs/email-csv?status[]=completed&codes_only=false", headers:, params: {}
+ }.to enqueue_job(EmailCsvJob).with(user, nil, { "status" => %w[completed] }, false, organisation, false)
+ end
- expect(page).to have_text("You've selected 3 logs.")
+ it "provides the export type to the mail job" do
+ codes_only_export_type = false
+ expect {
+ post "/organisations/#{organisation.id}/lettings-logs/email-csv?codes_only=#{codes_only_export_type}", headers:, params: {}
+ }.to enqueue_job(EmailCsvJob).with(user, nil, {}, false, organisation, codes_only_export_type)
+ codes_only_export_type = true
+ expect {
+ post "/organisations/#{organisation.id}/lettings-logs/email-csv?codes_only=#{codes_only_export_type}", headers:, params: {}
+ }.to enqueue_job(EmailCsvJob).with(user, nil, {}, false, organisation, codes_only_export_type)
+ end
end
+ end
- it "provides the organisation to the mail job" do
- expect {
- post "/organisations/#{organisation.id}/sales-logs/email-csv?status[]=completed&codes_only=false", headers:, params: {}
- }.to enqueue_job(EmailCsvJob).with(user, nil, { "status" => %w[completed] }, false, organisation, false, "sales")
+ context "when they view the sales logs tab" do
+ before do
+ create(:sales_log, owning_organisation: organisation)
end
- it "provides the log type to the mail job" do
- log_type = "sales"
- expect {
- post "/organisations/#{organisation.id}/sales-logs/email-csv?status[]=completed&codes_only=false", headers:, params: {}
- }.to enqueue_job(EmailCsvJob).with(user, nil, { "status" => %w[completed] }, false, organisation, false, log_type)
+ it "has CSV download buttons with the correct paths if at least 1 log exists" do
+ get "/organisations/#{organisation.id}/sales-logs"
+ expect(page).to have_link("Download (CSV)", href: "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=false")
+ expect(page).to have_link("Download (CSV, codes only)", href: "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=true")
end
- it "provides the export type to the mail job" do
- codes_only_export_type = false
- expect {
- post "/organisations/#{organisation.id}/sales-logs/email-csv?codes_only=#{codes_only_export_type}", headers:, params: {}
- }.to enqueue_job(EmailCsvJob).with(user, nil, {}, false, organisation, codes_only_export_type, "sales")
- codes_only_export_type = true
- expect {
- post "/organisations/#{organisation.id}/sales-logs/email-csv?codes_only=#{codes_only_export_type}", headers:, params: {}
- }.to enqueue_job(EmailCsvJob).with(user, nil, {}, false, organisation, codes_only_export_type, "sales")
- end
- end
- end
+ context "when you download the CSV" do
+ let(:other_organisation) { create(:organisation) }
- describe "GET #download_lettings_csv" do
- it "renders a page with the correct header" do
- get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=false", headers:, params: {}
- header = page.find_css("h1")
- expect(header.text).to include("Download CSV")
- end
+ before do
+ 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 "renders a form with the correct target containing a button with the correct text" do
- get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=false", headers:, params: {}
- form = page.find("form.button_to")
- expect(form[:method]).to eq("post")
- expect(form[:action]).to eq("/organisations/#{organisation.id}/lettings-logs/email-csv")
- expect(form).to have_button("Send email")
- end
+ it "only includes logs from that organisation" do
+ get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=false"
- it "when codes_only query parameter is false, form contains hidden field with correct value" do
- codes_only = false
- get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=#{codes_only}", headers:, params: {}
- hidden_field = page.find("form.button_to").find_field("codes_only", type: "hidden")
- expect(hidden_field.value).to eq(codes_only.to_s)
- end
+ expect(page).to have_text("You've selected 3 logs.")
+ end
- it "when codes_only query parameter is true, form contains hidden field with correct value" do
- codes_only = true
- get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=#{codes_only}", headers:, params: {}
- hidden_field = page.find("form.button_to").find_field("codes_only", type: "hidden")
- expect(hidden_field.value).to eq(codes_only.to_s)
- end
+ it "provides the organisation to the mail job" do
+ expect {
+ post "/organisations/#{organisation.id}/sales-logs/email-csv?status[]=completed&codes_only=false", headers:, params: {}
+ }.to enqueue_job(EmailCsvJob).with(user, nil, { "status" => %w[completed] }, false, organisation, false, "sales")
+ end
- it "when query string contains search parameter, form contains hidden field with correct value" do
- search_term = "blam"
- get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=true&search=#{search_term}", headers:, params: {}
- hidden_field = page.find("form.button_to").find_field("search", type: "hidden")
- expect(hidden_field.value).to eq(search_term)
- end
- end
+ it "provides the log type to the mail job" do
+ log_type = "sales"
+ expect {
+ post "/organisations/#{organisation.id}/sales-logs/email-csv?status[]=completed&codes_only=false", headers:, params: {}
+ }.to enqueue_job(EmailCsvJob).with(user, nil, { "status" => %w[completed] }, false, organisation, false, log_type)
+ end
- describe "GET #download_sales_csv" do
- it "renders a page with the correct header" do
- get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=false", headers:, params: {}
- header = page.find_css("h1")
- expect(header.text).to include("Download CSV")
+ it "provides the export type to the mail job" do
+ codes_only_export_type = false
+ expect {
+ post "/organisations/#{organisation.id}/sales-logs/email-csv?codes_only=#{codes_only_export_type}", headers:, params: {}
+ }.to enqueue_job(EmailCsvJob).with(user, nil, {}, false, organisation, codes_only_export_type, "sales")
+ codes_only_export_type = true
+ expect {
+ post "/organisations/#{organisation.id}/sales-logs/email-csv?codes_only=#{codes_only_export_type}", headers:, params: {}
+ }.to enqueue_job(EmailCsvJob).with(user, nil, {}, false, organisation, codes_only_export_type, "sales")
+ end
+ end
end
- it "renders a form with the correct target containing a button with the correct text" do
- get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=false", headers:, params: {}
- form = page.find("form.button_to")
- expect(form[:method]).to eq("post")
- expect(form[:action]).to eq("/organisations/#{organisation.id}/sales-logs/email-csv")
- expect(form).to have_button("Send email")
- end
+ describe "GET #download_lettings_csv" do
+ it "renders a page with the correct header" do
+ get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=false", headers:, params: {}
+ header = page.find_css("h1")
+ expect(header.text).to include("Download CSV")
+ end
- it "when codes_only query parameter is false, form contains hidden field with correct value" do
- codes_only = false
- get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=#{codes_only}", headers:, params: {}
- hidden_field = page.find("form.button_to").find_field("codes_only", type: "hidden")
- expect(hidden_field.value).to eq(codes_only.to_s)
- end
+ it "renders a form with the correct target containing a button with the correct text" do
+ get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=false", headers:, params: {}
+ form = page.find("form.button_to")
+ expect(form[:method]).to eq("post")
+ expect(form[:action]).to eq("/organisations/#{organisation.id}/lettings-logs/email-csv")
+ expect(form).to have_button("Send email")
+ end
- it "when codes_only query parameter is true, form contains hidden field with correct value" do
- codes_only = true
- get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=#{codes_only}", headers:, params: {}
- hidden_field = page.find("form.button_to").find_field("codes_only", type: "hidden")
- expect(hidden_field.value).to eq(codes_only.to_s)
- end
+ it "when codes_only query parameter is false, form contains hidden field with correct value" do
+ codes_only = false
+ get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=#{codes_only}", headers:, params: {}
+ hidden_field = page.find("form.button_to").find_field("codes_only", type: "hidden")
+ expect(hidden_field.value).to eq(codes_only.to_s)
+ end
- it "when query string contains search parameter, form contains hidden field with correct value" do
- search_term = "blam"
- get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=true&search=#{search_term}", headers:, params: {}
- hidden_field = page.find("form.button_to").find_field("search", type: "hidden")
- expect(hidden_field.value).to eq(search_term)
- end
- end
+ it "when codes_only query parameter is true, form contains hidden field with correct value" do
+ codes_only = true
+ get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=#{codes_only}", headers:, params: {}
+ hidden_field = page.find("form.button_to").find_field("codes_only", type: "hidden")
+ expect(hidden_field.value).to eq(codes_only.to_s)
+ end
- context "when they view the users tab" do
- before do
- get "/organisations/#{organisation.id}/users"
+ it "when query string contains search parameter, form contains hidden field with correct value" do
+ search_term = "blam"
+ get "/organisations/#{organisation.id}/lettings-logs/csv-download?codes_only=true&search=#{search_term}", headers:, params: {}
+ hidden_field = page.find("form.button_to").find_field("search", type: "hidden")
+ expect(hidden_field.value).to eq(search_term)
+ end
end
- it "has a CSV download button with the correct path" do
- expect(page).to have_link("Download (CSV)", href: "/organisations/#{organisation.id}/users.csv")
- end
+ describe "GET #download_sales_csv" do
+ it "renders a page with the correct header" do
+ get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=false", headers:, params: {}
+ header = page.find_css("h1")
+ expect(header.text).to include("Download CSV")
+ end
- context "when you download the CSV" do
- let(:headers) { { "Accept" => "text/csv" } }
- let(:other_organisation) { create(:organisation) }
+ it "renders a form with the correct target containing a button with the correct text" do
+ get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=false", headers:, params: {}
+ form = page.find("form.button_to")
+ expect(form[:method]).to eq("post")
+ expect(form[:action]).to eq("/organisations/#{organisation.id}/sales-logs/email-csv")
+ expect(form).to have_button("Send email")
+ end
+
+ it "when codes_only query parameter is false, form contains hidden field with correct value" do
+ codes_only = false
+ get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=#{codes_only}", headers:, params: {}
+ hidden_field = page.find("form.button_to").find_field("codes_only", type: "hidden")
+ expect(hidden_field.value).to eq(codes_only.to_s)
+ end
+
+ it "when codes_only query parameter is true, form contains hidden field with correct value" do
+ codes_only = true
+ get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=#{codes_only}", headers:, params: {}
+ hidden_field = page.find("form.button_to").find_field("codes_only", type: "hidden")
+ expect(hidden_field.value).to eq(codes_only.to_s)
+ end
+ it "when query string contains search parameter, form contains hidden field with correct value" do
+ search_term = "blam"
+ get "/organisations/#{organisation.id}/sales-logs/csv-download?codes_only=true&search=#{search_term}", headers:, params: {}
+ hidden_field = page.find("form.button_to").find_field("search", type: "hidden")
+ expect(hidden_field.value).to eq(search_term)
+ end
+ end
+
+ context "when they view the users tab" do
before do
- create_list(:user, 3, organisation:)
- create_list(:user, 2, organisation: other_organisation)
+ get "/organisations/#{organisation.id}/users"
+ end
+
+ it "has a CSV download button with the correct path" do
+ expect(page).to have_link("Download (CSV)", href: "/organisations/#{organisation.id}/users.csv")
end
- it "only includes users from that organisation" do
- get "/organisations/#{other_organisation.id}/users", headers:, params: {}
- csv = CSV.parse(response.body)
- expect(csv.count).to eq(other_organisation.users.count + 1)
+ context "when you download the CSV" do
+ let(:headers) { { "Accept" => "text/csv" } }
+ let(:other_organisation) { create(:organisation) }
+
+ before do
+ create_list(:user, 3, organisation:)
+ create_list(:user, 2, organisation: other_organisation)
+ end
+
+ it "only includes users from that organisation" do
+ get "/organisations/#{other_organisation.id}/users", headers:, params: {}
+ csv = CSV.parse(response.body)
+ expect(csv.count).to eq(other_organisation.users.count + 1)
+ end
end
end
end
diff --git a/spec/requests/sales_logs_controller_spec.rb b/spec/requests/sales_logs_controller_spec.rb
index 30d756356..ff9130e75 100644
--- a/spec/requests/sales_logs_controller_spec.rb
+++ b/spec/requests/sales_logs_controller_spec.rb
@@ -112,9 +112,11 @@ RSpec.describe SalesLogsController, type: :request do
let(:user) { FactoryBot.create(:user) }
let(:organisation) { user.organisation }
let(:other_organisation) { FactoryBot.create(:organisation) }
+ let(:purchaser_code) { "coop123" }
let!(:sales_log) do
FactoryBot.create(
:sales_log,
+ purchid: purchaser_code,
owning_organisation: organisation,
)
end
@@ -175,6 +177,36 @@ RSpec.describe SalesLogsController, type: :request do
end
end
+ context "and the state of filters and search is such that display_delete_logs returns true" do
+ before do
+ allow_any_instance_of(LogListHelper).to receive(:display_delete_logs?).and_return(true) # rubocop:disable RSpec/AnyInstance
+ end
+
+ it "displays the delete logs button with the correct path if there are logs visibile" do
+ get sales_logs_path(search: purchaser_code)
+ expect(page).to have_link "Delete logs", href: delete_logs_sales_logs_path(search: purchaser_code)
+ end
+
+ it "does not display the delete logs button if there are no logs displayed" do
+ SalesLog.destroy_all
+ get sales_logs_path(search: "gibberish_e9o87tvbyc4875g")
+ expect(page).not_to have_selector "article.app-log-summary"
+ expect(page).not_to have_link "Delete logs"
+ end
+ end
+
+ context "and the state of filters and search is such that display_delete_logs returns false" do
+ before do
+ allow_any_instance_of(LogListHelper).to receive(:display_delete_logs?).and_return(false) # rubocop:disable RSpec/AnyInstance
+ end
+
+ it "does not display the delete logs button even if there are logs displayed" do
+ get sales_logs_path
+ expect(page).to have_selector "article.app-log-summary"
+ expect(page).not_to have_link "Delete logs"
+ end
+ end
+
context "when there is a pending log" do
let!(:invisible_log) do
FactoryBot.create(
diff --git a/spec/views/form/page_view_spec.rb b/spec/views/form/page_view_spec.rb
index 074a7fd6a..614f56562 100644
--- a/spec/views/form/page_view_spec.rb
+++ b/spec/views/form/page_view_spec.rb
@@ -22,8 +22,6 @@ RSpec.describe "form/page" do
Singleton.__init__(FormHandler)
example.run
end
- Timecop.return
- Singleton.__init__(FormHandler)
end
before do
@@ -45,7 +43,7 @@ RSpec.describe "form/page" do
context "with a page containing a description" do
let(:description) { "Test description with link." }
let(:page_attributes) { { description: } }
- let(:expected_html) { 'Test description with link.
' }
+ let(:expected_html) { "#{description}
" }
it "renders the description" do
expect(rendered).to match(expected_html)
diff --git a/spec/views/logs/delete_logs_spec.rb b/spec/views/logs/delete_logs_spec.rb
new file mode 100644
index 000000000..6dcb8c423
--- /dev/null
+++ b/spec/views/logs/delete_logs_spec.rb
@@ -0,0 +1,121 @@
+require "rails_helper"
+
+RSpec.describe "logs/delete_logs.html.erb" do
+ let(:user) { create(:user, :support, name: "Dirk Gently") }
+ let(:lettings_log_1) { create(:lettings_log, tenancycode: "Holistic", propcode: "Detective Agency", created_by: user) }
+ let(:lettings_logs) { [lettings_log_1] }
+ let(:paths) do
+ {
+ delete_confirmation_path: delete_logs_confirmation_lettings_logs_path,
+ back_to_logs_path: lettings_logs_path(search: "search_term"),
+ delete_path: delete_logs_lettings_logs_path,
+ }
+ end
+ let(:delete_logs_form) { Forms::DeleteLogsForm.new(log_type: :lettings, current_user: user, **paths) }
+
+ before do
+ sign_in user
+ allow(FilterManager).to receive(:filter_logs).and_return lettings_logs
+ assign(:delete_logs_form, delete_logs_form)
+ end
+
+ it "has the correct h1 content" do
+ render
+ fragment = Capybara::Node::Simple.new(rendered)
+ h1 = fragment.find("h1")
+ expect(h1.text).to include "Review the logs you want to delete"
+ end
+
+ context "when there is one log to delete" do
+ it "shows the informative text in the singular" do
+ render
+ fragment = Capybara::Node::Simple.new(rendered)
+ info_text = fragment.first("p").text
+ expect(info_text).to eq "You've selected 1 log to delete"
+ end
+ end
+
+ context "when there is more than one log to delete" do
+ let(:lettings_log_2) { create(:lettings_log, tenancycode: "01-354", propcode: "9112") }
+
+ before do
+ lettings_logs << lettings_log_2
+ allow(FilterManager).to receive(:filter_logs).and_return lettings_logs
+ delete_logs_form = Forms::DeleteLogsForm.new(log_type: :lettings, current_user: user, **paths)
+ assign(:delete_logs_form, delete_logs_form)
+ end
+
+ it "shows the informative text in the plural" do
+ render
+ fragment = Capybara::Node::Simple.new(rendered)
+ info_text = fragment.first("p").text
+ expect(info_text).to eq "You've selected #{lettings_logs.count} logs to delete"
+ end
+ end
+
+ context "when the table contains lettings logs" do
+ it "shows the correct headers in the table" do
+ render
+ fragment = Capybara::Node::Simple.new(rendered)
+ headers = fragment.find_all("table thead tr th").map(&:text)
+ expect(headers).to eq ["Log ID", "Tenancy code", "Property reference", "Status", "Delete?"]
+ end
+
+ it "shows the correct information in each row" do
+ render
+ fragment = Capybara::Node::Simple.new(rendered)
+ row_data = fragment.find_all("table tbody tr td").map(&:text)[0...-1].map(&:strip)
+ expect(row_data).to eq [lettings_log_1.id.to_s, lettings_log_1.tenancycode, lettings_log_1.propcode, lettings_log_1.status.humanize.capitalize]
+ end
+
+ it "links to the relevant log on each row" do
+ render
+ fragment = Capybara::Node::Simple.new(rendered)
+ first_cell = fragment.first("table tbody tr td")
+ expect(first_cell).to have_link lettings_log_1.id.to_s, href: lettings_log_path(lettings_log_1)
+ end
+ end
+
+ context "when the table contains sales logs" do
+ let(:sales_log) { create(:sales_log, purchid: "Interconnectedness", saledate: Time.zone.today, created_by: user) }
+ let(:sales_logs) { [sales_log] }
+ let(:delete_logs_form_sales) { Forms::DeleteLogsForm.new(log_type: :sales, current_user: user, **paths) }
+
+ before do
+ sign_in user
+ allow(FilterManager).to receive(:filter_logs).and_return sales_logs
+ assign(:delete_logs_form, delete_logs_form_sales)
+ end
+
+ it "shows the correct headers in the table" do
+ render
+ fragment = Capybara::Node::Simple.new(rendered)
+ headers = fragment.find_all("table thead tr th").map(&:text)
+ expect(headers).to eq ["Log ID", "Purchaser code", "Sale completion date", "Status", "Delete?"]
+ end
+
+ it "shows the correct information in each row" do
+ render
+ fragment = Capybara::Node::Simple.new(rendered)
+ row_data = fragment.find_all("table tbody tr td").map(&:text)[0...-1].map(&:strip)
+ expect(row_data).to eq [sales_log.id.to_s, sales_log.purchid, sales_log.saledate.to_formatted_s(:govuk_date), sales_log.status.humanize.capitalize]
+ end
+
+ it "links to the relevant log on each row" do
+ render
+ fragment = Capybara::Node::Simple.new(rendered)
+ first_cell = fragment.first("table tbody tr td")
+ expect(first_cell).to have_link sales_log.id.to_s, href: sales_log_path(sales_log)
+ end
+ end
+
+ it "shows a checkbox with the correct hidden label in the final cell of each row" do
+ render
+ fragment = Capybara::Node::Simple.new(rendered)
+ final_cell = fragment.find_all("table tbody tr td")[-1]
+ expect(final_cell.find("input")[:type]).to eq "checkbox"
+ checkbox_label = final_cell.find("label span")
+ expect(checkbox_label.text).to eq lettings_log_1.id.to_s
+ expect(checkbox_label[:class]).to include "govuk-visually-hidden"
+ end
+end
From a4ebcc3f460025c954903c6c88d4779e2de1e678 Mon Sep 17 00:00:00 2001
From: Arthur Campbell <51094020+arfacamble@users.noreply.github.com>
Date: Thu, 15 Jun 2023 15:02:19 +0100
Subject: [PATCH 03/19] CLDC-2449 production recursion error on finding url for
next incomplete section (#1698)
* protect against stack level errors in the case where a log has the status in progress but due to changes in the form should now have the status completed
further update the lettings log factory as the :completed trait was producing logs that were not copmlete
* add tests for calculate_status that implicitly also check if the factory traits for copmleted are working
also some minor renaming and refactoring
* correct linting errors
* remove status update but protect against error resurfacing in the future
---
app/models/form.rb | 2 +-
app/models/log.rb | 8 ++++----
spec/factories/lettings_log.rb | 2 ++
spec/models/form_spec.rb | 16 ++++++++++++++--
spec/models/log_spec.rb | 22 ++++++++++++++++++++++
5 files changed, 43 insertions(+), 7 deletions(-)
diff --git a/app/models/form.rb b/app/models/form.rb
index 16c896e4c..7a55b2e0d 100644
--- a/app/models/form.rb
+++ b/app/models/form.rb
@@ -124,7 +124,7 @@ class Form
def next_incomplete_section_redirect_path(subsection, log)
subsection_ids = subsections.map(&:id)
- if log.status == "completed"
+ if log.status == "completed" || log.calculate_status == "completed" # if a log's status in in progress but then fields are made optional, all its subsections are complete, resulting in a stack error
return first_question_in_last_subsection(subsection_ids)
end
diff --git a/app/models/log.rb b/app/models/log.rb
index 452aa67c6..c02fdb02d 100644
--- a/app/models/log.rb
+++ b/app/models/log.rb
@@ -139,9 +139,9 @@ class Log < ApplicationRecord
def calculate_status
return "deleted" if discarded_at.present?
- if all_fields_completed? && errors.empty?
+ if all_subsections_completed? && errors.empty?
"completed"
- elsif all_fields_nil?
+ elsif all_subsections_unstarted?
"not_started"
else
"in_progress"
@@ -210,11 +210,11 @@ private
self.status = calculate_status
end
- def all_fields_completed?
+ def all_subsections_completed?
form.subsections.all? { |subsection| subsection.complete?(self) || subsection.not_displayed_in_tasklist?(self) }
end
- def all_fields_nil?
+ def all_subsections_unstarted?
not_started_statuses = %i[not_started cannot_start_yet]
form.subsections.all? { |subsection| not_started_statuses.include? subsection.status(self) }
end
diff --git a/spec/factories/lettings_log.rb b/spec/factories/lettings_log.rb
index ec119fbdc..8cb459117 100644
--- a/spec/factories/lettings_log.rb
+++ b/spec/factories/lettings_log.rb
@@ -146,6 +146,8 @@ FactoryBot.define do
joint { 3 }
address_line1 { "fake address" }
town_or_city { "London" }
+ ppcodenk { 0 }
+ tshortfall_known { 1 }
end
trait :export do
tenancycode { "987654" }
diff --git a/spec/models/form_spec.rb b/spec/models/form_spec.rb
index b63d05f62..439cd24b1 100644
--- a/spec/models/form_spec.rb
+++ b/spec/models/form_spec.rb
@@ -190,8 +190,6 @@ RSpec.describe Form, type: :model do
FormHandler.instance.use_real_forms!
example.run
-
- FormHandler.instance.use_fake_forms!
end
it "finds the path to the section after" do
@@ -202,6 +200,20 @@ RSpec.describe Form, type: :model do
expect(form.next_incomplete_section_redirect_path(subsection, lettings_log)).to eq("joint")
end
end
+
+ context "when a log has status in progress but all subsections are complete" do
+ let(:lettings_log) { build(:lettings_log, :completed, status: "in_progress") }
+ let(:subsection) { form.get_subsection("setup") }
+
+ before do
+ Timecop.return
+ FormHandler.instance.use_real_forms!
+ end
+
+ it "does not raise a Stack Error" do
+ expect { form.next_incomplete_section_redirect_path(subsection, lettings_log) }.not_to raise_error
+ end
+ end
end
describe "#reset_not_routed_questions_and_invalid_answers" do
diff --git a/spec/models/log_spec.rb b/spec/models/log_spec.rb
index 0cba24086..fb686e744 100644
--- a/spec/models/log_spec.rb
+++ b/spec/models/log_spec.rb
@@ -5,4 +5,26 @@ RSpec.describe Log, type: :model do
expect(SalesLog).to be < described_class
expect(LettingsLog).to be < described_class
end
+
+ describe "#calculate_status" do
+ it "returns the correct status for a completed sales log" do
+ complete_sales_log = create(:sales_log, :completed, status: nil)
+ expect(complete_sales_log.calculate_status).to eq "completed"
+ end
+
+ it "returns the correct status for an in progress sales log" do
+ in_progress_sales_log = create(:sales_log, :in_progress, status: nil)
+ expect(in_progress_sales_log.calculate_status).to eq "in_progress"
+ end
+
+ it "returns the correct status for a completed lettings log" do
+ complete_lettings_log = create(:lettings_log, :completed, status: nil)
+ expect(complete_lettings_log.calculate_status).to eq "completed"
+ end
+
+ it "returns the correct status for an in progress lettings log" do
+ in_progress_lettings_log = create(:lettings_log, :in_progress, status: nil)
+ expect(in_progress_lettings_log.calculate_status).to eq "in_progress"
+ end
+ end
end
From c53f4a770b5a43e40d0f6ffaa604ef087ec48c47 Mon Sep 17 00:00:00 2001
From: Arthur Campbell <51094020+arfacamble@users.noreply.github.com>
Date: Thu, 15 Jun 2023 15:06:38 +0100
Subject: [PATCH 04/19] Remove feature toggle for schemes and locations (#1700)
* remove scheme toggle
* remove location toggle
* minor linting complaint
---
app/helpers/locations_helper.rb | 9 +-
app/helpers/schemes_helper.rb | 5 +-
app/services/feature_toggle.rb | 8 --
app/views/locations/index.html.erb | 148 ++++++---------------
app/views/locations/show.html.erb | 6 +-
app/views/schemes/_scheme_list.html.erb | 22 +--
app/views/schemes/show.html.erb | 37 +++---
spec/features/schemes_spec.rb | 17 ---
spec/helpers/schemes_helper_spec.rb | 8 --
spec/requests/locations_controller_spec.rb | 26 ----
10 files changed, 69 insertions(+), 217 deletions(-)
diff --git a/app/helpers/locations_helper.rb b/app/helpers/locations_helper.rb
index 7e38ed36c..58fffd2e0 100644
--- a/app/helpers/locations_helper.rb
+++ b/app/helpers/locations_helper.rb
@@ -24,7 +24,7 @@ module LocationsHelper
end
def display_location_attributes(location)
- base_attributes = [
+ [
{ name: "Postcode", value: location.postcode, attribute: "postcode" },
{ name: "Location name", value: location.name, attribute: "name" },
{ name: "Local authority", value: formatted_local_authority_timeline(location, "name"), attribute: "local_authority" },
@@ -33,13 +33,8 @@ module LocationsHelper
{ name: "Mobility standards", value: location.mobility_type, attribute: "mobility_standards" },
{ name: "Location code", value: formatted_local_authority_timeline(location, "code"), attribute: "location_code" },
{ name: "Availability", value: location_availability(location), attribute: "availability" },
+ { name: "Status", value: location.status, attribute: "status" },
]
-
- if FeatureToggle.location_toggle_enabled?
- base_attributes.append({ name: "Status", value: location.status, attribute: "status" })
- end
-
- base_attributes
end
def display_location_attributes_for_check_answers(location)
diff --git a/app/helpers/schemes_helper.rb b/app/helpers/schemes_helper.rb
index 0503f88b3..50bd4efb2 100644
--- a/app/helpers/schemes_helper.rb
+++ b/app/helpers/schemes_helper.rb
@@ -14,12 +14,9 @@ module SchemesHelper
{ name: "Level of support given", value: scheme.support_type },
{ name: "Intended length of stay", value: scheme.intended_stay },
{ name: "Availability", value: scheme_availability(scheme) },
+ { name: "Status", value: status_tag(scheme.status) },
]
- if FeatureToggle.scheme_toggle_enabled?
- base_attributes.append({ name: "Status", value: status_tag(scheme.status) })
- end
-
if user.data_coordinator?
base_attributes.delete_if { |item| item[:name] == "Housing stock owned by" }
end
diff --git a/app/services/feature_toggle.rb b/app/services/feature_toggle.rb
index 88e1d17ef..2690d1e06 100644
--- a/app/services/feature_toggle.rb
+++ b/app/services/feature_toggle.rb
@@ -12,14 +12,6 @@ class FeatureToggle
Rails.env.production? || Rails.env.test? || Rails.env.staging? || Rails.env.review?
end
- def self.scheme_toggle_enabled?
- true
- end
-
- def self.location_toggle_enabled?
- true
- end
-
def self.bulk_upload_duplicate_log_check_enabled?
!Rails.env.staging?
end
diff --git a/app/views/locations/index.html.erb b/app/views/locations/index.html.erb
index 85ae27fed..e974d7dfb 100644
--- a/app/views/locations/index.html.erb
+++ b/app/views/locations/index.html.erb
@@ -10,120 +10,60 @@
<%= render partial: "organisations/headings", locals: { main: @scheme.service_name, sub: nil } %>
-<% if FeatureToggle.location_toggle_enabled? %>
-
-
-<% end %>
- <%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %>
+
+
+ <%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %>
-
Locations
+ Locations
- <%= render SearchComponent.new(current_user:, search_label: "Search by location name or postcode", value: @searched) %>
+ <%= render SearchComponent.new(current_user:, search_label: "Search by location name or postcode", value: @searched) %>
- <%= govuk_section_break(visible: true, size: "m") %>
-<% if FeatureToggle.location_toggle_enabled? %>
-
+ <%= govuk_section_break(visible: true, size: "m") %>
-<% end %>
-
-<% if FeatureToggle.location_toggle_enabled? %>
-
-
- <%= govuk_table do |table| %>
- <%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
- <%= render(SearchResultCaptionComponent.new(searched: @searched, count: @pagy.count, item_label:, total_count: @total_count, item: "locations", path: request.path)) %>
- <% end %>
- <%= table.head do |head| %>
- <%= head.row do |row| %>
- <% row.cell(header: true, text: "Postcode", html_attributes: {
- scope: "col",
- }) %>
- <% row.cell(header: true, text: "Name", html_attributes: {
- scope: "col",
- }) %>
- <% row.cell(header: true, text: "Location code", html_attributes: {
- scope: "col",
- }) %>
- <% row.cell(header: true, text: "Status", html_attributes: {
- scope: "col",
- }) %>
- <% end %>
- <% end %>
- <% @locations.each do |location| %>
- <%= table.body do |body| %>
- <%= body.row do |row| %>
- <% row.cell(text: simple_format(location_cell_postcode(location, if location.confirmed
- scheme_location_path(@scheme, location)
- else
- location.postcode.present? ? scheme_location_check_answers_path(@scheme, location, route: "locations") : scheme_location_postcode_path(@scheme, location)
- end), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
- <% row.cell(text: location.name) %>
- <% row.cell(text: location.id) %>
- <% row.cell(text: status_tag(location.status)) %>
- <% end %>
- <% end %>
- <% end %>
- <% end %>
+
- <% if LocationPolicy.new(current_user, @scheme.locations.new).create? %>
- <%= govuk_button_to "Add a location", scheme_locations_path(@scheme), method: "post", secondary: true %>
+
+
+ <%= govuk_table do |table| %>
+ <%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
+ <%= render(SearchResultCaptionComponent.new(searched: @searched, count: @pagy.count, item_label:, total_count: @total_count, item: "locations", path: request.path)) %>
<% end %>
-
-
-<% else %>
- <%= govuk_table do |table| %>
- <%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
- <%= render(SearchResultCaptionComponent.new(searched: @searched, count: @pagy.count, item_label:, total_count: @total_count, item: "locations", path: request.path)) %>
- <% end %>
- <%= table.head do |head| %>
- <%= head.row do |row| %>
- <% row.cell(header: true, text: "Code", html_attributes: {
- scope: "col",
- }) %>
- <% row.cell(header: true, text: "Postcode", html_attributes: {
- scope: "col",
- }) %>
- <% row.cell(header: true, text: "Units", html_attributes: {
- scope: "col",
- }) %>
- <% row.cell(header: true, text: "Common unit type", html_attributes: {
- scope: "col",
- }) %>
- <% row.cell(header: true, text: "Mobility type", html_attributes: {
- scope: "col",
- }) %>
- <% row.cell(header: true, text: "Local authority", html_attributes: {
- scope: "col",
- }) %>
- <% row.cell(header: true, text: "Available from", html_attributes: {
- scope: "col",
- }) %>
+ <%= table.head do |head| %>
+ <%= head.row do |row| %>
+ <% row.cell(header: true, text: "Postcode", html_attributes: {
+ scope: "col",
+ }) %>
+ <% row.cell(header: true, text: "Name", html_attributes: {
+ scope: "col",
+ }) %>
+ <% row.cell(header: true, text: "Location code", html_attributes: {
+ scope: "col",
+ }) %>
+ <% row.cell(header: true, text: "Status", html_attributes: {
+ scope: "col",
+ }) %>
+ <% end %>
<% end %>
- <% end %>
- <% @locations.each do |location| %>
- <%= table.body do |body| %>
- <%= body.row do |row| %>
- <% row.cell(text: location.id) %>
- <% row.cell(text: simple_format(location_cell_postcode(location, if location.confirmed
- scheme_location_path(@scheme, location)
- else
- location.postcode.present? ? scheme_location_check_answers_path(@scheme, location, route: "locations") : scheme_location_postcode_path(@scheme, location)
- end), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
- <% row.cell(text: location.units) %>
- <% row.cell do %>
-
<%= simple_format(location.type_of_unit) %>
- <% end %>
- <% row.cell(text: location.mobility_type) %>
- <% row.cell(text: location.location_admin_district) %>
- <% row.cell(text: location.startdate&.to_formatted_s(:govuk_date)) %>
+ <% @locations.each do |location| %>
+ <%= table.body do |body| %>
+ <%= body.row do |row| %>
+ <% row.cell(text: simple_format(location_cell_postcode(location, if location.confirmed
+ scheme_location_path(@scheme, location)
+ else
+ location.postcode.present? ? scheme_location_check_answers_path(@scheme, location, route: "locations") : scheme_location_postcode_path(@scheme, location)
+ end), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
+ <% row.cell(text: location.name) %>
+ <% row.cell(text: location.id) %>
+ <% row.cell(text: status_tag(location.status)) %>
<% end %>
+ <% end %>
<% end %>
<% end %>
- <% end %>
- <% if user_can_edit_scheme?(current_user, @scheme) %>
- <%= govuk_button_to "Add a location", scheme_locations_path(@scheme), method: "post", secondary: true %>
- <% end %>
-<% end %>
+ <% if LocationPolicy.new(current_user, @scheme.locations.new).create? %>
+ <%= govuk_button_to "Add a location", scheme_locations_path(@scheme), method: "post", secondary: true %>
+ <% end %>
+
+
<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "locations" } %>
diff --git a/app/views/locations/show.html.erb b/app/views/locations/show.html.erb
index d949a8c63..dd819b2fb 100644
--- a/app/views/locations/show.html.erb
+++ b/app/views/locations/show.html.erb
@@ -25,8 +25,6 @@
-<% if FeatureToggle.location_toggle_enabled? %>
- <% if LocationPolicy.new(current_user, @location).deactivate? %>
- <%= toggle_location_link(@location) %>
- <% end %>
+<% if LocationPolicy.new(current_user, @location).deactivate? %>
+ <%= toggle_location_link(@location) %>
<% end %>
diff --git a/app/views/schemes/_scheme_list.html.erb b/app/views/schemes/_scheme_list.html.erb
index 5fa712d1e..4f3b06454 100644
--- a/app/views/schemes/_scheme_list.html.erb
+++ b/app/views/schemes/_scheme_list.html.erb
@@ -5,20 +5,10 @@
<% end %>
<%= table.head do |head| %>
<%= head.row do |row| %>
- <% row.cell(header: true, text: "Scheme", html_attributes: {
- scope: "col",
- }) %>
- <% row.cell(header: true, text: "Code", html_attributes: {
- scope: "col",
- }) %>
- <% row.cell(header: true, text: "Locations", html_attributes: {
- scope: "col",
- }) %>
- <% if FeatureToggle.scheme_toggle_enabled? %>
- <% row.cell(header: true, text: "Status", html_attributes: {
- scope: "col",
- }) %>
- <% end %>
+ <% row.cell(header: true, text: "Scheme", html_attributes: { scope: "col" }) %>
+ <% row.cell(header: true, text: "Code", html_attributes: { scope: "col" }) %>
+ <% row.cell(header: true, text: "Locations", html_attributes: { scope: "col" }) %>
+ <% row.cell(header: true, text: "Status", html_attributes: { scope: "col" }) %>
<% end %>
<% end %>
<% @schemes.each do |scheme| %>
@@ -27,9 +17,7 @@
<% row.cell(text: simple_format(scheme_cell(scheme), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
<% row.cell(text: scheme.id_to_display) %>
<% row.cell(text: scheme.locations&.count) %>
- <% if FeatureToggle.scheme_toggle_enabled? %>
- <% row.cell(text: status_tag(scheme.status)) %>
- <% end %>
+ <% row.cell(text: status_tag(scheme.status)) %>
<% end %>
<% end %>
<% end %>
diff --git a/app/views/schemes/show.html.erb b/app/views/schemes/show.html.erb
index 0005582e8..4fe3a65de 100644
--- a/app/views/schemes/show.html.erb
+++ b/app/views/schemes/show.html.erb
@@ -9,33 +9,26 @@
<%= render partial: "organisations/headings", locals: { main: @scheme.service_name, sub: nil } %>
-<% if FeatureToggle.location_toggle_enabled? %>
-
-
-<% end %>
- <%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %>
+
+
+ <%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %>
-
Scheme
+ Scheme
- <%= govuk_summary_list do |summary_list| %>
- <% display_scheme_attributes(@scheme, current_user).each do |attr| %>
- <%= summary_list.row do |row| %>
- <% row.key { attr[:name] } %>
- <% row.value { details_html(attr) } %>
- <% if SchemePolicy.new(current_user, @scheme).update? %>
- <% row.action(text: "Change", href: scheme_edit_name_path(scheme_id: @scheme.id)) if attr[:edit] %>
- <% end %>
+ <%= govuk_summary_list do |summary_list| %>
+ <% display_scheme_attributes(@scheme, current_user).each do |attr| %>
+ <%= summary_list.row do |row| %>
+ <% row.key { attr[:name] } %>
+ <% row.value { details_html(attr) } %>
+ <% if SchemePolicy.new(current_user, @scheme).update? %>
+ <% row.action(text: "Change", href: scheme_edit_name_path(scheme_id: @scheme.id)) if attr[:edit] %>
<% end %>
<% end %>
<% end %>
-
-<% if FeatureToggle.location_toggle_enabled? %>
-
+ <% end %>
-<% end %>
+
-<% if FeatureToggle.scheme_toggle_enabled? %>
- <% if SchemePolicy.new(current_user, @scheme).deactivate? %>
- <%= toggle_scheme_link(@scheme) %>
- <% end %>
+<% if SchemePolicy.new(current_user, @scheme).deactivate? %>
+ <%= toggle_scheme_link(@scheme) %>
<% end %>
diff --git a/spec/features/schemes_spec.rb b/spec/features/schemes_spec.rb
index 1e45634c6..777a6c88d 100644
--- a/spec/features/schemes_spec.rb
+++ b/spec/features/schemes_spec.rb
@@ -211,23 +211,6 @@ RSpec.describe "Schemes scheme Features" do
end
end
- context "when I click locations link and the new locations layout feature toggle is disabled" do
- before do
- allow(FeatureToggle).to receive(:location_toggle_enabled?).and_return(false)
- click_link("Locations")
- end
-
- it "shows details of those locations" do
- locations.each do |location|
- expect(page).to have_content(location.id)
- expect(page).to have_content(location.postcode)
- expect(page).to have_content(location.units)
- expect(page).to have_content(location.type_of_unit)
- expect(page).to have_content(location.startdate&.to_formatted_s(:govuk_date))
- end
- end
- end
-
context "when I search for a specific location" do
before do
click_link("Locations")
diff --git a/spec/helpers/schemes_helper_spec.rb b/spec/helpers/schemes_helper_spec.rb
index 09a9c9e2e..905494ac8 100644
--- a/spec/helpers/schemes_helper_spec.rb
+++ b/spec/helpers/schemes_helper_spec.rb
@@ -195,14 +195,6 @@ RSpec.describe SchemesHelper do
expect(display_scheme_attributes(scheme, coordinator_user)).to eq(attributes)
end
- context "when the scheme toggle is disabled" do
- it "doesn't show the scheme status" do
- allow(FeatureToggle).to receive(:scheme_toggle_enabled?).and_return(false)
- attributes = display_scheme_attributes(scheme, support_user).find { |x| x[:name] == "Status" }
- expect(attributes).to be_nil
- end
- end
-
context "when the managing organisation is the owning organisation" do
it "doesn't show the organisation providing support" do
attributes = display_scheme_attributes(scheme_where_managing_organisation_is_owning_organisation, support_user).find { |x| x[:name] == "Organisation providing support" }
diff --git a/spec/requests/locations_controller_spec.rb b/spec/requests/locations_controller_spec.rb
index 04eb71f1a..88d73901c 100644
--- a/spec/requests/locations_controller_spec.rb
+++ b/spec/requests/locations_controller_spec.rb
@@ -153,19 +153,6 @@ RSpec.describe LocationsController, type: :request do
end
end
- it "shows locations with correct data when the new locations layout feature toggle is disabled" do
- allow(FeatureToggle).to receive(:location_toggle_enabled?).and_return(false)
- get "/schemes/#{scheme.id}/locations"
- locations.each do |location|
- expect(page).to have_content(location.id)
- expect(page).to have_content(location.postcode)
- expect(page).to have_content(location.type_of_unit)
- expect(page).to have_content(location.mobility_type)
- expect(page).to have_content(location.location_admin_district)
- expect(page).to have_content(location.startdate&.to_formatted_s(:govuk_date))
- end
- end
-
it "has page heading" do
expect(page).to have_content(scheme.service_name)
end
@@ -294,19 +281,6 @@ RSpec.describe LocationsController, type: :request do
expect(page).to have_button("Add a location")
end
- it "shows locations with correct data when the new locations layout feature toggle is disabled" do
- allow(FeatureToggle).to receive(:location_toggle_enabled?).and_return(false)
- get "/schemes/#{scheme.id}/locations"
- locations.each do |location|
- expect(page).to have_content(location.id)
- expect(page).to have_content(location.postcode)
- expect(page).to have_content(location.type_of_unit)
- expect(page).to have_content(location.mobility_type)
- expect(page).to have_content(location.location_admin_district)
- expect(page).to have_content(location.startdate&.to_formatted_s(:govuk_date))
- end
- end
-
it "has page heading" do
expect(page).to have_content(scheme.service_name)
end
From 72795874910202c995a13b4b331fe6ec85065ebf Mon Sep 17 00:00:00 2001
From: Jack <113976590+bibblobcode@users.noreply.github.com>
Date: Thu, 15 Jun 2023 15:34:40 +0100
Subject: [PATCH 05/19] CLDC-2321 Add Data Protection Confirmation validation
on log change (#1694)
* Add Data Protection Confirmation on log change
* Validate managing and owning org DPC presence
* Add missing translation
---
app/models/log.rb | 9 ---
app/models/validations/setup_validations.rb | 8 +++
app/models/validations/shared_validations.rb | 8 +++
config/locales/en.yml | 2 +
.../validations/setup_validations_spec.rb | 63 +++++++++++++++++
.../validations/shared_validations_spec.rb | 69 ++++++++++++++++++-
spec/shared/shared_log_examples.rb | 49 -------------
7 files changed, 148 insertions(+), 60 deletions(-)
diff --git a/app/models/log.rb b/app/models/log.rb
index c02fdb02d..3291b0ed8 100644
--- a/app/models/log.rb
+++ b/app/models/log.rb
@@ -9,7 +9,6 @@ class Log < ApplicationRecord
belongs_to :bulk_upload, optional: true
before_save :update_status!
- before_validation :verify_data_protection_confirmation, on: :create
STATUS = {
"not_started" => 0,
@@ -178,14 +177,6 @@ class Log < ApplicationRecord
private
- def verify_data_protection_confirmation
- return unless FeatureToggle.new_data_protection_confirmation?
- return unless owning_organisation
- return if owning_organisation.data_protection_confirmed?
-
- errors.add :owning_organisation, I18n.t("validations.organisation.data_sharing_agreement_not_signed")
- end
-
# Handle logs that are older than previous collection start date
def older_than_previous_collection_year?
return false unless startdate
diff --git a/app/models/validations/setup_validations.rb b/app/models/validations/setup_validations.rb
index d42ca2ec7..fd71dd4bc 100644
--- a/app/models/validations/setup_validations.rb
+++ b/app/models/validations/setup_validations.rb
@@ -42,6 +42,14 @@ module Validations::SetupValidations
end
end
+ def validate_managing_organisation_data_sharing_agremeent_signed(record)
+ return unless FeatureToggle.new_data_protection_confirmation?
+
+ if record.managing_organisation_id_changed? && record.managing_organisation.present? && !record.managing_organisation.data_protection_confirmed?
+ record.errors.add :managing_organisation_id, I18n.t("validations.setup.managing_organisation.data_sharing_agreement_not_signed")
+ end
+ end
+
private
def active_collection_start_date
diff --git a/app/models/validations/shared_validations.rb b/app/models/validations/shared_validations.rb
index 6a32563a7..3b361e9d6 100644
--- a/app/models/validations/shared_validations.rb
+++ b/app/models/validations/shared_validations.rb
@@ -117,6 +117,14 @@ module Validations::SharedValidations
end
end
+ def validate_owning_organisation_data_sharing_agremeent_signed(record)
+ return unless FeatureToggle.new_data_protection_confirmation?
+
+ if record.owning_organisation_id_changed? && record.owning_organisation.present? && !record.owning_organisation.data_protection_confirmed?
+ record.errors.add :owning_organisation_id, I18n.t("validations.setup.owning_organisation.data_sharing_agreement_not_signed")
+ end
+ end
+
private
def person_is_partner?(relationship)
diff --git a/config/locales/en.yml b/config/locales/en.yml
index e1635e748..59fa8ef32 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -254,8 +254,10 @@ en:
activating_soon: "%{name} is not available until %{date}. Enter a tenancy start date after %{date}"
owning_organisation:
invalid: "Please select the owning organisation or managing organisation that you belong to"
+ data_sharing_agreement_not_signed: "The organisation must accept the Data Sharing Agreement before it can be selected as the owning organisation."
managing_organisation:
invalid: "Please select the owning organisation or managing organisation that you belong to"
+ data_sharing_agreement_not_signed: "The organisation must accept the Data Sharing Agreement before it can be selected as the managing organisation."
created_by:
invalid: "Please select the owning organisation or managing organisation that you belong to"
lettype:
diff --git a/spec/models/validations/setup_validations_spec.rb b/spec/models/validations/setup_validations_spec.rb
index 5bb180aa2..51c157215 100644
--- a/spec/models/validations/setup_validations_spec.rb
+++ b/spec/models/validations/setup_validations_spec.rb
@@ -400,4 +400,67 @@ RSpec.describe Validations::SetupValidations do
end
end
end
+
+ describe "#validate_managing_organisation_data_sharing_agremeent_signed" do
+ before do
+ allow(FeatureToggle).to receive(:new_data_protection_confirmation?).and_return(false)
+ end
+
+ it "is valid if the DSA is signed" do
+ log = build(:lettings_log, :in_progress, owning_organisation: create(:organisation))
+
+ expect(log).to be_valid
+ end
+
+ it "is valid when owning_organisation nil" do
+ log = build(:lettings_log, owning_organisation: nil)
+
+ expect(log).to be_valid
+ end
+
+ it "is not valid if the DSA is not signed" do
+ log = build(:lettings_log, owning_organisation: create(:organisation, :without_dpc))
+
+ expect(log).to be_valid
+ end
+ end
+
+ context "when flag enabled" do
+ before do
+ allow(FeatureToggle).to receive(:new_data_protection_confirmation?).and_return(true)
+ end
+
+ it "is valid if the Data Protection Confirmation is signed" do
+ log = build(:lettings_log, :in_progress, managing_organisation: create(:organisation))
+
+ expect(log).to be_valid
+ end
+
+ it "is valid when managing_organisation nil" do
+ log = build(:lettings_log, managing_organisation: nil)
+
+ expect(log).to be_valid
+ end
+
+ it "is not valid if the Data Protection Confirmation is not signed" do
+ log = build(:lettings_log, managing_organisation: create(:organisation, :without_dpc))
+
+ expect(log).not_to be_valid
+ expect(log.errors[:managing_organisation_id]).to eq(["The organisation must accept the Data Sharing Agreement before it can be selected as the managing organisation."])
+ end
+
+ context "when updating" do
+ let(:log) { create(:lettings_log, :in_progress) }
+ let(:org_with_dpc) { create(:organisation) }
+ let(:org_without_dpc) { create(:organisation, :without_dpc) }
+
+ it "is valid when changing to another org with a signed Data Protection Confirmation" do
+ expect { log.managing_organisation = org_with_dpc }.to not_change(log, :valid?)
+ end
+
+ it "invalid when changing to another org without a signed Data Protection Confirmation" do
+ expect { log.managing_organisation = org_without_dpc }.to change(log, :valid?).from(true).to(false).and(change { log.errors[:managing_organisation_id] }.to(["The organisation must accept the Data Sharing Agreement before it can be selected as the managing organisation."]))
+ end
+ end
+ end
end
diff --git a/spec/models/validations/shared_validations_spec.rb b/spec/models/validations/shared_validations_spec.rb
index fe3612c3c..8751f443a 100644
--- a/spec/models/validations/shared_validations_spec.rb
+++ b/spec/models/validations/shared_validations_spec.rb
@@ -4,8 +4,8 @@ RSpec.describe Validations::SharedValidations do
subject(:shared_validator) { validator_class.new }
let(:validator_class) { Class.new { include Validations::SharedValidations } }
- let(:lettings_log) { FactoryBot.create(:lettings_log) }
- let(:sales_log) { FactoryBot.create(:sales_log, :completed) }
+ let(:lettings_log) { create(:lettings_log) }
+ let(:sales_log) { create(:sales_log, :completed) }
let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") }
describe "numeric min max validations" do
@@ -174,5 +174,70 @@ RSpec.describe Validations::SharedValidations do
expect(sales_log.errors).to be_empty
end
end
+
+ %i[sales_log lettings_log].each do |log_type|
+ describe "validate_owning_organisation_data_sharing_agremeent_signed" do
+ before do
+ allow(FeatureToggle).to receive(:new_data_protection_confirmation?).and_return(false)
+ end
+
+ it "is valid if the DSA is signed" do
+ log = build(log_type, :in_progress, owning_organisation: create(:organisation))
+
+ expect(log).to be_valid
+ end
+
+ it "is valid when owning_organisation nil" do
+ log = build(log_type, owning_organisation: nil)
+
+ expect(log).to be_valid
+ end
+
+ it "is not valid if the DSA is not signed" do
+ log = build(log_type, owning_organisation: create(:organisation, :without_dpc))
+
+ expect(log).to be_valid
+ end
+ end
+
+ context "when flag enabled" do
+ before do
+ allow(FeatureToggle).to receive(:new_data_protection_confirmation?).and_return(true)
+ end
+
+ it "is valid if the Data Protection Confirmation is signed" do
+ log = build(log_type, :in_progress, owning_organisation: create(:organisation))
+
+ expect(log).to be_valid
+ end
+
+ it "is valid when owning_organisation nil" do
+ log = build(log_type, owning_organisation: nil)
+
+ expect(log).to be_valid
+ end
+
+ it "is not valid if the Data Protection Confirmation is not signed" do
+ log = build(log_type, owning_organisation: create(:organisation, :without_dpc))
+
+ expect(log).not_to be_valid
+ expect(log.errors[:owning_organisation_id]).to eq(["The organisation must accept the Data Sharing Agreement before it can be selected as the owning organisation."])
+ end
+
+ context "when updating" do
+ let(:log) { create(log_type, :in_progress) }
+ let(:org_with_dpc) { create(:organisation) }
+ let(:org_without_dpc) { create(:organisation, :without_dpc) }
+
+ it "is valid when changing to another org with a signed Data Protection Confirmation" do
+ expect { log.owning_organisation = org_with_dpc }.not_to change(log, :valid?)
+ end
+
+ it "invalid when changing to another org without a signed Data Protection Confirmation" do
+ expect { log.owning_organisation = org_without_dpc }.to change(log, :valid?).from(true).to(false).and(change { log.errors[:owning_organisation_id] }.to(["The organisation must accept the Data Sharing Agreement before it can be selected as the owning organisation."]))
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/shared/shared_log_examples.rb b/spec/shared/shared_log_examples.rb
index 1a2629ba2..f9cc59633 100644
--- a/spec/shared/shared_log_examples.rb
+++ b/spec/shared/shared_log_examples.rb
@@ -104,54 +104,5 @@ RSpec.shared_examples "shared log examples" do |log_type|
end
end
end
-
- describe "#verify_data_protection_confirmation" do
- before do
- allow(FeatureToggle).to receive(:new_data_protection_confirmation?).and_return(false)
- end
-
- it "is valid if the DSA is signed" do
- log = build(log_type, :in_progress, owning_organisation: create(:organisation))
-
- expect(log).to be_valid
- end
-
- it "is valid when owning_organisation nil" do
- log = build(log_type, owning_organisation: nil)
-
- expect(log).to be_valid
- end
-
- it "is not valid if the DSA is not signed" do
- log = build(log_type, owning_organisation: create(:organisation, :without_dpc))
-
- expect(log).to be_valid
- end
- end
-
- context "when flag enabled" do
- before do
- allow(FeatureToggle).to receive(:new_data_protection_confirmation?).and_return(true)
- end
-
- it "is valid if the DSA is signed" do
- log = build(log_type, :in_progress, owning_organisation: create(:organisation))
-
- expect(log).to be_valid
- end
-
- it "is valid when owning_organisation nil" do
- log = build(log_type, owning_organisation: nil)
-
- expect(log).to be_valid
- end
-
- it "is not valid if the DSA is not signed" do
- log = build(log_type, owning_organisation: create(:organisation, :without_dpc))
-
- expect(log).not_to be_valid
- expect(log.errors[:owning_organisation]).to eq(["Your organisation must accept the Data Sharing Agreement before you can create any logs."])
- end
- end
end
# rubocop:enable RSpec/AnyInstance
From ede9beb38563c021318afe9b268a40726a69cee7 Mon Sep 17 00:00:00 2001
From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com>
Date: Fri, 16 Jun 2023 12:56:02 +0100
Subject: [PATCH 06/19] Only set @interruption_page_id if http referrer is
provided (#1697)
---
app/controllers/form_controller.rb | 2 +-
spec/requests/form_controller_spec.rb | 25 +++++++++++++++++++++++++
2 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/app/controllers/form_controller.rb b/app/controllers/form_controller.rb
index 5bd151181..07ec12c5d 100644
--- a/app/controllers/form_controller.rb
+++ b/app/controllers/form_controller.rb
@@ -46,7 +46,7 @@ class FormController < ApplicationController
end
def show_page
- if request.params["referrer"] == "interruption_screen"
+ if request.params["referrer"] == "interruption_screen" && request.headers["HTTP_REFERER"].present?
@interruption_page_id = URI.parse(request.headers["HTTP_REFERER"]).path.split("/").last.underscore
@interruption_page_referrer_type = referrer_from_query
end
diff --git a/spec/requests/form_controller_spec.rb b/spec/requests/form_controller_spec.rb
index 5d9042c7c..a2c06c4f1 100644
--- a/spec/requests/form_controller_spec.rb
+++ b/spec/requests/form_controller_spec.rb
@@ -582,6 +582,31 @@ RSpec.describe FormController, type: :request do
end
end
end
+
+ context "when requesting a soft validation page without a http referrer header" do
+ before do
+ get "/lettings-logs/#{lettings_log.id}/#{page_path}?referrer=interruption_screen", headers:
+ end
+
+ context "when the page is routed to" do
+ let(:page_path) { page_id.dasherize }
+
+ it "directs to the question page" do
+ expect(response.body).to include("What is the tenant’s age?")
+ expect(response.body).to include("Skip for now")
+ end
+ end
+
+ context "when the page is not routed to" do
+ let(:page_path) { "person-2-working-situation" }
+
+ it "redirects to the log page" do
+ follow_redirect!
+ expect(response.body).to include("Before you start")
+ expect(response.body).not_to include("Skip for now")
+ end
+ end
+ end
end
context "with checkbox questions" do
From f3ab2c496dcf84c2da571bac82ed609ca366a654 Mon Sep 17 00:00:00 2001
From: Jack <113976590+bibblobcode@users.noreply.github.com>
Date: Fri, 16 Jun 2023 14:53:29 +0100
Subject: [PATCH 07/19] Do not send deactivation emails when user not present
(#1705)
---
app/controllers/locations_controller.rb | 17 +++++++++-----
app/controllers/schemes_controller.rb | 15 ++++++++----
spec/requests/locations_controller_spec.rb | 27 ++++++++++++++++++----
spec/requests/schemes_controller_spec.rb | 12 +++++++++-
4 files changed, 55 insertions(+), 16 deletions(-)
diff --git a/app/controllers/locations_controller.rb b/app/controllers/locations_controller.rb
index 453c0934f..7ba4a1115 100644
--- a/app/controllers/locations_controller.rb
+++ b/app/controllers/locations_controller.rb
@@ -180,12 +180,17 @@ class LocationsController < ApplicationController
logs = reset_location_and_scheme_for_logs!
flash[:notice] = deactivate_success_notice
- logs.group_by(&:created_by).transform_values(&:count).compact.each do |user, count|
- LocationOrSchemeDeactivationMailer.send_deactivation_mail(user,
- count,
- url_for(controller: "lettings_logs", action: "update_logs"),
- @location.scheme.service_name,
- @location.postcode).deliver_later
+
+ logs.group_by(&:created_by).transform_values(&:count).each do |user, count|
+ next unless user
+
+ LocationOrSchemeDeactivationMailer.send_deactivation_mail(
+ user,
+ count,
+ url_for(controller: "lettings_logs", action: "update_logs"),
+ @location.scheme.service_name,
+ @location.postcode,
+ ).deliver_later
end
end
redirect_to scheme_location_path(@scheme, @location)
diff --git a/app/controllers/schemes_controller.rb b/app/controllers/schemes_controller.rb
index 784ad5f62..3a2be3909 100644
--- a/app/controllers/schemes_controller.rb
+++ b/app/controllers/schemes_controller.rb
@@ -58,11 +58,16 @@ class SchemesController < ApplicationController
logs = reset_location_and_scheme_for_logs!
flash[:notice] = deactivate_success_notice
- logs.group_by(&:created_by).transform_values(&:count).compact.each do |user, count|
- LocationOrSchemeDeactivationMailer.send_deactivation_mail(user,
- count,
- url_for(controller: "lettings_logs", action: "update_logs"),
- @scheme.service_name).deliver_later
+
+ logs.group_by(&:created_by).transform_values(&:count).each do |user, count|
+ next unless user
+
+ LocationOrSchemeDeactivationMailer.send_deactivation_mail(
+ user,
+ count,
+ url_for(controller: "lettings_logs", action: "update_logs"),
+ @scheme.service_name,
+ ).deliver_later
end
end
redirect_to scheme_details_path(@scheme)
diff --git a/spec/requests/locations_controller_spec.rb b/spec/requests/locations_controller_spec.rb
index 88d73901c..81b2e56b0 100644
--- a/spec/requests/locations_controller_spec.rb
+++ b/spec/requests/locations_controller_spec.rb
@@ -1467,13 +1467,14 @@ RSpec.describe LocationsController, type: :request do
context "when confirming deactivation" do
let(:params) { { deactivation_date:, confirm: true, deactivation_date_type: "other" } }
- let(:mailer) { instance_double(LocationOrSchemeDeactivationMailer) }
- let(:user_a) { create(:user, email: "user_a@example.com") }
- let(:user_b) { create(:user, email: "user_b@example.com") }
+ let(:user_a) { create(:user) }
+ let(:user_b) { create(:user) }
before do
- create_list(:lettings_log, 1, :sh, location:, scheme:, startdate:, created_by: user_a)
+ allow(LocationOrSchemeDeactivationMailer).to receive(:send_deactivation_mail).and_call_original
+
+ create(:lettings_log, :sh, location:, scheme:, startdate:, created_by: user_a)
create_list(:lettings_log, 3, :sh, location:, scheme:, startdate:, created_by: user_b)
Timecop.freeze(Time.utc(2022, 10, 10))
@@ -1511,6 +1512,24 @@ RSpec.describe LocationsController, type: :request do
lettings_log.reload
expect(lettings_log.unresolved).to eq(true)
end
+
+ it "sends deactivation emails" do
+ expect(LocationOrSchemeDeactivationMailer).to have_received(:send_deactivation_mail).with(
+ user_a,
+ 1,
+ update_logs_lettings_logs_url,
+ location.scheme.service_name,
+ location.postcode,
+ )
+
+ expect(LocationOrSchemeDeactivationMailer).to have_received(:send_deactivation_mail).with(
+ user_b,
+ 3,
+ update_logs_lettings_logs_url,
+ location.scheme.service_name,
+ location.postcode,
+ )
+ end
end
context "and the users need to be notified" do
diff --git a/spec/requests/schemes_controller_spec.rb b/spec/requests/schemes_controller_spec.rb
index 4c7bad55a..18ee51756 100644
--- a/spec/requests/schemes_controller_spec.rb
+++ b/spec/requests/schemes_controller_spec.rb
@@ -1930,7 +1930,6 @@ RSpec.describe SchemesController, type: :request do
context "when confirming deactivation" do
let(:params) { { deactivation_date:, confirm: true, deactivation_date_type: "other" } }
- let(:mailer) { instance_double(LocationOrSchemeDeactivationMailer) }
before do
Timecop.freeze(Time.utc(2022, 10, 10))
@@ -1943,6 +1942,8 @@ RSpec.describe SchemesController, type: :request do
context "and a log startdate is after scheme deactivation date" do
before do
+ allow(LocationOrSchemeDeactivationMailer).to receive(:send_deactivation_mail).and_call_original
+
patch "/schemes/#{scheme.id}/deactivate", params:
end
@@ -1969,6 +1970,15 @@ RSpec.describe SchemesController, type: :request do
lettings_log.reload
expect(lettings_log.unresolved).to eq(true)
end
+
+ it "sends deactivation emails" do
+ expect(LocationOrSchemeDeactivationMailer).to have_received(:send_deactivation_mail).with(
+ user,
+ 1,
+ update_logs_lettings_logs_url,
+ scheme.service_name,
+ )
+ end
end
context "and a log startdate is before scheme deactivation date" do
From 11d14a4d9eed3f5fbecbb830c91b9c8b7bb1be94 Mon Sep 17 00:00:00 2001
From: Rachael Booth
Date: Fri, 16 Jun 2023 15:08:44 +0100
Subject: [PATCH 08/19] CLDC-2341: Trigger retirement validations after all
relevant questions on lettings forms (#1690)
---
...lead_tenant_over_retirement_value_check.rb | 1 -
...ead_tenant_under_retirement_value_check.rb | 1 -
.../person_over_retirement_value_check.rb | 1 -
.../person_under_retirement_value_check.rb | 1 -
.../subsections/household_characteristics.rb | 56 +++++++++++++------
...person_over_retirement_value_check_spec.rb | 8 ---
...erson_under_retirement_value_check_spec.rb | 8 ---
.../household_characteristics_spec.rb | 56 +++++++++++++------
8 files changed, 80 insertions(+), 52 deletions(-)
diff --git a/app/models/form/lettings/pages/lead_tenant_over_retirement_value_check.rb b/app/models/form/lettings/pages/lead_tenant_over_retirement_value_check.rb
index ca6183f3c..9d21e21a8 100644
--- a/app/models/form/lettings/pages/lead_tenant_over_retirement_value_check.rb
+++ b/app/models/form/lettings/pages/lead_tenant_over_retirement_value_check.rb
@@ -1,7 +1,6 @@
class Form::Lettings::Pages::LeadTenantOverRetirementValueCheck < ::Form::Page
def initialize(id, hsh, subsection)
super
- @id = "lead_tenant_over_retirement_value_check"
@depends_on = [{ "person_1_not_retired_over_soft_max_age?" => true }]
@title_text = {
"translation" => "soft_validations.retirement.max.title",
diff --git a/app/models/form/lettings/pages/lead_tenant_under_retirement_value_check.rb b/app/models/form/lettings/pages/lead_tenant_under_retirement_value_check.rb
index b7ab02900..0c7a3ed2e 100644
--- a/app/models/form/lettings/pages/lead_tenant_under_retirement_value_check.rb
+++ b/app/models/form/lettings/pages/lead_tenant_under_retirement_value_check.rb
@@ -1,7 +1,6 @@
class Form::Lettings::Pages::LeadTenantUnderRetirementValueCheck < ::Form::Page
def initialize(id, hsh, subsection)
super
- @id = "lead_tenant_under_retirement_value_check"
@depends_on = [{ "person_1_retired_under_soft_min_age?" => true }]
@title_text = {
"translation" => "soft_validations.retirement.min.title",
diff --git a/app/models/form/lettings/pages/person_over_retirement_value_check.rb b/app/models/form/lettings/pages/person_over_retirement_value_check.rb
index b13a52ed5..550ae0373 100644
--- a/app/models/form/lettings/pages/person_over_retirement_value_check.rb
+++ b/app/models/form/lettings/pages/person_over_retirement_value_check.rb
@@ -1,7 +1,6 @@
class Form::Lettings::Pages::PersonOverRetirementValueCheck < ::Form::Page
def initialize(id, hsh, subsection, person_index:)
super(id, hsh, subsection)
- @id = "person_#{person_index}_over_retirement_value_check"
@depends_on = [{ "person_#{person_index}_not_retired_over_soft_max_age?" => true }]
@title_text = {
"translation" => "soft_validations.retirement.max.title",
diff --git a/app/models/form/lettings/pages/person_under_retirement_value_check.rb b/app/models/form/lettings/pages/person_under_retirement_value_check.rb
index 9f7540f56..ab9c81beb 100644
--- a/app/models/form/lettings/pages/person_under_retirement_value_check.rb
+++ b/app/models/form/lettings/pages/person_under_retirement_value_check.rb
@@ -1,7 +1,6 @@
class Form::Lettings::Pages::PersonUnderRetirementValueCheck < ::Form::Page
def initialize(id, hsh, subsection, person_index:)
super(id, hsh, subsection)
- @id = "person_#{person_index}_under_retirement_value_check"
@depends_on = [{ "person_#{person_index}_retired_under_soft_min_age?" => true }]
@title_text = {
"translation" => "soft_validations.retirement.min.title",
diff --git a/app/models/form/lettings/subsections/household_characteristics.rb b/app/models/form/lettings/subsections/household_characteristics.rb
index 9fc6948bd..bb8533c12 100644
--- a/app/models/form/lettings/subsections/household_characteristics.rb
+++ b/app/models/form/lettings/subsections/household_characteristics.rb
@@ -15,9 +15,12 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::LeadTenantAge.new(nil, nil, self),
Form::Lettings::Pages::NoFemalesPregnantHouseholdLeadAgeValueCheck.new(nil, nil, self),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdLeadAgeValueCheck.new(nil, nil, self),
+ Form::Lettings::Pages::LeadTenantUnderRetirementValueCheck.new("age_lead_tenant_under_retirement_value_check", nil, self),
+ Form::Lettings::Pages::LeadTenantOverRetirementValueCheck.new("age_lead_tenant_over_retirement_value_check", nil, self),
Form::Lettings::Pages::LeadTenantGenderIdentity.new(nil, nil, self),
Form::Lettings::Pages::NoFemalesPregnantHouseholdLeadValueCheck.new(nil, nil, self),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdLeadValueCheck.new(nil, nil, self),
+ Form::Lettings::Pages::LeadTenantOverRetirementValueCheck.new("gender_lead_tenant_over_retirement_value_check", nil, self),
Form::Lettings::Pages::LeadTenantEthnicGroup.new(nil, nil, self),
Form::Lettings::Pages::LeadTenantEthnicBackgroundArab.new(nil, nil, self),
Form::Lettings::Pages::LeadTenantEthnicBackgroundAsian.new(nil, nil, self),
@@ -26,8 +29,8 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
Form::Lettings::Pages::LeadTenantEthnicBackgroundWhite.new(nil, nil, self),
Form::Lettings::Pages::LeadTenantNationality.new(nil, nil, self),
Form::Lettings::Pages::LeadTenantWorkingSituation.new(nil, nil, self),
- Form::Lettings::Pages::LeadTenantUnderRetirementValueCheck.new(nil, nil, self),
- Form::Lettings::Pages::LeadTenantOverRetirementValueCheck.new(nil, nil, self),
+ Form::Lettings::Pages::LeadTenantUnderRetirementValueCheck.new("working_situation_lead_tenant_under_retirement_value_check", nil, self),
+ Form::Lettings::Pages::LeadTenantOverRetirementValueCheck.new("working_situation_lead_tenant_over_retirement_value_check", nil, self),
Form::Lettings::Pages::PersonKnown.new(nil, nil, self, person_index: 2),
Form::Lettings::Pages::PersonRelationshipToLead.new(nil, nil, self, person_index: 2),
Form::Lettings::Pages::PersonAge.new(nil, nil, self, person_index: 2, person_type: "child"),
@@ -36,13 +39,16 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
person_index: 2),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self,
person_index: 2),
+ Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("age_2_under_retirement_value_check", nil, self, person_index: 2),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("age_2_over_retirement_value_check", nil, self, person_index: 2),
Form::Lettings::Pages::PersonGenderIdentity.new(nil, nil, self, person_index: 2),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.new(nil, nil, self, person_index: 2),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
person_index: 2),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("gender_2_over_retirement_value_check", nil, self, person_index: 2),
Form::Lettings::Pages::PersonWorkingSituation.new(nil, nil, self, person_index: 2),
- Form::Lettings::Pages::PersonUnderRetirementValueCheck.new(nil, nil, self, person_index: 2),
- Form::Lettings::Pages::PersonOverRetirementValueCheck.new(nil, nil, self, person_index: 2),
+ Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("working_situation_2_under_retirement_value_check", nil, self, person_index: 2),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("working_situation_2_over_retirement_value_check", nil, self, person_index: 2),
Form::Lettings::Pages::PersonKnown.new(nil, nil, self, person_index: 3),
Form::Lettings::Pages::PersonRelationshipToLead.new(nil, nil, self, person_index: 3),
Form::Lettings::Pages::PersonAge.new(nil, nil, self, person_index: 3, person_type: "child"),
@@ -51,13 +57,16 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
person_index: 3),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self,
person_index: 3),
+ Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("age_3_under_retirement_value_check", nil, self, person_index: 3),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("age_3_over_retirement_value_check", nil, self, person_index: 3),
Form::Lettings::Pages::PersonGenderIdentity.new(nil, nil, self, person_index: 3),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.new(nil, nil, self, person_index: 3),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
person_index: 3),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("gender_3_over_retirement_value_check", nil, self, person_index: 3),
Form::Lettings::Pages::PersonWorkingSituation.new(nil, nil, self, person_index: 3),
- Form::Lettings::Pages::PersonUnderRetirementValueCheck.new(nil, nil, self, person_index: 3),
- Form::Lettings::Pages::PersonOverRetirementValueCheck.new(nil, nil, self, person_index: 3),
+ Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("working_situation_3_under_retirement_value_check", nil, self, person_index: 3),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("working_situation_3_over_retirement_value_check", nil, self, person_index: 3),
Form::Lettings::Pages::PersonKnown.new(nil, nil, self, person_index: 4),
Form::Lettings::Pages::PersonRelationshipToLead.new(nil, nil, self, person_index: 4),
Form::Lettings::Pages::PersonAge.new(nil, nil, self, person_index: 4, person_type: "child"),
@@ -66,13 +75,16 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
person_index: 4),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self,
person_index: 4),
+ Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("age_4_under_retirement_value_check", nil, self, person_index: 4),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("age_4_over_retirement_value_check", nil, self, person_index: 4),
Form::Lettings::Pages::PersonGenderIdentity.new(nil, nil, self, person_index: 4),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.new(nil, nil, self, person_index: 4),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
person_index: 4),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("gender_4_over_retirement_value_check", nil, self, person_index: 4),
Form::Lettings::Pages::PersonWorkingSituation.new(nil, nil, self, person_index: 4),
- Form::Lettings::Pages::PersonUnderRetirementValueCheck.new(nil, nil, self, person_index: 4),
- Form::Lettings::Pages::PersonOverRetirementValueCheck.new(nil, nil, self, person_index: 4),
+ Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("working_situation_4_under_retirement_value_check", nil, self, person_index: 4),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("working_situation_4_over_retirement_value_check", nil, self, person_index: 4),
Form::Lettings::Pages::PersonKnown.new(nil, nil, self, person_index: 5),
Form::Lettings::Pages::PersonRelationshipToLead.new(nil, nil, self, person_index: 5),
Form::Lettings::Pages::PersonAge.new(nil, nil, self, person_index: 5, person_type: "child"),
@@ -81,13 +93,16 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
person_index: 5),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self,
person_index: 5),
+ Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("age_5_under_retirement_value_check", nil, self, person_index: 5),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("age_5_over_retirement_value_check", nil, self, person_index: 5),
Form::Lettings::Pages::PersonGenderIdentity.new(nil, nil, self, person_index: 5),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.new(nil, nil, self, person_index: 5),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
person_index: 5),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("gender_5_over_retirement_value_check", nil, self, person_index: 5),
Form::Lettings::Pages::PersonWorkingSituation.new(nil, nil, self, person_index: 5),
- Form::Lettings::Pages::PersonUnderRetirementValueCheck.new(nil, nil, self, person_index: 5),
- Form::Lettings::Pages::PersonOverRetirementValueCheck.new(nil, nil, self, person_index: 5),
+ Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("working_situation_5_under_retirement_value_check", nil, self, person_index: 5),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("working_situation_5_over_retirement_value_check", nil, self, person_index: 5),
Form::Lettings::Pages::PersonKnown.new(nil, nil, self, person_index: 6),
Form::Lettings::Pages::PersonRelationshipToLead.new(nil, nil, self, person_index: 6),
Form::Lettings::Pages::PersonAge.new(nil, nil, self, person_index: 6, person_type: "child"),
@@ -96,13 +111,16 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
person_index: 6),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self,
person_index: 6),
+ Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("age_6_under_retirement_value_check", nil, self, person_index: 6),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("age_6_over_retirement_value_check", nil, self, person_index: 6),
Form::Lettings::Pages::PersonGenderIdentity.new(nil, nil, self, person_index: 6),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.new(nil, nil, self, person_index: 6),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
person_index: 6),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("gender_6_over_retirement_value_check", nil, self, person_index: 6),
Form::Lettings::Pages::PersonWorkingSituation.new(nil, nil, self, person_index: 6),
- Form::Lettings::Pages::PersonUnderRetirementValueCheck.new(nil, nil, self, person_index: 6),
- Form::Lettings::Pages::PersonOverRetirementValueCheck.new(nil, nil, self, person_index: 6),
+ Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("working_situation_6_under_retirement_value_check", nil, self, person_index: 6),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("working_situation_6_over_retirement_value_check", nil, self, person_index: 6),
Form::Lettings::Pages::PersonKnown.new(nil, nil, self, person_index: 7),
Form::Lettings::Pages::PersonRelationshipToLead.new(nil, nil, self, person_index: 7),
Form::Lettings::Pages::PersonAge.new(nil, nil, self, person_index: 7, person_type: "child"),
@@ -111,13 +129,16 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
person_index: 7),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self,
person_index: 7),
+ Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("age_7_under_retirement_value_check", nil, self, person_index: 7),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("age_7_over_retirement_value_check", nil, self, person_index: 7),
Form::Lettings::Pages::PersonGenderIdentity.new(nil, nil, self, person_index: 7),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.new(nil, nil, self, person_index: 7),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
person_index: 7),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("gender_7_over_retirement_value_check", nil, self, person_index: 7),
Form::Lettings::Pages::PersonWorkingSituation.new(nil, nil, self, person_index: 7),
- Form::Lettings::Pages::PersonUnderRetirementValueCheck.new(nil, nil, self, person_index: 7),
- Form::Lettings::Pages::PersonOverRetirementValueCheck.new(nil, nil, self, person_index: 7),
+ Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("working_situation_7_under_retirement_value_check", nil, self, person_index: 7),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("working_situation_7_over_retirement_value_check", nil, self, person_index: 7),
Form::Lettings::Pages::PersonKnown.new(nil, nil, self, person_index: 8),
Form::Lettings::Pages::PersonRelationshipToLead.new(nil, nil, self, person_index: 8),
Form::Lettings::Pages::PersonAge.new(nil, nil, self, person_index: 8, person_type: "child"),
@@ -126,13 +147,16 @@ class Form::Lettings::Subsections::HouseholdCharacteristics < ::Form::Subsection
person_index: 8),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self,
person_index: 8),
+ Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("age_8_under_retirement_value_check", nil, self, person_index: 8),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("age_8_over_retirement_value_check", nil, self, person_index: 8),
Form::Lettings::Pages::PersonGenderIdentity.new(nil, nil, self, person_index: 8),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.new(nil, nil, self, person_index: 8),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
person_index: 8),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("gender_8_over_retirement_value_check", nil, self, person_index: 8),
Form::Lettings::Pages::PersonWorkingSituation.new(nil, nil, self, person_index: 8),
- Form::Lettings::Pages::PersonUnderRetirementValueCheck.new(nil, nil, self, person_index: 8),
- Form::Lettings::Pages::PersonOverRetirementValueCheck.new(nil, nil, self, person_index: 8),
+ Form::Lettings::Pages::PersonUnderRetirementValueCheck.new("working_situation_8_under_retirement_value_check", nil, self, person_index: 8),
+ Form::Lettings::Pages::PersonOverRetirementValueCheck.new("working_situation_8_over_retirement_value_check", nil, self, person_index: 8),
].compact
end
end
diff --git a/spec/models/form/lettings/pages/person_over_retirement_value_check_spec.rb b/spec/models/form/lettings/pages/person_over_retirement_value_check_spec.rb
index 45587b4fb..865a01f61 100644
--- a/spec/models/form/lettings/pages/person_over_retirement_value_check_spec.rb
+++ b/spec/models/form/lettings/pages/person_over_retirement_value_check_spec.rb
@@ -24,10 +24,6 @@ RSpec.describe Form::Lettings::Pages::PersonOverRetirementValueCheck, type: :mod
end
context "with person 2" do
- it "has the correct id" do
- expect(page.id).to eq("person_2_over_retirement_value_check")
- end
-
it "has correct depends_on" do
expect(page.depends_on).to eq(
[{ "person_2_not_retired_over_soft_max_age?" => true }],
@@ -69,10 +65,6 @@ RSpec.describe Form::Lettings::Pages::PersonOverRetirementValueCheck, type: :mod
context "with person 3" do
let(:person_index) { 3 }
- it "has the correct id" do
- expect(page.id).to eq("person_3_over_retirement_value_check")
- end
-
it "has correct depends_on" do
expect(page.depends_on).to eq(
[{ "person_3_not_retired_over_soft_max_age?" => true }],
diff --git a/spec/models/form/lettings/pages/person_under_retirement_value_check_spec.rb b/spec/models/form/lettings/pages/person_under_retirement_value_check_spec.rb
index 929009ce7..58fbcf33d 100644
--- a/spec/models/form/lettings/pages/person_under_retirement_value_check_spec.rb
+++ b/spec/models/form/lettings/pages/person_under_retirement_value_check_spec.rb
@@ -24,10 +24,6 @@ RSpec.describe Form::Lettings::Pages::PersonUnderRetirementValueCheck, type: :mo
end
context "with person 2" do
- it "has the correct id" do
- expect(page.id).to eq("person_2_under_retirement_value_check")
- end
-
it "has correct depends_on" do
expect(page.depends_on).to eq(
[{ "person_2_retired_under_soft_min_age?" => true }],
@@ -55,10 +51,6 @@ RSpec.describe Form::Lettings::Pages::PersonUnderRetirementValueCheck, type: :mo
context "with person 3" do
let(:person_index) { 3 }
- it "has the correct id" do
- expect(page.id).to eq("person_3_under_retirement_value_check")
- end
-
it "has correct depends_on" do
expect(page.depends_on).to eq(
[{ "person_3_retired_under_soft_min_age?" => true }],
diff --git a/spec/models/form/lettings/subsections/household_characteristics_spec.rb b/spec/models/form/lettings/subsections/household_characteristics_spec.rb
index d0d55b8bf..4586d5592 100644
--- a/spec/models/form/lettings/subsections/household_characteristics_spec.rb
+++ b/spec/models/form/lettings/subsections/household_characteristics_spec.rb
@@ -21,9 +21,12 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
lead_tenant_age
no_females_pregnant_household_lead_age_value_check
females_in_soft_age_range_in_pregnant_household_lead_age_value_check
+ age_lead_tenant_under_retirement_value_check
+ age_lead_tenant_over_retirement_value_check
lead_tenant_gender_identity
no_females_pregnant_household_lead_value_check
females_in_soft_age_range_in_pregnant_household_lead_value_check
+ gender_lead_tenant_over_retirement_value_check
lead_tenant_ethnic_group
lead_tenant_ethnic_background_arab
lead_tenant_ethnic_background_asian
@@ -32,92 +35,113 @@ RSpec.describe Form::Lettings::Subsections::HouseholdCharacteristics, type: :mod
lead_tenant_ethnic_background_white
lead_tenant_nationality
lead_tenant_working_situation
- lead_tenant_under_retirement_value_check
- lead_tenant_over_retirement_value_check
+ working_situation_lead_tenant_under_retirement_value_check
+ working_situation_lead_tenant_over_retirement_value_check
person_2_known
person_2_relationship_to_lead
person_2_age_child
person_2_age_non_child
no_females_pregnant_household_person_2_age_value_check
females_in_soft_age_range_in_pregnant_household_person_2_age_value_check
+ age_2_under_retirement_value_check
+ age_2_over_retirement_value_check
person_2_gender_identity
no_females_pregnant_household_person_2_value_check
females_in_soft_age_range_in_pregnant_household_person_2_value_check
+ gender_2_over_retirement_value_check
person_2_working_situation
- person_2_under_retirement_value_check
- person_2_over_retirement_value_check
+ working_situation_2_under_retirement_value_check
+ working_situation_2_over_retirement_value_check
person_3_known
person_3_relationship_to_lead
person_3_age_child
person_3_age_non_child
no_females_pregnant_household_person_3_age_value_check
females_in_soft_age_range_in_pregnant_household_person_3_age_value_check
+ age_3_under_retirement_value_check
+ age_3_over_retirement_value_check
person_3_gender_identity
no_females_pregnant_household_person_3_value_check
females_in_soft_age_range_in_pregnant_household_person_3_value_check
+ gender_3_over_retirement_value_check
person_3_working_situation
- person_3_under_retirement_value_check
- person_3_over_retirement_value_check
+ working_situation_3_under_retirement_value_check
+ working_situation_3_over_retirement_value_check
person_4_known
person_4_relationship_to_lead
person_4_age_child
person_4_age_non_child
no_females_pregnant_household_person_4_age_value_check
females_in_soft_age_range_in_pregnant_household_person_4_age_value_check
+ age_4_under_retirement_value_check
+ age_4_over_retirement_value_check
person_4_gender_identity
no_females_pregnant_household_person_4_value_check
females_in_soft_age_range_in_pregnant_household_person_4_value_check
+ gender_4_over_retirement_value_check
person_4_working_situation
- person_4_under_retirement_value_check
- person_4_over_retirement_value_check
+ working_situation_4_under_retirement_value_check
+ working_situation_4_over_retirement_value_check
person_5_known
person_5_relationship_to_lead
person_5_age_child
person_5_age_non_child
no_females_pregnant_household_person_5_age_value_check
females_in_soft_age_range_in_pregnant_household_person_5_age_value_check
+ age_5_under_retirement_value_check
+ age_5_over_retirement_value_check
person_5_gender_identity
no_females_pregnant_household_person_5_value_check
females_in_soft_age_range_in_pregnant_household_person_5_value_check
+ gender_5_over_retirement_value_check
person_5_working_situation
- person_5_under_retirement_value_check
- person_5_over_retirement_value_check
+ working_situation_5_under_retirement_value_check
+ working_situation_5_over_retirement_value_check
person_6_known
person_6_relationship_to_lead
person_6_age_child
person_6_age_non_child
no_females_pregnant_household_person_6_age_value_check
females_in_soft_age_range_in_pregnant_household_person_6_age_value_check
+ age_6_under_retirement_value_check
+ age_6_over_retirement_value_check
person_6_gender_identity
no_females_pregnant_household_person_6_value_check
females_in_soft_age_range_in_pregnant_household_person_6_value_check
+ gender_6_over_retirement_value_check
person_6_working_situation
- person_6_under_retirement_value_check
- person_6_over_retirement_value_check
+ working_situation_6_under_retirement_value_check
+ working_situation_6_over_retirement_value_check
person_7_known
person_7_relationship_to_lead
person_7_age_child
person_7_age_non_child
no_females_pregnant_household_person_7_age_value_check
females_in_soft_age_range_in_pregnant_household_person_7_age_value_check
+ age_7_under_retirement_value_check
+ age_7_over_retirement_value_check
person_7_gender_identity
no_females_pregnant_household_person_7_value_check
females_in_soft_age_range_in_pregnant_household_person_7_value_check
+ gender_7_over_retirement_value_check
person_7_working_situation
- person_7_under_retirement_value_check
- person_7_over_retirement_value_check
+ working_situation_7_under_retirement_value_check
+ working_situation_7_over_retirement_value_check
person_8_known
person_8_relationship_to_lead
person_8_age_child
person_8_age_non_child
no_females_pregnant_household_person_8_age_value_check
females_in_soft_age_range_in_pregnant_household_person_8_age_value_check
+ age_8_under_retirement_value_check
+ age_8_over_retirement_value_check
person_8_gender_identity
no_females_pregnant_household_person_8_value_check
females_in_soft_age_range_in_pregnant_household_person_8_value_check
+ gender_8_over_retirement_value_check
person_8_working_situation
- person_8_under_retirement_value_check
- person_8_over_retirement_value_check
+ working_situation_8_under_retirement_value_check
+ working_situation_8_over_retirement_value_check
],
)
end
From 7bfe3f4d7590fad0cc5c38b2e08c7f4dc7dcd47a Mon Sep 17 00:00:00 2001
From: natdeanlewissoftwire
<94526761+natdeanlewissoftwire@users.noreply.github.com>
Date: Fri, 16 Jun 2023 15:28:35 +0100
Subject: [PATCH 09/19] CLDC-2447 Bulk upload 23-24 lettings net income known
bug (#1695)
* feat: fix bug
* feat: update tests
* feat: test ALL possibilities
---
.../lettings/year2023/row_parser.rb | 2 --
.../lettings/year2022/row_parser_spec.rb | 32 +++++++++++++++++--
.../lettings/year2023/row_parser_spec.rb | 24 ++++++++++++--
3 files changed, 50 insertions(+), 8 deletions(-)
diff --git a/app/services/bulk_upload/lettings/year2023/row_parser.rb b/app/services/bulk_upload/lettings/year2023/row_parser.rb
index 225c45b4f..0912b02e4 100644
--- a/app/services/bulk_upload/lettings/year2023/row_parser.rb
+++ b/app/services/bulk_upload/lettings/year2023/row_parser.rb
@@ -1442,8 +1442,6 @@ private
when 2
1
when 3
- 1
- when 4
2
end
end
diff --git a/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb
index 05120713d..365771bb6 100644
--- a/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb
+++ b/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb
@@ -1295,10 +1295,36 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do
end
describe "#net_income_known" do
- let(:attributes) { { bulk_upload:, field_51: "1" } }
+ context "when 1" do
+ let(:attributes) { { bulk_upload:, field_51: "1" } }
- it "sets value from correct mapping" do
- expect(parser.log.net_income_known).to eq(0)
+ it "sets value from correct mapping" do
+ expect(parser.log.net_income_known).to eq(0)
+ end
+ end
+
+ context "when 2" do
+ let(:attributes) { { bulk_upload:, field_51: "2" } }
+
+ it "sets value from correct mapping" do
+ expect(parser.log.net_income_known).to eq(1)
+ end
+ end
+
+ context "when 3" do
+ let(:attributes) { { bulk_upload:, field_51: "3" } }
+
+ it "sets value from correct mapping" do
+ expect(parser.log.net_income_known).to eq(1)
+ end
+ end
+
+ context "when 4" do
+ let(:attributes) { { bulk_upload:, field_51: "4" } }
+
+ it "sets value from correct mapping" do
+ expect(parser.log.net_income_known).to eq(2)
+ end
end
end
diff --git a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb
index 8586a470c..5bb6e8958 100644
--- a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb
+++ b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb
@@ -1365,10 +1365,28 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do
end
describe "#net_income_known" do
- let(:attributes) { { bulk_upload:, field_120: "1" } }
+ context "when 1" do
+ let(:attributes) { { bulk_upload:, field_120: "1" } }
- it "sets value from correct mapping" do
- expect(parser.log.net_income_known).to eq(0)
+ it "sets value from correct mapping" do
+ expect(parser.log.net_income_known).to eq(0)
+ end
+ end
+
+ context "when 2" do
+ let(:attributes) { { bulk_upload:, field_120: "2" } }
+
+ it "sets value from correct mapping" do
+ expect(parser.log.net_income_known).to eq(1)
+ end
+ end
+
+ context "when 3" do
+ let(:attributes) { { bulk_upload:, field_120: "3" } }
+
+ it "sets value from correct mapping" do
+ expect(parser.log.net_income_known).to eq(2)
+ end
end
end
From bc9519f365731718da0730d486a9a3472e5ba1ed Mon Sep 17 00:00:00 2001
From: Aaron Spencer <62190777+Airk0n@users.noreply.github.com>
Date: Fri, 16 Jun 2023 16:03:28 +0100
Subject: [PATCH 10/19] CLDC 2328: Resend invitation button (#1680)
* CDCL-2326: Init - button moved, button added
* CLDC-2328: Button cleanup, user controller endpoint created
* CLDC-2328: Tests added
* CLDC-2328: Tests added to user controller
* CLDC-2328: WIp Testing
* CLDC-2328: Testing of sent emails
* CLDC-2328: Button uses post instead of get, tests reflect this.
* CLDC-2328: Invite button only appears for support users.
* CLDC-2328: Email test refactor.
* CLDC-2328: Flash now shows email address, Button moved.
---
app/controllers/users_controller.rb | 6 ++
app/views/users/show.html.erb | 27 +++++----
config/routes.rb | 1 +
spec/features/user_spec.rb | 79 ++++++++++++++++++++++++++
spec/requests/users_controller_spec.rb | 35 ++++++++++++
5 files changed, 137 insertions(+), 11 deletions(-)
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 0c343b8c3..9e6491554 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -29,6 +29,12 @@ class UsersController < ApplicationController
end
end
+ def resend_invite
+ @user.send_confirmation_instructions
+ flash[:notice] = "Invitation sent to #{@user.email}"
+ render :show
+ end
+
def show; end
def dpo; end
diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb
index e6cb9fd3e..845e6ebba 100644
--- a/app/views/users/show.html.erb
+++ b/app/views/users/show.html.erb
@@ -5,17 +5,6 @@
<%= content_for(:title) %>
-
- <% if current_user.can_toggle_active?(@user) %>
- <% if @user.active? %>
- <%= govuk_link_to "Deactivate user", "/users/#{@user.id}/deactivate" %>
- <% else %>
-
- This user has been deactivated. <%= govuk_link_to "Reactivate user", "/users/#{@user.id}/reactivate" %>
-
- <% end %>
- <% end %>
-
Personal details
@@ -103,5 +92,21 @@
end
end %>
<% end %>
+
+
+ <% if current_user.can_toggle_active?(@user) %>
+ <% if @user.active? %>
+ <%= govuk_button_link_to "Deactivate user", deactivate_user_path(@user), warning: true %>
+ <% if current_user.support? %>
+ <%= govuk_button_to "Resend invite link", resend_invite_user_path(@user), secondary: true %>
+ <% end %>
+ <% else %>
+
+ This user has been deactivated. <%= govuk_button_link_to "Reactivate user", reactivate_user_path(@user) %>
+
+ <% end %>
+ <% end %>
+
+
diff --git a/config/routes.rb b/config/routes.rb
index fa0cb598e..8b61f1ece 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -110,6 +110,7 @@ Rails.application.routes.draw do
member do
get "deactivate", to: "users#deactivate"
get "reactivate", to: "users#reactivate"
+ post "resend-invite", to: "users#resend_invite"
end
end
diff --git a/spec/features/user_spec.rb b/spec/features/user_spec.rb
index 0c7f6c2c6..d4e1a6052 100644
--- a/spec/features/user_spec.rb
+++ b/spec/features/user_spec.rb
@@ -454,6 +454,85 @@ RSpec.describe "User Features" do
end
end
+ context "when signed in as support" do
+ let!(:user) { FactoryBot.create(:user, :support) }
+ let!(:other_user) { FactoryBot.create(:user, name: "new user", organisation: user.organisation, email: "new_user@example.com", confirmation_token: "abc") }
+
+ context "when reinviting a user before initial confirmation email has been sent" do
+ let(:personalisation) do
+ {
+ name: "new user",
+ email: "new_user@example.com",
+ organisation: other_user.organisation.name,
+ link: include("/account/confirmation?confirmation_token=#{other_user.confirmation_token}"),
+ }
+ end
+
+ before do
+ other_user.update!(initial_confirmation_sent: false)
+ allow(user).to receive(:need_two_factor_authentication?).and_return(false)
+ sign_in(user)
+ visit(user_path(user.id))
+ end
+
+ it "sends initial confirmable template email when the resend invite link is clicked" do
+ other_user.legacy_users.destroy_all
+ visit(user_path(other_user))
+ expect(notify_client).to receive(:send_email).with(email_address: "new_user@example.com", template_id: User::CONFIRMABLE_TEMPLATE_ID, personalisation:).once
+ click_button("Resend invite link")
+ end
+ end
+
+ context "when reinviting a user after initial confirmation email has been sent" do
+ let(:personalisation) do
+ {
+ name: "new user",
+ email: "new_user@example.com",
+ organisation: other_user.organisation.name,
+ link: include("/account/confirmation?confirmation_token=#{other_user.confirmation_token}"),
+ }
+ end
+
+ before do
+ other_user.update!(initial_confirmation_sent: true)
+ allow(user).to receive(:need_two_factor_authentication?).and_return(false)
+ sign_in(user)
+ visit(user_path(user.id))
+ end
+
+ it "sends and email when the resend invite link is clicked" do
+ other_user.legacy_users.destroy_all
+ visit(user_path(other_user))
+ expect(notify_client).to receive(:send_email).with(email_address: "new_user@example.com", template_id: User::RECONFIRMABLE_TEMPLATE_ID, personalisation:).once
+ click_button("Resend invite link")
+ end
+ end
+
+ context "when reinviting a legacy user" do
+ let(:personalisation) do
+ {
+ name: "new user",
+ email: "new_user@example.com",
+ organisation: other_user.organisation.name,
+ link: include("/account/confirmation?confirmation_token=#{other_user.confirmation_token}"),
+ }
+ end
+
+ before do
+ other_user.update!(initial_confirmation_sent: true)
+ allow(user).to receive(:need_two_factor_authentication?).and_return(false)
+ sign_in(user)
+ visit(user_path(user.id))
+ end
+
+ it "sends beta onboarding email to be sent when user is legacy" do
+ visit(user_path(other_user))
+ expect(notify_client).to receive(:send_email).with(email_address: "new_user@example.com", template_id: User::BETA_ONBOARDING_TEMPLATE_ID, personalisation:).once
+ click_button("Resend invite link")
+ end
+ end
+ end
+
context "when the user is a customer support person" do
let(:support_user) { FactoryBot.create(:user, :support, last_sign_in_at: Time.zone.now) }
let(:devise_notify_mailer) { DeviseNotifyMailer.new }
diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb
index a97086bb2..ecba12933 100644
--- a/spec/requests/users_controller_spec.rb
+++ b/spec/requests/users_controller_spec.rb
@@ -96,6 +96,13 @@ RSpec.describe UsersController, type: :request do
expect(response).to redirect_to("/account/sign-in")
end
end
+
+ describe "#resend_invite" do
+ it "does not allow resending activation emails" do
+ get deactivate_user_path(user.id), headers: headers, params: {}
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
end
context "when user is signed in as a data provider" do
@@ -123,6 +130,10 @@ RSpec.describe UsersController, type: :request do
expect(page).not_to have_link("Deactivate user", href: "/users/#{user.id}/deactivate")
end
+ it "does not allow resending invitation emails" do
+ expect(page).not_to have_button("Resend invite link")
+ end
+
context "when user is deactivated" do
before do
user.update!(active: false)
@@ -132,6 +143,10 @@ RSpec.describe UsersController, type: :request do
it "does not allow reactivating the user" do
expect(page).not_to have_link("Reactivate user", href: "/users/#{user.id}/reactivate")
end
+
+ it "does not allow resending invitation emails" do
+ expect(page).not_to have_link("Resend invite link")
+ end
end
end
@@ -184,6 +199,10 @@ RSpec.describe UsersController, type: :request do
it "does not allow reactivating the user" do
expect(page).not_to have_link("Reactivate user", href: "/users/#{other_user.id}/reactivate")
end
+
+ it "does not allow resending invitation emails" do
+ expect(page).not_to have_button("Resend invite link")
+ end
end
end
@@ -499,6 +518,10 @@ RSpec.describe UsersController, type: :request do
it "does not allow reactivating the user" do
expect(page).not_to have_link("Reactivate user", href: "/users/#{user.id}/reactivate")
end
+
+ it "does not allow resending invitation emails" do
+ expect(page).not_to have_button("Resend invite link")
+ end
end
end
@@ -530,6 +553,10 @@ RSpec.describe UsersController, type: :request do
expect(page).to have_link("Deactivate user", href: "/users/#{other_user.id}/deactivate")
end
+ it "does not allow you to resend invitation emails" do
+ expect(page).not_to have_button("Resend invite link")
+ end
+
context "when user is deactivated" do
before do
other_user.update!(active: false)
@@ -543,6 +570,10 @@ RSpec.describe UsersController, type: :request do
it "allows reactivating the user" do
expect(page).to have_link("Reactivate user", href: "/users/#{other_user.id}/reactivate")
end
+
+ it "does not allow you to resend invitation emails" do
+ expect(page).not_to have_button("Resend invite link")
+ end
end
end
@@ -1177,6 +1208,10 @@ RSpec.describe UsersController, type: :request do
expect(page).to have_link("Deactivate user", href: "/users/#{other_user.id}/deactivate")
end
+ it "allows you to resend invitation emails" do
+ expect(page).to have_button("Resend invite link")
+ end
+
context "when user is deactivated" do
before do
other_user.update!(active: false)
From 8e81d030510acca75bc1723a6ee1f558faf1f17f Mon Sep 17 00:00:00 2001
From: natdeanlewissoftwire
<94526761+natdeanlewissoftwire@users.noreply.github.com>
Date: Fri, 16 Jun 2023 16:50:29 +0100
Subject: [PATCH 11/19] feat: sort dpo names in alphabetical order (#1708)
---
app/components/data_protection_confirmation_banner_component.rb | 2 +-
.../data_protection_confirmation_banner_component_spec.rb | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/components/data_protection_confirmation_banner_component.rb b/app/components/data_protection_confirmation_banner_component.rb
index b1dc61152..b11c01f02 100644
--- a/app/components/data_protection_confirmation_banner_component.rb
+++ b/app/components/data_protection_confirmation_banner_component.rb
@@ -22,7 +22,7 @@ class DataProtectionConfirmationBannerComponent < ViewComponent::Base
def data_protection_officers_text
if org_or_user_org.data_protection_officers.any?
- "You can ask: #{org_or_user_org.data_protection_officers.map(&:name).join(', ')}"
+ "You can ask: #{org_or_user_org.data_protection_officers.map(&:name).sort_by(&:downcase).join(', ')}"
end
end
diff --git a/spec/components/data_protection_confirmation_banner_component_spec.rb b/spec/components/data_protection_confirmation_banner_component_spec.rb
index a76db4435..792e3a5a3 100644
--- a/spec/components/data_protection_confirmation_banner_component_spec.rb
+++ b/spec/components/data_protection_confirmation_banner_component_spec.rb
@@ -31,7 +31,7 @@ RSpec.describe DataProtectionConfirmationBannerComponent, type: :component do
create(:user, organisation:, is_dpo: true, name: "Test McTest")
end
- it "returns the correct text" do
+ it "returns the correct list of names, in alphabetical order)" do
expect(component.data_protection_officers_text).to eq("You can ask: Danny Rojas, Test McTest")
end
end
From aa4c250b3c4a6fe46327223c122f31b71a0a654f Mon Sep 17 00:00:00 2001
From: natdeanlewissoftwire
<94526761+natdeanlewissoftwire@users.noreply.github.com>
Date: Mon, 19 Jun 2023 08:54:30 +0100
Subject: [PATCH 12/19] CLDC-2390 CYA address redesign (#1696)
* feat: update cya design lettings
* feat: update cya sales
* feat: update some tests
* feat: fix error labels for address questions
* feat: fix error labels for address questions and update row parsers
* feat: update tests
* feat: update tests
* feat: rename display_label
---
app/helpers/question_view_helper.rb | 2 +-
.../form/lettings/questions/address_line1.rb | 17 ++-----
app/models/form/lettings/questions/county.rb | 7 ++-
.../questions/postcode_for_full_address.rb | 7 ++-
.../form/lettings/questions/town_or_city.rb | 7 ++-
app/models/form/question.rb | 14 +++---
.../form/sales/questions/address_line1.rb | 17 ++-----
app/models/form/sales/questions/county.rb | 7 ++-
.../questions/postcode_for_full_address.rb | 7 ++-
.../form/sales/questions/town_or_city.rb | 7 ++-
.../lettings/year2022/row_parser.rb | 4 +-
.../lettings/year2023/row_parser.rb | 4 +-
.../bulk_upload/sales/year2022/row_parser.rb | 4 +-
.../bulk_upload/sales/year2023/row_parser.rb | 4 +-
spec/helpers/question_view_helper_spec.rb | 8 ++++
.../lettings/questions/address_line1_spec.rb | 40 +++--------------
.../form/lettings/questions/county_spec.rb | 10 ++---
.../postcode_for_full_address_spec.rb | 10 ++---
.../lettings/questions/town_or_city_spec.rb | 10 ++---
.../sales/questions/address_line1_spec.rb | 44 ++++---------------
.../form/sales/questions/county_spec.rb | 10 ++---
.../postcode_for_full_address_spec.rb | 10 ++---
.../form/sales/questions/town_or_city_spec.rb | 10 ++---
.../lettings/year2023/row_parser_spec.rb | 4 +-
24 files changed, 99 insertions(+), 165 deletions(-)
diff --git a/app/helpers/question_view_helper.rb b/app/helpers/question_view_helper.rb
index fdb144c4e..a656344ca 100644
--- a/app/helpers/question_view_helper.rb
+++ b/app/helpers/question_view_helper.rb
@@ -7,7 +7,7 @@ module QuestionViewHelper
def legend(question, page_header, conditional)
{
- text: [question.question_number_string(conditional:), question.header.html_safe].compact.join(" - "),
+ text: [question.question_number_string(hidden: conditional || question.hide_question_number_on_page), question.header.html_safe].compact.join(" - "),
size: label_size(page_header, conditional, question),
tag: label_tag(page_header, conditional),
}
diff --git a/app/models/form/lettings/questions/address_line1.rb b/app/models/form/lettings/questions/address_line1.rb
index ef197e4fd..95702b8de 100644
--- a/app/models/form/lettings/questions/address_line1.rb
+++ b/app/models/form/lettings/questions/address_line1.rb
@@ -2,29 +2,20 @@ class Form::Lettings::Questions::AddressLine1 < ::Form::Question
def initialize(id, hsh, page)
super
@id = "address_line1"
- @check_answer_label = "Address"
@header = "Address line 1"
+ @error_label = "Address line 1"
@type = "text"
@plain_label = true
- @check_answer_label = "Q12 - Address"
+ @check_answer_label = "Address lines 1 and 2"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
+ @question_number = 12
+ @hide_question_number_on_page = true
end
def answer_label(log, _current_user = nil)
[
log.address_line1,
log.address_line2,
- log.postcode_full,
- log.town_or_city,
- log.county,
].select(&:present?).join("\n")
end
-
- def get_extra_check_answer_value(log)
- return unless log.is_la_inferred?
-
- la = LocalAuthority.find_by(code: log.la)&.name
-
- la.presence
- end
end
diff --git a/app/models/form/lettings/questions/county.rb b/app/models/form/lettings/questions/county.rb
index 9f0dc7138..8eaa410eb 100644
--- a/app/models/form/lettings/questions/county.rb
+++ b/app/models/form/lettings/questions/county.rb
@@ -5,10 +5,9 @@ class Form::Lettings::Questions::County < ::Form::Question
@header = "County (optional)"
@type = "text"
@plain_label = true
+ @check_answer_label = "County"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
- end
-
- def hidden_in_check_answers?(_log = nil, _current_user = nil)
- true
+ @question_number = 12
+ @hide_question_number_on_page = true
end
end
diff --git a/app/models/form/lettings/questions/postcode_for_full_address.rb b/app/models/form/lettings/questions/postcode_for_full_address.rb
index 4f41867d7..8f7de5f52 100644
--- a/app/models/form/lettings/questions/postcode_for_full_address.rb
+++ b/app/models/form/lettings/questions/postcode_for_full_address.rb
@@ -17,10 +17,9 @@ class Form::Lettings::Questions::PostcodeForFullAddress < ::Form::Question
},
}
@plain_label = true
+ @check_answer_label = "Postcode"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
- end
-
- def hidden_in_check_answers?(_log = nil, _current_user = nil)
- true
+ @question_number = 12
+ @hide_question_number_on_page = true
end
end
diff --git a/app/models/form/lettings/questions/town_or_city.rb b/app/models/form/lettings/questions/town_or_city.rb
index 501e9bec4..43aa48625 100644
--- a/app/models/form/lettings/questions/town_or_city.rb
+++ b/app/models/form/lettings/questions/town_or_city.rb
@@ -5,10 +5,9 @@ class Form::Lettings::Questions::TownOrCity < ::Form::Question
@header = "Town or city"
@type = "text"
@plain_label = true
+ @check_answer_label = "Town or city"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
- end
-
- def hidden_in_check_answers?(_log = nil, _current_user = nil)
- true
+ @question_number = 12
+ @hide_question_number_on_page = true
end
end
diff --git a/app/models/form/question.rb b/app/models/form/question.rb
index 82d3a09b5..2d6b3aa58 100644
--- a/app/models/form/question.rb
+++ b/app/models/form/question.rb
@@ -4,7 +4,7 @@ class Form::Question
:conditional_for, :readonly, :answer_options, :page, :check_answer_label,
:inferred_answers, :hidden_in_check_answers, :inferred_check_answers_value,
:guidance_partial, :prefix, :suffix, :requires_js, :fields_added, :derived,
- :check_answers_card_number, :unresolved_hint_text, :question_number, :plain_label
+ :check_answers_card_number, :unresolved_hint_text, :question_number, :hide_question_number_on_page, :plain_label, :error_label
module GuidancePosition
TOP = 1
@@ -41,7 +41,9 @@ class Form::Question
@check_answers_card_number = hsh["check_answers_card_number"] || 0
@unresolved_hint_text = hsh["unresolved_hint_text"]
@question_number = hsh["question_number"]
+ @hide_question_number_on_page = hsh["hide_question_number_on_page"] || false
@plain_label = hsh["plain_label"]
+ @error_label = hsh["error_label"]
@disable_clearing_if_not_routed_or_dynamic_answer_options = hsh["disable_clearing_if_not_routed_or_dynamic_answer_options"]
end
end
@@ -194,15 +196,15 @@ class Form::Question
type == "radio" && RADIO_REFUSED_VALUE[id.to_sym]&.include?(value)
end
- def display_label
- check_answer_label || header || id.humanize
+ def error_display_label
+ error_label || check_answer_label || header || id.humanize
end
def unanswered_error_message
return I18n.t("validations.declaration.missing") if id == "declaration"
return I18n.t("validations.privacynotice.missing") if id == "privacynotice"
- I18n.t("validations.not_answered", question: display_label.downcase)
+ I18n.t("validations.not_answered", question: error_display_label.downcase)
end
def suffix_label(log)
@@ -241,8 +243,8 @@ class Form::Question
selected_answer_option_is_derived?(log) || has_inferred_check_answers_value?(log)
end
- def question_number_string(conditional: false)
- if @question_number && !conditional && form.start_date.year >= 2023
+ def question_number_string(hidden: false)
+ if @question_number && !hidden && form.start_date.year >= 2023
"Q#{@question_number}"
end
end
diff --git a/app/models/form/sales/questions/address_line1.rb b/app/models/form/sales/questions/address_line1.rb
index edee2e7ee..fcf31a082 100644
--- a/app/models/form/sales/questions/address_line1.rb
+++ b/app/models/form/sales/questions/address_line1.rb
@@ -2,29 +2,20 @@ class Form::Sales::Questions::AddressLine1 < ::Form::Question
def initialize(id, hsh, page)
super
@id = "address_line1"
- @check_answer_label = "Address"
@header = "Address line 1"
+ @error_label = "Address line 1"
@type = "text"
@plain_label = true
- @check_answer_label = "Q15 - Address"
+ @check_answer_label = "Address lines 1 and 2"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
+ @question_number = 15
+ @hide_question_number_on_page = true
end
def answer_label(log, _current_user = nil)
[
log.address_line1,
log.address_line2,
- log.postcode_full,
- log.town_or_city,
- log.county,
].select(&:present?).join("\n")
end
-
- def get_extra_check_answer_value(log)
- return unless log.is_la_inferred?
-
- la = LocalAuthority.find_by(code: log.la)&.name
-
- la.presence
- end
end
diff --git a/app/models/form/sales/questions/county.rb b/app/models/form/sales/questions/county.rb
index 6586cb9e6..9bd68d350 100644
--- a/app/models/form/sales/questions/county.rb
+++ b/app/models/form/sales/questions/county.rb
@@ -5,10 +5,9 @@ class Form::Sales::Questions::County < ::Form::Question
@header = "County (optional)"
@type = "text"
@plain_label = true
+ @check_answer_label = "County"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
- end
-
- def hidden_in_check_answers?(_log = nil, _current_user = nil)
- true
+ @question_number = 15
+ @hide_question_number_on_page = true
end
end
diff --git a/app/models/form/sales/questions/postcode_for_full_address.rb b/app/models/form/sales/questions/postcode_for_full_address.rb
index 5d3b9f122..f4a21d0e3 100644
--- a/app/models/form/sales/questions/postcode_for_full_address.rb
+++ b/app/models/form/sales/questions/postcode_for_full_address.rb
@@ -17,10 +17,9 @@ class Form::Sales::Questions::PostcodeForFullAddress < ::Form::Question
},
}
@plain_label = true
+ @check_answer_label = "Postcode"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
- end
-
- def hidden_in_check_answers?(_log = nil, _current_user = nil)
- true
+ @question_number = 15
+ @hide_question_number_on_page = true
end
end
diff --git a/app/models/form/sales/questions/town_or_city.rb b/app/models/form/sales/questions/town_or_city.rb
index 25acfe036..b136d186c 100644
--- a/app/models/form/sales/questions/town_or_city.rb
+++ b/app/models/form/sales/questions/town_or_city.rb
@@ -5,10 +5,9 @@ class Form::Sales::Questions::TownOrCity < ::Form::Question
@header = "Town or city"
@type = "text"
@plain_label = true
+ @check_answer_label = "Town or city"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true
- end
-
- def hidden_in_check_answers?(_log = nil, _current_user = nil)
- true
+ @question_number = 15
+ @hide_question_number_on_page = true
end
end
diff --git a/app/services/bulk_upload/lettings/year2022/row_parser.rb b/app/services/bulk_upload/lettings/year2022/row_parser.rb
index b79447b67..c6251c81f 100644
--- a/app/services/bulk_upload/lettings/year2022/row_parser.rb
+++ b/app/services/bulk_upload/lettings/year2022/row_parser.rb
@@ -774,13 +774,13 @@ private
if setup_question?(question)
fields.each do |field|
if errors.select { |e| fields.include?(e.attribute) }.none?
- errors.add(field, I18n.t("validations.not_answered", question: question.check_answer_label&.downcase), category: :setup)
+ errors.add(field, I18n.t("validations.not_answered", question: question.error_display_label&.downcase), category: :setup)
end
end
else
fields.each do |field|
unless errors.any? { |e| fields.include?(e.attribute) }
- errors.add(field, I18n.t("validations.not_answered", question: question.check_answer_label&.downcase))
+ errors.add(field, I18n.t("validations.not_answered", question: question.error_display_label&.downcase))
end
end
end
diff --git a/app/services/bulk_upload/lettings/year2023/row_parser.rb b/app/services/bulk_upload/lettings/year2023/row_parser.rb
index 0912b02e4..9c3c76fab 100644
--- a/app/services/bulk_upload/lettings/year2023/row_parser.rb
+++ b/app/services/bulk_upload/lettings/year2023/row_parser.rb
@@ -681,14 +681,14 @@ private
if setup_question?(question)
fields.each do |field|
if errors.select { |e| fields.include?(e.attribute) }.none?
- question_text = question.check_answer_label.presence || question.header.presence || "this question"
+ question_text = question.error_display_label.presence || "this question"
errors.add(field, I18n.t("validations.not_answered", question: question_text.downcase), category: :setup)
end
end
else
fields.each do |field|
unless errors.any? { |e| fields.include?(e.attribute) }
- question_text = question.check_answer_label.presence || question.header.presence || "this question"
+ question_text = question.error_display_label.presence || "this question"
errors.add(field, I18n.t("validations.not_answered", question: question_text.downcase))
end
end
diff --git a/app/services/bulk_upload/sales/year2022/row_parser.rb b/app/services/bulk_upload/sales/year2022/row_parser.rb
index ed51a0601..1ac6258fd 100644
--- a/app/services/bulk_upload/sales/year2022/row_parser.rb
+++ b/app/services/bulk_upload/sales/year2022/row_parser.rb
@@ -1057,9 +1057,9 @@ private
fields.each do |field|
unless errors.any? { |e| fields.include?(e.attribute) }
if setup_question?(question)
- errors.add(field, I18n.t("validations.not_answered", question: question.check_answer_label&.downcase), category: :setup)
+ errors.add(field, I18n.t("validations.not_answered", question: question.error_display_label&.downcase), category: :setup)
else
- errors.add(field, I18n.t("validations.not_answered", question: question.check_answer_label&.downcase))
+ errors.add(field, I18n.t("validations.not_answered", question: question.error_display_label&.downcase))
end
end
end
diff --git a/app/services/bulk_upload/sales/year2023/row_parser.rb b/app/services/bulk_upload/sales/year2023/row_parser.rb
index e0f2b727f..17ec939d4 100644
--- a/app/services/bulk_upload/sales/year2023/row_parser.rb
+++ b/app/services/bulk_upload/sales/year2023/row_parser.rb
@@ -1210,13 +1210,13 @@ private
if setup_question?(question)
fields.each do |field|
unless errors.any? { |e| fields.include?(e.attribute) }
- errors.add(field, I18n.t("validations.not_answered", question: question.check_answer_label&.downcase), category: :setup)
+ errors.add(field, I18n.t("validations.not_answered", question: question.error_display_label&.downcase), category: :setup)
end
end
else
fields.each do |field|
unless errors.any? { |e| fields.include?(e.attribute) }
- errors.add(field, I18n.t("validations.not_answered", question: question.check_answer_label&.downcase))
+ errors.add(field, I18n.t("validations.not_answered", question: question.error_display_label&.downcase))
end
end
end
diff --git a/spec/helpers/question_view_helper_spec.rb b/spec/helpers/question_view_helper_spec.rb
index c24540709..2f7e82285 100644
--- a/spec/helpers/question_view_helper_spec.rb
+++ b/spec/helpers/question_view_helper_spec.rb
@@ -53,6 +53,10 @@ RSpec.describe QuestionViewHelper do
def plain_label
nil
end
+
+ def hide_question_number_on_page
+ false
+ end
end
end
@@ -98,6 +102,10 @@ RSpec.describe QuestionViewHelper do
def plain_label
true
end
+
+ def hide_question_number_on_page
+ false
+ end
end
end
diff --git a/spec/models/form/lettings/questions/address_line1_spec.rb b/spec/models/form/lettings/questions/address_line1_spec.rb
index 2c13aef48..0fc91a586 100644
--- a/spec/models/form/lettings/questions/address_line1_spec.rb
+++ b/spec/models/form/lettings/questions/address_line1_spec.rb
@@ -19,12 +19,16 @@ RSpec.describe Form::Lettings::Questions::AddressLine1, type: :model do
expect(question.header).to eq("Address line 1")
end
- it "has the correct question_number" do
- expect(question.question_number).to be_nil
+ it "has the correct error label" do
+ expect(question.error_label).to eq("Address line 1")
end
it "has the correct check_answer_label" do
- expect(question.check_answer_label).to eq("Q12 - Address")
+ expect(question.check_answer_label).to eq("Address lines 1 and 2")
+ end
+
+ it "has the correct question_number" do
+ expect(question.question_number).to eq(12)
end
it "has the correct type" do
@@ -46,34 +50,4 @@ RSpec.describe Form::Lettings::Questions::AddressLine1, type: :model do
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to be_nil
end
-
- describe "has the correct get_extra_check_answer_value" do
- context "when la is not present" do
- let(:log) { create(:lettings_log, la: nil) }
-
- it "returns nil" do
- expect(question.get_extra_check_answer_value(log)).to be_nil
- end
- end
-
- context "when la is present but not inferred" do
- let(:log) { create(:lettings_log, la: "E09000003", is_la_inferred: false) }
-
- it "returns nil" do
- expect(question.get_extra_check_answer_value(log)).to be_nil
- end
- end
-
- context "when la is present and inferred" do
- let(:log) { create(:lettings_log, la: "E09000003") }
-
- before do
- allow(log).to receive(:is_la_inferred?).and_return(true)
- end
-
- it "returns the la" do
- expect(question.get_extra_check_answer_value(log)).to eq("Barnet")
- end
- end
- end
end
diff --git a/spec/models/form/lettings/questions/county_spec.rb b/spec/models/form/lettings/questions/county_spec.rb
index cf8f814e4..1955dad8f 100644
--- a/spec/models/form/lettings/questions/county_spec.rb
+++ b/spec/models/form/lettings/questions/county_spec.rb
@@ -20,7 +20,11 @@ RSpec.describe Form::Lettings::Questions::County, type: :model do
end
it "has the correct check_answer_label" do
- expect(question.check_answer_label).to be_nil
+ expect(question.check_answer_label).to eq("County")
+ end
+
+ it "has the correct question_number" do
+ expect(question.question_number).to eq(12)
end
it "has the correct type" do
@@ -42,8 +46,4 @@ RSpec.describe Form::Lettings::Questions::County, type: :model do
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to be_nil
end
-
- it "has the correct hidden_in_check_answers" do
- expect(question.hidden_in_check_answers?).to eq(true)
- end
end
diff --git a/spec/models/form/lettings/questions/postcode_for_full_address_spec.rb b/spec/models/form/lettings/questions/postcode_for_full_address_spec.rb
index ccb02ef07..337d1e6fe 100644
--- a/spec/models/form/lettings/questions/postcode_for_full_address_spec.rb
+++ b/spec/models/form/lettings/questions/postcode_for_full_address_spec.rb
@@ -20,7 +20,11 @@ RSpec.describe Form::Lettings::Questions::PostcodeForFullAddress, type: :model d
end
it "has the correct check_answer_label" do
- expect(question.check_answer_label).to be_nil
+ expect(question.check_answer_label).to eq("Postcode")
+ end
+
+ it "has the correct question_number" do
+ expect(question.question_number).to eq(12)
end
it "has the correct type" do
@@ -55,8 +59,4 @@ RSpec.describe Form::Lettings::Questions::PostcodeForFullAddress, type: :model d
"value" => "Not known",
}])
end
-
- it "has the correct hidden_in_check_answers" do
- expect(question.hidden_in_check_answers?).to eq(true)
- end
end
diff --git a/spec/models/form/lettings/questions/town_or_city_spec.rb b/spec/models/form/lettings/questions/town_or_city_spec.rb
index 8741fb058..a18d63c04 100644
--- a/spec/models/form/lettings/questions/town_or_city_spec.rb
+++ b/spec/models/form/lettings/questions/town_or_city_spec.rb
@@ -20,7 +20,11 @@ RSpec.describe Form::Lettings::Questions::TownOrCity, type: :model do
end
it "has the correct check_answer_label" do
- expect(question.check_answer_label).to be_nil
+ expect(question.check_answer_label).to eq("Town or city")
+ end
+
+ it "has the correct question_number" do
+ expect(question.question_number).to eq(12)
end
it "has the correct type" do
@@ -42,8 +46,4 @@ RSpec.describe Form::Lettings::Questions::TownOrCity, type: :model do
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to be_nil
end
-
- it "has the correct hidden_in_check_answers" do
- expect(question.hidden_in_check_answers?).to eq(true)
- end
end
diff --git a/spec/models/form/sales/questions/address_line1_spec.rb b/spec/models/form/sales/questions/address_line1_spec.rb
index 8c73feef2..12643a27d 100644
--- a/spec/models/form/sales/questions/address_line1_spec.rb
+++ b/spec/models/form/sales/questions/address_line1_spec.rb
@@ -11,10 +11,6 @@ RSpec.describe Form::Sales::Questions::AddressLine1, type: :model do
expect(question.page).to eq(page)
end
- it "has the correct question_number" do
- expect(question.question_number).to be_nil
- end
-
it "has the correct id" do
expect(question.id).to eq("address_line1")
end
@@ -23,8 +19,16 @@ RSpec.describe Form::Sales::Questions::AddressLine1, type: :model do
expect(question.header).to eq("Address line 1")
end
+ it "has the correct error label" do
+ expect(question.error_label).to eq("Address line 1")
+ end
+
it "has the correct check_answer_label" do
- expect(question.check_answer_label).to eq("Q15 - Address")
+ expect(question.check_answer_label).to eq("Address lines 1 and 2")
+ end
+
+ it "has the correct question_number" do
+ expect(question.question_number).to eq(15)
end
it "has the correct type" do
@@ -46,34 +50,4 @@ RSpec.describe Form::Sales::Questions::AddressLine1, type: :model do
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to be_nil
end
-
- describe "has the correct get_extra_check_answer_value" do
- context "when la is not present" do
- let(:log) { create(:sales_log, la: nil) }
-
- it "returns nil" do
- expect(question.get_extra_check_answer_value(log)).to be_nil
- end
- end
-
- context "when la is present but not inferred" do
- let(:log) { create(:sales_log, la: "E09000003", is_la_inferred: false) }
-
- it "returns nil" do
- expect(question.get_extra_check_answer_value(log)).to be_nil
- end
- end
-
- context "when la is present and inferred" do
- let(:log) { create(:sales_log, la: "E09000003") }
-
- before do
- allow(log).to receive(:is_la_inferred?).and_return(true)
- end
-
- it "returns the la" do
- expect(question.get_extra_check_answer_value(log)).to eq("Barnet")
- end
- end
- end
end
diff --git a/spec/models/form/sales/questions/county_spec.rb b/spec/models/form/sales/questions/county_spec.rb
index d5800b260..19bb59eb4 100644
--- a/spec/models/form/sales/questions/county_spec.rb
+++ b/spec/models/form/sales/questions/county_spec.rb
@@ -20,7 +20,11 @@ RSpec.describe Form::Sales::Questions::County, type: :model do
end
it "has the correct check_answer_label" do
- expect(question.check_answer_label).to be_nil
+ expect(question.check_answer_label).to eq("County")
+ end
+
+ it "has the correct question_number" do
+ expect(question.question_number).to eq(15)
end
it "has the correct type" do
@@ -42,8 +46,4 @@ RSpec.describe Form::Sales::Questions::County, type: :model do
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to be_nil
end
-
- it "has the correct hidden_in_check_answers" do
- expect(question.hidden_in_check_answers?).to eq(true)
- end
end
diff --git a/spec/models/form/sales/questions/postcode_for_full_address_spec.rb b/spec/models/form/sales/questions/postcode_for_full_address_spec.rb
index a655172ec..a3c8ddfb8 100644
--- a/spec/models/form/sales/questions/postcode_for_full_address_spec.rb
+++ b/spec/models/form/sales/questions/postcode_for_full_address_spec.rb
@@ -20,7 +20,11 @@ RSpec.describe Form::Sales::Questions::PostcodeForFullAddress, type: :model do
end
it "has the correct check_answer_label" do
- expect(question.check_answer_label).to be_nil
+ expect(question.check_answer_label).to eq("Postcode")
+ end
+
+ it "has the correct question_number" do
+ expect(question.question_number).to eq(15)
end
it "has the correct type" do
@@ -55,8 +59,4 @@ RSpec.describe Form::Sales::Questions::PostcodeForFullAddress, type: :model do
"value" => "Not known",
}])
end
-
- it "has the correct hidden_in_check_answers" do
- expect(question.hidden_in_check_answers?).to eq(true)
- end
end
diff --git a/spec/models/form/sales/questions/town_or_city_spec.rb b/spec/models/form/sales/questions/town_or_city_spec.rb
index d66315864..df1c905b4 100644
--- a/spec/models/form/sales/questions/town_or_city_spec.rb
+++ b/spec/models/form/sales/questions/town_or_city_spec.rb
@@ -20,7 +20,11 @@ RSpec.describe Form::Sales::Questions::TownOrCity, type: :model do
end
it "has the correct check_answer_label" do
- expect(question.check_answer_label).to be_nil
+ expect(question.check_answer_label).to eq("Town or city")
+ end
+
+ it "has the correct question_number" do
+ expect(question.question_number).to eq(15)
end
it "has the correct type" do
@@ -42,8 +46,4 @@ RSpec.describe Form::Sales::Questions::TownOrCity, type: :model do
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to be_nil
end
-
- it "has the correct hidden_in_check_answers" do
- expect(question.hidden_in_check_answers?).to eq(true)
- end
end
diff --git a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb
index 5bb6e8958..f7e5527a5 100644
--- a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb
+++ b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb
@@ -340,7 +340,7 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do
it "fetches the question's check_answer_label if it exists, otherwise it gets the question's header" do
parser.valid?
- expect(parser.errors[:field_19]).to eql(["You must answer q12 - address"])
+ expect(parser.errors[:field_19]).to eql(["You must answer address line 1"])
expect(parser.errors[:field_21]).to eql(["You must answer town or city"])
end
end
@@ -937,7 +937,7 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do
it "adds appropriate errors" do
expect(parser.errors[:field_18]).to eql(["You must answer UPRN"])
- expect(parser.errors[:field_19]).to eql(["You must answer q12 - address"])
+ expect(parser.errors[:field_19]).to eql(["You must answer address line 1"])
expect(parser.errors[:field_21]).to eql(["You must answer town or city"])
end
end
From b63a0693117bf356691406bb0f335df06830a498 Mon Sep 17 00:00:00 2001
From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com>
Date: Mon, 19 Jun 2023 09:00:02 +0100
Subject: [PATCH 13/19] CLDC-2374 Update bulk upload guidance (#1703)
* Update 23/24 lettings prepare your file guidance
* Separate and update bulk upload sales guidance
* Update shared guidance and sales file path
* Rename old_template_path to legacy_template_path
* Remove wrong bullet point
* Update legacy template path based on year
---
.../forms/bulk_upload_lettings/guidance.rb | 4 +--
.../bulk_upload_lettings/prepare_your_file.rb | 2 +-
.../forms/bulk_upload_sales/guidance.rb | 4 +--
.../bulk_upload_sales/prepare_your_file.rb | 16 +++++++--
.../forms/prepare_your_file_2023.html.erb | 5 +--
...ml.erb => prepare_your_file_2022.html.erb} | 3 +-
.../forms/prepare_your_file_2023.html.erb | 36 +++++++++++++++++++
.../bulk_upload_shared/guidance.html.erb | 9 ++++-
8 files changed, 66 insertions(+), 13 deletions(-)
rename app/views/bulk_upload_sales_logs/forms/{prepare_your_file.html.erb => prepare_your_file_2022.html.erb} (79%)
create mode 100644 app/views/bulk_upload_sales_logs/forms/prepare_your_file_2023.html.erb
diff --git a/app/models/forms/bulk_upload_lettings/guidance.rb b/app/models/forms/bulk_upload_lettings/guidance.rb
index 55db10718..862a8af51 100644
--- a/app/models/forms/bulk_upload_lettings/guidance.rb
+++ b/app/models/forms/bulk_upload_lettings/guidance.rb
@@ -15,8 +15,8 @@ module Forms
bulk_upload_lettings_log_path(id: "prepare-your-file", form: { year: })
end
- def old_template_path
- Forms::BulkUploadLettings::PrepareYourFile.new.old_template_path
+ def legacy_template_path
+ Forms::BulkUploadLettings::PrepareYourFile.new.legacy_template_path
end
def template_path
diff --git a/app/models/forms/bulk_upload_lettings/prepare_your_file.rb b/app/models/forms/bulk_upload_lettings/prepare_your_file.rb
index 26032e614..601f09848 100644
--- a/app/models/forms/bulk_upload_lettings/prepare_your_file.rb
+++ b/app/models/forms/bulk_upload_lettings/prepare_your_file.rb
@@ -30,7 +30,7 @@ module Forms
bulk_upload_lettings_log_path(id: page_id, form: { year:, needstype: })
end
- def old_template_path
+ def legacy_template_path
case year
when 2022
"/files/bulk-upload-lettings-template-2022-23.xlsx"
diff --git a/app/models/forms/bulk_upload_sales/guidance.rb b/app/models/forms/bulk_upload_sales/guidance.rb
index c9869ee14..eb472ad3a 100644
--- a/app/models/forms/bulk_upload_sales/guidance.rb
+++ b/app/models/forms/bulk_upload_sales/guidance.rb
@@ -15,8 +15,8 @@ module Forms
bulk_upload_sales_log_path(id: "prepare-your-file", form: { year: })
end
- def old_template_path
- Forms::BulkUploadLettings::PrepareYourFile.new.old_template_path
+ def legacy_template_path
+ Forms::BulkUploadSales::PrepareYourFile.new.legacy_template_path
end
def template_path
diff --git a/app/models/forms/bulk_upload_sales/prepare_your_file.rb b/app/models/forms/bulk_upload_sales/prepare_your_file.rb
index 710cdbef3..9425a6662 100644
--- a/app/models/forms/bulk_upload_sales/prepare_your_file.rb
+++ b/app/models/forms/bulk_upload_sales/prepare_your_file.rb
@@ -8,7 +8,12 @@ module Forms
attribute :year, :integer
def view_path
- "bulk_upload_sales_logs/forms/prepare_your_file"
+ case year
+ when 2022
+ "bulk_upload_sales_logs/forms/prepare_your_file_2022"
+ else
+ "bulk_upload_sales_logs/forms/prepare_your_file_2023"
+ end
end
def back_path
@@ -23,8 +28,13 @@ module Forms
bulk_upload_sales_log_path(id: "upload-your-file", form: { year: })
end
- def old_template_path
- "/files/bulk-upload-sales-template-2022-23.xlsx"
+ def legacy_template_path
+ case year
+ when 2022
+ "/files/bulk-upload-sales-template-2022-23.xlsx"
+ else
+ "/files/bulk-upload-sales-legacy-template-2023-24.xlsx"
+ end
end
def template_path
diff --git a/app/views/bulk_upload_lettings_logs/forms/prepare_your_file_2023.html.erb b/app/views/bulk_upload_lettings_logs/forms/prepare_your_file_2023.html.erb
index a1d25397b..f15972491 100644
--- a/app/views/bulk_upload_lettings_logs/forms/prepare_your_file_2023.html.erb
+++ b/app/views/bulk_upload_lettings_logs/forms/prepare_your_file_2023.html.erb
@@ -12,13 +12,14 @@
Download template
+ Use one of these templates to upload logs for 2023/24:
-
- If your organisation is new to using bulk upload or you have updated your HMS export, use <%= govuk_link_to "this template (improved question ordering)", @form.template_path %>.
+ <%= govuk_link_to "New template", @form.template_path %>: In this template, the questions are in the same order as the 2023/24 paper form and web form.
-
- If your organisation was using bulk upload on the previous CORE website and hasn't changed its HMS to be compatible with the new CORE service, use <%= govuk_link_to "this template", @form.old_template_path %>.
+ <%= govuk_link_to "Legacy template", @form.legacy_template_path %>: In this template, the questions are in the same order as the 2022/23 template, with new questions added on to the end.
diff --git a/app/views/bulk_upload_sales_logs/forms/prepare_your_file.html.erb b/app/views/bulk_upload_sales_logs/forms/prepare_your_file_2022.html.erb
similarity index 79%
rename from app/views/bulk_upload_sales_logs/forms/prepare_your_file.html.erb
rename to app/views/bulk_upload_sales_logs/forms/prepare_your_file_2022.html.erb
index 71e759306..624b3fe8b 100644
--- a/app/views/bulk_upload_sales_logs/forms/prepare_your_file.html.erb
+++ b/app/views/bulk_upload_sales_logs/forms/prepare_your_file_2022.html.erb
@@ -12,8 +12,7 @@
Download template
- - If your organisation is new to using bulk upload or you have updated your HMS export use <%= govuk_link_to "this template (improved question ordering)", @form.template_path %>
- - If your organisation was using bulk upload on the previous CORE website and hasn't changed its HMS to be compatible with the new CORE service, use <%= govuk_link_to "this template", @form.old_template_path %>
+ - Download and use <%= govuk_link_to "this template", @form.legacy_template_path %>.
Create your file
diff --git a/app/views/bulk_upload_sales_logs/forms/prepare_your_file_2023.html.erb b/app/views/bulk_upload_sales_logs/forms/prepare_your_file_2023.html.erb
new file mode 100644
index 000000000..7fc522e88
--- /dev/null
+++ b/app/views/bulk_upload_sales_logs/forms/prepare_your_file_2023.html.erb
@@ -0,0 +1,36 @@
+<% content_for :before_content do %>
+ <%= govuk_back_link href: @form.back_path %>
+<% end %>
+
+
+
+ <%= form_with model: @form, scope: :form, url: bulk_upload_sales_log_path(id: "prepare-your-file"), method: :patch do |f| %>
+ <%= f.hidden_field :year %>
+
+
Upload sales logs in bulk (<%= @form.year_combo %>)
+
Prepare your file
+
+
Download template
+
+ - <%= govuk_link_to "New template", @form.template_path %>: In this template, the questions are in the same order as the 2023/24 paper form and web form.
+ - <%= govuk_link_to "Legacy template", @form.legacy_template_path %>: In this template, the questions are in the same order as the 2022/23 template, with new questions added on to the end.
+
+
+
Create your file
+
+ - Fill in the template with CORE data from your housing management system according to the <%= govuk_link_to "Sales #{@form.year_combo} Bulk Upload Specification", @form.specification_path %>
+ - Username field: To assign a log to someone else, enter the email address they use to log into CORE
+ - If you have to manually enter large volumes of data into the bulk upload template, we recommend creating logs directly in the service instead. <%= govuk_link_to "Find out more about exporting your data", bulk_upload_sales_log_path(id: "guidance", form: { year: @form.year }) %>
+ - If you are using the new template, keep the headers. If you are using the legacy template, you can either keep or remove the headers.
+
+
+
Save your file
+
+ - Save your file as a CSV
+ - Your file should now be ready to upload
+
+
+ <%= f.govuk_submit %>
+ <% end %>
+
+
diff --git a/app/views/bulk_upload_shared/guidance.html.erb b/app/views/bulk_upload_shared/guidance.html.erb
index 9d2147105..d2fdc9230 100644
--- a/app/views/bulk_upload_shared/guidance.html.erb
+++ b/app/views/bulk_upload_shared/guidance.html.erb
@@ -54,7 +54,14 @@
Getting help
-
There is no step-by-step bulk upload guide like there is with single log upload. However, you can download <%= govuk_link_to "our template", @form.template_path %>, which you can copy-paste data into from your systems column-by-column. You can also view a post-upload report showing any data errors, and our <%= govuk_link_to "data specification", @form.specification_path, target: "_blank" %> can help fix these.
+
There is no step-by-step bulk upload guide like there is with single log upload. However, you can download <%= @form.year == 2022 ? govuk_link_to("our template", @form.template_path) : "one of our templates" %>, which you can copy-paste data into from your systems column-by-column. You can also view a post-upload report showing any data errors, and our <%= govuk_link_to "data specification", @form.specification_path, target: "_blank" %> can help fix these.
+ <% if @form.year == 2023 %>
+ <%= govuk_details(summary_text: "How to choose the right template") do %>
+
Use one of these templates to upload logs for 2023/24:
+
<%= govuk_link_to "New template", @form.template_path %>: In this template, the questions are in the same order as the 2023/24 paper form and web form. Use this template if your organisation is new to bulk upload or if your housing management system matches the new column ordering.
+
<%= govuk_link_to "Legacy template", @form.legacy_template_path %>: In this template, the questions are in the same order as the 2022/23 template, with new questions added on to the end. Use this template if you have not updated your system to match the new template yet.
+ <% end %>
+ <% end %>
If you still need support mapping data in the way we need, DLUHC’s helpdesk can help. If your data is across multiple systems, or is hard to export as a single file in the correct format, you could try different exports, or copy-pasting data by hand.
From 8c23cf6550225ab0149ef41bf104603b2c4654f2 Mon Sep 17 00:00:00 2001
From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com>
Date: Mon, 19 Jun 2023 09:52:10 +0100
Subject: [PATCH 14/19] CLDC-2420 Update deactivating soon label (#1701)
* Display active status is location/scheme deactivation is in more than 6 months
* Override existing location deactivation period with new deactivation
* Override existing scheme deactivation period with new deactivation
* add reactivate to policy
* Change status tag method
* Update instance double in test
* Update deactivates_in_a_long_time? method
* Uncoment a test
---
app/controllers/locations_controller.rb | 8 ++-
app/controllers/schemes_controller.rb | 8 ++-
app/helpers/locations_helper.rb | 2 +-
app/helpers/schemes_helper.rb | 4 +-
app/helpers/tag_helper.rb | 6 ++
app/models/location.rb | 4 ++
app/models/location_deactivation_period.rb | 2 +-
app/models/scheme.rb | 4 ++
app/models/scheme_deactivation_period.rb | 2 +-
app/policies/scheme_policy.rb | 2 +
app/views/locations/index.html.erb | 2 +-
app/views/locations/show.html.erb | 2 +-
app/views/schemes/_scheme_list.html.erb | 2 +-
spec/requests/locations_controller_spec.rb | 80 ++++++++++++++++++++--
spec/requests/schemes_controller_spec.rb | 73 ++++++++++++++++++++
spec/views/locations/show.html.erb_spec.rb | 1 +
16 files changed, 186 insertions(+), 16 deletions(-)
diff --git a/app/controllers/locations_controller.rb b/app/controllers/locations_controller.rb
index 7ba4a1115..199c295dd 100644
--- a/app/controllers/locations_controller.rb
+++ b/app/controllers/locations_controller.rb
@@ -149,7 +149,11 @@ class LocationsController < ApplicationController
def show; end
def new_deactivation
- @location_deactivation_period = LocationDeactivationPeriod.new
+ @location_deactivation_period = if @location.deactivates_in_a_long_time?
+ @location.open_deactivation || LocationDeactivationPeriod.new
+ else
+ LocationDeactivationPeriod.new
+ end
if params[:location_deactivation_period].blank?
render "toggle_active", locals: { action: "deactivate" }
@@ -176,7 +180,7 @@ class LocationsController < ApplicationController
end
def deactivate
- if @location.location_deactivation_periods.create!(deactivation_date: params[:deactivation_date])
+ if @location.open_deactivation&.update!(deactivation_date: params[:deactivation_date]) || @location.location_deactivation_periods.create!(deactivation_date: params[:deactivation_date])
logs = reset_location_and_scheme_for_logs!
flash[:notice] = deactivate_success_notice
diff --git a/app/controllers/schemes_controller.rb b/app/controllers/schemes_controller.rb
index 3a2be3909..5c2fe355f 100644
--- a/app/controllers/schemes_controller.rb
+++ b/app/controllers/schemes_controller.rb
@@ -27,7 +27,11 @@ class SchemesController < ApplicationController
end
def new_deactivation
- @scheme_deactivation_period = SchemeDeactivationPeriod.new
+ @scheme_deactivation_period = if @scheme.deactivates_in_a_long_time?
+ @scheme.open_deactivation || SchemeDeactivationPeriod.new
+ else
+ SchemeDeactivationPeriod.new
+ end
if params[:scheme_deactivation_period].blank?
render "toggle_active", locals: { action: "deactivate" }
@@ -54,7 +58,7 @@ class SchemesController < ApplicationController
end
def deactivate
- if @scheme.scheme_deactivation_periods.create!(deactivation_date: params[:deactivation_date])
+ if @scheme.open_deactivation&.update!(deactivation_date: params[:deactivation_date]) || @scheme.scheme_deactivation_periods.create!(deactivation_date: params[:deactivation_date])
logs = reset_location_and_scheme_for_logs!
flash[:notice] = deactivate_success_notice
diff --git a/app/helpers/locations_helper.rb b/app/helpers/locations_helper.rb
index 58fffd2e0..619bf20d5 100644
--- a/app/helpers/locations_helper.rb
+++ b/app/helpers/locations_helper.rb
@@ -69,7 +69,7 @@ module LocationsHelper
end
def toggle_location_link(location)
- return govuk_button_link_to "Deactivate this location", scheme_location_new_deactivation_path(location.scheme, location), warning: true if location.active?
+ return govuk_button_link_to "Deactivate this location", scheme_location_new_deactivation_path(location.scheme, location), warning: true if location.active? || location.deactivates_in_a_long_time?
return govuk_button_link_to "Reactivate this location", scheme_location_new_reactivation_path(location.scheme, location) if location.deactivated?
end
diff --git a/app/helpers/schemes_helper.rb b/app/helpers/schemes_helper.rb
index 50bd4efb2..7f801d8a6 100644
--- a/app/helpers/schemes_helper.rb
+++ b/app/helpers/schemes_helper.rb
@@ -14,7 +14,7 @@ module SchemesHelper
{ name: "Level of support given", value: scheme.support_type },
{ name: "Intended length of stay", value: scheme.intended_stay },
{ name: "Availability", value: scheme_availability(scheme) },
- { name: "Status", value: status_tag(scheme.status) },
+ { name: "Status", value: status_tag_from_resource(scheme) },
]
if user.data_coordinator?
@@ -40,7 +40,7 @@ module SchemesHelper
end
def toggle_scheme_link(scheme)
- return govuk_button_link_to "Deactivate this scheme", scheme_new_deactivation_path(scheme), warning: true if scheme.active?
+ return govuk_button_link_to "Deactivate this scheme", scheme_new_deactivation_path(scheme), warning: true if scheme.active? || scheme.deactivates_in_a_long_time?
return govuk_button_link_to "Reactivate this scheme", scheme_new_reactivation_path(scheme) if scheme.deactivated?
end
diff --git a/app/helpers/tag_helper.rb b/app/helpers/tag_helper.rb
index f8b19dd83..074ef21ed 100644
--- a/app/helpers/tag_helper.rb
+++ b/app/helpers/tag_helper.rb
@@ -34,4 +34,10 @@ module TagHelper
text: TEXT[status.to_sym],
)
end
+
+ def status_tag_from_resource(resource, classes = [])
+ status = resource.status
+ status = :active if resource.deactivates_in_a_long_time?
+ status_tag(status, classes)
+ end
end
diff --git a/app/models/location.rb b/app/models/location.rb
index 57393d9fb..818ea5700 100644
--- a/app/models/location.rb
+++ b/app/models/location.rb
@@ -108,6 +108,10 @@ class Location < ApplicationRecord
status == :reactivating_soon
end
+ def deactivates_in_a_long_time?
+ status_at(6.months.from_now) == :deactivating_soon
+ end
+
def validate_postcode
if !postcode&.match(POSTCODE_REGEXP)
error_message = I18n.t("validations.postcode")
diff --git a/app/models/location_deactivation_period.rb b/app/models/location_deactivation_period.rb
index c9a24bdc9..be635a975 100644
--- a/app/models/location_deactivation_period.rb
+++ b/app/models/location_deactivation_period.rb
@@ -4,7 +4,7 @@ class LocationDeactivationPeriodValidator < ActiveModel::Validator
def validate(record)
location = record.location
recent_deactivation = location.location_deactivation_periods.deactivations_without_reactivation.first
- if recent_deactivation.present?
+ if recent_deactivation.present? && recent_deactivation.deactivation_date <= 6.months.from_now
validate_reactivation(record, recent_deactivation, location)
else
validate_deactivation(record, location)
diff --git a/app/models/scheme.rb b/app/models/scheme.rb
index 75b37dec6..fdb5cf394 100644
--- a/app/models/scheme.rb
+++ b/app/models/scheme.rb
@@ -242,4 +242,8 @@ class Scheme < ApplicationRecord
def deactivated?
status == :deactivated
end
+
+ def deactivates_in_a_long_time?
+ status_at(6.months.from_now) == :deactivating_soon
+ end
end
diff --git a/app/models/scheme_deactivation_period.rb b/app/models/scheme_deactivation_period.rb
index 01aafbcb4..e413bb6a9 100644
--- a/app/models/scheme_deactivation_period.rb
+++ b/app/models/scheme_deactivation_period.rb
@@ -4,7 +4,7 @@ class SchemeDeactivationPeriodValidator < ActiveModel::Validator
def validate(record)
scheme = record.scheme
recent_deactivation = scheme.scheme_deactivation_periods.deactivations_without_reactivation.first
- if recent_deactivation.present?
+ if recent_deactivation.present? && recent_deactivation.deactivation_date <= 6.months.from_now
validate_reactivation(record, recent_deactivation, scheme)
else
validate_deactivation(record, scheme)
diff --git a/app/policies/scheme_policy.rb b/app/policies/scheme_policy.rb
index 58a4efb11..39842a160 100644
--- a/app/policies/scheme_policy.rb
+++ b/app/policies/scheme_policy.rb
@@ -47,7 +47,9 @@ class SchemePolicy
confirm_secondary_client_group?
secondary_client_group?
new_deactivation?
+ new_reactivation?
deactivate?
+ reactivate?
details?
support?
deactivate_confirm?
diff --git a/app/views/locations/index.html.erb b/app/views/locations/index.html.erb
index e974d7dfb..7641bbd48 100644
--- a/app/views/locations/index.html.erb
+++ b/app/views/locations/index.html.erb
@@ -54,7 +54,7 @@
end), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
<% row.cell(text: location.name) %>
<% row.cell(text: location.id) %>
- <% row.cell(text: status_tag(location.status)) %>
+ <% row.cell(text: status_tag_from_resource(location)) %>
<% end %>
<% end %>
<% end %>
diff --git a/app/views/locations/show.html.erb b/app/views/locations/show.html.erb
index dd819b2fb..fcee0a175 100644
--- a/app/views/locations/show.html.erb
+++ b/app/views/locations/show.html.erb
@@ -15,7 +15,7 @@
<% display_location_attributes(@location).each do |attr| %>
<%= summary_list.row do |row| %>
<% row.key { attr[:name] } %>
- <% row.value { attr[:attribute].eql?("status") ? status_tag(attr[:value]) : details_html(attr) } %>
+ <% row.value { attr[:attribute].eql?("status") ? status_tag_from_resource(@location) : details_html(attr) } %>
<% if LocationPolicy.new(current_user, @location).update? %>
<% row.action(text: "Change", href: scheme_location_name_path(@scheme, @location, referrer: "details")) if attr[:attribute] == "name" %>
<% end %>
diff --git a/app/views/schemes/_scheme_list.html.erb b/app/views/schemes/_scheme_list.html.erb
index 4f3b06454..43e03cd65 100644
--- a/app/views/schemes/_scheme_list.html.erb
+++ b/app/views/schemes/_scheme_list.html.erb
@@ -17,7 +17,7 @@
<% row.cell(text: simple_format(scheme_cell(scheme), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
<% row.cell(text: scheme.id_to_display) %>
<% row.cell(text: scheme.locations&.count) %>
- <% row.cell(text: status_tag(scheme.status)) %>
+ <% row.cell(text: status_tag_from_resource(scheme)) %>
<% end %>
<% end %>
<% end %>
diff --git a/spec/requests/locations_controller_spec.rb b/spec/requests/locations_controller_spec.rb
index 81b2e56b0..edc5dc452 100644
--- a/spec/requests/locations_controller_spec.rb
+++ b/spec/requests/locations_controller_spec.rb
@@ -1557,6 +1557,37 @@ RSpec.describe LocationsController, type: :request do
expect(lettings_log.unresolved).to eq(nil)
end
end
+
+ context "and there already is a deactivation period" do
+ let(:add_deactivations) { create(:location_deactivation_period, deactivation_date: Time.zone.local(2023, 6, 5), reactivation_date: nil, location:) }
+
+ before do
+ patch "/schemes/#{scheme.id}/locations/#{location.id}/deactivate", params:
+ end
+
+ it "updates existing location with valid deactivation date and renders location page" do
+ follow_redirect!
+ expect(response).to have_http_status(:ok)
+ expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success")
+ location.reload
+ expect(location.location_deactivation_periods.count).to eq(1)
+ expect(location.location_deactivation_periods.first.deactivation_date).to eq(deactivation_date)
+ end
+
+ it "clears the location and scheme answers" do
+ expect(lettings_log.location).to eq(location)
+ expect(lettings_log.scheme).to eq(scheme)
+ lettings_log.reload
+ expect(lettings_log.location).to eq(nil)
+ expect(lettings_log.scheme).to eq(nil)
+ end
+
+ it "marks log as needing attention" do
+ expect(lettings_log.unresolved).to eq(nil)
+ lettings_log.reload
+ expect(lettings_log.unresolved).to eq(true)
+ end
+ end
end
context "when the date is not selected" do
@@ -1623,6 +1654,34 @@ RSpec.describe LocationsController, type: :request do
expect(page).to have_content(I18n.t("validations.location.deactivation.during_deactivated_period"))
end
end
+
+ context "when there is an earlier open deactivation" do
+ let(:deactivation_date) { Time.zone.local(2022, 10, 10) }
+ let(:params) { { location_deactivation_period: { deactivation_date_type: "other", "deactivation_date(3i)": "8", "deactivation_date(2i)": "9", "deactivation_date(1i)": "2023" } } }
+ let(:add_deactivations) { create(:location_deactivation_period, deactivation_date: Time.zone.local(2023, 6, 5), reactivation_date: nil, location:) }
+
+ it "redirects to the location page and updates the existing deactivation period" do
+ follow_redirect!
+ follow_redirect!
+ expect(response).to have_http_status(:ok)
+ expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success")
+ location.reload
+ expect(location.location_deactivation_periods.count).to eq(1)
+ expect(location.location_deactivation_periods.first.deactivation_date).to eq(Time.zone.local(2023, 9, 8))
+ end
+ end
+
+ context "when there is a later open deactivation" do
+ let(:deactivation_date) { Time.zone.local(2022, 10, 10) }
+ let(:params) { { location_deactivation_period: { deactivation_date_type: "other", "deactivation_date(3i)": "8", "deactivation_date(2i)": "9", "deactivation_date(1i)": "2022" } } }
+ let(:add_deactivations) { create(:location_deactivation_period, deactivation_date: Time.zone.local(2023, 6, 5), reactivation_date: nil, location:) }
+
+ it "redirects to the confirmation page" do
+ follow_redirect!
+ expect(response).to have_http_status(:ok)
+ expect(page).to have_content("This change will affect 1 logs")
+ end
+ end
end
end
@@ -1692,6 +1751,19 @@ RSpec.describe LocationsController, type: :request do
expect(response).to have_http_status(:ok)
expect(page).not_to have_link("Reactivate this location")
expect(page).not_to have_link("Deactivate this location")
+ expect(page).to have_content("Deactivating soon")
+ end
+ end
+
+ context "with location that's deactivating in more than 6 months" do
+ let(:location_deactivation_period) { create(:location_deactivation_period, deactivation_date: Time.zone.local(2023, 6, 12), location:) }
+
+ it "does render toggle location link" do
+ expect(response).to have_http_status(:ok)
+ expect(page).not_to have_link("Reactivate this location")
+ expect(page).to have_link("Deactivate this location")
+ expect(response.body).not_to include("Deactivating soon")
+ expect(response.body).to include("Active")
end
end
@@ -1758,10 +1830,10 @@ RSpec.describe LocationsController, type: :request do
let(:scheme) { create(:scheme, owning_organisation: user.organisation) }
let(:location) { create(:location, scheme:) }
let(:deactivation_date) { Time.zone.local(2022, 4, 1) }
- let(:startdate) { Time.utc(2022, 10, 11) }
+ let(:startdate) { Time.utc(2022, 9, 11) }
before do
- Timecop.freeze(Time.utc(2022, 10, 10))
+ Timecop.freeze(Time.utc(2022, 9, 10))
sign_in user
create(:location_deactivation_period, deactivation_date:, location:)
location.save!
@@ -1792,7 +1864,7 @@ RSpec.describe LocationsController, type: :request do
end
context "with other date" do
- let(:params) { { location_deactivation_period: { reactivation_date_type: "other", "reactivation_date(3i)": "10", "reactivation_date(2i)": "10", "reactivation_date(1i)": "2022" } } }
+ let(:params) { { location_deactivation_period: { reactivation_date_type: "other", "reactivation_date(3i)": "10", "reactivation_date(2i)": "9", "reactivation_date(1i)": "2022" } } }
it "redirects to the location page and displays a success banner" do
expect(response).to redirect_to("/schemes/#{scheme.id}/locations/#{location.id}")
@@ -1805,7 +1877,7 @@ RSpec.describe LocationsController, type: :request do
follow_redirect!
location.reload
expect(location.location_deactivation_periods.count).to eq(1)
- expect(location.location_deactivation_periods.first.reactivation_date).to eq(Time.zone.local(2022, 10, 10))
+ expect(location.location_deactivation_periods.first.reactivation_date).to eq(Time.zone.local(2022, 9, 10))
end
end
diff --git a/spec/requests/schemes_controller_spec.rb b/spec/requests/schemes_controller_spec.rb
index 18ee51756..b68e31b06 100644
--- a/spec/requests/schemes_controller_spec.rb
+++ b/spec/requests/schemes_controller_spec.rb
@@ -330,6 +330,18 @@ RSpec.describe SchemesController, type: :request do
expect(page).not_to have_link("Deactivate this scheme")
end
end
+
+ context "with scheme that's deactivating in more than 6 months" do
+ let(:scheme_deactivation_period) { create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2023, 5, 12), scheme:) }
+
+ it "does not render toggle scheme link" do
+ expect(response).to have_http_status(:ok)
+ expect(page).not_to have_link("Reactivate this scheme")
+ expect(page).to have_link("Deactivate this scheme")
+ expect(response.body).not_to include("Deactivating soon")
+ expect(response.body).to include("Active")
+ end
+ end
end
context "when coordinator attempts to see scheme belonging to a parent organisation" do
@@ -1999,6 +2011,38 @@ RSpec.describe SchemesController, type: :request do
end
end
+ context "and there already is a deactivation period" do
+ let(:add_deactivations) { create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2023, 6, 5), reactivation_date: nil, scheme:) }
+
+ before do
+ create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2023, 6, 5), reactivation_date: nil, scheme:)
+ patch "/schemes/#{scheme.id}/deactivate", params:
+ end
+
+ it "updates existing scheme with valid deactivation date and renders scheme page" do
+ follow_redirect!
+ follow_redirect!
+ expect(response).to have_http_status(:ok)
+ expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success")
+ scheme.reload
+ expect(scheme.scheme_deactivation_periods.count).to eq(1)
+ expect(scheme.scheme_deactivation_periods.first.deactivation_date).to eq(deactivation_date)
+ end
+
+ it "clears the scheme and scheme answers" do
+ expect(lettings_log.scheme).to eq(scheme)
+ lettings_log.reload
+ expect(lettings_log.scheme).to eq(nil)
+ expect(lettings_log.scheme).to eq(nil)
+ end
+
+ it "marks log as needing attention" do
+ expect(lettings_log.unresolved).to eq(nil)
+ lettings_log.reload
+ expect(lettings_log.unresolved).to eq(true)
+ end
+ end
+
context "and the users need to be notified" do
it "sends E-mails to the creators of affected logs with counts" do
expect {
@@ -2061,6 +2105,35 @@ RSpec.describe SchemesController, type: :request do
expect(page).to have_content(I18n.t("validations.scheme.toggle_date.invalid"))
end
end
+
+ context "when there is an earlier open deactivation" do
+ let(:deactivation_date) { Time.zone.local(2022, 10, 10) }
+ let(:params) { { scheme_deactivation_period: { deactivation_date_type: "other", "deactivation_date(3i)": "8", "deactivation_date(2i)": "9", "deactivation_date(1i)": "2023" } } }
+ let(:add_deactivations) { create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2023, 6, 5), reactivation_date: nil, scheme:) }
+
+ it "redirects to the scheme page and updates the existing deactivation period" do
+ follow_redirect!
+ follow_redirect!
+ follow_redirect!
+ expect(response).to have_http_status(:ok)
+ expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success")
+ scheme.reload
+ expect(scheme.scheme_deactivation_periods.count).to eq(1)
+ expect(scheme.scheme_deactivation_periods.first.deactivation_date).to eq(Time.zone.local(2023, 9, 8))
+ end
+ end
+
+ context "when there is a later open deactivation" do
+ let(:deactivation_date) { Time.zone.local(2022, 10, 10) }
+ let(:params) { { scheme_deactivation_period: { deactivation_date_type: "other", "deactivation_date(3i)": "8", "deactivation_date(2i)": "9", "deactivation_date(1i)": "2022" } } }
+ let(:add_deactivations) { create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2023, 6, 5), reactivation_date: nil, scheme:) }
+
+ it "redirects to the confirmation page" do
+ follow_redirect!
+ expect(response).to have_http_status(:ok)
+ expect(page).to have_content("This change will affect 1 logs")
+ end
+ end
end
end
end
diff --git a/spec/views/locations/show.html.erb_spec.rb b/spec/views/locations/show.html.erb_spec.rb
index ad8540eeb..65c6c07e2 100644
--- a/spec/views/locations/show.html.erb_spec.rb
+++ b/spec/views/locations/show.html.erb_spec.rb
@@ -40,6 +40,7 @@ RSpec.describe "locations/show.html.erb" do
status: :active,
active?: true,
scheme:,
+ deactivates_in_a_long_time?: false,
)
end
From 963a09f469c50c7409fbabada82112e5d629ba9a Mon Sep 17 00:00:00 2001
From: Arthur Campbell <51094020+arfacamble@users.noreply.github.com>
Date: Mon, 19 Jun 2023 11:18:59 +0100
Subject: [PATCH 15/19] CLDC-2421 explain why schemes without locations are
incomplete (#1704)
* amend ordering in scheme and location show pages to raise status towards top
implement a muted explanation text on schemes when they have complete details but no active locations to explain why they are incomplete
write tests to ensure this text is shown under the right conditions
* update ordering in spec files
* correct rebase change
---
app/helpers/locations_helper.rb | 2 +-
app/helpers/schemes_helper.rb | 2 +-
app/views/schemes/show.html.erb | 7 ++++++-
spec/helpers/locations_helper_spec.rb | 6 +++---
spec/helpers/schemes_helper_spec.rb | 8 ++++----
spec/requests/schemes_controller_spec.rb | 17 +++++++++++++++++
6 files changed, 32 insertions(+), 10 deletions(-)
diff --git a/app/helpers/locations_helper.rb b/app/helpers/locations_helper.rb
index 619bf20d5..235029e9d 100644
--- a/app/helpers/locations_helper.rb
+++ b/app/helpers/locations_helper.rb
@@ -27,13 +27,13 @@ module LocationsHelper
[
{ name: "Postcode", value: location.postcode, attribute: "postcode" },
{ name: "Location name", value: location.name, attribute: "name" },
+ { name: "Status", value: location.status, attribute: "status" },
{ name: "Local authority", value: formatted_local_authority_timeline(location, "name"), attribute: "local_authority" },
{ name: "Number of units", value: location.units, attribute: "units" },
{ name: "Most common unit", value: location.type_of_unit, attribute: "type_of_unit" },
{ name: "Mobility standards", value: location.mobility_type, attribute: "mobility_standards" },
{ name: "Location code", value: formatted_local_authority_timeline(location, "code"), attribute: "location_code" },
{ name: "Availability", value: location_availability(location), attribute: "availability" },
- { name: "Status", value: location.status, attribute: "status" },
]
end
diff --git a/app/helpers/schemes_helper.rb b/app/helpers/schemes_helper.rb
index 7f801d8a6..f7da6d3f7 100644
--- a/app/helpers/schemes_helper.rb
+++ b/app/helpers/schemes_helper.rb
@@ -3,6 +3,7 @@ module SchemesHelper
base_attributes = [
{ name: "Scheme code", value: scheme.id_to_display },
{ name: "Name", value: scheme.service_name, edit: true },
+ { name: "Status", value: status_tag_from_resource(scheme) },
{ name: "Confidential information", value: scheme.sensitive, edit: true },
{ name: "Type of scheme", value: scheme.scheme_type },
{ name: "Registered under Care Standards Act 2000", value: scheme.registered_under_care_act },
@@ -14,7 +15,6 @@ module SchemesHelper
{ name: "Level of support given", value: scheme.support_type },
{ name: "Intended length of stay", value: scheme.intended_stay },
{ name: "Availability", value: scheme_availability(scheme) },
- { name: "Status", value: status_tag_from_resource(scheme) },
]
if user.data_coordinator?
diff --git a/app/views/schemes/show.html.erb b/app/views/schemes/show.html.erb
index 4fe3a65de..66209fa28 100644
--- a/app/views/schemes/show.html.erb
+++ b/app/views/schemes/show.html.erb
@@ -19,7 +19,12 @@
<% display_scheme_attributes(@scheme, current_user).each do |attr| %>
<%= summary_list.row do |row| %>
<% row.key { attr[:name] } %>
- <% row.value { details_html(attr) } %>
+ <% row.value do %>
+ <%= details_html(attr) %>
+ <% if attr[:name] == "Status" && @scheme.confirmed? && @scheme.locations.confirmed.none? %>
+ Add a location to complete this scheme
+ <% end %>
+ <% end %>
<% if SchemePolicy.new(current_user, @scheme).update? %>
<% row.action(text: "Change", href: scheme_edit_name_path(scheme_id: @scheme.id)) if attr[:edit] %>
<% end %>
diff --git a/spec/helpers/locations_helper_spec.rb b/spec/helpers/locations_helper_spec.rb
index 354b948d4..368833bd2 100644
--- a/spec/helpers/locations_helper_spec.rb
+++ b/spec/helpers/locations_helper_spec.rb
@@ -140,13 +140,13 @@ RSpec.describe LocationsHelper do
attributes = [
{ attribute: "postcode", name: "Postcode", value: location.postcode },
{ attribute: "name", name: "Location name", value: location.name },
+ { attribute: "status", name: "Status", value: :active },
{ attribute: "local_authority", name: "Local authority", value: location.location_admin_district },
{ attribute: "units", name: "Number of units", value: location.units },
{ attribute: "type_of_unit", name: "Most common unit", value: location.type_of_unit },
{ attribute: "mobility_standards", name: "Mobility standards", value: location.mobility_type },
{ attribute: "location_code", name: "Location code", value: location.location_code },
{ attribute: "availability", name: "Availability", value: "Active from 1 April 2022" },
- { attribute: "status", name: "Status", value: :active },
]
expect(display_location_attributes(location)).to eq(attributes)
@@ -162,13 +162,13 @@ RSpec.describe LocationsHelper do
attributes = [
{ attribute: "postcode", name: "Postcode", value: location.postcode },
{ attribute: "name", name: "Location name", value: location.name },
+ { attribute: "status", name: "Status", value: :active },
{ attribute: "local_authority", name: "Local authority", value: "Eden (until 31 March 2023)\nCumberland (1 April 2023 - present)" },
{ attribute: "units", name: "Number of units", value: location.units },
{ attribute: "type_of_unit", name: "Most common unit", value: location.type_of_unit },
{ attribute: "mobility_standards", name: "Mobility standards", value: location.mobility_type },
{ attribute: "location_code", name: "Location code", value: "E07000030 (until 31 March 2023)\nE06000063 (1 April 2023 - present)" },
{ attribute: "availability", name: "Availability", value: "Active from 1 April 2022" },
- { attribute: "status", name: "Status", value: :active },
]
expect(display_location_attributes(location)).to eq(attributes)
@@ -185,13 +185,13 @@ RSpec.describe LocationsHelper do
attributes = [
{ attribute: "postcode", name: "Postcode", value: location.postcode },
{ attribute: "name", name: "Location name", value: location.name },
+ { attribute: "status", name: "Status", value: :incomplete },
{ attribute: "local_authority", name: "Local authority", value: "" },
{ attribute: "units", name: "Number of units", value: location.units },
{ attribute: "type_of_unit", name: "Most common unit", value: location.type_of_unit },
{ attribute: "mobility_standards", name: "Mobility standards", value: location.mobility_type },
{ attribute: "location_code", name: "Location code", value: "" },
{ attribute: "availability", name: "Availability", value: "Active from 1 April 2022" },
- { attribute: "status", name: "Status", value: :incomplete },
]
expect(display_location_attributes(location)).to eq(attributes)
diff --git a/spec/helpers/schemes_helper_spec.rb b/spec/helpers/schemes_helper_spec.rb
index 905494ac8..c2350d428 100644
--- a/spec/helpers/schemes_helper_spec.rb
+++ b/spec/helpers/schemes_helper_spec.rb
@@ -115,6 +115,7 @@ RSpec.describe SchemesHelper do
attributes = [
{ name: "Scheme code", value: "S#{scheme.id}" },
{ name: "Name", value: "Test service_name", edit: true },
+ { name: "Status", value: status_tag(:incomplete) },
{ name: "Confidential information", value: "No", edit: true },
{ name: "Type of scheme", value: "Housing for older people" },
{ name: "Registered under Care Standards Act 2000", value: "Yes – registered care home providing personal care" },
@@ -126,7 +127,6 @@ RSpec.describe SchemesHelper do
{ name: "Level of support given", value: "High level" },
{ name: "Intended length of stay", value: "Permanent" },
{ name: "Availability", value: "Active from 1 April 2021" },
- { name: "Status", value: status_tag(:incomplete) },
]
expect(display_scheme_attributes(scheme, support_user)).to eq(attributes)
end
@@ -135,6 +135,7 @@ RSpec.describe SchemesHelper do
attributes = [
{ name: "Scheme code", value: "S#{scheme.id}" },
{ name: "Name", value: "Test service_name", edit: true },
+ { name: "Status", value: status_tag(:incomplete) },
{ name: "Confidential information", value: "No", edit: true },
{ name: "Type of scheme", value: "Housing for older people" },
{ name: "Registered under Care Standards Act 2000", value: "Yes – registered care home providing personal care" },
@@ -145,7 +146,6 @@ RSpec.describe SchemesHelper do
{ name: "Level of support given", value: "High level" },
{ name: "Intended length of stay", value: "Permanent" },
{ name: "Availability", value: "Active from 1 April 2021" },
- { name: "Status", value: status_tag(:incomplete) },
]
expect(display_scheme_attributes(scheme, coordinator_user)).to eq(attributes)
end
@@ -160,6 +160,7 @@ RSpec.describe SchemesHelper do
attributes = [
{ name: "Scheme code", value: "S#{scheme.id}" },
{ name: "Name", value: "Test service_name", edit: true },
+ { name: "Status", value: status_tag(:active) },
{ name: "Confidential information", value: "No", edit: true },
{ name: "Type of scheme", value: "Housing for older people" },
{ name: "Registered under Care Standards Act 2000", value: "Yes – registered care home providing personal care" },
@@ -171,7 +172,6 @@ RSpec.describe SchemesHelper do
{ name: "Level of support given", value: "High level" },
{ name: "Intended length of stay", value: "Permanent" },
{ name: "Availability", value: "Active from 1 April 2021" },
- { name: "Status", value: status_tag(:active) },
]
expect(display_scheme_attributes(scheme, support_user)).to eq(attributes)
end
@@ -180,6 +180,7 @@ RSpec.describe SchemesHelper do
attributes = [
{ name: "Scheme code", value: "S#{scheme.id}" },
{ name: "Name", value: "Test service_name", edit: true },
+ { name: "Status", value: status_tag(:active) },
{ name: "Confidential information", value: "No", edit: true },
{ name: "Type of scheme", value: "Housing for older people" },
{ name: "Registered under Care Standards Act 2000", value: "Yes – registered care home providing personal care" },
@@ -190,7 +191,6 @@ RSpec.describe SchemesHelper do
{ name: "Level of support given", value: "High level" },
{ name: "Intended length of stay", value: "Permanent" },
{ name: "Availability", value: "Active from 1 April 2021" },
- { name: "Status", value: status_tag(:active) },
]
expect(display_scheme_attributes(scheme, coordinator_user)).to eq(attributes)
end
diff --git a/spec/requests/schemes_controller_spec.rb b/spec/requests/schemes_controller_spec.rb
index b68e31b06..754d8cb22 100644
--- a/spec/requests/schemes_controller_spec.rb
+++ b/spec/requests/schemes_controller_spec.rb
@@ -364,6 +364,23 @@ RSpec.describe SchemesController, type: :request do
expect(page).not_to have_content("Deactivate this scheme")
end
end
+
+ context "when the scheme has all details but no confirmed locations" do
+ it "shows the scheme as incomplete with text to explain" do
+ get scheme_path(specific_scheme)
+ expect(page).to have_content "Incomplete"
+ expect(page).to have_content "Add a location to complete this scheme"
+ end
+ end
+
+ context "when the scheme has all details and confirmed locations" do
+ it "shows the scheme as complete" do
+ create(:location, scheme: specific_scheme)
+ get scheme_path(specific_scheme)
+ expect(page).to have_content "Active"
+ expect(page).not_to have_content "Add a location to complete this scheme"
+ end
+ end
end
context "when signed in as a support user" do
From d0ab30ce8b637a9d26e03c868cfc29ce59752b1e Mon Sep 17 00:00:00 2001
From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com>
Date: Mon, 19 Jun 2023 14:45:18 +0100
Subject: [PATCH 16/19] CLDC-1783 Add telephone number question to the user
form (#1706)
* Add telephone number question to the user form
* Extract user policy
---
app/controllers/users_controller.rb | 16 +++-
app/helpers/user_helper.rb | 24 ------
app/policies/user_policy.rb | 36 +++++++++
app/views/users/edit.html.erb | 5 ++
app/views/users/new.html.erb | 6 ++
app/views/users/show.html.erb | 22 ++++--
config/locales/en.yml | 2 +
spec/features/user_spec.rb | 26 +++++++
spec/helpers/user_helper_spec.rb | 99 ------------------------
spec/policies/user_policy_spec.rb | 103 +++++++++++++++++++++++++
spec/requests/users_controller_spec.rb | 7 ++
11 files changed, 213 insertions(+), 133 deletions(-)
create mode 100644 app/policies/user_policy.rb
create mode 100644 spec/policies/user_policy_spec.rb
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 9e6491554..3f91fa659 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -120,6 +120,14 @@ private
if user_params[:role].present? && !current_user.assignable_roles.key?(user_params[:role].to_sym)
@resource.errors.add :role, I18n.t("validations.role.invalid")
end
+
+ if user_params[:phone].present? && !valid_phone_number?(user_params[:phone])
+ @resource.errors.add :phone
+ end
+ end
+
+ def valid_phone_number?(number)
+ number.to_i.to_s == number && number.length >= 11
end
def format_error_messages
@@ -151,14 +159,14 @@ private
def user_params
if @user == current_user
if current_user.data_coordinator? || current_user.support?
- params.require(:user).permit(:email, :name, :password, :password_confirmation, :role, :is_dpo, :is_key_contact, :initial_confirmation_sent)
+ params.require(:user).permit(:email, :phone, :name, :password, :password_confirmation, :role, :is_dpo, :is_key_contact, :initial_confirmation_sent)
else
- params.require(:user).permit(:email, :name, :password, :password_confirmation, :initial_confirmation_sent)
+ params.require(:user).permit(:email, :phone, :name, :password, :password_confirmation, :initial_confirmation_sent)
end
elsif current_user.data_coordinator?
- params.require(:user).permit(:email, :name, :role, :is_dpo, :is_key_contact, :active, :initial_confirmation_sent)
+ params.require(:user).permit(:email, :phone, :name, :role, :is_dpo, :is_key_contact, :active, :initial_confirmation_sent)
elsif current_user.support?
- params.require(:user).permit(:email, :name, :role, :is_dpo, :is_key_contact, :organisation_id, :active, :initial_confirmation_sent)
+ params.require(:user).permit(:email, :phone, :name, :role, :is_dpo, :is_key_contact, :organisation_id, :active, :initial_confirmation_sent)
end
end
diff --git a/app/helpers/user_helper.rb b/app/helpers/user_helper.rb
index 957e2dc2c..76fb78f57 100644
--- a/app/helpers/user_helper.rb
+++ b/app/helpers/user_helper.rb
@@ -7,30 +7,6 @@ module UserHelper
current_user == user ? "Are you" : "Is this person"
end
- def can_edit_names?(user, current_user)
- (current_user == user || current_user.data_coordinator? || current_user.support?) && user.active?
- end
-
- def can_edit_emails?(user, current_user)
- (current_user == user || current_user.data_coordinator? || current_user.support?) && user.active?
- end
-
- def can_edit_password?(user, current_user)
- current_user == user
- end
-
- def can_edit_roles?(user, current_user)
- (current_user.data_coordinator? || current_user.support?) && user.active?
- end
-
- def can_edit_dpo?(user, current_user)
- (current_user.data_coordinator? || current_user.support?) && user.active?
- end
-
- def can_edit_key_contact?(user, current_user)
- (current_user.data_coordinator? || current_user.support?) && user.active?
- end
-
def can_edit_org?(current_user)
current_user.data_coordinator? || current_user.support?
end
diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb
new file mode 100644
index 000000000..a4b1a3d5c
--- /dev/null
+++ b/app/policies/user_policy.rb
@@ -0,0 +1,36 @@
+class UserPolicy
+ attr_reader :current_user, :user
+
+ def initialize(current_user, user)
+ @current_user = current_user
+ @user = user
+ end
+
+ def edit_password?
+ @current_user == @user
+ end
+
+ def edit_roles?
+ (@current_user.data_coordinator? || @current_user.support?) && @user.active?
+ end
+
+ %w[
+ edit_roles?
+ edit_dpo?
+ edit_key_contact?
+ ].each do |method_name|
+ define_method method_name do
+ (@current_user.data_coordinator? || @current_user.support?) && @user.active?
+ end
+ end
+
+ %w[
+ edit_emails?
+ edit_telephone_numbers?
+ edit_names?
+ ].each do |method_name|
+ define_method method_name do
+ (@current_user == @user || @current_user.data_coordinator? || @current_user.support?) && @user.active?
+ end
+ end
+end
diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb
index fb7b10755..3f7259bc3 100644
--- a/app/views/users/edit.html.erb
+++ b/app/views/users/edit.html.erb
@@ -22,6 +22,11 @@
autocomplete: "email",
spellcheck: "false" %>
+ <%= f.govuk_phone_field :phone,
+ label: { text: "Telephone number", size: "m" },
+ autocomplete: "phone",
+ spellcheck: "false" %>
+
<% if current_user.data_coordinator? || current_user.support? %>
<% roles = current_user.assignable_roles.map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize) } %>
diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb
index b3311bcfb..fc66bcad8 100644
--- a/app/views/users/new.html.erb
+++ b/app/views/users/new.html.erb
@@ -23,6 +23,12 @@
spellcheck: "false",
value: @resource.email %>
+ <%= f.govuk_phone_field :phone,
+ label: { text: "Telephone number", size: "m" },
+ autocomplete: "phone",
+ spellcheck: "false",
+ value: @resource.phone %>
+
<% if current_user.support? %>
<% null_option = [OpenStruct.new(id: "", name: "Select an option")] %>
<% organisations = Organisation.all.map { |org| OpenStruct.new(id: org.id, name: org.name) } %>
diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb
index 845e6ebba..844a0c965 100644
--- a/app/views/users/show.html.erb
+++ b/app/views/users/show.html.erb
@@ -13,7 +13,7 @@
<%= summary_list.row do |row|
row.key { "Name" }
row.value { @user.name }
- if can_edit_names?(@user, current_user)
+ if UserPolicy.new(current_user, @user).edit_names?
row.action(visually_hidden_text: "name", href: aliased_user_edit(@user, current_user), html_attributes: { "data-qa": "change-name" })
else
row.action
@@ -23,17 +23,27 @@
<%= summary_list.row do |row|
row.key { "Email address" }
row.value { @user.email }
- if can_edit_emails?(@user, current_user)
+ if UserPolicy.new(current_user, @user).edit_emails?
row.action(visually_hidden_text: "email address", href: aliased_user_edit(@user, current_user), html_attributes: { "data-qa": "change-email-address" })
else
row.action
end
end %>
+ <%= summary_list.row do |row|
+ row.key { "Telephone number" }
+ row.value { @user.phone }
+ if UserPolicy.new(current_user, @user).edit_telephone_numbers?
+ row.action(visually_hidden_text: "telephone number", href: aliased_user_edit(@user, current_user), html_attributes: { "data-qa": "change-telephone-number" })
+ else
+ row.action
+ end
+ end %>
+
<%= summary_list.row do |row|
row.key { "Password" }
row.value { "••••••••" }
- if can_edit_password?(@user, current_user)
+ if UserPolicy.new(current_user, @user).edit_password?
row.action(
visually_hidden_text: "password",
href: edit_password_account_path,
@@ -53,7 +63,7 @@
<%= summary_list.row do |row|
row.key { "Role" }
row.value { @user.role&.humanize }
- if can_edit_roles?(@user, current_user)
+ if UserPolicy.new(current_user, @user).edit_roles?
row.action(
visually_hidden_text: "role",
href: aliased_user_edit(@user, current_user),
@@ -67,7 +77,7 @@
<%= summary_list.row do |row|
row.key { "Data protection officer" }
row.value { @user.is_data_protection_officer? ? "Yes" : "No" }
- if can_edit_dpo?(@user, current_user)
+ if UserPolicy.new(current_user, @user).edit_dpo?
row.action(
visually_hidden_text: "if data protection officer",
href: user_edit_dpo_path(@user),
@@ -81,7 +91,7 @@
<%= summary_list.row do |row|
row.key { "Key contact" }
row.value { @user.is_key_contact? ? "Yes" : "No" }
- if can_edit_key_contact?(@user, current_user)
+ if UserPolicy.new(current_user, @user).edit_key_contact?
row.action(
visually_hidden_text: "if a key contact",
href: user_edit_key_contact_path(@user),
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 59fa8ef32..73cf91d0a 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -154,6 +154,8 @@ en:
invalid: "Enter an email address in the correct format, like name@example.com"
blank: "Enter an email address"
taken: "Enter an email address that hasn’t already been used to sign up"
+ phone:
+ invalid: "Enter a telephone number in the correct format"
role:
invalid: "Role must be data accessor, data provider or data coordinator"
blank: "Select role"
diff --git a/spec/features/user_spec.rb b/spec/features/user_spec.rb
index d4e1a6052..222d2ff04 100644
--- a/spec/features/user_spec.rb
+++ b/spec/features/user_spec.rb
@@ -322,16 +322,42 @@ RSpec.describe "User Features" do
expect(page).to have_title("Error")
end
+ it "validates telephone number is numeric" do
+ visit("users/new")
+ fill_in("user[name]", with: "New User")
+ fill_in("user[email]", with: "newuser@example.com")
+ fill_in("user[phone]", with: "randomstring")
+ click_button("Continue")
+ expect(page).to have_selector("#error-summary-title")
+ expect(page).to have_selector("#user-phone-field-error")
+ expect(page).to have_content(/Enter a telephone number in the correct format/)
+ expect(page).to have_title("Error")
+ end
+
+ it "validates telephone number is longer than 11 digits" do
+ visit("users/new")
+ fill_in("user[name]", with: "New User")
+ fill_in("user[email]", with: "newuser@example.com")
+ fill_in("user[phone]", with: "123")
+ click_button("Continue")
+ expect(page).to have_selector("#error-summary-title")
+ expect(page).to have_selector("#user-phone-field-error")
+ expect(page).to have_content(/Enter a telephone number in the correct format/)
+ expect(page).to have_title("Error")
+ end
+
it "sets name, email, role, is_dpo and is_key_contact fields" do
visit("users/new")
fill_in("user[name]", with: "New User")
fill_in("user[email]", with: "newuser@example.com")
+ fill_in("user[phone]", with: "12345678910")
choose("user-role-data-provider-field")
click_button("Continue")
expect(User.find_by(
name: "New User",
email: "newuser@example.com",
role: "data_provider",
+ phone: "12345678910",
is_dpo: false,
is_key_contact: false,
)).to be_a(User)
diff --git a/spec/helpers/user_helper_spec.rb b/spec/helpers/user_helper_spec.rb
index e49c7d6a2..829195f6c 100644
--- a/spec/helpers/user_helper_spec.rb
+++ b/spec/helpers/user_helper_spec.rb
@@ -37,105 +37,6 @@ RSpec.describe UserHelper do
end
describe "change button permissions" do
- context "when the user is a data provider viewing their own details" do
- let(:current_user) { FactoryBot.create(:user, :data_provider) }
- let(:user) { current_user }
-
- it "allows changing name" do
- expect(can_edit_names?(user, current_user)).to be true
- end
-
- it "allows changing email" do
- expect(can_edit_emails?(user, current_user)).to be true
- end
-
- it "allows changing password" do
- expect(can_edit_password?(user, current_user)).to be true
- end
-
- it "does not allow changing roles" do
- expect(can_edit_roles?(user, current_user)).to be false
- end
-
- it "does not allow changing dpo" do
- expect(can_edit_dpo?(user, current_user)).to be false
- end
-
- it "does not allow changing key contact" do
- expect(can_edit_key_contact?(user, current_user)).to be false
- end
- end
-
- context "when the user is a data coordinator viewing another user's details" do
- it "allows changing name" do
- expect(can_edit_names?(user, current_user)).to be true
- end
-
- it "allows changing email" do
- expect(can_edit_emails?(user, current_user)).to be true
- end
-
- it "allows changing password" do
- expect(can_edit_password?(user, current_user)).to be false
- end
-
- it "does not allow changing roles" do
- expect(can_edit_roles?(user, current_user)).to be true
- end
-
- it "does not allow changing dpo" do
- expect(can_edit_dpo?(user, current_user)).to be true
- end
-
- it "does not allow changing key contact" do
- expect(can_edit_key_contact?(user, current_user)).to be true
- end
-
- context "when the user is a data coordinator viewing their own details" do
- let(:user) { current_user }
-
- it "allows changing password" do
- expect(can_edit_password?(user, current_user)).to be true
- end
- end
- end
-
- context "when the user is a support user viewing another user's details" do
- let(:current_user) { FactoryBot.create(:user, :support) }
-
- it "allows changing name" do
- expect(can_edit_names?(user, current_user)).to be true
- end
-
- it "allows changing email" do
- expect(can_edit_emails?(user, current_user)).to be true
- end
-
- it "allows changing password" do
- expect(can_edit_password?(user, current_user)).to be false
- end
-
- it "does not allow changing roles" do
- expect(can_edit_roles?(user, current_user)).to be true
- end
-
- it "does not allow changing dpo" do
- expect(can_edit_dpo?(user, current_user)).to be true
- end
-
- it "does not allow changing key contact" do
- expect(can_edit_key_contact?(user, current_user)).to be true
- end
-
- context "when the user is a support user viewing their own details" do
- let(:user) { current_user }
-
- it "allows changing password" do
- expect(can_edit_password?(user, current_user)).to be true
- end
- end
- end
-
context "when the user is a data provider viewing organisation details" do
let(:current_user) { FactoryBot.create(:user, :data_provider) }
diff --git a/spec/policies/user_policy_spec.rb b/spec/policies/user_policy_spec.rb
new file mode 100644
index 000000000..ec84fbceb
--- /dev/null
+++ b/spec/policies/user_policy_spec.rb
@@ -0,0 +1,103 @@
+require "rails_helper"
+# rubocop:disable RSpec/RepeatedExample
+
+RSpec.describe UserPolicy do
+ subject(:policy) { described_class }
+
+ let(:data_provider) { FactoryBot.create(:user, :data_provider) }
+ let(:data_coordinator) { FactoryBot.create(:user, :data_coordinator) }
+ let(:support) { FactoryBot.create(:user, :support) }
+
+ permissions :edit_names? do
+ it "allows changing their own name" do
+ expect(policy).to permit(data_provider, data_provider)
+ end
+
+ it "as a coordinator it allows changing other user's name" do
+ expect(policy).to permit(data_coordinator, data_provider)
+ end
+
+ it "as a support user it allows changing other user's name" do
+ expect(policy).to permit(support, data_provider)
+ end
+ end
+
+ permissions :edit_emails? do
+ it "allows changing their own email" do
+ expect(policy).to permit(data_provider, data_provider)
+ end
+
+ it "as a coordinator it allows changing other user's email" do
+ expect(policy).to permit(data_coordinator, data_provider)
+ end
+
+ it "as a support user it allows changing other user's email" do
+ expect(policy).to permit(support, data_provider)
+ end
+ end
+
+ permissions :edit_password? do
+ it "as a provider it allows changing their own password" do
+ expect(policy).to permit(data_provider, data_provider)
+ end
+
+ it "as a coordinator it allows changing their own password" do
+ expect(policy).to permit(data_coordinator, data_coordinator)
+ end
+
+ it "as a support user it allows changing their own password" do
+ expect(policy).to permit(support, support)
+ end
+
+ it "as a coordinator it does not allow changing other user's password" do
+ expect(policy).not_to permit(data_coordinator, data_provider)
+ end
+
+ it "as a support user it does not allow changing other user's password" do
+ expect(policy).not_to permit(support, data_provider)
+ end
+ end
+
+ permissions :edit_roles? do
+ it "as a provider it does not allow changing roles" do
+ expect(policy).not_to permit(data_provider, data_provider)
+ end
+
+ it "as a coordinator allows changing other user's roles" do
+ expect(policy).to permit(data_coordinator, data_provider)
+ end
+
+ it "as a support user allows changing other user's roles" do
+ expect(policy).to permit(support, data_provider)
+ end
+ end
+
+ permissions :edit_dpo? do
+ it "as a provider it does not allow changing dpo" do
+ expect(policy).not_to permit(data_provider, data_provider)
+ end
+
+ it "as a coordinator allows changing other user's dpo" do
+ expect(policy).to permit(data_coordinator, data_provider)
+ end
+
+ it "as a support user allows changing other user's dpo" do
+ expect(policy).to permit(support, data_provider)
+ end
+ end
+
+ permissions :edit_key_contact? do
+ it "as a provider it does not allow changing key_contact" do
+ expect(policy).not_to permit(data_provider, data_provider)
+ end
+
+ it "as a coordinator allows changing other user's key_contact" do
+ expect(policy).to permit(data_coordinator, data_provider)
+ end
+
+ it "as a support user allows changing other user's key_contact" do
+ expect(policy).to permit(support, data_provider)
+ end
+ end
+end
+# rubocop:enable RSpec/RepeatedExample
diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb
index ecba12933..564167354 100644
--- a/spec/requests/users_controller_spec.rb
+++ b/spec/requests/users_controller_spec.rb
@@ -120,6 +120,7 @@ RSpec.describe UsersController, type: :request do
it "allows changing name, email and password" do
expect(page).to have_link("Change", text: "name")
expect(page).to have_link("Change", text: "email address")
+ expect(page).to have_link("Change", text: "telephone number")
expect(page).to have_link("Change", text: "password")
expect(page).not_to have_link("Change", text: "role")
expect(page).not_to have_link("Change", text: "if data protection officer")
@@ -180,6 +181,7 @@ RSpec.describe UsersController, type: :request do
it "does not have edit links" do
expect(page).not_to have_link("Change", text: "name")
expect(page).not_to have_link("Change", text: "email address")
+ expect(page).not_to have_link("Change", text: "telephone number")
expect(page).not_to have_link("Change", text: "password")
expect(page).not_to have_link("Change", text: "role")
expect(page).not_to have_link("Change", text: "if data protection officer")
@@ -499,6 +501,7 @@ RSpec.describe UsersController, type: :request do
it "allows changing name, email, password, role, dpo and key contact" do
expect(page).to have_link("Change", text: "name")
expect(page).to have_link("Change", text: "email address")
+ expect(page).to have_link("Change", text: "telephone number")
expect(page).to have_link("Change", text: "password")
expect(page).to have_link("Change", text: "role")
expect(page).to have_link("Change", text: "if data protection officer")
@@ -543,6 +546,7 @@ RSpec.describe UsersController, type: :request do
it "allows changing name, email, role, dpo and key contact" do
expect(page).to have_link("Change", text: "name")
expect(page).to have_link("Change", text: "email address")
+ expect(page).to have_link("Change", text: "telephone number")
expect(page).not_to have_link("Change", text: "password")
expect(page).to have_link("Change", text: "role")
expect(page).to have_link("Change", text: "if data protection officer")
@@ -1169,6 +1173,7 @@ RSpec.describe UsersController, type: :request do
it "allows changing name, email, password, role, dpo and key contact" do
expect(page).to have_link("Change", text: "name")
expect(page).to have_link("Change", text: "email address")
+ expect(page).to have_link("Change", text: "telephone number")
expect(page).to have_link("Change", text: "password")
expect(page).to have_link("Change", text: "role")
expect(page).to have_link("Change", text: "if data protection officer")
@@ -1198,6 +1203,7 @@ RSpec.describe UsersController, type: :request do
it "allows changing name, email, role, dpo and key contact" do
expect(page).to have_link("Change", text: "name")
expect(page).to have_link("Change", text: "email address")
+ expect(page).to have_link("Change", text: "telephone number")
expect(page).not_to have_link("Change", text: "password")
expect(page).to have_link("Change", text: "role")
expect(page).to have_link("Change", text: "if data protection officer")
@@ -1242,6 +1248,7 @@ RSpec.describe UsersController, type: :request do
it "allows changing name, email, role, dpo and key contact" do
expect(page).to have_link("Change", text: "name")
expect(page).to have_link("Change", text: "email address")
+ expect(page).to have_link("Change", text: "telephone number")
expect(page).not_to have_link("Change", text: "password")
expect(page).to have_link("Change", text: "role")
expect(page).to have_link("Change", text: "if data protection officer")
From a0b708b21eb75b1e58aadac4c93ab16c6a04475b Mon Sep 17 00:00:00 2001
From: Phil Lee
Date: Mon, 19 Jun 2023 15:07:17 +0100
Subject: [PATCH 17/19] check bulk upload template against date (#1681)
---
.../bulk_upload/lettings/validator.rb | 10 ++++--
.../lettings/year2022/csv_parser.rb | 11 ++++++-
.../lettings/year2022/row_parser.rb | 12 +++----
.../bulk_upload/sales/year2022/csv_parser.rb | 11 ++++++-
.../bulk_upload/sales/year2022/row_parser.rb | 12 +++----
.../bulk_upload/lettings/validator_spec.rb | 4 ++-
.../lettings/year2022/csv_parser_spec.rb | 28 +++++++++++++++++
.../sales/year2022/csv_parser_spec.rb | 31 +++++++++++++++++++
8 files changed, 102 insertions(+), 17 deletions(-)
diff --git a/app/services/bulk_upload/lettings/validator.rb b/app/services/bulk_upload/lettings/validator.rb
index 47600978b..14c31b6d7 100644
--- a/app/services/bulk_upload/lettings/validator.rb
+++ b/app/services/bulk_upload/lettings/validator.rb
@@ -150,13 +150,19 @@ private
def validate_field_numbers_count
return if halt_validations?
- errors.add(:base, :wrong_field_numbers_count) unless csv_parser.correct_field_count?
+ unless csv_parser.correct_field_count?
+ errors.add(:base, :wrong_field_numbers_count)
+ halt_validations!
+ end
end
def validate_max_columns_count_if_no_headers
return if halt_validations?
- errors.add(:base, :over_max_column_count) if csv_parser.too_many_columns?
+ if csv_parser.too_many_columns?
+ errors.add(:base, :over_max_column_count)
+ halt_validations!
+ end
end
def validate_correct_template
diff --git a/app/services/bulk_upload/lettings/year2022/csv_parser.rb b/app/services/bulk_upload/lettings/year2022/csv_parser.rb
index f5bba15bf..f21e92a24 100644
--- a/app/services/bulk_upload/lettings/year2022/csv_parser.rb
+++ b/app/services/bulk_upload/lettings/year2022/csv_parser.rb
@@ -3,6 +3,7 @@ require "csv"
class BulkUpload::Lettings::Year2022::CsvParser
FIELDS = 134
MAX_COLUMNS = 135
+ FORM_YEAR = 2022
attr_reader :path
@@ -62,11 +63,19 @@ class BulkUpload::Lettings::Year2022::CsvParser
end
def wrong_template_for_year?
- false
+ !(first_record_start_date >= form.start_date && first_record_start_date <= form.end_date)
end
private
+ def form
+ @form ||= FormHandler.instance.lettings_form_for_start_year(FORM_YEAR)
+ end
+
+ def first_record_start_date
+ @first_record_start_date ||= row_parsers.first.startdate || Date.new
+ end
+
def default_field_numbers
("field_1".."field_#{FIELDS}").to_a
end
diff --git a/app/services/bulk_upload/lettings/year2022/row_parser.rb b/app/services/bulk_upload/lettings/year2022/row_parser.rb
index c6251c81f..83ed20457 100644
--- a/app/services/bulk_upload/lettings/year2022/row_parser.rb
+++ b/app/services/bulk_upload/lettings/year2022/row_parser.rb
@@ -461,6 +461,12 @@ class BulkUpload::Lettings::Year2022::RowParser
end
end
+ def startdate
+ Date.new(field_98 + 2000, field_97, field_96) if field_98.present? && field_97.present? && field_96.present?
+ rescue Date::Error
+ Date.new
+ end
+
private
def validate_declaration_acceptance
@@ -983,12 +989,6 @@ private
}
end
- def startdate
- Date.new(field_98 + 2000, field_97, field_96) if field_98.present? && field_97.present? && field_96.present?
- rescue Date::Error
- Date.new
- end
-
def renttype
case field_1
when 1, 2, 3, 4
diff --git a/app/services/bulk_upload/sales/year2022/csv_parser.rb b/app/services/bulk_upload/sales/year2022/csv_parser.rb
index 4bae79d33..939ff5689 100644
--- a/app/services/bulk_upload/sales/year2022/csv_parser.rb
+++ b/app/services/bulk_upload/sales/year2022/csv_parser.rb
@@ -2,6 +2,7 @@ require "csv"
class BulkUpload::Sales::Year2022::CsvParser
MAX_COLUMNS = 126
+ FORM_YEAR = 2022
attr_reader :path
@@ -44,11 +45,19 @@ class BulkUpload::Sales::Year2022::CsvParser
end
def wrong_template_for_year?
- false
+ !(first_record_sale_date >= form.start_date && first_record_sale_date <= form.end_date)
end
private
+ def form
+ @form ||= FormHandler.instance.sales_form_for_start_year(FORM_YEAR)
+ end
+
+ def first_record_sale_date
+ @first_record_sale_date ||= row_parsers.first.saledate || Date.new
+ end
+
def headers
@headers ||= ("field_1".."field_125").to_a
end
diff --git a/app/services/bulk_upload/sales/year2022/row_parser.rb b/app/services/bulk_upload/sales/year2022/row_parser.rb
index 1ac6258fd..40860c0d0 100644
--- a/app/services/bulk_upload/sales/year2022/row_parser.rb
+++ b/app/services/bulk_upload/sales/year2022/row_parser.rb
@@ -453,6 +453,12 @@ class BulkUpload::Sales::Year2022::RowParser
end
end
+ def saledate
+ Date.new(field_4 + 2000, field_3, field_2) if field_2.present? && field_3.present? && field_4.present?
+ rescue Date::Error
+ Date.new
+ end
+
private
def validate_data_protection_answered
@@ -762,12 +768,6 @@ private
end
end
- def saledate
- Date.new(field_4 + 2000, field_3, field_2) if field_2.present? && field_3.present? && field_4.present?
- rescue Date::Error
- Date.new
- end
-
def hodate
Date.new(field_61 + 2000, field_60, field_59) if field_59.present? && field_60.present? && field_61.present?
rescue Date::Error
diff --git a/spec/services/bulk_upload/lettings/validator_spec.rb b/spec/services/bulk_upload/lettings/validator_spec.rb
index 27d018229..9f202e2f9 100644
--- a/spec/services/bulk_upload/lettings/validator_spec.rb
+++ b/spec/services/bulk_upload/lettings/validator_spec.rb
@@ -36,7 +36,9 @@ RSpec.describe BulkUpload::Lettings::Validator do
context "and doesn't have too many columns" do
before do
- file.write(("a" * 135).chars.join(","))
+ file.write(("a" * 95).chars.join(","))
+ file.write(",1,10,22,")
+ file.write(("a" * 37).chars.join(","))
file.write("\n")
file.rewind
end
diff --git a/spec/services/bulk_upload/lettings/year2022/csv_parser_spec.rb b/spec/services/bulk_upload/lettings/year2022/csv_parser_spec.rb
index 5ae063567..15d20e9b2 100644
--- a/spec/services/bulk_upload/lettings/year2022/csv_parser_spec.rb
+++ b/spec/services/bulk_upload/lettings/year2022/csv_parser_spec.rb
@@ -187,4 +187,32 @@ RSpec.describe BulkUpload::Lettings::Year2022::CsvParser do
expect(service.row_parsers[0].field_12.to_i).to eq(35)
end
end
+
+ describe "#wrong_template_for_year?" do
+ context "when 23/24 file with 23/24 data" do
+ let(:log) { build(:lettings_log, :completed, startdate: Date.new(2023, 10, 1)) }
+
+ before do
+ file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_2023_csv_row)
+ file.rewind
+ end
+
+ it "returns true" do
+ expect(service).to be_wrong_template_for_year
+ end
+ end
+
+ context "when 22/23 file with 22/23 data" do
+ let(:log) { build(:lettings_log, :completed, startdate: Date.new(2022, 10, 1)) }
+
+ before do
+ file.write(BulkUpload::LettingsLogToCsv.new(log:, col_offset: 0).to_2022_csv_row)
+ file.rewind
+ end
+
+ it "returns false" do
+ expect(service).not_to be_wrong_template_for_year
+ end
+ end
+ end
end
diff --git a/spec/services/bulk_upload/sales/year2022/csv_parser_spec.rb b/spec/services/bulk_upload/sales/year2022/csv_parser_spec.rb
index 76504b974..c33f6cf2f 100644
--- a/spec/services/bulk_upload/sales/year2022/csv_parser_spec.rb
+++ b/spec/services/bulk_upload/sales/year2022/csv_parser_spec.rb
@@ -116,4 +116,35 @@ RSpec.describe BulkUpload::Sales::Year2022::CsvParser do
expect(service.column_for_field("field_125")).to eql("DU")
end
end
+
+ describe "#wrong_template_for_year?" do
+ let(:file) { Tempfile.new }
+ let(:path) { file.path }
+
+ context "when 23/24 file with 23/24 data" do
+ let(:log) { build(:sales_log, :completed, saledate: Date.new(2023, 10, 1)) }
+
+ before do
+ file.write(BulkUpload::SalesLogToCsv.new(log:, col_offset: 0).to_2023_csv_row)
+ file.rewind
+ end
+
+ it "returns true" do
+ expect(service).to be_wrong_template_for_year
+ end
+ end
+
+ context "when 22/23 file with 22/23 data" do
+ let(:log) { build(:sales_log, :completed, saledate: Date.new(2022, 10, 1)) }
+
+ before do
+ file.write(BulkUpload::SalesLogToCsv.new(log:, col_offset: 0).to_2022_csv_row)
+ file.rewind
+ end
+
+ it "returns false" do
+ expect(service).not_to be_wrong_template_for_year
+ end
+ end
+ end
end
From 08348973082289e176d28d699ec97e65c5bc0d41 Mon Sep 17 00:00:00 2001
From: Phil Lee
Date: Mon, 19 Jun 2023 15:52:21 +0100
Subject: [PATCH 18/19] CLDC-2330 Fix again (#1670)
* persist how to fix choice
* fix choice decision persisted
* set no cache headers
* add chosen to sales journey
* set no cache headers for sales
* fix linting
* Add chosen journey for sales soft validations
* Add chosen journey for lettings soft validations
* Update copy
* Also redirect fix inline and confirm if soft validations are chosen to be bulk fixed
---------
Co-authored-by: Kat
---
.../bulk_upload_lettings_resume_controller.rb | 9 +++
...tings_soft_validations_check_controller.rb | 9 +++
.../bulk_upload_sales_resume_controller.rb | 9 +++
...sales_soft_validations_check_controller.rb | 9 +++
.../bulk_upload_lettings_resume/chosen.rb | 31 ++++++++++
.../bulk_upload_lettings_resume/confirm.rb | 21 ++++++-
.../bulk_upload_lettings_resume/fix_choice.rb | 15 +++++
.../chosen.rb | 31 ++++++++++
.../confirm.rb | 21 ++++++-
.../confirm_soft_errors.rb | 13 +++++
.../forms/bulk_upload_sales_resume/chosen.rb | 31 ++++++++++
.../forms/bulk_upload_sales_resume/confirm.rb | 21 ++++++-
.../bulk_upload_sales_resume/fix_choice.rb | 15 +++++
.../chosen.rb | 31 ++++++++++
.../confirm.rb | 21 ++++++-
.../confirm_soft_errors.rb | 13 +++++
.../chosen.html.erb | 14 +++++
.../chosen.html.erb | 14 +++++
.../bulk_upload_sales_resume/chosen.html.erb | 14 +++++
.../chosen.html.erb | 14 +++++
...0230525090508_add_choice_to_bulk_upload.rb | 5 ++
db/schema.rb | 1 +
..._upload_lettings_resume_controller_spec.rb | 58 +++++++++++++++++++
..._soft_validations_check_controller_spec.rb | 34 +++++++++++
...ulk_upload_sales_resume_controller_spec.rb | 56 ++++++++++++++++++
..._soft_validations_check_controller_spec.rb | 34 +++++++++++
26 files changed, 536 insertions(+), 8 deletions(-)
create mode 100644 app/models/forms/bulk_upload_lettings_resume/chosen.rb
create mode 100644 app/models/forms/bulk_upload_lettings_soft_validations_check/chosen.rb
create mode 100644 app/models/forms/bulk_upload_sales_resume/chosen.rb
create mode 100644 app/models/forms/bulk_upload_sales_soft_validations_check/chosen.rb
create mode 100644 app/views/bulk_upload_lettings_resume/chosen.html.erb
create mode 100644 app/views/bulk_upload_lettings_soft_validations_check/chosen.html.erb
create mode 100644 app/views/bulk_upload_sales_resume/chosen.html.erb
create mode 100644 app/views/bulk_upload_sales_soft_validations_check/chosen.html.erb
create mode 100644 db/migrate/20230525090508_add_choice_to_bulk_upload.rb
diff --git a/app/controllers/bulk_upload_lettings_resume_controller.rb b/app/controllers/bulk_upload_lettings_resume_controller.rb
index 7041051ba..132ca095b 100644
--- a/app/controllers/bulk_upload_lettings_resume_controller.rb
+++ b/app/controllers/bulk_upload_lettings_resume_controller.rb
@@ -1,5 +1,10 @@
class BulkUploadLettingsResumeController < ApplicationController
before_action :authenticate_user!
+ before_action :set_no_cache_headers
+
+ def set_no_cache_headers
+ response.set_header("Cache-Control", "no-store")
+ end
def start
@bulk_upload = current_user.bulk_uploads.find(params[:id])
@@ -11,6 +16,8 @@ class BulkUploadLettingsResumeController < ApplicationController
@bulk_upload = current_user.bulk_uploads.find(params[:id])
@soft_errors_only = params[:soft_errors_only] == "true"
+ return redirect_to form.preflight_redirect unless form.preflight_valid?
+
render form.view_path
end
@@ -30,6 +37,8 @@ private
@form ||= case params[:page]
when "fix-choice"
Forms::BulkUploadLettingsResume::FixChoice.new(form_params.merge(bulk_upload: @bulk_upload))
+ when "chosen"
+ Forms::BulkUploadLettingsResume::Chosen.new(form_params.merge(bulk_upload: @bulk_upload))
when "confirm"
Forms::BulkUploadLettingsResume::Confirm.new(form_params.merge(bulk_upload: @bulk_upload))
else
diff --git a/app/controllers/bulk_upload_lettings_soft_validations_check_controller.rb b/app/controllers/bulk_upload_lettings_soft_validations_check_controller.rb
index 5f9140452..a70af3c4b 100644
--- a/app/controllers/bulk_upload_lettings_soft_validations_check_controller.rb
+++ b/app/controllers/bulk_upload_lettings_soft_validations_check_controller.rb
@@ -2,10 +2,17 @@ class BulkUploadLettingsSoftValidationsCheckController < ApplicationController
include ActionView::Helpers::TextHelper
before_action :authenticate_user!
+ before_action :set_no_cache_headers
+
+ def set_no_cache_headers
+ response.set_header("Cache-Control", "no-store")
+ end
def show
@bulk_upload = current_user.bulk_uploads.find(params[:id])
+ return redirect_to form.preflight_redirect unless form.preflight_valid?
+
render form.view_path
end
@@ -30,6 +37,8 @@ private
@form ||= case params[:page]
when "confirm-soft-errors"
Forms::BulkUploadLettingsSoftValidationsCheck::ConfirmSoftErrors.new(form_params.merge(bulk_upload: @bulk_upload))
+ when "chosen"
+ Forms::BulkUploadLettingsSoftValidationsCheck::Chosen.new(form_params.merge(bulk_upload: @bulk_upload))
when "confirm"
Forms::BulkUploadLettingsSoftValidationsCheck::Confirm.new(form_params.merge(bulk_upload: @bulk_upload))
else
diff --git a/app/controllers/bulk_upload_sales_resume_controller.rb b/app/controllers/bulk_upload_sales_resume_controller.rb
index e120620a2..9f937630d 100644
--- a/app/controllers/bulk_upload_sales_resume_controller.rb
+++ b/app/controllers/bulk_upload_sales_resume_controller.rb
@@ -1,5 +1,10 @@
class BulkUploadSalesResumeController < ApplicationController
before_action :authenticate_user!
+ before_action :set_no_cache_headers
+
+ def set_no_cache_headers
+ response.set_header("Cache-Control", "no-store")
+ end
def start
@bulk_upload = current_user.bulk_uploads.find(params[:id])
@@ -11,6 +16,8 @@ class BulkUploadSalesResumeController < ApplicationController
@bulk_upload = current_user.bulk_uploads.find(params[:id])
@soft_errors_only = params[:soft_errors_only] == "true"
+ return redirect_to form.preflight_redirect unless form.preflight_valid?
+
render form.view_path
end
@@ -30,6 +37,8 @@ private
@form ||= case params[:page]
when "fix-choice"
Forms::BulkUploadSalesResume::FixChoice.new(form_params.merge(bulk_upload: @bulk_upload))
+ when "chosen"
+ Forms::BulkUploadSalesResume::Chosen.new(form_params.merge(bulk_upload: @bulk_upload))
when "confirm"
Forms::BulkUploadSalesResume::Confirm.new(form_params.merge(bulk_upload: @bulk_upload))
else
diff --git a/app/controllers/bulk_upload_sales_soft_validations_check_controller.rb b/app/controllers/bulk_upload_sales_soft_validations_check_controller.rb
index 6daf167f8..5b71b2c40 100644
--- a/app/controllers/bulk_upload_sales_soft_validations_check_controller.rb
+++ b/app/controllers/bulk_upload_sales_soft_validations_check_controller.rb
@@ -2,10 +2,17 @@ class BulkUploadSalesSoftValidationsCheckController < ApplicationController
include ActionView::Helpers::TextHelper
before_action :authenticate_user!
+ before_action :set_no_cache_headers
+
+ def set_no_cache_headers
+ response.set_header("Cache-Control", "no-store")
+ end
def show
@bulk_upload = current_user.bulk_uploads.find(params[:id])
+ return redirect_to form.preflight_redirect unless form.preflight_valid?
+
render form.view_path
end
@@ -30,6 +37,8 @@ private
@form ||= case params[:page]
when "confirm-soft-errors"
Forms::BulkUploadSalesSoftValidationsCheck::ConfirmSoftErrors.new(form_params.merge(bulk_upload: @bulk_upload))
+ when "chosen"
+ Forms::BulkUploadSalesSoftValidationsCheck::Chosen.new(form_params.merge(bulk_upload: @bulk_upload))
when "confirm"
Forms::BulkUploadSalesSoftValidationsCheck::Confirm.new(form_params.merge(bulk_upload: @bulk_upload))
else
diff --git a/app/models/forms/bulk_upload_lettings_resume/chosen.rb b/app/models/forms/bulk_upload_lettings_resume/chosen.rb
new file mode 100644
index 000000000..6a6f670c4
--- /dev/null
+++ b/app/models/forms/bulk_upload_lettings_resume/chosen.rb
@@ -0,0 +1,31 @@
+module Forms
+ module BulkUploadLettingsResume
+ class Chosen
+ include ActiveModel::Model
+ include ActiveModel::Attributes
+ include Rails.application.routes.url_helpers
+
+ attribute :bulk_upload
+
+ def view_path
+ "bulk_upload_lettings_resume/chosen"
+ end
+
+ def back_path
+ lettings_logs_path
+ end
+
+ def next_path
+ lettings_logs_path
+ end
+
+ def save!
+ true
+ end
+
+ def preflight_valid?
+ true
+ end
+ end
+ end
+end
diff --git a/app/models/forms/bulk_upload_lettings_resume/confirm.rb b/app/models/forms/bulk_upload_lettings_resume/confirm.rb
index 7760ab2e8..c109cd1b1 100644
--- a/app/models/forms/bulk_upload_lettings_resume/confirm.rb
+++ b/app/models/forms/bulk_upload_lettings_resume/confirm.rb
@@ -20,11 +20,28 @@ module Forms
end
def save!
- processor = BulkUpload::Processor.new(bulk_upload:)
- processor.approve
+ ApplicationRecord.transaction do
+ processor = BulkUpload::Processor.new(bulk_upload:)
+ processor.approve
+
+ bulk_upload.update!(choice: "create-fix-inline")
+ end
true
end
+
+ def preflight_valid?
+ bulk_upload.choice != "create-fix-inline" && bulk_upload.choice != "bulk-confirm-soft-validations"
+ end
+
+ def preflight_redirect
+ case bulk_upload.choice
+ when "create-fix-inline"
+ page_bulk_upload_lettings_resume_path(bulk_upload, :chosen)
+ when "bulk-confirm-soft-validations"
+ page_bulk_upload_lettings_soft_validations_check_path(bulk_upload, :chosen)
+ end
+ end
end
end
end
diff --git a/app/models/forms/bulk_upload_lettings_resume/fix_choice.rb b/app/models/forms/bulk_upload_lettings_resume/fix_choice.rb
index 5513434de..76ee10d17 100644
--- a/app/models/forms/bulk_upload_lettings_resume/fix_choice.rb
+++ b/app/models/forms/bulk_upload_lettings_resume/fix_choice.rb
@@ -46,8 +46,23 @@ module Forms
end
def save!
+ bulk_upload.update!(choice:) if choice == "upload-again"
+
true
end
+
+ def preflight_valid?
+ bulk_upload.choice != "create-fix-inline" && bulk_upload.choice != "bulk-confirm-soft-validations"
+ end
+
+ def preflight_redirect
+ case bulk_upload.choice
+ when "create-fix-inline"
+ page_bulk_upload_lettings_resume_path(bulk_upload, :chosen)
+ when "bulk-confirm-soft-validations"
+ page_bulk_upload_lettings_soft_validations_check_path(bulk_upload, :chosen)
+ end
+ end
end
end
end
diff --git a/app/models/forms/bulk_upload_lettings_soft_validations_check/chosen.rb b/app/models/forms/bulk_upload_lettings_soft_validations_check/chosen.rb
new file mode 100644
index 000000000..b3091bc51
--- /dev/null
+++ b/app/models/forms/bulk_upload_lettings_soft_validations_check/chosen.rb
@@ -0,0 +1,31 @@
+module Forms
+ module BulkUploadLettingsSoftValidationsCheck
+ class Chosen
+ include ActiveModel::Model
+ include ActiveModel::Attributes
+ include Rails.application.routes.url_helpers
+
+ attribute :bulk_upload
+
+ def view_path
+ "bulk_upload_lettings_soft_validations_check/chosen"
+ end
+
+ def back_path
+ lettings_logs_path
+ end
+
+ def next_path
+ lettings_logs_path
+ end
+
+ def save!
+ true
+ end
+
+ def preflight_valid?
+ true
+ end
+ end
+ end
+end
diff --git a/app/models/forms/bulk_upload_lettings_soft_validations_check/confirm.rb b/app/models/forms/bulk_upload_lettings_soft_validations_check/confirm.rb
index b804d7767..aba75791e 100644
--- a/app/models/forms/bulk_upload_lettings_soft_validations_check/confirm.rb
+++ b/app/models/forms/bulk_upload_lettings_soft_validations_check/confirm.rb
@@ -20,11 +20,28 @@ module Forms
end
def save!
- processor = BulkUpload::Processor.new(bulk_upload:)
- processor.approve_and_confirm_soft_validations
+ ApplicationRecord.transaction do
+ processor = BulkUpload::Processor.new(bulk_upload:)
+ processor.approve_and_confirm_soft_validations
+
+ bulk_upload.update!(choice: "bulk-confirm-soft-validations")
+ end
true
end
+
+ def preflight_valid?
+ bulk_upload.choice != "bulk-confirm-soft-validations" && bulk_upload.choice != "create-fix-inline"
+ end
+
+ def preflight_redirect
+ case bulk_upload.choice
+ when "bulk-confirm-soft-validations"
+ page_bulk_upload_lettings_soft_validations_check_path(bulk_upload, :chosen)
+ when "create-fix-inline"
+ page_bulk_upload_lettings_resume_path(bulk_upload, :chosen)
+ end
+ end
end
end
end
diff --git a/app/models/forms/bulk_upload_lettings_soft_validations_check/confirm_soft_errors.rb b/app/models/forms/bulk_upload_lettings_soft_validations_check/confirm_soft_errors.rb
index cbf486a4d..34b4b97f3 100644
--- a/app/models/forms/bulk_upload_lettings_soft_validations_check/confirm_soft_errors.rb
+++ b/app/models/forms/bulk_upload_lettings_soft_validations_check/confirm_soft_errors.rb
@@ -35,6 +35,19 @@ module Forms
def save!
true
end
+
+ def preflight_valid?
+ bulk_upload.choice != "bulk-confirm-soft-validations" && bulk_upload.choice != "create-fix-inline"
+ end
+
+ def preflight_redirect
+ case bulk_upload.choice
+ when "bulk-confirm-soft-validations"
+ page_bulk_upload_lettings_soft_validations_check_path(bulk_upload, :chosen)
+ when "create-fix-inline"
+ page_bulk_upload_lettings_resume_path(bulk_upload, :chosen)
+ end
+ end
end
end
end
diff --git a/app/models/forms/bulk_upload_sales_resume/chosen.rb b/app/models/forms/bulk_upload_sales_resume/chosen.rb
new file mode 100644
index 000000000..2fa85c6c9
--- /dev/null
+++ b/app/models/forms/bulk_upload_sales_resume/chosen.rb
@@ -0,0 +1,31 @@
+module Forms
+ module BulkUploadSalesResume
+ class Chosen
+ include ActiveModel::Model
+ include ActiveModel::Attributes
+ include Rails.application.routes.url_helpers
+
+ attribute :bulk_upload
+
+ def view_path
+ "bulk_upload_sales_resume/chosen"
+ end
+
+ def back_path
+ sales_logs_path
+ end
+
+ def next_path
+ sales_logs_path
+ end
+
+ def save!
+ true
+ end
+
+ def preflight_valid?
+ true
+ end
+ end
+ end
+end
diff --git a/app/models/forms/bulk_upload_sales_resume/confirm.rb b/app/models/forms/bulk_upload_sales_resume/confirm.rb
index 4ce50fb55..1211ef3f0 100644
--- a/app/models/forms/bulk_upload_sales_resume/confirm.rb
+++ b/app/models/forms/bulk_upload_sales_resume/confirm.rb
@@ -20,11 +20,28 @@ module Forms
end
def save!
- processor = BulkUpload::Processor.new(bulk_upload:)
- processor.approve
+ ApplicationRecord.transaction do
+ processor = BulkUpload::Processor.new(bulk_upload:)
+ processor.approve
+
+ bulk_upload.update!(choice: "create-fix-inline")
+ end
true
end
+
+ def preflight_valid?
+ bulk_upload.choice != "create-fix-inline" && bulk_upload.choice != "bulk-confirm-soft-validations"
+ end
+
+ def preflight_redirect
+ case bulk_upload.choice
+ when "create-fix-inline"
+ page_bulk_upload_sales_resume_path(bulk_upload, :chosen)
+ when "bulk-confirm-soft-validations"
+ page_bulk_upload_sales_soft_validations_check_path(bulk_upload, :chosen)
+ end
+ end
end
end
end
diff --git a/app/models/forms/bulk_upload_sales_resume/fix_choice.rb b/app/models/forms/bulk_upload_sales_resume/fix_choice.rb
index 671891429..fc565e2f6 100644
--- a/app/models/forms/bulk_upload_sales_resume/fix_choice.rb
+++ b/app/models/forms/bulk_upload_sales_resume/fix_choice.rb
@@ -46,8 +46,23 @@ module Forms
end
def save!
+ bulk_upload.update!(choice:) if choice == "upload-again"
+
true
end
+
+ def preflight_valid?
+ bulk_upload.choice != "create-fix-inline" && bulk_upload.choice != "bulk-confirm-soft-validations"
+ end
+
+ def preflight_redirect
+ case bulk_upload.choice
+ when "create-fix-inline"
+ page_bulk_upload_sales_resume_path(bulk_upload, :chosen)
+ when "bulk-confirm-soft-validations"
+ page_bulk_upload_sales_soft_validations_check_path(bulk_upload, :chosen)
+ end
+ end
end
end
end
diff --git a/app/models/forms/bulk_upload_sales_soft_validations_check/chosen.rb b/app/models/forms/bulk_upload_sales_soft_validations_check/chosen.rb
new file mode 100644
index 000000000..2286d3b39
--- /dev/null
+++ b/app/models/forms/bulk_upload_sales_soft_validations_check/chosen.rb
@@ -0,0 +1,31 @@
+module Forms
+ module BulkUploadSalesSoftValidationsCheck
+ class Chosen
+ include ActiveModel::Model
+ include ActiveModel::Attributes
+ include Rails.application.routes.url_helpers
+
+ attribute :bulk_upload
+
+ def view_path
+ "bulk_upload_sales_soft_validations_check/chosen"
+ end
+
+ def back_path
+ sales_logs_path
+ end
+
+ def next_path
+ sales_logs_path
+ end
+
+ def save!
+ true
+ end
+
+ def preflight_valid?
+ true
+ end
+ end
+ end
+end
diff --git a/app/models/forms/bulk_upload_sales_soft_validations_check/confirm.rb b/app/models/forms/bulk_upload_sales_soft_validations_check/confirm.rb
index 579af7e84..894f55123 100644
--- a/app/models/forms/bulk_upload_sales_soft_validations_check/confirm.rb
+++ b/app/models/forms/bulk_upload_sales_soft_validations_check/confirm.rb
@@ -20,11 +20,28 @@ module Forms
end
def save!
- processor = BulkUpload::Processor.new(bulk_upload:)
- processor.approve_and_confirm_soft_validations
+ ApplicationRecord.transaction do
+ processor = BulkUpload::Processor.new(bulk_upload:)
+ processor.approve_and_confirm_soft_validations
+
+ bulk_upload.update!(choice: "bulk-confirm-soft-validations")
+ end
true
end
+
+ def preflight_valid?
+ bulk_upload.choice != "bulk-confirm-soft-validations" && bulk_upload.choice != "create-fix-inline"
+ end
+
+ def preflight_redirect
+ case bulk_upload.choice
+ when "bulk-confirm-soft-validations"
+ page_bulk_upload_sales_soft_validations_check_path(bulk_upload, :chosen)
+ when "create-fix-inline"
+ page_bulk_upload_sales_resume_path(bulk_upload, :chosen)
+ end
+ end
end
end
end
diff --git a/app/models/forms/bulk_upload_sales_soft_validations_check/confirm_soft_errors.rb b/app/models/forms/bulk_upload_sales_soft_validations_check/confirm_soft_errors.rb
index e6fe00495..041647cf0 100644
--- a/app/models/forms/bulk_upload_sales_soft_validations_check/confirm_soft_errors.rb
+++ b/app/models/forms/bulk_upload_sales_soft_validations_check/confirm_soft_errors.rb
@@ -35,6 +35,19 @@ module Forms
def save!
true
end
+
+ def preflight_valid?
+ bulk_upload.choice != "bulk-confirm-soft-validations" && bulk_upload.choice != "create-fix-inline"
+ end
+
+ def preflight_redirect
+ case bulk_upload.choice
+ when "bulk-confirm-soft-validations"
+ page_bulk_upload_sales_soft_validations_check_path(bulk_upload, :chosen)
+ when "create-fix-inline"
+ page_bulk_upload_sales_resume_path(bulk_upload, :chosen)
+ end
+ end
end
end
end
diff --git a/app/views/bulk_upload_lettings_resume/chosen.html.erb b/app/views/bulk_upload_lettings_resume/chosen.html.erb
new file mode 100644
index 000000000..47ed9bbfd
--- /dev/null
+++ b/app/views/bulk_upload_lettings_resume/chosen.html.erb
@@ -0,0 +1,14 @@
+<% content_for :before_content do %>
+ <%= govuk_back_link href: @form.back_path %>
+<% end %>
+
+
+
+
Bulk upload for lettings (<%= @bulk_upload.year_combo %>)
+
You need to fix logs from your bulk upload
+
+
You have chosen to create logs from your recent bulk upload. To view and complete these logs, return to the list of lettings logs.
+
+ <%= govuk_button_link_to "Return to lettings logs", lettings_logs_path %>
+
+
diff --git a/app/views/bulk_upload_lettings_soft_validations_check/chosen.html.erb b/app/views/bulk_upload_lettings_soft_validations_check/chosen.html.erb
new file mode 100644
index 000000000..418387fc9
--- /dev/null
+++ b/app/views/bulk_upload_lettings_soft_validations_check/chosen.html.erb
@@ -0,0 +1,14 @@
+<% content_for :before_content do %>
+ <%= govuk_back_link href: @form.back_path %>
+<% end %>
+
+
+
+
Bulk upload for lettings (<%= @bulk_upload.year_combo %>)
+
These logs have been created
+
+
You have created logs from your bulk upload. Return to lettings logs to view them.
+
+ <%= govuk_button_link_to "Return to lettings logs", lettings_logs_path %>
+
+
diff --git a/app/views/bulk_upload_sales_resume/chosen.html.erb b/app/views/bulk_upload_sales_resume/chosen.html.erb
new file mode 100644
index 000000000..0d381e6a6
--- /dev/null
+++ b/app/views/bulk_upload_sales_resume/chosen.html.erb
@@ -0,0 +1,14 @@
+<% content_for :before_content do %>
+ <%= govuk_back_link href: @form.back_path %>
+<% end %>
+
+
+
+
Bulk upload for sales (<%= @bulk_upload.year_combo %>)
+
You need to fix logs from your bulk upload
+
+
You have chosen to create logs from your recent bulk upload. To view and complete these logs, return to the list of sales logs.
+
+ <%= govuk_button_link_to "Return to sales logs", sales_logs_path %>
+
+
diff --git a/app/views/bulk_upload_sales_soft_validations_check/chosen.html.erb b/app/views/bulk_upload_sales_soft_validations_check/chosen.html.erb
new file mode 100644
index 000000000..211f9c03c
--- /dev/null
+++ b/app/views/bulk_upload_sales_soft_validations_check/chosen.html.erb
@@ -0,0 +1,14 @@
+<% content_for :before_content do %>
+ <%= govuk_back_link href: @form.back_path %>
+<% end %>
+
+
+
+
Bulk upload for sales (<%= @bulk_upload.year_combo %>)
+
These logs have been created
+
+
You have created logs from your bulk upload. Return to sales logs to view them.
+
+ <%= govuk_button_link_to "Return to sales logs", sales_logs_path %>
+
+
diff --git a/db/migrate/20230525090508_add_choice_to_bulk_upload.rb b/db/migrate/20230525090508_add_choice_to_bulk_upload.rb
new file mode 100644
index 000000000..e8c299ec6
--- /dev/null
+++ b/db/migrate/20230525090508_add_choice_to_bulk_upload.rb
@@ -0,0 +1,5 @@
+class AddChoiceToBulkUpload < ActiveRecord::Migration[7.0]
+ def change
+ add_column :bulk_uploads, :choice, :text, null: true
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 8337c5305..1bbbba02a 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -39,6 +39,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_09_101144) do
t.datetime "updated_at", null: false
t.text "filename"
t.integer "needstype"
+ t.text "choice"
t.index ["identifier"], name: "index_bulk_uploads_on_identifier", unique: true
t.index ["user_id"], name: "index_bulk_uploads_on_user_id"
end
diff --git a/spec/requests/bulk_upload_lettings_resume_controller_spec.rb b/spec/requests/bulk_upload_lettings_resume_controller_spec.rb
index 3666bc777..7ba8bbc85 100644
--- a/spec/requests/bulk_upload_lettings_resume_controller_spec.rb
+++ b/spec/requests/bulk_upload_lettings_resume_controller_spec.rb
@@ -29,6 +29,32 @@ RSpec.describe BulkUploadLettingsResumeController, type: :request do
expect(response.body).to include(bulk_upload.filename)
expect(response.body).not_to include("Cancel")
end
+
+ it "sets no cache headers" do
+ get "/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/fix-choice"
+
+ expect(response.headers["Cache-Control"]).to eql("no-store")
+ end
+
+ context "and previously told us to fix inline" do
+ let(:bulk_upload) { create(:bulk_upload, :lettings, user:, bulk_upload_errors:, choice: "create-fix-inline") }
+
+ it "redirects to chosen" do
+ get "/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/fix-choice"
+
+ expect(response).to redirect_to("/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/chosen")
+ end
+ end
+
+ context "and previously told us to bulk confirm soft validations" do
+ let(:bulk_upload) { create(:bulk_upload, :lettings, user:, bulk_upload_errors:, choice: "bulk-confirm-soft-validations") }
+
+ it "redirects to soft validations check chosen" do
+ get "/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/fix-choice"
+
+ expect(response).to redirect_to("/lettings-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/chosen")
+ end
+ end
end
describe "GET /lettings-logs/bulk-upload-resume/:ID/fix-choice?soft_errors_only=true" do
@@ -58,6 +84,8 @@ RSpec.describe BulkUploadLettingsResumeController, type: :request do
patch "/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/fix-choice", params: { form: { choice: "upload-again" } }
expect(response).to redirect_to("/lettings-logs/bulk-upload-results/#{bulk_upload.id}")
+
+ expect(bulk_upload.reload.choice).to eql("upload-again")
end
end
@@ -66,6 +94,8 @@ RSpec.describe BulkUploadLettingsResumeController, type: :request do
patch "/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/fix-choice", params: { form: { choice: "create-fix-inline" } }
expect(response).to redirect_to("/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/confirm")
+
+ expect(bulk_upload.reload.choice).to be_blank
end
end
end
@@ -78,6 +108,32 @@ RSpec.describe BulkUploadLettingsResumeController, type: :request do
expect(response.body).to include("Are you sure")
end
+
+ it "sets no cache headers" do
+ get "/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/confirm"
+
+ expect(response.headers["Cache-Control"]).to eql("no-store")
+ end
+
+ context "and previously told us to fix inline" do
+ let(:bulk_upload) { create(:bulk_upload, :lettings, user:, bulk_upload_errors:, choice: "create-fix-inline") }
+
+ it "redirects to chosen" do
+ get "/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/confirm"
+
+ expect(response).to redirect_to("/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/chosen")
+ end
+ end
+
+ context "and previously told us to bulk confirm soft validations" do
+ let(:bulk_upload) { create(:bulk_upload, :lettings, user:, bulk_upload_errors:, choice: "bulk-confirm-soft-validations") }
+
+ it "redirects to soft validations check chosen" do
+ get "/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/confirm"
+
+ expect(response).to redirect_to("/lettings-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/chosen")
+ end
+ end
end
describe "PATCH /lettings-logs/bulk-upload-resume/:ID/confirm" do
@@ -90,6 +146,8 @@ RSpec.describe BulkUploadLettingsResumeController, type: :request do
expect(mock_processor).to have_received(:approve)
+ expect(bulk_upload.reload.choice).to eql("create-fix-inline")
+
expect(response).to redirect_to("/lettings-logs/bulk-upload-results/#{bulk_upload.id}/resume")
end
end
diff --git a/spec/requests/bulk_upload_lettings_soft_validations_check_controller_spec.rb b/spec/requests/bulk_upload_lettings_soft_validations_check_controller_spec.rb
index d1252b5e2..0603e3d34 100644
--- a/spec/requests/bulk_upload_lettings_soft_validations_check_controller_spec.rb
+++ b/spec/requests/bulk_upload_lettings_soft_validations_check_controller_spec.rb
@@ -28,6 +28,32 @@ RSpec.describe BulkUploadLettingsSoftValidationsCheckController, type: :request
expect(response.body).to include("Tenant code")
expect(response.body).to include("some error")
end
+
+ it "sets no cache headers" do
+ get "/lettings-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/confirm-soft-errors"
+
+ expect(response.headers["Cache-Control"]).to eql("no-store")
+ end
+
+ context "and previously told us to fix inline" do
+ let(:bulk_upload) { create(:bulk_upload, :lettings, user:, bulk_upload_errors:, choice: "create-fix-inline") }
+
+ it "redirects to resume chosen" do
+ get "/lettings-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/confirm-soft-errors"
+
+ expect(response).to redirect_to("/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/chosen")
+ end
+ end
+
+ context "and previously told us to bulk confirm soft validations" do
+ let(:bulk_upload) { create(:bulk_upload, :lettings, user:, bulk_upload_errors:, choice: "bulk-confirm-soft-validations") }
+
+ it "redirects to soft validations check chosen" do
+ get "/lettings-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/confirm-soft-errors"
+
+ expect(response).to redirect_to("/lettings-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/chosen")
+ end
+ end
end
describe "PATCH /lettings-logs/bulk-upload-soft-validations-check/:ID/confirm-soft-errors" do
@@ -38,6 +64,8 @@ RSpec.describe BulkUploadLettingsSoftValidationsCheckController, type: :request
expect(response).to be_successful
expect(response.body).to include("You must select if there are errors in these fields")
+
+ expect(bulk_upload.reload.choice).to be_blank
end
end
@@ -46,6 +74,8 @@ RSpec.describe BulkUploadLettingsSoftValidationsCheckController, type: :request
patch "/lettings-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/confirm-soft-errors", params: { form: { confirm_soft_errors: "no" } }
expect(response).to redirect_to("/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/fix-choice?soft_errors_only=true")
+
+ expect(bulk_upload.reload.choice).to be_blank
end
end
@@ -56,6 +86,8 @@ RSpec.describe BulkUploadLettingsSoftValidationsCheckController, type: :request
expect(response).to redirect_to("/lettings-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/confirm")
follow_redirect!
expect(response.body).not_to include("You’ve successfully uploaded")
+
+ expect(bulk_upload.reload.choice).to be_blank
end
end
end
@@ -85,6 +117,8 @@ RSpec.describe BulkUploadLettingsSoftValidationsCheckController, type: :request
expect(response).to redirect_to("/lettings-logs")
follow_redirect!
expect(response.body).to include("You’ve successfully uploaded 2 logs")
+
+ expect(bulk_upload.reload.choice).to eql("bulk-confirm-soft-validations")
end
end
end
diff --git a/spec/requests/bulk_upload_sales_resume_controller_spec.rb b/spec/requests/bulk_upload_sales_resume_controller_spec.rb
index 8dcc0ba00..9c0a7112c 100644
--- a/spec/requests/bulk_upload_sales_resume_controller_spec.rb
+++ b/spec/requests/bulk_upload_sales_resume_controller_spec.rb
@@ -29,6 +29,32 @@ RSpec.describe BulkUploadSalesResumeController, type: :request do
expect(response.body).to include(bulk_upload.filename)
expect(response.body).not_to include("Cancel")
end
+
+ it "sets no cache headers" do
+ get "/sales-logs/bulk-upload-resume/#{bulk_upload.id}/fix-choice"
+
+ expect(response.headers["Cache-Control"]).to eql("no-store")
+ end
+
+ context "and previously told us to fix inline" do
+ let(:bulk_upload) { create(:bulk_upload, :sales, user:, bulk_upload_errors:, choice: "create-fix-inline") }
+
+ it "redirects to chosen" do
+ get "/sales-logs/bulk-upload-resume/#{bulk_upload.id}/fix-choice"
+
+ expect(response).to redirect_to("/sales-logs/bulk-upload-resume/#{bulk_upload.id}/chosen")
+ end
+ end
+
+ context "and previously told us to bulk confirm soft validations" do
+ let(:bulk_upload) { create(:bulk_upload, :sales, user:, bulk_upload_errors:, choice: "bulk-confirm-soft-validations") }
+
+ it "redirects to soft validations check chosen" do
+ get "/sales-logs/bulk-upload-resume/#{bulk_upload.id}/fix-choice"
+
+ expect(response).to redirect_to("/sales-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/chosen")
+ end
+ end
end
describe "GET /sales-logs/bulk-upload-resume/:ID/fix-choice?soft_errors_only=true" do
@@ -58,6 +84,8 @@ RSpec.describe BulkUploadSalesResumeController, type: :request do
patch "/sales-logs/bulk-upload-resume/#{bulk_upload.id}/fix-choice", params: { form: { choice: "upload-again" } }
expect(response).to redirect_to("/sales-logs/bulk-upload-results/#{bulk_upload.id}")
+
+ expect(bulk_upload.reload.choice).to eql("upload-again")
end
end
@@ -66,6 +94,8 @@ RSpec.describe BulkUploadSalesResumeController, type: :request do
patch "/sales-logs/bulk-upload-resume/#{bulk_upload.id}/fix-choice", params: { form: { choice: "create-fix-inline" } }
expect(response).to redirect_to("/sales-logs/bulk-upload-resume/#{bulk_upload.id}/confirm")
+
+ expect(bulk_upload.reload.choice).to be_blank
end
end
end
@@ -78,6 +108,22 @@ RSpec.describe BulkUploadSalesResumeController, type: :request do
expect(response.body).to include("Are you sure")
end
+
+ it "sets no cache headers" do
+ get "/sales-logs/bulk-upload-resume/#{bulk_upload.id}/confirm"
+
+ expect(response.headers["Cache-Control"]).to eql("no-store")
+ end
+
+ context "and previously told us to bulk confirm soft validations" do
+ let(:bulk_upload) { create(:bulk_upload, :sales, user:, bulk_upload_errors:, choice: "bulk-confirm-soft-validations") }
+
+ it "redirects to soft validations check chosen" do
+ get "/sales-logs/bulk-upload-resume/#{bulk_upload.id}/confirm"
+
+ expect(response).to redirect_to("/sales-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/chosen")
+ end
+ end
end
describe "PATCH /sales-logs/bulk-upload-resume/:ID/confirm" do
@@ -90,7 +136,17 @@ RSpec.describe BulkUploadSalesResumeController, type: :request do
expect(mock_processor).to have_received(:approve)
+ expect(bulk_upload.reload.choice).to eql("create-fix-inline")
+
expect(response).to redirect_to("/sales-logs/bulk-upload-results/#{bulk_upload.id}/resume")
end
end
+
+ describe "GET /sales-logs/bulk-upload-resume/:ID/chosen" do
+ it "displays correct content" do
+ get "/sales-logs/bulk-upload-resume/#{bulk_upload.id}/chosen"
+
+ expect(response.body).to include("You need to fix logs from your bulk upload")
+ end
+ end
end
diff --git a/spec/requests/bulk_upload_sales_soft_validations_check_controller_spec.rb b/spec/requests/bulk_upload_sales_soft_validations_check_controller_spec.rb
index 3aaa433f0..0b496aea4 100644
--- a/spec/requests/bulk_upload_sales_soft_validations_check_controller_spec.rb
+++ b/spec/requests/bulk_upload_sales_soft_validations_check_controller_spec.rb
@@ -28,6 +28,32 @@ RSpec.describe BulkUploadSalesSoftValidationsCheckController, type: :request do
expect(response.body).to include("Purchaser code")
expect(response.body).to include("some error")
end
+
+ it "sets no cache headers" do
+ get "/sales-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/confirm-soft-errors"
+
+ expect(response.headers["Cache-Control"]).to eql("no-store")
+ end
+
+ context "and previously told us to fix inline" do
+ let(:bulk_upload) { create(:bulk_upload, :sales, user:, bulk_upload_errors:, choice: "create-fix-inline") }
+
+ it "redirects to resume chosen" do
+ get "/sales-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/confirm-soft-errors"
+
+ expect(response).to redirect_to("/sales-logs/bulk-upload-resume/#{bulk_upload.id}/chosen")
+ end
+ end
+
+ context "and previously told us to bulk confirm soft validations" do
+ let(:bulk_upload) { create(:bulk_upload, :sales, user:, bulk_upload_errors:, choice: "bulk-confirm-soft-validations") }
+
+ it "redirects to soft validations check chosen" do
+ get "/sales-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/confirm-soft-errors"
+
+ expect(response).to redirect_to("/sales-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/chosen")
+ end
+ end
end
describe "PATCH /sales-logs/bulk-upload-soft-validations-check/:ID/confirm-soft-errors" do
@@ -38,6 +64,8 @@ RSpec.describe BulkUploadSalesSoftValidationsCheckController, type: :request do
expect(response).to be_successful
expect(response.body).to include("You must select if there are errors in these fields")
+
+ expect(bulk_upload.reload.choice).to be_blank
end
end
@@ -46,6 +74,8 @@ RSpec.describe BulkUploadSalesSoftValidationsCheckController, type: :request do
patch "/sales-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/confirm-soft-errors", params: { form: { confirm_soft_errors: "no" } }
expect(response).to redirect_to("/sales-logs/bulk-upload-resume/#{bulk_upload.id}/fix-choice?soft_errors_only=true")
+
+ expect(bulk_upload.reload.choice).to be_blank
end
end
@@ -56,6 +86,8 @@ RSpec.describe BulkUploadSalesSoftValidationsCheckController, type: :request do
expect(response).to redirect_to("/sales-logs/bulk-upload-soft-validations-check/#{bulk_upload.id}/confirm")
follow_redirect!
expect(response.body).not_to include("You’ve successfully uploaded")
+
+ expect(bulk_upload.reload.choice).to be_blank
end
end
end
@@ -85,6 +117,8 @@ RSpec.describe BulkUploadSalesSoftValidationsCheckController, type: :request do
expect(response).to redirect_to("/sales-logs")
follow_redirect!
expect(response.body).to include("You’ve successfully uploaded 2 logs")
+
+ expect(bulk_upload.reload.choice).to eql("bulk-confirm-soft-validations")
end
end
end
From de3e44459df68b3b7c09ccd29a710775ae686b31 Mon Sep 17 00:00:00 2001
From: Jack <113976590+bibblobcode@users.noreply.github.com>
Date: Mon, 19 Jun 2023 16:07:32 +0100
Subject: [PATCH 19/19] Put bulk upload controller checks behind feature flag
(#1712)
---
.../bulk_upload_lettings_logs_controller.rb | 7 ++++---
app/controllers/bulk_upload_sales_logs_controller.rb | 7 ++++---
.../bulk_upload_lettings_logs_controller_spec.rb | 12 ++++++++++++
.../bulk_upload_sales_logs_controller_spec.rb | 12 ++++++++++++
4 files changed, 32 insertions(+), 6 deletions(-)
diff --git a/app/controllers/bulk_upload_lettings_logs_controller.rb b/app/controllers/bulk_upload_lettings_logs_controller.rb
index 1d011dcca..035621e41 100644
--- a/app/controllers/bulk_upload_lettings_logs_controller.rb
+++ b/app/controllers/bulk_upload_lettings_logs_controller.rb
@@ -25,9 +25,10 @@ class BulkUploadLettingsLogsController < ApplicationController
private
def validate_data_protection_agrement_signed!
- unless @current_user.organisation.data_protection_confirmed?
- redirect_to lettings_logs_path
- end
+ return unless FeatureToggle.new_data_protection_confirmation?
+ return if @current_user.organisation.data_protection_confirmed?
+
+ redirect_to lettings_logs_path
end
def current_year
diff --git a/app/controllers/bulk_upload_sales_logs_controller.rb b/app/controllers/bulk_upload_sales_logs_controller.rb
index aa865f0c7..7a7a20297 100644
--- a/app/controllers/bulk_upload_sales_logs_controller.rb
+++ b/app/controllers/bulk_upload_sales_logs_controller.rb
@@ -25,9 +25,10 @@ class BulkUploadSalesLogsController < ApplicationController
private
def validate_data_protection_agrement_signed!
- unless @current_user.organisation.data_protection_confirmed?
- redirect_to sales_logs_path
- end
+ return unless FeatureToggle.new_data_protection_confirmation?
+ return if @current_user.organisation.data_protection_confirmed?
+
+ redirect_to sales_logs_path
end
def current_year
diff --git a/spec/requests/bulk_upload_lettings_logs_controller_spec.rb b/spec/requests/bulk_upload_lettings_logs_controller_spec.rb
index f901cdb7e..db5a3c4a6 100644
--- a/spec/requests/bulk_upload_lettings_logs_controller_spec.rb
+++ b/spec/requests/bulk_upload_lettings_logs_controller_spec.rb
@@ -18,6 +18,18 @@ RSpec.describe BulkUploadLettingsLogsController, type: :request do
expect(response).to redirect_to("/lettings-logs")
end
+
+ context "when feature flag disabled" do
+ before do
+ allow(FeatureToggle).to receive(:new_data_protection_confirmation?).and_return(false)
+ end
+
+ it "does not redirect to lettings index page" do
+ get "/lettings-logs/bulk-upload-logs/start", params: {}
+
+ expect(response).not_to redirect_to("/lettings-logs")
+ end
+ end
end
context "when not in crossover period" do
diff --git a/spec/requests/bulk_upload_sales_logs_controller_spec.rb b/spec/requests/bulk_upload_sales_logs_controller_spec.rb
index 3220ff885..c7de6598e 100644
--- a/spec/requests/bulk_upload_sales_logs_controller_spec.rb
+++ b/spec/requests/bulk_upload_sales_logs_controller_spec.rb
@@ -18,6 +18,18 @@ RSpec.describe BulkUploadSalesLogsController, type: :request do
expect(response).to redirect_to("/sales-logs")
end
+
+ context "when feature flag disabled" do
+ before do
+ allow(FeatureToggle).to receive(:new_data_protection_confirmation?).and_return(false)
+ end
+
+ it "does not redirect to lettings index page" do
+ get "/lettings-logs/bulk-upload-logs/start", params: {}
+
+ expect(response).not_to redirect_to("/sales-logs")
+ end
+ end
end
context "when not in crossover period" do