mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-04 22:16:08 +00:00
As part of this PR https://github.com/openfoodfoundation/openfoodnetwork/pull/10329 LocalisedNumber validation was tightened, but that means negative number were not valid anymore. This commit has been cherry-picked after this fix has already been applied. Now we just change the order of characters in the regex because humans are used to reading the minus at the beginning of the number. But this change doesn't change the logic at all.
84 lines
3.1 KiB
Ruby
84 lines
3.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Spree
|
|
module LocalizedNumber
|
|
# This method overwrites the attribute setters of a model
|
|
# to make them use the LocalizedNumber parsing method.
|
|
# It works with ActiveRecord "normal" attributes
|
|
# and Preference attributes.
|
|
# It also adds a validation on the input format.
|
|
# It accepts as arguments a variable number of attribute as symbols
|
|
def localize_number(*attributes)
|
|
validate :validate_localizable_number
|
|
|
|
attributes.each do |attribute|
|
|
setter = "#{attribute}="
|
|
old_setter = instance_method(setter) if non_activerecord_attribute?(attribute)
|
|
|
|
define_method(setter) do |number|
|
|
if Spree::Config.enable_localized_number? && Spree::LocalizedNumber.valid_localizable_number?(number)
|
|
number = Spree::LocalizedNumber.parse(number)
|
|
elsif Spree::Config.enable_localized_number?
|
|
@invalid_localized_number ||= []
|
|
@invalid_localized_number << attribute
|
|
number = nil unless is_a?(Spree::Calculator)
|
|
end
|
|
if has_attribute?(attribute)
|
|
# In this case it's a regular AR attribute with standard setters
|
|
self[attribute] = number
|
|
else
|
|
# In this case it's a Spree preference, and the interface is very different
|
|
old_setter.bind(self).call(number)
|
|
end
|
|
end
|
|
end
|
|
|
|
define_method(:validate_localizable_number) do
|
|
return unless Spree::Config.enable_localized_number?
|
|
|
|
@invalid_localized_number&.each do |error_attribute|
|
|
errors.add(error_attribute, I18n.t('spree.localized_number.invalid_format'))
|
|
end
|
|
end
|
|
end
|
|
|
|
def self.valid_localizable_number?(number)
|
|
return true unless number.is_a?(String) || number.respond_to?(:to_d)
|
|
# Invalid if only two digits between dividers, or if any non-number characters
|
|
return false if number.to_s =~ /[.,]\d{2}[.,]/ || number.to_s =~ /[^-0-9,.]+/
|
|
|
|
true
|
|
end
|
|
|
|
def self.parse(number)
|
|
return nil if number.blank?
|
|
return number.to_d unless number.is_a?(String)
|
|
|
|
number = number.gsub(/[^\d.,-]/, '') # Replace all Currency Symbols, Letters and -- from the string
|
|
|
|
add_trailing_zeros(number)
|
|
|
|
number = number.gsub(/[.,]/, '') # Replace all (.) and (,) so the string result becomes in "cents"
|
|
number.to_d / 100 # Let to_decimal do the rest
|
|
end
|
|
|
|
def self.add_trailing_zeros(number)
|
|
# If string ends in a single digit (e.g. ,2), make it ,20 in order for the result to be in "cents"
|
|
number << "0" if number =~ /^.*[.,]\d{1}$/
|
|
|
|
# If does not end in ,00 / .00 then add trailing 00 to turn it into cents
|
|
number << "00" unless number =~ /^.*[.,]\d{2}$/
|
|
end
|
|
|
|
private
|
|
|
|
def non_activerecord_attribute?(attribute)
|
|
table_exists? && !column_names.include?(attribute.to_s)
|
|
rescue ::ActiveRecord::NoDatabaseError
|
|
# This class is now loaded during `rake db:create` (since Rails 5.2), and not only does the
|
|
# table not exist, but the database does not even exist yet, and throws a fatal error.
|
|
# We can rescue and safely ignore it in that case.
|
|
end
|
|
end
|
|
end
|