RubyMoney / money-rails

Integration of RubyMoney - Money with Rails
MIT License
1.8k stars 387 forks source link

Validation failing when using less than with delimiter equal to '.' #634

Closed paulogrupp closed 1 year ago

paulogrupp commented 3 years ago

Hi, I'm having a problem where using '.' as a delimiter fails the "less_than" validation when doing the following in the tests at spec/active_record/monetizable_spec.rb:286:

I18n.default_locale = :it # using the locale present in spec/dummy/config/locales/it.yml
product.price_in_a_range = 100
puts product.price_in_a_range #=> 100,00
puts product.price_in_a_range_cents #=> 10000
expect(product).to be_valid # this passes

product.price_in_a_range = '100,0'
puts product.price_in_a_range #=> 100,00
puts product.price_in_a_range_cents #=> 10000
expect(product).to be_valid # this passes

product.price_in_a_range = 100.0
puts product.price_in_a_range #=> 100,00
puts product.price_in_a_range_cents #=> 10000
expect(product).to be_valid # this fails

I noticed that using an empty string as a delimiter makes the test pass. Removing the delimiter key from spec/dummy/config/locales/it.yml keeps the test failing Is this the expected behavior?

I created this PR with the failing test using the behvior I mentioned: https://github.com/RubyMoney/money-rails/pull/633

semmons99 commented 3 years ago

I am not sure Ruby can handle a , as a delimiter in a Float itself. I think it can only handle it on the output via the i18n gem.

I would be happy to accept a PR that makes it work consistently for all locales.

Maysora commented 2 years ago

got this issue a few days ago

class Product < ApplicationRecord
  monetize :price_cents, numericality: { greater_than_or_equal_to: 0, less_than: 5000 }
end
I18n.locale = :id
product = Product.create!(name: 'test', price: '4999,99') #=> saved without error
product.reload.valid? #=> false
product.update!(price: '1000') #=> saved without error
product.reload.valid? #=> false
product.errors.details #=>  {:price=>[{:error=>:less_than, :value=>10000, :count=>5000}]}

my solution is to override MoneyValidator#normalize to do nothing when details.raw_value already Numeric :

module MoneyRails
  module ActiveModel
    class MoneyValidator < ::ActiveModel::Validations::NumericalityValidator

      private

      def normalize(details)
        return details.raw_value if details.raw_value.is_a?(Numeric)
        super
      end
    end
  end
end
semmons99 commented 2 years ago

should we make this a change?