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/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/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_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_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/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/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/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/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/locations_controller.rb b/app/controllers/locations_controller.rb
index 453c0934f..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,16 +180,21 @@ 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
- 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/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/controllers/schemes_controller.rb b/app/controllers/schemes_controller.rb
index 784ad5f62..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,15 +58,20 @@ 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
- 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/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 0c343b8c3..3f91fa659 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
@@ -114,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
@@ -145,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/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/locations_helper.rb b/app/helpers/locations_helper.rb
index 7e38ed36c..235029e9d 100644
--- a/app/helpers/locations_helper.rb
+++ b/app/helpers/locations_helper.rb
@@ -24,9 +24,10 @@ 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: "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" },
@@ -34,12 +35,6 @@ module LocationsHelper
{ name: "Location code", value: formatted_local_authority_timeline(location, "code"), attribute: "location_code" },
{ name: "Availability", value: location_availability(location), attribute: "availability" },
]
-
- 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)
@@ -74,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/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/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/helpers/schemes_helper.rb b/app/helpers/schemes_helper.rb
index 610d33af5..5703f0350 100644
--- a/app/helpers/schemes_helper.rb
+++ b/app/helpers/schemes_helper.rb
@@ -3,6 +3,7 @@ module SchemesHelper
[
{ 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 },
@@ -30,7 +31,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/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/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/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/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/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/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/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_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/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/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/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/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/log.rb b/app/models/log.rb
index 452aa67c6..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,
@@ -139,9 +138,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"
@@ -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
@@ -210,11 +201,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/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/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/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/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/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 b79447b67..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
@@ -774,13 +780,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
@@ -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/lettings/year2023/row_parser.rb b/app/services/bulk_upload/lettings/year2023/row_parser.rb
index 225c45b4f..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
@@ -1442,8 +1442,6 @@ private
when 2
1
when 3
- 1
- when 4
2
end
end
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 ed51a0601..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
@@ -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/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/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_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_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_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/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.
diff --git a/app/views/locations/index.html.erb b/app/views/locations/index.html.erb
index 85ae27fed..7641bbd48 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_from_resource(location)) %>
<% 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..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 %>
@@ -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/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/app/views/schemes/_scheme_list.html.erb b/app/views/schemes/_scheme_list.html.erb
index 5fa712d1e..43e03cd65 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_from_resource(scheme)) %>
<% end %>
<% end %>
<% end %>
diff --git a/app/views/schemes/show.html.erb b/app/views/schemes/show.html.erb
index 0005582e8..66209fa28 100644
--- a/app/views/schemes/show.html.erb
+++ b/app/views/schemes/show.html.erb
@@ -9,33 +9,31 @@
<%= 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] %>
+ <%= 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 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 %>
<% 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/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 e6cb9fd3e..844a0c965 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
@@ -24,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
@@ -34,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,
@@ -64,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),
@@ -78,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),
@@ -92,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),
@@ -103,5 +102,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/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..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"
@@ -175,6 +177,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.
@@ -249,8 +256,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/config/routes.rb b/config/routes.rb
index 9dd9b7325..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
@@ -122,7 +123,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 +179,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 +241,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/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/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
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/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/features/schemes_spec.rb b/spec/features/schemes_spec.rb
index fd1c49d7e..676895f62 100644
--- a/spec/features/schemes_spec.rb
+++ b/spec/features/schemes_spec.rb
@@ -234,23 +234,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/features/user_spec.rb b/spec/features/user_spec.rb
index 0c7f6c2c6..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)
@@ -454,6 +480,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/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/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/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/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/helpers/schemes_helper_spec.rb b/spec/helpers/schemes_helper_spec.rb
index 09a9c9e2e..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,19 +191,10 @@ 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
- 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/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/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/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/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
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/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/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/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
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/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/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
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_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_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
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
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/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
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/locations_controller_spec.rb b/spec/requests/locations_controller_spec.rb
index 04eb71f1a..edc5dc452 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
@@ -1493,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))
@@ -1537,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
@@ -1564,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
@@ -1630,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
@@ -1699,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
@@ -1765,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!
@@ -1799,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}")
@@ -1812,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/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/requests/schemes_controller_spec.rb b/spec/requests/schemes_controller_spec.rb
index 4c7bad55a..754d8cb22 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
@@ -352,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
@@ -1930,7 +1959,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 +1971,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 +1999,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
@@ -1989,6 +2028,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 {
@@ -2051,6 +2122,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/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb
index a97086bb2..564167354 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
@@ -113,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")
@@ -123,6 +131,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 +144,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
@@ -165,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")
@@ -184,6 +201,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
@@ -480,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")
@@ -499,6 +521,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
@@ -520,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")
@@ -530,6 +557,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 +574,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
@@ -1138,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")
@@ -1167,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")
@@ -1177,6 +1214,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)
@@ -1207,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")
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/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..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
@@ -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
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
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
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/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
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