From f07867ecd778e88262d57778559b4a3e39676169 Mon Sep 17 00:00:00 2001 From: Nat Dean-Lewis Date: Wed, 18 Mar 2026 10:16:34 +0000 Subject: [PATCH] CLDC-4297: updated error handling --- .../bulk_upload/sales/year2026/row_parser.rb | 39 ++++++++++++++--- .../validations/sales/2026/bulk_upload.en.yml | 4 ++ .../sales/year2026/row_parser_spec.rb | 42 ++++++++++++++++++- 3 files changed, 79 insertions(+), 6 deletions(-) diff --git a/app/services/bulk_upload/sales/year2026/row_parser.rb b/app/services/bulk_upload/sales/year2026/row_parser.rb index b67d121a8..96d80eecb 100644 --- a/app/services/bulk_upload/sales/year2026/row_parser.rb +++ b/app/services/bulk_upload/sales/year2026/row_parser.rb @@ -477,6 +477,8 @@ class BulkUpload::Sales::Year2026::RowParser }, on: :after_log + validate :validate_service_charge_fields, on: :before_log + validate :validate_buyer1_economic_status, on: :before_log validate :validate_buyer2_economic_status, on: :before_log validate :validate_valid_radio_option, on: :before_log @@ -954,7 +956,7 @@ private attributes["gender_description6"] = field_69 attributes["newservicecharges"] = field_126.to_d if field_126.present? && field_126 != "R" && field_126.to_d.positive? - attributes["hasservicechargeschanged"] = attributes["newservicecharges"].present? ? 1 : 2 + attributes["hasservicechargeschanged"] = attributes["newservicecharges"].present? ? 1 : 2 if field_126.present? attributes["relat2"] = relationship_from_is_partner(field_37) attributes["relat3"] = relationship_from_is_partner(field_47) @@ -1030,7 +1032,7 @@ private attributes["cashdis"] = field_105 attributes["mrent"] = mrent attributes["mscharge"] = mscharge.to_d if mscharge.present? && mscharge != "R" && mscharge.to_d.positive? - attributes["has_mscharge"] = attributes["mscharge"].present? ? 1 : 0 + attributes["has_mscharge"] = attributes["mscharge"].present? ? 1 : 0 if mscharge.present? attributes["grant"] = field_129 attributes["discount"] = field_130 @@ -1337,11 +1339,11 @@ private end def mscharge_fields - return [:field_107] if shared_ownership? - return [:field_136] if discounted_ownership? + return [:field_107] if shared_ownership_initial_purchase? return [:field_125] if staircasing? + return [:field_136] if discounted_ownership? - %i[field_107 field_136 field_125] + %i[field_107 field_125 field_136] end def mortlen_fields @@ -1394,6 +1396,33 @@ private end end + def validate_service_charge_fields + service_charge_format = /\A(\d+(\.\d+)?|R)\z/ + message = I18n.t("#{ERROR_BASE_KEY}.mscharge.invalid") + + if shared_ownership_initial_purchase? && field_107.present? && !field_107.match?(service_charge_format) + block_log_creation! + errors.add(:field_107, message) + end + + if staircasing? + if field_125.present? && !field_125.match?(service_charge_format) + block_log_creation! + errors.add(:field_125, message) + end + + if field_126.present? && !field_126.match?(service_charge_format) + block_log_creation! + errors.add(:field_126, I18n.t("#{ERROR_BASE_KEY}.newservicecharges.invalid")) + end + end + + if discounted_ownership? && field_136.present? && !field_136.match?(service_charge_format) + block_log_creation! + errors.add(:field_136, message) + end + end + def block_log_creation! self.block_log_creation = true end diff --git a/config/locales/validations/sales/2026/bulk_upload.en.yml b/config/locales/validations/sales/2026/bulk_upload.en.yml index a45ade4ea..7ba1ad646 100644 --- a/config/locales/validations/sales/2026/bulk_upload.en.yml +++ b/config/locales/validations/sales/2026/bulk_upload.en.yml @@ -44,6 +44,10 @@ en: not_answered: "Enter either the UPRN or the full address." nationality: invalid: "Select a valid nationality." + mscharge: + invalid: "Service charge must be a number or the letter R." + newservicecharges: + invalid: "New service charge must be a number or the letter R." mortlen: invalid: "Mortgage length must be a number or the letter R" invalid_for_interviewed: "You indicated that the buyer was interviewed, but selected “Don’t know” for mortgage length. Please provide the mortgage length or update your response." diff --git a/spec/services/bulk_upload/sales/year2026/row_parser_spec.rb b/spec/services/bulk_upload/sales/year2026/row_parser_spec.rb index 661e0eae3..600d2bfb5 100644 --- a/spec/services/bulk_upload/sales/year2026/row_parser_spec.rb +++ b/spec/services/bulk_upload/sales/year2026/row_parser_spec.rb @@ -2007,13 +2007,53 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do context "when newservicecharges is blank" do let(:attributes) { valid_attributes.merge(field_126: nil) } - it "does not set newservicecharges and sets hasservicechargeschanged to no" do + it "does not set newservicecharges and leaves hasservicechargeschanged nil" do + log = parser.log + expect(log["newservicecharges"]).to be_nil + expect(log["hasservicechargeschanged"]).to be_nil + end + end + + context "when newservicecharges is an invalid string" do + let(:attributes) { valid_attributes.merge(field_126: "S") } + + 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 "sets hasservicechargeschanged to no" do log = parser.log expect(log["newservicecharges"]).to be_nil expect(log["hasservicechargeschanged"]).to eq(2) end end + context "when mscharge is an invalid string for staircasing" 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 "sets has_mscharge to no" do + log = parser.log + expect(log["mscharge"]).to be_nil + expect(log["has_mscharge"]).to eq(0) + end + end + + context "when mscharge is blank for staircasing" do + let(:attributes) { valid_attributes.merge(field_125: nil) } + + it "leaves has_mscharge nil" do + log = parser.log + expect(log["mscharge"]).to be_nil + expect(log["has_mscharge"]).to be_nil + end + end + describe "shared ownership sale type" do context "when 32 is selected for shared ownership type" do let(:attributes) { valid_attributes.merge(field_9: "32") }