Browse Source

Merge branch 'main' into CLDC-4325-ensure-tests-will-pass-on-go-live

pull/3250/head
samyou-softwire 3 weeks ago
parent
commit
ba0431a78d
  1. 119
      Gemfile.lock
  2. 3
      app/services/bulk_upload/lettings/year2025/row_parser.rb
  3. 3
      app/services/bulk_upload/lettings/year2026/row_parser.rb
  4. 3
      app/services/bulk_upload/sales/year2025/row_parser.rb
  5. 83
      app/services/bulk_upload/sales/year2026/row_parser.rb
  6. 4
      config/locales/validations/sales/2026/bulk_upload.en.yml
  7. 27
      docs/Gemfile.lock
  8. 6
      spec/services/bulk_upload/sales/year2023/row_parser_spec.rb
  9. 6
      spec/services/bulk_upload/sales/year2024/row_parser_spec.rb
  10. 4
      spec/services/bulk_upload/sales/year2025/row_parser_spec.rb
  11. 550
      spec/services/bulk_upload/sales/year2026/row_parser_spec.rb

119
Gemfile.lock

@ -1,70 +1,72 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (7.2.2.2) actioncable (7.2.3.1)
actionpack (= 7.2.2.2) actionpack (= 7.2.3.1)
activesupport (= 7.2.2.2) activesupport (= 7.2.3.1)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6) zeitwerk (~> 2.6)
actionmailbox (7.2.2.2) actionmailbox (7.2.3.1)
actionpack (= 7.2.2.2) actionpack (= 7.2.3.1)
activejob (= 7.2.2.2) activejob (= 7.2.3.1)
activerecord (= 7.2.2.2) activerecord (= 7.2.3.1)
activestorage (= 7.2.2.2) activestorage (= 7.2.3.1)
activesupport (= 7.2.2.2) activesupport (= 7.2.3.1)
mail (>= 2.8.0) mail (>= 2.8.0)
actionmailer (7.2.2.2) actionmailer (7.2.3.1)
actionpack (= 7.2.2.2) actionpack (= 7.2.3.1)
actionview (= 7.2.2.2) actionview (= 7.2.3.1)
activejob (= 7.2.2.2) activejob (= 7.2.3.1)
activesupport (= 7.2.2.2) activesupport (= 7.2.3.1)
mail (>= 2.8.0) mail (>= 2.8.0)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
actionpack (7.2.2.2) actionpack (7.2.3.1)
actionview (= 7.2.2.2) actionview (= 7.2.3.1)
activesupport (= 7.2.2.2) activesupport (= 7.2.3.1)
cgi
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
racc racc
rack (>= 2.2.4, < 3.2) rack (>= 2.2.4, < 3.3)
rack-session (>= 1.0.1) rack-session (>= 1.0.1)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6) rails-html-sanitizer (~> 1.6)
useragent (~> 0.16) useragent (~> 0.16)
actiontext (7.2.2.2) actiontext (7.2.3.1)
actionpack (= 7.2.2.2) actionpack (= 7.2.3.1)
activerecord (= 7.2.2.2) activerecord (= 7.2.3.1)
activestorage (= 7.2.2.2) activestorage (= 7.2.3.1)
activesupport (= 7.2.2.2) activesupport (= 7.2.3.1)
globalid (>= 0.6.0) globalid (>= 0.6.0)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
actionview (7.2.2.2) actionview (7.2.3.1)
activesupport (= 7.2.2.2) activesupport (= 7.2.3.1)
builder (~> 3.1) builder (~> 3.1)
cgi
erubi (~> 1.11) erubi (~> 1.11)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6) rails-html-sanitizer (~> 1.6)
activejob (7.2.2.2) activejob (7.2.3.1)
activesupport (= 7.2.2.2) activesupport (= 7.2.3.1)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (7.2.2.2) activemodel (7.2.3.1)
activesupport (= 7.2.2.2) activesupport (= 7.2.3.1)
activemodel-serializers-xml (1.0.3) activemodel-serializers-xml (1.0.3)
activemodel (>= 5.0.0.a) activemodel (>= 5.0.0.a)
activesupport (>= 5.0.0.a) activesupport (>= 5.0.0.a)
builder (~> 3.1) builder (~> 3.1)
activerecord (7.2.2.2) activerecord (7.2.3.1)
activemodel (= 7.2.2.2) activemodel (= 7.2.3.1)
activesupport (= 7.2.2.2) activesupport (= 7.2.3.1)
timeout (>= 0.4.0) timeout (>= 0.4.0)
activestorage (7.2.2.2) activestorage (7.2.3.1)
actionpack (= 7.2.2.2) actionpack (= 7.2.3.1)
activejob (= 7.2.2.2) activejob (= 7.2.3.1)
activerecord (= 7.2.2.2) activerecord (= 7.2.3.1)
activesupport (= 7.2.2.2) activesupport (= 7.2.3.1)
marcel (~> 1.0) marcel (~> 1.0)
activesupport (7.2.2.2) activesupport (7.2.3.1)
base64 base64
benchmark (>= 0.3) benchmark (>= 0.3)
bigdecimal bigdecimal
@ -73,7 +75,7 @@ GEM
drb drb
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
logger (>= 1.4.2) logger (>= 1.4.2)
minitest (>= 5.1) minitest (>= 5.1, < 6)
securerandom (>= 0.3) securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5) tzinfo (~> 2.0, >= 2.0.5)
addressable (2.8.6) addressable (2.8.6)
@ -147,6 +149,7 @@ GEM
capybara-screenshot (1.0.26) capybara-screenshot (1.0.26)
capybara (>= 1.0, < 4) capybara (>= 1.0, < 4)
launchy launchy
cgi (0.5.1)
childprocess (5.0.0) childprocess (5.0.0)
coderay (1.1.3) coderay (1.1.3)
coercible (1.0.0) coercible (1.0.0)
@ -283,9 +286,7 @@ GEM
matrix (0.4.2) matrix (0.4.2)
method_source (1.1.0) method_source (1.1.0)
mini_mime (1.1.5) mini_mime (1.1.5)
minitest (6.0.2) minitest (5.27.0)
drb (~> 2.0)
prism (~> 1.5)
msgpack (1.7.2) msgpack (1.7.2)
multipart-post (2.4.1) multipart-post (2.4.1)
nested_form (0.3.2) nested_form (0.3.2)
@ -368,20 +369,20 @@ GEM
rack (>= 1.3) rack (>= 1.3)
rackup (2.3.1) rackup (2.3.1)
rack (>= 3) rack (>= 3)
rails (7.2.2.2) rails (7.2.3.1)
actioncable (= 7.2.2.2) actioncable (= 7.2.3.1)
actionmailbox (= 7.2.2.2) actionmailbox (= 7.2.3.1)
actionmailer (= 7.2.2.2) actionmailer (= 7.2.3.1)
actionpack (= 7.2.2.2) actionpack (= 7.2.3.1)
actiontext (= 7.2.2.2) actiontext (= 7.2.3.1)
actionview (= 7.2.2.2) actionview (= 7.2.3.1)
activejob (= 7.2.2.2) activejob (= 7.2.3.1)
activemodel (= 7.2.2.2) activemodel (= 7.2.3.1)
activerecord (= 7.2.2.2) activerecord (= 7.2.3.1)
activestorage (= 7.2.2.2) activestorage (= 7.2.3.1)
activesupport (= 7.2.2.2) activesupport (= 7.2.3.1)
bundler (>= 1.15.0) bundler (>= 1.15.0)
railties (= 7.2.2.2) railties (= 7.2.3.1)
rails-dom-testing (2.3.0) rails-dom-testing (2.3.0)
activesupport (>= 5.0.0) activesupport (>= 5.0.0)
minitest minitest
@ -396,13 +397,15 @@ GEM
nested_form (~> 0.3) nested_form (~> 0.3)
rails (>= 6.0, < 9) rails (>= 6.0, < 9)
turbo-rails (>= 1.0, < 3) turbo-rails (>= 1.0, < 3)
railties (7.2.2.2) railties (7.2.3.1)
actionpack (= 7.2.2.2) actionpack (= 7.2.3.1)
activesupport (= 7.2.2.2) activesupport (= 7.2.3.1)
cgi
irb (~> 1.13) irb (~> 1.13)
rackup (>= 1.0.0) rackup (>= 1.0.0)
rake (>= 12.2) rake (>= 12.2)
thor (~> 1.0, >= 1.2.2) thor (~> 1.0, >= 1.2.2)
tsort (>= 0.2)
zeitwerk (~> 2.6) zeitwerk (~> 2.6)
rainbow (3.1.1) rainbow (3.1.1)
rake (13.3.1) rake (13.3.1)

3
app/services/bulk_upload/lettings/year2025/row_parser.rb

@ -525,6 +525,9 @@ class BulkUpload::Lettings::Year2025::RowParser
@log ||= LettingsLog.new(attributes_for_log) @log ||= LettingsLog.new(attributes_for_log)
end end
# Will send a "Bulk upload failed" email rather than an "Errors in bulk upload" email.
# The body of the "Bulk upload failed" email says there are errors in the setup section,
# so only use this method for setup section errors.
def block_log_creation! def block_log_creation!
self.block_log_creation = true self.block_log_creation = true
end end

3
app/services/bulk_upload/lettings/year2026/row_parser.rb

@ -560,6 +560,9 @@ class BulkUpload::Lettings::Year2026::RowParser
@log ||= LettingsLog.new(attributes_for_log) @log ||= LettingsLog.new(attributes_for_log)
end end
# Will send a "Bulk upload failed" email rather than an "Errors in bulk upload" email.
# The body of the "Bulk upload failed" email says there are errors in the setup section,
# so only use this method for setup section errors.
def block_log_creation! def block_log_creation!
self.block_log_creation = true self.block_log_creation = true
end end

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

@ -1290,6 +1290,9 @@ private
end end
end end
# Will send a "Bulk upload failed" email rather than an "Errors in bulk upload" email.
# The body of the "Bulk upload failed" email says there are errors in the setup section,
# so only use this method for setup section errors.
def block_log_creation! def block_log_creation!
self.block_log_creation = true self.block_log_creation = true
end end

83
app/services/bulk_upload/sales/year2026/row_parser.rb

@ -152,6 +152,7 @@ class BulkUpload::Sales::Year2026::RowParser
}.freeze }.freeze
ERROR_BASE_KEY = "validations.sales.2026.bulk_upload".freeze ERROR_BASE_KEY = "validations.sales.2026.bulk_upload".freeze
NUMBER_OR_R_FORMAT = /\A(\d+(\.\d+)?|R)\z/i
CASE_INSENSITIVE_FIELDS = [ CASE_INSENSITIVE_FIELDS = [
:field_29, # Age of buyer 1 :field_29, # Age of buyer 1
@ -176,6 +177,11 @@ class BulkUpload::Sales::Year2026::RowParser
:field_103, # What is the length of the mortgage in years? - Shared ownership :field_103, # What is the length of the mortgage in years? - Shared ownership
:field_133, # What is the length of the mortgage in years? - Discounted ownership :field_133, # What is the length of the mortgage in years? - Discounted ownership
:field_107, # What are the total monthly service charges for the property?
:field_125, # What are the monthly service charges for the property?
:field_126, # New monthly service charge amount
:field_136, # What are the total monthly leasehold charges for the property?
].freeze ].freeze
attribute :bulk_upload attribute :bulk_upload
@ -296,7 +302,7 @@ class BulkUpload::Sales::Year2026::RowParser
attribute :field_104, :decimal attribute :field_104, :decimal
attribute :field_105, :decimal attribute :field_105, :decimal
attribute :field_106, :decimal attribute :field_106, :decimal
attribute :field_107, :decimal attribute :field_107, :string
attribute :field_108, :decimal attribute :field_108, :decimal
attribute :field_109, :decimal attribute :field_109, :decimal
@ -315,8 +321,8 @@ class BulkUpload::Sales::Year2026::RowParser
attribute :field_122, :integer attribute :field_122, :integer
attribute :field_123, :decimal attribute :field_123, :decimal
attribute :field_124, :decimal attribute :field_124, :decimal
attribute :field_125, :integer attribute :field_125, :string
attribute :field_126, :decimal attribute :field_126, :string
attribute :field_127, :integer attribute :field_127, :integer
attribute :field_128, :decimal attribute :field_128, :decimal
@ -327,7 +333,7 @@ class BulkUpload::Sales::Year2026::RowParser
attribute :field_133, :string attribute :field_133, :string
attribute :field_134, :integer attribute :field_134, :integer
attribute :field_135, :decimal attribute :field_135, :decimal
attribute :field_136, :decimal attribute :field_136, :string
validates :field_1, validates :field_1,
presence: { presence: {
@ -496,6 +502,8 @@ class BulkUpload::Sales::Year2026::RowParser
validate :validate_buyer_2_nationality, on: :after_log validate :validate_buyer_2_nationality, on: :after_log
validate :validate_mortlen_field_if_buyer_interviewed, on: :after_log validate :validate_mortlen_field_if_buyer_interviewed, on: :after_log
validate :validate_service_charge_fields, on: :after_log
validate :validate_nulls, on: :after_log validate :validate_nulls, on: :after_log
def self.question_for_field(field) def self.question_for_field(field)
@ -899,7 +907,7 @@ private
gender_same_as_sex6: %i[field_68], gender_same_as_sex6: %i[field_68],
gender_description6: %i[field_69], gender_description6: %i[field_69],
hasservicechargeschanged: %i[field_125], hasservicechargeschanged: %i[field_126],
newservicecharges: %i[field_126], newservicecharges: %i[field_126],
} }
end end
@ -950,9 +958,6 @@ private
attributes["gender_same_as_sex6"] = field_68 attributes["gender_same_as_sex6"] = field_68
attributes["gender_description6"] = field_69 attributes["gender_description6"] = field_69
attributes["hasservicechargeschanged"] = field_125
attributes["newservicecharges"] = field_126
attributes["relat2"] = relationship_from_is_partner(field_37) attributes["relat2"] = relationship_from_is_partner(field_37)
attributes["relat3"] = relationship_from_is_partner(field_47) attributes["relat3"] = relationship_from_is_partner(field_47)
attributes["relat4"] = relationship_from_is_partner(field_53) attributes["relat4"] = relationship_from_is_partner(field_53)
@ -1026,8 +1031,6 @@ private
attributes["cashdis"] = field_105 attributes["cashdis"] = field_105
attributes["mrent"] = mrent attributes["mrent"] = mrent
attributes["mscharge"] = mscharge if mscharge&.positive?
attributes["has_mscharge"] = attributes["mscharge"].present? ? 1 : 0
attributes["grant"] = field_129 attributes["grant"] = field_129
attributes["discount"] = field_130 attributes["discount"] = field_130
@ -1097,6 +1100,11 @@ private
attributes["management_fee"] = field_108 attributes["management_fee"] = field_108
attributes["has_management_fee"] = field_108.present? && field_108.positive? ? 1 : 0 attributes["has_management_fee"] = field_108.present? && field_108.positive? ? 1 : 0
attributes["has_mscharge"] = has_mscharge_value
attributes["mscharge"] = mscharge_value
attributes["hasservicechargeschanged"] = hasservicechargeschanged_value
attributes["newservicecharges"] = newservicecharges_value
attributes attributes
end end
@ -1256,11 +1264,36 @@ private
end end
def mscharge def mscharge
return field_107 if shared_ownership? return field_107 if shared_ownership_initial_purchase?
return field_125 if staircasing?
field_136 if discounted_ownership? field_136 if discounted_ownership?
end end
def has_mscharge_value
return unless mscharge.present? && mscharge.match?(NUMBER_OR_R_FORMAT)
mscharge.casecmp?("R") ? 0 : 1
end
def mscharge_value
return unless mscharge.present? && mscharge.match?(NUMBER_OR_R_FORMAT) && !mscharge.casecmp?("R")
mscharge.to_d
end
def hasservicechargeschanged_value
return unless field_126.present? && field_126.match?(NUMBER_OR_R_FORMAT)
field_126.casecmp?("R") ? 2 : 1
end
def newservicecharges_value
return unless field_126.present? && field_126.match?(NUMBER_OR_R_FORMAT) && !field_126.casecmp?("R")
field_126.to_d
end
def mortlen def mortlen
return field_103 if shared_ownership? return field_103 if shared_ownership?
@ -1333,10 +1366,11 @@ private
end end
def mscharge_fields def mscharge_fields
return [:field_107] if shared_ownership? return [:field_107] if shared_ownership_initial_purchase?
return [:field_125] if staircasing?
return [:field_136] if discounted_ownership? return [:field_136] if discounted_ownership?
%i[field_107 field_136] %i[field_107 field_125 field_136]
end end
def mortlen_fields def mortlen_fields
@ -1389,6 +1423,29 @@ private
end end
end end
def validate_service_charge_fields
message = I18n.t("#{ERROR_BASE_KEY}.mscharge.invalid")
if shared_ownership_initial_purchase? && field_107.present? && !field_107.match?(NUMBER_OR_R_FORMAT)
errors.add(:field_107, message)
end
if staircasing?
if field_125.present? && !field_125.match?(NUMBER_OR_R_FORMAT)
errors.add(:field_125, message)
end
if field_126.present? && !field_126.match?(NUMBER_OR_R_FORMAT)
errors.add(:field_126, I18n.t("#{ERROR_BASE_KEY}.newservicecharges.invalid"))
end
end
if discounted_ownership? && field_136.present? && !field_136.match?(NUMBER_OR_R_FORMAT)
errors.add(:field_136, message)
end
end
# Will send a "Bulk upload failed" email rather than an "Errors in bulk upload" email
def block_log_creation! def block_log_creation!
self.block_log_creation = true self.block_log_creation = true
end end

4
config/locales/validations/sales/2026/bulk_upload.en.yml

@ -44,6 +44,10 @@ en:
not_answered: "Enter either the UPRN or the full address." not_answered: "Enter either the UPRN or the full address."
nationality: nationality:
invalid: "Select a valid nationality." invalid: "Select a valid nationality."
mscharge:
invalid: "Service charge must be a positive number or the letter R."
newservicecharges:
invalid: "New service charge must be a number or the letter R."
mortlen: mortlen:
invalid: "Mortgage length must be a number or the letter R" invalid: "Mortgage length must be a number or the letter R"
invalid_for_interviewed: "You indicated that you interviewed the buyer(s), but selected “Don’t know” for mortgage length. Please provide the mortgage length or update your response." invalid_for_interviewed: "You indicated that you interviewed the buyer(s), but selected “Don’t know” for mortgage length. Please provide the mortgage length or update your response."

27
docs/Gemfile.lock

@ -1,22 +1,34 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
activesupport (7.0.7.2) activesupport (7.2.3.1)
concurrent-ruby (~> 1.0, >= 1.0.2) base64
benchmark (>= 0.3)
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
minitest (>= 5.1) logger (>= 1.4.2)
tzinfo (~> 2.0) minitest (>= 5.1, < 6)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
addressable (2.8.1) addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0) public_suffix (>= 2.0.2, < 6.0)
base64 (0.3.0)
benchmark (0.5.0)
bigdecimal (4.0.1)
coffee-script (2.4.1) coffee-script (2.4.1)
coffee-script-source coffee-script-source
execjs execjs
coffee-script-source (1.11.1) coffee-script-source (1.11.1)
colorator (1.1.0) colorator (1.1.0)
commonmarker (0.23.10) commonmarker (0.23.10)
concurrent-ruby (1.2.2) concurrent-ruby (1.3.6)
connection_pool (3.0.2)
dnsruby (1.61.9) dnsruby (1.61.9)
simpleidn (~> 0.1) simpleidn (~> 0.1)
drb (2.2.3)
em-websocket (0.5.3) em-websocket (0.5.3)
eventmachine (>= 0.12.9) eventmachine (>= 0.12.9)
http_parser.rb (~> 0) http_parser.rb (~> 0)
@ -88,7 +100,7 @@ GEM
activesupport (>= 2) activesupport (>= 2)
nokogiri (>= 1.4) nokogiri (>= 1.4)
http_parser.rb (0.8.0) http_parser.rb (0.8.0)
i18n (1.14.1) i18n (1.14.8)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
jekyll (3.9.3) jekyll (3.9.3)
addressable (~> 2.4) addressable (~> 2.4)
@ -213,7 +225,7 @@ GEM
jekyll (>= 3.5, < 5.0) jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9) jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1) jekyll-seo-tag (~> 2.1)
minitest (5.19.0) minitest (5.27.0)
net-http (0.9.1) net-http (0.9.1)
uri (>= 0.11.1) uri (>= 0.11.1)
nokogiri (1.19.1-arm64-darwin) nokogiri (1.19.1-arm64-darwin)
@ -244,6 +256,7 @@ GEM
sawyer (0.9.2) sawyer (0.9.2)
addressable (>= 2.3.5) addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3) faraday (>= 0.17.3, < 3)
securerandom (0.4.1)
simpleidn (0.2.1) simpleidn (0.2.1)
unf (~> 0.1.4) unf (~> 0.1.4)
terminal-table (1.8.0) terminal-table (1.8.0)

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

@ -1334,7 +1334,7 @@ RSpec.describe BulkUpload::Sales::Year2023::RowParser do
context "when mscharge is given, but is set to 0 for shared ownership" do context "when mscharge is given, but is set to 0 for shared ownership" do
let(:attributes) { valid_attributes.merge(field_114: "0") } let(:attributes) { valid_attributes.merge(field_114: "0") }
it "does not override variables correctly" do it "does not override variables" do
log = parser.log log = parser.log
expect(log["has_mscharge"]).to eq(0) # no expect(log["has_mscharge"]).to eq(0) # no
expect(log["mscharge"]).to be_nil expect(log["mscharge"]).to be_nil
@ -1344,7 +1344,7 @@ RSpec.describe BulkUpload::Sales::Year2023::RowParser do
context "when mscharge is given, but is set to 0 for discounted ownership" do context "when mscharge is given, but is set to 0 for discounted ownership" do
let(:attributes) { valid_attributes.merge(field_7: "2", field_126: "0") } let(:attributes) { valid_attributes.merge(field_7: "2", field_126: "0") }
it "does not override variables correctly" do it "does not override variables" do
log = parser.log log = parser.log
expect(log["has_mscharge"]).to eq(0) # no expect(log["has_mscharge"]).to eq(0) # no
expect(log["mscharge"]).to be_nil expect(log["mscharge"]).to be_nil
@ -1354,7 +1354,7 @@ RSpec.describe BulkUpload::Sales::Year2023::RowParser do
context "when mscharge is given, but is set to 0 for outright sale" do context "when mscharge is given, but is set to 0 for outright sale" do
let(:attributes) { valid_attributes.merge(field_7: "3", field_135: "0") } let(:attributes) { valid_attributes.merge(field_7: "3", field_135: "0") }
it "does not override variables correctly" do it "does not override variables" do
log = parser.log log = parser.log
expect(log["has_mscharge"]).to eq(0) # no expect(log["has_mscharge"]).to eq(0) # no
expect(log["mscharge"]).to be_nil expect(log["mscharge"]).to be_nil

6
spec/services/bulk_upload/sales/year2024/row_parser_spec.rb

@ -1973,7 +1973,7 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do
context "when mscharge is given, but is set to 0 for shared ownership" do context "when mscharge is given, but is set to 0 for shared ownership" do
let(:attributes) { valid_attributes.merge(field_112: "0") } let(:attributes) { valid_attributes.merge(field_112: "0") }
it "does not override variables correctly" do it "does not override variables" do
log = parser.log log = parser.log
expect(log["has_mscharge"]).to eq(0) # no expect(log["has_mscharge"]).to eq(0) # no
expect(log["mscharge"]).to be_nil expect(log["mscharge"]).to be_nil
@ -1983,7 +1983,7 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do
context "when mscharge is given, but is set to 0 for discounted ownership" do 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_124: "0") }
it "does not override variables correctly" do it "does not override variables" do
log = parser.log log = parser.log
expect(log["has_mscharge"]).to eq(0) # no expect(log["has_mscharge"]).to eq(0) # no
expect(log["mscharge"]).to be_nil expect(log["mscharge"]).to be_nil
@ -1993,7 +1993,7 @@ RSpec.describe BulkUpload::Sales::Year2024::RowParser do
context "when mscharge is given, but is set to 0 for outright sale" do context "when mscharge is given, but is set to 0 for outright sale" do
let(:attributes) { valid_attributes.merge(field_8: "3", field_131: "0") } let(:attributes) { valid_attributes.merge(field_8: "3", field_131: "0") }
it "does not override variables correctly" do it "does not override variables" do
log = parser.log log = parser.log
expect(log["has_mscharge"]).to eq(0) # no expect(log["has_mscharge"]).to eq(0) # no
expect(log["mscharge"]).to be_nil expect(log["mscharge"]).to be_nil

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

@ -1889,7 +1889,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
context "when mscharge is given, but is set to 0 for shared ownership" do context "when mscharge is given, but is set to 0 for shared ownership" do
let(:attributes) { valid_attributes.merge(field_94: "0") } let(:attributes) { valid_attributes.merge(field_94: "0") }
it "does not override variables correctly" do it "does not override variables" do
log = parser.log log = parser.log
expect(log["has_mscharge"]).to eq(0) # no expect(log["has_mscharge"]).to eq(0) # no
expect(log["mscharge"]).to be_nil expect(log["mscharge"]).to be_nil
@ -1899,7 +1899,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
context "when mscharge is given, but is set to 0 for discounted ownership" do context "when mscharge is given, but is set to 0 for discounted ownership" do
let(:attributes) { valid_attributes.merge(field_8: "2", field_121: "0") } let(:attributes) { valid_attributes.merge(field_8: "2", field_121: "0") }
it "does not override variables correctly" do it "does not override variables" do
log = parser.log log = parser.log
expect(log["has_mscharge"]).to eq(0) # no expect(log["has_mscharge"]).to eq(0) # no
expect(log["mscharge"]).to be_nil expect(log["mscharge"]).to be_nil

550
spec/services/bulk_upload/sales/year2026/row_parser_spec.rb

@ -116,7 +116,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do
field_31: "1", field_31: "1",
field_40: "2", field_40: "2",
field_41: "Non-binary", field_41: "Non-binary",
field_125: "1", field_125: "200",
field_126: "150", field_126: "150",
} }
end end
@ -301,7 +301,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do
context "and case insensitive fields are set to lowercase" do context "and case insensitive fields are set to lowercase" do
let(:case_insensitive_fields) { %w[field_30 field_39 field_49 field_55 field_61 field_67] } let(:case_insensitive_fields) { %w[field_30 field_39 field_49 field_55 field_61 field_67] }
let(:case_insensitive_integer_fields_with_r_option) { %w[field_29 field_38 field_48 field_54 field_60 field_66 field_77 field_88 field_83 field_85 field_103 field_133] } let(:case_insensitive_integer_fields_with_r_option) { %w[field_29 field_38 field_48 field_54 field_60 field_66 field_77 field_88 field_83 field_85 field_103 field_107 field_125 field_126 field_133 field_136] }
let(:attributes) do let(:attributes) do
valid_attributes valid_attributes
.merge(case_insensitive_fields.each_with_object({}) { |field, h| h[field.to_sym] = valid_attributes[field.to_sym]&.downcase }) .merge(case_insensitive_fields.each_with_object({}) { |field, h| h[field.to_sym] = valid_attributes[field.to_sym]&.downcase })
@ -1948,25 +1948,557 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do
end end
end end
context "when mscharge is given, but is set to 0 for shared ownership" do context "with service charges fields" do
let(:attributes) { valid_attributes.merge(field_107: "0") } context "with mscharge for shared ownership initial purchase (field_107)" do
context "when positive" do
let(:attributes) { valid_attributes.merge(field_10: "2", field_107: "100") }
it "does not override variables correctly" do it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_107]).to be_blank
end
it "sets has_mscharge to yes and mscharge to the value" do
log = parser.log
expect(log["has_mscharge"]).to eq(1)
expect(log["mscharge"]).to eq(100)
end
end
context "when set to 1" do
let(:attributes) { valid_attributes.merge(field_10: "2", field_107: "1") }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_107]).to be_blank
end
it "sets has_mscharge to yes and mscharge to the value" do
log = parser.log
expect(log["has_mscharge"]).to eq(1)
expect(log["mscharge"]).to eq(1)
end
end
context "when set to 0" do
let(:attributes) { valid_attributes.merge(field_10: "2", field_107: "0") }
it "does not add a bulk upload format validation error but adds a site validation error" do
parser.valid?
expect(parser.errors[:field_107]).not_to include(I18n.t("validations.sales.2026.bulk_upload.mscharge.invalid"))
expect(parser.errors[:field_107]).to include(I18n.t("validations.sales.financial.mscharge.monthly_leasehold_charges.not_zero"))
end
it "sets has_mscharge to yes and mscharge to the value" do
log = parser.log
expect(log["has_mscharge"]).to eq(1)
expect(log["mscharge"]).to eq(0)
end
end
context "when set to 0.0" do
let(:attributes) { valid_attributes.merge(field_10: "2", field_107: "0.0") }
it "does not add a bulk upload format validation error but adds a site validation error" do
parser.valid?
expect(parser.errors[:field_107]).not_to include(I18n.t("validations.sales.2026.bulk_upload.mscharge.invalid"))
expect(parser.errors[:field_107]).to include(I18n.t("validations.sales.financial.mscharge.monthly_leasehold_charges.not_zero"))
end
it "sets has_mscharge to yes and mscharge to the value" do
log = parser.log
expect(log["has_mscharge"]).to eq(1)
expect(log["mscharge"]).to eq(0)
end
end
context "when set to R" do
let(:attributes) { valid_attributes.merge(field_10: "2", field_107: "R") }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_107]).to be_blank
end
it "sets has_mscharge to no and does not set mscharge" do
log = parser.log
expect(log["has_mscharge"]).to eq(0)
expect(log["mscharge"]).to be_nil
end
end
context "when set to lowercase r" do
let(:attributes) { valid_attributes.merge(field_10: "2", field_107: "r") }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_107]).to be_blank
end
it "sets has_mscharge to no and does not set mscharge" do
log = parser.log
expect(log["has_mscharge"]).to eq(0)
expect(log["mscharge"]).to be_nil
end
end
context "when an invalid string" do
let(:attributes) { valid_attributes.merge(field_10: "2", field_107: "X") }
it "adds a validation error" do
parser.valid?
expect(parser.errors[:field_107]).to include(I18n.t("validations.sales.2026.bulk_upload.mscharge.invalid"))
end
it "does not set has_mscharge or mscharge" do
log = parser.log
expect(log["has_mscharge"]).to be_nil
expect(log["mscharge"]).to be_nil
end
end
context "when blank" do
let(:attributes) { valid_attributes.merge(field_10: "2", field_107: nil) }
it "does not add a bulk upload format validation error but adds a site validation error" do
parser.valid?
expect(parser.errors[:field_107]).not_to include(I18n.t("validations.sales.2026.bulk_upload.mscharge.invalid"))
expect(parser.errors[:field_107]).to include("You must answer property service charges.")
end
it "does not set has_mscharge or mscharge" do
log = parser.log log = parser.log
expect(log["has_mscharge"]).to eq(0) # no expect(log["has_mscharge"]).to be_nil
expect(log["mscharge"]).to be_nil expect(log["mscharge"]).to be_nil
end end
end end
context "when mscharge is given, but is set to 0 for discounted ownership" do context "when negative" do
let(:attributes) { valid_attributes.merge(field_10: "2", field_107: "-100") }
it "adds a validation error" do
parser.valid?
expect(parser.errors[:field_107]).to include(I18n.t("validations.sales.2026.bulk_upload.mscharge.invalid"))
end
it "does not set has_mscharge or mscharge" do
log = parser.log
expect(log["has_mscharge"]).to be_nil
expect(log["mscharge"]).to be_nil
end
end
end
context "with mscharge for staircasing (field_125)" do
context "when positive" do
let(:attributes) { valid_attributes.merge(field_125: "100") }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_125]).to be_blank
end
it "sets has_mscharge to yes and mscharge to the value" do
log = parser.log
expect(log["has_mscharge"]).to eq(1)
expect(log["mscharge"]).to eq(100)
end
end
context "when set to 1" do
let(:attributes) { valid_attributes.merge(field_125: "1") }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_125]).to be_blank
end
it "sets has_mscharge to yes and mscharge to the value" do
log = parser.log
expect(log["has_mscharge"]).to eq(1)
expect(log["mscharge"]).to eq(1)
end
end
context "when set to 0" do
let(:attributes) { valid_attributes.merge(field_125: "0") }
it "does not add a bulk upload format validation error but adds a site validation error" do
parser.valid?
expect(parser.errors[:field_125]).not_to include(I18n.t("validations.sales.2026.bulk_upload.mscharge.invalid"))
expect(parser.errors[:field_125]).to include(I18n.t("validations.sales.financial.mscharge.monthly_leasehold_charges.not_zero"))
end
it "sets has_mscharge to yes and mscharge to the value" do
log = parser.log
expect(log["has_mscharge"]).to eq(1)
expect(log["mscharge"]).to eq(0)
end
end
context "when set to 0.0" do
let(:attributes) { valid_attributes.merge(field_125: "0.0") }
it "does not add a bulk upload format validation error but adds a site validation error" do
parser.valid?
expect(parser.errors[:field_125]).not_to include(I18n.t("validations.sales.2026.bulk_upload.mscharge.invalid"))
expect(parser.errors[:field_125]).to include(I18n.t("validations.sales.financial.mscharge.monthly_leasehold_charges.not_zero"))
end
it "sets has_mscharge to yes and mscharge to the value" do
log = parser.log
expect(log["has_mscharge"]).to eq(1)
expect(log["mscharge"]).to eq(0)
end
end
context "when set to R" do
let(:attributes) { valid_attributes.merge(field_125: "R") }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_125]).to be_blank
end
it "sets has_mscharge to no and does not set mscharge" do
log = parser.log
expect(log["has_mscharge"]).to eq(0)
expect(log["mscharge"]).to be_nil
end
end
context "when set to lowercase r" do
let(:attributes) { valid_attributes.merge(field_125: "r") }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_125]).to be_blank
end
it "sets has_mscharge to no and does not set mscharge" do
log = parser.log
expect(log["has_mscharge"]).to eq(0)
expect(log["mscharge"]).to be_nil
end
end
context "when an invalid string" do
let(:attributes) { valid_attributes.merge(field_125: "X") }
it "adds a validation error" do
parser.valid?
expect(parser.errors[:field_125]).to include(I18n.t("validations.sales.2026.bulk_upload.mscharge.invalid"))
end
it "does not set has_mscharge or mscharge" do
log = parser.log
expect(log["has_mscharge"]).to be_nil
expect(log["mscharge"]).to be_nil
end
end
context "when blank" do
let(:attributes) { valid_attributes.merge(field_125: nil) }
it "does not add a bulk upload format validation error but adds a site validation error" do
parser.valid?
expect(parser.errors[:field_125]).not_to include(I18n.t("validations.sales.2026.bulk_upload.mscharge.invalid"))
expect(parser.errors[:field_125]).to include("You must answer property service charges.")
end
it "does not set has_mscharge or mscharge" do
log = parser.log
expect(log["has_mscharge"]).to be_nil
expect(log["mscharge"]).to be_nil
end
end
context "when negative" do
let(:attributes) { valid_attributes.merge(field_125: "-100") }
it "adds a validation error" do
parser.valid?
expect(parser.errors[:field_125]).to include(I18n.t("validations.sales.2026.bulk_upload.mscharge.invalid"))
end
it "does not set has_mscharge or mscharge" do
log = parser.log
expect(log["has_mscharge"]).to be_nil
expect(log["mscharge"]).to be_nil
end
end
end
context "with mscharge for discounted ownership (field_136)" do
context "when positive" do
let(:attributes) { valid_attributes.merge(field_8: "2", field_136: "100") }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_136]).to be_blank
end
it "sets has_mscharge to yes and mscharge to the value" do
log = parser.log
expect(log["has_mscharge"]).to eq(1)
expect(log["mscharge"]).to eq(100)
end
end
context "when set to 1" do
let(:attributes) { valid_attributes.merge(field_8: "2", field_136: "1") }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_136]).to be_blank
end
it "sets has_mscharge to yes and mscharge to the value" do
log = parser.log
expect(log["has_mscharge"]).to eq(1)
expect(log["mscharge"]).to eq(1)
end
end
context "when set to 0" do
let(:attributes) { valid_attributes.merge(field_8: "2", field_136: "0") } let(:attributes) { valid_attributes.merge(field_8: "2", field_136: "0") }
it "does not override variables correctly" do it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_136]).to be_blank
end
it "sets has_mscharge to yes and mscharge to the value" do
log = parser.log
expect(log["has_mscharge"]).to eq(1)
expect(log["mscharge"]).to eq(0)
end
end
context "when set to 0.0" do
let(:attributes) { valid_attributes.merge(field_8: "2", field_136: "0.0") }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_136]).to be_blank
end
it "sets has_mscharge to yes and mscharge to the value" do
log = parser.log
expect(log["has_mscharge"]).to eq(1)
expect(log["mscharge"]).to eq(0)
end
end
context "when set to R" do
let(:attributes) { valid_attributes.merge(field_8: "2", field_136: "R") }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_136]).to be_blank
end
it "sets has_mscharge to no and does not set mscharge" do
log = parser.log
expect(log["has_mscharge"]).to eq(0)
expect(log["mscharge"]).to be_nil
end
end
context "when set to lowercase r" do
let(:attributes) { valid_attributes.merge(field_8: "2", field_136: "r") }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_136]).to be_blank
end
it "sets has_mscharge to no and does not set mscharge" do
log = parser.log
expect(log["has_mscharge"]).to eq(0)
expect(log["mscharge"]).to be_nil
end
end
context "when an invalid string" do
let(:attributes) { valid_attributes.merge(field_8: "2", field_136: "X") }
it "adds a validation error" do
parser.valid?
expect(parser.errors[:field_136]).to include(I18n.t("validations.sales.2026.bulk_upload.mscharge.invalid"))
end
it "does not set has_mscharge or mscharge" do
log = parser.log
expect(log["has_mscharge"]).to be_nil
expect(log["mscharge"]).to be_nil
end
end
context "when blank" do
let(:attributes) { valid_attributes.merge(field_8: "2", field_136: nil) }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_136]).to be_blank
end
it "does not set has_mscharge or mscharge" do
log = parser.log
expect(log["has_mscharge"]).to be_nil
expect(log["mscharge"]).to be_nil
end
end
context "when negative" do
let(:attributes) { valid_attributes.merge(field_8: "2", field_136: "-100") }
it "adds a validation error" do
parser.valid?
expect(parser.errors[:field_136]).to include(I18n.t("validations.sales.2026.bulk_upload.mscharge.invalid"))
end
it "does not set has_mscharge or mscharge" do
log = parser.log log = parser.log
expect(log["has_mscharge"]).to eq(0) # no expect(log["has_mscharge"]).to be_nil
expect(log["mscharge"]).to be_nil expect(log["mscharge"]).to be_nil
end end
end end
end
context "with newservicecharges (field_126)" do
context "when positive" do
let(:attributes) { valid_attributes.merge(field_126: "150") }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_126]).to be_blank
end
it "sets hasservicechargeschanged to yes and newservicecharges to the value" do
log = parser.log
expect(log["hasservicechargeschanged"]).to eq(1)
expect(log["newservicecharges"]).to eq(150)
end
end
context "when set to 0" do
let(:attributes) { valid_attributes.merge(field_126: "0") }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_126]).to be_blank
end
it "sets hasservicechargeschanged to yes and newservicecharges to 0" do
log = parser.log
expect(log["hasservicechargeschanged"]).to eq(1)
expect(log["newservicecharges"]).to eq(0)
end
end
context "when set to 0.0" do
let(:attributes) { valid_attributes.merge(field_126: "0.0") }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_126]).to be_blank
end
it "sets hasservicechargeschanged to yes and newservicecharges to 0" do
log = parser.log
expect(log["hasservicechargeschanged"]).to eq(1)
expect(log["newservicecharges"]).to eq(0)
end
end
context "when set to R" do
let(:attributes) { valid_attributes.merge(field_126: "R") }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_126]).to be_blank
end
it "sets hasservicechargeschanged to no and does not set newservicecharges" do
log = parser.log
expect(log["hasservicechargeschanged"]).to eq(2)
expect(log["newservicecharges"]).to be_nil
end
end
context "when set to lowercase r" do
let(:attributes) { valid_attributes.merge(field_126: "r") }
it "does not add a validation error" do
parser.valid?
expect(parser.errors[:field_126]).to be_blank
end
it "sets hasservicechargeschanged to no and does not set newservicecharges" do
log = parser.log
expect(log["hasservicechargeschanged"]).to eq(2)
expect(log["newservicecharges"]).to be_nil
end
end
context "when an invalid string" do
let(:attributes) { valid_attributes.merge(field_126: "X") }
it "adds a validation error" do
parser.valid?
expect(parser.errors[:field_126]).to include(I18n.t("validations.sales.2026.bulk_upload.newservicecharges.invalid"))
end
it "does not set hasservicechargeschanged or newservicecharges" do
log = parser.log
expect(log["hasservicechargeschanged"]).to be_nil
expect(log["newservicecharges"]).to be_nil
end
end
context "when blank" do
let(:attributes) { valid_attributes.merge(field_126: nil) }
it "does not add a bulk upload format validation error but adds a site validation error" do
parser.valid?
expect(parser.errors[:field_126]).not_to include(I18n.t("validations.sales.2026.bulk_upload.newservicecharges.invalid"))
expect(parser.errors[:field_126]).to include("You must answer service charge will change.")
end
it "does not set hasservicechargeschanged or newservicecharges" do
log = parser.log
expect(log["hasservicechargeschanged"]).to be_nil
expect(log["newservicecharges"]).to be_nil
end
end
context "when negative" do
let(:attributes) { valid_attributes.merge(field_126: "-150") }
it "adds a validation error" do
parser.valid?
expect(parser.errors[:field_126]).to include(I18n.t("validations.sales.2026.bulk_upload.newservicecharges.invalid"))
end
it "does not set hasservicechargeschanged or newservicecharges" do
log = parser.log
expect(log["hasservicechargeschanged"]).to be_nil
expect(log["newservicecharges"]).to be_nil
end
end
end
context "when newservicecharges equals mscharge (field_125 == field_126)" do
let(:attributes) { valid_attributes.merge(field_125: "200", field_126: "200") }
it "adds validation errors to both fields" do
parser.valid?
expect(parser.errors[:field_125]).to include(I18n.t("validations.sales.financial.mscharge.same_as_new"))
expect(parser.errors[:field_126]).to include(I18n.t("validations.sales.financial.newservicecharges.same_as_previous"))
end
end
end
describe "shared ownership sale type" do describe "shared ownership sale type" do
context "when 32 is selected for shared ownership type" do context "when 32 is selected for shared ownership type" do

Loading…
Cancel
Save