From 32a3443de4029f47375b27a8dd1a348d2d001471 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Tue, 4 Apr 2023 17:01:27 +0100 Subject: [PATCH 1/7] CLDC-2222 Set housingneeds as yes unless field_59 is given (#1513) * Correclty save carehome charges * Set housingneeds as yes if field_59 is not given * lint * Apply the change to 2023/24 --- .../bulk_upload/lettings/year2022/row_parser.rb | 2 +- .../bulk_upload/lettings/year2023/row_parser.rb | 2 +- .../bulk_upload/lettings/year2022/row_parser_spec.rb | 10 ++++++++++ .../bulk_upload/lettings/year2023/row_parser_spec.rb | 10 ++++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/app/services/bulk_upload/lettings/year2022/row_parser.rb b/app/services/bulk_upload/lettings/year2022/row_parser.rb index cdd47f99f..66cd4d12b 100644 --- a/app/services/bulk_upload/lettings/year2022/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2022/row_parser.rb @@ -1254,7 +1254,7 @@ private 2 elsif field_60 == 1 3 - elsif field_59&.zero? + elsif field_59.blank? || field_59&.zero? 1 end end diff --git a/app/services/bulk_upload/lettings/year2023/row_parser.rb b/app/services/bulk_upload/lettings/year2023/row_parser.rb index 7374e6208..69327ffc0 100644 --- a/app/services/bulk_upload/lettings/year2023/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2023/row_parser.rb @@ -1177,7 +1177,7 @@ private 2 elsif field_88 == 1 3 - elsif field_87&.zero? + elsif field_87.blank? || field_87&.zero? 1 end end diff --git a/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb index afd68644e..657773be4 100644 --- a/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb @@ -1472,6 +1472,16 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do end end + context "when housingneeds are given and field_59 is nil" do + let(:attributes) { { bulk_upload:, field_57: "1", field_58: "1", field_59: nil } } + + it "sets correct housingneeds" do + expect(parser.log.housingneeds).to eq(1) + expect(parser.log.housingneeds_type).to eq(2) + expect(parser.log.housingneeds_other).to eq(1) + end + end + context "when housingneeds a and b are selected" do let(:attributes) { { bulk_upload:, field_55: "1", field_56: "1" } } diff --git a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb index 67dc69bbe..03a5f5ca3 100644 --- a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb @@ -1468,6 +1468,16 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do end end + context "when housingneeds are given and field_86 is nil" do + let(:attributes) { { bulk_upload:, field_87: nil, field_85: "1", field_86: "1" } } + + it "sets correct housingneeds" do + expect(parser.log.housingneeds).to eq(1) + expect(parser.log.housingneeds_type).to eq(2) + expect(parser.log.housingneeds_other).to eq(1) + end + end + context "when housingneeds a and b are selected" do let(:attributes) { { bulk_upload:, field_83: "1", field_84: "1" } } From 4da87589718ab132e05b4bc7300c9f559c51d94c Mon Sep 17 00:00:00 2001 From: Phil Lee Date: Wed, 5 Apr 2023 11:36:53 +0100 Subject: [PATCH 2/7] CLDC-2224 Bulk upload validate radio options (#1514) * validate radio options for bulk upload * pend tests until uprn validations fixed --- app/models/validations/shared_validations.rb | 13 -------- .../lettings/year2022/row_parser.rb | 17 +++++++++++ .../lettings/year2023/row_parser.rb | 17 +++++++++++ config/initializers/feature_toggle.rb | 4 --- .../validations/shared_validations_spec.rb | 30 ------------------- .../lettings/year2023/row_parser_spec.rb | 24 +++++++++++++-- 6 files changed, 55 insertions(+), 50 deletions(-) diff --git a/app/models/validations/shared_validations.rb b/app/models/validations/shared_validations.rb index b5bad6828..57e1d05c6 100644 --- a/app/models/validations/shared_validations.rb +++ b/app/models/validations/shared_validations.rb @@ -77,19 +77,6 @@ module Validations::SharedValidations { scope: status, date: date&.to_formatted_s(:govuk_date), deactivation_date: closest_reactivation&.deactivation_date&.to_formatted_s(:govuk_date) } end - def validate_valid_radio_option(record) - return unless FeatureToggle.validate_valid_radio_options? - - record.attributes.each do |question_id, _v| - question = record.form.get_question(question_id, record) - - next unless question&.type == "radio" - next unless record[question_id].present? && !question.answer_options.key?(record[question_id].to_s) && question.page.routed_to?(record, nil) - - record.errors.add(question_id, I18n.t("validations.invalid_option", question: question.check_answer_label&.downcase)) - end - end - def shared_validate_partner_count(record, max_people) partner_numbers = (2..max_people).select { |n| person_is_partner?(record["relat#{n}"]) } if partner_numbers.count > 1 diff --git a/app/services/bulk_upload/lettings/year2022/row_parser.rb b/app/services/bulk_upload/lettings/year2022/row_parser.rb index 66cd4d12b..59824f3c5 100644 --- a/app/services/bulk_upload/lettings/year2022/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2022/row_parser.rb @@ -329,6 +329,8 @@ class BulkUpload::Lettings::Year2022::RowParser validate :validate_created_by_related validate :validate_rent_type + validate :validate_valid_radio_option + def self.question_for_field(field) QUESTIONS[field] end @@ -386,6 +388,21 @@ class BulkUpload::Lettings::Year2022::RowParser private + def validate_valid_radio_option + log.attributes.each do |question_id, _v| + question = log.form.get_question(question_id, log) + + next unless question&.type == "radio" + next if log[question_id].blank? || question.answer_options.key?(log[question_id].to_s) || !question.page.routed_to?(log, nil) + + fields = field_mapping_for_errors[question_id.to_sym] || [] + + fields.each do |field| + errors.add(field, I18n.t("validations.invalid_option", question: QUESTIONS[field])) + end + end + end + def validate_created_by_exists return if field_112.blank? diff --git a/app/services/bulk_upload/lettings/year2023/row_parser.rb b/app/services/bulk_upload/lettings/year2023/row_parser.rb index 69327ffc0..72c4efbe3 100644 --- a/app/services/bulk_upload/lettings/year2023/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2023/row_parser.rb @@ -331,6 +331,8 @@ class BulkUpload::Lettings::Year2023::RowParser validate :validate_created_by_exists validate :validate_created_by_related + validate :validate_valid_radio_option + def self.question_for_field(field) QUESTIONS[field] end @@ -388,6 +390,21 @@ class BulkUpload::Lettings::Year2023::RowParser private + def validate_valid_radio_option + log.attributes.each do |question_id, _v| + question = log.form.get_question(question_id, log) + + next unless question&.type == "radio" + next if log[question_id].blank? || question.answer_options.key?(log[question_id].to_s) || !question.page.routed_to?(log, nil) + + fields = field_mapping_for_errors[question_id.to_sym] || [] + + fields.each do |field| + errors.add(field, I18n.t("validations.invalid_option", question: QUESTIONS[field])) + end + end + end + def validate_created_by_exists return if field_3.blank? diff --git a/config/initializers/feature_toggle.rb b/config/initializers/feature_toggle.rb index 6e4ac0511..125aa6770 100644 --- a/config/initializers/feature_toggle.rb +++ b/config/initializers/feature_toggle.rb @@ -50,10 +50,6 @@ class FeatureToggle !Rails.env.production? end - def self.validate_valid_radio_options? - !(Rails.env.production? || Rails.env.staging?) - end - def self.collection_2023_2024_year_enabled? true end diff --git a/spec/models/validations/shared_validations_spec.rb b/spec/models/validations/shared_validations_spec.rb index 5c389bb6c..8f5038c5d 100644 --- a/spec/models/validations/shared_validations_spec.rb +++ b/spec/models/validations/shared_validations_spec.rb @@ -113,34 +113,4 @@ RSpec.describe Validations::SharedValidations do end end end - - describe "radio options validations" do - it "allows only possible values" do - record.needstype = 1 - shared_validator.validate_valid_radio_option(record) - - expect(record.errors["needstype"]).to be_empty - end - - it "denies impossible values" do - record.needstype = 3 - shared_validator.validate_valid_radio_option(record) - - expect(record.errors["needstype"]).to be_present - expect(record.errors["needstype"]).to eql(["Enter a valid value for needs type"]) - end - - context "when feature is toggled off" do - before do - allow(FeatureToggle).to receive(:validate_valid_radio_options?).and_return(false) - end - - it "allows any values" do - record.needstype = 3 - shared_validator.validate_valid_radio_option(record) - - expect(record.errors["needstype"]).to be_empty - end - end - end end diff --git a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb index 03a5f5ca3..50d3fb81a 100644 --- a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb @@ -3,7 +3,7 @@ require "rails_helper" RSpec.describe BulkUpload::Lettings::Year2023::RowParser do subject(:parser) { described_class.new(attributes) } - let(:now) { Time.zone.parse("01/03/2023") } + let(:now) { Time.zone.now.beginning_of_day } let(:attributes) { { bulk_upload: } } let(:bulk_upload) { create(:bulk_upload, :lettings, user:, needstype: nil) } @@ -223,11 +223,11 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do } end - it "returns true" do + xit "returns true" do expect(parser).to be_valid end - it "instantiates a log with everything completed", aggregate_failures: true do + xit "instantiates a log with everything completed", aggregate_failures: true do questions = parser.send(:questions).reject do |q| parser.send(:log).optional_fields.include?(q.id) || q.completed?(parser.send(:log)) end @@ -504,6 +504,14 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do end end end + + context "when no longer a valid option from previous year" do + let(:attributes) { setup_section_params.merge({ field_102: "7" }) } + + it "returns an error" do + expect(parser.errors[:field_102]).to be_present + end + end end describe "#field_83, #field_84, #field_85" do @@ -790,6 +798,16 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do end end + describe "#field_26" do # unitletas + context "when no longer a valid option from previous year" do + let(:attributes) { setup_section_params.merge({ field_26: "4" }) } + + it "returns an error" do + expect(parser.errors[:field_26]).to be_present + end + end + end + describe "#field_30" do context "when null" do let(:attributes) { setup_section_params.merge({ field_30: nil }) } From 454df8389ee6672c86fe45e6f532d860320b3c0f Mon Sep 17 00:00:00 2001 From: Phil Lee Date: Wed, 5 Apr 2023 13:11:23 +0100 Subject: [PATCH 3/7] CLDC-2135 Bulk upload resume with how fix (#1503) * add first page for bulk upload resume journey * bulk upload resume handles upload again * add confirm page to bulk upload resume journey * replace placeholder count with correct value * apply recommendation for bulk upload resume choice * add how to fix bulk upload mailer * integrate new bulk upload approve journey * add missing bulk upload error mappings * remove test * prevent approve being called multiple times * bulk upload creates invisible logs ahead of time * work invisible logs into bulk upload flow * sort errors so deterministic * remove unused ensure * remove expected_log_count and processed - these fields are no longer used or needed * introduce pending status * swap visible for pending logs * only show visible lettings logs * hard code status filters * remove unused model methods * only show visible sales logs * form controller ignores hidden logs * locations and schemes only affect visible logs --- .../bulk_upload_lettings_resume_controller.rb | 42 +++++ app/controllers/form_controller.rb | 8 +- app/controllers/lettings_logs_controller.rb | 4 +- app/controllers/locations_controller.rb | 4 +- app/controllers/organisations_controller.rb | 4 +- app/controllers/sales_logs_controller.rb | 4 +- app/controllers/schemes_controller.rb | 4 +- app/helpers/filters_helper.rb | 8 +- app/jobs/email_csv_job.rb | 2 +- app/mailers/bulk_upload_mailer.rb | 39 ++-- app/models/bulk_upload.rb | 8 + .../bulk_upload_lettings_resume/confirm.rb | 30 ++++ .../bulk_upload_lettings_resume/fix_choice.rb | 53 ++++++ app/models/lettings_log.rb | 12 -- app/models/log.rb | 32 +++- app/models/organisation.rb | 8 - app/models/sales_log.rb | 8 - app/models/user.rb | 8 - .../bulk_upload/lettings/log_creator.rb | 3 + .../bulk_upload/lettings/validator.rb | 1 - .../lettings/year2022/row_parser.rb | 1 + .../lettings/year2023/row_parser.rb | 1 + app/services/bulk_upload/processor.rb | 27 ++- app/services/csv/lettings_log_csv_service.rb | 2 +- .../exports/lettings_log_export_service.rb | 5 +- .../imports/lettings_logs_import_service.rb | 2 +- .../forms/needstype.erb | 1 + .../confirm.html.erb | 22 +++ .../fix_choice.html.erb | 36 ++++ config/locales/en.yml | 5 + config/routes.rb | 9 + ...094840_add_status_cache_to_lettings_log.rb | 5 + db/schema.rb | 3 +- .../fixtures/files/lettings_logs_download.csv | 4 +- .../lettings_logs_download_codes_only.csv | 4 +- spec/jobs/email_csv_job_spec.rb | 1 + spec/mailers/bulk_upload_mailer_spec.rb | 10 +- spec/models/organisation_spec.rb | 5 - spec/models/user_spec.rb | 5 - ..._upload_lettings_resume_controller_spec.rb | 84 +++++++++ spec/requests/form_controller_spec.rb | 17 ++ .../requests/lettings_logs_controller_spec.rb | 33 +++- .../requests/organisations_controller_spec.rb | 5 +- spec/requests/sales_logs_controller_spec.rb | 16 ++ .../bulk_upload/lettings/log_creator_spec.rb | 22 +++ .../bulk_upload/lettings/validator_spec.rb | 53 ------ spec/services/bulk_upload/processor_spec.rb | 167 ++++++++---------- .../csv/lettings_log_csv_service_spec.rb | 1 + .../lettings_log_export_service_spec.rb | 29 +++ .../lettings_logs_import_service_spec.rb | 20 +-- spec/support/bulk_upload/log_to_csv.rb | 2 +- 51 files changed, 620 insertions(+), 259 deletions(-) create mode 100644 app/controllers/bulk_upload_lettings_resume_controller.rb create mode 100644 app/models/forms/bulk_upload_lettings_resume/confirm.rb create mode 100644 app/models/forms/bulk_upload_lettings_resume/fix_choice.rb create mode 100644 app/views/bulk_upload_lettings_resume/confirm.html.erb create mode 100644 app/views/bulk_upload_lettings_resume/fix_choice.html.erb create mode 100644 db/migrate/20230331094840_add_status_cache_to_lettings_log.rb create mode 100644 spec/requests/bulk_upload_lettings_resume_controller_spec.rb diff --git a/app/controllers/bulk_upload_lettings_resume_controller.rb b/app/controllers/bulk_upload_lettings_resume_controller.rb new file mode 100644 index 000000000..4c21d39e2 --- /dev/null +++ b/app/controllers/bulk_upload_lettings_resume_controller.rb @@ -0,0 +1,42 @@ +class BulkUploadLettingsResumeController < ApplicationController + before_action :authenticate_user! + + def start + @bulk_upload = current_user.bulk_uploads.find(params[:id]) + + redirect_to page_bulk_upload_lettings_resume_path(@bulk_upload, page: "fix-choice") + end + + def show + @bulk_upload = current_user.bulk_uploads.find(params[:id]) + + render form.view_path + end + + def update + @bulk_upload = current_user.bulk_uploads.find(params[:id]) + + if form.valid? && form.save! + redirect_to form.next_path + else + render form.view_path + end + end + +private + + def form + @form ||= case params[:page] + when "fix-choice" + Forms::BulkUploadLettingsResume::FixChoice.new(form_params.merge(bulk_upload: @bulk_upload)) + when "confirm" + Forms::BulkUploadLettingsResume::Confirm.new(form_params.merge(bulk_upload: @bulk_upload)) + else + raise "invalid form" + end + end + + def form_params + params.fetch(:form, {}).permit(:choice) + end +end diff --git a/app/controllers/form_controller.rb b/app/controllers/form_controller.rb index 92b62a511..43a805d9f 100644 --- a/app/controllers/form_controller.rb +++ b/app/controllers/form_controller.rb @@ -108,17 +108,17 @@ private def find_resource @log = if params.key?("sales_log") - current_user.sales_logs.find_by(id: params[:id]) + current_user.sales_logs.visible.find_by(id: params[:id]) else - current_user.lettings_logs.find_by(id: params[:id]) + current_user.lettings_logs.visible.find_by(id: params[:id]) end end def find_resource_by_named_id @log = if params[:sales_log_id].present? - current_user.sales_logs.find_by(id: params[:sales_log_id]) + current_user.sales_logs.visible.find_by(id: params[:sales_log_id]) else - current_user.lettings_logs.find_by(id: params[:lettings_log_id]) + current_user.lettings_logs.visible.find_by(id: params[:lettings_log_id]) end end diff --git a/app/controllers/lettings_logs_controller.rb b/app/controllers/lettings_logs_controller.rb index e07878b0b..c97f5f45d 100644 --- a/app/controllers/lettings_logs_controller.rb +++ b/app/controllers/lettings_logs_controller.rb @@ -15,7 +15,7 @@ class LettingsLogsController < LogsController def index respond_to do |format| format.html do - all_logs = current_user.lettings_logs + all_logs = current_user.lettings_logs.visible unpaginated_filtered_logs = filtered_logs(all_logs, search_term, @session_filters) @search_term = search_term @@ -140,7 +140,7 @@ private end def find_resource - @log = LettingsLog.find_by(id: params[:id]) + @log = LettingsLog.visible.find_by(id: params[:id]) end def post_create_redirect_url(log) diff --git a/app/controllers/locations_controller.rb b/app/controllers/locations_controller.rb index 5b39fa875..50102ee17 100644 --- a/app/controllers/locations_controller.rb +++ b/app/controllers/locations_controller.rb @@ -158,7 +158,7 @@ class LocationsController < ApplicationController end def deactivate_confirm - @affected_logs = @location.lettings_logs.filter_by_before_startdate(params[:deactivation_date]) + @affected_logs = @location.lettings_logs.visible.filter_by_before_startdate(params[:deactivation_date]) if @affected_logs.count.zero? deactivate else @@ -260,7 +260,7 @@ private end def reset_location_and_scheme_for_logs! - logs = @location.lettings_logs.filter_by_before_startdate(params[:deactivation_date].to_time) + logs = @location.lettings_logs.visible.filter_by_before_startdate(params[:deactivation_date].to_time) logs.update!(location: nil, scheme: nil, unresolved: true) logs end diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb index 418526960..1bd4694ec 100644 --- a/app/controllers/organisations_controller.rb +++ b/app/controllers/organisations_controller.rb @@ -90,7 +90,7 @@ class OrganisationsController < ApplicationController end def lettings_logs - organisation_logs = LettingsLog.where(owning_organisation_id: @organisation.id) + organisation_logs = LettingsLog.visible.where(owning_organisation_id: @organisation.id) unpaginated_filtered_logs = filtered_logs(organisation_logs, search_term, @session_filters) respond_to do |format| @@ -105,7 +105,7 @@ class OrganisationsController < ApplicationController end def download_csv - organisation_logs = LettingsLog.all.where(owning_organisation_id: @organisation.id) + organisation_logs = LettingsLog.visible.where(owning_organisation_id: @organisation.id) unpaginated_filtered_logs = filtered_logs(organisation_logs, search_term, @session_filters) codes_only = params.require(:codes_only) == "true" diff --git a/app/controllers/sales_logs_controller.rb b/app/controllers/sales_logs_controller.rb index ecfeabcad..155f606ab 100644 --- a/app/controllers/sales_logs_controller.rb +++ b/app/controllers/sales_logs_controller.rb @@ -9,7 +9,7 @@ class SalesLogsController < LogsController def index respond_to do |format| format.html do - all_logs = current_user.sales_logs + all_logs = current_user.sales_logs.visible unpaginated_filtered_logs = filtered_logs(all_logs, search_term, @session_filters) @search_term = search_term @@ -28,7 +28,7 @@ class SalesLogsController < LogsController end def edit - @log = current_user.sales_logs.find_by(id: params[:id]) + @log = current_user.sales_logs.visible.find_by(id: params[:id]) if @log render "logs/edit", locals: { current_user: } else diff --git a/app/controllers/schemes_controller.rb b/app/controllers/schemes_controller.rb index d23c3a8ee..f248bffe5 100644 --- a/app/controllers/schemes_controller.rb +++ b/app/controllers/schemes_controller.rb @@ -39,7 +39,7 @@ class SchemesController < ApplicationController end def deactivate_confirm - @affected_logs = @scheme.lettings_logs.filter_by_before_startdate(params[:deactivation_date]) + @affected_logs = @scheme.lettings_logs.visible.filter_by_before_startdate(params[:deactivation_date]) if @affected_logs.count.zero? deactivate else @@ -310,7 +310,7 @@ private end def reset_location_and_scheme_for_logs! - logs = @scheme.lettings_logs.filter_by_before_startdate(params[:deactivation_date].to_time) + logs = @scheme.lettings_logs.visible.filter_by_before_startdate(params[:deactivation_date].to_time) logs.update!(location: nil, scheme: nil, unresolved: true) logs end diff --git a/app/helpers/filters_helper.rb b/app/helpers/filters_helper.rb index 7f906b4ea..f38d52909 100644 --- a/app/helpers/filters_helper.rb +++ b/app/helpers/filters_helper.rb @@ -12,9 +12,11 @@ module FiltersHelper end def status_filters - statuses = {} - LettingsLog.statuses.keys.map { |status| statuses[status] = status.humanize } - statuses + { + "not_started" => "Not started", + "in_progress" => "In progress", + "completed" => "Completed", + }.freeze end def selected_option(filter) diff --git a/app/jobs/email_csv_job.rb b/app/jobs/email_csv_job.rb index 6e7888f18..f313c87b8 100644 --- a/app/jobs/email_csv_job.rb +++ b/app/jobs/email_csv_job.rb @@ -6,7 +6,7 @@ class EmailCsvJob < ApplicationJob EXPIRATION_TIME = 3.hours.to_i def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil, codes_only_export = false) # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params - unfiltered_logs = organisation.present? && user.support? ? LettingsLog.where(owning_organisation_id: organisation.id) : user.lettings_logs + unfiltered_logs = organisation.present? && user.support? ? LettingsLog.visible.where(owning_organisation_id: organisation.id) : user.lettings_logs.visible filtered_logs = FilterService.filter_logs(unfiltered_logs, search_term, filters, all_orgs, user) filename = organisation.present? ? "logs-#{organisation.name}-#{Time.zone.now}.csv" : "logs-#{Time.zone.now}.csv" diff --git a/app/mailers/bulk_upload_mailer.rb b/app/mailers/bulk_upload_mailer.rb index d91842bce..2d0b20a92 100644 --- a/app/mailers/bulk_upload_mailer.rb +++ b/app/mailers/bulk_upload_mailer.rb @@ -1,11 +1,30 @@ class BulkUploadMailer < NotifyMailer include ActionView::Helpers::TextHelper - BULK_UPLOAD_COMPLETE_TEMPLATE_ID = "83279578-c890-4168-838b-33c9cf0dc9f0".freeze - BULK_UPLOAD_FAILED_CSV_ERRORS_TEMPLATE_ID = "e27abcd4-5295-48c2-b127-e9ee4b781b75".freeze - BULK_UPLOAD_FAILED_FILE_SETUP_ERROR_TEMPLATE_ID = "24c9f4c7-96ad-470a-ba31-eb51b7cbafd9".freeze - BULK_UPLOAD_FAILED_SERVICE_ERROR_TEMPLATE_ID = "c3f6288c-7a74-4e77-99ee-6c4a0f6e125a".freeze - BULK_UPLOAD_WITH_ERRORS_TEMPLATE_ID = "eb539005-6234-404e-812d-167728cf4274".freeze + COMPLETE_TEMPLATE_ID = "83279578-c890-4168-838b-33c9cf0dc9f0".freeze + FAILED_CSV_ERRORS_TEMPLATE_ID = "e27abcd4-5295-48c2-b127-e9ee4b781b75".freeze + FAILED_FILE_SETUP_ERROR_TEMPLATE_ID = "24c9f4c7-96ad-470a-ba31-eb51b7cbafd9".freeze + FAILED_SERVICE_ERROR_TEMPLATE_ID = "c3f6288c-7a74-4e77-99ee-6c4a0f6e125a".freeze + WITH_ERRORS_TEMPLATE_ID = "eb539005-6234-404e-812d-167728cf4274".freeze + HOW_FIX_UPLOAD_TEMPLATE_ID = "21a07b26-f625-4846-9f4d-39e30937aa24".freeze + + def send_how_fix_upload_mail(bulk_upload:) + title = "We found #{pluralize(bulk_upload.bulk_upload_errors.count, 'error')} in your bulk upload" + description = "There was a problem with your #{bulk_upload.year_combo} #{bulk_upload.log_type} data. Check the error report below to fix these errors." + cta_link = start_bulk_upload_lettings_resume_url(bulk_upload) + + send_email( + bulk_upload.user.email, + HOW_FIX_UPLOAD_TEMPLATE_ID, + { + title:, + filename: bulk_upload.filename, + upload_timestamp: bulk_upload.created_at.to_fs(:govuk_date_and_time), + description:, + cta_link:, + }, + ) + end def send_bulk_upload_complete_mail(user:, bulk_upload:) url = if bulk_upload.lettings? @@ -22,7 +41,7 @@ class BulkUploadMailer < NotifyMailer send_email( user.email, - BULK_UPLOAD_COMPLETE_TEMPLATE_ID, + COMPLETE_TEMPLATE_ID, { title:, filename: bulk_upload.filename, @@ -42,7 +61,7 @@ class BulkUploadMailer < NotifyMailer send_email( bulk_upload.user.email, - BULK_UPLOAD_FAILED_CSV_ERRORS_TEMPLATE_ID, + FAILED_CSV_ERRORS_TEMPLATE_ID, { filename: bulk_upload.filename, upload_timestamp: bulk_upload.created_at.to_fs(:govuk_date_and_time), @@ -75,7 +94,7 @@ class BulkUploadMailer < NotifyMailer send_email( bulk_upload.user.email, - BULK_UPLOAD_FAILED_FILE_SETUP_ERROR_TEMPLATE_ID, + FAILED_FILE_SETUP_ERROR_TEMPLATE_ID, { filename: bulk_upload.filename, upload_timestamp: bulk_upload.created_at.to_fs(:govuk_date_and_time), @@ -96,7 +115,7 @@ class BulkUploadMailer < NotifyMailer send_email( bulk_upload.user.email, - BULK_UPLOAD_FAILED_SERVICE_ERROR_TEMPLATE_ID, + FAILED_SERVICE_ERROR_TEMPLATE_ID, { filename: bulk_upload.filename, upload_timestamp: bulk_upload.created_at.to_fs(:govuk_date_and_time), @@ -119,7 +138,7 @@ class BulkUploadMailer < NotifyMailer send_email( bulk_upload.user.email, - BULK_UPLOAD_WITH_ERRORS_TEMPLATE_ID, + WITH_ERRORS_TEMPLATE_ID, { title:, filename: bulk_upload.filename, diff --git a/app/models/bulk_upload.rb b/app/models/bulk_upload.rb index 0952b60af..66e83cd85 100644 --- a/app/models/bulk_upload.rb +++ b/app/models/bulk_upload.rb @@ -60,6 +60,14 @@ class BulkUpload < ApplicationRecord "BulkUpload::#{type_class}::#{year_class}".constantize end + def unpend + logs.find_each do |log| + log.skip_update_status = true + log.status = log.status_cache + log.save! + end + end + private def generate_identifier diff --git a/app/models/forms/bulk_upload_lettings_resume/confirm.rb b/app/models/forms/bulk_upload_lettings_resume/confirm.rb new file mode 100644 index 000000000..7760ab2e8 --- /dev/null +++ b/app/models/forms/bulk_upload_lettings_resume/confirm.rb @@ -0,0 +1,30 @@ +module Forms + module BulkUploadLettingsResume + class Confirm + include ActiveModel::Model + include ActiveModel::Attributes + include Rails.application.routes.url_helpers + + attribute :bulk_upload + + def view_path + "bulk_upload_lettings_resume/confirm" + end + + def back_path + page_bulk_upload_lettings_resume_path(bulk_upload, page: "fix-choice") + end + + def next_path + resume_bulk_upload_lettings_result_path(bulk_upload) + end + + def save! + processor = BulkUpload::Processor.new(bulk_upload:) + processor.approve + + true + end + end + end +end diff --git a/app/models/forms/bulk_upload_lettings_resume/fix_choice.rb b/app/models/forms/bulk_upload_lettings_resume/fix_choice.rb new file mode 100644 index 000000000..5513434de --- /dev/null +++ b/app/models/forms/bulk_upload_lettings_resume/fix_choice.rb @@ -0,0 +1,53 @@ +module Forms + module BulkUploadLettingsResume + class FixChoice + include ActiveModel::Model + include ActiveModel::Attributes + include Rails.application.routes.url_helpers + + attribute :bulk_upload + attribute :choice, :string + + validates :choice, presence: true, + inclusion: { in: %w[create-fix-inline upload-again] } + + def options + [ + OpenStruct.new(id: "create-fix-inline", name: "Upload these logs and fix errors on CORE site"), + OpenStruct.new(id: "upload-again", name: "Fix errors in the CSV and re-upload"), + ] + end + + def view_path + "bulk_upload_lettings_resume/fix_choice" + end + + def next_path + case choice + when "create-fix-inline" + page_bulk_upload_lettings_resume_path(bulk_upload, page: "confirm") + when "upload-again" + if BulkUploadErrorSummaryTableComponent.new(bulk_upload:).errors? + summary_bulk_upload_lettings_result_path(bulk_upload) + else + bulk_upload_lettings_result_path(bulk_upload) + end + else + raise "invalid choice" + end + end + + def recommendation + if BulkUploadErrorSummaryTableComponent.new(bulk_upload:).errors? + "For this many errors we recommend to fix errors in the CSV and re-upload as you may be able to edit many fields at once in a CSV." + else + "For this many errors we recommend to upload logs and fix errors on site as you can easily see the questions and select the appropriate answer." + end + end + + def save! + true + end + end + end +end diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb index f96c41c88..4a0651a29 100644 --- a/app/models/lettings_log.rb +++ b/app/models/lettings_log.rb @@ -115,18 +115,6 @@ class LettingsLog < Log end end - def completed? - status == "completed" - end - - def not_started? - status == "not_started" - end - - def in_progress? - status == "in_progress" - end - def weekly_net_income return unless earnings && incfreq diff --git a/app/models/log.rb b/app/models/log.rb index 407ec2746..bc036d356 100644 --- a/app/models/log.rb +++ b/app/models/log.rb @@ -8,8 +8,16 @@ class Log < ApplicationRecord before_save :update_status! - STATUS = { "not_started" => 0, "in_progress" => 1, "completed" => 2 }.freeze + STATUS = { + "not_started" => 0, + "in_progress" => 1, + "completed" => 2, + "pending" => 3, + }.freeze enum status: STATUS + enum status_cache: STATUS, _prefix: true + + scope :visible, -> { where(status: %w[not_started in_progress completed]) } scope :filter_by_status, ->(status, _user = nil) { where status: } scope :filter_by_years, lambda { |years, _user = nil| @@ -31,6 +39,8 @@ class Log < ApplicationRecord } scope :created_by, ->(user) { where(created_by: user) } + attr_accessor :skip_update_status + def process_uprn_change! if uprn.present? service = UprnClient.new(uprn) @@ -106,6 +116,16 @@ class Log < ApplicationRecord end end + def calculate_status + if all_fields_completed? && errors.empty? + "completed" + elsif all_fields_nil? + "not_started" + else + "in_progress" + end + end + private def plural_gender_for_person(person_num) @@ -120,13 +140,9 @@ private end def update_status! - self.status = if all_fields_completed? && errors.empty? - "completed" - elsif all_fields_nil? - "not_started" - else - "in_progress" - end + return if skip_update_status + + self.status = calculate_status end def all_fields_completed? diff --git a/app/models/organisation.rb b/app/models/organisation.rb index 279d60f49..bd468e093 100644 --- a/app/models/organisation.rb +++ b/app/models/organisation.rb @@ -70,14 +70,6 @@ class Organisation < ApplicationRecord SalesLog.filter_by_organisation(self) end - def completed_lettings_logs - lettings_logs.completed - end - - def not_completed_lettings_logs - lettings_logs.not_completed - end - def address_string %i[address_line1 address_line2 postcode].map { |field| public_send(field) }.join("\n") end diff --git a/app/models/sales_log.rb b/app/models/sales_log.rb index 1cfa91acb..b0aed712a 100644 --- a/app/models/sales_log.rb +++ b/app/models/sales_log.rb @@ -104,14 +104,6 @@ class SalesLog < Log collection_start_year < 2023 end - def not_started? - status == "not_started" - end - - def completed? - status == "completed" - end - def setup_completed? form.setup_sections.all? { |sections| sections.subsections.all? { |subsection| subsection.status(self) == :completed } } end diff --git a/app/models/user.rb b/app/models/user.rb index 6354086b5..7c86d8796 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -69,14 +69,6 @@ class User < ApplicationRecord end end - def completed_lettings_logs - lettings_logs.completed - end - - def not_completed_lettings_logs - lettings_logs.not_completed - end - def is_key_contact? is_key_contact end diff --git a/app/services/bulk_upload/lettings/log_creator.rb b/app/services/bulk_upload/lettings/log_creator.rb index 089639d4f..8d3a6cbd7 100644 --- a/app/services/bulk_upload/lettings/log_creator.rb +++ b/app/services/bulk_upload/lettings/log_creator.rb @@ -14,6 +14,9 @@ class BulkUpload::Lettings::LogCreator row_parser.log.blank_invalid_non_setup_fields! row_parser.log.bulk_upload = bulk_upload + row_parser.log.skip_update_status = true + row_parser.log.status = "pending" + row_parser.log.status_cache = row_parser.log.calculate_status begin row_parser.log.save! diff --git a/app/services/bulk_upload/lettings/validator.rb b/app/services/bulk_upload/lettings/validator.rb index e60435f75..1a0e6ecf5 100644 --- a/app/services/bulk_upload/lettings/validator.rb +++ b/app/services/bulk_upload/lettings/validator.rb @@ -42,7 +42,6 @@ class BulkUpload::Lettings::Validator def create_logs? return false if any_setup_errors? - return false if over_column_error_threshold? return false if row_parsers.any?(&:block_log_creation?) row_parsers.all? { |row_parser| row_parser.log.valid? } diff --git a/app/services/bulk_upload/lettings/year2022/row_parser.rb b/app/services/bulk_upload/lettings/year2022/row_parser.rb index 59824f3c5..d23b5e7ba 100644 --- a/app/services/bulk_upload/lettings/year2022/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2022/row_parser.rb @@ -789,6 +789,7 @@ private cbl: %i[field_75], chr: %i[field_76], cap: %i[field_77], + letting_allocation: %i[field_75 field_76 field_77], referral: %i[field_78], diff --git a/app/services/bulk_upload/lettings/year2023/row_parser.rb b/app/services/bulk_upload/lettings/year2023/row_parser.rb index 72c4efbe3..b363b1bad 100644 --- a/app/services/bulk_upload/lettings/year2023/row_parser.rb +++ b/app/services/bulk_upload/lettings/year2023/row_parser.rb @@ -767,6 +767,7 @@ private cbl: %i[field_116], chr: %i[field_118], cap: %i[field_117], + letting_allocation: %i[field_116 field_117 field_118], referral: %i[field_119], diff --git a/app/services/bulk_upload/processor.rb b/app/services/bulk_upload/processor.rb index 2d27464f0..4fe449348 100644 --- a/app/services/bulk_upload/processor.rb +++ b/app/services/bulk_upload/processor.rb @@ -16,10 +16,17 @@ class BulkUpload::Processor send_setup_errors_mail elsif validator.create_logs? create_logs - send_fix_errors_mail if created_logs_but_incompleted? - send_success_mail if created_logs_and_all_completed? + + if created_logs_but_incompleted? + send_how_fix_upload_mail + end + + if created_logs_and_all_completed? + bulk_upload.unpend + send_success_mail + end else - send_correct_and_upload_again_mail + send_correct_and_upload_again_mail # summary/full report end rescue StandardError => e Sentry.capture_exception(e) @@ -28,8 +35,18 @@ class BulkUpload::Processor downloader.delete_local_file! end + def approve + bulk_upload.unpend + end + private + def send_how_fix_upload_mail + BulkUploadMailer + .send_how_fix_upload_mail(bulk_upload:) + .deliver_later + end + def send_setup_errors_mail BulkUploadMailer .send_bulk_upload_failed_file_setup_error_mail(bulk_upload:) @@ -55,11 +72,11 @@ private end def created_logs_but_incompleted? - validator.create_logs? && bulk_upload.logs.where.not(status: %w[completed]).count.positive? + bulk_upload.logs.where.not(status_cache: %w[completed]).count.positive? end def created_logs_and_all_completed? - validator.create_logs? && bulk_upload.logs.group(:status).count.keys == %w[completed] + bulk_upload.logs.group(:status_cache).count.keys == %w[completed] end def send_failure_mail(errors: []) diff --git a/app/services/csv/lettings_log_csv_service.rb b/app/services/csv/lettings_log_csv_service.rb index 7b90b5eda..e0815d164 100644 --- a/app/services/csv/lettings_log_csv_service.rb +++ b/app/services/csv/lettings_log_csv_service.rb @@ -1,6 +1,6 @@ module Csv class LettingsLogCsvService - CSV_FIELDS_TO_OMIT = %w[hhmemb net_income_value_check first_time_property_let_as_social_housing renttype needstype postcode_known is_la_inferred totchild totelder totadult net_income_known is_carehome previous_la_known is_previous_la_inferred age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known letting_allocation_unknown details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 rent_type_detail wrent wscharge wpschrge wsupchrg wtcharge wtshortfall rent_value_check old_form_id old_id retirement_value_check tshortfall_known pregnancy_value_check hhtype new_old vacdays la prevloc unresolved updated_by_id bulk_upload_id uprn_confirmed].freeze + CSV_FIELDS_TO_OMIT = %w[hhmemb net_income_value_check first_time_property_let_as_social_housing renttype needstype postcode_known is_la_inferred totchild totelder totadult net_income_known is_carehome previous_la_known is_previous_la_inferred age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known letting_allocation_unknown details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 rent_type_detail wrent wscharge wpschrge wsupchrg wtcharge wtshortfall rent_value_check old_form_id old_id retirement_value_check tshortfall_known pregnancy_value_check hhtype new_old vacdays la prevloc unresolved updated_by_id bulk_upload_id uprn_confirmed status_cache].freeze def initialize(user, export_type:) @user = user diff --git a/app/services/exports/lettings_log_export_service.rb b/app/services/exports/lettings_log_export_service.rb index 197e0ed4b..158d52147 100644 --- a/app/services/exports/lettings_log_export_service.rb +++ b/app/services/exports/lettings_log_export_service.rb @@ -115,12 +115,13 @@ module Exports def retrieve_lettings_logs(start_time, full_update) recent_export = LogsExport.order("started_at").last + if !full_update && recent_export params = { from: recent_export.started_at, to: start_time } - LettingsLog.where("updated_at >= :from and updated_at <= :to", params) + LettingsLog.visible.where("updated_at >= :from and updated_at <= :to", params) else params = { to: start_time } - LettingsLog.where("updated_at <= :to", params) + LettingsLog.visible.where("updated_at <= :to", params) end end diff --git a/app/services/imports/lettings_logs_import_service.rb b/app/services/imports/lettings_logs_import_service.rb index 734cfe7de..94fb2ec3e 100644 --- a/app/services/imports/lettings_logs_import_service.rb +++ b/app/services/imports/lettings_logs_import_service.rb @@ -313,7 +313,7 @@ module Imports attribute, _type = error fields.each do |field| - @logger.warn("Log #{lettings_log.old_id}: Removing #{field} with error: #{lettings_log.errors[attribute].join(', ')}") + @logger.warn("Log #{lettings_log.old_id}: Removing #{field} with error: #{lettings_log.errors[attribute].sort.join(', ')}") attributes.delete(field) end @logs_overridden << lettings_log.old_id diff --git a/app/views/bulk_upload_lettings_logs/forms/needstype.erb b/app/views/bulk_upload_lettings_logs/forms/needstype.erb index a9bc28c4f..6deec7e1d 100644 --- a/app/views/bulk_upload_lettings_logs/forms/needstype.erb +++ b/app/views/bulk_upload_lettings_logs/forms/needstype.erb @@ -1,6 +1,7 @@ <% content_for :before_content do %> <%= govuk_back_link href: @form.back_path %> <% end %> +
<%= form_with model: @form, scope: :form, url: bulk_upload_lettings_log_path(id: "needstype"), method: :patch do |f| %> diff --git a/app/views/bulk_upload_lettings_resume/confirm.html.erb b/app/views/bulk_upload_lettings_resume/confirm.html.erb new file mode 100644 index 000000000..af73b33e9 --- /dev/null +++ b/app/views/bulk_upload_lettings_resume/confirm.html.erb @@ -0,0 +1,22 @@ +<% content_for :before_content do %> + <%= govuk_back_link href: @form.back_path %> +<% end %> + +
+
+ Bulk upload for lettings (<%= @bulk_upload.year_combo %>) +

Are you sure you want to upload all logs from this bulk upload?

+ +

There are <%= pluralize(@bulk_upload.logs.count, "log") %> in this bulk upload with <%= pluralize(@bulk_upload.bulk_upload_errors.count, "error") %> that still need to be fixed after upload.

+ + <%= govuk_warning_text(icon_fallback_text: "Danger") do %> + You can not delete logs once you create them + <% end %> + + <%= form_with model: @form, scope: :form, url: page_bulk_upload_lettings_resume_path(@bulk_upload, page: "confirm"), method: :patch do |f| %> + <%= f.govuk_submit %> + + <%= govuk_button_link_to "Cancel", @form.back_path, secondary: true %> + <% end %> +
+
diff --git a/app/views/bulk_upload_lettings_resume/fix_choice.html.erb b/app/views/bulk_upload_lettings_resume/fix_choice.html.erb new file mode 100644 index 000000000..cc8e33eaa --- /dev/null +++ b/app/views/bulk_upload_lettings_resume/fix_choice.html.erb @@ -0,0 +1,36 @@ +
+
+ <%= form_with model: @form, scope: :form, url: page_bulk_upload_lettings_resume_path(@bulk_upload, page: "fix-choice"), method: :patch do |f| %> + <%= f.govuk_error_summary %> + + Bulk upload for lettings (<%= @bulk_upload.year_combo %>) +

How would you like to fix <%= pluralize(@bulk_upload.bulk_upload_errors.count, "error") %>?

+ +
+ <%= @bulk_upload.filename %> +
+ +
+ <%= @form.recommendation %> +
+ + <%= govuk_details(summary_text: "How to choose between fixing errors on the CORE site or in the CSV") do %> +

When it comes to fixing errors, there are pros and cons to doing it on a CSV versus doing it on a website.

+ +

Fixing errors on a CSV file can be beneficial because it allows you to easily make changes to multiple records at once, and you can use tools like Excel to quickly identify and correct errors. However, if the CSV file is not properly formatted, it can be difficult to identify which records contain errors.

+ +

Fixing errors on a website can be convenient because you can see the data in context and make changes in real-time. However, this approach can be time-consuming if you need to make changes to multiple records, and it may be more difficult to identify errors in a large dataset.

+ +

Ultimately, the best approach will depend on the specific situation and the nature of the errors that need to be fixed.

+ <% end %> + + <%= f.govuk_collection_radio_buttons :choice, + @form.options, + :id, + :name, + legend: { hidden: true } %> + + <%= f.govuk_submit %> + <% end %> +
+
diff --git a/config/locales/en.yml b/config/locales/en.yml index bcfe32f46..b6735e3b5 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -68,6 +68,11 @@ en: attributes: needstype: blank: You must answer needs type + forms/bulk_upload_lettings_resume/fix_choice: + attributes: + choice: + blank: You must select how would you like to fix errors + inclusion: You must select one of the following options for how would like to fix errors activerecord: errors: diff --git a/config/routes.rb b/config/routes.rb index eeadffa03..1f00815c8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -144,6 +144,15 @@ Rails.application.routes.draw do end end + resources :bulk_upload_lettings_resume, path: "bulk-upload-resume", only: %i[show update] do + member do + get :start + + get "*page", to: "bulk_upload_lettings_resume#show", as: "page" + patch "*page", to: "bulk_upload_lettings_resume#update" + end + end + get "update-logs", to: "lettings_logs#update_logs" end diff --git a/db/migrate/20230331094840_add_status_cache_to_lettings_log.rb b/db/migrate/20230331094840_add_status_cache_to_lettings_log.rb new file mode 100644 index 000000000..e815b15ad --- /dev/null +++ b/db/migrate/20230331094840_add_status_cache_to_lettings_log.rb @@ -0,0 +1,5 @@ +class AddStatusCacheToLettingsLog < ActiveRecord::Migration[7.0] + def change + add_column :lettings_logs, :status_cache, :integer, null: false, default: 0 + end +end diff --git a/db/schema.rb b/db/schema.rb index 225129dfd..2c00fb921 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_03_20_084057) do +ActiveRecord::Schema[7.0].define(version: 2023_03_31_094840) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -287,6 +287,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_20_084057) do t.string "town_or_city" t.string "county" t.integer "carehome_charges_value_check" + t.integer "status_cache", default: 0, null: false t.index ["bulk_upload_id"], name: "index_lettings_logs_on_bulk_upload_id" t.index ["created_by_id"], name: "index_lettings_logs_on_created_by_id" t.index ["location_id"], name: "index_lettings_logs_on_location_id" diff --git a/spec/fixtures/files/lettings_logs_download.csv b/spec/fixtures/files/lettings_logs_download.csv index 463f81d3c..b9aefc3c9 100644 --- a/spec/fixtures/files/lettings_logs_download.csv +++ b/spec/fixtures/files/lettings_logs_download.csv @@ -1,2 +1,2 @@ -id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,rent_value_check,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate -{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,No,DLUHC,DLUHC,2021,Supported housing,,2 October 2021,London Affordable Rent,,,,,,,,,,,,,,,,,,,,No,,,,,No,Westminster,E09000033,,SE1 1TE,,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,6,{scheme_code},{scheme_service_name},{scheme_sensitive},Missing,No,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,Bungalow,Fitted with equipment and adaptations,Westminster,{location_startdate} +id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,rent_value_check,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate +{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,No,DLUHC,DLUHC,2021,Supported housing,,2 October 2021,London Affordable Rent,,,,,,,,,,,,,,,,,,,,No,,,,,No,Westminster,E09000033,,SE1 1TE,,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,6,{scheme_code},{scheme_service_name},{scheme_sensitive},Missing,No,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,Bungalow,Fitted with equipment and adaptations,Westminster,{location_startdate} diff --git a/spec/fixtures/files/lettings_logs_download_codes_only.csv b/spec/fixtures/files/lettings_logs_download_codes_only.csv index 6d15538ef..dec2664b5 100644 --- a/spec/fixtures/files/lettings_logs_download_codes_only.csv +++ b/spec/fixtures/files/lettings_logs_download_codes_only.csv @@ -1,2 +1,2 @@ -id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,rent_value_check,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate -{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,false,DLUHC,DLUHC,2021,2,,2 October 2021,2,,,,,,,,,,,,,,,,,,,,false,,,,,false,Westminster,E09000033,,SE1 1TE,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,6,{scheme_code},{scheme_service_name},{scheme_sensitive},0,1,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,6,A,Westminster,{location_startdate} +id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,rent_value_check,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate +{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,false,DLUHC,DLUHC,2021,2,,2 October 2021,2,,,,,,,,,,,,,,,,,,,,false,,,,,false,Westminster,E09000033,,SE1 1TE,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,6,{scheme_code},{scheme_service_name},{scheme_sensitive},0,1,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,6,A,Westminster,{location_startdate} diff --git a/spec/jobs/email_csv_job_spec.rb b/spec/jobs/email_csv_job_spec.rb index 04776aac7..29dceff59 100644 --- a/spec/jobs/email_csv_job_spec.rb +++ b/spec/jobs/email_csv_job_spec.rb @@ -61,6 +61,7 @@ describe EmailCsvJob do context "when writing to S3" do before do FactoryBot.create_list(:lettings_log, 4, owning_organisation: other_organisation) + FactoryBot.create(:lettings_log, owning_organisation: other_organisation, status: "pending", skip_update_status: true) end def expect_csv diff --git a/spec/mailers/bulk_upload_mailer_spec.rb b/spec/mailers/bulk_upload_mailer_spec.rb index 2e4337328..cd2c4767d 100644 --- a/spec/mailers/bulk_upload_mailer_spec.rb +++ b/spec/mailers/bulk_upload_mailer_spec.rb @@ -29,7 +29,7 @@ RSpec.describe BulkUploadMailer do it "sends correctly formed email" do expect(notify_client).to receive(:send_email).with( email_address: bulk_upload.user.email, - template_id: described_class::BULK_UPLOAD_FAILED_FILE_SETUP_ERROR_TEMPLATE_ID, + template_id: described_class::FAILED_FILE_SETUP_ERROR_TEMPLATE_ID, personalisation: { filename: bulk_upload.filename, upload_timestamp: bulk_upload.created_at.to_fs(:govuk_date_and_time), @@ -48,7 +48,7 @@ RSpec.describe BulkUploadMailer do it "sends correctly formed email" do expect(notify_client).to receive(:send_email).with( email_address: user.email, - template_id: described_class::BULK_UPLOAD_COMPLETE_TEMPLATE_ID, + template_id: described_class::COMPLETE_TEMPLATE_ID, personalisation: { title: "You’ve successfully uploaded 0 logs", filename: bulk_upload.filename, @@ -66,7 +66,7 @@ RSpec.describe BulkUploadMailer do it "sends correctly formed email" do expect(notify_client).to receive(:send_email).with( email_address: user.email, - template_id: described_class::BULK_UPLOAD_FAILED_SERVICE_ERROR_TEMPLATE_ID, + template_id: described_class::FAILED_SERVICE_ERROR_TEMPLATE_ID, personalisation: { filename: bulk_upload.filename, upload_timestamp: bulk_upload.created_at.to_fs(:govuk_date_and_time), @@ -94,7 +94,7 @@ RSpec.describe BulkUploadMailer do it "sends correctly formed email" do expect(notify_client).to receive(:send_email).with( email_address: bulk_upload.user.email, - template_id: described_class::BULK_UPLOAD_WITH_ERRORS_TEMPLATE_ID, + template_id: described_class::WITH_ERRORS_TEMPLATE_ID, personalisation: { title: "We found 1 log with errors", filename: bulk_upload.filename, @@ -119,7 +119,7 @@ RSpec.describe BulkUploadMailer do it "sends correctly formed email" do expect(notify_client).to receive(:send_email).with( email_address: user.email, - template_id: described_class::BULK_UPLOAD_FAILED_CSV_ERRORS_TEMPLATE_ID, + template_id: described_class::FAILED_CSV_ERRORS_TEMPLATE_ID, personalisation: { filename: bulk_upload.filename, upload_timestamp: bulk_upload.created_at.to_fs(:govuk_date_and_time), diff --git a/spec/models/organisation_spec.rb b/spec/models/organisation_spec.rb index 0b69fd504..362a7e2a9 100644 --- a/spec/models/organisation_spec.rb +++ b/spec/models/organisation_spec.rb @@ -169,11 +169,6 @@ RSpec.describe Organisation, type: :model do it "has lettings logs" do expect(organisation.lettings_logs.to_a).to match_array([owned_lettings_log, managed_lettings_log]) end - - it "has lettings log status helper methods" do - expect(organisation.completed_lettings_logs.to_a).to eq([owned_lettings_log]) - expect(organisation.not_completed_lettings_logs.to_a).to eq([managed_lettings_log]) - end end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index b285a55e7..8d9407dc2 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -36,11 +36,6 @@ RSpec.describe User, type: :model do expect(user.lettings_logs.to_a).to match_array([owned_lettings_log, managed_lettings_log]) end - it "has lettings log status helper methods" do - expect(user.completed_lettings_logs.to_a).to match_array([owned_lettings_log]) - expect(user.not_completed_lettings_logs.to_a).to match_array([managed_lettings_log]) - end - it "has a role" do expect(user.role).to eq("data_provider") expect(user.data_provider?).to be true diff --git a/spec/requests/bulk_upload_lettings_resume_controller_spec.rb b/spec/requests/bulk_upload_lettings_resume_controller_spec.rb new file mode 100644 index 000000000..5529a13db --- /dev/null +++ b/spec/requests/bulk_upload_lettings_resume_controller_spec.rb @@ -0,0 +1,84 @@ +require "rails_helper" + +RSpec.describe BulkUploadLettingsResumeController, type: :request do + let(:user) { create(:user) } + let(:bulk_upload) { create(:bulk_upload, :lettings, user:, bulk_upload_errors:) } + let(:bulk_upload_errors) { create_list(:bulk_upload_error, 2) } + + before do + sign_in user + end + + describe "GET /lettings-logs/bulk-upload-resume/:ID/start" do + it "redirects to choice page" do + get "/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/start" + + expect(response).to redirect_to("/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/fix-choice") + end + end + + describe "GET /lettings-logs/bulk-upload-resume/:ID/fix-choice" do + it "renders the page correctly" do + get "/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/fix-choice" + + expect(response).to be_successful + + expect(response.body).to include("Bulk upload for lettings") + expect(response.body).to include("2022/23") + expect(response.body).to include("How would you like to fix 2 errors?") + expect(response.body).to include(bulk_upload.filename) + end + end + + describe "PATCH /lettings-logs/bulk-upload-resume/:ID/fix-choice" do + context "when no option selected" do + it "renders error message" do + patch "/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/fix-choice" + + expect(response).to be_successful + + expect(response.body).to include("You must select") + end + end + + context "when upload again selected" do + it "sends them to relevant report" do + patch "/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/fix-choice", params: { form: { choice: "upload-again" } } + + expect(response).to redirect_to("/lettings-logs/bulk-upload-results/#{bulk_upload.id}") + end + end + + context "when fix inline selected" do + it "sends them to confirm choice" do + patch "/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/fix-choice", params: { form: { choice: "create-fix-inline" } } + + expect(response).to redirect_to("/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/confirm") + end + end + end + + describe "GET /lettings-logs/bulk-upload-resume/:ID/confirm" do + it "renders page" do + get "/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/confirm" + + expect(response).to be_successful + + expect(response.body).to include("Are you sure") + end + end + + describe "PATCH /lettings-logs/bulk-upload-resume/:ID/confirm" do + let(:mock_processor) { instance_double(BulkUpload::Processor, approve: nil) } + + it "approves logs for creation" do + allow(BulkUpload::Processor).to receive(:new).with(bulk_upload:).and_return(mock_processor) + + patch "/lettings-logs/bulk-upload-resume/#{bulk_upload.id}/confirm" + + expect(mock_processor).to have_received(:approve) + + expect(response).to redirect_to("/lettings-logs/bulk-upload-results/#{bulk_upload.id}/resume") + end + end +end diff --git a/spec/requests/form_controller_spec.rb b/spec/requests/form_controller_spec.rb index 9787ce097..eb47fdb87 100644 --- a/spec/requests/form_controller_spec.rb +++ b/spec/requests/form_controller_spec.rb @@ -321,6 +321,23 @@ RSpec.describe FormController, type: :request do get "/sales-logs/#{log.id}/review", headers: headers, params: { sales_log: true } expect(response.body).to match("Review sales log") end + + context "when log is pending" do + let(:pending_log) do + create( + :lettings_log, + owning_organisation: organisation, + created_by: user, + status: "pending", + skip_update_status: true, + ) + end + + it "does not render pending log and returns 404" do + get "/lettings-logs/#{pending_log.id}/review", headers: headers, params: {} + expect(response).to be_not_found + end + end end context "when viewing a user dependent page" do diff --git a/spec/requests/lettings_logs_controller_spec.rb b/spec/requests/lettings_logs_controller_spec.rb index 23df8e1b8..d95e0a3fd 100644 --- a/spec/requests/lettings_logs_controller_spec.rb +++ b/spec/requests/lettings_logs_controller_spec.rb @@ -224,6 +224,15 @@ RSpec.describe LettingsLogsController, type: :request do tenancycode: "UA984", ) end + let!(:pending_lettings_log) do + FactoryBot.create( + :lettings_log, + created_by: user, + tenancycode: "LC999", + status: "pending", + skip_update_status: true, + ) + end context "when displaying a collection of logs" do let(:headers) { { "Accept" => "text/html" } } @@ -261,6 +270,7 @@ RSpec.describe LettingsLogsController, type: :request do get "/lettings-logs", headers:, params: {} expect(page).to have_content("LC783") expect(page).to have_content("UA984") + expect(page).not_to have_content(pending_lettings_log.tenancycode) end it "displays CSV download links with the correct paths" do @@ -841,6 +851,24 @@ RSpec.describe LettingsLogsController, type: :request do end end + context "when viewing a pending log" do + let(:completed_lettings_log) do + FactoryBot.create( + :lettings_log, + :completed, + owning_organisation: user.organisation, + managing_organisation: user.organisation, + created_by: user, + status: "pending", + skip_update_status: true, + ) + end + + it "returns 404" do + expect(response).to have_http_status(:not_found) + end + end + context "when editing a lettings log" do let(:headers) { { "Accept" => "text/html" } } @@ -1319,9 +1347,12 @@ RSpec.describe LettingsLogsController, type: :request do end context "when a lettings log deletion fails" do + let(:mock_scope) { instance_double("LettingsLog::ActiveRecord_Relation", find_by: lettings_log) } + before do - allow(LettingsLog).to receive(:find_by).and_return(lettings_log) + allow(LettingsLog).to receive(:visible).and_return(mock_scope) allow(lettings_log).to receive(:delete).and_return(false) + delete "/lettings-logs/#{id}", headers: end diff --git a/spec/requests/organisations_controller_spec.rb b/spec/requests/organisations_controller_spec.rb index e7cb7fea3..15cfba390 100644 --- a/spec/requests/organisations_controller_spec.rb +++ b/spec/requests/organisations_controller_spec.rb @@ -591,6 +591,7 @@ RSpec.describe OrganisationsController, type: :request do before do FactoryBot.create_list(:lettings_log, number_of_org1_lettings_logs, created_by: user) + FactoryBot.create(:lettings_log, created_by: user, status: "pending", skip_update_status: true) FactoryBot.create_list(:lettings_log, number_of_org2_lettings_logs, created_by: nil, owning_organisation_id: unauthorised_organisation.id, managing_organisation_id: unauthorised_organisation.id) get "/organisations/#{organisation.id}/lettings-logs", headers:, params: {} @@ -598,7 +599,8 @@ RSpec.describe OrganisationsController, type: :request do it "only shows logs for that organisation" do expect(page).to have_content("#{number_of_org1_lettings_logs} total logs") - organisation.lettings_logs.map(&:id).each do |lettings_log_id| + + organisation.lettings_logs.visible.map(&:id).each do |lettings_log_id| expect(page).to have_link lettings_log_id.to_s, href: "/lettings-logs/#{lettings_log_id}" end @@ -1155,6 +1157,7 @@ RSpec.describe OrganisationsController, type: :request do before do FactoryBot.create_list(:lettings_log, 2, owning_organisation: organisation) + FactoryBot.create(:lettings_log, owning_organisation: organisation, status: "pending", skip_update_status: true) FactoryBot.create_list(:lettings_log, 2, owning_organisation: other_organisation) end diff --git a/spec/requests/sales_logs_controller_spec.rb b/spec/requests/sales_logs_controller_spec.rb index 14341cd33..e400366df 100644 --- a/spec/requests/sales_logs_controller_spec.rb +++ b/spec/requests/sales_logs_controller_spec.rb @@ -159,6 +159,22 @@ RSpec.describe SalesLogsController, type: :request do end end + context "when there is a pending log" do + let!(:invisible_log) do + FactoryBot.create( + :sales_log, + owning_organisation: organisation, + status: "pending", + skip_update_status: true, + ) + end + + it "does not render pending logs" do + get "/sales-logs", headers: headers, params: {} + expect(page).not_to have_content(invisible_log.id) + end + end + context "when filtering" do context "with status filter" do let(:organisation_2) { FactoryBot.create(:organisation) } diff --git a/spec/services/bulk_upload/lettings/log_creator_spec.rb b/spec/services/bulk_upload/lettings/log_creator_spec.rb index 8b320c35f..2f05a0ba5 100644 --- a/spec/services/bulk_upload/lettings/log_creator_spec.rb +++ b/spec/services/bulk_upload/lettings/log_creator_spec.rb @@ -15,6 +15,11 @@ RSpec.describe BulkUpload::Lettings::LogCreator do expect { service.call }.to change(LettingsLog, :count) end + it "create a log with pending status" do + service.call + expect(LettingsLog.last.status).to eql("pending") + end + it "associates log with bulk upload" do service.call @@ -77,6 +82,23 @@ RSpec.describe BulkUpload::Lettings::LogCreator do end end + context "when pre-creating logs" do + subject(:service) { described_class.new(bulk_upload:, path:) } + + it "creates a new log" do + expect { service.call }.to change(LettingsLog, :count) + end + + it "creates a log with correct states" do + service.call + + last_log = LettingsLog.last + + expect(last_log.status).to eql("pending") + expect(last_log.status_cache).to eql("completed") + end + end + context "when valid csv with existing log" do xit "what should happen?" end diff --git a/spec/services/bulk_upload/lettings/validator_spec.rb b/spec/services/bulk_upload/lettings/validator_spec.rb index e5ac5350c..46c01fa91 100644 --- a/spec/services/bulk_upload/lettings/validator_spec.rb +++ b/spec/services/bulk_upload/lettings/validator_spec.rb @@ -273,58 +273,5 @@ RSpec.describe BulkUpload::Lettings::Validator do end end end - - context "when a column has error rate above absolute threshold" do - before do - stub_const("BulkUpload::Lettings::Validator::COLUMN_ABSOLUTE_ERROR_THRESHOLD", 1) - end - - context "when a column is over 60% error threshold" do - let(:log_1) { build(:lettings_log, :completed, renttype: 1, created_by: user) } - let(:log_2) { build(:lettings_log, renttype: 2, created_by: user, builtype: nil, startdate: Time.zone.local(2022, 5, 1)) } - let(:log_3) { build(:lettings_log, renttype: 2, created_by: user, builtype: nil, startdate: Time.zone.local(2022, 5, 1)) } - let(:log_4) { build(:lettings_log, renttype: 2, created_by: user, builtype: nil, startdate: Time.zone.local(2022, 5, 1)) } - let(:log_5) { build(:lettings_log, renttype: 2, created_by: user, builtype: nil, startdate: Time.zone.local(2022, 5, 1)) } - - before do - file.write(BulkUpload::LogToCsv.new(log: log_1, line_ending: "\r\n", col_offset: 0).to_2022_csv_row) - file.write(BulkUpload::LogToCsv.new(log: log_2, line_ending: "\r\n", col_offset: 0).to_2022_csv_row) - file.write(BulkUpload::LogToCsv.new(log: log_3, line_ending: "\r\n", col_offset: 0).to_2022_csv_row) - file.write(BulkUpload::LogToCsv.new(log: log_4, line_ending: "\r\n", col_offset: 0).to_2022_csv_row) - file.write(BulkUpload::LogToCsv.new(log: log_5, line_ending: "\r\n", col_offset: 0).to_2022_csv_row) - file.close - end - - it "returns false" do - validator.call - expect(validator).not_to be_create_logs - end - end - - context "when a column is under 60% error threshold" do - let(:log_1) { build(:lettings_log, :completed, renttype: 1, created_by: user) } - let(:log_2) { build(:lettings_log, :completed, renttype: 1, created_by: user) } - let(:log_3) { build(:lettings_log, renttype: 2, created_by: user, builtype: nil, startdate: Time.zone.local(2022, 5, 1)) } - let(:log_4) { build(:lettings_log, renttype: 2, created_by: user, builtype: nil, startdate: Time.zone.local(2022, 5, 1)) } - let(:log_5) { build(:lettings_log, renttype: 2, created_by: user, builtype: nil, startdate: Time.zone.local(2022, 5, 1)) } - - before do - overrides = { age1: 50, age2: "R", age3: "R", age4: "4", age5: "R", age6: "R", age7: "R", age8: "R" } - - file.write(BulkUpload::LogToCsv.new(log: log_1, line_ending: "\r\n", col_offset: 0, overrides:).to_2022_csv_row) - file.write(BulkUpload::LogToCsv.new(log: log_2, line_ending: "\r\n", col_offset: 0, overrides:).to_2022_csv_row) - file.write(BulkUpload::LogToCsv.new(log: log_3, line_ending: "\r\n", col_offset: 0, overrides:).to_2022_csv_row) - file.write(BulkUpload::LogToCsv.new(log: log_4, line_ending: "\r\n", col_offset: 0, overrides:).to_2022_csv_row) - file.write(BulkUpload::LogToCsv.new(log: log_5, line_ending: "\r\n", col_offset: 0, overrides:).to_2022_csv_row) - - file.close - end - - it "returns true" do - validator.call - expect(validator).to be_create_logs - end - end - end end end diff --git a/spec/services/bulk_upload/processor_spec.rb b/spec/services/bulk_upload/processor_spec.rb index bd9e1a478..19bb1e73f 100644 --- a/spec/services/bulk_upload/processor_spec.rb +++ b/spec/services/bulk_upload/processor_spec.rb @@ -130,120 +130,111 @@ RSpec.describe BulkUpload::Processor do end end - context "when processing a bulk upload with errors but below threshold (therefore creates logs)" do + context "when processing a bulk with perfect data" do let(:mock_downloader) do instance_double( BulkUpload::Downloader, call: nil, - path: file_fixture("2022_23_lettings_bulk_upload.csv"), + path:, delete_local_file!: nil, ) end - let(:mock_validator) do - instance_double( - BulkUpload::Lettings::Validator, - invalid?: false, - call: nil, - any_setup_errors?: false, - create_logs?: true, + let(:file) { Tempfile.new } + let(:path) { file.path } + + let(:log) do + build( + :lettings_log, + :completed, + renttype: 3, + age1: 20, + owning_organisation: owning_org, + managing_organisation: owning_org, + created_by: nil, + national: 18, + waityear: 9, + joint: 2, + tenancy: 9, + ppcodenk: 0, + voiddate: nil, + mrcdate: nil, + startdate: Date.new(2022, 10, 1), + tenancylength: nil, ) end before do + file.write(BulkUpload::LogToCsv.new(log:, col_offset: 0).to_2022_csv_row) + file.rewind + allow(BulkUpload::Downloader).to receive(:new).with(bulk_upload:).and_return(mock_downloader) - allow(BulkUpload::Lettings::Validator).to receive(:new).and_return(mock_validator) end - it "deletes the local file afterwards" do - processor.call - - expect(mock_downloader).to have_received(:delete_local_file!) + it "creates logs as not pending" do + expect { processor.call }.to change(LettingsLog.completed, :count).by(1) end - it "sends fix errors email" do + it "sends success email" do mail_double = instance_double("ActionMailer::MessageDelivery", deliver_later: nil) - allow(BulkUploadMailer).to receive(:send_bulk_upload_with_errors_mail).and_return(mail_double) + allow(BulkUploadMailer).to receive(:send_bulk_upload_complete_mail).and_return(mail_double) processor.call - expect(BulkUploadMailer).to have_received(:send_bulk_upload_with_errors_mail) + expect(BulkUploadMailer).to have_received(:send_bulk_upload_complete_mail) expect(mail_double).to have_received(:deliver_later) end - - it "does not send success email" do - allow(BulkUploadMailer).to receive(:send_bulk_upload_complete_mail).and_call_original - - processor.call - - expect(BulkUploadMailer).not_to have_received(:send_bulk_upload_complete_mail) - end end - context "when processing a bulk upload with errors but above threshold (therefore does not create logs)" do + context "when a bulk upload has an in progress log" do let(:mock_downloader) do instance_double( BulkUpload::Downloader, call: nil, - path: file_fixture("2022_23_lettings_bulk_upload.csv"), + path:, delete_local_file!: nil, ) end - let(:mock_validator) do - instance_double( - BulkUpload::Lettings::Validator, - invalid?: false, - call: nil, - any_setup_errors?: false, - create_logs?: false, + let(:file) { Tempfile.new } + let(:path) { file.path } + + let(:log) do + LettingsLog.new( + lettype: 2, + renttype: 3, + owning_organisation: owning_org, + managing_organisation: owning_org, + startdate: Time.zone.local(2022, 10, 1), + renewal: 2, ) end before do + file.write(BulkUpload::LogToCsv.new(log:, col_offset: 0).to_2022_csv_row) + file.rewind + allow(BulkUpload::Downloader).to receive(:new).with(bulk_upload:).and_return(mock_downloader) - allow(BulkUpload::Lettings::Validator).to receive(:new).and_return(mock_validator) end - it "deletes the local file afterwards" do - processor.call - - expect(mock_downloader).to have_received(:delete_local_file!) + it "creates pending log" do + expect { processor.call }.to change(LettingsLog.pending, :count).by(1) end - it "sends correct and upload again mail" do + it "sends how_fix_upload_mail" do mail_double = instance_double("ActionMailer::MessageDelivery", deliver_later: nil) - allow(BulkUploadMailer).to receive(:send_correct_and_upload_again_mail).and_return(mail_double) + allow(BulkUploadMailer).to receive(:send_how_fix_upload_mail).and_return(mail_double) processor.call - expect(BulkUploadMailer).to have_received(:send_correct_and_upload_again_mail) + expect(BulkUploadMailer).to have_received(:send_how_fix_upload_mail) expect(mail_double).to have_received(:deliver_later) end - - it "does not send fix errors email" do - allow(BulkUploadMailer).to receive(:send_bulk_upload_with_errors_mail).and_call_original - - processor.call - - expect(BulkUploadMailer).not_to have_received(:send_bulk_upload_with_errors_mail) - end - - it "does not send success email" do - allow(BulkUploadMailer).to receive(:send_bulk_upload_complete_mail).and_call_original - - processor.call - - expect(BulkUploadMailer).not_to have_received(:send_bulk_upload_complete_mail) - end end - context "when processing a bulk with perfect data" do - let(:file) { Tempfile.new } - let(:path) { file.path } - + context "when upload has no setup errors something blocks log creation" do let(:mock_downloader) do instance_double( BulkUpload::Downloader, @@ -253,24 +244,20 @@ RSpec.describe BulkUpload::Processor do ) end + let(:file) { Tempfile.new } + let(:path) { file.path } + + let(:other_user) { create(:user) } + let(:log) do - build( - :lettings_log, - :completed, + LettingsLog.new( + lettype: 2, renttype: 3, - age1: 20, owning_organisation: owning_org, managing_organisation: owning_org, - created_by: nil, - national: 18, - waityear: 9, - joint: 2, - tenancy: 9, - ppcodenk: 0, - voiddate: nil, - mrcdate: nil, - startdate: Date.new(2022, 10, 1), - tenancylength: nil, + startdate: Time.zone.local(2022, 10, 1), + renewal: 2, + created_by: other_user, # unaffiliated user ) end @@ -281,28 +268,26 @@ RSpec.describe BulkUpload::Processor do allow(BulkUpload::Downloader).to receive(:new).with(bulk_upload:).and_return(mock_downloader) end - it "creates logs" do - expect { processor.call }.to change(LettingsLog, :count).by(1) - end - - it "does not send fix errors email" do - allow(BulkUploadMailer).to receive(:send_bulk_upload_with_errors_mail).and_call_original - - processor.call - - expect(BulkUploadMailer).not_to have_received(:send_bulk_upload_with_errors_mail) - end - - it "sends success email" do + it "sends correct_and_upload_again_mail" do mail_double = instance_double("ActionMailer::MessageDelivery", deliver_later: nil) - allow(BulkUploadMailer).to receive(:send_bulk_upload_complete_mail).and_return(mail_double) + allow(BulkUploadMailer).to receive(:send_correct_and_upload_again_mail).and_return(mail_double) processor.call - expect(BulkUploadMailer).to have_received(:send_bulk_upload_complete_mail) + expect(BulkUploadMailer).to have_received(:send_correct_and_upload_again_mail) expect(mail_double).to have_received(:deliver_later) end end end + + describe "#approve" do + let!(:log) { create(:lettings_log, bulk_upload:, status: "pending", skip_update_status: true, status_cache: "not_started") } + + it "makes pending logs no longer pending" do + expect(log.status).to eql("pending") + processor.approve + expect(log.reload.status).to eql("not_started") + end + end end diff --git a/spec/services/csv/lettings_log_csv_service_spec.rb b/spec/services/csv/lettings_log_csv_service_spec.rb index 95d24e0da..1a752035d 100644 --- a/spec/services/csv/lettings_log_csv_service_spec.rb +++ b/spec/services/csv/lettings_log_csv_service_spec.rb @@ -209,6 +209,7 @@ RSpec.describe Csv::LettingsLogCsvService do address_line2 town_or_city county + status_cache unittype_sh scheme_code scheme_service_name diff --git a/spec/services/exports/lettings_log_export_service_spec.rb b/spec/services/exports/lettings_log_export_service_spec.rb index dbc4b246b..6580a6acd 100644 --- a/spec/services/exports/lettings_log_export_service_spec.rb +++ b/spec/services/exports/lettings_log_export_service_spec.rb @@ -59,6 +59,35 @@ RSpec.describe Exports::LettingsLogExportService do end end + context "when one pending lettings log exists" do + before do + FactoryBot.create( + :lettings_log, + :completed, + status: "pending", + skip_update_status: true, + propcode: "123", + ppostcode_full: "SE2 6RT", + postcode_full: "NW1 5TY", + tenancycode: "BZ737", + startdate: Time.zone.local(2022, 2, 2, 10, 36, 49), + voiddate: Time.zone.local(2019, 11, 3), + mrcdate: Time.zone.local(2020, 5, 5, 10, 36, 49), + tenancylength: 5, + underoccupation_benefitcap: 4, + ) + end + + it "generates a master manifest with CSV headers but no data" do + actual_content = nil + expected_content = "zip-name,date-time zipped folder generated,zip-file-uri\n" + allow(storage_service).to receive(:write_file).with(expected_master_manifest_filename, any_args) { |_, arg2| actual_content = arg2&.string } + + export_service.export_xml_lettings_logs + expect(actual_content).to eq(expected_content) + end + end + context "and one lettings log is available for export" do let!(:lettings_log) { FactoryBot.create(:lettings_log, :completed, propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", tenancycode: "BZ737", startdate: Time.zone.local(2022, 2, 2, 10, 36, 49), voiddate: Time.zone.local(2019, 11, 3), mrcdate: Time.zone.local(2020, 5, 5, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4) } diff --git a/spec/services/imports/lettings_logs_import_service_spec.rb b/spec/services/imports/lettings_logs_import_service_spec.rb index 2acdadf7b..50a285ed9 100644 --- a/spec/services/imports/lettings_logs_import_service_spec.rb +++ b/spec/services/imports/lettings_logs_import_service_spec.rb @@ -532,11 +532,11 @@ RSpec.describe Imports::LettingsLogsImportService do end it "intercepts the relevant validation error" do - expect(logger).to receive(:warn).with(/Removing brent with error: Enter a total charge that is at least £10 per week, Answer either the ‘household rent and charges’ question or ‘is this accommodation a care home‘, or select ‘no’ for ‘does the household pay rent or charges for the accommodation?/) - expect(logger).to receive(:warn).with(/Removing scharge with error: Enter a total charge that is at least £10 per week, Answer either the ‘household rent and charges’ question or ‘is this accommodation a care home‘, or select ‘no’ for ‘does the household pay rent or charges for the accommodation?/) - expect(logger).to receive(:warn).with(/Removing pscharge with error: Enter a total charge that is at least £10 per week, Answer either the ‘household rent and charges’ question or ‘is this accommodation a care home‘, or select ‘no’ for ‘does the household pay rent or charges for the accommodation?/) - expect(logger).to receive(:warn).with(/Removing supcharg with error: Enter a total charge that is at least £10 per week, Answer either the ‘household rent and charges’ question or ‘is this accommodation a care home‘, or select ‘no’ for ‘does the household pay rent or charges for the accommodation?/) - expect(logger).to receive(:warn).with(/Removing tcharge with error: Enter a total charge that is at least £10 per week, Answer either the ‘household rent and charges’ question or ‘is this accommodation a care home‘, or select ‘no’ for ‘does the household pay rent or charges for the accommodation?/) + expect(logger).to receive(:warn).with("Log 0b4a68df-30cc-474a-93c0-a56ce8fdad3b: Removing brent with error: Answer either the ‘household rent and charges’ question or ‘is this accommodation a care home‘, or select ‘no’ for ‘does the household pay rent or charges for the accommodation?’, Enter a total charge that is at least £10 per week") + expect(logger).to receive(:warn).with("Log 0b4a68df-30cc-474a-93c0-a56ce8fdad3b: Removing scharge with error: Answer either the ‘household rent and charges’ question or ‘is this accommodation a care home‘, or select ‘no’ for ‘does the household pay rent or charges for the accommodation?’, Enter a total charge that is at least £10 per week") + expect(logger).to receive(:warn).with("Log 0b4a68df-30cc-474a-93c0-a56ce8fdad3b: Removing pscharge with error: Answer either the ‘household rent and charges’ question or ‘is this accommodation a care home‘, or select ‘no’ for ‘does the household pay rent or charges for the accommodation?’, Enter a total charge that is at least £10 per week") + expect(logger).to receive(:warn).with("Log 0b4a68df-30cc-474a-93c0-a56ce8fdad3b: Removing supcharg with error: Answer either the ‘household rent and charges’ question or ‘is this accommodation a care home‘, or select ‘no’ for ‘does the household pay rent or charges for the accommodation?’, Enter a total charge that is at least £10 per week") + expect(logger).to receive(:warn).with("Log 0b4a68df-30cc-474a-93c0-a56ce8fdad3b: Removing tcharge with error: Answer either the ‘household rent and charges’ question or ‘is this accommodation a care home‘, or select ‘no’ for ‘does the household pay rent or charges for the accommodation?’, Enter a total charge that is at least £10 per week") expect { lettings_log_service.send(:create_log, lettings_log_xml) } .not_to raise_error end @@ -560,11 +560,11 @@ RSpec.describe Imports::LettingsLogsImportService do end it "intercepts the relevant validation error" do - expect(logger).to receive(:warn).with(/Removing brent with error: Enter an amount above 0, Enter a value for the service charge between £0 and £480 per week if the landlord is a private registered provider and it is a supported housing letting, Service charge must be at least £0 every week/) - expect(logger).to receive(:warn).with(/Removing scharge with error: Enter an amount above 0, Enter a value for the service charge between £0 and £480 per week if the landlord is a private registered provider and it is a supported housing letting, Service charge must be at least £0 every week/) - expect(logger).to receive(:warn).with(/Removing pscharge with error: Enter an amount above 0, Enter a value for the service charge between £0 and £480 per week if the landlord is a private registered provider and it is a supported housing letting, Service charge must be at least £0 every week/) - expect(logger).to receive(:warn).with(/Removing supcharg with error: Enter an amount above 0, Enter a value for the service charge between £0 and £480 per week if the landlord is a private registered provider and it is a supported housing letting, Service charge must be at least £0 every week/) - expect(logger).to receive(:warn).with(/Removing tcharge with error: Enter an amount above 0, Enter a value for the service charge between £0 and £480 per week if the landlord is a private registered provider and it is a supported housing letting, Service charge must be at least £0 every week/) + expect(logger).to receive(:warn).with(/Removing brent with error: Enter a value for the service charge between £0 and £480 per week if the landlord is a private registered provider and it is a supported housing letting, Enter an amount above 0, Service charge must be at least £0 every week/) + expect(logger).to receive(:warn).with(/Removing scharge with error: Enter a value for the service charge between £0 and £480 per week if the landlord is a private registered provider and it is a supported housing letting, Enter an amount above 0, Service charge must be at least £0 every week/) + expect(logger).to receive(:warn).with(/Removing pscharge with error: Enter a value for the service charge between £0 and £480 per week if the landlord is a private registered provider and it is a supported housing letting, Enter an amount above 0, Service charge must be at least £0 every week/) + expect(logger).to receive(:warn).with(/Removing supcharg with error: Enter a value for the service charge between £0 and £480 per week if the landlord is a private registered provider and it is a supported housing letting, Enter an amount above 0, Service charge must be at least £0 every week/) + expect(logger).to receive(:warn).with(/Removing tcharge with error: Enter a value for the service charge between £0 and £480 per week if the landlord is a private registered provider and it is a supported housing letting, Enter an amount above 0, Service charge must be at least £0 every week/) expect { lettings_log_service.send(:create_log, lettings_log_xml) } .not_to raise_error end diff --git a/spec/support/bulk_upload/log_to_csv.rb b/spec/support/bulk_upload/log_to_csv.rb index a2d8c2f5f..55a199e7f 100644 --- a/spec/support/bulk_upload/log_to_csv.rb +++ b/spec/support/bulk_upload/log_to_csv.rb @@ -170,7 +170,7 @@ class BulkUpload::LogToCsv nil, # 110 log.owning_organisation&.old_visible_id, - nil, + log.created_by&.email, log.managing_organisation&.old_visible_id, leftreg, nil, From d0046256cbb35a879bd0043df62c6b4cd756fdf4 Mon Sep 17 00:00:00 2001 From: Phil Lee Date: Thu, 6 Apr 2023 10:20:51 +0100 Subject: [PATCH 4/7] CLDC-2170 Bulk upload summary tweaks (#1522) * show summary when on error threshold * remove bulk upload mailer template - this template is no longer used * setup errors do not consider threshold - return approprate intro test depending if there are setup errors or not --- ...oad_error_summary_table_component.html.erb | 4 ++ ...lk_upload_error_summary_table_component.rb | 11 ++++- app/mailers/bulk_upload_mailer.rb | 43 ++----------------- app/services/bulk_upload/processor.rb | 6 --- .../summary.html.erb | 4 -- ...load_error_summary_table_component_spec.rb | 27 ++++++++++++ spec/mailers/bulk_upload_mailer_spec.rb | 38 +--------------- 7 files changed, 44 insertions(+), 89 deletions(-) diff --git a/app/components/bulk_upload_error_summary_table_component.html.erb b/app/components/bulk_upload_error_summary_table_component.html.erb index 2f9643271..ab5254c0f 100644 --- a/app/components/bulk_upload_error_summary_table_component.html.erb +++ b/app/components/bulk_upload_error_summary_table_component.html.erb @@ -1,3 +1,7 @@ +

+ <%= intro %> +

+ <% sorted_errors.each do |error| %> <%= govuk_table do |table| %> <% table.head do |head| %> diff --git a/app/components/bulk_upload_error_summary_table_component.rb b/app/components/bulk_upload_error_summary_table_component.rb index 8dddb61d2..7b483077f 100644 --- a/app/components/bulk_upload_error_summary_table_component.rb +++ b/app/components/bulk_upload_error_summary_table_component.rb @@ -15,7 +15,7 @@ class BulkUploadErrorSummaryTableComponent < ViewComponent::Base @sorted_errors ||= setup_errors.presence || bulk_upload .bulk_upload_errors .group(:col, :field, :error) - .having("count(*) > ?", display_threshold) + .having("count(*) >= ?", display_threshold) .count .sort_by { |el| el[0][0].rjust(3, "0") } end @@ -24,6 +24,14 @@ class BulkUploadErrorSummaryTableComponent < ViewComponent::Base sorted_errors.present? end + def intro + if setup_errors.present? + "This summary shows important questions that have errors. See full error report for more details." + else + "This summary shows questions that have more than #{BulkUploadErrorSummaryTableComponent::DISPLAY_THRESHOLD - 1} errors. See full error report for more details." + end + end + private def setup_errors @@ -31,7 +39,6 @@ private .bulk_upload_errors .where(category: "setup") .group(:col, :field, :error) - .having("count(*) > ?", display_threshold) .count .sort_by { |el| el[0][0].rjust(3, "0") } end diff --git a/app/mailers/bulk_upload_mailer.rb b/app/mailers/bulk_upload_mailer.rb index 2d0b20a92..b6388213a 100644 --- a/app/mailers/bulk_upload_mailer.rb +++ b/app/mailers/bulk_upload_mailer.rb @@ -5,7 +5,6 @@ class BulkUploadMailer < NotifyMailer FAILED_CSV_ERRORS_TEMPLATE_ID = "e27abcd4-5295-48c2-b127-e9ee4b781b75".freeze FAILED_FILE_SETUP_ERROR_TEMPLATE_ID = "24c9f4c7-96ad-470a-ba31-eb51b7cbafd9".freeze FAILED_SERVICE_ERROR_TEMPLATE_ID = "c3f6288c-7a74-4e77-99ee-6c4a0f6e125a".freeze - WITH_ERRORS_TEMPLATE_ID = "eb539005-6234-404e-812d-167728cf4274".freeze HOW_FIX_UPLOAD_TEMPLATE_ID = "21a07b26-f625-4846-9f4d-39e30937aa24".freeze def send_how_fix_upload_mail(bulk_upload:) @@ -73,25 +72,12 @@ class BulkUploadMailer < NotifyMailer end def send_bulk_upload_failed_file_setup_error_mail(bulk_upload:) - bulk_upload_link = if bulk_upload.lettings? - start_bulk_upload_lettings_logs_url + bulk_upload_link = if BulkUploadErrorSummaryTableComponent.new(bulk_upload:).errors? + summary_bulk_upload_lettings_result_url(bulk_upload) else - start_bulk_upload_sales_logs_url + bulk_upload_lettings_result_url(bulk_upload) end - row_parser_class = bulk_upload.prefix_namespace::RowParser - - errors = bulk_upload - .bulk_upload_errors - .where(category: "setup") - .group(:col, :field) - .count - .keys - .sort_by { |_col, field| field } - .map do |col, field| - "- #{row_parser_class.question_for_field(field.to_sym)} (Column #{col})" - end - send_email( bulk_upload.user.email, FAILED_FILE_SETUP_ERROR_TEMPLATE_ID, @@ -100,7 +86,6 @@ class BulkUploadMailer < NotifyMailer upload_timestamp: bulk_upload.created_at.to_fs(:govuk_date_and_time), lettings_or_sales: bulk_upload.log_type, year_combo: bulk_upload.year_combo, - errors_list: errors.join("\n"), bulk_upload_link:, }, ) @@ -126,26 +111,4 @@ class BulkUploadMailer < NotifyMailer }, ) end - - def send_bulk_upload_with_errors_mail(bulk_upload:) - count = bulk_upload.logs.where.not(status: %w[completed]).count - - n_logs = pluralize(count, "log") - - title = "We found #{n_logs} with errors" - - error_description = "We created logs from your #{bulk_upload.year_combo} #{bulk_upload.log_type} data. There was a problem with #{count} of the logs. Click the below link to fix these logs." - - send_email( - bulk_upload.user.email, - WITH_ERRORS_TEMPLATE_ID, - { - title:, - filename: bulk_upload.filename, - upload_timestamp: bulk_upload.created_at.to_fs(:govuk_date_and_time), - error_description:, - summary_report_link: resume_bulk_upload_lettings_result_url(bulk_upload), - }, - ) - end end diff --git a/app/services/bulk_upload/processor.rb b/app/services/bulk_upload/processor.rb index 4fe449348..49d44261f 100644 --- a/app/services/bulk_upload/processor.rb +++ b/app/services/bulk_upload/processor.rb @@ -59,12 +59,6 @@ private .deliver_later end - def send_fix_errors_mail - BulkUploadMailer - .send_bulk_upload_with_errors_mail(bulk_upload:) - .deliver_later - end - def send_success_mail BulkUploadMailer .send_bulk_upload_complete_mail(user:, bulk_upload:) diff --git a/app/views/bulk_upload_lettings_results/summary.html.erb b/app/views/bulk_upload_lettings_results/summary.html.erb index a6072fe89..533e2af05 100644 --- a/app/views/bulk_upload_lettings_results/summary.html.erb +++ b/app/views/bulk_upload_lettings_results/summary.html.erb @@ -16,10 +16,6 @@
<%= govuk_tabs(title: "Error reports") do |c| %> <% c.with_tab(label: "Summary") do %> -

- This summary shows questions that have at least <%= BulkUploadErrorSummaryTableComponent::DISPLAY_THRESHOLD %> errors or more. See full error report for more details. -

- <%= render BulkUploadErrorSummaryTableComponent.new(bulk_upload: @bulk_upload) %> <% end %> diff --git a/spec/components/bulk_upload_error_summary_table_component_spec.rb b/spec/components/bulk_upload_error_summary_table_component_spec.rb index 32da38119..79be32901 100644 --- a/spec/components/bulk_upload_error_summary_table_component_spec.rb +++ b/spec/components/bulk_upload_error_summary_table_component_spec.rb @@ -30,6 +30,25 @@ RSpec.describe BulkUploadErrorSummaryTableComponent, type: :component do end end + context "when on threshold" do + before do + stub_const("BulkUploadErrorSummaryTableComponent::DISPLAY_THRESHOLD", 1) + + create(:bulk_upload_error, bulk_upload:, col: "A", row: 1) + end + + it "renders tables" do + result = render_inline(component) + expect(result).to have_selector("table", count: 1) + end + + it "renders intro with threshold" do + result = render_inline(component) + + expect(result).to have_content("This summary shows questions that have more than 0 errors. See full error report for more details.") + end + end + context "when there are 2 independent errors" do let!(:error_2) { create(:bulk_upload_error, bulk_upload:, col: "B", row: 2) } let!(:error_1) { create(:bulk_upload_error, bulk_upload:, col: "A", row: 1) } @@ -99,6 +118,8 @@ RSpec.describe BulkUploadErrorSummaryTableComponent, type: :component do before do create(:bulk_upload_error, bulk_upload:, col: "B", row: 2, category: nil) + + stub_const("BulkUploadErrorSummaryTableComponent::DISPLAY_THRESHOLD", 16) end it "only returns the setup errors" do @@ -117,6 +138,12 @@ RSpec.describe BulkUploadErrorSummaryTableComponent, type: :component do "1 error", ]) end + + it "renders intro with setup errors" do + result = render_inline(component) + + expect(result).to have_content("This summary shows important questions that have errors. See full error report for more details.") + end end end diff --git a/spec/mailers/bulk_upload_mailer_spec.rb b/spec/mailers/bulk_upload_mailer_spec.rb index cd2c4767d..20f78de4d 100644 --- a/spec/mailers/bulk_upload_mailer_spec.rb +++ b/spec/mailers/bulk_upload_mailer_spec.rb @@ -19,13 +19,6 @@ RSpec.describe BulkUploadMailer do create(:bulk_upload_error, bulk_upload:, col: "F", field: "field_5") end - let(:expected_errors) do - [ - "- What is the letting type? (Column A)", - "- Management group code (Column E)", - ] - end - it "sends correctly formed email" do expect(notify_client).to receive(:send_email).with( email_address: bulk_upload.user.email, @@ -35,8 +28,7 @@ RSpec.describe BulkUploadMailer do upload_timestamp: bulk_upload.created_at.to_fs(:govuk_date_and_time), lettings_or_sales: bulk_upload.log_type, year_combo: bulk_upload.year_combo, - errors_list: expected_errors.join("\n"), - bulk_upload_link: start_bulk_upload_lettings_logs_url, + bulk_upload_link: summary_bulk_upload_lettings_result_url(bulk_upload), }, ) @@ -81,34 +73,6 @@ RSpec.describe BulkUploadMailer do end end - context "when bulk upload has log which is not completed" do - before do - create(:lettings_log, :in_progress, bulk_upload:) - end - - describe "#send_bulk_upload_with_errors_mail" do - let(:error_description) do - "We created logs from your 2022/23 lettings data. There was a problem with 1 of the logs. Click the below link to fix these logs." - end - - it "sends correctly formed email" do - expect(notify_client).to receive(:send_email).with( - email_address: bulk_upload.user.email, - template_id: described_class::WITH_ERRORS_TEMPLATE_ID, - personalisation: { - title: "We found 1 log with errors", - filename: bulk_upload.filename, - upload_timestamp: bulk_upload.created_at.to_fs(:govuk_date_and_time), - error_description:, - summary_report_link: "http://localhost:3000/lettings-logs/bulk-upload-results/#{bulk_upload.id}/resume", - }, - ) - - mailer.send_bulk_upload_with_errors_mail(bulk_upload:) - end - end - end - describe "#send_correct_and_upload_again_mail" do context "when 2 columns with errors" do before do From 9bbc2c2e1712c370eaeedaaf638276cc1f5b5640 Mon Sep 17 00:00:00 2001 From: Arthur Campbell <51094020+arfacamble@users.noreply.github.com> Date: Thu, 6 Apr 2023 11:09:03 +0100 Subject: [PATCH 5/7] CLDC-1687 allow deactivation or reactivation of last year schemes and locations in crossover period (#1396) * create method to test whether we are currently in the crossover period and associated tests * update copy, use method for testing whether we are in the crossover period, remove focus from test file * reuse existing method to determine whether we are in a collection period * use the existing method and update validations * fix test broken by changes * update location in same way as scheme * create method on FormHandler that finds the start date of the earliest collection period * ensure that default deactivation and reactivation dates also reflect the changes * create tests for the new validations * lint correction * minor copy change * minor logic change * amend naming error after rebase conflict remove test that is no longer correct after change on another branch to functionality update a test after a copy change --- app/controllers/locations_controller.rb | 2 +- app/controllers/schemes_controller.rb | 2 +- app/models/form_handler.rb | 4 + app/models/location_deactivation_period.rb | 12 +-- app/models/scheme_deactivation_period.rb | 10 ++- app/views/locations/toggle_active.html.erb | 6 +- app/views/schemes/toggle_active.html.erb | 6 +- config/locales/en.yml | 6 +- spec/features/schemes_spec.rb | 4 +- .../location_deactivation_period_spec.rb | 83 +++++++++++++++++++ .../models/scheme_deactivation_period_spec.rb | 68 +++++++++++++++ 11 files changed, 182 insertions(+), 21 deletions(-) create mode 100644 spec/models/location_deactivation_period_spec.rb create mode 100644 spec/models/scheme_deactivation_period_spec.rb diff --git a/app/controllers/locations_controller.rb b/app/controllers/locations_controller.rb index 50102ee17..876f0e405 100644 --- a/app/controllers/locations_controller.rb +++ b/app/controllers/locations_controller.rb @@ -269,7 +269,7 @@ private if params[:location_deactivation_period].blank? return elsif params[:location_deactivation_period]["#{key}_type".to_sym] == "default" - return FormHandler.instance.current_collection_start_date + return FormHandler.instance.start_date_of_earliest_open_collection_period elsif params[:location_deactivation_period][key.to_sym].present? return params[:location_deactivation_period][key.to_sym] end diff --git a/app/controllers/schemes_controller.rb b/app/controllers/schemes_controller.rb index f248bffe5..e60620bc5 100644 --- a/app/controllers/schemes_controller.rb +++ b/app/controllers/schemes_controller.rb @@ -296,7 +296,7 @@ private if params[:scheme_deactivation_period].blank? return elsif params[:scheme_deactivation_period]["#{key}_type".to_sym] == "default" - return FormHandler.instance.current_collection_start_date + return FormHandler.instance.start_date_of_earliest_open_collection_period elsif params[:scheme_deactivation_period][key.to_sym].present? return params[:scheme_deactivation_period][key.to_sym] end diff --git a/app/models/form_handler.rb b/app/models/form_handler.rb index ab22e6bc5..ee84c3e32 100644 --- a/app/models/form_handler.rb +++ b/app/models/form_handler.rb @@ -77,6 +77,10 @@ class FormHandler form_mappings[current_collection_start_year - year] end + def start_date_of_earliest_open_collection_period + in_crossover_period? ? previous_collection_start_date : current_collection_start_date + end + def in_crossover_period?(now: Time.zone.now) lettings_in_crossover_period?(now:) || sales_in_crossover_period?(now:) end diff --git a/app/models/location_deactivation_period.rb b/app/models/location_deactivation_period.rb index dcf347d24..c9a24bdc9 100644 --- a/app/models/location_deactivation_period.rb +++ b/app/models/location_deactivation_period.rb @@ -1,4 +1,6 @@ class LocationDeactivationPeriodValidator < ActiveModel::Validator + include CollectionTimeHelper + def validate(record) location = record.location recent_deactivation = location.location_deactivation_periods.deactivations_without_reactivation.first @@ -16,7 +18,7 @@ class LocationDeactivationPeriodValidator < ActiveModel::Validator elsif record.reactivation_date_type == "other" record.errors.add(:reactivation_date, message: I18n.t("validations.location.toggle_date.invalid")) end - elsif !record.reactivation_date.between?(location.available_from, Time.zone.local(2200, 1, 1)) + elsif record.reactivation_date.before? location.available_from record.errors.add(:reactivation_date, message: I18n.t("validations.location.toggle_date.out_of_range", date: location.available_from.to_formatted_s(:govuk_date))) elsif record.reactivation_date < recent_deactivation.deactivation_date record.errors.add(:reactivation_date, message: I18n.t("validations.location.reactivation.before_deactivation", date: recent_deactivation.deactivation_date.to_formatted_s(:govuk_date))) @@ -32,10 +34,10 @@ class LocationDeactivationPeriodValidator < ActiveModel::Validator end elsif location.location_deactivation_periods.any? { |period| period.reactivation_date.present? && record.deactivation_date.between?(period.deactivation_date, period.reactivation_date - 1.day) } record.errors.add(:deactivation_date, message: I18n.t("validations.location.deactivation.during_deactivated_period")) - else - unless record.deactivation_date.between?(location.available_from, Time.zone.local(2200, 1, 1)) - record.errors.add(:deactivation_date, message: I18n.t("validations.location.toggle_date.out_of_range", date: location.available_from.to_formatted_s(:govuk_date))) - end + elsif record.deactivation_date.before? FormHandler.instance.start_date_of_earliest_open_collection_period + record.errors.add(:deactivation_date, message: I18n.t("validations.location.toggle_date.out_of_range", date: FormHandler.instance.start_date_of_earliest_open_collection_period.to_formatted_s(:govuk_date))) + elsif record.deactivation_date.before? location.available_from + record.errors.add(:deactivation_date, message: I18n.t("validations.location.toggle_date.before_creation", date: location.available_from.to_formatted_s(:govuk_date))) end end end diff --git a/app/models/scheme_deactivation_period.rb b/app/models/scheme_deactivation_period.rb index f716cbc32..01aafbcb4 100644 --- a/app/models/scheme_deactivation_period.rb +++ b/app/models/scheme_deactivation_period.rb @@ -1,4 +1,6 @@ class SchemeDeactivationPeriodValidator < ActiveModel::Validator + include CollectionTimeHelper + def validate(record) scheme = record.scheme recent_deactivation = scheme.scheme_deactivation_periods.deactivations_without_reactivation.first @@ -32,10 +34,10 @@ class SchemeDeactivationPeriodValidator < ActiveModel::Validator end elsif scheme.scheme_deactivation_periods.any? { |period| period.reactivation_date.present? && record.deactivation_date.between?(period.deactivation_date, period.reactivation_date - 1.day) } record.errors.add(:deactivation_date, message: I18n.t("validations.scheme.deactivation.during_deactivated_period")) - else - unless record.deactivation_date.between?(scheme.available_from, Time.zone.local(2200, 1, 1)) - record.errors.add(:deactivation_date, message: I18n.t("validations.scheme.toggle_date.out_of_range", date: scheme.available_from.to_formatted_s(:govuk_date))) - end + elsif record.deactivation_date.before? FormHandler.instance.start_date_of_earliest_open_collection_period + record.errors.add(:deactivation_date, message: I18n.t("validations.scheme.toggle_date.out_of_range", date: FormHandler.instance.start_date_of_earliest_open_collection_period.to_formatted_s(:govuk_date))) + elsif record.deactivation_date.before? scheme.available_from + record.errors.add(:deactivation_date, message: I18n.t("validations.scheme.toggle_date.before_creation", date: scheme.available_from.to_formatted_s(:govuk_date))) end end end diff --git a/app/views/locations/toggle_active.html.erb b/app/views/locations/toggle_active.html.erb index 5c030de89..7b995b2b5 100644 --- a/app/views/locations/toggle_active.html.erb +++ b/app/views/locations/toggle_active.html.erb @@ -11,16 +11,16 @@ <%= form_with model: @location_deactivation_period, url: toggle_location_form_path(action, @location), method: "patch", local: true do |f| %>
- <% collection_start_date = FormHandler.instance.earliest_open_collection_start_date(now: @location.available_from) %> + <% start_date = FormHandler.instance.earliest_open_collection_start_date(now: @location.available_from) %> <%= f.govuk_error_summary %> <%= f.govuk_radio_buttons_fieldset date_type_question(action), legend: { text: I18n.t("questions.location.toggle_active.apply_from") }, caption: { text: title }, - hint: { text: I18n.t("hints.location.toggle_active", date: collection_start_date.to_formatted_s(:govuk_date)) } do %> + hint: { text: I18n.t("hints.location.toggle_active", date: start_date.to_formatted_s(:govuk_date)) } do %> <%= govuk_warning_text text: I18n.t("warnings.location.#{action}.existing_logs") %> <%= f.govuk_radio_button date_type_question(action), "default", - label: { text: "From the start of the current collection period (#{collection_start_date.to_formatted_s(:govuk_date)})" } %> + label: { text: "From the start of the open collection period (#{start_date.to_formatted_s(:govuk_date)})" } %> <%= f.govuk_radio_button date_type_question(action), "other", diff --git a/app/views/schemes/toggle_active.html.erb b/app/views/schemes/toggle_active.html.erb index 1b7507375..f2ecc4a1a 100644 --- a/app/views/schemes/toggle_active.html.erb +++ b/app/views/schemes/toggle_active.html.erb @@ -11,16 +11,16 @@ <%= form_with model: @scheme_deactivation_period, url: toggle_scheme_form_path(action, @scheme), method: "patch", local: true do |f| %>
- <% collection_start_date = FormHandler.instance.current_collection_start_date %> + <% start_date = FormHandler.instance.start_date_of_earliest_open_collection_period %> <%= f.govuk_error_summary %> <%= f.govuk_radio_buttons_fieldset date_type_question(action), legend: { text: I18n.t("questions.scheme.toggle_active.apply_from") }, caption: { text: title }, - hint: { text: I18n.t("hints.scheme.toggle_active", date: collection_start_date.to_formatted_s(:govuk_date)) } do %> + hint: { text: I18n.t("hints.scheme.toggle_active", date: start_date.to_formatted_s(:govuk_date)) } do %> <%= govuk_warning_text text: I18n.t("warnings.scheme.#{action}.existing_logs") %> <%= f.govuk_radio_button date_type_question(action), "default", - label: { text: "From the start of the current collection period (#{collection_start_date.to_formatted_s(:govuk_date)})" } %> + label: { text: "From the start of the open collection period (#{start_date.to_formatted_s(:govuk_date)})" } %> <%= f.govuk_radio_button date_type_question(action), "other", label: { text: "For tenancies starting after a certain date" }, diff --git a/config/locales/en.yml b/config/locales/en.yml index b6735e3b5..1e7ed58af 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -456,6 +456,7 @@ en: toggle_date: not_selected: "Select one of the options" invalid: "Enter a valid day, month and year" + before_creation: "The scheme cannot be deactivated before %{date}, the start of the collection year when it was created" out_of_range: "The date must be on or after the %{date}" reactivation: before_deactivation: "This scheme was deactivated on %{date}. The reactivation date must be on or after deactivation date" @@ -474,6 +475,7 @@ en: toggle_date: not_selected: "Select one of the options" invalid: "Enter a valid day, month and year" + before_creation: "The location cannot be deactivated before %{date}, the date when it was first available" out_of_range: "The date must be on or after the %{date}" reactivation: before_deactivation: "This location was deactivated on %{date}. The reactivation date must be on or after deactivation date" @@ -594,10 +596,10 @@ en: postcode: "For example, SW1P 4DF." name: "This is how you refer to this location within your organisation" units: "A unit is the space being let. For example, the property might be a block of flats and the unit would be the specific flat being let. A unit can also be a bedroom in a shared house or flat. Do not include spaces used for staff." - toggle_active: "If the date is before %{date}, select ‘From the start of the current collection period’ because the previous period has now closed." + toggle_active: "If the date is before %{date}, select ‘From the start of the open collection period’ because the previous period has now closed." startdate: "For example, 27 3 2021" scheme: - toggle_active: "If the date is before %{date}, select ‘From the start of the current collection period’ because the previous period has now closed." + toggle_active: "If the date is before %{date}, select ‘From the start of the open collection period’ because the previous period has now closed." bulk_upload: needstype: "General needs housing includes both self-contained and shared housing without support or specific adaptations. Supported housing can include direct access hostels, group homes, residential care and nursing homes." offered: "Do not include the offer that led to this letting. This is after the last tenancy ended. If the property is being offered for let for the first time, enter 0." diff --git a/spec/features/schemes_spec.rb b/spec/features/schemes_spec.rb index f43dc67eb..1e45634c6 100644 --- a/spec/features/schemes_spec.rb +++ b/spec/features/schemes_spec.rb @@ -742,7 +742,7 @@ RSpec.describe "Schemes scheme Features" do expect(page).to have_current_path("/schemes/#{scheme.id}/locations") end - context "when location is incative" do + context "when location is inactive" do context "and I click to view the location" do before do click_link(deactivated_location.postcode) @@ -766,7 +766,7 @@ RSpec.describe "Schemes scheme Features" do expect(page).to have_current_path("/schemes/#{scheme.id}/locations/#{deactivated_location.id}/new-reactivation") expect(page).to have_content("Reactivate #{deactivated_location.name}") expect(page).to have_content("You’ll be able to add logs with this location if their tenancy start date is on or after the date you enter.") - expect(page).to have_content("If the date is before 1 April 2022, select ‘From the start of the current collection period’ because the previous period has now closed.") + expect(page).to have_content("If the date is before 1 April 2022, select ‘From the start of the open collection period’ because the previous period has now closed.") end context "when I press the back button" do diff --git a/spec/models/location_deactivation_period_spec.rb b/spec/models/location_deactivation_period_spec.rb new file mode 100644 index 000000000..4e0bb3384 --- /dev/null +++ b/spec/models/location_deactivation_period_spec.rb @@ -0,0 +1,83 @@ +require "rails_helper" + +RSpec.describe LocationDeactivationPeriod do + let(:validator) { LocationDeactivationPeriodValidator.new } + let(:location) { FactoryBot.create(:location, startdate: now - 2.years) } + let(:record) { FactoryBot.create(:location_deactivation_period, deactivation_date: now, location:) } + + describe "#validate" do + around do |example| + Timecop.freeze(now) do + example.run + end + end + + context "when not in a crossover period" do + let(:now) { Time.utc(2023, 3, 1) } + + context "with a deactivation date before the current collection period" do + it "adds an error" do + record.deactivation_date = now - 1.year + location.location_deactivation_periods.clear + validator.validate(record) + expect(record.errors[:deactivation_date]).to include "The date must be on or after the 1 April 2022" + end + end + + context "with a deactivation date in the current collection period" do + it "does not add an error" do + record.deactivation_date = now - 1.day + location.location_deactivation_periods.clear + validator.validate(record) + expect(record.errors).to be_empty + end + end + end + + context "when in a crossover period" do + let(:now) { Time.utc(2023, 5, 1) } + + context "with a deactivation date before the previous collection period" do + it "does not add an error" do + record.deactivation_date = now - 2.years + location.location_deactivation_periods.clear + validator.validate(record) + expect(record.errors[:deactivation_date]).to include "The date must be on or after the 1 April 2022" + end + end + + context "with a deactivation date in the previous collection period" do + it "does not add an error" do + record.deactivation_date = now - 1.year + location.location_deactivation_periods.clear + validator.validate(record) + expect(record.errors).to be_empty + end + end + + context "with a deactivation date in the current collection period" do + it "does not add an error" do + record.deactivation_date = now - 1.day + location.location_deactivation_periods.clear + validator.validate(record) + expect(record.errors).to be_empty + end + end + + context "but the location was created in the current collection period" do + let(:location) { FactoryBot.create(:location, startdate:) } + let(:startdate) { now - 2.days } + + context "with a deactivation date in the previous collection period" do + it "adds an error" do + record.deactivation_date = now - 1.year + location.location_deactivation_periods.clear + validator.validate(record) + start_date = startdate.to_formatted_s(:govuk_date) + expect(record.errors[:deactivation_date]).to include "The location cannot be deactivated before #{start_date}, the date when it was first available" + end + end + end + end + end +end diff --git a/spec/models/scheme_deactivation_period_spec.rb b/spec/models/scheme_deactivation_period_spec.rb new file mode 100644 index 000000000..eb46ee62f --- /dev/null +++ b/spec/models/scheme_deactivation_period_spec.rb @@ -0,0 +1,68 @@ +require "rails_helper" + +RSpec.describe SchemeDeactivationPeriod do + let(:validator) { SchemeDeactivationPeriodValidator.new } + let(:scheme) { FactoryBot.create(:scheme, created_at: now - 2.years) } + let(:record) { FactoryBot.create(:scheme_deactivation_period, deactivation_date: now, scheme:) } + + describe "#validate" do + around do |example| + Timecop.freeze(now) do + example.run + end + end + + context "when not in a crossover period" do + let(:now) { Time.utc(2023, 3, 1) } + + context "with a deactivation date before the current collection period" do + it "adds an error" do + record.deactivation_date = now - 1.year + scheme.scheme_deactivation_periods.clear + validator.validate(record) + expect(record.errors[:deactivation_date]).to include("The date must be on or after the 1 April 2022") + end + end + + context "with a deactivation date in the current collection period" do + it "does not add an error" do + record.deactivation_date = now - 1.day + scheme.scheme_deactivation_periods.clear + validator.validate(record) + expect(record.errors[:deactivation_date]).to be_empty + end + end + end + + context "when in a crossover period" do + let(:now) { Time.utc(2023, 5, 1) } + + context "with a deactivation date before the previous collection period" do + it "does not add an error" do + record.deactivation_date = now - 2.years + scheme.scheme_deactivation_periods.clear + validator.validate(record) + expect(record.errors[:deactivation_date]).to include("The date must be on or after the 1 April 2022") + end + end + + context "with a deactivation date in the previous collection period" do + it "does not add an error" do + record.deactivation_date = now - 1.year + scheme.scheme_deactivation_periods.clear + validator.validate(record) + expect(record.errors[:deactivation_date]).to be_empty + end + end + + context "with a deactivation date in the current collection period" do + it "does not add an error" do + record.deactivation_date = now - 1.day + scheme.scheme_deactivation_periods.clear + validator.validate(record) + expect(record.errors[:deactivation_date]).to be_empty + end + end + end + end +end From 97e1463fda292d6fbcf128fe4b71b1a0ac53a8e8 Mon Sep 17 00:00:00 2001 From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com> Date: Thu, 6 Apr 2023 11:59:25 +0100 Subject: [PATCH 6/7] Re-render page with errors (#1523) --- app/controllers/form_controller.rb | 29 ++++++-------- app/views/form/page.html.erb | 2 +- config/routes.rb | 2 + spec/features/form/page_routing_spec.rb | 4 +- spec/requests/form_controller_spec.rb | 51 ++++++++++++------------- 5 files changed, 40 insertions(+), 48 deletions(-) diff --git a/app/controllers/form_controller.rb b/app/controllers/form_controller.rb index 43a805d9f..c584e40ee 100644 --- a/app/controllers/form_controller.rb +++ b/app/controllers/form_controller.rb @@ -1,7 +1,7 @@ class FormController < ApplicationController before_action :authenticate_user! - before_action :find_resource, only: %i[submit_form review] - before_action :find_resource_by_named_id, except: %i[submit_form review] + before_action :find_resource, only: %i[review] + before_action :find_resource_by_named_id, except: %i[review] before_action :check_collection_period, only: %i[submit_form show_page] def submit_form @@ -11,16 +11,15 @@ class FormController < ApplicationController mandatory_questions_with_no_response = mandatory_questions_with_no_response(responses_for_page) if mandatory_questions_with_no_response.empty? && @log.update(responses_for_page.merge(updated_by: current_user)) - session[:errors] = session[:fields] = nil redirect_to(successful_redirect_path) else - redirect_path = "#{@log.model_name.param_key}_#{@page.id}_path" mandatory_questions_with_no_response.map do |question| @log.errors.add question.id.to_sym, question.unanswered_error_message end - session[:errors] = @log.errors.to_json Rails.logger.info "User triggered validation(s) on: #{@log.errors.map(&:attribute).join(', ')}" - redirect_to(send(redirect_path, @log)) + @subsection = form.subsection_for_page(@page) + restore_error_field_values(@page&.questions) + render "form/page" end else render_not_found @@ -47,7 +46,6 @@ class FormController < ApplicationController def show_page if @log - restore_error_field_values page_id = request.path.split("/")[-1].underscore @page = form.get_page(page_id) @subsection = form.subsection_for_page(@page) @@ -63,17 +61,12 @@ class FormController < ApplicationController private - def restore_error_field_values - if session["errors"] - JSON(session["errors"]).each do |field, messages| - messages.each { |message| @log.errors.add field.to_sym, message } - end - end - if session["fields"] - session["fields"].each do |field, value| - if form.get_question(field, @log)&.type != "date" && @log.attributes.key?(field) - @log[field] = value - end + def restore_error_field_values(questions) + return unless questions + + questions.each do |question| + if question&.type == "date" && @log.attributes.key?(question.id) + @log[question.id] = @log.send("#{question.id}_was") end end end diff --git a/app/views/form/page.html.erb b/app/views/form/page.html.erb index 5469219e3..45711b0f1 100644 --- a/app/views/form/page.html.erb +++ b/app/views/form/page.html.erb @@ -5,7 +5,7 @@ <% end %>
-<%= form_with model: @log, url: form_lettings_log_path(@log), method: "post", local: true do |f| %> +<%= form_with model: @log, url: request.original_url, method: "post", local: true do |f| %>
"> <% remove_other_page_errors(@log, @page) %> diff --git a/config/routes.rb b/config/routes.rb index 1f00815c8..c01f38027 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -164,6 +164,7 @@ Rails.application.routes.draw do FormHandler.instance.lettings_forms.each do |_key, form| form.pages.map do |page| get page.id.to_s.dasherize, to: "form#show_page" + post page.id.to_s.dasherize, to: "form#submit_form" end form.subsections.map do |subsection| @@ -190,6 +191,7 @@ Rails.application.routes.draw do FormHandler.instance.sales_forms.each do |_key, form| form.pages.map do |page| get page.id.to_s.dasherize, to: "form#show_page" + post page.id.to_s.dasherize, to: "form#submit_form" end form.subsections.map do |subsection| diff --git a/spec/features/form/page_routing_spec.rb b/spec/features/form/page_routing_spec.rb index 0ba8ece6e..98c370cff 100644 --- a/spec/features/form/page_routing_spec.rb +++ b/spec/features/form/page_routing_spec.rb @@ -85,11 +85,11 @@ RSpec.describe "Form Page Routing" do context "when answer is invalid" do it "shows error with invalid value in the field" do visit("/lettings-logs/#{id}/property-postcode") - fill_in("lettings-log-postcode-full-field", with: "fake_postcode") + fill_in("lettings-log-postcode-full-field", with: "FAKE_POSTCODE") click_button("Save and continue") expect(page).to have_current_path("/lettings-logs/#{id}/property-postcode") - expect(find("#lettings-log-postcode-full-field-error").value).to eq("fake_postcode") + expect(find("#lettings-log-postcode-full-field-error").value).to eq("FAKE_POSTCODE") end it "does not reset the displayed date" do diff --git a/spec/requests/form_controller_spec.rb b/spec/requests/form_controller_spec.rb index eb47fdb87..9b109ef36 100644 --- a/spec/requests/form_controller_spec.rb +++ b/spec/requests/form_controller_spec.rb @@ -57,7 +57,7 @@ RSpec.describe FormController, type: :request do describe "POST" do it "does not let you post form answers to lettings logs you don't have access to" do - post "/lettings-logs/#{lettings_log.id}/form", params: {} + post "/lettings-logs/#{lettings_log.id}/net-income", params: {} expect(response).to redirect_to("/account/sign-in") end end @@ -102,7 +102,7 @@ RSpec.describe FormController, type: :request do end it "resets created by and renders the next page" do - post "/lettings-logs/#{lettings_log.id}/form", params: params + post "/lettings-logs/#{lettings_log.id}/net-income", params: params expect(response).to redirect_to("/lettings-logs/#{lettings_log.id}/created-by") follow_redirect! lettings_log.reload @@ -127,7 +127,7 @@ RSpec.describe FormController, type: :request do end it "does not reset created by" do - post "/lettings-logs/#{lettings_log.id}/form", params: params + post "/lettings-logs/#{lettings_log.id}/net-income", params: params expect(response).to redirect_to("/lettings-logs/#{lettings_log.id}/created-by") follow_redirect! lettings_log.reload @@ -152,7 +152,7 @@ RSpec.describe FormController, type: :request do end it "does not reset created by" do - post "/lettings-logs/#{lettings_log.id}/form", params: params + post "/lettings-logs/#{lettings_log.id}/stock-owner", params: params expect(response).to redirect_to("/lettings-logs/#{lettings_log.id}/managing-organisation") follow_redirect! lettings_log.reload @@ -177,7 +177,7 @@ RSpec.describe FormController, type: :request do end it "does not reset created by" do - post "/lettings-logs/#{lettings_log.id}/form", params: params + post "/lettings-logs/#{lettings_log.id}/stock-owner", params: params expect(response).to redirect_to("/lettings-logs/#{lettings_log.id}/managing-organisation") follow_redirect! lettings_log.reload @@ -400,22 +400,21 @@ RSpec.describe FormController, type: :request do end it "re-renders the same page with errors if validation fails" do - post "/lettings-logs/#{lettings_log.id}/form", params: params - expect(response).to redirect_to("/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}") - follow_redirect! + post "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}", params: params expect(page).to have_content("There is a problem") + expect(page).to have_content("Error: What is the tenant’s age?") end it "resets errors when fixed" do - post "/lettings-logs/#{lettings_log.id}/form", params: params - post "/lettings-logs/#{lettings_log.id}/form", params: valid_params + post "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}", params: params + post "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}", params: valid_params get "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}" expect(page).not_to have_content("There is a problem") end it "logs that validation was triggered" do expect(Rails.logger).to receive(:info).with("User triggered validation(s) on: age1").once - post "/lettings-logs/#{lettings_log.id}/form", params: + post "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}", params: end context "when the number of days is too high for the month" do @@ -433,8 +432,7 @@ RSpec.describe FormController, type: :request do end it "validates the date correctly" do - post "/lettings-logs/#{lettings_log.id}/form", params: params - follow_redirect! + post "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}", params: params expect(page).to have_content("There is a problem") end end @@ -465,10 +463,9 @@ RSpec.describe FormController, type: :request do end it "re-renders the same page with errors if validation fails" do - post "/lettings-logs/#{lettings_log.id}/form", params: params - expect(response).to redirect_to("/lettings-logs/#{lettings_log.id}/managing-organisation") - follow_redirect! + post "/lettings-logs/#{lettings_log.id}/managing-organisation", params: params expect(page).to have_content("There is a problem") + expect(page).to have_content("Error: Which organisation manages this letting?") end end @@ -486,7 +483,7 @@ RSpec.describe FormController, type: :request do end before do - post "/lettings-logs/#{lettings_log.id}/form", params: + post "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}", params: end it "re-renders the same page with errors if validation fails" do @@ -524,7 +521,7 @@ RSpec.describe FormController, type: :request do before do lettings_log.update!(postcode_known: 1, postcode_full: "NW1 8RR") - post "/lettings-logs/#{lettings_log.id}/form", params: valid_params + post "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}", params: valid_params end it "does not require you to answer that question" do @@ -558,14 +555,14 @@ RSpec.describe FormController, type: :request do end it "sets checked items to true" do - post "/lettings-logs/#{lettings_log.id}/form", params: lettings_log_form_params + post "/lettings-logs/#{lettings_log.id}/accessibility-requirements", params: lettings_log_form_params lettings_log.reload expect(lettings_log.housingneeds_b).to eq(1) end it "sets previously submitted items to false when resubmitted with new values" do - post "/lettings-logs/#{lettings_log.id}/form", params: new_lettings_log_form_params + post "/lettings-logs/#{lettings_log.id}/accessibility-requirements", params: new_lettings_log_form_params lettings_log.reload expect(lettings_log.housingneeds_b).to eq(0) @@ -609,7 +606,7 @@ RSpec.describe FormController, type: :request do it "updates both question fields" do allow(page).to receive(:questions).and_return(questions_for_page) - post "/lettings-logs/#{lettings_log.id}/form", params: lettings_log_form_params + post "/lettings-logs/#{lettings_log.id}/#{page.id.dasherize}", params: lettings_log_form_params lettings_log.reload expect(lettings_log.housingneeds_a).to eq(1) @@ -650,16 +647,16 @@ RSpec.describe FormController, type: :request do end it "routes to the appropriate conditional page based on the question answer of the current page" do - post "/lettings-logs/#{lettings_log.id}/form", params: lettings_log_form_conditional_question_yes_params + post "/lettings-logs/#{lettings_log.id}/property-wheelchair-accessible", params: lettings_log_form_conditional_question_yes_params expect(response).to redirect_to("/lettings-logs/#{lettings_log.id}/conditional-question-yes-page") - post "/lettings-logs/#{lettings_log.id}/form", params: lettings_log_form_conditional_question_no_params + post "/lettings-logs/#{lettings_log.id}/property-wheelchair-accessible", params: lettings_log_form_conditional_question_no_params expect(response).to redirect_to("/lettings-logs/#{lettings_log.id}/conditional-question-no-page") end it "routes to the page if at least one of the condition sets is met" do - post "/lettings-logs/#{lettings_log.id}/form", params: lettings_log_form_conditional_question_wchair_yes_params - post "/lettings-logs/#{lettings_log.id}/form", params: lettings_log_form_conditional_question_no_params + post "/lettings-logs/#{lettings_log.id}/property-wheelchair-accessible", params: lettings_log_form_conditional_question_wchair_yes_params + post "/lettings-logs/#{lettings_log.id}/property-wheelchair-accessible", params: lettings_log_form_conditional_question_no_params expect(response).to redirect_to("/lettings-logs/#{lettings_log.id}/conditional-question-yes-page") end end @@ -690,7 +687,7 @@ RSpec.describe FormController, type: :request do completed_lettings_log.update!(ecstat1: 1, earnings: 130, hhmemb: 1) # we're not routing to that page, so it gets cleared? allow(completed_lettings_log).to receive(:net_income_soft_validation_triggered?).and_return(true) allow(completed_lettings_log.form).to receive(:end_date).and_return(Time.zone.today + 1.day) - post "/lettings-logs/#{completed_lettings_log.id}/form", params: interrupt_params, headers: headers.merge({ "HTTP_REFERER" => referrer }) + post "/lettings-logs/#{completed_lettings_log.id}/net-income-value-check", params: interrupt_params, headers: headers.merge({ "HTTP_REFERER" => referrer }) end context "when yes is answered" do @@ -722,7 +719,7 @@ RSpec.describe FormController, type: :request do end before do - post "/lettings-logs/#{unauthorized_lettings_log.id}/form", params: {} + post "/lettings-logs/#{unauthorized_lettings_log.id}/net-income", params: {} end it "does not let you post form answers to lettings logs you don't have access to" do From 69aa90a1cd05cc13aa2bd27a6da6eb76a92d9195 Mon Sep 17 00:00:00 2001 From: Jack <113976590+bibblobcode@users.noreply.github.com> Date: Thu, 6 Apr 2023 12:12:15 +0100 Subject: [PATCH 7/7] CLDC-1975 Format money values with 2 decimals (#1493) * Format errors on money amounts with 2 decimals * CLDC-1975 Format money input and error messages * format discounted_ownership_value * Format more locales * Update lettings_log tests * Fix import service spec --- app/helpers/money_formatting_helper.rb | 23 +++++++ app/models/lettings_log.rb | 5 +- app/models/log.rb | 4 -- app/models/sales_log.rb | 3 +- .../validations/financial_validations.rb | 29 ++++++--- .../sales/sale_information_validations.rb | 9 ++- .../form/_numeric_output_question.html.erb | 3 +- app/views/form/_numeric_question.html.erb | 11 +++- config/locales/en.yml | 16 ++--- spec/helpers/money_formatting_helper_spec.rb | 41 ++++++++++++ spec/models/lettings_log_spec.rb | 8 +-- spec/models/sales_log_spec.rb | 2 +- .../validations/financial_validations_spec.rb | 40 ++++++------ .../sale_information_validations_spec.rb | 63 +++++++++++-------- .../lettings_logs_import_service_spec.rb | 8 +-- 15 files changed, 184 insertions(+), 81 deletions(-) create mode 100644 app/helpers/money_formatting_helper.rb create mode 100644 spec/helpers/money_formatting_helper_spec.rb diff --git a/app/helpers/money_formatting_helper.rb b/app/helpers/money_formatting_helper.rb new file mode 100644 index 000000000..246d910fe --- /dev/null +++ b/app/helpers/money_formatting_helper.rb @@ -0,0 +1,23 @@ +module MoneyFormattingHelper + include ActionView::Helpers::NumberHelper + + def format_money_input(log:, question:) + value = log[question.id] + + return unless value + return value unless question.prefix == "£" + + number_with_precision( + value, + precision: 2, + ) + end + + def format_as_currency(num_string) + number_to_currency( + num_string, + unit: "£", + precision: 2, + ) + end +end diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb index 4a0651a29..0ddc921be 100644 --- a/app/models/lettings_log.rb +++ b/app/models/lettings_log.rb @@ -20,6 +20,7 @@ class LettingsLog < Log include DerivedVariables::LettingsLogVariables include Validations::DateValidations include Validations::FinancialValidations + include MoneyFormattingHelper has_paper_trail @@ -137,7 +138,7 @@ class LettingsLog < Log def weekly_to_value_per_period(field_value) num_of_weeks = NUM_OF_WEEKS_FROM_PERIOD[period] - ((field_value * 52) / num_of_weeks).round(2) + format_as_currency((field_value * 52) / num_of_weeks) end def applicable_income_range @@ -638,7 +639,7 @@ private num_of_weeks = NUM_OF_WEEKS_FROM_PERIOD[period] return "" unless value && num_of_weeks - (value * 52 / num_of_weeks).round(2) + format_as_currency((value * 52 / num_of_weeks)) end def fully_wheelchair_accessible? diff --git a/app/models/log.rb b/app/models/log.rb index bc036d356..6a36b948d 100644 --- a/app/models/log.rb +++ b/app/models/log.rb @@ -217,8 +217,4 @@ private self[is_inferred_key] = false self[postcode_key] = nil end - - def format_as_currency(num_string) - ActionController::Base.helpers.number_to_currency(num_string, unit: "£") - end end diff --git a/app/models/sales_log.rb b/app/models/sales_log.rb index b0aed712a..b86124a2f 100644 --- a/app/models/sales_log.rb +++ b/app/models/sales_log.rb @@ -18,6 +18,7 @@ class SalesLog < Log include DerivedVariables::SalesLogVariables include Validations::Sales::SoftValidations include Validations::SoftValidations + include MoneyFormattingHelper self.inheritance_column = :_type_disabled @@ -215,7 +216,7 @@ class SalesLog < Log def expected_shared_ownership_deposit_value return unless value && equity - (value * equity / 100).round(2) + format_as_currency(value * equity / 100) end def process_postcode(postcode, postcode_known_key, la_inferred_key, la_key) diff --git a/app/models/validations/financial_validations.rb b/app/models/validations/financial_validations.rb index 5fba288bb..3a0014119 100644 --- a/app/models/validations/financial_validations.rb +++ b/app/models/validations/financial_validations.rb @@ -1,5 +1,6 @@ module Validations::FinancialValidations include Validations::SharedValidations + include MoneyFormattingHelper # Validations methods need to be called 'validate_' to run on model save # or 'validate_' to run on submit as well def validate_outstanding_rent_amount(record) @@ -24,12 +25,26 @@ module Validations::FinancialValidations def validate_net_income(record) if record.ecstat1 && record.weekly_net_income if record.weekly_net_income > record.applicable_income_range.hard_max - record.errors.add :earnings, :over_hard_max, message: I18n.t("validations.financial.earnings.over_hard_max", hard_max: record.applicable_income_range.hard_max) - record.errors.add :ecstat1, :over_hard_max, message: I18n.t("validations.financial.ecstat.over_hard_max", hard_max: record.applicable_income_range.hard_max) + hard_max = format_as_currency(record.applicable_income_range.hard_max) + record.errors.add( + :earnings, + :over_hard_max, + message: I18n.t("validations.financial.earnings.over_hard_max", hard_max:), + ) + record.errors.add( + :ecstat1, + :over_hard_max, + message: I18n.t("validations.financial.ecstat.over_hard_max", hard_max:), + ) end if record.weekly_net_income < record.applicable_income_range.hard_min - record.errors.add :earnings, :under_hard_min, message: I18n.t("validations.financial.earnings.under_hard_min", hard_min: record.applicable_income_range.hard_min) + hard_min = format_as_currency(record.applicable_income_range.hard_min) + record.errors.add( + :earnings, + :under_hard_min, + message: I18n.t("validations.financial.earnings.under_hard_min", hard_min:), + ) end end @@ -120,10 +135,10 @@ module Validations::FinancialValidations elsif !weekly_value_in_range(record, "chcharge", 10, 1000) max_chcharge = record.weekly_to_value_per_period(1000) min_chcharge = record.weekly_to_value_per_period(10) - max_chcharge = [record.form.get_question("chcharge", record).prefix, max_chcharge].join("") if record.form.get_question("chcharge", record).present? - min_chcharge = [record.form.get_question("chcharge", record).prefix, min_chcharge].join("") if record.form.get_question("chcharge", record).present? - record.errors.add :period, I18n.t("validations.financial.carehome.out_of_range", period:, min_chcharge:, max_chcharge:) - record.errors.add :chcharge, :out_of_range, message: I18n.t("validations.financial.carehome.out_of_range", period:, min_chcharge:, max_chcharge:) + message = I18n.t("validations.financial.carehome.out_of_range", period:, min_chcharge:, max_chcharge:) + + record.errors.add :period, message + record.errors.add :chcharge, :out_of_range, message: message end end end diff --git a/app/models/validations/sales/sale_information_validations.rb b/app/models/validations/sales/sale_information_validations.rb index fcd1854f4..e4714e81a 100644 --- a/app/models/validations/sales/sale_information_validations.rb +++ b/app/models/validations/sales/sale_information_validations.rb @@ -1,5 +1,6 @@ module Validations::Sales::SaleInformationValidations include CollectionTimeHelper + include MoneyFormattingHelper def validate_practical_completion_date_before_saledate(record) return if record.saledate.blank? || record.hodate.blank? @@ -54,7 +55,13 @@ module Validations::Sales::SaleInformationValidations if record.mortgage_deposit_and_grant_total != record.value_with_discount && record.discounted_ownership_sale? %i[mortgage deposit grant value discount ownershipsch].each do |field| - record.errors.add field, I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: sprintf("%.2f", record.value_with_discount)) + record.errors.add( + field, + I18n.t( + "validations.sale_information.discounted_ownership_value", + value_with_discount: format_as_currency(record.value_with_discount), + ), + ) end end end diff --git a/app/views/form/_numeric_output_question.html.erb b/app/views/form/_numeric_output_question.html.erb index 8892523aa..7d681f7ea 100644 --- a/app/views/form/_numeric_output_question.html.erb +++ b/app/views/form/_numeric_output_question.html.erb @@ -16,7 +16,8 @@ type="number" name="lettings_log[tcharge]" for="<%= question.fields_added.present? ? question.fields_added.map { |x| "lettings-log-#{x}-field" }.join(" ") : "" %>"> - <%= lettings_log[question.id] %> + <%= format_money_input(log: lettings_log, question:) %> + <%= question.suffix_label(lettings_log) %>
diff --git a/app/views/form/_numeric_question.html.erb b/app/views/form/_numeric_question.html.erb index 0aeb63801..b92d0f736 100644 --- a/app/views/form/_numeric_question.html.erb +++ b/app/views/form/_numeric_question.html.erb @@ -1,14 +1,19 @@ <%= render partial: "form/guidance/#{question.guidance_partial}" if question.top_guidance? %> -<%= f.govuk_number_field question.id.to_sym, +<%= f.govuk_number_field( + question.id.to_sym, caption: caption(caption_text, page_header, conditional), label: legend(question, page_header, conditional), hint: { text: question.hint_text&.html_safe }, - min: question.min, max: question.max, step: question.step, + min: question.min, + max: question.max, + step: question.step, width: question.width, readonly: question.read_only?, prefix_text: question.prefix.to_s, suffix_text: question.suffix_label(@log), - **stimulus_html_attributes(question) %> + value: format_money_input(log: @log, question:), + **stimulus_html_attributes(question), +) %> <%= render partial: "form/guidance/#{question.guidance_partial}" if question.bottom_guidance? %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 1e7ed58af..68e1d72fc 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -248,15 +248,15 @@ en: benefits: part_or_full_time: "Answer cannot be ‘all’ for income from Universal Credit, state pensions or benefits if the tenant or their partner works part-time or full-time" earnings: - over_hard_max: "Net income cannot be greater than £%{hard_max} per week given the tenant’s working situation" - under_hard_min: "Net income cannot be less than £%{hard_min} per week given the tenant’s working situation" + over_hard_max: "Net income cannot be greater than %{hard_max} per week given the tenant’s working situation" + under_hard_min: "Net income cannot be less than %{hard_min} per week given the tenant’s working situation" freq_missing: "Select how often the household receives income" earnings_missing: "Enter how much income the household has in total" income: - over_hard_max_for_london: "Income must not exceed £90,000 for properties within London local authorities" - over_hard_max_for_outside_london: "Income must not exceed £80,000 for properties outside London local authorities" - combined_over_hard_max_for_london: "Combined income must not exceed £90,000 for properties within London local authorities" - combined_over_hard_max_for_outside_london: "Combined income must not exceed £80,000 for properties outside London local authorities" + over_hard_max_for_london: "Income must not exceed £90,000.00 for properties within London local authorities" + over_hard_max_for_outside_london: "Income must not exceed £80,000.00 for properties outside London local authorities" + combined_over_hard_max_for_london: "Combined income must not exceed £90,000.00 for properties within London local authorities" + combined_over_hard_max_for_outside_london: "Combined income must not exceed £80,000.00 for properties outside London local authorities" child_has_income: "Child's income must be £0" negative_currency: "Enter an amount above 0" rent: @@ -498,9 +498,9 @@ en: must_be_after_hodate: "Sale completion date must be after practical completion or handover date" previous_property_type: property_type_bedsit: "A bedsit cannot have more than 1 bedroom" - discounted_ownership_value: "Mortgage, deposit, and grant total must equal £%{value_with_discount}" + discounted_ownership_value: "Mortgage, deposit, and grant total must equal %{value_with_discount}" monthly_rent: - higher_than_expected: "Basic monthly rent must be between £0 and £9,999" + higher_than_expected: "Basic monthly rent must be between £0.00 and £9,999.00" soft_validations: net_income: diff --git a/spec/helpers/money_formatting_helper_spec.rb b/spec/helpers/money_formatting_helper_spec.rb new file mode 100644 index 000000000..cd16ea86c --- /dev/null +++ b/spec/helpers/money_formatting_helper_spec.rb @@ -0,0 +1,41 @@ +require "rails_helper" + +RSpec.describe MoneyFormattingHelper do + describe "#format_money_input" do + let!(:log) { create(:lettings_log, :completed, brent: 1000) } + let(:question) { instance_double(Form::Question, id: "brent", prefix:) } + + context "with £ prefix" do + let(:prefix) { "£" } + + it "returns formatted input" do + expect(format_money_input(log:, question:)).to eq("1000.00") + end + end + + context "with other prefix" do + let(:prefix) { "other" } + + it "does not format the input" do + expect(format_money_input(log:, question:)).to eq(BigDecimal(1000)) + end + end + + context "without prefix" do + let(:prefix) { nil } + + it "does not format the input" do + expect(format_money_input(log:, question:)).to eq(BigDecimal(1000)) + end + end + + context "when value is nil" do + let(:prefix) { "£" } + let(:log) { create(:lettings_log, brent: nil) } + + it "does not format the input" do + expect(format_money_input(log:, question:)).to be_nil + end + end + end +end diff --git a/spec/models/lettings_log_spec.rb b/spec/models/lettings_log_spec.rb index 2358317af..1e6fd3244 100644 --- a/spec/models/lettings_log_spec.rb +++ b/spec/models/lettings_log_spec.rb @@ -2605,24 +2605,24 @@ RSpec.describe LettingsLog do context "when period is weekly for 52 weeks" do it "returns weekly soft min for 52 weeks" do lettings_log.period = 1 - expect(lettings_log.soft_min_for_period).to eq("100.0 every week") + expect(lettings_log.soft_min_for_period).to eq("£100.00 every week") end it "returns weekly soft max for 52 weeks" do lettings_log.period = 1 - expect(lettings_log.soft_max_for_period).to eq("400.0 every week") + expect(lettings_log.soft_max_for_period).to eq("£400.00 every week") end end context "when period is weekly for 47 weeks" do it "returns weekly soft min for 47 weeks" do lettings_log.period = 8 - expect(lettings_log.soft_min_for_period).to eq("110.64 every week") + expect(lettings_log.soft_min_for_period).to eq("£110.64 every week") end it "returns weekly soft max for 47 weeks" do lettings_log.period = 8 - expect(lettings_log.soft_max_for_period).to eq("442.55 every week") + expect(lettings_log.soft_max_for_period).to eq("£442.55 every week") end end end diff --git a/spec/models/sales_log_spec.rb b/spec/models/sales_log_spec.rb index ca72794a5..0632af547 100644 --- a/spec/models/sales_log_spec.rb +++ b/spec/models/sales_log_spec.rb @@ -498,7 +498,7 @@ RSpec.describe SalesLog, type: :model do let!(:completed_sales_log) { create(:sales_log, :completed, ownershipsch: 1, type: 2, value: 1000, equity: 50) } it "is set to completed for a completed sales log" do - expect(completed_sales_log.expected_shared_ownership_deposit_value).to eq(500) + expect(completed_sales_log.expected_shared_ownership_deposit_value).to eq("£500.00") end end diff --git a/spec/models/validations/financial_validations_spec.rb b/spec/models/validations/financial_validations_spec.rb index 8cb777029..6ae2a4607 100644 --- a/spec/models/validations/financial_validations_spec.rb +++ b/spec/models/validations/financial_validations_spec.rb @@ -202,7 +202,7 @@ RSpec.describe Validations::FinancialValidations do record.ecstat1 = 1 financial_validator.validate_net_income(record) expect(record.errors["earnings"]) - .to include(match I18n.t("validations.financial.earnings.over_hard_max", hard_max: 1230)) + .to eq(["Net income cannot be greater than £1,230.00 per week given the tenant’s working situation"]) end end @@ -213,7 +213,7 @@ RSpec.describe Validations::FinancialValidations do record.ecstat1 = 1 financial_validator.validate_net_income(record) expect(record.errors["earnings"]) - .to include(match I18n.t("validations.financial.earnings.under_hard_min", hard_min: 90)) + .to eq(["Net income cannot be less than £90.00 per week given the tenant’s working situation"]) end end end @@ -913,15 +913,15 @@ RSpec.describe Validations::FinancialValidations do record.is_carehome = 1 end - context "and charges are over the valid limit (£1000 per week)" do + context "and charges are over the valid limit (£1,000 per week)" do it "validates charge when period is weekly for 52 weeks" do record.period = 1 record.chcharge = 1001 financial_validator.validate_care_home_charges(record) expect(record.errors["chcharge"]) - .to include(match I18n.t("validations.financial.carehome.out_of_range", min_chcharge: 10, period: "weekly for 52 weeks", max_chcharge: 1000)) + .to include("Household rent and other charges must be between £10.00 and £1,000.00 if paying weekly for 52 weeks") expect(record.errors["period"]) - .to include(match I18n.t("validations.financial.carehome.out_of_range", min_chcharge: 10, period: "weekly for 52 weeks", max_chcharge: 1000)) + .to include("Household rent and other charges must be between £10.00 and £1,000.00 if paying weekly for 52 weeks") end it "validates charge when period is monthly" do @@ -929,9 +929,9 @@ RSpec.describe Validations::FinancialValidations do record.chcharge = 4334 financial_validator.validate_care_home_charges(record) expect(record.errors["chcharge"]) - .to include(match I18n.t("validations.financial.carehome.out_of_range", min_chcharge: 43, period: "every calendar month", max_chcharge: 4333)) + .to include("Household rent and other charges must be between £43.00 and £4,333.00 if paying every calendar month") expect(record.errors["period"]) - .to include(match I18n.t("validations.financial.carehome.out_of_range", min_chcharge: 43, period: "every calendar month", max_chcharge: 4333)) + .to include("Household rent and other charges must be between £43.00 and £4,333.00 if paying every calendar month") end it "validates charge when period is every 2 weeks" do @@ -939,9 +939,9 @@ RSpec.describe Validations::FinancialValidations do record.chcharge = 2001 financial_validator.validate_care_home_charges(record) expect(record.errors["chcharge"]) - .to include(match I18n.t("validations.financial.carehome.out_of_range", min_chcharge: 20, period: "every 2 weeks", max_chcharge: 2000)) + .to include("Household rent and other charges must be between £20.00 and £2,000.00 if paying every 2 weeks") expect(record.errors["period"]) - .to include(match I18n.t("validations.financial.carehome.out_of_range", min_chcharge: 20, period: "every 2 weeks", max_chcharge: 2000)) + .to include("Household rent and other charges must be between £20.00 and £2,000.00 if paying every 2 weeks") end it "validates charge when period is every 4 weeks" do @@ -949,13 +949,13 @@ RSpec.describe Validations::FinancialValidations do record.chcharge = 4001 financial_validator.validate_care_home_charges(record) expect(record.errors["chcharge"]) - .to include(match I18n.t("validations.financial.carehome.out_of_range", min_chcharge: 40, period: "every 4 weeks", max_chcharge: 4000)) + .to include("Household rent and other charges must be between £40.00 and £4,000.00 if paying every 4 weeks") expect(record.errors["period"]) - .to include(match I18n.t("validations.financial.carehome.out_of_range", min_chcharge: 40, period: "every 4 weeks", max_chcharge: 4000)) + .to include("Household rent and other charges must be between £40.00 and £4,000.00 if paying every 4 weeks") end end - context "and charges are within the valid limit (£1000 per week)" do + context "and charges are within the valid limit (£1,000 per week)" do it "does not throw error when period is weekly for 52 weeks" do record.period = 1 record.chcharge = 999 @@ -1007,9 +1007,9 @@ RSpec.describe Validations::FinancialValidations do record.chcharge = 9 financial_validator.validate_care_home_charges(record) expect(record.errors["chcharge"]) - .to include(match I18n.t("validations.financial.carehome.out_of_range", min_chcharge: 10, period: "weekly for 52 weeks", max_chcharge: 1000)) + .to include("Household rent and other charges must be between £10.00 and £1,000.00 if paying weekly for 52 weeks") expect(record.errors["period"]) - .to include(match I18n.t("validations.financial.carehome.out_of_range", min_chcharge: 10, period: "weekly for 52 weeks", max_chcharge: 1000)) + .to include("Household rent and other charges must be between £10.00 and £1,000.00 if paying weekly for 52 weeks") end it "validates charge when period is monthly" do @@ -1017,9 +1017,9 @@ RSpec.describe Validations::FinancialValidations do record.chcharge = 42 financial_validator.validate_care_home_charges(record) expect(record.errors["chcharge"]) - .to include(match I18n.t("validations.financial.carehome.out_of_range", min_chcharge: 43, period: "every calendar month", max_chcharge: 4333)) + .to include("Household rent and other charges must be between £43.00 and £4,333.00 if paying every calendar month") expect(record.errors["period"]) - .to include(match I18n.t("validations.financial.carehome.out_of_range", min_chcharge: 43, period: "every calendar month", max_chcharge: 4333)) + .to include("Household rent and other charges must be between £43.00 and £4,333.00 if paying every calendar month") end it "validates charge when period is every 2 weeks" do @@ -1027,9 +1027,9 @@ RSpec.describe Validations::FinancialValidations do record.chcharge = 19 financial_validator.validate_care_home_charges(record) expect(record.errors["chcharge"]) - .to include(match I18n.t("validations.financial.carehome.out_of_range", min_chcharge: 20, period: "every 2 weeks", max_chcharge: 2000)) + .to include("Household rent and other charges must be between £20.00 and £2,000.00 if paying every 2 weeks") expect(record.errors["period"]) - .to include(match I18n.t("validations.financial.carehome.out_of_range", min_chcharge: 20, period: "every 2 weeks", max_chcharge: 2000)) + .to include("Household rent and other charges must be between £20.00 and £2,000.00 if paying every 2 weeks") end it "validates charge when period is every 4 weeks" do @@ -1037,9 +1037,9 @@ RSpec.describe Validations::FinancialValidations do record.chcharge = 39 financial_validator.validate_care_home_charges(record) expect(record.errors["chcharge"]) - .to include(match I18n.t("validations.financial.carehome.out_of_range", min_chcharge: 40, period: "every 4 weeks", max_chcharge: 4000)) + .to include("Household rent and other charges must be between £40.00 and £4,000.00 if paying every 4 weeks") expect(record.errors["period"]) - .to include(match I18n.t("validations.financial.carehome.out_of_range", min_chcharge: 40, period: "every 4 weeks", max_chcharge: 4000)) + .to include("Household rent and other charges must be between £40.00 and £4,000.00 if paying every 4 weeks") end end end diff --git a/spec/models/validations/sales/sale_information_validations_spec.rb b/spec/models/validations/sales/sale_information_validations_spec.rb index 3a8944e79..cccb19d40 100644 --- a/spec/models/validations/sales/sale_information_validations_spec.rb +++ b/spec/models/validations/sales/sale_information_validations_spec.rb @@ -246,11 +246,13 @@ RSpec.describe Validations::Sales::SaleInformationValidations do it "adds an error if mortgage, deposit and grant total does not equal market value" do record.grant = 3_000 sale_information_validator.validate_discounted_ownership_value(record) - expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00")) - expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00")) - expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00")) - expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00")) - expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00")) + expected_message = ["Mortgage, deposit, and grant total must equal £30,000.00"] + + expect(record.errors[:mortgage]).to eq(expected_message) + expect(record.errors[:deposit]).to eq(expected_message) + expect(record.errors[:grant]).to eq(expected_message) + expect(record.errors[:value]).to eq(expected_message) + expect(record.errors[:discount]).to eq(expected_message) end it "does not add an error if mortgage, deposit and grant total equals market value" do @@ -280,11 +282,14 @@ RSpec.describe Validations::Sales::SaleInformationValidations do it "adds an error if mortgage and deposit total does not equal market value - discount" do record.discount = 10 sale_information_validator.validate_discounted_ownership_value(record) - expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00")) - expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00")) - expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00")) - expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00")) - expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00")) + + expected_message = ["Mortgage, deposit, and grant total must equal £27,000.00"] + + expect(record.errors[:mortgage]).to eq(expected_message) + expect(record.errors[:deposit]).to eq(expected_message) + expect(record.errors[:grant]).to eq(expected_message) + expect(record.errors[:value]).to eq(expected_message) + expect(record.errors[:discount]).to eq(expected_message) end it "does not add an error if mortgage and deposit total equals market value - discount" do @@ -301,11 +306,14 @@ RSpec.describe Validations::Sales::SaleInformationValidations do it "adds an error if mortgage and deposit total does not equal market value" do record.deposit = 2_000 sale_information_validator.validate_discounted_ownership_value(record) - expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00")) - expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00")) - expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00")) - expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00")) - expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00")) + + expected_message = ["Mortgage, deposit, and grant total must equal £30,000.00"] + + expect(record.errors[:mortgage]).to eq(expected_message) + expect(record.errors[:deposit]).to eq(expected_message) + expect(record.errors[:grant]).to eq(expected_message) + expect(record.errors[:value]).to eq(expected_message) + expect(record.errors[:discount]).to eq(expected_message) end it "does not add an error if mortgage and deposit total equals market value" do @@ -334,11 +342,14 @@ RSpec.describe Validations::Sales::SaleInformationValidations do it "adds an error if mortgage, grant and deposit total does not equal market value - discount" do record.mortgage = 10 sale_information_validator.validate_discounted_ownership_value(record) - expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00")) - expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00")) - expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00")) - expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00")) - expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00")) + + expected_message = ["Mortgage, deposit, and grant total must equal £18,000.00"] + + expect(record.errors[:mortgage]).to eq(expected_message) + expect(record.errors[:deposit]).to eq(expected_message) + expect(record.errors[:grant]).to eq(expected_message) + expect(record.errors[:value]).to eq(expected_message) + expect(record.errors[:discount]).to eq(expected_message) end it "does not add an error if mortgage, grant and deposit total equals market value - discount" do @@ -354,11 +365,13 @@ RSpec.describe Validations::Sales::SaleInformationValidations do it "adds an error if grant and deposit total does not equal market value - discount" do sale_information_validator.validate_discounted_ownership_value(record) - expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00")) - expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00")) - expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00")) - expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00")) - expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00")) + + expected_message = ["Mortgage, deposit, and grant total must equal £18,000.00"] + expect(record.errors[:mortgage]).to eq(expected_message) + expect(record.errors[:deposit]).to eq(expected_message) + expect(record.errors[:grant]).to eq(expected_message) + expect(record.errors[:value]).to eq(expected_message) + expect(record.errors[:discount]).to eq(expected_message) end it "does not add an error if mortgage, grant and deposit total equals market value - discount" do diff --git a/spec/services/imports/lettings_logs_import_service_spec.rb b/spec/services/imports/lettings_logs_import_service_spec.rb index 50a285ed9..deccde0c0 100644 --- a/spec/services/imports/lettings_logs_import_service_spec.rb +++ b/spec/services/imports/lettings_logs_import_service_spec.rb @@ -223,8 +223,8 @@ RSpec.describe Imports::LettingsLogsImportService do end it "intercepts the relevant validation error" do - expect(logger).to receive(:warn).with(/Removing earnings with error: Net income cannot be less than £10 per week given the tenant’s working situation/) - expect(logger).to receive(:warn).with(/Removing incfreq with error: Net income cannot be less than £10 per week given the tenant’s working situation/) + expect(logger).to receive(:warn).with(/Removing earnings with error: Net income cannot be less than £10.00 per week given the tenant’s working situation/) + expect(logger).to receive(:warn).with(/Removing incfreq with error: Net income cannot be less than £10.00 per week given the tenant’s working situation/) expect { lettings_log_service.send(:create_log, lettings_log_xml) } .not_to raise_error end @@ -428,7 +428,7 @@ RSpec.describe Imports::LettingsLogsImportService do end it "intercepts the relevant validation error" do - expect(logger).to receive(:warn).with(/Removing ecstat1 with error: Net income cannot be greater than £890 per week given the tenant’s working situation/) + expect(logger).to receive(:warn).with(/Removing ecstat1 with error: Net income cannot be greater than £890.00 per week given the tenant’s working situation/) expect { lettings_log_service.send(:create_log, lettings_log_xml) } .not_to raise_error end @@ -762,7 +762,7 @@ RSpec.describe Imports::LettingsLogsImportService do end it "intercepts the relevant validation error" do - expect(logger).to receive(:warn).with(/Removing chcharge with error: Household rent and other charges must be between £10 and £1000 if paying weekly for 52 weeks/) + expect(logger).to receive(:warn).with(/Removing chcharge with error: Household rent and other charges must be between £10.00 and £1,000.00 if paying weekly for 52 weeks/) expect { lettings_log_service.send(:create_log, lettings_log_xml) } .not_to raise_error end