diff --git a/Gemfile b/Gemfile index 0ddf83cdd..741f484bd 100644 --- a/Gemfile +++ b/Gemfile @@ -49,7 +49,7 @@ gem "paper_trail" # Store active record objects in version whodunnits gem "paper_trail-globalid" # Request rate limiting -gem "rack", "~> 2.2.6.3" +gem "rack", ">= 2.2.6.3" gem "rack-attack" gem "redis", "~> 4.8" # Receive exceptions and configure alerts diff --git a/Gemfile.lock b/Gemfile.lock index fab8359ea..b9b16ab1e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -478,7 +478,7 @@ DEPENDENCIES propshaft pry-byebug puma (~> 5.0) - rack (~> 2.2.6.3) + rack (>= 2.2.6.3) rack-attack rack-mini-profiler (~> 2.0) rails (~> 7.0.2) diff --git a/app/controllers/bulk_upload_lettings_logs_controller.rb b/app/controllers/bulk_upload_lettings_logs_controller.rb index 391651cde..9b37cd2b5 100644 --- a/app/controllers/bulk_upload_lettings_logs_controller.rb +++ b/app/controllers/bulk_upload_lettings_logs_controller.rb @@ -21,6 +21,11 @@ class BulkUploadLettingsLogsController < ApplicationController end end + def guidance + @form = Forms::BulkUploadLettings::PrepareYourFile.new + render "bulk_upload_shared/guidance" + end + private def current_year diff --git a/app/controllers/bulk_upload_sales_logs_controller.rb b/app/controllers/bulk_upload_sales_logs_controller.rb index 5f9b8d9a6..2b3432399 100644 --- a/app/controllers/bulk_upload_sales_logs_controller.rb +++ b/app/controllers/bulk_upload_sales_logs_controller.rb @@ -21,6 +21,11 @@ class BulkUploadSalesLogsController < ApplicationController end end + def guidance + @form = Forms::BulkUploadSales::PrepareYourFile.new + render "bulk_upload_shared/guidance" + end + private def current_year diff --git a/app/models/form/lettings/questions/net_income_known.rb b/app/models/form/lettings/questions/net_income_known.rb index 5e9b1b4da..760c20b9f 100644 --- a/app/models/form/lettings/questions/net_income_known.rb +++ b/app/models/form/lettings/questions/net_income_known.rb @@ -2,7 +2,7 @@ class Form::Lettings::Questions::NetIncomeKnown < ::Form::Question def initialize(id, hsh, page) super @id = "net_income_known" - @check_answer_label = "Do you know the household’s combined income?" + @check_answer_label = "Do you know the household’s combined total income after tax?" @header = "Do you know the household’s combined income after tax?" @type = "radio" @check_answers_card_number = 0 diff --git a/app/models/form/sales/questions/property_local_authority_known.rb b/app/models/form/sales/questions/property_local_authority_known.rb index cbb534a06..912ddbd8e 100644 --- a/app/models/form/sales/questions/property_local_authority_known.rb +++ b/app/models/form/sales/questions/property_local_authority_known.rb @@ -3,7 +3,7 @@ class Form::Sales::Questions::PropertyLocalAuthorityKnown < ::Form::Question super @id = "la_known" @check_answer_label = "Local authority known" - @header = "Do you know the local authority of the property?" + @header = "Do you know the property’s local authority?" @type = "radio" @answer_options = ANSWER_OPTIONS @conditional_for = { "la" => [1] } diff --git a/app/models/forms/bulk_upload_lettings/prepare_your_file.rb b/app/models/forms/bulk_upload_lettings/prepare_your_file.rb index cc6fe2a7e..8b97d9b0b 100644 --- a/app/models/forms/bulk_upload_lettings/prepare_your_file.rb +++ b/app/models/forms/bulk_upload_lettings/prepare_your_file.rb @@ -41,6 +41,10 @@ module Forms end end + def specification_path + "/files/bulk-upload-lettings-specification-2022-23.xlsx" + end + def year_combo "#{year}/#{year + 1 - 2000}" end diff --git a/app/models/forms/bulk_upload_sales/prepare_your_file.rb b/app/models/forms/bulk_upload_sales/prepare_your_file.rb index e15e89262..400419dfb 100644 --- a/app/models/forms/bulk_upload_sales/prepare_your_file.rb +++ b/app/models/forms/bulk_upload_sales/prepare_your_file.rb @@ -39,6 +39,10 @@ module Forms end end + def specification_path + "/files/bulk-upload-sales-specification-2022-23.xlsx" + end + def year_combo "#{year}/#{year + 1 - 2000}" end diff --git a/app/services/bulk_upload/lettings/row_parser.rb b/app/services/bulk_upload/lettings/row_parser.rb index d5a3b035c..f974613e4 100644 --- a/app/services/bulk_upload/lettings/row_parser.rb +++ b/app/services/bulk_upload/lettings/row_parser.rb @@ -143,6 +143,20 @@ class BulkUpload::Lettings::RowParser validates :field_1, presence: { message: I18n.t("validations.not_answered", question: "letting type") }, inclusion: { in: (1..12).to_a, message: I18n.t("validations.invalid_option", question: "letting type") } validates :field_4, presence: { if: proc { [2, 4, 6, 8, 10, 12].include?(field_1) } } + + validates :field_12, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 1 must be a number or the letter R" } + validates :field_13, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 2 must be a number or the letter R" } + validates :field_14, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 3 must be a number or the letter R" } + validates :field_15, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 4 must be a number or the letter R" } + validates :field_16, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 5 must be a number or the letter R" } + validates :field_17, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 6 must be a number or the letter R" } + validates :field_18, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 7 must be a number or the letter R" } + validates :field_19, format: { with: /\A\d{1,3}\z|\AR\z/, message: "Age of person 8 must be a number or the letter R" } + + validates :field_96, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (day)") } + validates :field_97, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (month)") } + validates :field_98, presence: { message: I18n.t("validations.not_answered", question: "tenancy start date (year)") } + validates :field_98, format: { with: /\A\d{2}\z/, message: I18n.t("validations.setup.startdate.year_not_two_digits") } validate :validate_data_types @@ -181,7 +195,12 @@ class BulkUpload::Lettings::RowParser log.errors.each do |error| fields = field_mapping_for_errors[error.attribute] || [] - fields.each { |field| errors.add(field, error.type) } + + fields.each do |field| + unless errors.include?(field) + errors.add(field, error.type) + end + end end errors.blank? @@ -341,7 +360,7 @@ private end def validate_relevant_collection_window - return unless start_date && bulk_upload.form + return if start_date.blank? || bulk_upload.form.blank? unless bulk_upload.form.valid_start_date_for_form?(start_date) errors.add(:field_96, I18n.t("validations.date.outside_collection_window")) @@ -351,6 +370,8 @@ private end def start_date + return if field_98.blank? || field_97.blank? || field_96.blank? + Date.parse("20#{field_98.to_s.rjust(2, '0')}-#{field_97}-#{field_96}") rescue StandardError nil @@ -392,9 +413,17 @@ private next if question.completed?(log) if setup_question?(question) - fields.each { |field| errors.add(field, I18n.t("validations.not_answered", question: question.check_answer_label&.downcase), category: :setup) } + fields.each do |field| + if errors[field].present? + errors.add(field, I18n.t("validations.not_answered", question: question.check_answer_label&.downcase), category: :setup) + end + end else - fields.each { |field| errors.add(field, I18n.t("validations.not_answered", question: question.check_answer_label&.downcase)) } + fields.each do |field| + unless errors.any? { |e| fields.include?(e.attribute) } + errors.add(field, I18n.t("validations.not_answered", question: question.check_answer_label&.downcase)) + end + end end end end @@ -642,28 +671,28 @@ private attributes["declaration"] = field_132 attributes["age1_known"] = field_12 == "R" ? 1 : 0 - attributes["age1"] = field_12 if attributes["age1_known"].zero? + attributes["age1"] = field_12 if attributes["age1_known"].zero? && field_12&.match(/\A\d{1,3}\z|\AR\z/) attributes["age2_known"] = field_13 == "R" ? 1 : 0 - attributes["age2"] = field_13 if attributes["age2_known"].zero? + attributes["age2"] = field_13 if attributes["age2_known"].zero? && field_13&.match(/\A\d{1,3}\z|\AR\z/) attributes["age3_known"] = field_14 == "R" ? 1 : 0 - attributes["age3"] = field_14 if attributes["age3_known"].zero? + attributes["age3"] = field_14 if attributes["age3_known"].zero? && field_14&.match(/\A\d{1,3}\z|\AR\z/) attributes["age4_known"] = field_15 == "R" ? 1 : 0 - attributes["age4"] = field_15 if attributes["age4_known"].zero? + attributes["age4"] = field_15 if attributes["age4_known"].zero? && field_15&.match(/\A\d{1,3}\z|\AR\z/) attributes["age5_known"] = field_16 == "R" ? 1 : 0 - attributes["age5"] = field_16 if attributes["age5_known"].zero? + attributes["age5"] = field_16 if attributes["age5_known"].zero? && field_16&.match(/\A\d{1,3}\z|\AR\z/) attributes["age6_known"] = field_17 == "R" ? 1 : 0 - attributes["age6"] = field_17 if attributes["age6_known"].zero? + attributes["age6"] = field_17 if attributes["age6_known"].zero? && field_17&.match(/\A\d{1,3}\z|\AR\z/) attributes["age7_known"] = field_18 == "R" ? 1 : 0 - attributes["age7"] = field_18 if attributes["age7_known"].zero? + attributes["age7"] = field_18 if attributes["age7_known"].zero? && field_18&.match(/\A\d{1,3}\z|\AR\z/) attributes["age8_known"] = field_19 == "R" ? 1 : 0 - attributes["age8"] = field_19 if attributes["age8_known"].zero? + attributes["age8"] = field_19 if attributes["age8_known"].zero? && field_19&.match(/\A\d{1,3}\z|\AR\z/) attributes["sex1"] = field_20 attributes["sex2"] = field_21 @@ -882,6 +911,8 @@ private 0 when nil rsnvac == 14 ? 1 : 0 + else + field_134 end end diff --git a/app/views/bulk_upload_lettings_logs/forms/prepare_your_file.html.erb b/app/views/bulk_upload_lettings_logs/forms/prepare_your_file.html.erb index 2bfda7e06..1ae824639 100644 --- a/app/views/bulk_upload_lettings_logs/forms/prepare_your_file.html.erb +++ b/app/views/bulk_upload_lettings_logs/forms/prepare_your_file.html.erb @@ -14,7 +14,7 @@

Create your file

diff --git a/app/views/bulk_upload_sales_logs/forms/prepare_your_file.html.erb b/app/views/bulk_upload_sales_logs/forms/prepare_your_file.html.erb index 521953cee..0f4e75bd1 100644 --- a/app/views/bulk_upload_sales_logs/forms/prepare_your_file.html.erb +++ b/app/views/bulk_upload_sales_logs/forms/prepare_your_file.html.erb @@ -14,7 +14,7 @@

Create your file

diff --git a/app/views/bulk_upload_shared/guidance.html.erb b/app/views/bulk_upload_shared/guidance.html.erb new file mode 100644 index 000000000..0c1f67bcc --- /dev/null +++ b/app/views/bulk_upload_shared/guidance.html.erb @@ -0,0 +1,52 @@ +<% content_for :before_content do %> + <%= govuk_back_link href: :back %> +<% end %> + +
+
+ +

How to upload logs in bulk

+ +
+

Uploading sales and lettings logs

+

You can upload one sales or lettings log at a time, or many at once (known as ‘bulk upload’) with a comma-separated values (CSV) spreadsheet file.

+

Bulk upload may be easier if your organisation deals with many logs, or if you can export CSV data from your Housing Management System (HMS). If your organisation only deals with a small amount of logs, or you cannot export CSV data, it’s probably easier to enter logs individually.

+ <%= govuk_warning_text text: "You cannot upload lettings and sales logs with the same template - you must export each data type separately, then upload them" %> +
+ +
+

Creating your CSV files

+

To bulk upload successfully, all spreadsheets must be in the correct CSV format.

+

In most programs, you must resave files as CSV - it’s not usually the default setting. CSV files are also unformatted, so any formatting added before saving (for example colours) will automatically disappear.

+ <%= govuk_details(summary_text: "More about CSV") do %> +

A CSV file is a basic spreadsheet with data values in plain text, and columns separated by commas. Each data row is a new text line.

+

CSV data is easier to process than more common advanced spreadsheet formats, for example Excel. It means CSV is well suited to upload large, or multiple data sets.

+ <% end %> +
+ +
+

Exporting CSV data

+

Export CSV data directly from your current systems, or export then adjust it to CSV.

+

You can then upload it via a button at the top of the lettings and sales logs pages.

+ <%= govuk_details(summary_text: "My organisation has a CMS") do %> +

Some HMS providers sell an add-on "eCORE" module, which exports CSV data for you.

+

It can take HMS providers a while to update these per new collection year, so you may have to wait for updates to export, or adjust your data manually post-export.

+ <% end %> + <%= govuk_details(summary_text: "My organisation does not have a CMS") do %> +

Your organisation’s IT team may be able to export CSV data for you - <%= govuk_link_to "find out more about data specification", @form.specification_path %>. This document outlines:

+
    +
  • required fields
  • +
  • each field's valid response
  • +
  • if/when certain fields can be left blank
  • +
+

Fields can appear in any order, as long as you include the <%= govuk_link_to "template document", @form.template_path %> headers, to easily identify what each column represents. You can rearrange data columns to match your system exports, copy-pasting multiple columns at once. For data stored in multiple systems, you can copy-paste all columns for one system next to each other, repeating this for subsequent system exports.

+ <% end %> +
+ +
+

Getting help

+

There is no step-by-step bulk upload guide like there is with single log upload. However, you can download <%= govuk_link_to "our template", @form.template_path %>, which you can copy-paste data into from your systems column-by-column. You can also view a post-upload report showing any data errors, and our <%= govuk_link_to "data specification", @form.specification_path %> can help fix these.

+

If you still need support mapping data in the way we need, DLUHC’s helpdesk can help. If your data is across multiple systems, or is hard to export as a single file in the correct format, you could try different exports, or copy-pasting data by hand.

+
+
+
diff --git a/config/routes.rb b/config/routes.rb index 74b613989..02a9cb707 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -131,6 +131,7 @@ Rails.application.routes.draw do resources :bulk_upload_lettings_logs, path: "bulk-upload-logs", only: %i[show update] do collection do get :start + get "guidance", to: "bulk_upload_lettings_logs#guidance" end end @@ -165,6 +166,7 @@ Rails.application.routes.draw do resources :bulk_upload_sales_logs, path: "bulk-upload-logs" do collection do get :start + get "guidance", to: "bulk_upload_sales_logs#guidance" end end diff --git a/spec/models/form/lettings/questions/net_income_known_spec.rb b/spec/models/form/lettings/questions/net_income_known_spec.rb new file mode 100644 index 000000000..4a0925fa4 --- /dev/null +++ b/spec/models/form/lettings/questions/net_income_known_spec.rb @@ -0,0 +1,17 @@ +require "rails_helper" + +RSpec.describe Form::Lettings::Questions::NetIncomeKnown do + subject(:question) { described_class.new(nil, {}, nil) } + + describe "#id" do + it "is net_income_known" do + expect(question.id).to eql("net_income_known") + end + end + + describe "#type" do + it "is radio" do + expect(question.type).to eql("radio") + end + end +end diff --git a/spec/requests/bulk_upload_lettings_logs_controller_spec.rb b/spec/requests/bulk_upload_lettings_logs_controller_spec.rb index fc27ed0d1..b915b571c 100644 --- a/spec/requests/bulk_upload_lettings_logs_controller_spec.rb +++ b/spec/requests/bulk_upload_lettings_logs_controller_spec.rb @@ -31,4 +31,28 @@ RSpec.describe BulkUploadLettingsLogsController, type: :request do end end end + + describe "GET /lettings-logs/bulk-upload-logs/guidance" do + context "when not in crossover period" do + let(:expected_year) { FormHandler.instance.forms["current_lettings"].start_date.year } + + it "shows guidance page with correct title" do + Timecop.freeze(2022, 1, 1) do + get "/lettings-logs/bulk-upload-logs/guidance", params: {} + + expect(response.body).to include("How to upload logs in bulk") + end + end + end + + context "when in crossover period" do + it "shows guidance page with correct title" do + Timecop.freeze(2023, 6, 1) do + get "/lettings-logs/bulk-upload-logs/guidance", params: {} + + expect(response.body).to include("How to upload logs in bulk") + end + end + end + end end diff --git a/spec/requests/bulk_upload_sales_logs_controller_spec.rb b/spec/requests/bulk_upload_sales_logs_controller_spec.rb index 348179dd2..3e2aa5910 100644 --- a/spec/requests/bulk_upload_sales_logs_controller_spec.rb +++ b/spec/requests/bulk_upload_sales_logs_controller_spec.rb @@ -31,4 +31,28 @@ RSpec.describe BulkUploadSalesLogsController, type: :request do end end end + + describe "GET /sales-logs/bulk-upload-logs/guidance" do + context "when not in crossover period" do + let(:expected_year) { FormHandler.instance.forms["current_sales"].start_date.year } + + it "shows guidance page with correct title" do + Timecop.freeze(2022, 1, 1) do + get "/sales-logs/bulk-upload-logs/guidance", params: {} + + expect(response.body).to include("How to upload logs in bulk") + end + end + end + + context "when in crossover period" do + it "shows guidance page with correct title" do + Timecop.freeze(2023, 6, 1) do + get "/sales-logs/bulk-upload-logs/guidance", params: {} + + expect(response.body).to include("How to upload logs in bulk") + end + end + end + end end diff --git a/spec/services/bulk_upload/lettings/row_parser_spec.rb b/spec/services/bulk_upload/lettings/row_parser_spec.rb index 867e5028b..73b35d70c 100644 --- a/spec/services/bulk_upload/lettings/row_parser_spec.rb +++ b/spec/services/bulk_upload/lettings/row_parser_spec.rb @@ -27,6 +27,118 @@ RSpec.describe BulkUpload::Lettings::RowParser do } end + let(:valid_attributes) do + { + bulk_upload:, + field_1: "1", + field_4: scheme.old_visible_id, + field_7: "123", + field_96: now.day.to_s, + field_97: now.month.to_s, + field_98: now.strftime("%g"), + field_108: "EC1N", + field_109: "2TD", + field_111: owning_org.old_visible_id, + field_113: managing_org.old_visible_id, + field_130: "1", + field_134: "2", + field_102: "2", + field_103: "1", + field_104: "1", + field_101: "1", + field_133: "2", + field_8: "1", + field_9: "2", + field_132: "1", + + field_12: "42", + field_13: "41", + field_14: "20", + field_15: "18", + field_16: "16", + field_17: "14", + field_18: "12", + field_19: "20", + + field_20: "F", + field_21: "M", + field_22: "F", + field_23: "M", + field_24: "F", + field_25: "M", + field_26: "F", + field_27: "M", + + field_43: "17", + field_44: "18", + + field_28: "P", + field_29: "C", + field_30: "X", + field_31: "R", + field_32: "C", + field_33: "C", + field_34: "X", + + field_35: "1", + field_36: "2", + field_37: "6", + field_38: "7", + field_39: "8", + field_40: "9", + field_41: "0", + field_42: "10", + + field_45: "1", + field_114: "4", + field_46: "1", + + field_47: "1", + + field_118: "2", + + field_66: "5", + field_67: "2", + field_52: "31", + field_61: "3", + field_68: "12", + + field_65: "1", + field_63: "EC1N", + field_64: "2TD", + + field_69: "1", + field_70: "1", + field_71: "", + field_72: "1", + field_73: "", + field_74: "", + + field_75: "1", + field_76: "2", + field_77: "2", + + field_78: "2", + + field_51: "1", + field_50: "2000", + field_116: "2", + field_48: "1", + field_49: "1", + + field_79: "4", + field_80: "1234.56", + field_87: "1", + field_88: "234.56", + + field_106: "15", + field_99: "0", + field_89: now.day.to_s, + field_90: now.month.to_s, + field_91: now.strftime("%g"), + } + end + before do create(:organisation_relationship, parent_organisation: owning_org, child_organisation: managing_org) end @@ -83,117 +195,7 @@ RSpec.describe BulkUpload::Lettings::RowParser do end context "when valid row" do - let(:attributes) do - { - bulk_upload:, - field_1: "1", - field_4: scheme.old_visible_id, - field_7: "123", - field_96: now.day.to_s, - field_97: now.month.to_s, - field_98: now.strftime("%g"), - field_108: "EC1N", - field_109: "2TD", - field_111: owning_org.old_visible_id, - field_113: managing_org.old_visible_id, - field_130: "1", - field_134: "2", - field_102: "2", - field_103: "1", - field_104: "1", - field_101: "1", - field_133: "2", - field_8: "1", - field_9: "2", - field_132: "1", - - field_12: "42", - field_13: "41", - field_14: "20", - field_15: "18", - field_16: "16", - field_17: "14", - field_18: "12", - field_19: "20", - - field_20: "F", - field_21: "M", - field_22: "F", - field_23: "M", - field_24: "F", - field_25: "M", - field_26: "F", - field_27: "M", - - field_43: "17", - field_44: "18", - - field_28: "P", - field_29: "C", - field_30: "X", - field_31: "R", - field_32: "C", - field_33: "C", - field_34: "X", - - field_35: "1", - field_36: "2", - field_37: "6", - field_38: "7", - field_39: "8", - field_40: "9", - field_41: "0", - field_42: "10", - - field_45: "1", - field_114: "4", - field_46: "1", - - field_47: "1", - - field_118: "2", - - field_66: "5", - field_67: "2", - field_52: "31", - field_61: "3", - field_68: "12", - - field_65: "1", - field_63: "EC1N", - field_64: "2TD", - - field_69: "1", - field_70: "1", - field_71: "", - field_72: "1", - field_73: "", - field_74: "", - - field_75: "1", - field_76: "2", - field_77: "2", - - field_78: "2", - - field_51: "1", - field_50: "2000", - field_116: "2", - field_48: "1", - field_49: "1", - - field_79: "4", - field_80: "1234.56", - field_87: "1", - field_88: "234.56", - - field_106: "15", - field_99: "0", - field_89: now.day.to_s, - field_90: now.month.to_s, - field_91: now.strftime("%g"), - } - end + let(:attributes) { valid_attributes } it "returns true" do expect(parser).to be_valid @@ -216,7 +218,7 @@ RSpec.describe BulkUpload::Lettings::RowParser do it "has errors on setup fields" do errors = parser.errors.select { |e| e.options[:category] == :setup }.map(&:attribute) - expect(errors).to eql(%i[field_1 field_129 field_130 field_98 field_97 field_96 field_111 field_113]) + expect(errors).to eql(%i[field_1 field_98 field_97 field_96 field_111 field_113]) end end @@ -420,6 +422,16 @@ RSpec.describe BulkUpload::Lettings::RowParser do end end + describe "#field_12" do + context "when set to a non-sensical value" do + let(:attributes) { valid_attributes.merge(field_12: "A", field_35: "1") } + + it "returns only one error" do + expect(parser.errors[:field_12].size).to be(1) + end + end + end + describe "#field_52" do # leaving reason context "when field_134 is 1 meaning it is a renewal" do context "when field_52 is 40" do @@ -529,7 +541,7 @@ RSpec.describe BulkUpload::Lettings::RowParser do end describe "fields 96, 97, 98 => startdate" do - context "when any one of these fields is blank" do + context "when all of these fields are blank" do let(:attributes) { { bulk_upload:, field_1: "1", field_96: nil, field_97: nil, field_98: nil } } it "returns an error" do @@ -541,6 +553,18 @@ RSpec.describe BulkUpload::Lettings::RowParser do end end + context "when one of these fields is blank" do + let(:attributes) { { bulk_upload:, field_1: "1", field_96: "1", field_97: "1", field_98: nil } } + + it "returns an error only on blank field" do + parser.valid? + + expect(parser.errors[:field_96]).to be_blank + expect(parser.errors[:field_97]).to be_blank + expect(parser.errors[:field_98]).to be_present + end + end + context "when field 98 is 4 digits instead of 2" do let(:attributes) { { bulk_upload:, field_98: "2022" } } @@ -658,7 +682,7 @@ RSpec.describe BulkUpload::Lettings::RowParser do describe "#field_134" do context "when an unpermitted value" do - let(:attributes) { { bulk_upload:, field_134: 3 } } + let(:attributes) { { bulk_upload:, field_134: "3" } } it "has errors on the field" do expect(parser.errors[:field_134]).to be_present @@ -724,6 +748,18 @@ RSpec.describe BulkUpload::Lettings::RowParser do expect(parser.log.public_send(age)).to be(50) end end + + context "when #{field} is a non-sensical value" do + let(:attributes) { { bulk_upload:, field.to_s => "A" } } + + it "sets ##{known} to 0" do + expect(parser.log.public_send(known)).to be(0) + end + + it "sets ##{age} to nil" do + expect(parser.log.public_send(age)).to be_nil + end + end end end diff --git a/spec/services/bulk_upload/lettings/validator_spec.rb b/spec/services/bulk_upload/lettings/validator_spec.rb index ca52a19fe..b9d351281 100644 --- a/spec/services/bulk_upload/lettings/validator_spec.rb +++ b/spec/services/bulk_upload/lettings/validator_spec.rb @@ -42,7 +42,7 @@ RSpec.describe BulkUpload::Lettings::Validator do it "create validation error with correct values" do validator.call - error = BulkUploadError.order(:row, :field).first + error = BulkUploadError.find_by(field: "field_11") expect(error.field).to eql("field_11") expect(error.error).to eql("You must only answer the length of the tenancy if it's fixed-term") @@ -260,11 +260,14 @@ RSpec.describe BulkUpload::Lettings::Validator do 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_csv_row) - file.write(BulkUpload::LogToCsv.new(log: log_2, line_ending: "\r\n", col_offset: 0).to_csv_row) - file.write(BulkUpload::LogToCsv.new(log: log_3, line_ending: "\r\n", col_offset: 0).to_csv_row) - file.write(BulkUpload::LogToCsv.new(log: log_4, line_ending: "\r\n", col_offset: 0).to_csv_row) - file.write(BulkUpload::LogToCsv.new(log: log_5, line_ending: "\r\n", col_offset: 0).to_csv_row) + 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_csv_row) + file.write(BulkUpload::LogToCsv.new(log: log_2, line_ending: "\r\n", col_offset: 0, overrides:).to_csv_row) + file.write(BulkUpload::LogToCsv.new(log: log_3, line_ending: "\r\n", col_offset: 0, overrides:).to_csv_row) + file.write(BulkUpload::LogToCsv.new(log: log_4, line_ending: "\r\n", col_offset: 0, overrides:).to_csv_row) + file.write(BulkUpload::LogToCsv.new(log: log_5, line_ending: "\r\n", col_offset: 0, overrides:).to_csv_row) + file.close end diff --git a/spec/support/bulk_upload/log_to_csv.rb b/spec/support/bulk_upload/log_to_csv.rb index 628ee14b8..e15c9173b 100644 --- a/spec/support/bulk_upload/log_to_csv.rb +++ b/spec/support/bulk_upload/log_to_csv.rb @@ -22,14 +22,14 @@ class BulkUpload::LogToCsv log.tenancy, log.tenancyother, # 10 log.tenancylength, - log.age1, - log.age2, - log.age3, - log.age4, - log.age5, - log.age6, - log.age7, - log.age8, + log.age1 || overrides[:age1], + log.age2 || overrides[:age2], + log.age3 || overrides[:age3], + log.age4 || overrides[:age4], + log.age5 || overrides[:age5], + log.age6 || overrides[:age6], + log.age7 || overrides[:age7], + log.age8 || overrides[:age8], log.sex1, # 20 log.sex2,