Browse Source

Merge branch 'main' into CLDC-2257-refactor-filters-3

pull/1675/head
natdeanlewissoftwire 3 years ago
parent
commit
d73396d77f
  1. 14
      app/helpers/check_answers_helper.rb
  2. 32
      app/services/bulk_upload/lettings/validator.rb
  3. 23
      app/services/bulk_upload/lettings/year2022/row_parser.rb
  4. 23
      app/services/bulk_upload/lettings/year2023/row_parser.rb
  5. 6
      app/services/bulk_upload/processor.rb
  6. 1
      app/services/bulk_upload/sales/log_creator.rb
  7. 19
      app/services/bulk_upload/sales/validator.rb
  8. 49
      app/services/bulk_upload/sales/year2022/row_parser.rb
  9. 21
      app/services/bulk_upload/sales/year2023/row_parser.rb
  10. 5
      app/views/form/_check_answers_summary_list.html.erb
  11. 2
      config/cloud_foundry/review_manifest.yml
  12. 4
      config/initializers/sidekiq.rb
  13. 11
      config/locales/en.yml
  14. 15450
      config/rent_range_data/2022.csv
  15. 15125
      config/rent_range_data/2023.csv
  16. 10
      docs/setup.md
  17. 4
      manifest.yml
  18. 1
      spec/factories/sales_log.rb
  19. 14
      spec/helpers/check_answers_helper_spec.rb
  20. 322
      spec/models/lettings_log_spec.rb
  21. 44
      spec/models/sales_log_spec.rb
  22. 17
      spec/services/bulk_upload/lettings/validator_spec.rb
  23. 12
      spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb
  24. 12
      spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb
  25. 10
      spec/services/bulk_upload/processor_spec.rb
  26. 16
      spec/services/bulk_upload/sales/validator_spec.rb
  27. 36
      spec/services/bulk_upload/sales/year2022/row_parser_spec.rb
  28. 12
      spec/services/bulk_upload/sales/year2023/row_parser_spec.rb
  29. 2
      spec/services/csv/sales_log_csv_service_spec.rb

14
app/helpers/check_answers_helper.rb

@ -46,10 +46,22 @@ private
end end
def get_answer_label(question, lettings_log) def get_answer_label(question, lettings_log)
question.answer_label(lettings_log, current_user).presence || "<span class=\"app-!-colour-muted\">You didn’t answer this question</span>".html_safe question.answer_label(lettings_log, current_user).presence || unanswered_value(log: lettings_log)
end end
def get_question_label(question) def get_question_label(question)
[question.question_number_string, question.check_answer_label.to_s.presence || question.header.to_s].compact.join(" - ") [question.question_number_string, question.check_answer_label.to_s.presence || question.header.to_s].compact.join(" - ")
end end
def unanswered_value(log:)
if bulk_uploaded?(log:)
"<span class=\"app-!-colour-red\">You still need to answer this question</span>".html_safe
else
"<span class=\"app-!-colour-muted\">You didn’t answer this question</span>".html_safe
end
end
def bulk_uploaded?(log:)
log.bulk_upload
end
end end

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

@ -1,5 +1,3 @@
require "csv"
class BulkUpload::Lettings::Validator class BulkUpload::Lettings::Validator
include ActiveModel::Validations include ActiveModel::Validations
@ -16,9 +14,11 @@ class BulkUpload::Lettings::Validator
end end
def call def call
row_parsers.each_with_index do |row_parser, index| row_parsers.each(&:valid?)
row_parser.valid?
validate_duplicate_rows if FeatureToggle.bulk_upload_duplicate_log_check_enabled?
row_parsers.each_with_index do |row_parser, index|
row = index + row_offset + 1 row = index + row_offset + 1
row_parser.errors.each do |error| row_parser.errors.each do |error|
@ -69,25 +69,25 @@ class BulkUpload::Lettings::Validator
errors.count == errors.where(category: "soft_validation").count && errors.count.positive? errors.count == errors.where(category: "soft_validation").count && errors.count.positive?
end end
def over_column_error_threshold? def any_logs_already_exist?
fields = ("field_1".."field_134").to_a row_parsers.any?(&:log_already_exists?)
percentage_threshold = (row_parsers.size * COLUMN_PERCENTAGE_ERROR_THRESHOLD).ceil end
fields.any? do |field| private
count = row_parsers.count { |row_parser| row_parser.errors[field].present? }
next if count < COLUMN_ABSOLUTE_ERROR_THRESHOLD # n^2 algo
def validate_duplicate_rows
row_parsers.each do |rp|
dupe = row_parsers.reject { |r| r.object_id.equal?(rp.object_id) }.any? do |rp_counter|
rp.spreadsheet_duplicate_hash == rp_counter.spreadsheet_duplicate_hash
end
count > percentage_threshold if dupe
rp.add_duplicate_found_in_spreadsheet_errors
end end
end end
def any_logs_already_exist?
row_parsers.any?(&:log_already_exists?)
end end
private
def any_logs_invalid? def any_logs_invalid?
row_parsers.any? { |row_parser| row_parser.log.invalid? } row_parsers.any? { |row_parser| row_parser.log.invalid? }
end end

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

@ -438,6 +438,29 @@ class BulkUpload::Lettings::Year2022::RowParser
.exists?(duplicate_check_fields.index_with { |field| log.public_send(field) }) .exists?(duplicate_check_fields.index_with { |field| log.public_send(field) })
end end
def spreadsheet_duplicate_hash
attributes.slice(
"field_5", # location
"field_12", # age1
"field_20", # sex1
"field_35", # ecstat1
"field_84", # tcharge
"field_96", # startdate
"field_97", # startdate
"field_98", # startdate
"field_100", # propcode
"field_108", # postcode
"field_109", # postcode
"field_111", # owning org
)
end
def add_duplicate_found_in_spreadsheet_errors
spreadsheet_duplicate_hash.each_key do |field|
errors.add(field, :spreadsheet_dupe, category: :setup)
end
end
private private
def validate_declaration_acceptance def validate_declaration_acceptance

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

@ -455,6 +455,29 @@ class BulkUpload::Lettings::Year2023::RowParser
.exists?(duplicate_check_fields.index_with { |field| log.public_send(field) }) .exists?(duplicate_check_fields.index_with { |field| log.public_send(field) })
end end
def spreadsheet_duplicate_hash
attributes.slice(
"field_1", # owning org
"field_7", # startdate
"field_8", # startdate
"field_9", # startdate
"field_14", # propcode
"field_17", # location
"field_23", # postcode
"field_24", # postcode
"field_46", # age1
"field_47", # sex1
"field_50", # ecstat1
"field_132", # tcharge
)
end
def add_duplicate_found_in_spreadsheet_errors
spreadsheet_duplicate_hash.each_key do |field|
errors.add(field, :spreadsheet_dupe, category: :setup)
end
end
private private
def validate_declaration_acceptance def validate_declaration_acceptance

6
app/services/bulk_upload/processor.rb

@ -6,6 +6,8 @@ class BulkUpload::Processor
end end
def call def call
destroy_any_existing_errors_from_prior_run
download download
return send_failure_mail(errors: validator.errors.full_messages) if validator.invalid? return send_failure_mail(errors: validator.errors.full_messages) if validator.invalid?
@ -46,6 +48,10 @@ class BulkUpload::Processor
private private
def destroy_any_existing_errors_from_prior_run
bulk_upload.bulk_upload_errors.destroy_all
end
def send_how_to_fix_upload_mail def send_how_to_fix_upload_mail
BulkUploadMailer BulkUploadMailer
.send_how_to_fix_upload_mail(bulk_upload:) .send_how_to_fix_upload_mail(bulk_upload:)

1
app/services/bulk_upload/sales/log_creator.rb

@ -16,7 +16,6 @@ class BulkUpload::Sales::LogCreator
row_parser.log.bulk_upload = bulk_upload row_parser.log.bulk_upload = bulk_upload
row_parser.log.skip_update_status = true row_parser.log.skip_update_status = true
row_parser.log.status = "pending" row_parser.log.status = "pending"
row_parser.log.status_cache = row_parser.log.calculate_status row_parser.log.status_cache = row_parser.log.calculate_status
begin begin

19
app/services/bulk_upload/sales/validator.rb

@ -13,9 +13,11 @@ class BulkUpload::Sales::Validator
end end
def call def call
row_parsers.each_with_index do |row_parser, index| row_parsers.each(&:valid?)
row_parser.valid?
validate_duplicate_rows if FeatureToggle.bulk_upload_duplicate_log_check_enabled?
row_parsers.each_with_index do |row_parser, index|
row = index + row_offset + 1 row = index + row_offset + 1
row_parser.errors.each do |error| row_parser.errors.each do |error|
@ -67,6 +69,19 @@ class BulkUpload::Sales::Validator
private private
# n^2 algo
def validate_duplicate_rows
row_parsers.each do |rp|
dupe = row_parsers.reject { |r| r.object_id.equal?(rp.object_id) }.any? do |rp_counter|
rp.spreadsheet_duplicate_hash == rp_counter.spreadsheet_duplicate_hash
end
if dupe
rp.add_duplicate_found_in_spreadsheet_errors
end
end
end
def any_logs_invalid? def any_logs_invalid?
row_parsers.any? { |row_parser| row_parser.log.invalid? } row_parsers.any? { |row_parser| row_parser.log.invalid? }
end end

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

@ -324,6 +324,27 @@ class BulkUpload::Sales::Year2022::RowParser
if: proc { field_84 == 12 }, if: proc { field_84 == 12 },
on: :after_log on: :after_log
validates :field_68,
presence: {
message: I18n.t("validations.not_answered", question: "What was the full purchase price?"),
},
if: :shared_ownership?,
on: :after_log
validates :field_77,
presence: {
message: I18n.t("validations.not_answered", question: "What was the full purchase price?"),
},
if: :discounted_ownership?,
on: :after_log
validates :field_87,
presence: {
message: I18n.t("validations.not_answered", question: "What was the full purchase price?"),
},
if: :outright_sale?,
on: :after_log
validates :field_109, presence: { message: I18n.t("validations.not_answered", question: "more than 2 buyers"), category: :setup }, if: :joint_purchase?, on: :after_log validates :field_109, presence: { message: I18n.t("validations.not_answered", question: "more than 2 buyers"), category: :setup }, if: :joint_purchase?, on: :after_log
validates :field_113, presence: { message: I18n.t("validations.not_answered", question: "ownership type"), category: :setup }, on: :after_log validates :field_113, presence: { message: I18n.t("validations.not_answered", question: "ownership type"), category: :setup }, on: :after_log
@ -411,6 +432,27 @@ class BulkUpload::Sales::Year2022::RowParser
field_1 field_1
end end
def spreadsheet_duplicate_hash
attributes.slice(
"field_1", # purcahser code
"field_2", # saledate
"field_3", # saledate
"field_4", # saledate
"field_7", # age1
"field_13", # sex1
"field_24", # ecstat1
"field_54", # postcode
"field_55", # postcode
"field_92", # owning org
)
end
def add_duplicate_found_in_spreadsheet_errors
spreadsheet_duplicate_hash.each_key do |field|
errors.add(field, :spreadsheet_dupe, category: :setup)
end
end
private private
def validate_data_protection_answered def validate_data_protection_answered
@ -517,7 +559,6 @@ private
lanomagr: %i[field_65], lanomagr: %i[field_65],
frombeds: %i[field_66], frombeds: %i[field_66],
fromprop: %i[field_67], fromprop: %i[field_67],
value: %i[field_68 field_77 field_87],
equity: %i[field_69], equity: %i[field_69],
mortgage: %i[field_70 field_80 field_88], mortgage: %i[field_70 field_80 field_88],
extrabor: %i[field_71 field_81 field_89], extrabor: %i[field_71 field_81 field_89],
@ -1013,15 +1054,11 @@ private
next if log.optional_fields.include?(question.id) next if log.optional_fields.include?(question.id)
next if question.completed?(log) next if question.completed?(log)
if setup_question?(question)
fields.each do |field| fields.each do |field|
unless errors.any? { |e| fields.include?(e.attribute) } unless errors.any? { |e| fields.include?(e.attribute) }
if setup_question?(question)
errors.add(field, I18n.t("validations.not_answered", question: question.check_answer_label&.downcase), category: :setup) errors.add(field, I18n.t("validations.not_answered", question: question.check_answer_label&.downcase), category: :setup)
end
end
else else
fields.each do |field|
unless errors.any? { |e| fields.include?(e.attribute) }
errors.add(field, I18n.t("validations.not_answered", question: question.check_answer_label&.downcase)) errors.add(field, I18n.t("validations.not_answered", question: question.check_answer_label&.downcase))
end end
end end

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

@ -533,6 +533,27 @@ class BulkUpload::Sales::Year2023::RowParser
field_6 field_6
end end
def spreadsheet_duplicate_hash
attributes.slice(
"field_1", # owning org
"field_3", # saledate
"field_4", # saledate
"field_5", # saledate
"field_6", # purchaser_code
"field_24", # postcode
"field_25", # postcode
"field_30", # age1
"field_31", # sex1
"field_35", # ecstat1
)
end
def add_duplicate_found_in_spreadsheet_errors
spreadsheet_duplicate_hash.each_key do |field|
errors.add(field, :spreadsheet_dupe, category: :setup)
end
end
private private
def validate_data_protection_answered def validate_data_protection_answered

5
app/views/form/_check_answers_summary_list.html.erb

@ -2,13 +2,16 @@
<% questions.each do |question| %> <% questions.each do |question| %>
<% summary_list.row do |row| %> <% summary_list.row do |row| %>
<% row.key { get_question_label(question) } %> <% row.key { get_question_label(question) } %>
<% row.value do %> <% row.value do %>
<%= simple_format( <%= simple_format(
get_answer_label(question, @log), get_answer_label(question, @log),
wrapper_tag: "span", wrapper_tag: "span",
class: "govuk-!-margin-right-4", class: "govuk-!-margin-right-4",
) %> ) %>
<% extra_value = question.get_extra_check_answer_value(@log) %> <% extra_value = question.get_extra_check_answer_value(@log) %>
<% if extra_value && question.answer_label(@log, current_user).present? %> <% if extra_value && question.answer_label(@log, current_user).present? %>
<%= simple_format( <%= simple_format(
extra_value, extra_value,
@ -16,10 +19,12 @@
class: "govuk-!-font-weight-regular app-!-colour-muted", class: "govuk-!-font-weight-regular app-!-colour-muted",
) %> ) %>
<% end %> <% end %>
<% question.get_inferred_answers(@log).each do |inferred_answer| %> <% question.get_inferred_answers(@log).each do |inferred_answer| %>
<span class="govuk-!-font-weight-regular app-!-colour-muted"><%= inferred_answer %></span> <span class="govuk-!-font-weight-regular app-!-colour-muted"><%= inferred_answer %></span>
<% end %> <% end %>
<% end %> <% end %>
<% if @log.collection_period_open? %> <% if @log.collection_period_open? %>
<% row.action( <% row.action(
text: question.action_text(@log), text: question.action_text(@log),

2
config/cloud_foundry/review_manifest.yml

@ -8,7 +8,7 @@ defaults: &defaults
instances: 1 instances: 1
memory: 1G memory: 1G
- type: worker - type: worker
command: bundle exec sidekiq command: bundle exec sidekiq -t 3
health-check-type: process health-check-type: process
instances: 1 instances: 1
health-check-type: http health-check-type: http

4
config/initializers/sidekiq.rb

@ -32,4 +32,8 @@ Sidekiq.configure_server do |config|
config.on(:startup) do config.on(:startup) do
Sidekiq::Cron::Job.load_from_hash YAML.load_file("config/sidekiq_cron_schedule.yml") Sidekiq::Cron::Job.load_from_hash YAML.load_file("config/sidekiq_cron_schedule.yml")
end end
config.on(:shutdown) do
Sidekiq::CLI.instance.launcher.quiet
end
end end

11
config/locales/en.yml

@ -41,10 +41,17 @@ en:
activemodel: activemodel:
errors: errors:
models: models:
bulk_upload/sales/year2022/row_parser: bulk_upload/row_parser: &bulk_upload__row_parser__base
inclusion: Enter a valid value for %{question} inclusion: Enter a valid value for %{question}
spreadsheet_dupe: This is a duplicate of a log in your file
bulk_upload/lettings/year2022/row_parser:
<<: *bulk_upload__row_parser__base
bulk_upload/lettings/year2023/row_parser:
<<: *bulk_upload__row_parser__base
bulk_upload/sales/year2022/row_parser:
<<: *bulk_upload__row_parser__base
bulk_upload/sales/year2023/row_parser: bulk_upload/sales/year2023/row_parser:
inclusion: Enter a valid value for %{question} <<: *bulk_upload__row_parser__base
bulk_upload/lettings/validator: bulk_upload/lettings/validator:
attributes: attributes:
base: base:

15450
config/rent_range_data/2022.csv

File diff suppressed because it is too large Load Diff

15125
config/rent_range_data/2023.csv

File diff suppressed because it is too large Load Diff

10
docs/setup.md

@ -16,6 +16,8 @@ Dependencies:
We recommend using [RBenv](https://github.com/rbenv/rbenv) to manage Ruby versions. We recommend using [RBenv](https://github.com/rbenv/rbenv) to manage Ruby versions.
We recommend using [nvm](https://github.com/nvm-sh/nvm) to manage NodeJS versions.
1. Install PostgreSQL 1. Install PostgreSQL
macOS: macOS:
@ -70,10 +72,12 @@ We recommend using [RBenv](https://github.com/rbenv/rbenv) to manage Ruby versio
5. Install JavaScript dependencies 5. Install JavaScript dependencies
macOS: Note that we currently use node v16, which is no longer the latest LTS version so you will need to specify the version number when installing
macOS (using nvm):
```bash ```bash
brew install node nvm install 16
nvm use 16
brew install yarn brew install yarn
``` ```
@ -168,6 +172,8 @@ We recommend using [RBenv](https://github.com/rbenv/rbenv) to manage Ruby versio
bundle exec rspec bundle exec rspec
``` ```
Note that these tests assume you have firefox installed.
## Using Docker ## Using Docker
1. Build the image: 1. Build the image:

4
manifest.yml

@ -8,7 +8,7 @@ defaults: &defaults
instances: 2 instances: 2
memory: 1G memory: 1G
- type: worker - type: worker
command: bundle exec sidekiq command: bundle exec sidekiq -t 3
health-check-type: process health-check-type: process
instances: 2 instances: 2
health-check-type: http health-check-type: http
@ -31,7 +31,7 @@ applications:
instances: 4 instances: 4
memory: 1G memory: 1G
- type: worker - type: worker
command: bundle exec sidekiq command: bundle exec sidekiq -t 3
health-check-type: process health-check-type: process
instances: 2 instances: 2
env: env:

1
spec/factories/sales_log.rb

@ -36,6 +36,7 @@ FactoryBot.define do
jointpur { 2 } jointpur { 2 }
end end
trait :completed do trait :completed do
purchid { rand(999_999_999).to_s }
ownershipsch { 2 } ownershipsch { 2 }
type { 8 } type { 8 }
saledate { Time.utc(2023, 2, 2, 10, 36, 49) } saledate { Time.utc(2023, 2, 2, 10, 36, 49) }

14
spec/helpers/check_answers_helper_spec.rb

@ -11,8 +11,6 @@ RSpec.describe CheckAnswersHelper do
Singleton.__init__(FormHandler) Singleton.__init__(FormHandler)
example.run example.run
end end
Timecop.return
Singleton.__init__(FormHandler)
end end
describe "display_answered_questions_summary" do describe "display_answered_questions_summary" do
@ -37,4 +35,16 @@ RSpec.describe CheckAnswersHelper do
end end
end end
end end
describe "#get_answer_label" do
context "when unanswered and bulk upload" do
let(:question) { log.form.questions.sample }
let(:bulk_upload) { build(:bulk_upload, :sales) }
let(:log) { build(:sales_log, bulk_upload:) }
it "is red" do
expect(get_answer_label(question, log)).to include("red")
end
end
end
end end

322
spec/models/lettings_log_spec.rb

File diff suppressed because it is too large Load Diff

44
spec/models/sales_log_spec.rb

@ -165,7 +165,7 @@ RSpec.describe SalesLog, type: :model do
it "correctly derives and saves exday, exmonth and exyear" do it "correctly derives and saves exday, exmonth and exyear" do
sales_log.update!(exdate: Time.gm(2022, 5, 4), saledate: Time.gm(2022, 7, 4), ownershipsch: 1, type: 18, staircase: 2, resale: 2, proplen: 0) sales_log.update!(exdate: Time.gm(2022, 5, 4), saledate: Time.gm(2022, 7, 4), ownershipsch: 1, type: 18, staircase: 2, resale: 2, proplen: 0)
record_from_db = ActiveRecord::Base.connection.execute("select exday, exmonth, exyear from sales_logs where id=#{sales_log.id}").to_a[0] record_from_db = described_class.find(sales_log.id)
expect(record_from_db["exday"]).to eq(4) expect(record_from_db["exday"]).to eq(4)
expect(record_from_db["exmonth"]).to eq(5) expect(record_from_db["exmonth"]).to eq(5)
expect(record_from_db["exyear"]).to eq(2022) expect(record_from_db["exyear"]).to eq(2022)
@ -173,25 +173,25 @@ RSpec.describe SalesLog, type: :model do
it "correctly derives and saves deposit for outright sales when no mortgage is used" do it "correctly derives and saves deposit for outright sales when no mortgage is used" do
sales_log.update!(value: 123_400, deposit: nil, mortgageused: 2, ownershipsch: 3, type: 10, companybuy: 1, jointpur: 1, jointmore: 1) sales_log.update!(value: 123_400, deposit: nil, mortgageused: 2, ownershipsch: 3, type: 10, companybuy: 1, jointpur: 1, jointmore: 1)
record_from_db = ActiveRecord::Base.connection.execute("select deposit from sales_logs where id=#{sales_log.id}").to_a[0] record_from_db = described_class.find(sales_log.id)
expect(record_from_db["deposit"]).to eq(123_400) expect(record_from_db["deposit"]).to eq(123_400)
end end
it "does not derive deposit if the sale isn't outright" do it "does not derive deposit if the sale isn't outright" do
sales_log.update!(value: 123_400, deposit: nil, mortgageused: 2, ownershipsch: 2) sales_log.update!(value: 123_400, deposit: nil, mortgageused: 2, ownershipsch: 2)
record_from_db = ActiveRecord::Base.connection.execute("select deposit from sales_logs where id=#{sales_log.id}").to_a[0] record_from_db = described_class.find(sales_log.id)
expect(record_from_db["deposit"]).to eq(nil) expect(record_from_db["deposit"]).to eq(nil)
end end
it "does not derive deposit if the mortgage is used" do it "does not derive deposit if the mortgage is used" do
sales_log.update!(value: 123_400, deposit: nil, mortgageused: 1, ownershipsch: 3, type: 10, companybuy: 1, jointpur: 1, jointmore: 1) sales_log.update!(value: 123_400, deposit: nil, mortgageused: 1, ownershipsch: 3, type: 10, companybuy: 1, jointpur: 1, jointmore: 1)
record_from_db = ActiveRecord::Base.connection.execute("select deposit from sales_logs where id=#{sales_log.id}").to_a[0] record_from_db = described_class.find(sales_log.id)
expect(record_from_db["deposit"]).to eq(nil) expect(record_from_db["deposit"]).to eq(nil)
end end
it "correctly derives and saves pcode1 and pcode1 and pcode2" do it "correctly derives and saves pcode1 and pcode1 and pcode2" do
sales_log.update!(postcode_full: "W6 0SP") sales_log.update!(postcode_full: "W6 0SP")
record_from_db = ActiveRecord::Base.connection.execute("select pcode1, pcode2 from sales_logs where id=#{sales_log.id}").to_a[0] record_from_db = described_class.find(sales_log.id)
expect(record_from_db["pcode1"]).to eq("W6") expect(record_from_db["pcode1"]).to eq("W6")
expect(record_from_db["pcode2"]).to eq("0SP") expect(record_from_db["pcode2"]).to eq("0SP")
end end
@ -200,7 +200,7 @@ RSpec.describe SalesLog, type: :model do
# to avoid log failing validations when mortgage value is removed: # to avoid log failing validations when mortgage value is removed:
new_grant_value = sales_log.grant + sales_log.mortgage new_grant_value = sales_log.grant + sales_log.mortgage
sales_log.update!(mortgageused: 2, grant: new_grant_value) sales_log.update!(mortgageused: 2, grant: new_grant_value)
record_from_db = ActiveRecord::Base.connection.execute("select mortgage from sales_logs where id=#{sales_log.id}").to_a[0] record_from_db = described_class.find(sales_log.id)
expect(record_from_db["mortgage"]).to eq(0.0) expect(record_from_db["mortgage"]).to eq(0.0)
end end
@ -209,7 +209,7 @@ RSpec.describe SalesLog, type: :model do
new_grant_value = sales_log.grant + sales_log.mortgage new_grant_value = sales_log.grant + sales_log.mortgage
sales_log.update!(mortgageused: 2, grant: new_grant_value) sales_log.update!(mortgageused: 2, grant: new_grant_value)
sales_log.update!(mortgageused: 1) sales_log.update!(mortgageused: 1)
record_from_db = ActiveRecord::Base.connection.execute("select mortgage from sales_logs where id=#{sales_log.id}").to_a[0] record_from_db = described_class.find(sales_log.id)
expect(record_from_db["mortgage"]).to eq(nil) expect(record_from_db["mortgage"]).to eq(nil)
end end
@ -276,7 +276,7 @@ RSpec.describe SalesLog, type: :model do
end end
def check_postcode_fields(postcode_field) def check_postcode_fields(postcode_field)
record_from_db = ActiveRecord::Base.connection.execute("select #{postcode_field} from sales_logs where id=#{address_sales_log.id}").to_a[0] record_from_db = described_class.find(address_sales_log.id)
expect(address_sales_log[postcode_field]).to eq("M1 1AE") expect(address_sales_log[postcode_field]).to eq("M1 1AE")
expect(record_from_db[postcode_field]).to eq("M1 1AE") expect(record_from_db[postcode_field]).to eq("M1 1AE")
end end
@ -311,7 +311,7 @@ RSpec.describe SalesLog, type: :model do
end end
it "correctly infers la" do it "correctly infers la" do
record_from_db = ActiveRecord::Base.connection.execute("select la from sales_logs where id=#{address_sales_log.id}").to_a[0] record_from_db = described_class.find(address_sales_log.id)
expect(address_sales_log.la).to eq("E08000003") expect(address_sales_log.la).to eq("E08000003")
expect(record_from_db["la"]).to eq("E08000003") expect(record_from_db["la"]).to eq("E08000003")
end end
@ -340,7 +340,7 @@ RSpec.describe SalesLog, type: :model do
end end
it "correctly sets la as nil" do it "correctly sets la as nil" do
record_from_db = ActiveRecord::Base.connection.execute("select la from sales_logs where id=#{address_sales_log_22_23.id}").to_a[0] record_from_db = described_class.find(address_sales_log_22_23.id)
expect(address_sales_log_22_23.la).to eq(nil) expect(address_sales_log_22_23.la).to eq(nil)
expect(record_from_db["la"]).to eq(nil) expect(record_from_db["la"]).to eq(nil)
end end
@ -370,7 +370,7 @@ RSpec.describe SalesLog, type: :model do
end end
it "correctly infers new la" do it "correctly infers new la" do
record_from_db = ActiveRecord::Base.connection.execute("select la from sales_logs where id=#{address_sales_log_23_24.id}").to_a[0] record_from_db = described_class.find(address_sales_log_23_24.id)
expect(address_sales_log_23_24.la).to eq("E06000064") expect(address_sales_log_23_24.la).to eq("E06000064")
expect(record_from_db["la"]).to eq("E06000064") expect(record_from_db["la"]).to eq("E06000064")
end end
@ -400,7 +400,7 @@ RSpec.describe SalesLog, type: :model do
it "correctly resets all fields if property postcode not known" do it "correctly resets all fields if property postcode not known" do
address_sales_log.update!({ pcodenk: 1 }) address_sales_log.update!({ pcodenk: 1 })
record_from_db = ActiveRecord::Base.connection.execute("select la, postcode_full from sales_logs where id=#{address_sales_log.id}").to_a[0] record_from_db = described_class.find(address_sales_log.id)
expect(record_from_db["postcode_full"]).to eq(nil) expect(record_from_db["postcode_full"]).to eq(nil)
expect(address_sales_log.la).to eq(nil) expect(address_sales_log.la).to eq(nil)
expect(record_from_db["la"]).to eq(nil) expect(record_from_db["la"]).to eq(nil)
@ -410,14 +410,14 @@ RSpec.describe SalesLog, type: :model do
address_sales_log.update!({ pcodenk: 1 }) address_sales_log.update!({ pcodenk: 1 })
address_sales_log.update!({ la: "E09000033" }) address_sales_log.update!({ la: "E09000033" })
record_from_db = ActiveRecord::Base.connection.execute("select la, postcode_full from sales_logs where id=#{address_sales_log.id}").to_a[0] record_from_db = described_class.find(address_sales_log.id)
expect(record_from_db["postcode_full"]).to eq(nil) expect(record_from_db["postcode_full"]).to eq(nil)
expect(address_sales_log.la).to eq("E09000033") expect(address_sales_log.la).to eq("E09000033")
expect(record_from_db["la"]).to eq("E09000033") expect(record_from_db["la"]).to eq("E09000033")
address_sales_log.update!({ pcodenk: 0, postcode_full: "M1 1AD" }) address_sales_log.update!({ pcodenk: 0, postcode_full: "M1 1AD" })
record_from_db = ActiveRecord::Base.connection.execute("select la, postcode_full from sales_logs where id=#{address_sales_log.id}").to_a[0] record_from_db = described_class.find(address_sales_log.id)
expect(record_from_db["postcode_full"]).to eq("M1 1AD") expect(record_from_db["postcode_full"]).to eq("M1 1AD")
expect(address_sales_log.la).to eq("E08000003") expect(address_sales_log.la).to eq("E08000003")
expect(record_from_db["la"]).to eq("E08000003") expect(record_from_db["la"]).to eq("E08000003")
@ -453,35 +453,35 @@ RSpec.describe SalesLog, type: :model do
end end
it "correctly derives and saves hhmemb" do it "correctly derives and saves hhmemb" do
record_from_db = ActiveRecord::Base.connection.execute("select hhmemb from sales_logs where id=#{sales_log.id}").to_a[0] record_from_db = described_class.find(sales_log.id)
expect(record_from_db["hhmemb"]).to eq(6) expect(record_from_db["hhmemb"]).to eq(6)
end end
it "correctly derives and saves hhmemb if it's a joint purchase" do it "correctly derives and saves hhmemb if it's a joint purchase" do
sales_log.update!(jointpur: 2, jointmore: 2) sales_log.update!(jointpur: 2, jointmore: 2)
record_from_db = ActiveRecord::Base.connection.execute("select hhmemb from sales_logs where id=#{sales_log.id}").to_a[0] record_from_db = described_class.find(sales_log.id)
expect(record_from_db["hhmemb"]).to eq(5) expect(record_from_db["hhmemb"]).to eq(5)
end end
it "correctly derives and saves totchild" do it "correctly derives and saves totchild" do
record_from_db = ActiveRecord::Base.connection.execute("select totchild from sales_logs where id=#{sales_log.id}").to_a[0] record_from_db = described_class.find(sales_log.id)
expect(record_from_db["totchild"]).to eq(2) expect(record_from_db["totchild"]).to eq(2)
end end
it "correctly derives and saves totadult" do it "correctly derives and saves totadult" do
record_from_db = ActiveRecord::Base.connection.execute("select totadult from sales_logs where id=#{sales_log.id}").to_a[0] record_from_db = described_class.find(sales_log.id)
expect(record_from_db["totadult"]).to eq(4) expect(record_from_db["totadult"]).to eq(4)
end end
it "correctly derives and saves hhtype" do it "correctly derives and saves hhtype" do
record_from_db = ActiveRecord::Base.connection.execute("select hhtype from sales_logs where id=#{sales_log.id}").to_a[0] record_from_db = described_class.find(sales_log.id)
expect(record_from_db["hhtype"]).to eq(9) expect(record_from_db["hhtype"]).to eq(9)
end end
end end
context "when saving previous address" do context "when saving previous address" do
def check_previous_postcode_fields(postcode_field) def check_previous_postcode_fields(postcode_field)
record_from_db = ActiveRecord::Base.connection.execute("select #{postcode_field} from sales_logs where id=#{address_sales_log.id}").to_a[0] record_from_db = described_class.find(address_sales_log.id)
expect(address_sales_log[postcode_field]).to eq("M1 1AE") expect(address_sales_log[postcode_field]).to eq("M1 1AE")
expect(record_from_db[postcode_field]).to eq("M1 1AE") expect(record_from_db[postcode_field]).to eq("M1 1AE")
end end
@ -519,7 +519,7 @@ RSpec.describe SalesLog, type: :model do
end end
it "correctly infers prevloc" do it "correctly infers prevloc" do
record_from_db = ActiveRecord::Base.connection.execute("select prevloc from sales_logs where id=#{address_sales_log.id}").to_a[0] record_from_db = described_class.find(address_sales_log.id)
expect(address_sales_log.prevloc).to eq("E08000003") expect(address_sales_log.prevloc).to eq("E08000003")
expect(record_from_db["prevloc"]).to eq("E08000003") expect(record_from_db["prevloc"]).to eq("E08000003")
end end
@ -537,7 +537,7 @@ RSpec.describe SalesLog, type: :model do
it "correctly resets all fields if previous postcode not known" do it "correctly resets all fields if previous postcode not known" do
address_sales_log.update!({ ppcodenk: 1 }) address_sales_log.update!({ ppcodenk: 1 })
record_from_db = ActiveRecord::Base.connection.execute("select prevloc, ppostcode_full from sales_logs where id=#{address_sales_log.id}").to_a[0] record_from_db = described_class.find(address_sales_log.id)
expect(record_from_db["ppostcode_full"]).to eq(nil) expect(record_from_db["ppostcode_full"]).to eq(nil)
expect(address_sales_log.prevloc).to eq(nil) expect(address_sales_log.prevloc).to eq(nil)
expect(record_from_db["prevloc"]).to eq(nil) expect(record_from_db["prevloc"]).to eq(nil)

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

@ -290,6 +290,23 @@ RSpec.describe BulkUpload::Lettings::Validator do
end end
end end
context "when duplicate rows present" do
let(:file) { Tempfile.new }
let(:path) { file.path }
let(:log) { build(:lettings_log, :completed) }
before do
file.write(BulkUpload::LettingsLogToCsv.new(log:, line_ending: "\r\n").default_2023_field_numbers_row)
file.write(BulkUpload::LettingsLogToCsv.new(log:, line_ending: "\r\n").to_2023_csv_row)
file.write(BulkUpload::LettingsLogToCsv.new(log:, line_ending: "\r\n").to_2023_csv_row)
file.close
end
it "creates errors" do
expect { validator.call }.to change(BulkUploadError.where(category: :setup, error: "This is a duplicate of a log in your file"), :count).by(24)
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 }

12
spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb

@ -1760,4 +1760,16 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do
end end
end end
end end
describe "#spreadsheet_duplicate_hash" do
it "returns a hash" do
expect(parser.spreadsheet_duplicate_hash).to be_a(Hash)
end
end
describe "#add_duplicate_found_in_spreadsheet_errors" do
it "adds errors" do
expect { parser.add_duplicate_found_in_spreadsheet_errors }.to change(parser.errors, :size)
end
end
end end

12
spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb

@ -1830,4 +1830,16 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do
end end
end end
end end
describe "#spreadsheet_duplicate_hash" do
it "returns a hash" do
expect(parser.spreadsheet_duplicate_hash).to be_a(Hash)
end
end
describe "#add_duplicate_found_in_spreadsheet_errors" do
it "adds errors" do
expect { parser.add_duplicate_found_in_spreadsheet_errors }.to change(parser.errors, :size)
end
end
end end

10
spec/services/bulk_upload/processor_spec.rb

@ -8,6 +8,16 @@ RSpec.describe BulkUpload::Processor do
let(:owning_org) { create(:organisation, old_visible_id: 123) } let(:owning_org) { create(:organisation, old_visible_id: 123) }
describe "#call" do describe "#call" do
context "when errors exist from prior job run" do
let!(:existing_error) { create(:bulk_upload_error, bulk_upload:) }
it "destroys existing errors" do
processor.call
expect { existing_error.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
context "when the bulk upload itself is not considered valid" do context "when the bulk upload itself is not considered valid" do
let(:mock_downloader) do let(:mock_downloader) do
instance_double( instance_double(

16
spec/services/bulk_upload/sales/validator_spec.rb

@ -125,6 +125,22 @@ RSpec.describe BulkUpload::Sales::Validator do
expect { validator.call }.to change(BulkUploadError, :count) expect { validator.call }.to change(BulkUploadError, :count)
end end
end end
context "when duplicate rows present" do
let(:file) { Tempfile.new }
let(:path) { file.path }
let(:log) { build(:sales_log, :completed) }
before do
file.write(BulkUpload::SalesLogToCsv.new(log:, col_offset: 0).to_2022_csv_row)
file.write(BulkUpload::SalesLogToCsv.new(log:, col_offset: 0).to_2022_csv_row)
file.close
end
it "creates errors" do
expect { validator.call }.to change(BulkUploadError.where(category: :setup, error: "This is a duplicate of a log in your file"), :count).by(20)
end
end
end end
describe "#create_logs?" do describe "#create_logs?" do

36
spec/services/bulk_upload/sales/year2022/row_parser_spec.rb

@ -419,6 +419,30 @@ RSpec.describe BulkUpload::Sales::Year2022::RowParser do
end end
end end
context "when type is shared ownership" do
let(:attributes) { valid_attributes.merge({ field_113: 1, field_68: nil }) }
it "has error on correct fields" do
expect(parser.errors).to include(:field_68)
end
end
context "when type is discounted ownership" do
let(:attributes) { valid_attributes.merge({ field_113: 2, field_77: nil }) }
it "has error on correct fields" do
expect(parser.errors).to include(:field_77)
end
end
context "when type is outright sale" do
let(:attributes) { valid_attributes.merge({ field_113: 3, field_87: nil }) }
it "has error on correct fields" do
expect(parser.errors).to include(:field_87)
end
end
describe "#field_93" do # username for created_by describe "#field_93" do # username for created_by
context "when blank" do context "when blank" do
let(:attributes) { { bulk_upload:, field_93: "" } } let(:attributes) { { bulk_upload:, field_93: "" } }
@ -832,4 +856,16 @@ RSpec.describe BulkUpload::Sales::Year2022::RowParser do
end end
end end
end end
describe "#spreadsheet_duplicate_hash" do
it "returns a hash" do
expect(parser.spreadsheet_duplicate_hash).to be_a(Hash)
end
end
describe "#add_duplicate_found_in_spreadsheet_errors" do
it "adds errors" do
expect { parser.add_duplicate_found_in_spreadsheet_errors }.to change(parser.errors, :size)
end
end
end end

12
spec/services/bulk_upload/sales/year2023/row_parser_spec.rb

@ -1081,4 +1081,16 @@ RSpec.describe BulkUpload::Sales::Year2023::RowParser do
end end
end end
end end
describe "#spreadsheet_duplicate_hash" do
it "returns a hash" do
expect(parser.spreadsheet_duplicate_hash).to be_a(Hash)
end
end
describe "#add_duplicate_found_in_spreadsheet_errors" do
it "adds errors" do
expect { parser.add_duplicate_found_in_spreadsheet_errors }.to change(parser.errors, :size)
end
end
end end

2
spec/services/csv/sales_log_csv_service_spec.rb

@ -3,7 +3,7 @@ require "rails_helper"
RSpec.describe Csv::SalesLogCsvService do RSpec.describe Csv::SalesLogCsvService do
let(:form_handler_mock) { instance_double(FormHandler) } let(:form_handler_mock) { instance_double(FormHandler) }
let(:organisation) { create(:organisation) } let(:organisation) { create(:organisation) }
let!(:log) { create(:sales_log, :completed, owning_organisation: organisation) } let!(:log) { create(:sales_log, :completed, owning_organisation: organisation, purchid: nil) }
let(:service) { described_class.new(export_type: "labels") } let(:service) { described_class.new(export_type: "labels") }
let(:csv) { CSV.parse(service.prepare_csv(SalesLog.all)) } let(:csv) { CSV.parse(service.prepare_csv(SalesLog.all)) }

Loading…
Cancel
Save