diff --git a/app/models/user.rb b/app/models/user.rb index 75b5a366b..0ca6d0944 100644 --- a/app/models/user.rb +++ b/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? diff --git a/app/models/validations/shared_validations.rb b/app/models/validations/shared_validations.rb index 74b5c42c4..1593f07eb 100644 --- a/app/models/validations/shared_validations.rb +++ b/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 diff --git a/app/services/bulk_upload/sales/year2024/row_parser.rb b/app/services/bulk_upload/sales/year2024/row_parser.rb index 7b72eef5c..c22d81964 100644 --- a/app/services/bulk_upload/sales/year2024/row_parser.rb +++ b/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?, diff --git a/config/locales/en.yml b/config/locales/en.yml index 08706530a..05dda8f7b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -234,7 +234,7 @@ en: scheme_deleted: "%{service_name} has been deleted." user_deleted: "%{name} has been deleted." organisation_deleted: "%{name} has been deleted." - user_updated: + user_updated: self: "Your account details have been updated." other: "%{name}’s details have been updated." @@ -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." diff --git a/config/locales/validations/shared.en.yml b/config/locales/validations/shared.en.yml new file mode 100644 index 000000000..97a3eb2b8 --- /dev/null +++ b/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." + + + + diff --git a/lib/tasks/generate_lettings_documentation.rake b/lib/tasks/generate_lettings_documentation.rake index 8b11e3ff5..3dc8e71b4 100644 --- a/lib/tasks/generate_lettings_documentation.rake +++ b/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 diff --git a/lib/tasks/generate_sales_documentation.rake b/lib/tasks/generate_sales_documentation.rake index 62272f6f8..c595c4f18 100644 --- a/lib/tasks/generate_sales_documentation.rake +++ b/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 diff --git a/spec/models/validations/date_validations_spec.rb b/spec/models/validations/date_validations_spec.rb index 59be2537e..9e79f6189 100644 --- a/spec/models/validations/date_validations_spec.rb +++ b/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 diff --git a/spec/models/validations/shared_validations_spec.rb b/spec/models/validations/shared_validations_spec.rb index b4d7fb0b8..53efc6675 100644 --- a/spec/models/validations/shared_validations_spec.rb +++ b/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 diff --git a/spec/models/validations/tenancy_validations_spec.rb b/spec/models/validations/tenancy_validations_spec.rb index a41752616..90a6a1597 100644 --- a/spec/models/validations/tenancy_validations_spec.rb +++ b/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 diff --git a/spec/requests/lettings_logs_controller_spec.rb b/spec/requests/lettings_logs_controller_spec.rb index 308b90c1e..3d2c27400 100644 --- a/spec/requests/lettings_logs_controller_spec.rb +++ b/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