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:

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

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

+ + +

Create your file

+ + +

Save your file

+ + + <%= 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