Browse Source

fix typos, field numbers, staircasing tests

pull/2945/head
Carolyn 2 weeks ago
parent
commit
4bd1b04eda
  1. 8
      app/services/bulk_upload/sales/year2025/csv_parser.rb
  2. 678
      app/services/bulk_upload/sales/year2025/row_parser.rb
  3. 14
      spec/services/bulk_upload/sales/year2025/csv_parser_spec.rb
  4. 324
      spec/services/bulk_upload/sales/year2025/row_parser_spec.rb

8
app/services/bulk_upload/sales/year2025/csv_parser.rb

@ -114,11 +114,11 @@ private
def first_record_start_date
if with_headers?
year = row_parsers.first.field_6.to_s.strip.length.between?(1, 2) ? row_parsers.first.field_6.to_i + 2000 : row_parsers.first.field_6.to_i
Date.new(year, row_parsers.first.field_5.to_i, row_parsers.first.field_4.to_i)
year = row_parsers.first.field_3.to_s.strip.length.between?(1, 2) ? row_parsers.first.field_3.to_i + 2000 : row_parsers.first.field_3.to_i
Date.new(year, row_parsers.first.field_2.to_i, row_parsers.first.field_1.to_i)
else
year = rows.first[5].to_s.strip.length.between?(1, 2) ? rows.first[5].to_i + 2000 : rows.first[5].to_i
Date.new(year, rows.first[4].to_i, rows.first[3].to_i)
year = rows.first[2].to_s.strip.length.between?(1, 2) ? rows.first[2].to_i + 2000 : rows.first[2].to_i
Date.new(year, rows.first[1].to_i, rows.first[0].to_i)
end
end
end

678
app/services/bulk_upload/sales/year2025/row_parser.rb

@ -5,12 +5,12 @@ class BulkUpload::Sales::Year2025::RowParser
include FormattingHelper
QUESTIONS = {
field_1: "Which organisation owned this property before the sale?",
field_2: "Which organisation is reporting this sale?",
field_3: "Username",
field_4: "What is the day of the sale completion date? - DD",
field_5: "What is the month of the sale completion date? - MM",
field_6: "What is the year of the sale completion date? - YY",
field_1: "What is the day of the sale completion date? - DD",
field_2: "What is the month of the sale completion date? - MM",
field_3: "What is the year of the sale completion date? - YY",
field_4: "Which organisation owned this property before the sale?",
field_5: "Which organisation is reporting this sale?",
field_6: "Username",
field_7: "What is the purchaser code?",
field_8: "What is the sale type?",
field_9: "What is the type of shared ownership sale?",
@ -144,12 +144,12 @@ class BulkUpload::Sales::Year2025::RowParser
attribute :field_blank
attribute :field_1, :string
attribute :field_2, :string
attribute :field_3, :string
attribute :field_4, :integer
attribute :field_5, :integer
attribute :field_6, :integer
attribute :field_1, :integer
attribute :field_2, :integer
attribute :field_3, :integer
attribute :field_4, :string
attribute :field_5, :string
attribute :field_6, :string
attribute :field_7, :string
attribute :field_8, :integer
attribute :field_9, :integer
@ -196,7 +196,7 @@ class BulkUpload::Sales::Year2025::RowParser
attribute :field_47, :string
attribute :field_48, :string
attribute :field_49, :integer
attribute :field_49, :integer
attribute :field_50, :integer
attribute :field_51, :string
attribute :field_52, :string
attribute :field_53, :integer
@ -244,6 +244,8 @@ class BulkUpload::Sales::Year2025::RowParser
attribute :field_91, :integer
attribute :field_92, :integer
attribute :field_93, :decimal
attribute :field_94, :decimal
attribute :field_95, :decimal
attribute :field_96, :integer
attribute :field_97, :integer
@ -273,20 +275,20 @@ class BulkUpload::Sales::Year2025::RowParser
attribute :field_120, :integer
attribute :field_121, :integer
validates :field_4,
validates :field_1,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "sale completion date (day)."),
category: :setup,
},
on: :after_log
validates :field_5,
validates :field_2,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "sale completion date (month)."),
category: :setup,
}, on: :after_log
validates :field_6,
validates :field_3,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "sale completion date (year)."),
category: :setup,
@ -295,7 +297,7 @@ class BulkUpload::Sales::Year2025::RowParser
with: /\A(\d{2}|\d{4})\z/,
message: I18n.t("#{ERROR_BASE_KEY}.saledate.year_not_two_or_four_digits"),
category: :setup,
if: proc { field_6.present? },
if: proc { field_3.present? },
}, on: :after_log
validates :field_8,
@ -390,565 +392,6 @@ class BulkUpload::Sales::Year2025::RowParser
},
on: :after_log
validates :field_18,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "type of building."),
category: :setup,
if: :shared_or_discounted_but_not_staircasing?,
},
on: :after_log
validates :field_27,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "meets wheelchair standards."),
category: :setup,
if: :shared_or_discounted_but_not_staircasing?,
},
on: :after_log
validates :field_30,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "buyer 1 ethnicity."),
category: :setup,
if: :shared_or_discounted_but_not_staircasing?,
},
on: :after_log
validates :field_31,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "buyer 1 nationality."),
category: :setup,
if: :shared_or_discounted_but_not_staircasing?,
},
on: :after_log
validates :field_32,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "buyer 1 working situation."),
category: :setup,
if: :shared_or_discounted_but_not_staircasing?,
},
on: :after_log
validates :field_33,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "whether buyer 1 will live in the property."),
category: :setup,
if: :shared_or_discounted_but_not_staircasing?,
},
on: :after_log
validates :field_37,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "buyer 2 ethnic background."),
category: :setup,
if: :shared_or_discounted_but_not_staircasing?,
},
on: :after_log
validates :field_38,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "buyer 2 nationality."),
category: :setup,
if: :shared_or_discounted_but_not_staircasing?,
},
on: :after_log
validates :field_39,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "buyer 2 working situation."),
category: :setup,
if: :shared_or_discounted_but_not_staircasing?,
},
on: :after_log
validates :field_40,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "whether buyer 2 will live in the property."),
category: :setup,
if: :shared_or_discounted_but_not_staircasing?,
},
on: :after_log
validates :field_41,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "number of other people living in the property."),
category: :setup,
unless: :staircasing?,
},
on: :after_log
validates :field_58,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "buyer 1's previous tenure."),
category: :setup,
unless: :staircasing?,
},
on: :after_log
validates :field_59,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "buyer 1's last settled accommodation."),
category: :setup,
unless: :discounted_ownership || :staircasing?,
},
on: :after_log
validates :field_63,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "whether buyer 2 was living with buyer 1."),
category: :setup,
if: :shared_or_discounted_but_not_staircasing?,
},
on: :after_log
validates :field_64,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "buyer 2's previous tenure."),
category: :setup,
if: :shared_or_discounted_but_not_staircasing? && :two_buyers_share_address?,
},
on: :after_log
validates :field_65,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "whether a buyer has served in the armed forces."),
category: :setup,
unless: :staircasing?,
},
on: :after_log
validates :field_66,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "whether a buyer is currently in the armed forces."),
category: :setup,
unless: :staircasing?,
},
on: :after_log
validates :field_67,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "whether a buyer's spouse recently died in service."),
category: :setup,
unless: :staircasing?,
},
on: :after_log
validates :field_68,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "whether any buyer has a disability."),
category: :setup,
unless: :staircasing?,
},
on: :after_log
validates :field_69,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "whether any buyer uses a wheelchair."),
category: :setup,
unless: :staircasing?,
},
on: :after_log
validates :field_70,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "buyer 1's annual income."),
category: :setup,
unless: :staircasing?,
},
on: :after_log
validates :field_72,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "buyer 2's annual income."),
category: :setup,
if: :joint_purchase,
unless: :staircasing?,
},
on: :after_log
validates :field_74,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "whether buyers were receiving housing benefits."),
category: :setup,
unless: :staircasing?,
},
on: :after_log
validates :field_75,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "the buyers' total savings."),
category: :setup,
unless: :staircasing?,
},
on: :after_log
validates :field_76,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "whether the buyers have previously owned a property."),
category: :setup,
unless: :staircasing?,
},
on: :after_log
validates :field_78,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "whether this is a resale."),
category: :setup,
if: :shared_or_discounted_but_not_staircasing?,
},
on: :after_log
validates :field_79,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "how long the buyer lived there prior to purchase."),
category: :setup,
if: :shared_ownership_initial_purchase? && :not_resale?,
},
on: :after_log
validates :field_80,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "day of practical completion or handover."),
category: :setup,
if: :shared_ownership_initial_purchase? && :not_resale?,
},
on: :after_log
validates :field_81,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "month of practical completion or handover."),
category: :setup,
if: :shared_ownership_initial_purchase? && :not_resale?,
},
on: :after_log
validates :field_82,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "year of practical completion or handover."),
category: :setup,
if: :shared_ownership_initial_purchase? && :not_resale?,
},
on: :after_log
validates :field_83,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "number of bedrooms in previous property."),
category: :setup,
if: :shared_ownership_initial_purchase? && :buyer_1_previous_tenure_not_1_or_2?,
},
on: :after_log
validates :field_84,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "previous property type."),
category: :setup,
if: :shared_ownership_initial_purchase? && :buyer_1_previous_tenure_not_1_or_2?,
},
on: :after_log
validates :field_85,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "buyer's previous tenure."),
category: :setup,
if: :shared_ownership_initial_purchase? && :buyer_1_previous_tenure_not_1_or_2?,
},
on: :after_log
validates :field_86,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "full purchase price."),
category: :setup,
if: :shared_ownership_initial_purchase?,
},
on: :after_log
validates :field_87,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "initial percentage share purchased."),
category: :setup,
if: :shared_ownership_initial_purchase?,
},
on: :after_log
validates :field_88,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "whether a mortgage was used."),
category: :setup,
if: :shared_ownership_initial_purchase?,
},
on: :after_log
validates :field_88,
inclusion: {
in: [1, 2],
if: proc { field_88.present? },
category: :setup,
question: QUESTIONS[:field_88].downcase,
},
on: :before_log
validates :field_89,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "mortgage amount."),
category: :setup,
if: :shared_ownership_initial_purchase? && :mortgage_used?,
},
on: :after_log
validates :field_90,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "mortgage length."),
category: :setup,
if: :shared_ownership_initial_purchase? && :mortgage_used?,
},
on: :after_log
validates :field_91,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "deposit amount."),
category: :setup,
if: :shared_ownership_initial_purchase?,
},
on: :after_log
validates :field_92,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "deposit amount."),
category: :setup,
if: :shared_ownership_initial_purchase?,
unless: :social_homebuy?,
},
on: :after_log
validates :field_93,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "basic monthly rent."),
category: :setup,
if: :shared_ownership_initial_purchase?,
},
on: :after_log
validates :field_94,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "monthly service charges."),
category: :setup,
if: :shared_ownership_initial_purchase?,
},
on: :after_log
validates :field_95,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "monthly estate management fees."),
category: :setup,
if: :shared_ownership_initial_purchase?,
},
on: :after_log
validates :field_96,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "percentage of property bought this transaction."),
category: :setup,
if: :staircasing?,
},
on: :after_log
validates :field_97,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "total percentage of property buyers now own."),
category: :setup,
if: :staircasing?,
},
on: :after_log
validates :field_98,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "whether transaction is part of a back to back staircasing transaction."),
category: :setup,
if: :staircasing? && :buyers_own_all?,
},
on: :after_log
validates :field_99,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "whether it is the first time the buyer has engaged in staircasing."),
category: :setup,
if: :staircasing?,
},
on: :after_log
validates :field_99,
inclusion: {
in: [1, 2],
if: proc { field_99.present? },
category: :setup,
question: QUESTIONS[:field_99].downcase,
},
on: :before_log
validates :field_100,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "day of the initial purchase of a share."),
category: :setup,
if: :staircasing?,
},
on: :after_log
validates :field_101,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "month of the initial purchase of a share."),
category: :setup,
if: :staircasing?,
},
on: :after_log
validates :field_102,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "year of the initial purchase of a share."),
category: :setup,
if: :staircasing?,
},
on: :after_log
validates :field_103,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "number of times buyer has engaged in staircasing."),
category: :setup,
if: :staircasing? && :buyer_staircased_before?,
},
on: :after_log
validates :field_103,
inclusion: {
in: [2, 3, 4, 5, 6, 7, 8, 9, 10],
if: proc { field_103.present? },
category: :setup,
question: QUESTIONS[:field_103].downcase,
},
on: :before_log
validates :field_104,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "day of last staircasing transaction."),
category: :setup,
if: :staircasing? && :buyer_staircased_before?,
},
on: :after_log
validates :field_105,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "month of last staircasing transaction."),
category: :setup,
if: :staircasing? && :buyer_staircased_before?,
},
on: :after_log
validates :field_106,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "year of last staircasing transaction."),
category: :setup,
if: :staircasing? && :buyer_staircased_before?,
},
on: :after_log
validates :field_107,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "full purchase price."),
category: :setup,
if: :staircasing?,
},
on: :after_log
validates :field_108,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "percentage share purchased initially."),
category: :setup,
if: :staircasing?,
},
on: :after_log
validates :field_109,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "whether a mortgage was used."),
category: :setup,
if: :staircasing?,
},
on: :after_log
validates :field_110,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "basic monthly rent before staircasing."),
category: :setup,
if: :staircasing?,
},
on: :after_log
validates :field_111,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "basic monthly rent after staircasing."),
category: :setup,
if: :staircasing?,
unless: :buyers_own_all?,
},
on: :after_log
validates :field_112,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "how long buyers lived in property before purchasing."),
category: :setup,
if: :discounted_ownership?,
},
on: :after_log
validates :field_113,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "full purchase price."),
category: :setup,
if: :discounted_ownership?,
},
on: :after_log
validates :field_114,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "amount of loan or subsidy."),
category: :setup,
if: :discounted_ownership?,
unless: :rtb_like_sale_type?,
},
on: :after_log
validates :field_115,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "percentage discount."),
category: :setup,
if: :discounted_ownership? && :rtb_like_sale_type?,
},
on: :after_log
validates :field_117,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "mortgage amount."),
category: :setup,
if: :discounted_ownership? && :mortgage_used?,
},
on: :after_log
validates :field_118,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "mortgage duration."),
category: :setup,
if: :discounted_ownership? && :mortgage_used?,
},
on: :after_log
validates :field_119,
presence: {
message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "whether any extra borrowing is included."),
category: :setup,
if: :discounted_ownership? && :mortgage_used?,
},
on: :after_log
validate :validate_buyer1_economic_status, on: :before_log
validate :validate_address_option_found, on: :after_log
validate :validate_buyer2_economic_status, on: :before_log
@ -1052,10 +495,10 @@ class BulkUpload::Sales::Year2025::RowParser
def spreadsheet_duplicate_hash
attributes.slice(
"field_1", # owning org
"field_4", # saledate
"field_5", # saledate
"field_6", # saledate
"field_4", # owning org
"field_1", # saledate
"field_2", # saledate
"field_3", # saledate
"field_7", # purchaser_code
"field_24", # postcode
"field_25", # postcode
@ -1202,13 +645,13 @@ private
end
def rtb_like_sale_type?
[9, 14, 27, 29].includes(field_11)
[9, 14, 27, 29].include?(field_11)
end
def field_mapping_for_errors
{
purchid: %i[field_7],
saledate: %i[field_4 field_5 field_6],
saledate: %i[field_1 field_2 field_3],
noint: %i[field_14],
age1_known: %i[field_28],
age1: %i[field_28],
@ -1292,15 +735,13 @@ private
mscharge: mscharge_fields,
grant: %i[field_114],
discount: %i[field_115],
owning_organisation_id: %i[field_1],
managing_organisation_id: [:field_2],
assigned_to: %i[field_3],
owning_organisation_id: %i[field_4],
managing_organisation_id: [:field_5],
assigned_to: %i[field_6],
hhregres: %i[field_65],
hhregresstill: %i[field_66],
armedforcesspouse: %i[field_67],
mortgagelenderother: mortgagelenderother_fields,
hb: %i[field_74],
mortlen: mortlen_fields,
proplen: proplen_fields,
@ -1484,9 +925,6 @@ private
attributes["hhregresstill"] = field_66
attributes["armedforcesspouse"] = field_67
attributes["mortgagelender"] = mortgagelender
attributes["mortgagelenderother"] = mortgagelenderother
attributes["hb"] = field_74
attributes["mortlen"] = mortlen
@ -1552,8 +990,8 @@ private
end
def saledate
year = field_6.to_s.strip.length.between?(1, 2) ? field_6 + 2000 : field_6
Date.new(year, field_5, field_4) if field_6.present? && field_5.present? && field_4.present?
year = field_3.to_s.strip.length.between?(1, 2) ? field_3 + 2000 : field_3
Date.new(year, field_2, field_1) if field_3.present? && field_2.present? && field_1.present?
rescue Date::Error
Date.new
end
@ -1777,11 +1215,11 @@ private
end
def owning_organisation
@owning_organisation ||= Organisation.find_by_id_on_multiple_fields(field_1)
@owning_organisation ||= Organisation.find_by_id_on_multiple_fields(field_4)
end
def assigned_to
@assigned_to ||= User.where("lower(email) = ?", field_3&.downcase).first
@assigned_to ||= User.where("lower(email) = ?", field_6&.downcase).first
end
def previous_la_known
@ -1825,11 +1263,11 @@ private
end
def validate_owning_org_data_given
if field_1.blank?
if field_4.blank?
block_log_creation!
if errors[:field_1].blank?
errors.add(:field_1, I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "owning organisation."), category: :setup)
if errors[:field_4].blank?
errors.add(:field_4, I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "owning organisation."), category: :setup)
end
end
end
@ -1838,8 +1276,8 @@ private
if owning_organisation.nil?
block_log_creation!
if field_1.present? && errors[:field_1].blank?
errors.add(:field_1, I18n.t("#{ERROR_BASE_KEY}.owning_organisation.not_found"), category: :setup)
if field_4.present? && errors[:field_4].blank?
errors.add(:field_4, I18n.t("#{ERROR_BASE_KEY}.owning_organisation.not_found"), category: :setup)
end
end
end
@ -1848,8 +1286,8 @@ private
if owning_organisation && !owning_organisation.holds_own_stock?
block_log_creation!
if errors[:field_1].blank?
errors.add(:field_1, I18n.t("#{ERROR_BASE_KEY}.owning_organisation.not_stock_owner"), category: :setup)
if errors[:field_4].blank?
errors.add(:field_4, I18n.t("#{ERROR_BASE_KEY}.owning_organisation.not_stock_owner"), category: :setup)
end
end
end
@ -1860,26 +1298,26 @@ private
block_log_creation!
return if errors[:field_1].present?
return if errors[:field_4].present?
if bulk_upload.user.support?
errors.add(:field_1, I18n.t("#{ERROR_BASE_KEY}.owning_organisation.not_permitted.support", name: bulk_upload_organisation.name), category: :setup)
errors.add(:field_4, I18n.t("#{ERROR_BASE_KEY}.owning_organisation.not_permitted.support", name: bulk_upload_organisation.name), category: :setup)
else
errors.add(:field_1, I18n.t("#{ERROR_BASE_KEY}.owning_organisation.not_permitted.not_support"), category: :setup)
errors.add(:field_4, I18n.t("#{ERROR_BASE_KEY}.owning_organisation.not_permitted.not_support"), category: :setup)
end
end
def validate_assigned_to_exists
return if field_3.blank?
return if field_6.blank?
unless assigned_to
errors.add(:field_3, I18n.t("#{ERROR_BASE_KEY}.assigned_to.not_found"))
errors.add(:field_6, I18n.t("#{ERROR_BASE_KEY}.assigned_to.not_found"))
end
end
def validate_assigned_to_when_support
if field_3.blank? && bulk_upload.user.support?
errors.add(:field_3, category: :setup, message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "what is the CORE username of the account this sales log should be assigned to?"))
if field_6.blank? && bulk_upload.user.support?
errors.add(:field_6, category: :setup, message: I18n.t("#{ERROR_BASE_KEY}.not_answered", question: "what is the CORE username of the account this sales log should be assigned to?"))
end
end
@ -1889,11 +1327,11 @@ private
return if assigned_to.organisation == owning_organisation&.absorbing_organisation || assigned_to.organisation == managing_organisation&.absorbing_organisation
block_log_creation!
errors.add(:field_3, I18n.t("#{ERROR_BASE_KEY}.assigned_to.organisation_not_related"), category: :setup)
errors.add(:field_6, I18n.t("#{ERROR_BASE_KEY}.assigned_to.organisation_not_related"), category: :setup)
end
def managing_organisation
Organisation.find_by_id_on_multiple_fields(field_2)
Organisation.find_by_id_on_multiple_fields(field_5)
end
def nationality_group(nationality_value)
@ -1908,8 +1346,8 @@ private
if owning_organisation && managing_organisation && !owning_organisation.can_be_managed_by?(organisation: managing_organisation)
block_log_creation!
if errors[:field_2].blank?
errors.add(:field_2, I18n.t("#{ERROR_BASE_KEY}.assigned_to.managing_organisation_not_related"), category: :setup)
if errors[:field_5].blank?
errors.add(:field_5, I18n.t("#{ERROR_BASE_KEY}.assigned_to.managing_organisation_not_related"), category: :setup)
end
end
end
@ -1973,12 +1411,12 @@ private
def validate_relevant_collection_window
return if saledate.blank? || bulk_upload.form.blank?
return if errors.key?(:field_4) || errors.key?(:field_5) || errors.key?(:field_6)
return if errors.key?(:field_1) || errors.key?(:field_2) || errors.key?(:field_3)
unless bulk_upload.form.valid_start_date_for_form?(saledate)
errors.add(:field_4, I18n.t("#{ERROR_BASE_KEY}.saledate.outside_collection_window", year_combo: bulk_upload.year_combo, start_year: bulk_upload.year, end_year: bulk_upload.end_year), category: :setup)
errors.add(:field_5, I18n.t("#{ERROR_BASE_KEY}.saledate.outside_collection_window", year_combo: bulk_upload.year_combo, start_year: bulk_upload.year, end_year: bulk_upload.end_year), category: :setup)
errors.add(:field_6, I18n.t("#{ERROR_BASE_KEY}.saledate.outside_collection_window", year_combo: bulk_upload.year_combo, start_year: bulk_upload.year, end_year: bulk_upload.end_year), category: :setup)
errors.add(:field_1, I18n.t("#{ERROR_BASE_KEY}.saledate.outside_collection_window", year_combo: bulk_upload.year_combo, start_year: bulk_upload.year, end_year: bulk_upload.end_year), category: :setup)
errors.add(:field_2, I18n.t("#{ERROR_BASE_KEY}.saledate.outside_collection_window", year_combo: bulk_upload.year_combo, start_year: bulk_upload.year, end_year: bulk_upload.end_year), category: :setup)
errors.add(:field_3, I18n.t("#{ERROR_BASE_KEY}.saledate.outside_collection_window", year_combo: bulk_upload.year_combo, start_year: bulk_upload.year, end_year: bulk_upload.end_year), category: :setup)
end
end
@ -1986,10 +1424,10 @@ private
if log_already_exists?
error_message = I18n.t("#{ERROR_BASE_KEY}.duplicate")
errors.add(:field_1, error_message) # Owning org
errors.add(:field_4, error_message) # Sale completion date
errors.add(:field_5, error_message) # Sale completion date
errors.add(:field_6, error_message) # Sale completion date
errors.add(:field_4, error_message) # Owning org
errors.add(:field_1, error_message) # Sale completion date
errors.add(:field_2, error_message) # Sale completion date
errors.add(:field_3, error_message) # Sale completion date
errors.add(:field_24, error_message) # Postcode
errors.add(:field_25, error_message) # Postcode
errors.add(:field_28, error_message) # Buyer 1 age

14
spec/services/bulk_upload/sales/year2025/csv_parser_spec.rb

@ -27,7 +27,7 @@ RSpec.describe BulkUpload::Sales::Year2025::CsvParser do
end
it "parses csv correctly" do
expect(service.row_parsers[0].field_22).to eql(log.uprn)
expect(service.row_parsers[0].field_19).to eql(log.uprn)
end
it "counts the number of valid field numbers correctly" do
@ -59,7 +59,7 @@ RSpec.describe BulkUpload::Sales::Year2025::CsvParser do
end
it "parses csv correctly" do
expect(service.row_parsers[0].field_22).to eql(log.uprn)
expect(service.row_parsers[0].field_19).to eql(log.uprn)
end
it "counts the number of valid field numbers correctly" do
@ -92,7 +92,7 @@ RSpec.describe BulkUpload::Sales::Year2025::CsvParser do
end
it "parses csv correctly" do
expect(service.row_parsers[0].field_22).to eql(log.uprn)
expect(service.row_parsers[0].field_19).to eql(log.uprn)
end
end
@ -112,7 +112,7 @@ RSpec.describe BulkUpload::Sales::Year2025::CsvParser do
end
it "parses csv correctly" do
expect(service.row_parsers[0].field_22).to eql(log.uprn)
expect(service.row_parsers[0].field_19).to eql(log.uprn)
end
end
@ -129,7 +129,7 @@ RSpec.describe BulkUpload::Sales::Year2025::CsvParser do
end
it "parses csv correctly" do
expect(service.row_parsers[0].field_22).to eql(log.uprn)
expect(service.row_parsers[0].field_19).to eql(log.uprn)
end
end
@ -146,7 +146,7 @@ RSpec.describe BulkUpload::Sales::Year2025::CsvParser do
end
it "parses csv correctly" do
expect(service.row_parsers[0].field_22).to eql(log.uprn)
expect(service.row_parsers[0].field_19).to eql(log.uprn)
end
end
@ -185,7 +185,7 @@ RSpec.describe BulkUpload::Sales::Year2025::CsvParser do
end
it "parses csv correctly" do
expect(service.row_parsers[0].field_22).to eql(log.uprn)
expect(service.row_parsers[0].field_19).to eql(log.uprn)
end
end
end

324
spec/services/bulk_upload/sales/year2025/row_parser_spec.rb

@ -14,12 +14,12 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
let(:setup_section_params) do
{
bulk_upload:,
field_1: owning_org.old_visible_id, # organisation
field_2: managing_org.old_visible_id, # organisation
field_3: user.email, # user
field_4: now.day.to_s, # sale day
field_5: now.month.to_s, # sale month
field_6: now.strftime("%g"), # sale year
field_1: now.day.to_s, # sale day
field_2: now.month.to_s, # sale month
field_3: now.strftime("%g"), # sale year
field_4: owning_org.old_visible_id, # organisation
field_5: managing_org.old_visible_id, # organisation
field_6: user.email, # user
field_7: "test id", # purchase id
field_8: "1", # owhershipsch
field_9: "2", # shared ownership sale type
@ -32,12 +32,11 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
let(:valid_attributes) do
{
bulk_upload:,
field_1: owning_org.old_visible_id,
field_2: managing_org.old_visible_id,
field_4: "12",
field_5: "5",
field_6: "24",
field_1: "12",
field_2: "5",
field_3: "25",
field_4: owning_org.old_visible_id,
field_5: managing_org.old_visible_id,
field_7: "test id",
field_8: "1",
field_9: "2",
@ -93,26 +92,33 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
field_83: "1",
field_84: "1",
field_85: "1",
field_86: "250000",
field_87: "25",
field_88: "1",
field_107: "250000",
field_108: "25",
field_109: "1",
field_89: "5000",
field_90: "20",
field_91: "30",
field_92: "3",
field_93: "2022",
field_96: "10",
field_97: "40",
field_98: "1",
field_99: "2",
field_94: "200",
field_120: "20000",
field_91: "20000",
field_111: "800",
field_100: "05",
field_101: "04",
field_102: "2020",
field_103: "4",
field_104: "06",
field_105: "07",
field_106: "2023",
field_110: "900",
}
end
around do |example|
create(:organisation_relationship, parent_organisation: owning_org, child_organisation: managing_org)
Timecop.freeze(Time.zone.local(2025, 2, 22)) do
Timecop.freeze(Time.zone.local(2026, 2, 22)) do
Singleton.__init__(FormHandler)
example.run
end
@ -127,7 +133,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
context "when any field is populated" do
before do
parser.field_1 = "1"
parser.field_4 = "1"
end
it "returns false" do
@ -308,7 +314,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end
context "when an invalid value error has been added" do
let(:attributes) { setup_section_params.merge({ field_32: "100" }) }
let(:attributes) { setup_section_params.merge({ field_10: "2", field_32: "100" }) }
it "does not add an additional error" do
parser.valid?
@ -332,7 +338,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
errors = parser.errors.select { |e| e.options[:category] == :setup }.map(&:attribute).sort
expect(errors).to eql(%i[field_1 field_14 field_15 field_2 field_4 field_5 field_6 field_8])
expect(errors).to eql(%i[field_1 field_14 field_15 field_2 field_3 field_4 field_5 field_8])
end
end
@ -350,7 +356,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
errors = parser.errors.select { |e| e.options[:category] == :setup }.map(&:attribute).sort
expect(errors).to eql(%i[field_1 field_12 field_14 field_15 field_2 field_4 field_5 field_6 field_9])
expect(errors).to eql(%i[field_1 field_10 field_12 field_14 field_15 field_2 field_3 field_4 field_5 field_9])
end
end
@ -370,7 +376,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
errors = parser.errors.select { |e| e.options[:category] == :setup }.map(&:attribute).sort
expect(errors).to eql(%i[field_1 field_13 field_14 field_15 field_2 field_4 field_5 field_6])
expect(errors).to eql(%i[field_1 field_10 field_13 field_14 field_15 field_2 field_3 field_4 field_5])
end
end
@ -380,7 +386,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
bulk_upload:,
field_7: "test id",
field_8: "2",
field_10: nil,
field_11: nil,
}
end
@ -389,7 +395,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
errors = parser.errors.select { |e| e.options[:category] == :setup }.map(&:attribute).sort
expect(errors).to eql(%i[field_1 field_10 field_12 field_14 field_15 field_2 field_4 field_5 field_6])
expect(errors).to eql(%i[field_1 field_11 field_12 field_14 field_15 field_2 field_3 field_4 field_5])
end
end
@ -408,17 +414,17 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
errors = parser.errors.select { |e| e.options[:category] == :setup }.map(&:attribute).sort
expect(errors).to eql(%i[field_1 field_14 field_15 field_2 field_4 field_5 field_6 field_8])
expect(errors).to eql(%i[field_1 field_14 field_15 field_2 field_3 field_4 field_5 field_8])
end
end
describe "#field_1" do # owning org
describe "#field_4" do # owning org
context "when no data given" do
let(:attributes) { setup_section_params.merge(field_1: nil) }
let(:attributes) { setup_section_params.merge(field_4: nil) }
it "is not permitted as setup error" do
parser.valid?
expect(parser.errors.where(:field_1, category: :setup).map(&:message)).to eql([I18n.t("validations.sales.2025.bulk_upload.not_answered", question: "owning organisation.")])
expect(parser.errors.where(:field_4, category: :setup).map(&:message)).to eql([I18n.t("validations.sales.2025.bulk_upload.not_answered", question: "owning organisation.")])
end
it "blocks log creation" do
@ -428,11 +434,11 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end
context "when cannot find owning org" do
let(:attributes) { { bulk_upload:, field_1: "donotexist" } }
let(:attributes) { { bulk_upload:, field_4: "donotexist" } }
it "is not permitted as a setup error" do
parser.valid?
expect(parser.errors.where(:field_1, category: :setup).map(&:message)).to eql([I18n.t("validations.sales.2025.bulk_upload.owning_organisation.not_found")])
expect(parser.errors.where(:field_4, category: :setup).map(&:message)).to eql([I18n.t("validations.sales.2025.bulk_upload.owning_organisation.not_found")])
end
it "blocks log creation" do
@ -444,11 +450,11 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
context "when not affiliated with owning org" do
let(:unaffiliated_org) { create(:organisation, :with_old_visible_id) }
let(:attributes) { { bulk_upload:, field_1: unaffiliated_org.old_visible_id } }
let(:attributes) { { bulk_upload:, field_4: unaffiliated_org.old_visible_id } }
it "is not permitted as setup error" do
parser.valid?
expect(parser.errors.where(:field_1, category: :setup).map(&:message)).to eql([I18n.t("validations.sales.2025.bulk_upload.owning_organisation.not_permitted.not_support")])
expect(parser.errors.where(:field_4, category: :setup).map(&:message)).to eql([I18n.t("validations.sales.2025.bulk_upload.owning_organisation.not_permitted.not_support")])
end
it "blocks log creation" do
@ -461,7 +467,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
let(:merged_org) { create(:organisation, :with_old_visible_id, holds_own_stock: true) }
let(:merged_org_stock_owner) { create(:organisation, :with_old_visible_id, holds_own_stock: true) }
let(:attributes) { { bulk_upload:, field_1: merged_org_stock_owner.old_visible_id } }
let(:attributes) { { bulk_upload:, field_4: merged_org_stock_owner.old_visible_id } }
before do
create(:organisation_relationship, parent_organisation: merged_org_stock_owner, child_organisation: merged_org)
@ -472,14 +478,14 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
it "is permitted" do
parser.valid?
expect(parser.errors.where(:field_1)).not_to be_present
expect(parser.errors.where(:field_4)).not_to be_present
end
end
context "when user's org has absorbed owning organisation" do
let(:merged_org) { create(:organisation, :with_old_visible_id, holds_own_stock: true) }
let(:attributes) { { bulk_upload:, field_1: merged_org.old_visible_id, field_3: user.email } }
let(:attributes) { { bulk_upload:, field_4: merged_org.old_visible_id, field_6: user.email } }
before do
merged_org.update!(absorbing_organisation: user.organisation, merge_date: Time.zone.today)
@ -492,15 +498,15 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
parser = described_class.new(attributes)
parser.valid?
expect(parser.errors.where(:field_1)).not_to be_present
expect(parser.errors.where(:field_3)).not_to be_present
expect(parser.errors.where(:field_4)).not_to be_present
expect(parser.errors.where(:field_6)).not_to be_present
end
end
context "when user's org has absorbed owning organisation before the startdate" do
let(:merged_org) { create(:organisation, :with_old_visible_id, holds_own_stock: true) }
let(:attributes) { setup_section_params.merge({ field_1: merged_org.old_visible_id, field_3: user.email }) }
let(:attributes) { setup_section_params.merge({ field_4: merged_org.old_visible_id, field_6: user.email }) }
before do
merged_org.update!(absorbing_organisation: user.organisation, merge_date: Time.zone.today - 3.years)
@ -513,17 +519,17 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
parser = described_class.new(attributes)
parser.valid?
expect(parser.errors[:field_1]).to include(/The owning organisation must be active on the sale completion date/)
expect(parser.errors[:field_4]).to include(/Enter a date when the owning organisation was active/)
expect(parser.errors[:field_5]).to include(/Enter a date when the owning organisation was active/)
expect(parser.errors[:field_6]).to include(/Enter a date when the owning organisation was active/)
expect(parser.errors[:field_4]).to include(/The owning organisation must be active on the sale completion date/)
expect(parser.errors[:field_1]).to include(/Enter a date when the owning organisation was active/)
expect(parser.errors[:field_2]).to include(/Enter a date when the owning organisation was active/)
expect(parser.errors[:field_3]).to include(/Enter a date when the owning organisation was active/)
end
end
context "when user is an unaffiliated non-support user and bulk upload organisation is affiliated with the owning organisation" do
let(:affiliated_org) { create(:organisation, :with_old_visible_id) }
let(:unaffiliated_user) { create(:user, organisation: create(:organisation)) }
let(:attributes) { { bulk_upload:, field_1: affiliated_org.old_visible_id } }
let(:attributes) { { bulk_upload:, field_4: affiliated_org.old_visible_id } }
let(:organisation_id) { unaffiliated_user.organisation_id }
before do
@ -531,18 +537,18 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
bulk_upload.update!(organisation_id:, user: unaffiliated_user)
end
it "blocks log creation and adds an error to field_1" do
it "blocks log creation and adds an error to field_4" do
parser = described_class.new(attributes)
parser.valid?
expect(parser).to be_block_log_creation
expect(parser.errors[:field_1]).to include(I18n.t("validations.sales.2025.bulk_upload.owning_organisation.not_permitted.not_support"))
expect(parser.errors[:field_4]).to include(I18n.t("validations.sales.2025.bulk_upload.owning_organisation.not_permitted.not_support"))
end
end
context "when user is an unaffiliated support user and bulk upload organisation is affiliated with the owning organisation" do
let(:affiliated_org) { create(:organisation, :with_old_visible_id) }
let(:unaffiliated_support_user) { create(:user, :support, organisation: create(:organisation)) }
let(:attributes) { { bulk_upload:, field_1: affiliated_org.old_visible_id } }
let(:attributes) { { bulk_upload:, field_4: affiliated_org.old_visible_id } }
let(:organisation_id) { affiliated_org.id }
before do
@ -550,21 +556,21 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
bulk_upload.update!(organisation_id:, user: unaffiliated_support_user)
end
it "does not block log creation and does not add an error to field_1" do
it "does not block log creation and does not add an error to field_4" do
parser = described_class.new(attributes)
parser.valid?
expect(parser.errors[:field_1]).not_to include(I18n.t("validations.sales.2025.bulk_upload.owning_organisation.not_permitted.not_support"))
expect(parser.errors[:field_4]).not_to include(I18n.t("validations.sales.2025.bulk_upload.owning_organisation.not_permitted.not_support"))
end
end
end
describe "#field_3" do # username for assigned_to
describe "#field_6" do # username for assigned_to
context "when blank" do
let(:attributes) { setup_section_params.merge(bulk_upload:, field_3: nil) }
let(:attributes) { setup_section_params.merge(bulk_upload:, field_6: nil) }
it "is permitted" do
parser.valid?
expect(parser.errors[:field_3]).to be_blank
expect(parser.errors[:field_6]).to be_blank
end
it "sets assigned to to bulk upload user" do
@ -581,12 +587,12 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
context "when blank and bulk upload user is support" do
let(:bulk_upload) { create(:bulk_upload, :sales, user: create(:user, :support), year: 2025) }
let(:attributes) { setup_section_params.merge(bulk_upload:, field_3: nil) }
let(:attributes) { setup_section_params.merge(bulk_upload:, field_6: nil) }
it "is not permitted" do
parser.valid?
expect(parser.errors[:field_3]).to be_present
expect(parser.errors[:field_3]).to include(I18n.t("validations.sales.2025.bulk_upload.not_answered", question: "what is the CORE username of the account this sales log should be assigned to?"))
expect(parser.errors[:field_6]).to be_present
expect(parser.errors[:field_6]).to include(I18n.t("validations.sales.2025.bulk_upload.not_answered", question: "what is the CORE username of the account this sales log should be assigned to?"))
end
it "blocks log creation" do
@ -596,22 +602,22 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end
context "when user could not be found" do
let(:attributes) { { bulk_upload:, field_3: "idonotexist@example.com" } }
let(:attributes) { { bulk_upload:, field_6: "idonotexist@example.com" } }
it "is not permitted" do
parser.valid?
expect(parser.errors[:field_3]).to be_present
expect(parser.errors[:field_6]).to be_present
end
end
context "when an unaffiliated user" do
let(:other_user) { create(:user) }
let(:attributes) { { bulk_upload:, field_1: owning_org.old_visible_id, field_3: other_user.email } }
let(:attributes) { { bulk_upload:, field_4: owning_org.old_visible_id, field_6: other_user.email } }
it "is not permitted as a setup error" do
parser.valid?
expect(parser.errors.where(:field_3, category: :setup)).to be_present
expect(parser.errors.where(:field_6, category: :setup)).to be_present
end
it "blocks log creation" do
@ -623,11 +629,11 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
context "when a user part of owning org" do
let(:other_user) { create(:user, organisation: owning_org) }
let(:attributes) { { bulk_upload:, field_1: owning_org.old_visible_id, field_3: other_user.email } }
let(:attributes) { { bulk_upload:, field_4: owning_org.old_visible_id, field_6: other_user.email } }
it "is permitted" do
parser.valid?
expect(parser.errors[:field_3]).to be_blank
expect(parser.errors[:field_6]).to be_blank
end
it "sets assigned to to the user" do
@ -644,59 +650,59 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
context "when email matches other than casing" do
let(:other_user) { create(:user, organisation: owning_org) }
let(:attributes) { { bulk_upload:, field_1: owning_org.old_visible_id, field_3: other_user.email.upcase! } }
let(:attributes) { { bulk_upload:, field_4: owning_org.old_visible_id, field_6: other_user.email.upcase! } }
it "is permitted" do
parser.valid?
expect(parser.errors[:field_3]).to be_blank
expect(parser.errors[:field_6]).to be_blank
end
end
end
describe "fields 3, 4, 5 => saledate" do
describe "fields 1, 2, 3 => saledate" do
context "when all of these fields are blank" do
let(:attributes) { setup_section_params.merge({ field_4: nil, field_5: nil, field_6: nil }) }
let(:attributes) { setup_section_params.merge({ field_1: nil, field_2: nil, field_3: nil }) }
it "returns them as setup errors" do
parser.valid?
expect(parser.errors.where(:field_4, category: :setup)).to be_present
expect(parser.errors.where(:field_5, category: :setup)).to be_present
expect(parser.errors.where(:field_6, category: :setup)).to be_present
expect(parser.errors.where(:field_1, category: :setup)).to be_present
expect(parser.errors.where(:field_2, category: :setup)).to be_present
expect(parser.errors.where(:field_3, category: :setup)).to be_present
end
end
context "when one of these fields is blank" do
let(:attributes) { setup_section_params.merge({ field_4: "1", field_5: "1", field_6: nil }) }
let(:attributes) { setup_section_params.merge({ field_1: "1", field_2: "1", field_3: nil }) }
it "returns an error only on blank field as setup error" do
parser.valid?
expect(parser.errors[:field_4]).to be_blank
expect(parser.errors[:field_5]).to be_blank
expect(parser.errors.where(:field_6, category: :setup)).to be_present
expect(parser.errors[:field_1]).to be_blank
expect(parser.errors[:field_2]).to be_blank
expect(parser.errors.where(:field_3, category: :setup)).to be_present
end
end
context "when field 6 is 4 digits instead of 2" do
let(:attributes) { setup_section_params.merge({ bulk_upload:, field_6: "2025" }) }
context "when field 3 is 4 digits instead of 2" do
let(:attributes) { setup_section_params.merge({ bulk_upload:, field_3: "2025" }) }
it "correctly sets the date" do
parser.valid?
expect(parser.errors.where(:field_6, category: :setup)).to be_empty
expect(parser.errors.where(:field_3, category: :setup)).to be_empty
expect(parser.log.saledate).to eq(Time.zone.local(2025, 5, 1))
end
end
context "when field 5 is not 2 or 4 digits" do
let(:attributes) { setup_section_params.merge({ bulk_upload:, field_6: "202" }) }
context "when field 2 is not 2 or 4 digits" do
let(:attributes) { setup_section_params.merge({ bulk_upload:, field_3: "202" }) }
it "returns a setup error" do
parser.valid?
expect(parser.errors.where(:field_6, category: :setup).map(&:message)).to include(I18n.t("validations.sales.2025.bulk_upload.saledate.year_not_two_or_four_digits"))
expect(parser.errors.where(:field_3, category: :setup).map(&:message)).to include(I18n.t("validations.sales.2025.bulk_upload.saledate.year_not_two_or_four_digits"))
end
end
context "when invalid date given" do
let(:attributes) { setup_section_params.merge({ field_4: "a", field_5: "12", field_6: "2023" }) }
let(:attributes) { setup_section_params.merge({ field_1: "a", field_2: "12", field_3: "2023" }) }
it "does not raise an error" do
expect { parser.valid? }.not_to raise_error
@ -710,15 +716,15 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end
end
let(:attributes) { setup_section_params.merge({ field_4: "1", field_5: "10", field_6: "24" }) }
let(:attributes) { setup_section_params.merge({ field_1: "1", field_2: "10", field_3: "25" }) }
let(:bulk_upload) { create(:bulk_upload, :sales, user:, year: 2025) }
it "does not return errors" do
parser.valid?
expect(parser.errors[:field_4]).not_to be_present
expect(parser.errors[:field_5]).not_to be_present
expect(parser.errors[:field_6]).not_to be_present
expect(parser.errors[:field_1]).not_to be_present
expect(parser.errors[:field_2]).not_to be_present
expect(parser.errors[:field_3]).not_to be_present
end
end
@ -729,15 +735,15 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end
end
let(:attributes) { setup_section_params.merge({ field_4: "1", field_5: "1", field_6: "22" }) }
let(:attributes) { setup_section_params.merge({ field_1: "1", field_2: "1", field_3: "22" }) }
let(:bulk_upload) { create(:bulk_upload, :sales, user:, year: 2023) }
it "returns setup errors" do
parser.valid?
expect(parser.errors.where(:field_4, category: :setup)).to be_present
expect(parser.errors.where(:field_5, category: :setup)).to be_present
expect(parser.errors.where(:field_6, category: :setup)).to be_present
expect(parser.errors.where(:field_1, category: :setup)).to be_present
expect(parser.errors.where(:field_2, category: :setup)).to be_present
expect(parser.errors.where(:field_3, category: :setup)).to be_present
end
end
end
@ -760,10 +766,10 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
error_message = I18n.t("validations.sales.2025.bulk_upload.duplicate")
[
:field_1, # Owning org
:field_4, # Sale completion date
:field_5, # Sale completion date
:field_6, # Sale completion date
:field_4, # Owning org
:field_1, # Sale completion date
:field_2, # Sale completion date
:field_3, # Sale completion date
:field_24, # Postcode
:field_25, # Postcode
:field_28, # Buyer 1 age
@ -790,10 +796,10 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
parser.valid?
[
:field_1, # Owning org
:field_4, # Sale completion date
:field_5, # Sale completion date
:field_6, # Sale completion date
:field_4, # Owning org
:field_1, # Sale completion date
:field_2, # Sale completion date
:field_3, # Sale completion date
:field_24, # Postcode
:field_25, # Postcode
:field_28, # Buyer 1 age
@ -828,42 +834,42 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end
end
describe "#field_10" do # type for discounted sale
describe "#field_11" do # type for discounted sale
context "when an invalid option" do
let(:attributes) { setup_section_params.merge({ field_10: "100" }) }
let(:attributes) { setup_section_params.merge({ field_11: "100" }) }
it "returns setup error" do
parser.valid?
expect(parser.errors.where(:field_10, category: :setup)).to be_present
expect(parser.errors.where(:field_11, category: :setup)).to be_present
end
end
end
describe "#field_116" do # percentage discount
describe "#field_115" do # percentage discount
context "when percentage discount over 70" do
let(:attributes) { valid_attributes.merge({ field_8: "2", field_116: "71" }) }
let(:attributes) { valid_attributes.merge({ field_8: "2", field_10: "2", field_11: "9", field_115: "71" }) }
it "returns correct error" do
parser.valid?
expect(parser.errors.where(:field_116).map(&:message)).to include(I18n.t("validations.sales.2025.bulk_upload.numeric.within_range", field: "Percentage discount", min: "0%", max: "70%"))
expect(parser.errors.where(:field_115).map(&:message)).to include(I18n.t("validations.sales.2025.bulk_upload.numeric.within_range", field: "Percentage discount", min: "0%", max: "70%"))
end
end
context "when percentage discount not over 70" do
let(:attributes) { valid_attributes.merge({ field_8: "2", field_116: "70" }) }
let(:attributes) { valid_attributes.merge({ field_8: "2", field_10: "2", field_115: "70" }) }
it "does not return error" do
parser.valid?
expect(parser.errors.where(:field_116)).not_to be_present
expect(parser.errors.where(:field_115)).not_to be_present
end
end
context "when percentage less than 0" do
let(:attributes) { valid_attributes.merge({ field_8: "2", field_116: "-1" }) }
let(:attributes) { valid_attributes.merge({ field_8: "2", field_10: "2", field_115: "-1" }) }
it "returns correct error" do
parser.valid?
expect(parser.errors.where(:field_116).map(&:message)).to include(I18n.t("validations.sales.2025.bulk_upload.numeric.within_range", field: "Percentage discount", min: "0%", max: "70%"))
expect(parser.errors.where(:field_115).map(&:message)).to include(I18n.t("validations.sales.2025.bulk_upload.numeric.within_range", field: "Percentage discount", min: "0%", max: "70%"))
end
end
end
@ -1199,7 +1205,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
describe "#field_33" do # will buyer1 live in property?
context "when not a possible value" do
let(:attributes) { valid_attributes.merge({ field_33: "3" }) }
let(:attributes) { valid_attributes.merge({ field_10: "2", field_33: "3" }) }
it "is not valid" do
parser.valid?
@ -1208,13 +1214,13 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end
end
describe "#field_88" do # shared ownership mortgageused
describe "#field_109" do # staircasing mortgageused
context "when invalid value" do
let(:attributes) { setup_section_params.merge(field_88: "4") }
let(:attributes) { setup_section_params.merge(field_109: "4") }
it "returns correct errors" do
parser.valid?
expect(parser.errors[:field_88]).to include(I18n.t("validations.sales.2025.bulk_upload.invalid_option", question: "was a mortgage used for the purchase of this property? - Shared ownership."))
expect(parser.errors[:field_109]).to include(I18n.t("validations.sales.2025.bulk_upload.invalid_option", question: "was a mortgage used for this staircasing transaction?"))
parser.log.blank_invalid_non_setup_fields!
parser.log.save!
@ -1223,11 +1229,11 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end
context "when value is 3 and stairowned is not 100" do
let(:attributes) { setup_section_params.merge(field_88: "3", field_10: "1", field_96: "50", field_97: "99", field_120: nil) }
let(:attributes) { setup_section_params.merge(field_109: "3", field_10: "1", field_96: "50", field_97: "99", field_120: nil) }
it "returns correct errors" do
parser.valid?
expect(parser.errors[:field_88]).to include("The percentage owned has to be 100% if the mortgage used is 'Don’t know'")
expect(parser.errors[:field_109]).to include("The percentage owned has to be 100% if the mortgage used is 'Don’t know'")
parser.log.blank_invalid_non_setup_fields!
parser.log.save!
@ -1236,22 +1242,35 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end
context "when value is 3 and stairowned is not answered" do
let(:attributes) { setup_section_params.merge(field_88: "3", field_10: "1", field_96: "50", field_97: nil, field_120: nil) }
let(:attributes) { setup_section_params.merge(field_109: "3", field_10: "1", field_96: "50", field_97: nil, field_120: nil) }
it "does not add errors" do
parser.valid?
expect(parser.errors[:field_88]).to be_empty
expect(parser.errors[:field_88]).to be_empty
expect(parser.errors[:field_109]).to be_empty
end
end
context "when it is not a staircasing transaction" do
context "when value is 3 and stairowned is not answered" do
let(:attributes) { setup_section_params.merge(field_88: "3", field_10: "2", field_96: "50", field_97: nil, field_120: nil) }
context "when value is 3 and stairowned is 100" do
let(:attributes) { setup_section_params.merge(field_109: "3", field_10: "1", field_96: "50", field_97: "100", field_120: nil) }
it "does not add errors and sets mortgage used to 3" do
parser.valid?
expect(parser.log.mortgageused).to eq(3)
expect(parser.log.stairowned).to eq(100)
expect(parser.log.deposit).to be_nil
expect(parser.errors[:field_109]).to be_empty
expect(parser.errors[:field_120]).to be_empty
end
end
end
describe "#field_88" do # shared ownership mortgageused
context "when invalid value" do
let(:attributes) { setup_section_params.merge(field_10: "2", field_88: "4") }
it "returns correct errors" do
parser.valid?
expect(parser.errors[:field_88]).to include(I18n.t("validations.invalid_option", question: "was a mortgage used for the purchase of this property?"))
expect(parser.errors[:field_88]).to include(I18n.t("validations.sales.2025.bulk_upload.invalid_option", question: "was a mortgage used for the purchase of this property? - Shared ownership."))
parser.log.blank_invalid_non_setup_fields!
parser.log.save!
@ -1259,8 +1278,8 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end
end
context "when value is 3 and stairowned is 100" do
let(:attributes) { setup_section_params.merge(field_88: "3", field_10: "2", field_96: "50", field_97: "100", field_120: nil) }
context "when value is 3 and stairowned is not answered" do
let(:attributes) { setup_section_params.merge(field_88: "3", field_10: "2", field_96: "50", field_97: nil, field_120: nil) }
it "returns correct errors" do
parser.valid?
@ -1271,23 +1290,9 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
expect(parser.log.mortgageused).to be_nil
end
end
end
context "when value is 3 and stairowned is 100" do
let(:attributes) { setup_section_params.merge(field_88: "3", field_10: "1", field_96: "50", field_97: "100", field_120: nil) }
it "does not add errors and sets mortgage used to 3" do
parser.valid?
expect(parser.log.mortgageused).to eq(3)
expect(parser.log.stairowned).to eq(100)
expect(parser.log.deposit).to be_nil
expect(parser.errors[:field_88]).to be_empty
expect(parser.errors[:field_120]).to be_empty
end
end
context "with non staircasing mortgage error" do
let(:attributes) { setup_section_params.merge(field_9: "30", field_88: "1", field_89: "10000", field_120: "5000", field_86: "30000", field_87: "28", field_10: "2") }
let(:attributes) { setup_section_params.merge(field_9: "30", field_88: "1", field_89: "10000", field_91: "5000", field_86: "30000", field_87: "28", field_10: "2") }
it "does not add a BU error on type (because it's a setup field and would block log creation)" do
parser.valid?
@ -1297,7 +1302,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
it "includes errors on other related fields" do
parser.valid?
expect(parser.errors[:field_89]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.</br></br>The full purchase price (£30,000.00) multiplied by the percentage equity stake purchased (28.0%) is £8,400.00.</br></br>These two amounts should be the same.")
expect(parser.errors[:field_120]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.</br></br>The full purchase price (£30,000.00) multiplied by the percentage equity stake purchased (28.0%) is £8,400.00.</br></br>These two amounts should be the same.")
expect(parser.errors[:field_91]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.</br></br>The full purchase price (£30,000.00) multiplied by the percentage equity stake purchased (28.0%) is £8,400.00.</br></br>These two amounts should be the same.")
expect(parser.errors[:field_86]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.</br></br>The full purchase price (£30,000.00) multiplied by the percentage equity stake purchased (28.0%) is £8,400.00.</br></br>These two amounts should be the same.")
expect(parser.errors[:field_87]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.</br></br>The full purchase price (£30,000.00) multiplied by the percentage equity stake purchased (28.0%) is £8,400.00.</br></br>These two amounts should be the same.")
end
@ -1305,23 +1310,22 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
it "does not add errors to other ownership type fields" do
parser.valid?
expect(parser.errors[:field_117]).to be_empty
expect(parser.errors[:field_126]).to be_empty
expect(parser.errors[:field_118]).to be_empty
expect(parser.errors[:field_127]).to be_empty
expect(parser.errors[:field_123]).to be_empty
expect(parser.errors[:field_130]).to be_empty
expect(parser.errors[:field_114]).to be_empty
expect(parser.errors[:field_125]).to be_empty
expect(parser.errors[:field_120]).to be_empty
expect(parser.errors[:field_113]).to be_empty
expect(parser.errors[:field_107]).to be_empty
expect(parser.errors[:field_108]).to be_empty
expect(parser.errors[:field_116]).to be_empty
expect(parser.errors[:field_109]).to be_empty
end
end
end
describe "#field_117" do
let(:attributes) { valid_attributes.merge({ field_8: "2", field_10: "9", field_117: "3" }) }
describe "#field_116" do
let(:attributes) { valid_attributes.merge({ field_8: "2", field_11: "9", field_116: "3" }) }
it "does not allow 3 (don't know) as an option for discounted ownership" do
parser.valid?
expect(parser.errors[:field_117]).to include(I18n.t("validations.invalid_option", question: "was a mortgage used for the purchase of this property?"))
expect(parser.errors[:field_116]).to include(I18n.t("validations.invalid_option", question: "was a mortgage used for the purchase of this property?"))
parser.log.blank_invalid_non_setup_fields!
parser.log.save!
@ -1329,7 +1333,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end
context "when validate_discounted_ownership_value is triggered" do
let(:attributes) { setup_section_params.merge(field_114: 100, field_123: 100, field_8: 2, field_10: 9, field_117: 2, field_116: 10) }
let(:attributes) { setup_section_params.merge(field_113: 100, field_120: 100, field_8: 2, field_10: 2, field_11: 9, field_116: 2, field_115: 10) }
it "only adds errors to the discounted ownership field" do
parser.valid?
@ -1342,7 +1346,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
describe "soft validations" do
context "when soft validation is triggered" do
let(:attributes) { valid_attributes.merge({ field_28: 22, field_32: 5 }) }
let(:attributes) { valid_attributes.merge({ field_10: 2, field_28: 22, field_32: 5 }) }
it "adds an error to the relevant fields" do
parser.valid?
@ -1756,7 +1760,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end
describe "with living before purchase years for discounted ownership more than 0" do
let(:attributes) { setup_section_params.merge({ field_8: "2", field_113: "1" }) }
let(:attributes) { setup_section_params.merge({ field_8: "2", field_112: "1" }) }
it "is sets living before purchase asked to yes and sets the correct living before purchase years" do
expect(parser.log.proplen_asked).to be(0)
@ -1774,7 +1778,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end
describe "with living before purchase 0 years for discounted ownership set to 0" do
let(:attributes) { setup_section_params.merge({ field_8: "2", field_113: "0" }) }
let(:attributes) { setup_section_params.merge({ field_8: "2", field_112: "0" }) }
it "is sets living before purchase asked to no" do
expect(parser.log.proplen_asked).to be(1)
@ -1793,7 +1797,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end
context "when mscharge is given, but is set to 0 for discounted ownership" do
let(:attributes) { valid_attributes.merge(field_8: "2", field_124: "0") }
let(:attributes) { valid_attributes.merge(field_8: "2", field_121: "0") }
it "does not override variables correctly" do
log = parser.log
@ -1825,13 +1829,13 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end
context "when blank" do
let(:attributes) { { bulk_upload:, field_2: "", field_6: "not blank" } }
let(:attributes) { { bulk_upload:, field_5: "", field_3: "not blank" } }
it "is not permitted as setup error" do
parser.valid?
setup_errors = parser.errors.select { |e| e.options[:category] == :setup }
expect(setup_errors.find { |e| e.attribute == :field_2 }.message).to eql(I18n.t("validations.not_answered", question: "reported by."))
expect(setup_errors.find { |e| e.attribute == :field_5 }.message).to eql(I18n.t("validations.not_answered", question: "reported by."))
end
it "blocks log creation" do
@ -1841,13 +1845,13 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end
context "when cannot find managing org" do
let(:attributes) { { bulk_upload:, field_2: "donotexist" } }
let(:attributes) { { bulk_upload:, field_5: "donotexist" } }
it "is not permitted as setup error" do
parser.valid?
setup_errors = parser.errors.select { |e| e.options[:category] == :setup }
expect(setup_errors.find { |e| e.attribute == :field_2 }.message).to eql(I18n.t("validations.not_answered", question: "reported by."))
expect(setup_errors.find { |e| e.attribute == :field_5 }.message).to eql(I18n.t("validations.not_answered", question: "reported by."))
end
it "blocks log creation" do
@ -1859,13 +1863,13 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
context "when not affiliated with managing org" do
let(:unaffiliated_org) { create(:organisation, :with_old_visible_id) }
let(:attributes) { { bulk_upload:, field_1: owning_org.old_visible_id, field_2: unaffiliated_org.old_visible_id } }
let(:attributes) { { bulk_upload:, field_4: owning_org.old_visible_id, field_5: unaffiliated_org.old_visible_id } }
it "is not permitted as setup error" do
parser.valid?
setup_errors = parser.errors.select { |e| e.options[:category] == :setup }
expect(setup_errors.find { |e| e.attribute == :field_2 }.message).to eql(I18n.t("validations.sales.2025.bulk_upload.assigned_to.managing_organisation_not_related"))
expect(setup_errors.find { |e| e.attribute == :field_5 }.message).to eql(I18n.t("validations.sales.2025.bulk_upload.assigned_to.managing_organisation_not_related"))
end
it "blocks log creation" do
@ -1887,7 +1891,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
parser.valid?
setup_errors = parser.errors.select { |e| e.options[:category] == :setup }
expect(setup_errors.find { |e| e.attribute == :field_1 }.message).to eql(I18n.t("validations.sales.2025.bulk_upload.owning_organisation.not_stock_owner"))
expect(setup_errors.find { |e| e.attribute == :field_4 }.message).to eql(I18n.t("validations.sales.2025.bulk_upload.owning_organisation.not_stock_owner"))
end
it "blocks log creation" do

Loading…
Cancel
Save