-
Notifications
You must be signed in to change notification settings - Fork 108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Possible regression in monetize 1.9.0 with Money#allocate #118
Comments
This commit comes from @huoxito work on #15 (Thanks!) On newer versions monetize requires money 6.12 which has a rewrite of the Money#allocate method that we use in DistrubutedAmount calculator. This is the PR that is causing issues: RubyMoney/money#772 This is the issue we need to be solved before relaxing the dependency again: RubyMoney/monetize#118
@huoxito thanks for raising this. While the change in behaviour was definitely introduced by a rewrite of the First of all, are you using Second of all, you are relying on ruby's >> BigDecimal('0.13333333333333333') + BigDecimal('0.2') + BigDecimal('0.6666666666666666')
=> 0.99999999999999993e0 The older implementation had some imprecisions which could have resulted in loosing money, especially when dealing with In your particular case (repeating decimals) it would be much better to use >> weights = [Rational(2, 15), Rational(1, 5), Rational(2, 3)]
=> [(2/15), (1/5), (2/3)]
>> Money.new(15).allocate(weights).map(&:to_d)
=> [0.2e-1, 0.3e-1, 0.1e0] resulting in expected allocations. Can you please provide me with a bit more detail on your use case in order to figure out what would be the right solution here? |
hey @antstorm thanks! I'm not sure it's a regression either. Honestly I don't know much about numbers precision. Also I don't think solidus use This is the code that raised the issue: def weights
elligible_amounts.map { |amount| amount.to_f / subtotal.to_f }
end
def allocated_amounts
total_amount.to_money.allocate(weights).map(&:to_money)
end The amounts are BigDecimals but it does do See below pls. Maybe this is a better example. For this one the first test fails for both monetize 1.9 and 1.8 although it seems they both should be green? RSpec.describe "Money#allocate #{Money::VERSION} monetize #{Monetize::VERSION}" do
let(:total) { 15 }
def build_weights(lines, total)
lines.map { |line| line / total }
end
it 'rounds properly' do
lines = [BigDecimal('150.0'), BigDecimal('50.0'), BigDecimal('100.0')]
weights = build_weights lines, BigDecimal('300.0')
expect(
total.to_money.allocate(weights).map(&:to_money).map(&:to_d)
).to eq([7.5, 2.5, 5.0])
end
it 'also rounds properly' do
lines = [BigDecimal('20.0'), BigDecimal('30.0'), BigDecimal('100.0')]
weights = build_weights lines, BigDecimal('150.0')
expect(
total.to_money.allocate(weights).map(&:to_money).map(&:to_d)
).to eq([2, 3, 10])
end
end |
@huoxito I'm not sure if I fully understand the business logic behind this operation, but it seems to me that you don't need to calculate weights yourself — you can let The trick is that items in the array that you pass to >> Money.new(500).allocate([2, 3])
=> [#<Money fractional:200 currency:USD>, #<Money fractional:300 currency:USD>] In your example this means replacing def weights
elligible_amounts
end or loosing it completely in favour of As for the failing spec, here's my slightly version of it that passes (note that I'm not using monetised here, but the allocate comes from require 'spec_helper'
RSpec.describe "Money#allocate #{Money::VERSION}" do
let(:total) { 15 }
it 'rounds properly' do
lines = [BigDecimal('150.0'), BigDecimal('50.0'), BigDecimal('100.0')]
expect(
Money.from_amount(total).allocate(lines).map(&:to_d)
).to eq([7.5, 2.5, 5.0])
end
it 'also rounds properly' do
lines = [BigDecimal('20.0'), BigDecimal('30.0'), BigDecimal('100.0')]
expect(
Money.from_amount(total).allocate(lines).map(&:to_d)
).to eq([2, 3, 10])
end
end In fact this also works without |
zomg 🤣 I didn't know that thanks a lot @antstorm I should have read docs more carefully. Appreciate it. |
@huoxito no worries, hope that helped! 👍 |
This commit comes from @huoxito work on #15 (Thanks!) On newer versions monetize requires money 6.12 which has a rewrite of the Money#allocate method that we use in DistrubutedAmount calculator. This is the PR that is causing issues: RubyMoney/money#772 This is the issue we need to be solved before relaxing the dependency again: RubyMoney/monetize#118
This might be a Money gem issue but I couldn't isolate it using only money gem in example.
Solidus uses this heavily and we noticed a couple failures related to a bump from monetize 1.8.0 to 1.9.0. See solidusio/solidus#2826 and nebulab/solidus#15. The failing examples in solidus build checks if a given amount is split properly among a collection of order line items. Example:
Those two examples pass on 1.8.0 but the last one fail on 1.9.0. It seems to split the given amount as
[2.01, 2.99, 10]
.The text was updated successfully, but these errors were encountered: