Browse Source

Merge branch 'main' into CLDC-1917-startdate-validation

# Conflicts:
#	config/locales/en.yml
pull/1378/head
natdeanlewissoftwire 3 years ago
parent
commit
2f17ae2cbe
  1. 3
      app/mailers/bulk_upload_mailer.rb
  2. 15
      app/models/validations/sales/financial_validations.rb
  3. 21
      app/services/bulk_upload/lettings/validator.rb
  4. 1
      app/services/bulk_upload/lettings/year2022/csv_parser.rb
  5. 8
      app/services/bulk_upload/lettings/year2022/row_parser.rb
  6. 1
      app/services/bulk_upload/lettings/year2023/csv_parser.rb
  7. 8
      app/services/bulk_upload/lettings/year2023/row_parser.rb
  8. 6
      app/services/bulk_upload/processor.rb
  9. 8
      config/locales/en.yml
  10. 3
      spec/mailers/bulk_upload_mailer_spec.rb
  11. 42
      spec/models/validations/sales/financial_validations_spec.rb
  12. 52
      spec/services/bulk_upload/lettings/validator_spec.rb
  13. 1
      spec/services/bulk_upload/processor_spec.rb

3
app/mailers/bulk_upload_mailer.rb

@ -100,7 +100,7 @@ class BulkUploadMailer < NotifyMailer
) )
end end
def send_bulk_upload_failed_service_error_mail(bulk_upload:) def send_bulk_upload_failed_service_error_mail(bulk_upload:, errors: [])
bulk_upload_link = if bulk_upload.lettings? bulk_upload_link = if bulk_upload.lettings?
start_bulk_upload_lettings_logs_url start_bulk_upload_lettings_logs_url
else else
@ -115,6 +115,7 @@ class BulkUploadMailer < NotifyMailer
upload_timestamp: bulk_upload.created_at, upload_timestamp: bulk_upload.created_at,
lettings_or_sales: bulk_upload.log_type, lettings_or_sales: bulk_upload.log_type,
year_combo: bulk_upload.year_combo, year_combo: bulk_upload.year_combo,
errors: errors.map { |e| "- #{e}" }.join("\n"),
bulk_upload_link:, bulk_upload_link:,
}, },
) )

15
app/models/validations/sales/financial_validations.rb

@ -52,6 +52,21 @@ module Validations::Sales::FinancialValidations
end end
end end
def validate_percentage_bought_at_least_threshold(record)
return unless record.stairbought && record.type
threshold = if [2, 16, 18, 24].include? record.type
10
else
1
end
if threshold && record.stairbought < threshold
record.errors.add :stairbought, I18n.t("validations.financial.staircasing.percentage_bought_must_be_at_least_threshold", threshold:)
record.errors.add :type, I18n.t("validations.setup.type.percentage_bought_must_be_at_least_threshold", threshold:)
end
end
def validate_child_income(record) def validate_child_income(record)
return unless record.income2 && record.ecstat2 return unless record.income2 && record.ecstat2

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

@ -9,6 +9,7 @@ class BulkUpload::Lettings::Validator
attr_reader :bulk_upload, :path attr_reader :bulk_upload, :path
validate :validate_file_not_empty validate :validate_file_not_empty
validate :validate_min_columns
validate :validate_max_columns validate :validate_max_columns
def initialize(bulk_upload:, path:) def initialize(bulk_upload:, path:)
@ -23,14 +24,16 @@ class BulkUpload::Lettings::Validator
row = index + row_offset + 1 row = index + row_offset + 1
row_parser.errors.each do |error| row_parser.errors.each do |error|
col = csv_parser.column_for_field(error.attribute.to_s)
bulk_upload.bulk_upload_errors.create!( bulk_upload.bulk_upload_errors.create!(
field: error.attribute, field: error.attribute,
error: error.message, error: error.message,
tenant_code: row_parser.field_7, tenant_code: row_parser.tenant_code,
property_ref: row_parser.field_100, property_ref: row_parser.property_ref,
row:, row:,
cell: "#{cols[field_number_for_attribute(error.attribute) - col_offset + 1]}#{row}", cell: "#{col}#{row}",
col: csv_parser.column_for_field(error.attribute.to_s), col:,
category: error.options[:category], category: error.options[:category],
) )
end end
@ -127,12 +130,20 @@ private
end end
end end
def validate_min_columns
return if halt_validations?
column_count = rows.map(&:size).min
errors.add(:base, :under_min_column_count) if column_count < csv_parser.class::MIN_COLUMNS
end
def validate_max_columns def validate_max_columns
return if halt_validations? return if halt_validations?
column_count = rows.map(&:size).max column_count = rows.map(&:size).max
errors.add(:file, :column_count) if column_count > csv_parser.class::MAX_COLUMNS errors.add(:base, :over_max_column_count) if column_count > csv_parser.class::MAX_COLUMNS
end end
def halt_validations! def halt_validations!

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

@ -1,6 +1,7 @@
require "csv" require "csv"
class BulkUpload::Lettings::Year2022::CsvParser class BulkUpload::Lettings::Year2022::CsvParser
MIN_COLUMNS = 134
MAX_COLUMNS = 136 MAX_COLUMNS = 136
attr_reader :path attr_reader :path

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

@ -372,6 +372,14 @@ class BulkUpload::Lettings::Year2022::RowParser
block_log_creation block_log_creation
end end
def tenant_code
field_7
end
def property_ref
field_100
end
private private
def validate_location_related def validate_location_related

1
app/services/bulk_upload/lettings/year2023/csv_parser.rb

@ -1,6 +1,7 @@
require "csv" require "csv"
class BulkUpload::Lettings::Year2023::CsvParser class BulkUpload::Lettings::Year2023::CsvParser
MIN_COLUMNS = 141
MAX_COLUMNS = 143 MAX_COLUMNS = 143
attr_reader :path attr_reader :path

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

@ -375,6 +375,14 @@ class BulkUpload::Lettings::Year2023::RowParser
block_log_creation block_log_creation
end end
def tenant_code
field_13
end
def property_ref
field_14
end
private private
def validate_needs_type_present def validate_needs_type_present

6
app/services/bulk_upload/processor.rb

@ -8,7 +8,7 @@ class BulkUpload::Processor
def call def call
download download
return send_failure_mail if validator.invalid? return send_failure_mail(errors: validator.errors.full_messages) if validator.invalid?
validator.call validator.call
@ -62,9 +62,9 @@ private
validator.create_logs? && bulk_upload.logs.group(:status).count.keys == %w[completed] validator.create_logs? && bulk_upload.logs.group(:status).count.keys == %w[completed]
end end
def send_failure_mail def send_failure_mail(errors: [])
BulkUploadMailer BulkUploadMailer
.send_bulk_upload_failed_service_error_mail(bulk_upload:) .send_bulk_upload_failed_service_error_mail(bulk_upload:, errors:)
.deliver_later .deliver_later
end end

8
config/locales/en.yml

@ -41,6 +41,11 @@ en:
activemodel: activemodel:
errors: errors:
models: models:
bulk_upload/lettings/validator:
attributes:
base:
over_max_column_count: "too many columns, please ensure you have used the correct template"
under_min_column_count: "too few columns, please ensure you have used the correct template"
forms/bulk_upload_lettings/year: forms/bulk_upload_lettings/year:
attributes: attributes:
year: year:
@ -154,6 +159,8 @@ en:
Enter a date within the %{current_start_year_short}/%{current_end_year_short} collection year, which is between %{current_start_year_long} and %{current_end_year_long} Enter a date within the %{current_start_year_short}/%{current_end_year_short} collection year, which is between %{current_start_year_long} and %{current_end_year_long}
previous_and_current_financial_year: previous_and_current_financial_year:
"Enter a date within the %{previous_start_year_short}/%{previous_end_year_short} or %{previous_end_year_short}/%{current_end_year_short} collection years, which is between %{previous_start_year_long} and %{current_end_year_long}" "Enter a date within the %{previous_start_year_short}/%{previous_end_year_short} or %{previous_end_year_short}/%{current_end_year_short} collection years, which is between %{previous_start_year_long} and %{current_end_year_long}"
type:
percentage_bought_must_be_at_least_threshold: "The minimum increase in equity while staircasing is %{threshold}% for this shared ownership type"
startdate: startdate:
current_financial_year: current_financial_year:
@ -301,6 +308,7 @@ en:
staircasing: staircasing:
percentage_bought_must_be_greater_than_percentage_owned: "Total percentage buyer now owns must be more than percentage bought in this transaction" percentage_bought_must_be_greater_than_percentage_owned: "Total percentage buyer now owns must be more than percentage bought in this transaction"
older_person_percentage_owned_maximum_75: "Percentage cannot be above 75% under Older Person's Shared Ownership" older_person_percentage_owned_maximum_75: "Percentage cannot be above 75% under Older Person's Shared Ownership"
percentage_bought_must_be_at_least_threshold: "The minimum increase in equity while staircasing is %{threshold}%"
household: household:
reasonpref: reasonpref:

3
spec/mailers/bulk_upload_mailer_spec.rb

@ -72,11 +72,12 @@ RSpec.describe BulkUploadMailer do
upload_timestamp: bulk_upload.created_at, upload_timestamp: bulk_upload.created_at,
lettings_or_sales: bulk_upload.log_type, lettings_or_sales: bulk_upload.log_type,
year_combo: bulk_upload.year_combo, year_combo: bulk_upload.year_combo,
errors: "- foo\n- bar",
bulk_upload_link: start_bulk_upload_lettings_logs_url, bulk_upload_link: start_bulk_upload_lettings_logs_url,
}, },
) )
mailer.send_bulk_upload_failed_service_error_mail(bulk_upload:) mailer.send_bulk_upload_failed_service_error_mail(bulk_upload:, errors: %w[foo bar])
end end
end end

42
spec/models/validations/sales/financial_validations_spec.rb

@ -160,6 +160,48 @@ RSpec.describe Validations::Sales::FinancialValidations do
end end
end end
describe "#validate_percentage_bought_at_least_threshold" do
let(:record) { FactoryBot.create(:sales_log) }
it "adds an error to stairbought and type if the percentage bought is less than the threshold (which is 1% by default, but higher for some shared ownership types)" do
record.stairbought = 9
[2, 16, 18, 24].each do |type|
record.type = type
financial_validator.validate_percentage_bought_at_least_threshold(record)
expect(record.errors["stairbought"]).to eq(["The minimum increase in equity while staircasing is 10%"])
expect(record.errors["type"]).to eq(["The minimum increase in equity while staircasing is 10% for this shared ownership type"])
record.errors.clear
end
record.stairbought = 0
[28, 30, 31, 32].each do |type|
record.type = type
financial_validator.validate_percentage_bought_at_least_threshold(record)
expect(record.errors["stairbought"]).to eq(["The minimum increase in equity while staircasing is 1%"])
expect(record.errors["type"]).to eq(["The minimum increase in equity while staircasing is 1% for this shared ownership type"])
record.errors.clear
end
end
it "doesn't add an error to stairbought and type if the percentage bought is less than the threshold (which is 1% by default, but higher for some shared ownership types)" do
record.stairbought = 10
[2, 16, 18, 24].each do |type|
record.type = type
financial_validator.validate_percentage_bought_at_least_threshold(record)
expect(record.errors).to be_empty
record.errors.clear
end
record.stairbought = 1
[28, 30, 31, 32].each do |type|
record.type = type
financial_validator.validate_percentage_bought_at_least_threshold(record)
expect(record.errors).to be_empty
record.errors.clear
end
end
end
describe "#validate_percentage_owned_not_too_much_if_older_person" do describe "#validate_percentage_owned_not_too_much_if_older_person" do
let(:record) { FactoryBot.create(:sales_log) } let(:record) { FactoryBot.create(:sales_log) }

52
spec/services/bulk_upload/lettings/validator_spec.rb

@ -28,6 +28,18 @@ RSpec.describe BulkUpload::Lettings::Validator do
end end
end end
context "when file has too few columns" do
before do
file.write("a," * 132)
file.write("\n")
file.rewind
end
it "is not valid" do
expect(validator).not_to be_valid
end
end
context "when incorrect headers" context "when incorrect headers"
end end
@ -59,6 +71,46 @@ RSpec.describe BulkUpload::Lettings::Validator do
end end
end end
context "with arbitrary ordered 23/24 csv" do
let(:bulk_upload) { create(:bulk_upload, user:, year: 2023) }
let(:log) { build(:lettings_log, :completed) }
let(:file) { Tempfile.new }
let(:path) { file.path }
let(:seed) { 321 }
around do |example|
FormHandler.instance.use_real_forms!
example.run
FormHandler.instance.use_fake_forms!
end
before do
file.write(BulkUpload::LogToCsv.new(log:, line_ending: "\r\n").default_2023_field_numbers_row(seed:))
file.write(BulkUpload::LogToCsv.new(log:, line_ending: "\r\n").to_2023_csv_row(seed:))
file.close
end
it "creates validation errors" do
expect { validator.call }.to change(BulkUploadError, :count)
end
it "create validation error with correct values" do
validator.call
error = BulkUploadError.find_by(field: "field_5")
expect(error.field).to eql("field_5")
expect(error.error).to eql("You must answer letting type")
expect(error.tenant_code).to eql(log.tenancycode)
expect(error.property_ref).to eql(log.propcode)
expect(error.row).to eql("2")
expect(error.cell).to eql("DD2")
expect(error.col).to eql("DD")
end
end
context "with unix line endings" do context "with unix line endings" do
let(:fixture_path) { file_fixture("2022_23_lettings_bulk_upload.csv") } let(:fixture_path) { file_fixture("2022_23_lettings_bulk_upload.csv") }
let(:file) { Tempfile.new } let(:file) { Tempfile.new }

1
spec/services/bulk_upload/processor_spec.rb

@ -21,6 +21,7 @@ RSpec.describe BulkUpload::Processor do
BulkUpload::Lettings::Validator, BulkUpload::Lettings::Validator,
invalid?: true, invalid?: true,
call: nil, call: nil,
errors: [],
) )
end end

Loading…
Cancel
Save