Browse Source

Extract only shared validations

pull/2744/head
Manny Dinssa 8 months ago
parent
commit
77b66201cc
  1. 2
      app/models/user.rb
  2. 24
      app/models/validations/shared_validations.rb
  3. 2
      app/services/bulk_upload/sales/year2024/row_parser.rb
  4. 12
      config/locales/en.yml
  5. 27
      config/locales/validations/shared.en.yml
  6. 4
      lib/tasks/generate_lettings_documentation.rake
  7. 4
      lib/tasks/generate_sales_documentation.rake
  8. 2
      spec/models/validations/date_validations_spec.rb
  9. 34
      spec/models/validations/shared_validations_spec.rb
  10. 4
      spec/models/validations/tenancy_validations_spec.rb
  11. 2
      spec/requests/lettings_logs_controller_spec.rb

2
app/models/user.rb

@ -19,7 +19,7 @@ class User < ApplicationRecord
validates :password, presence: { if: :password_required? }
validates :password, length: { within: Devise.password_length, allow_blank: true }
validates :password, confirmation: { if: :password_required? }
validates :phone_extension, format: { with: /\A\d+\z/, allow_blank: true, message: I18n.t("validations.numeric.format", field: "") }
validates :phone_extension, format: { with: /\A\d+\z/, allow_blank: true, message: I18n.t("validations.shared.numeric.format", field: "") }
after_validation :send_data_protection_confirmation_reminder, if: :is_dpo_changed?

24
app/models/validations/shared_validations.rb

@ -7,12 +7,12 @@ module Validations::SharedValidations
main_field_label = main_label || main_field.to_s.humanize(capitalize: false)
other_field_label = other_label || other_field.to_s.humanize(capitalize: false)
if record[main_field] == value_other && record[other_field].blank?
record.errors.add main_field.to_sym, I18n.t("validations.other_field_missing", main_field_label:, other_field_label:)
record.errors.add other_field.to_sym, I18n.t("validations.other_field_missing", main_field_label:, other_field_label:)
record.errors.add main_field.to_sym, I18n.t("validations.shared.other_field_missing", main_field_label:, other_field_label:)
record.errors.add other_field.to_sym, I18n.t("validations.shared.other_field_missing", main_field_label:, other_field_label:)
end
if record[main_field] != value_other && record[other_field].present?
record.errors.add other_field.to_sym, I18n.t("validations.other_field_not_required", main_field_label:, other_field_label:)
record.errors.add other_field.to_sym, I18n.t("validations.shared.other_field_not_required", main_field_label:, other_field_label:)
end
end
@ -22,7 +22,7 @@ module Validations::SharedValidations
next if record.send("#{question.id}_before_type_cast").to_s.match?(/\A\d+(\.\d+)?\z/)
field = question.check_answer_label || question.id
record.errors.add question.id.to_sym, I18n.t("validations.numeric.format", field:)
record.errors.add question.id.to_sym, I18n.t("validations.shared.numeric.format", field:)
end
end
@ -55,12 +55,12 @@ module Validations::SharedValidations
incorrect_accuracy = (value.to_d * 100) % (question.step * 100) != 0
if question.step < 1 && incorrect_accuracy
record.errors.add question.id.to_sym, I18n.t("validations.numeric.nearest_hundredth", field:)
record.errors.add question.id.to_sym, I18n.t("validations.shared.numeric.nearest_hundredth", field:)
elsif incorrect_accuracy || value.to_d != value.to_i # if the user enters a value in exponent notation (eg '4e1') the to_i method does not convert this to the correct value
field = question.check_answer_label || question.id
case question.step
when 1 then record.errors.add question.id.to_sym, :not_integer, message: I18n.t("validations.numeric.whole_number", field:)
when 10 then record.errors.add question.id.to_sym, I18n.t("validations.numeric.nearest_ten", field:)
when 1 then record.errors.add question.id.to_sym, :not_integer, message: I18n.t("validations.shared.numeric.whole_number", field:)
when 10 then record.errors.add question.id.to_sym, I18n.t("validations.shared.numeric.nearest_ten", field:)
end
end
end
@ -69,7 +69,7 @@ module Validations::SharedValidations
def validate_property_postcode(record)
postcode = record.postcode_full
if record.postcode_known? && (postcode.blank? || !postcode.match(POSTCODE_REGEXP))
error_message = I18n.t("validations.postcode")
error_message = I18n.t("validations.shared.postcode")
record.errors.add :postcode_full, :wrong_format, message: error_message
end
end
@ -142,7 +142,7 @@ module Validations::SharedValidations
def date_valid?(question, record)
if record[question].is_a?(ActiveSupport::TimeWithZone) && record[question].year.zero?
record.errors.add question, I18n.t("validations.date.invalid_date")
record.errors.add question, I18n.t("validations.shared.date.invalid_date")
false
else
true
@ -153,7 +153,7 @@ module Validations::SharedValidations
return if record.skip_dpo_validation
if record.owning_organisation_id_changed? && record.owning_organisation.present? && !record.owning_organisation.data_protection_confirmed?
record.errors.add :owning_organisation_id, I18n.t("validations.setup.owning_organisation.data_sharing_agreement_not_signed")
record.errors.add :owning_organisation_id, I18n.t("validations.shared.setup.owning_organisation.data_sharing_agreement_not_signed")
end
end
@ -169,9 +169,9 @@ private
max = [question.prefix, number_with_delimiter(question.max, delimiter: ","), question.suffix].join("") if question.max
if min && max
record.errors.add question.id.to_sym, :outside_the_range, message: I18n.t("validations.numeric.within_range", field:, min:, max:)
record.errors.add question.id.to_sym, :outside_the_range, message: I18n.t("validations.shared.numeric.within_range", field:, min:, max:)
elsif min
record.errors.add question.id.to_sym, :under_min, message: I18n.t("validations.numeric.above_min", field:, min:)
record.errors.add question.id.to_sym, :under_min, message: I18n.t("validations.shared.numeric.above_min", field:, min:)
end
end
end

2
app/services/bulk_upload/sales/year2024/row_parser.rb

@ -369,7 +369,7 @@ class BulkUpload::Sales::Year2024::RowParser
validates :field_116,
numericality: {
message: I18n.t("validations.numeric.within_range", field: "Percentage discount", min: "0%", max: "70%"),
message: I18n.t("validations.shared.numeric.within_range", field: "Percentage discount", min: "0%", max: "70%"),
greater_than_or_equal_to: 0,
less_than_or_equal_to: 70,
if: :discounted_ownership?,

12
config/locales/en.yml

@ -257,19 +257,7 @@ en:
invalid_number: "Enter a number for %{question}"
no_address_found: "We could not find this address. Check the address data in your CSV file is correct and complete, or select the correct address using the CORE site."
other_field_missing: "If %{main_field_label} is other then %{other_field_label} must be provided."
other_field_not_required: "%{other_field_label} must not be provided if %{main_field_label} was not other."
numeric:
within_range: "%{field} must be between %{min} and %{max}."
above_min: "%{field} must be at least %{min}."
whole_number: "%{field} must be a whole number."
nearest_ten: "%{field} must be given to the nearest ten."
nearest_hundredth: "%{field} must be given to the nearest hundredth."
normal_format: "Enter a number."
format: "%{field} must be a number."
date:
invalid_date: "Enter a date in the correct format, for example 31 1 2024."
outside_collection_window: "Enter a date within the %{year_combo} collection year, which is between 1st April %{start_year} and 31st March %{end_year}."
postcode: "Enter a postcode in the correct format, for example AA1 1AA."
location_admin_district: "Select a local authority."

27
config/locales/validations/shared.en.yml

@ -0,0 +1,27 @@
en:
validations:
shared:
other_field_missing: "If %{main_field_label} is other then %{other_field_label} must be provided."
other_field_not_required: "%{other_field_label} must not be provided if %{main_field_label} was not other."
numeric:
within_range: "%{field} must be between %{min} and %{max}."
above_min: "%{field} must be at least %{min}."
whole_number: "%{field} must be a whole number."
nearest_ten: "%{field} must be given to the nearest ten."
nearest_hundredth: "%{field} must be given to the nearest hundredth."
normal_format: "Enter a number."
format: "%{field} must be a number."
postcode: "Enter a postcode in the correct format, for example AA1 1AA."
date:
invalid_date: "Enter a date in the correct format, for example 31 1 2024."
setup:
owning_organisation:
data_sharing_agreement_not_signed: "The organisation must accept the Data Sharing Agreement before it can be selected as the owning organisation."

4
lib/tasks/generate_lettings_documentation.rake

@ -60,13 +60,13 @@ namespace :generate_lettings_documentation do
min = [question.prefix, question.min].join("") if question.min
max = [question.prefix, question.max].join("") if question.max
error_message = I18n.t("validations.numeric.above_min", field:, min:)
error_message = I18n.t("validations.shared.numeric.above_min", field:, min:)
validation_name = "minimum"
validation_description = "Field value is lower than the minimum value"
if min && max
validation_name = "range"
error_message = I18n.t("validations.numeric.within_range", field:, min:, max:)
error_message = I18n.t("validations.shared.numeric.within_range", field:, min:, max:)
validation_description = "Field value is lower than the minimum value or higher than the maximum value"
end

4
lib/tasks/generate_sales_documentation.rake

@ -59,13 +59,13 @@ namespace :generate_sales_documentation do
min = [question.prefix, question.min].join("") if question.min
max = [question.prefix, question.max].join("") if question.max
error_message = I18n.t("validations.numeric.above_min", field:, min:)
error_message = I18n.t("validations.shared.numeric.above_min", field:, min:)
validation_name = "minimum"
validation_description = "Field value is lower than the minimum value"
if min && max
validation_name = "range"
error_message = I18n.t("validations.numeric.within_range", field:, min:, max:)
error_message = I18n.t("validations.shared.numeric.within_range", field:, min:, max:)
validation_description = "Field value is lower than the minimum value or higher than the maximum value"
end

2
spec/models/validations/date_validations_spec.rb

@ -12,7 +12,7 @@ RSpec.describe Validations::DateValidations do
it "must be a valid date" do
record.startdate = Time.zone.local(0, 7, 1)
date_validator.validate_startdate(record)
expect(record.errors["startdate"]).to include(match I18n.t("validations.date.invalid_date"))
expect(record.errors["startdate"]).to include(match I18n.t("validations.shared.date.invalid_date"))
end
it "does not raise an error when valid" do

34
spec/models/validations/shared_validations_spec.rb

@ -18,42 +18,42 @@ RSpec.describe Validations::SharedValidations do
lettings_log.age1 = "random"
shared_validator.validate_numeric_min_max(lettings_log)
expect(lettings_log.errors["age1"])
.to include(match I18n.t("validations.numeric.within_range", field: "Lead tenant’s age", min: 16, max: 120))
.to include(match I18n.t("validations.shared.numeric.within_range", field: "Lead tenant’s age", min: 16, max: 120))
end
it "validates that other household member ages are a number" do
lettings_log.age2 = "random"
shared_validator.validate_numeric_min_max(lettings_log)
expect(lettings_log.errors["age2"])
.to include(match I18n.t("validations.numeric.within_range", field: "Person 2’s age", min: 1, max: 120))
.to include(match I18n.t("validations.shared.numeric.within_range", field: "Person 2’s age", min: 1, max: 120))
end
it "validates that person 1's age is greater than 16" do
lettings_log.age1 = 15
shared_validator.validate_numeric_min_max(lettings_log)
expect(lettings_log.errors["age1"])
.to include(match I18n.t("validations.numeric.within_range", field: "Lead tenant’s age", min: 16, max: 120))
.to include(match I18n.t("validations.shared.numeric.within_range", field: "Lead tenant’s age", min: 16, max: 120))
end
it "validates that other household member ages are greater than 1" do
lettings_log.age2 = 0
shared_validator.validate_numeric_min_max(lettings_log)
expect(lettings_log.errors["age2"])
.to include(match I18n.t("validations.numeric.within_range", field: "Person 2’s age", min: 1, max: 120))
.to include(match I18n.t("validations.shared.numeric.within_range", field: "Person 2’s age", min: 1, max: 120))
end
it "validates that person 1's age is less than 121" do
lettings_log.age1 = 121
shared_validator.validate_numeric_min_max(lettings_log)
expect(lettings_log.errors["age1"])
.to include(match I18n.t("validations.numeric.within_range", field: "Lead tenant’s age", min: 16, max: 120))
.to include(match I18n.t("validations.shared.numeric.within_range", field: "Lead tenant’s age", min: 16, max: 120))
end
it "validates that other household member ages are greater than 121" do
lettings_log.age2 = 123
shared_validator.validate_numeric_min_max(lettings_log)
expect(lettings_log.errors["age2"])
.to include(match I18n.t("validations.numeric.within_range", field: "Person 2’s age", min: 1, max: 120))
.to include(match I18n.t("validations.shared.numeric.within_range", field: "Person 2’s age", min: 1, max: 120))
end
it "validates that person 1's age is between 16 and 120" do
@ -91,7 +91,7 @@ RSpec.describe Validations::SharedValidations do
sales_log.savings = -10
sales_log.jointpur = 1
shared_validator.validate_numeric_min_max(sales_log)
expect(sales_log.errors["savings"]).to include(match I18n.t("validations.numeric.above_min", field: "Buyers’ total savings before any deposit paid", min: "£0"))
expect(sales_log.errors["savings"]).to include(match I18n.t("validations.shared.numeric.above_min", field: "Buyers’ total savings before any deposit paid", min: "£0"))
end
context "when validating percent" do
@ -101,7 +101,7 @@ RSpec.describe Validations::SharedValidations do
sales_log.stairbought = 150
shared_validator.validate_numeric_min_max(sales_log)
expect(sales_log.errors["stairbought"])
.to include(match I18n.t("validations.numeric.within_range", field: "Percentage bought in this staircasing transaction", min: "0%", max: "100%"))
.to include(match I18n.t("validations.shared.numeric.within_range", field: "Percentage bought in this staircasing transaction", min: "0%", max: "100%"))
end
end
@ -110,7 +110,7 @@ RSpec.describe Validations::SharedValidations do
sales_log.income1 = -5
shared_validator.validate_numeric_min_max(sales_log)
expect(sales_log.errors["income1"])
.to include(match I18n.t("validations.numeric.within_range", field: "Buyer 1’s gross annual income", min: "£0", max: "£999,999"))
.to include(match I18n.t("validations.shared.numeric.within_range", field: "Buyer 1’s gross annual income", min: "£0", max: "£999,999"))
end
end
end
@ -120,13 +120,13 @@ RSpec.describe Validations::SharedValidations do
it "adds an error if input is a decimal" do
sales_log.income1 = 30_000.5
shared_validator.validate_numeric_step(sales_log)
expect(sales_log.errors[:income1]).to include I18n.t("validations.numeric.whole_number", field: "Buyer 1’s gross annual income")
expect(sales_log.errors[:income1]).to include I18n.t("validations.shared.numeric.whole_number", field: "Buyer 1’s gross annual income")
end
it "adds an error if the user attempts to input a number in exponent format" do
sales_log.income1 = "3e5"
shared_validator.validate_numeric_step(sales_log)
expect(sales_log.errors[:income1]).to include I18n.t("validations.numeric.whole_number", field: "Buyer 1’s gross annual income")
expect(sales_log.errors[:income1]).to include I18n.t("validations.shared.numeric.whole_number", field: "Buyer 1’s gross annual income")
end
it "does not add an error if input is an integer" do
@ -141,14 +141,14 @@ RSpec.describe Validations::SharedValidations do
sales_log.savings = 30_005
sales_log.jointpur = 1
shared_validator.validate_numeric_step(sales_log)
expect(sales_log.errors[:savings]).to include I18n.t("validations.numeric.nearest_ten", field: "Buyers’ total savings before any deposit paid")
expect(sales_log.errors[:savings]).to include I18n.t("validations.shared.numeric.nearest_ten", field: "Buyers’ total savings before any deposit paid")
end
it "adds an error if the user attempts to input a number in exponent format" do
sales_log.savings = "3e5"
sales_log.jointpur = 1
shared_validator.validate_numeric_step(sales_log)
expect(sales_log.errors[:savings]).to include I18n.t("validations.numeric.nearest_ten", field: "Buyers’ total savings before any deposit paid")
expect(sales_log.errors[:savings]).to include I18n.t("validations.shared.numeric.nearest_ten", field: "Buyers’ total savings before any deposit paid")
end
it "does not add an error if input is a multiple of ten" do
@ -162,7 +162,7 @@ RSpec.describe Validations::SharedValidations do
it "adds an error if input has more than 2 decimal places" do
sales_log.mscharge = 30.7418
shared_validator.validate_numeric_step(sales_log)
expect(sales_log.errors[:mscharge]).to include I18n.t("validations.numeric.nearest_hundredth", field: "Monthly leasehold charges")
expect(sales_log.errors[:mscharge]).to include I18n.t("validations.shared.numeric.nearest_hundredth", field: "Monthly leasehold charges")
end
it "does not add an error if the user attempts to input a number in exponent format" do
@ -220,13 +220,13 @@ RSpec.describe Validations::SharedValidations do
it "does not allow letters" do
sales_log.income1 = "abc"
shared_validator.validate_numeric_input(sales_log)
expect(sales_log.errors[:income1]).to include I18n.t("validations.numeric.format", field: "Buyer 1’s gross annual income")
expect(sales_log.errors[:income1]).to include I18n.t("validations.shared.numeric.format", field: "Buyer 1’s gross annual income")
end
it "does not allow special characters" do
sales_log.income1 = "3%5"
shared_validator.validate_numeric_input(sales_log)
expect(sales_log.errors[:income1]).to include I18n.t("validations.numeric.format", field: "Buyer 1’s gross annual income")
expect(sales_log.errors[:income1]).to include I18n.t("validations.shared.numeric.format", field: "Buyer 1’s gross annual income")
end
it "allows a digit" do
@ -244,7 +244,7 @@ RSpec.describe Validations::SharedValidations do
it "does not allow decimal point in a wrong format" do
sales_log.income1 = "300.09.78"
shared_validator.validate_numeric_input(sales_log)
expect(sales_log.errors[:income1]).to include I18n.t("validations.numeric.format", field: "Buyer 1’s gross annual income")
expect(sales_log.errors[:income1]).to include I18n.t("validations.shared.numeric.format", field: "Buyer 1’s gross annual income")
end
end
end

4
spec/models/validations/tenancy_validations_spec.rb

@ -277,7 +277,7 @@ RSpec.describe Validations::TenancyValidations do
describe "tenancy type validations" do
let(:record) { FactoryBot.build(:lettings_log, :setup_completed) }
let(:field) { "validations.other_field_missing" }
let(:field) { "validations.shared.other_field_missing" }
let(:main_field_label) { "tenancy type" }
let(:other_field) { "tenancyother" }
let(:other_field_label) { "other tenancy type" }
@ -300,7 +300,7 @@ RSpec.describe Validations::TenancyValidations do
end
context "when tenancy type is not other" do
let(:field) { "validations.other_field_not_required" }
let(:field) { "validations.shared.other_field_not_required" }
it "validates that other tenancy type is not provided" do
record.tenancy = 2

2
spec/requests/lettings_logs_controller_spec.rb

@ -82,7 +82,7 @@ RSpec.describe LettingsLogsController, type: :request do
it "validates lettings log parameters" do
json_response = JSON.parse(response.body)
expect(response).to have_http_status(:unprocessable_entity)
expect(json_response["errors"]).to match_array([["offered", [I18n.t("validations.numeric.within_range", field: "Times previously offered since becoming available", min: 0, max: 20)]], ["age1", [I18n.t("validations.numeric.within_range", field: "Lead tenant’s age", min: 16, max: 120)]]])
expect(json_response["errors"]).to match_array([["offered", [I18n.t("validations.shared.numeric.within_range", field: "Times previously offered since becoming available", min: 0, max: 20)]], ["age1", [I18n.t("validations.shared.numeric.within_range", field: "Lead tenant’s age", min: 16, max: 120)]]])
end
end

Loading…
Cancel
Save