Browse Source

Merge branch 'main' into CLDC-1741-scheme-attributes-bug-fix

# Conflicts:
#	app/helpers/schemes_helper.rb
CLDC-1741-scheme-attributes-bug-fix
natdeanlewissoftwire 2 years ago
parent
commit
36b057dbcf
  1. 2
      app/components/data_protection_confirmation_banner_component.rb
  2. 2
      app/controllers/auth/passwords_controller.rb
  3. 7
      app/controllers/bulk_upload_lettings_logs_controller.rb
  4. 9
      app/controllers/bulk_upload_lettings_resume_controller.rb
  5. 9
      app/controllers/bulk_upload_lettings_soft_validations_check_controller.rb
  6. 7
      app/controllers/bulk_upload_sales_logs_controller.rb
  7. 9
      app/controllers/bulk_upload_sales_resume_controller.rb
  8. 9
      app/controllers/bulk_upload_sales_soft_validations_check_controller.rb
  9. 196
      app/controllers/delete_logs_controller.rb
  10. 2
      app/controllers/form_controller.rb
  11. 2
      app/controllers/lettings_logs_controller.rb
  12. 25
      app/controllers/locations_controller.rb
  13. 4
      app/controllers/organisations_controller.rb
  14. 1
      app/controllers/sales_logs_controller.rb
  15. 23
      app/controllers/schemes_controller.rb
  16. 22
      app/controllers/users_controller.rb
  17. 5
      app/frontend/styles/_delete-logs-table.scss
  18. 3
      app/frontend/styles/application.scss
  19. 12
      app/helpers/filters_helper.rb
  20. 11
      app/helpers/locations_helper.rb
  21. 13
      app/helpers/log_list_helper.rb
  22. 2
      app/helpers/question_view_helper.rb
  23. 3
      app/helpers/schemes_helper.rb
  24. 6
      app/helpers/tag_helper.rb
  25. 24
      app/helpers/user_helper.rb
  26. 2
      app/models/form.rb
  27. 1
      app/models/form/lettings/pages/lead_tenant_over_retirement_value_check.rb
  28. 1
      app/models/form/lettings/pages/lead_tenant_under_retirement_value_check.rb
  29. 1
      app/models/form/lettings/pages/person_over_retirement_value_check.rb
  30. 1
      app/models/form/lettings/pages/person_under_retirement_value_check.rb
  31. 17
      app/models/form/lettings/questions/address_line1.rb
  32. 7
      app/models/form/lettings/questions/county.rb
  33. 7
      app/models/form/lettings/questions/postcode_for_full_address.rb
  34. 7
      app/models/form/lettings/questions/town_or_city.rb
  35. 56
      app/models/form/lettings/subsections/household_characteristics.rb
  36. 14
      app/models/form/question.rb
  37. 17
      app/models/form/sales/questions/address_line1.rb
  38. 7
      app/models/form/sales/questions/county.rb
  39. 7
      app/models/form/sales/questions/postcode_for_full_address.rb
  40. 7
      app/models/form/sales/questions/town_or_city.rb
  41. 4
      app/models/forms/bulk_upload_lettings/guidance.rb
  42. 2
      app/models/forms/bulk_upload_lettings/prepare_your_file.rb
  43. 31
      app/models/forms/bulk_upload_lettings_resume/chosen.rb
  44. 21
      app/models/forms/bulk_upload_lettings_resume/confirm.rb
  45. 15
      app/models/forms/bulk_upload_lettings_resume/fix_choice.rb
  46. 31
      app/models/forms/bulk_upload_lettings_soft_validations_check/chosen.rb
  47. 21
      app/models/forms/bulk_upload_lettings_soft_validations_check/confirm.rb
  48. 13
      app/models/forms/bulk_upload_lettings_soft_validations_check/confirm_soft_errors.rb
  49. 4
      app/models/forms/bulk_upload_sales/guidance.rb
  50. 16
      app/models/forms/bulk_upload_sales/prepare_your_file.rb
  51. 31
      app/models/forms/bulk_upload_sales_resume/chosen.rb
  52. 21
      app/models/forms/bulk_upload_sales_resume/confirm.rb
  53. 15
      app/models/forms/bulk_upload_sales_resume/fix_choice.rb
  54. 31
      app/models/forms/bulk_upload_sales_soft_validations_check/chosen.rb
  55. 21
      app/models/forms/bulk_upload_sales_soft_validations_check/confirm.rb
  56. 13
      app/models/forms/bulk_upload_sales_soft_validations_check/confirm_soft_errors.rb
  57. 44
      app/models/forms/delete_logs_form.rb
  58. 4
      app/models/location.rb
  59. 2
      app/models/location_deactivation_period.rb
  60. 17
      app/models/log.rb
  61. 4
      app/models/scheme.rb
  62. 2
      app/models/scheme_deactivation_period.rb
  63. 8
      app/models/validations/setup_validations.rb
  64. 8
      app/models/validations/shared_validations.rb
  65. 2
      app/policies/scheme_policy.rb
  66. 36
      app/policies/user_policy.rb
  67. 10
      app/services/bulk_upload/lettings/validator.rb
  68. 11
      app/services/bulk_upload/lettings/year2022/csv_parser.rb
  69. 16
      app/services/bulk_upload/lettings/year2022/row_parser.rb
  70. 6
      app/services/bulk_upload/lettings/year2023/row_parser.rb
  71. 11
      app/services/bulk_upload/sales/year2022/csv_parser.rb
  72. 16
      app/services/bulk_upload/sales/year2022/row_parser.rb
  73. 4
      app/services/bulk_upload/sales/year2023/row_parser.rb
  74. 8
      app/services/feature_toggle.rb
  75. 5
      app/views/bulk_upload_lettings_logs/forms/prepare_your_file_2023.html.erb
  76. 14
      app/views/bulk_upload_lettings_resume/chosen.html.erb
  77. 14
      app/views/bulk_upload_lettings_soft_validations_check/chosen.html.erb
  78. 3
      app/views/bulk_upload_sales_logs/forms/prepare_your_file_2022.html.erb
  79. 36
      app/views/bulk_upload_sales_logs/forms/prepare_your_file_2023.html.erb
  80. 14
      app/views/bulk_upload_sales_resume/chosen.html.erb
  81. 14
      app/views/bulk_upload_sales_soft_validations_check/chosen.html.erb
  82. 9
      app/views/bulk_upload_shared/guidance.html.erb
  83. 148
      app/views/locations/index.html.erb
  84. 8
      app/views/locations/show.html.erb
  85. 30
      app/views/logs/_delete_logs_table_lettings.html.erb
  86. 30
      app/views/logs/_delete_logs_table_sales.html.erb
  87. 12
      app/views/logs/_log_list.html.erb
  88. 22
      app/views/logs/delete_logs.html.erb
  89. 25
      app/views/logs/delete_logs_confirmation.html.erb
  90. 6
      app/views/logs/index.html.erb
  91. 65
      app/views/logs/update_logs.html.erb
  92. 2
      app/views/organisations/logs.html.erb
  93. 22
      app/views/schemes/_scheme_list.html.erb
  94. 40
      app/views/schemes/show.html.erb
  95. 5
      app/views/users/edit.html.erb
  96. 6
      app/views/users/new.html.erb
  97. 49
      app/views/users/show.html.erb
  98. 9
      config/application.rb
  99. 9
      config/locales/en.yml
  100. 19
      config/routes.rb
  101. Some files were not shown because too many files have changed in this diff Show More

2
app/components/data_protection_confirmation_banner_component.rb

@ -22,7 +22,7 @@ class DataProtectionConfirmationBannerComponent < ViewComponent::Base
def data_protection_officers_text def data_protection_officers_text
if org_or_user_org.data_protection_officers.any? 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
end end

2
app/controllers/auth/passwords_controller.rb

@ -4,7 +4,7 @@ class Auth::PasswordsController < Devise::PasswordsController
def reset_confirmation def reset_confirmation
self.resource = resource_class.new self.resource = resource_class.new
@email = params["email"] @email = params["email"]
if @email.empty? if @email.blank?
resource.errors.add :email, I18n.t("validations.email.blank") resource.errors.add :email, I18n.t("validations.email.blank")
render "devise/passwords/new", status: :unprocessable_entity render "devise/passwords/new", status: :unprocessable_entity
elsif !email_valid?(@email) elsif !email_valid?(@email)

7
app/controllers/bulk_upload_lettings_logs_controller.rb

@ -25,9 +25,10 @@ class BulkUploadLettingsLogsController < ApplicationController
private private
def validate_data_protection_agrement_signed! def validate_data_protection_agrement_signed!
unless @current_user.organisation.data_protection_confirmed? return unless FeatureToggle.new_data_protection_confirmation?
redirect_to lettings_logs_path return if @current_user.organisation.data_protection_confirmed?
end
redirect_to lettings_logs_path
end end
def current_year def current_year

9
app/controllers/bulk_upload_lettings_resume_controller.rb

@ -1,5 +1,10 @@
class BulkUploadLettingsResumeController < ApplicationController class BulkUploadLettingsResumeController < ApplicationController
before_action :authenticate_user! 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 def start
@bulk_upload = current_user.bulk_uploads.find(params[:id]) @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]) @bulk_upload = current_user.bulk_uploads.find(params[:id])
@soft_errors_only = params[:soft_errors_only] == "true" @soft_errors_only = params[:soft_errors_only] == "true"
return redirect_to form.preflight_redirect unless form.preflight_valid?
render form.view_path render form.view_path
end end
@ -30,6 +37,8 @@ private
@form ||= case params[:page] @form ||= case params[:page]
when "fix-choice" when "fix-choice"
Forms::BulkUploadLettingsResume::FixChoice.new(form_params.merge(bulk_upload: @bulk_upload)) 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" when "confirm"
Forms::BulkUploadLettingsResume::Confirm.new(form_params.merge(bulk_upload: @bulk_upload)) Forms::BulkUploadLettingsResume::Confirm.new(form_params.merge(bulk_upload: @bulk_upload))
else else

9
app/controllers/bulk_upload_lettings_soft_validations_check_controller.rb

@ -2,10 +2,17 @@ class BulkUploadLettingsSoftValidationsCheckController < ApplicationController
include ActionView::Helpers::TextHelper include ActionView::Helpers::TextHelper
before_action :authenticate_user! 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 def show
@bulk_upload = current_user.bulk_uploads.find(params[:id]) @bulk_upload = current_user.bulk_uploads.find(params[:id])
return redirect_to form.preflight_redirect unless form.preflight_valid?
render form.view_path render form.view_path
end end
@ -30,6 +37,8 @@ private
@form ||= case params[:page] @form ||= case params[:page]
when "confirm-soft-errors" when "confirm-soft-errors"
Forms::BulkUploadLettingsSoftValidationsCheck::ConfirmSoftErrors.new(form_params.merge(bulk_upload: @bulk_upload)) 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" when "confirm"
Forms::BulkUploadLettingsSoftValidationsCheck::Confirm.new(form_params.merge(bulk_upload: @bulk_upload)) Forms::BulkUploadLettingsSoftValidationsCheck::Confirm.new(form_params.merge(bulk_upload: @bulk_upload))
else else

7
app/controllers/bulk_upload_sales_logs_controller.rb

@ -25,9 +25,10 @@ class BulkUploadSalesLogsController < ApplicationController
private private
def validate_data_protection_agrement_signed! def validate_data_protection_agrement_signed!
unless @current_user.organisation.data_protection_confirmed? return unless FeatureToggle.new_data_protection_confirmation?
redirect_to sales_logs_path return if @current_user.organisation.data_protection_confirmed?
end
redirect_to sales_logs_path
end end
def current_year def current_year

9
app/controllers/bulk_upload_sales_resume_controller.rb

@ -1,5 +1,10 @@
class BulkUploadSalesResumeController < ApplicationController class BulkUploadSalesResumeController < ApplicationController
before_action :authenticate_user! 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 def start
@bulk_upload = current_user.bulk_uploads.find(params[:id]) @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]) @bulk_upload = current_user.bulk_uploads.find(params[:id])
@soft_errors_only = params[:soft_errors_only] == "true" @soft_errors_only = params[:soft_errors_only] == "true"
return redirect_to form.preflight_redirect unless form.preflight_valid?
render form.view_path render form.view_path
end end
@ -30,6 +37,8 @@ private
@form ||= case params[:page] @form ||= case params[:page]
when "fix-choice" when "fix-choice"
Forms::BulkUploadSalesResume::FixChoice.new(form_params.merge(bulk_upload: @bulk_upload)) 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" when "confirm"
Forms::BulkUploadSalesResume::Confirm.new(form_params.merge(bulk_upload: @bulk_upload)) Forms::BulkUploadSalesResume::Confirm.new(form_params.merge(bulk_upload: @bulk_upload))
else else

9
app/controllers/bulk_upload_sales_soft_validations_check_controller.rb

@ -2,10 +2,17 @@ class BulkUploadSalesSoftValidationsCheckController < ApplicationController
include ActionView::Helpers::TextHelper include ActionView::Helpers::TextHelper
before_action :authenticate_user! 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 def show
@bulk_upload = current_user.bulk_uploads.find(params[:id]) @bulk_upload = current_user.bulk_uploads.find(params[:id])
return redirect_to form.preflight_redirect unless form.preflight_valid?
render form.view_path render form.view_path
end end
@ -30,6 +37,8 @@ private
@form ||= case params[:page] @form ||= case params[:page]
when "confirm-soft-errors" when "confirm-soft-errors"
Forms::BulkUploadSalesSoftValidationsCheck::ConfirmSoftErrors.new(form_params.merge(bulk_upload: @bulk_upload)) 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" when "confirm"
Forms::BulkUploadSalesSoftValidationsCheck::Confirm.new(form_params.merge(bulk_upload: @bulk_upload)) Forms::BulkUploadSalesSoftValidationsCheck::Confirm.new(form_params.merge(bulk_upload: @bulk_upload))
else else

196
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

2
app/controllers/form_controller.rb

@ -46,7 +46,7 @@ class FormController < ApplicationController
end end
def show_page 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_id = URI.parse(request.headers["HTTP_REFERER"]).path.split("/").last.underscore
@interruption_page_referrer_type = referrer_from_query @interruption_page_referrer_type = referrer_from_query
end end

2
app/controllers/lettings_logs_controller.rb

@ -16,7 +16,7 @@ class LettingsLogsController < LogsController
all_logs = current_user.lettings_logs.visible all_logs = current_user.lettings_logs.visible
unpaginated_filtered_logs = filter_manager.filtered_logs(all_logs, search_term, session_filters) 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) @pagy, @logs = pagy(unpaginated_filtered_logs)
@searched = search_term.presence @searched = search_term.presence
@total_count = all_logs.size @total_count = all_logs.size

25
app/controllers/locations_controller.rb

@ -149,7 +149,11 @@ class LocationsController < ApplicationController
def show; end def show; end
def new_deactivation 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? if params[:location_deactivation_period].blank?
render "toggle_active", locals: { action: "deactivate" } render "toggle_active", locals: { action: "deactivate" }
@ -176,16 +180,21 @@ class LocationsController < ApplicationController
end end
def deactivate 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! logs = reset_location_and_scheme_for_logs!
flash[:notice] = deactivate_success_notice flash[:notice] = deactivate_success_notice
logs.group_by(&:created_by).transform_values(&:count).compact.each do |user, count|
LocationOrSchemeDeactivationMailer.send_deactivation_mail(user, logs.group_by(&:created_by).transform_values(&:count).each do |user, count|
count, next unless user
url_for(controller: "lettings_logs", action: "update_logs"),
@location.scheme.service_name, LocationOrSchemeDeactivationMailer.send_deactivation_mail(
@location.postcode).deliver_later user,
count,
url_for(controller: "lettings_logs", action: "update_logs"),
@location.scheme.service_name,
@location.postcode,
).deliver_later
end end
end end
redirect_to scheme_location_path(@scheme, @location) redirect_to scheme_location_path(@scheme, @location)

4
app/controllers/organisations_controller.rb

@ -96,6 +96,7 @@ class OrganisationsController < ApplicationController
format.html do format.html do
@search_term = search_term @search_term = search_term
@pagy, @logs = pagy(unpaginated_filtered_logs) @pagy, @logs = pagy(unpaginated_filtered_logs)
@delete_logs_path = delete_lettings_logs_organisation_path(search: @search_term)
@searched = search_term.presence @searched = search_term.presence
@total_count = organisation_logs.size @total_count = organisation_logs.size
@log_type = :lettings @log_type = :lettings
@ -119,13 +120,14 @@ class OrganisationsController < ApplicationController
end end
def sales_logs 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) unpaginated_filtered_logs = filter_manager.filtered_logs(organisation_logs, search_term, session_filters)
respond_to do |format| respond_to do |format|
format.html do format.html do
@search_term = search_term @search_term = search_term
@pagy, @logs = pagy(unpaginated_filtered_logs) @pagy, @logs = pagy(unpaginated_filtered_logs)
@delete_logs_path = delete_sales_logs_organisation_path(search: @search_term)
@searched = search_term.presence @searched = search_term.presence
@total_count = organisation_logs.size @total_count = organisation_logs.size
@log_type = :sales @log_type = :sales

1
app/controllers/sales_logs_controller.rb

@ -18,6 +18,7 @@ class SalesLogsController < LogsController
all_logs = current_user.sales_logs.visible all_logs = current_user.sales_logs.visible
unpaginated_filtered_logs = filter_manager.filtered_logs(all_logs, search_term, session_filters) 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 @search_term = search_term
@pagy, @logs = pagy(unpaginated_filtered_logs) @pagy, @logs = pagy(unpaginated_filtered_logs)
@searched = search_term.presence @searched = search_term.presence

23
app/controllers/schemes_controller.rb

@ -27,7 +27,11 @@ class SchemesController < ApplicationController
end end
def new_deactivation 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? if params[:scheme_deactivation_period].blank?
render "toggle_active", locals: { action: "deactivate" } render "toggle_active", locals: { action: "deactivate" }
@ -54,15 +58,20 @@ class SchemesController < ApplicationController
end end
def deactivate 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! logs = reset_location_and_scheme_for_logs!
flash[:notice] = deactivate_success_notice flash[:notice] = deactivate_success_notice
logs.group_by(&:created_by).transform_values(&:count).compact.each do |user, count|
LocationOrSchemeDeactivationMailer.send_deactivation_mail(user, logs.group_by(&:created_by).transform_values(&:count).each do |user, count|
count, next unless user
url_for(controller: "lettings_logs", action: "update_logs"),
@scheme.service_name).deliver_later LocationOrSchemeDeactivationMailer.send_deactivation_mail(
user,
count,
url_for(controller: "lettings_logs", action: "update_logs"),
@scheme.service_name,
).deliver_later
end end
end end
redirect_to scheme_details_path(@scheme) redirect_to scheme_details_path(@scheme)

22
app/controllers/users_controller.rb

@ -29,6 +29,12 @@ class UsersController < ApplicationController
end end
end end
def resend_invite
@user.send_confirmation_instructions
flash[:notice] = "Invitation sent to #{@user.email}"
render :show
end
def show; end def show; end
def dpo; end def dpo; end
@ -114,6 +120,14 @@ private
if user_params[:role].present? && !current_user.assignable_roles.key?(user_params[:role].to_sym) if user_params[:role].present? && !current_user.assignable_roles.key?(user_params[:role].to_sym)
@resource.errors.add :role, I18n.t("validations.role.invalid") @resource.errors.add :role, I18n.t("validations.role.invalid")
end 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 end
def format_error_messages def format_error_messages
@ -145,14 +159,14 @@ private
def user_params def user_params
if @user == current_user if @user == current_user
if current_user.data_coordinator? || current_user.support? 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 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 end
elsif current_user.data_coordinator? 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? 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
end end

5
app/frontend/styles/_delete-logs-table.scss

@ -0,0 +1,5 @@
.checkbox-cell {
.govuk-checkboxes__item {
margin-top: -7px;
}
}

3
app/frontend/styles/application.scss

@ -44,6 +44,7 @@ $govuk-breakpoints: (
@import "search"; @import "search";
@import "sub-navigation"; @import "sub-navigation";
@import "errors"; @import "errors";
@import "delete-logs-table";
// App utilities // App utilities
.app-\!-colour-muted { .app-\!-colour-muted {
@ -52,7 +53,7 @@ $govuk-breakpoints: (
} }
.app-\!-colour-red { .app-\!-colour-red {
color: govuk-colour("red"); color: govuk-colour("red") !important;
} }
.app-\!-font-tabular { .app-\!-font-tabular {

12
app/helpers/filters_helper.rb

@ -11,6 +11,18 @@ module FiltersHelper
selected_filters[filter].include?(value.to_s) selected_filters[filter].include?(value.to_s)
end 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 def status_filters
{ {
"not_started" => "Not started", "not_started" => "Not started",

11
app/helpers/locations_helper.rb

@ -24,9 +24,10 @@ module LocationsHelper
end end
def display_location_attributes(location) def display_location_attributes(location)
base_attributes = [ [
{ name: "Postcode", value: location.postcode, attribute: "postcode" }, { name: "Postcode", value: location.postcode, attribute: "postcode" },
{ name: "Location name", value: location.name, attribute: "name" }, { 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: "Local authority", value: formatted_local_authority_timeline(location, "name"), attribute: "local_authority" },
{ name: "Number of units", value: location.units, attribute: "units" }, { name: "Number of units", value: location.units, attribute: "units" },
{ name: "Most common unit", value: location.type_of_unit, attribute: "type_of_unit" }, { 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: "Location code", value: formatted_local_authority_timeline(location, "code"), attribute: "location_code" },
{ name: "Availability", value: location_availability(location), attribute: "availability" }, { 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 end
def display_location_attributes_for_check_answers(location) def display_location_attributes_for_check_answers(location)
@ -74,7 +69,7 @@ module LocationsHelper
end end
def toggle_location_link(location) 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? return govuk_button_link_to "Reactivate this location", scheme_location_new_reactivation_path(location.scheme, location) if location.deactivated?
end end

13
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

2
app/helpers/question_view_helper.rb

@ -7,7 +7,7 @@ module QuestionViewHelper
def legend(question, page_header, conditional) 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), size: label_size(page_header, conditional, question),
tag: label_tag(page_header, conditional), tag: label_tag(page_header, conditional),
} }

3
app/helpers/schemes_helper.rb

@ -3,6 +3,7 @@ module SchemesHelper
[ [
{ name: "Scheme code", value: scheme.id_to_display }, { name: "Scheme code", value: scheme.id_to_display },
{ name: "Name", value: scheme.service_name, edit: true }, { 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: "Confidential information", value: scheme.sensitive, edit: true },
{ name: "Type of scheme", value: scheme.scheme_type }, { name: "Type of scheme", value: scheme.scheme_type },
{ name: "Registered under Care Standards Act 2000", value: scheme.registered_under_care_act }, { name: "Registered under Care Standards Act 2000", value: scheme.registered_under_care_act },
@ -30,7 +31,7 @@ module SchemesHelper
end end
def toggle_scheme_link(scheme) 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? return govuk_button_link_to "Reactivate this scheme", scheme_new_reactivation_path(scheme) if scheme.deactivated?
end end

6
app/helpers/tag_helper.rb

@ -34,4 +34,10 @@ module TagHelper
text: TEXT[status.to_sym], text: TEXT[status.to_sym],
) )
end 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 end

24
app/helpers/user_helper.rb

@ -7,30 +7,6 @@ module UserHelper
current_user == user ? "Are you" : "Is this person" current_user == user ? "Are you" : "Is this person"
end 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) def can_edit_org?(current_user)
current_user.data_coordinator? || current_user.support? current_user.data_coordinator? || current_user.support?
end end

2
app/models/form.rb

@ -124,7 +124,7 @@ class Form
def next_incomplete_section_redirect_path(subsection, log) def next_incomplete_section_redirect_path(subsection, log)
subsection_ids = subsections.map(&:id) 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) return first_question_in_last_subsection(subsection_ids)
end end

1
app/models/form/lettings/pages/lead_tenant_over_retirement_value_check.rb

@ -1,7 +1,6 @@
class Form::Lettings::Pages::LeadTenantOverRetirementValueCheck < ::Form::Page class Form::Lettings::Pages::LeadTenantOverRetirementValueCheck < ::Form::Page
def initialize(id, hsh, subsection) def initialize(id, hsh, subsection)
super super
@id = "lead_tenant_over_retirement_value_check"
@depends_on = [{ "person_1_not_retired_over_soft_max_age?" => true }] @depends_on = [{ "person_1_not_retired_over_soft_max_age?" => true }]
@title_text = { @title_text = {
"translation" => "soft_validations.retirement.max.title", "translation" => "soft_validations.retirement.max.title",

1
app/models/form/lettings/pages/lead_tenant_under_retirement_value_check.rb

@ -1,7 +1,6 @@
class Form::Lettings::Pages::LeadTenantUnderRetirementValueCheck < ::Form::Page class Form::Lettings::Pages::LeadTenantUnderRetirementValueCheck < ::Form::Page
def initialize(id, hsh, subsection) def initialize(id, hsh, subsection)
super super
@id = "lead_tenant_under_retirement_value_check"
@depends_on = [{ "person_1_retired_under_soft_min_age?" => true }] @depends_on = [{ "person_1_retired_under_soft_min_age?" => true }]
@title_text = { @title_text = {
"translation" => "soft_validations.retirement.min.title", "translation" => "soft_validations.retirement.min.title",

1
app/models/form/lettings/pages/person_over_retirement_value_check.rb

@ -1,7 +1,6 @@
class Form::Lettings::Pages::PersonOverRetirementValueCheck < ::Form::Page class Form::Lettings::Pages::PersonOverRetirementValueCheck < ::Form::Page
def initialize(id, hsh, subsection, person_index:) def initialize(id, hsh, subsection, person_index:)
super(id, hsh, subsection) super(id, hsh, subsection)
@id = "person_#{person_index}_over_retirement_value_check"
@depends_on = [{ "person_#{person_index}_not_retired_over_soft_max_age?" => true }] @depends_on = [{ "person_#{person_index}_not_retired_over_soft_max_age?" => true }]
@title_text = { @title_text = {
"translation" => "soft_validations.retirement.max.title", "translation" => "soft_validations.retirement.max.title",

1
app/models/form/lettings/pages/person_under_retirement_value_check.rb

@ -1,7 +1,6 @@
class Form::Lettings::Pages::PersonUnderRetirementValueCheck < ::Form::Page class Form::Lettings::Pages::PersonUnderRetirementValueCheck < ::Form::Page
def initialize(id, hsh, subsection, person_index:) def initialize(id, hsh, subsection, person_index:)
super(id, hsh, subsection) super(id, hsh, subsection)
@id = "person_#{person_index}_under_retirement_value_check"
@depends_on = [{ "person_#{person_index}_retired_under_soft_min_age?" => true }] @depends_on = [{ "person_#{person_index}_retired_under_soft_min_age?" => true }]
@title_text = { @title_text = {
"translation" => "soft_validations.retirement.min.title", "translation" => "soft_validations.retirement.min.title",

17
app/models/form/lettings/questions/address_line1.rb

@ -2,29 +2,20 @@ class Form::Lettings::Questions::AddressLine1 < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "address_line1" @id = "address_line1"
@check_answer_label = "Address"
@header = "Address line 1" @header = "Address line 1"
@error_label = "Address line 1"
@type = "text" @type = "text"
@plain_label = true @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 @disable_clearing_if_not_routed_or_dynamic_answer_options = true
@question_number = 12
@hide_question_number_on_page = true
end end
def answer_label(log, _current_user = nil) def answer_label(log, _current_user = nil)
[ [
log.address_line1, log.address_line1,
log.address_line2, log.address_line2,
log.postcode_full,
log.town_or_city,
log.county,
].select(&:present?).join("\n") ].select(&:present?).join("\n")
end 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 end

7
app/models/form/lettings/questions/county.rb

@ -5,10 +5,9 @@ class Form::Lettings::Questions::County < ::Form::Question
@header = "County (optional)" @header = "County (optional)"
@type = "text" @type = "text"
@plain_label = true @plain_label = true
@check_answer_label = "County"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end @question_number = 12
@hide_question_number_on_page = true
def hidden_in_check_answers?(_log = nil, _current_user = nil)
true
end end
end end

7
app/models/form/lettings/questions/postcode_for_full_address.rb

@ -17,10 +17,9 @@ class Form::Lettings::Questions::PostcodeForFullAddress < ::Form::Question
}, },
} }
@plain_label = true @plain_label = true
@check_answer_label = "Postcode"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end @question_number = 12
@hide_question_number_on_page = true
def hidden_in_check_answers?(_log = nil, _current_user = nil)
true
end end
end end

7
app/models/form/lettings/questions/town_or_city.rb

@ -5,10 +5,9 @@ class Form::Lettings::Questions::TownOrCity < ::Form::Question
@header = "Town or city" @header = "Town or city"
@type = "text" @type = "text"
@plain_label = true @plain_label = true
@check_answer_label = "Town or city"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end @question_number = 12
@hide_question_number_on_page = true
def hidden_in_check_answers?(_log = nil, _current_user = nil)
true
end end
end end

56
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::LeadTenantAge.new(nil, nil, self),
Form::Lettings::Pages::NoFemalesPregnantHouseholdLeadAgeValueCheck.new(nil, nil, self), Form::Lettings::Pages::NoFemalesPregnantHouseholdLeadAgeValueCheck.new(nil, nil, self),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdLeadAgeValueCheck.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::LeadTenantGenderIdentity.new(nil, nil, self),
Form::Lettings::Pages::NoFemalesPregnantHouseholdLeadValueCheck.new(nil, nil, self), Form::Lettings::Pages::NoFemalesPregnantHouseholdLeadValueCheck.new(nil, nil, self),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdLeadValueCheck.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::LeadTenantEthnicGroup.new(nil, nil, self),
Form::Lettings::Pages::LeadTenantEthnicBackgroundArab.new(nil, nil, self), Form::Lettings::Pages::LeadTenantEthnicBackgroundArab.new(nil, nil, self),
Form::Lettings::Pages::LeadTenantEthnicBackgroundAsian.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::LeadTenantEthnicBackgroundWhite.new(nil, nil, self),
Form::Lettings::Pages::LeadTenantNationality.new(nil, nil, self), Form::Lettings::Pages::LeadTenantNationality.new(nil, nil, self),
Form::Lettings::Pages::LeadTenantWorkingSituation.new(nil, nil, self), Form::Lettings::Pages::LeadTenantWorkingSituation.new(nil, nil, self),
Form::Lettings::Pages::LeadTenantUnderRetirementValueCheck.new(nil, nil, self), Form::Lettings::Pages::LeadTenantUnderRetirementValueCheck.new("working_situation_lead_tenant_under_retirement_value_check", nil, self),
Form::Lettings::Pages::LeadTenantOverRetirementValueCheck.new(nil, 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::PersonKnown.new(nil, nil, self, person_index: 2),
Form::Lettings::Pages::PersonRelationshipToLead.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"), 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), person_index: 2),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self, Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self,
person_index: 2), 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::PersonGenderIdentity.new(nil, nil, self, person_index: 2),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.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, Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
person_index: 2), 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::PersonWorkingSituation.new(nil, nil, self, person_index: 2),
Form::Lettings::Pages::PersonUnderRetirementValueCheck.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(nil, 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::PersonKnown.new(nil, nil, self, person_index: 3),
Form::Lettings::Pages::PersonRelationshipToLead.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"), 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), person_index: 3),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self, Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self,
person_index: 3), 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::PersonGenderIdentity.new(nil, nil, self, person_index: 3),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.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, Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
person_index: 3), 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::PersonWorkingSituation.new(nil, nil, self, person_index: 3),
Form::Lettings::Pages::PersonUnderRetirementValueCheck.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(nil, 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::PersonKnown.new(nil, nil, self, person_index: 4),
Form::Lettings::Pages::PersonRelationshipToLead.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"), 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), person_index: 4),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self, Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self,
person_index: 4), 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::PersonGenderIdentity.new(nil, nil, self, person_index: 4),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.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, Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
person_index: 4), 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::PersonWorkingSituation.new(nil, nil, self, person_index: 4),
Form::Lettings::Pages::PersonUnderRetirementValueCheck.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(nil, 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::PersonKnown.new(nil, nil, self, person_index: 5),
Form::Lettings::Pages::PersonRelationshipToLead.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"), 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), person_index: 5),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self, Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self,
person_index: 5), 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::PersonGenderIdentity.new(nil, nil, self, person_index: 5),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.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, Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
person_index: 5), 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::PersonWorkingSituation.new(nil, nil, self, person_index: 5),
Form::Lettings::Pages::PersonUnderRetirementValueCheck.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(nil, 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::PersonKnown.new(nil, nil, self, person_index: 6),
Form::Lettings::Pages::PersonRelationshipToLead.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"), 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), person_index: 6),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self, Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self,
person_index: 6), 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::PersonGenderIdentity.new(nil, nil, self, person_index: 6),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.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, Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
person_index: 6), 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::PersonWorkingSituation.new(nil, nil, self, person_index: 6),
Form::Lettings::Pages::PersonUnderRetirementValueCheck.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(nil, 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::PersonKnown.new(nil, nil, self, person_index: 7),
Form::Lettings::Pages::PersonRelationshipToLead.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"), 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), person_index: 7),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self, Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self,
person_index: 7), 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::PersonGenderIdentity.new(nil, nil, self, person_index: 7),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.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, Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
person_index: 7), 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::PersonWorkingSituation.new(nil, nil, self, person_index: 7),
Form::Lettings::Pages::PersonUnderRetirementValueCheck.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(nil, 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::PersonKnown.new(nil, nil, self, person_index: 8),
Form::Lettings::Pages::PersonRelationshipToLead.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"), 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), person_index: 8),
Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self, Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonAgeValueCheck.new(nil, nil, self,
person_index: 8), 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::PersonGenderIdentity.new(nil, nil, self, person_index: 8),
Form::Lettings::Pages::NoFemalesPregnantHouseholdPersonValueCheck.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, Form::Lettings::Pages::FemalesInSoftAgeRangeInPregnantHouseholdPersonValueCheck.new(nil, nil, self,
person_index: 8), 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::PersonWorkingSituation.new(nil, nil, self, person_index: 8),
Form::Lettings::Pages::PersonUnderRetirementValueCheck.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(nil, nil, self, person_index: 8), Form::Lettings::Pages::PersonOverRetirementValueCheck.new("working_situation_8_over_retirement_value_check", nil, self, person_index: 8),
].compact ].compact
end end
end end

14
app/models/form/question.rb

@ -4,7 +4,7 @@ class Form::Question
:conditional_for, :readonly, :answer_options, :page, :check_answer_label, :conditional_for, :readonly, :answer_options, :page, :check_answer_label,
:inferred_answers, :hidden_in_check_answers, :inferred_check_answers_value, :inferred_answers, :hidden_in_check_answers, :inferred_check_answers_value,
:guidance_partial, :prefix, :suffix, :requires_js, :fields_added, :derived, :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 module GuidancePosition
TOP = 1 TOP = 1
@ -41,7 +41,9 @@ class Form::Question
@check_answers_card_number = hsh["check_answers_card_number"] || 0 @check_answers_card_number = hsh["check_answers_card_number"] || 0
@unresolved_hint_text = hsh["unresolved_hint_text"] @unresolved_hint_text = hsh["unresolved_hint_text"]
@question_number = hsh["question_number"] @question_number = hsh["question_number"]
@hide_question_number_on_page = hsh["hide_question_number_on_page"] || false
@plain_label = hsh["plain_label"] @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"] @disable_clearing_if_not_routed_or_dynamic_answer_options = hsh["disable_clearing_if_not_routed_or_dynamic_answer_options"]
end end
end end
@ -194,15 +196,15 @@ class Form::Question
type == "radio" && RADIO_REFUSED_VALUE[id.to_sym]&.include?(value) type == "radio" && RADIO_REFUSED_VALUE[id.to_sym]&.include?(value)
end end
def display_label def error_display_label
check_answer_label || header || id.humanize error_label || check_answer_label || header || id.humanize
end end
def unanswered_error_message def unanswered_error_message
return I18n.t("validations.declaration.missing") if id == "declaration" return I18n.t("validations.declaration.missing") if id == "declaration"
return I18n.t("validations.privacynotice.missing") if id == "privacynotice" 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 end
def suffix_label(log) def suffix_label(log)
@ -241,8 +243,8 @@ class Form::Question
selected_answer_option_is_derived?(log) || has_inferred_check_answers_value?(log) selected_answer_option_is_derived?(log) || has_inferred_check_answers_value?(log)
end end
def question_number_string(conditional: false) def question_number_string(hidden: false)
if @question_number && !conditional && form.start_date.year >= 2023 if @question_number && !hidden && form.start_date.year >= 2023
"Q#{@question_number}" "Q#{@question_number}"
end end
end end

17
app/models/form/sales/questions/address_line1.rb

@ -2,29 +2,20 @@ class Form::Sales::Questions::AddressLine1 < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "address_line1" @id = "address_line1"
@check_answer_label = "Address"
@header = "Address line 1" @header = "Address line 1"
@error_label = "Address line 1"
@type = "text" @type = "text"
@plain_label = true @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 @disable_clearing_if_not_routed_or_dynamic_answer_options = true
@question_number = 15
@hide_question_number_on_page = true
end end
def answer_label(log, _current_user = nil) def answer_label(log, _current_user = nil)
[ [
log.address_line1, log.address_line1,
log.address_line2, log.address_line2,
log.postcode_full,
log.town_or_city,
log.county,
].select(&:present?).join("\n") ].select(&:present?).join("\n")
end 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 end

7
app/models/form/sales/questions/county.rb

@ -5,10 +5,9 @@ class Form::Sales::Questions::County < ::Form::Question
@header = "County (optional)" @header = "County (optional)"
@type = "text" @type = "text"
@plain_label = true @plain_label = true
@check_answer_label = "County"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end @question_number = 15
@hide_question_number_on_page = true
def hidden_in_check_answers?(_log = nil, _current_user = nil)
true
end end
end end

7
app/models/form/sales/questions/postcode_for_full_address.rb

@ -17,10 +17,9 @@ class Form::Sales::Questions::PostcodeForFullAddress < ::Form::Question
}, },
} }
@plain_label = true @plain_label = true
@check_answer_label = "Postcode"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end @question_number = 15
@hide_question_number_on_page = true
def hidden_in_check_answers?(_log = nil, _current_user = nil)
true
end end
end end

7
app/models/form/sales/questions/town_or_city.rb

@ -5,10 +5,9 @@ class Form::Sales::Questions::TownOrCity < ::Form::Question
@header = "Town or city" @header = "Town or city"
@type = "text" @type = "text"
@plain_label = true @plain_label = true
@check_answer_label = "Town or city"
@disable_clearing_if_not_routed_or_dynamic_answer_options = true @disable_clearing_if_not_routed_or_dynamic_answer_options = true
end @question_number = 15
@hide_question_number_on_page = true
def hidden_in_check_answers?(_log = nil, _current_user = nil)
true
end end
end end

4
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: }) bulk_upload_lettings_log_path(id: "prepare-your-file", form: { year: })
end end
def old_template_path def legacy_template_path
Forms::BulkUploadLettings::PrepareYourFile.new.old_template_path Forms::BulkUploadLettings::PrepareYourFile.new.legacy_template_path
end end
def template_path def template_path

2
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: }) bulk_upload_lettings_log_path(id: page_id, form: { year:, needstype: })
end end
def old_template_path def legacy_template_path
case year case year
when 2022 when 2022
"/files/bulk-upload-lettings-template-2022-23.xlsx" "/files/bulk-upload-lettings-template-2022-23.xlsx"

31
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

21
app/models/forms/bulk_upload_lettings_resume/confirm.rb

@ -20,11 +20,28 @@ module Forms
end end
def save! def save!
processor = BulkUpload::Processor.new(bulk_upload:) ApplicationRecord.transaction do
processor.approve processor = BulkUpload::Processor.new(bulk_upload:)
processor.approve
bulk_upload.update!(choice: "create-fix-inline")
end
true true
end 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 end
end end

15
app/models/forms/bulk_upload_lettings_resume/fix_choice.rb

@ -46,8 +46,23 @@ module Forms
end end
def save! def save!
bulk_upload.update!(choice:) if choice == "upload-again"
true true
end 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 end
end end

31
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

21
app/models/forms/bulk_upload_lettings_soft_validations_check/confirm.rb

@ -20,11 +20,28 @@ module Forms
end end
def save! def save!
processor = BulkUpload::Processor.new(bulk_upload:) ApplicationRecord.transaction do
processor.approve_and_confirm_soft_validations processor = BulkUpload::Processor.new(bulk_upload:)
processor.approve_and_confirm_soft_validations
bulk_upload.update!(choice: "bulk-confirm-soft-validations")
end
true true
end 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 end
end end

13
app/models/forms/bulk_upload_lettings_soft_validations_check/confirm_soft_errors.rb

@ -35,6 +35,19 @@ module Forms
def save! def save!
true true
end 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 end
end end

4
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: }) bulk_upload_sales_log_path(id: "prepare-your-file", form: { year: })
end end
def old_template_path def legacy_template_path
Forms::BulkUploadLettings::PrepareYourFile.new.old_template_path Forms::BulkUploadSales::PrepareYourFile.new.legacy_template_path
end end
def template_path def template_path

16
app/models/forms/bulk_upload_sales/prepare_your_file.rb

@ -8,7 +8,12 @@ module Forms
attribute :year, :integer attribute :year, :integer
def view_path 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 end
def back_path def back_path
@ -23,8 +28,13 @@ module Forms
bulk_upload_sales_log_path(id: "upload-your-file", form: { year: }) bulk_upload_sales_log_path(id: "upload-your-file", form: { year: })
end end
def old_template_path def legacy_template_path
"/files/bulk-upload-sales-template-2022-23.xlsx" case year
when 2022
"/files/bulk-upload-sales-template-2022-23.xlsx"
else
"/files/bulk-upload-sales-legacy-template-2023-24.xlsx"
end
end end
def template_path def template_path

31
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

21
app/models/forms/bulk_upload_sales_resume/confirm.rb

@ -20,11 +20,28 @@ module Forms
end end
def save! def save!
processor = BulkUpload::Processor.new(bulk_upload:) ApplicationRecord.transaction do
processor.approve processor = BulkUpload::Processor.new(bulk_upload:)
processor.approve
bulk_upload.update!(choice: "create-fix-inline")
end
true true
end 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 end
end end

15
app/models/forms/bulk_upload_sales_resume/fix_choice.rb

@ -46,8 +46,23 @@ module Forms
end end
def save! def save!
bulk_upload.update!(choice:) if choice == "upload-again"
true true
end 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 end
end end

31
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

21
app/models/forms/bulk_upload_sales_soft_validations_check/confirm.rb

@ -20,11 +20,28 @@ module Forms
end end
def save! def save!
processor = BulkUpload::Processor.new(bulk_upload:) ApplicationRecord.transaction do
processor.approve_and_confirm_soft_validations processor = BulkUpload::Processor.new(bulk_upload:)
processor.approve_and_confirm_soft_validations
bulk_upload.update!(choice: "bulk-confirm-soft-validations")
end
true true
end 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 end
end end

13
app/models/forms/bulk_upload_sales_soft_validations_check/confirm_soft_errors.rb

@ -35,6 +35,19 @@ module Forms
def save! def save!
true true
end 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 end
end end

44
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

4
app/models/location.rb

@ -108,6 +108,10 @@ class Location < ApplicationRecord
status == :reactivating_soon status == :reactivating_soon
end end
def deactivates_in_a_long_time?
status_at(6.months.from_now) == :deactivating_soon
end
def validate_postcode def validate_postcode
if !postcode&.match(POSTCODE_REGEXP) if !postcode&.match(POSTCODE_REGEXP)
error_message = I18n.t("validations.postcode") error_message = I18n.t("validations.postcode")

2
app/models/location_deactivation_period.rb

@ -4,7 +4,7 @@ class LocationDeactivationPeriodValidator < ActiveModel::Validator
def validate(record) def validate(record)
location = record.location location = record.location
recent_deactivation = location.location_deactivation_periods.deactivations_without_reactivation.first 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) validate_reactivation(record, recent_deactivation, location)
else else
validate_deactivation(record, location) validate_deactivation(record, location)

17
app/models/log.rb

@ -9,7 +9,6 @@ class Log < ApplicationRecord
belongs_to :bulk_upload, optional: true belongs_to :bulk_upload, optional: true
before_save :update_status! before_save :update_status!
before_validation :verify_data_protection_confirmation, on: :create
STATUS = { STATUS = {
"not_started" => 0, "not_started" => 0,
@ -139,9 +138,9 @@ class Log < ApplicationRecord
def calculate_status def calculate_status
return "deleted" if discarded_at.present? return "deleted" if discarded_at.present?
if all_fields_completed? && errors.empty? if all_subsections_completed? && errors.empty?
"completed" "completed"
elsif all_fields_nil? elsif all_subsections_unstarted?
"not_started" "not_started"
else else
"in_progress" "in_progress"
@ -178,14 +177,6 @@ class Log < ApplicationRecord
private 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 # Handle logs that are older than previous collection start date
def older_than_previous_collection_year? def older_than_previous_collection_year?
return false unless startdate return false unless startdate
@ -210,11 +201,11 @@ private
self.status = calculate_status self.status = calculate_status
end end
def all_fields_completed? def all_subsections_completed?
form.subsections.all? { |subsection| subsection.complete?(self) || subsection.not_displayed_in_tasklist?(self) } form.subsections.all? { |subsection| subsection.complete?(self) || subsection.not_displayed_in_tasklist?(self) }
end end
def all_fields_nil? def all_subsections_unstarted?
not_started_statuses = %i[not_started cannot_start_yet] not_started_statuses = %i[not_started cannot_start_yet]
form.subsections.all? { |subsection| not_started_statuses.include? subsection.status(self) } form.subsections.all? { |subsection| not_started_statuses.include? subsection.status(self) }
end end

4
app/models/scheme.rb

@ -242,4 +242,8 @@ class Scheme < ApplicationRecord
def deactivated? def deactivated?
status == :deactivated status == :deactivated
end end
def deactivates_in_a_long_time?
status_at(6.months.from_now) == :deactivating_soon
end
end end

2
app/models/scheme_deactivation_period.rb

@ -4,7 +4,7 @@ class SchemeDeactivationPeriodValidator < ActiveModel::Validator
def validate(record) def validate(record)
scheme = record.scheme scheme = record.scheme
recent_deactivation = scheme.scheme_deactivation_periods.deactivations_without_reactivation.first 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) validate_reactivation(record, recent_deactivation, scheme)
else else
validate_deactivation(record, scheme) validate_deactivation(record, scheme)

8
app/models/validations/setup_validations.rb

@ -42,6 +42,14 @@ module Validations::SetupValidations
end end
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 private
def active_collection_start_date def active_collection_start_date

8
app/models/validations/shared_validations.rb

@ -117,6 +117,14 @@ module Validations::SharedValidations
end end
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 private
def person_is_partner?(relationship) def person_is_partner?(relationship)

2
app/policies/scheme_policy.rb

@ -47,7 +47,9 @@ class SchemePolicy
confirm_secondary_client_group? confirm_secondary_client_group?
secondary_client_group? secondary_client_group?
new_deactivation? new_deactivation?
new_reactivation?
deactivate? deactivate?
reactivate?
details? details?
support? support?
deactivate_confirm? deactivate_confirm?

36
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

10
app/services/bulk_upload/lettings/validator.rb

@ -150,13 +150,19 @@ private
def validate_field_numbers_count def validate_field_numbers_count
return if halt_validations? 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 end
def validate_max_columns_count_if_no_headers def validate_max_columns_count_if_no_headers
return if halt_validations? 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 end
def validate_correct_template def validate_correct_template

11
app/services/bulk_upload/lettings/year2022/csv_parser.rb

@ -3,6 +3,7 @@ require "csv"
class BulkUpload::Lettings::Year2022::CsvParser class BulkUpload::Lettings::Year2022::CsvParser
FIELDS = 134 FIELDS = 134
MAX_COLUMNS = 135 MAX_COLUMNS = 135
FORM_YEAR = 2022
attr_reader :path attr_reader :path
@ -62,11 +63,19 @@ class BulkUpload::Lettings::Year2022::CsvParser
end end
def wrong_template_for_year? def wrong_template_for_year?
false !(first_record_start_date >= form.start_date && first_record_start_date <= form.end_date)
end end
private 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 def default_field_numbers
("field_1".."field_#{FIELDS}").to_a ("field_1".."field_#{FIELDS}").to_a
end end

16
app/services/bulk_upload/lettings/year2022/row_parser.rb

@ -461,6 +461,12 @@ class BulkUpload::Lettings::Year2022::RowParser
end end
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 private
def validate_declaration_acceptance def validate_declaration_acceptance
@ -774,13 +780,13 @@ private
if setup_question?(question) if setup_question?(question)
fields.each do |field| fields.each do |field|
if errors.select { |e| fields.include?(e.attribute) }.none? 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
end end
else else
fields.each do |field| fields.each do |field|
unless errors.any? { |e| fields.include?(e.attribute) } 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 end
end end
@ -983,12 +989,6 @@ private
} }
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
def renttype def renttype
case field_1 case field_1
when 1, 2, 3, 4 when 1, 2, 3, 4

6
app/services/bulk_upload/lettings/year2023/row_parser.rb

@ -681,14 +681,14 @@ private
if setup_question?(question) if setup_question?(question)
fields.each do |field| fields.each do |field|
if errors.select { |e| fields.include?(e.attribute) }.none? 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) errors.add(field, I18n.t("validations.not_answered", question: question_text.downcase), category: :setup)
end end
end end
else else
fields.each do |field| fields.each do |field|
unless errors.any? { |e| fields.include?(e.attribute) } 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)) errors.add(field, I18n.t("validations.not_answered", question: question_text.downcase))
end end
end end
@ -1442,8 +1442,6 @@ private
when 2 when 2
1 1
when 3 when 3
1
when 4
2 2
end end
end end

11
app/services/bulk_upload/sales/year2022/csv_parser.rb

@ -2,6 +2,7 @@ require "csv"
class BulkUpload::Sales::Year2022::CsvParser class BulkUpload::Sales::Year2022::CsvParser
MAX_COLUMNS = 126 MAX_COLUMNS = 126
FORM_YEAR = 2022
attr_reader :path attr_reader :path
@ -44,11 +45,19 @@ class BulkUpload::Sales::Year2022::CsvParser
end end
def wrong_template_for_year? def wrong_template_for_year?
false !(first_record_sale_date >= form.start_date && first_record_sale_date <= form.end_date)
end end
private 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 def headers
@headers ||= ("field_1".."field_125").to_a @headers ||= ("field_1".."field_125").to_a
end end

16
app/services/bulk_upload/sales/year2022/row_parser.rb

@ -453,6 +453,12 @@ class BulkUpload::Sales::Year2022::RowParser
end end
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 private
def validate_data_protection_answered def validate_data_protection_answered
@ -762,12 +768,6 @@ private
end end
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 def hodate
Date.new(field_61 + 2000, field_60, field_59) if field_59.present? && field_60.present? && field_61.present? Date.new(field_61 + 2000, field_60, field_59) if field_59.present? && field_60.present? && field_61.present?
rescue Date::Error rescue Date::Error
@ -1057,9 +1057,9 @@ private
fields.each do |field| fields.each do |field|
unless errors.any? { |e| fields.include?(e.attribute) } unless errors.any? { |e| fields.include?(e.attribute) }
if setup_question?(question) 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 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 end
end end

4
app/services/bulk_upload/sales/year2023/row_parser.rb

@ -1210,13 +1210,13 @@ private
if setup_question?(question) if setup_question?(question)
fields.each do |field| fields.each do |field|
unless errors.any? { |e| fields.include?(e.attribute) } 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
end end
else else
fields.each do |field| fields.each do |field|
unless errors.any? { |e| fields.include?(e.attribute) } 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 end
end end

8
app/services/feature_toggle.rb

@ -12,14 +12,6 @@ class FeatureToggle
Rails.env.production? || Rails.env.test? || Rails.env.staging? || Rails.env.review? Rails.env.production? || Rails.env.test? || Rails.env.staging? || Rails.env.review?
end end
def self.scheme_toggle_enabled?
true
end
def self.location_toggle_enabled?
true
end
def self.bulk_upload_duplicate_log_check_enabled? def self.bulk_upload_duplicate_log_check_enabled?
!Rails.env.staging? !Rails.env.staging?
end end

5
app/views/bulk_upload_lettings_logs/forms/prepare_your_file_2023.html.erb

@ -12,13 +12,14 @@
<h2 class="govuk-heading-s">Download template</h2> <h2 class="govuk-heading-s">Download template</h2>
<p class="govuk-body govuk-!-margin-bottom-2">Use one of these templates to upload logs for 2023/24:</p>
<ul class="govuk-list govuk-list--bullet"> <ul class="govuk-list govuk-list--bullet">
<li> <li>
If your organisation is new to using bulk upload or you have updated your HMS export, use <%= govuk_link_to "this template (improved question ordering)", @form.template_path %>. <%= govuk_link_to "New template", @form.template_path %>: In this template, the questions are in the same order as the 2023/24 paper form and web form.
</li> </li>
<li> <li>
If your organisation was using bulk upload on the previous CORE website and hasn't changed its HMS to be compatible with the new CORE service, use <%= govuk_link_to "this template", @form.old_template_path %>. <%= govuk_link_to "Legacy template", @form.legacy_template_path %>: In this template, the questions are in the same order as the 2022/23 template, with new questions added on to the end.
</li> </li>
</ul> </ul>

14
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 %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<span class="govuk-caption-l">Bulk upload for lettings (<%= @bulk_upload.year_combo %>)</span>
<h1 class="govuk-heading-l">You need to fix logs from your bulk upload</h1>
<p class="govuk-body">You have chosen to create logs from your recent bulk upload. To view and complete these logs, return to the list of lettings logs.</p>
<%= govuk_button_link_to "Return to lettings logs", lettings_logs_path %>
</div>
</div>

14
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 %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<span class="govuk-caption-l">Bulk upload for lettings (<%= @bulk_upload.year_combo %>)</span>
<h1 class="govuk-heading-l">These logs have been created</h1>
<p class="govuk-body">You have created logs from your bulk upload. Return to lettings logs to view them.</p>
<%= govuk_button_link_to "Return to lettings logs", lettings_logs_path %>
</div>
</div>

3
app/views/bulk_upload_sales_logs/forms/prepare_your_file.html.erb → app/views/bulk_upload_sales_logs/forms/prepare_your_file_2022.html.erb

@ -12,8 +12,7 @@
<h2 class="govuk-heading-s">Download template</h2> <h2 class="govuk-heading-s">Download template</h2>
<ul class="govuk-list govuk-list--bullet"> <ul class="govuk-list govuk-list--bullet">
<li>If your organisation is new to using bulk upload or you have updated your HMS export use <%= govuk_link_to "this template (improved question ordering)", @form.template_path %></li> <li>Download and use <%= govuk_link_to "this template", @form.legacy_template_path %>.</li>
<li>If your organisation was using bulk upload on the previous CORE website and hasn't changed its HMS to be compatible with the new CORE service, use <%= govuk_link_to "this template", @form.old_template_path %></li>
</ul> </ul>
<h2 class="govuk-heading-s">Create your file</h2> <h2 class="govuk-heading-s">Create your file</h2>

36
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 %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= form_with model: @form, scope: :form, url: bulk_upload_sales_log_path(id: "prepare-your-file"), method: :patch do |f| %>
<%= f.hidden_field :year %>
<span class="govuk-caption-l">Upload sales logs in bulk (<%= @form.year_combo %>)</span>
<h1 class="govuk-heading-l">Prepare your file</h1>
<h2 class="govuk-heading-s">Download template</h2>
<ul class="govuk-list govuk-list--bullet">
<li><%= 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.</li>
<li><%= 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.</li>
</ul>
<h2 class="govuk-heading-s">Create your file</h2>
<ul class="govuk-list govuk-list--bullet">
<li>Fill in the template with CORE data from your housing management system according to the <%= govuk_link_to "Sales #{@form.year_combo} Bulk Upload Specification", @form.specification_path %></li>
<li><strong>Username field:</strong> To assign a log to someone else, enter the email address they use to log into CORE</li>
<li>If you have to manually enter large volumes of data into the bulk upload template, we recommend creating logs directly in the service instead. <%= govuk_link_to "Find out more about exporting your data", bulk_upload_sales_log_path(id: "guidance", form: { year: @form.year }) %></li>
<li>If you are using the new template, keep the headers. If you are using the legacy template, you can either keep or remove the headers.</li>
</ul>
<h2 class="govuk-heading-s">Save your file</h2>
<ul class="govuk-list govuk-list--bullet">
<li>Save your file as a CSV</li>
<li>Your file should now be ready to upload</li>
</ul>
<%= f.govuk_submit %>
<% end %>
</div>
</div>

14
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 %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<span class="govuk-caption-l">Bulk upload for sales (<%= @bulk_upload.year_combo %>)</span>
<h1 class="govuk-heading-l">You need to fix logs from your bulk upload</h1>
<p class="govuk-body">You have chosen to create logs from your recent bulk upload. To view and complete these logs, return to the list of sales logs.</p>
<%= govuk_button_link_to "Return to sales logs", sales_logs_path %>
</div>
</div>

14
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 %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<span class="govuk-caption-l">Bulk upload for sales (<%= @bulk_upload.year_combo %>)</span>
<h1 class="govuk-heading-l">These logs have been created</h1>
<p class="govuk-body">You have created logs from your bulk upload. Return to sales logs to view them.</p>
<%= govuk_button_link_to "Return to sales logs", sales_logs_path %>
</div>
</div>

9
app/views/bulk_upload_shared/guidance.html.erb

@ -54,7 +54,14 @@
<div class="govuk-!-padding-bottom-4"> <div class="govuk-!-padding-bottom-4">
<h2 class="govuk-heading-s">Getting help</h2> <h2 class="govuk-heading-s">Getting help</h2>
<p class="govuk-body">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.</p> <p class="govuk-body">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.</p>
<% if @form.year == 2023 %>
<%= govuk_details(summary_text: "How to choose the right template") do %>
<p class="govuk-body">Use one of these templates to upload logs for 2023/24:</p>
<p class="govuk-body"><%= 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.</p>
<p class="govuk-body"><%= 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.</p>
<% end %>
<% end %>
<p class="govuk-body">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.</p> <p class="govuk-body">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.</p>
</div> </div>
</div> </div>

148
app/views/locations/index.html.erb

@ -10,120 +10,60 @@
<%= render partial: "organisations/headings", locals: { main: @scheme.service_name, sub: nil } %> <%= render partial: "organisations/headings", locals: { main: @scheme.service_name, sub: nil } %>
<% if FeatureToggle.location_toggle_enabled? %> <div class="govuk-grid-row">
<div class="govuk-grid-row"> <div class="govuk-grid-column-two-thirds-from-desktop">
<div class="govuk-grid-column-two-thirds-from-desktop"> <%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %>
<% end %>
<%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %>
<h2 class="govuk-visually-hidden">Locations</h2> <h2 class="govuk-visually-hidden">Locations</h2>
<%= 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") %> <%= govuk_section_break(visible: true, size: "m") %>
<% if FeatureToggle.location_toggle_enabled? %>
</div>
</div> </div>
<% end %> </div>
<% if FeatureToggle.location_toggle_enabled? %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<%= 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? %> <div class="govuk-grid-row">
<%= govuk_button_to "Add a location", scheme_locations_path(@scheme), method: "post", secondary: true %> <div class="govuk-grid-column-two-thirds-from-desktop">
<%= 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 %> <% end %>
</div> <%= table.head do |head| %>
</div> <%= head.row do |row| %>
<% else %> <% row.cell(header: true, text: "Postcode", html_attributes: {
<%= govuk_table do |table| %> scope: "col",
<%= 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)) %> <% row.cell(header: true, text: "Name", html_attributes: {
<% end %> scope: "col",
<%= table.head do |head| %> }) %>
<%= head.row do |row| %> <% row.cell(header: true, text: "Location code", html_attributes: {
<% row.cell(header: true, text: "Code", html_attributes: { scope: "col",
scope: "col", }) %>
}) %> <% row.cell(header: true, text: "Status", html_attributes: {
<% row.cell(header: true, text: "Postcode", html_attributes: { scope: "col",
scope: "col", }) %>
}) %> <% end %>
<% 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",
}) %>
<% end %> <% end %>
<% end %> <% @locations.each do |location| %>
<% @locations.each do |location| %> <%= table.body do |body| %>
<%= table.body do |body| %> <%= body.row do |row| %>
<%= body.row do |row| %> <% row.cell(text: simple_format(location_cell_postcode(location, if location.confirmed
<% row.cell(text: location.id) %> scheme_location_path(@scheme, location)
<% row.cell(text: simple_format(location_cell_postcode(location, if location.confirmed else
scheme_location_path(@scheme, location) location.postcode.present? ? scheme_location_check_answers_path(@scheme, location, route: "locations") : scheme_location_postcode_path(@scheme, location)
else end), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
location.postcode.present? ? scheme_location_check_answers_path(@scheme, location, route: "locations") : scheme_location_postcode_path(@scheme, location) <% row.cell(text: location.name) %>
end), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %> <% row.cell(text: location.id) %>
<% row.cell(text: location.units) %> <% row.cell(text: status_tag_from_resource(location)) %>
<% row.cell do %>
<span><%= simple_format(location.type_of_unit) %></span>
<% end %>
<% row.cell(text: location.mobility_type) %>
<% row.cell(text: location.location_admin_district) %>
<% row.cell(text: location.startdate&.to_formatted_s(:govuk_date)) %>
<% end %> <% end %>
<% end %>
<% 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 %>
</div>
</div>
<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "locations" } %> <%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "locations" } %>

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

@ -15,7 +15,7 @@
<% display_location_attributes(@location).each do |attr| %> <% display_location_attributes(@location).each do |attr| %>
<%= summary_list.row do |row| %> <%= summary_list.row do |row| %>
<% row.key { attr[:name] } %> <% 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? %> <% if LocationPolicy.new(current_user, @location).update? %>
<% row.action(text: "Change", href: scheme_location_name_path(@scheme, @location, referrer: "details")) if attr[:attribute] == "name" %> <% row.action(text: "Change", href: scheme_location_name_path(@scheme, @location, referrer: "details")) if attr[:attribute] == "name" %>
<% end %> <% end %>
@ -25,8 +25,6 @@
</div> </div>
</div> </div>
<% if FeatureToggle.location_toggle_enabled? %> <% if LocationPolicy.new(current_user, @location).deactivate? %>
<% if LocationPolicy.new(current_user, @location).deactivate? %> <%= toggle_location_link(@location) %>
<%= toggle_location_link(@location) %>
<% end %>
<% end %> <% end %>

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

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

12
app/views/logs/_log_list.html.erb

@ -1,11 +1,21 @@
<h2 class="govuk-body"> <h2 class="govuk-body">
<div class="govuk-grid-row">
<div class="govuk-grid-column-three-quarters">
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "logs", path: request.path)) %> <%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "logs", path: request.path)) %>
<% if logs&.any? %> <% if logs&.any? %>
<%= govuk_link_to "Download (CSV)", csv_download_url, type: "text/csv", class: "govuk-!-margin-right-4" %> <%= govuk_link_to "Download (CSV)", csv_download_url, type: "text/csv", class: "govuk-!-margin-right-4" %>
<% if @current_user.support? %> <% 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 %>
<% end %> <% end %>
</div>
<div class="govuk-grid-column-one-quarter govuk-!-text-align-right">
<% 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 %>
</div>
</div>
</h2> </h2>
<% logs.map do |log| %> <% logs.map do |log| %>
<%= render(LogSummaryComponent.new(current_user:, log:)) %> <%= render(LogSummaryComponent.new(current_user:, log:)) %>

22
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 %>
<h1 class="govuk-heading-l">
<span class="govuk-caption-l"><%= title %></span>
Review the logs you want to delete
</h1>
<p>You've selected <%= @delete_logs_form.log_count %> <%= "log".pluralize(@delete_logs_form.log_count) %> to delete</p>
<div class="govuk-checkboxes govuk-checkboxes--small">
<%= 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 %>
</div>

25
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 %>
<h1 class="govuk-heading-l">
<span class="govuk-caption-l"><%= title %></span>
Are you sure you want to delete these logs?
</h1>
<% log_count = @delete_logs_form.selected_ids.count %>
<p>You've selected <%= log_count %> <%= "log".pluralize(log_count) %> to delete</p>
<%= govuk_warning_text(icon_fallback_text: "Danger") do %>
You will not be able to undo this action
<% end %>
<div class="govuk-button-group">
<%= 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 %>
</div>

6
app/views/logs/index.html.erb

@ -68,8 +68,10 @@
searched: @searched, searched: @searched,
item_label:, item_label:,
total_count: @total_count, total_count: @total_count,
csv_download_url: csv_download_url_for_controller(controller:, search: @search_term, codes_only: false), 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: @search_term, codes_only: true), 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" } %> <%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %>
</div> </div>

65
app/views/logs/update_logs.html.erb

@ -4,43 +4,42 @@
<% content_for :title, title %> <% content_for :title, title %>
<% if @total_count < 1 %> <% if @total_count < 1 %>
<%= render partial: "organisations/headings", locals: { main: "There are no more logs that need updating", sub: "" } %> <%= render partial: "organisations/headings", locals: { main: "There are no more logs that need updating", sub: "" } %>
<p class="govuk-body"> <p class="govuk-body">
You’ve completed all the logs that were affected by scheme changes. You’ve completed all the logs that were affected by scheme changes.
</p> </p>
<div> <div>
<%= govuk_button_link_to "Back to all logs", lettings_logs_path %> <%= govuk_button_link_to "Back to all logs", lettings_logs_path %>
</div> </div>
<% else %> <% else %>
<%= render partial: "organisations/headings", locals: { main: "You need to update #{@total_count} logs", sub: "" } %> <%= render partial: "organisations/headings", locals: { main: "You need to update #{@total_count} logs", sub: "" } %>
<%= govuk_table do |table| %>
<%= govuk_table do |table| %> <% table.head do |head| %>
<% table.head do |head| %> <% head.row do |row| %>
<% head.row do |row| %> <% row.cell(header: true, text: "Log ID") %>
<% row.cell(header: true, text: "Log ID") %> <% row.cell(header: true, text: "Tenancy code") %>
<% row.cell(header: true, text: "Tenancy code") %> <% row.cell(header: true, text: "Property reference") %>
<% row.cell(header: true, text: "Property reference") %> <% row.cell(header: true, text: "Status") %>
<% row.cell(header: true, text: "Status") %> <% row.cell(header: true, text: "") %>
<% row.cell(header: true, text: "") %> <% end %>
<% end %> <% end %>
<% end %>
<% @logs.each do |log| %> <% @logs.each do |log| %>
<% table.body do |body| %> <% table.body do |body| %>
<% body.row do |row| %> <% body.row do |row| %>
<% row.cell(text: log.id) %> <% row.cell(text: log.id) %>
<% row.cell(text: log.tenancycode) %> <% row.cell(text: log.tenancycode) %>
<% row.cell(text: log.propcode) %> <% row.cell(text: log.propcode) %>
<% row.cell(text: status_tag(log.status)) %> <% row.cell(text: status_tag(log.status)) %>
<% row.cell(html_attributes: { <% row.cell(html_attributes: {
scope: "row", scope: "row",
class: "govuk-!-text-align-right", class: "govuk-!-text-align-right",
}) do %> }) do %>
<%= govuk_link_to("Update now", send(log.form.unresolved_log_path, log)) %> <%= govuk_link_to "Update now", send(log.form.unresolved_log_path, log) %>
<% end %> <% end %>
<% end %>
<% end %> <% end %>
<% end %>
<% end %> <% end %>
<% end %> <% end %>
<% end %> <% end %>
<%= render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %> <%= render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %>

2
app/views/organisations/logs.html.erb

@ -34,6 +34,8 @@
total_count: @total_count, total_count: @total_count,
csv_download_url: csv_download_url_by_log_type(@log_type, @organisation, search: @search_term, codes_only: false), 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), 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" } %> <%= render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %>
</div> </div>

22
app/views/schemes/_scheme_list.html.erb

@ -5,20 +5,10 @@
<% end %> <% end %>
<%= table.head do |head| %> <%= table.head do |head| %>
<%= head.row do |row| %> <%= head.row do |row| %>
<% row.cell(header: true, text: "Scheme", html_attributes: { <% row.cell(header: true, text: "Scheme", html_attributes: { scope: "col" }) %>
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: "Code", html_attributes: { <% row.cell(header: true, text: "Status", html_attributes: { scope: "col" }) %>
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 %>
<% end %> <% end %>
<% end %> <% end %>
<% @schemes.each do |scheme| %> <% @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: simple_format(scheme_cell(scheme), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
<% row.cell(text: scheme.id_to_display) %> <% row.cell(text: scheme.id_to_display) %>
<% row.cell(text: scheme.locations&.count) %> <% row.cell(text: scheme.locations&.count) %>
<% if FeatureToggle.scheme_toggle_enabled? %> <% row.cell(text: status_tag_from_resource(scheme)) %>
<% row.cell(text: status_tag(scheme.status)) %>
<% end %>
<% end %> <% end %>
<% end %> <% end %>
<% end %> <% end %>

40
app/views/schemes/show.html.erb

@ -9,33 +9,31 @@
<%= render partial: "organisations/headings", locals: { main: @scheme.service_name, sub: nil } %> <%= render partial: "organisations/headings", locals: { main: @scheme.service_name, sub: nil } %>
<% if FeatureToggle.location_toggle_enabled? %> <div class="govuk-grid-row">
<div class="govuk-grid-row"> <div class="govuk-grid-column-two-thirds-from-desktop">
<div class="govuk-grid-column-two-thirds-from-desktop"> <%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %>
<% end %>
<%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %>
<h2 class="govuk-visually-hidden">Scheme</h2> <h2 class="govuk-visually-hidden">Scheme</h2>
<%= govuk_summary_list do |summary_list| %> <%= govuk_summary_list do |summary_list| %>
<% display_scheme_attributes(@scheme, current_user).each do |attr| %> <% display_scheme_attributes(@scheme, current_user).each do |attr| %>
<%= summary_list.row do |row| %> <%= summary_list.row do |row| %>
<% row.key { attr[:name] } %> <% row.key { attr[:name] } %>
<% row.value { details_html(attr) } %> <% row.value do %>
<% if SchemePolicy.new(current_user, @scheme).update? %> <%= details_html(attr) %>
<% row.action(text: "Change", href: scheme_edit_name_path(scheme_id: @scheme.id)) if attr[:edit] %> <% if attr[:name] == "Status" && @scheme.confirmed? && @scheme.locations.confirmed.none? %>
<span class="app-!-colour-muted">Add a location to complete this scheme</span>
<% end %> <% end %>
<% 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 %>
<% end %> <% end %>
<% end %>
<% if FeatureToggle.location_toggle_enabled? %>
</div>
</div> </div>
<% end %> </div>
<% if FeatureToggle.scheme_toggle_enabled? %> <% if SchemePolicy.new(current_user, @scheme).deactivate? %>
<% if SchemePolicy.new(current_user, @scheme).deactivate? %> <%= toggle_scheme_link(@scheme) %>
<%= toggle_scheme_link(@scheme) %>
<% end %>
<% end %> <% end %>

5
app/views/users/edit.html.erb

@ -22,6 +22,11 @@
autocomplete: "email", autocomplete: "email",
spellcheck: "false" %> 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? %> <% if current_user.data_coordinator? || current_user.support? %>
<% roles = current_user.assignable_roles.map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize) } %> <% roles = current_user.assignable_roles.map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize) } %>

6
app/views/users/new.html.erb

@ -23,6 +23,12 @@
spellcheck: "false", spellcheck: "false",
value: @resource.email %> 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? %> <% if current_user.support? %>
<% null_option = [OpenStruct.new(id: "", name: "Select an option")] %> <% null_option = [OpenStruct.new(id: "", name: "Select an option")] %>
<% organisations = Organisation.all.map { |org| OpenStruct.new(id: org.id, name: org.name) } %> <% organisations = Organisation.all.map { |org| OpenStruct.new(id: org.id, name: org.name) } %>

49
app/views/users/show.html.erb

@ -5,17 +5,6 @@
<h1 class="govuk-heading-l"> <h1 class="govuk-heading-l">
<%= content_for(:title) %> <%= content_for(:title) %>
</h1> </h1>
<p>
<% if current_user.can_toggle_active?(@user) %>
<% if @user.active? %>
<%= govuk_link_to "Deactivate user", "/users/#{@user.id}/deactivate" %>
<% else %>
<span class="app-!-colour-muted govuk-!-margin-right-2">
This user has been deactivated. <%= govuk_link_to "Reactivate user", "/users/#{@user.id}/reactivate" %>
</span>
<% end %>
<% end %>
</p>
<h2 class="govuk-heading-m"> <h2 class="govuk-heading-m">
Personal details Personal details
@ -24,7 +13,7 @@
<%= summary_list.row do |row| <%= summary_list.row do |row|
row.key { "Name" } row.key { "Name" }
row.value { @user.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" }) row.action(visually_hidden_text: "name", href: aliased_user_edit(@user, current_user), html_attributes: { "data-qa": "change-name" })
else else
row.action row.action
@ -34,17 +23,27 @@
<%= summary_list.row do |row| <%= summary_list.row do |row|
row.key { "Email address" } row.key { "Email address" }
row.value { @user.email } 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" }) row.action(visually_hidden_text: "email address", href: aliased_user_edit(@user, current_user), html_attributes: { "data-qa": "change-email-address" })
else else
row.action row.action
end end
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| <%= summary_list.row do |row|
row.key { "Password" } row.key { "Password" }
row.value { "••••••••" } row.value { "••••••••" }
if can_edit_password?(@user, current_user) if UserPolicy.new(current_user, @user).edit_password?
row.action( row.action(
visually_hidden_text: "password", visually_hidden_text: "password",
href: edit_password_account_path, href: edit_password_account_path,
@ -64,7 +63,7 @@
<%= summary_list.row do |row| <%= summary_list.row do |row|
row.key { "Role" } row.key { "Role" }
row.value { @user.role&.humanize } row.value { @user.role&.humanize }
if can_edit_roles?(@user, current_user) if UserPolicy.new(current_user, @user).edit_roles?
row.action( row.action(
visually_hidden_text: "role", visually_hidden_text: "role",
href: aliased_user_edit(@user, current_user), href: aliased_user_edit(@user, current_user),
@ -78,7 +77,7 @@
<%= summary_list.row do |row| <%= summary_list.row do |row|
row.key { "Data protection officer" } row.key { "Data protection officer" }
row.value { @user.is_data_protection_officer? ? "Yes" : "No" } 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( row.action(
visually_hidden_text: "if data protection officer", visually_hidden_text: "if data protection officer",
href: user_edit_dpo_path(@user), href: user_edit_dpo_path(@user),
@ -92,7 +91,7 @@
<%= summary_list.row do |row| <%= summary_list.row do |row|
row.key { "Key contact" } row.key { "Key contact" }
row.value { @user.is_key_contact? ? "Yes" : "No" } 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( row.action(
visually_hidden_text: "if a key contact", visually_hidden_text: "if a key contact",
href: user_edit_key_contact_path(@user), href: user_edit_key_contact_path(@user),
@ -103,5 +102,21 @@
end end
end %> end %>
<% end %> <% end %>
<div class="govuk-button-group">
<% 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 %>
<span class="app-!-colour-muted govuk-!-margin-right-2">
This user has been deactivated. <%= govuk_button_link_to "Reactivate user", reactivate_user_path(@user) %>
</span>
<% end %>
<% end %>
</div>
</div> </div>
</div> </div>

9
config/application.rb

@ -41,5 +41,14 @@ module DataCollector
key: "_data_collector_session", key: "_data_collector_session",
secure: (Rails.env.production? || Rails.env.staging? || Rails.env.review?) 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
end end

9
config/locales/en.yml

@ -154,6 +154,8 @@ en:
invalid: "Enter an email address in the correct format, like name@example.com" invalid: "Enter an email address in the correct format, like name@example.com"
blank: "Enter an email address" blank: "Enter an email address"
taken: "Enter an email address that hasn’t already been used to sign up" 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: role:
invalid: "Role must be data accessor, data provider or data coordinator" invalid: "Role must be data accessor, data provider or data coordinator"
blank: "Select role" blank: "Select role"
@ -175,6 +177,11 @@ en:
new_organisation_telephone_number: new_organisation_telephone_number:
blank: "Enter a valid telephone number" blank: "Enter a valid telephone number"
notification:
logs_deleted:
one: "%{count} log has been deleted"
other: "%{count} logs have been deleted"
validations: validations:
organisation: organisation:
data_sharing_agreement_not_signed: Your organisation must accept the Data Sharing Agreement before you can create any logs. 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}" activating_soon: "%{name} is not available until %{date}. Enter a tenancy start date after %{date}"
owning_organisation: owning_organisation:
invalid: "Please select the owning organisation or managing organisation that you belong to" 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: managing_organisation:
invalid: "Please select the owning organisation or managing organisation that you belong to" 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: created_by:
invalid: "Please select the owning organisation or managing organisation that you belong to" invalid: "Please select the owning organisation or managing organisation that you belong to"
lettype: lettype:

19
config/routes.rb

@ -110,6 +110,7 @@ Rails.application.routes.draw do
member do member do
get "deactivate", to: "users#deactivate" get "deactivate", to: "users#deactivate"
get "reactivate", to: "users#reactivate" get "reactivate", to: "users#reactivate"
post "resend-invite", to: "users#resend_invite"
end end
end end
@ -122,7 +123,15 @@ Rails.application.routes.draw do
get "users", to: "organisations#users" get "users", to: "organisations#users"
get "users/invite", to: "users/account#new" get "users/invite", to: "users/account#new"
get "lettings-logs", to: "organisations#lettings_logs" 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 "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" get "lettings-logs/csv-download", to: "organisations#download_lettings_csv"
post "lettings-logs/email-csv", to: "organisations#email_lettings_csv" post "lettings-logs/email-csv", to: "organisations#email_lettings_csv"
get "lettings-logs/csv-confirmation", to: "lettings_logs#csv_confirmation" 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" post "email-csv", to: "lettings_logs#email_csv"
get "csv-confirmation", to: "lettings_logs#csv_confirmation" 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 resources :bulk_upload_lettings_logs, path: "bulk-upload-logs", only: %i[show update] do
collection do collection do
get :start get :start
@ -227,6 +241,11 @@ Rails.application.routes.draw do
post "email-csv", to: "sales_logs#email_csv" post "email-csv", to: "sales_logs#email_csv"
get "csv-confirmation", to: "sales_logs#csv_confirmation" 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 resources :bulk_upload_sales_logs, path: "bulk-upload-logs" do
collection do collection do
get :start get :start

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save