Reputation: 10207
I recently came across this weird bug in Rails.
class PaymentsController < ApplicationController
def new
@payment = current_user.payments.build(:invoice_id => params[:invoice_id])
@title = "Make payment"
end
end
class Payment < ActiveRecord::Base
attr_accessible :amount, :date, :invoice_id
after_initialize :set_amount
private
def set_amount
if new_record? && invoice.present?
self.amount ||= invoice.amount_payable
end
end
end
When I call this action from an invoice
like so...
<%= link_to "Make payment", new_payment_path(:invoice_id => invoice.id) %>
...the payment form appears with the correct invoice
preselected in the dropdown field (which is correct).
The payment amount
is populated with the correct amount in about 90% of all cases.
However, sometimes it is not populated with the 2-digit amount_payable
from the database, but rather some other weird value such as:
87.31999999999999
(where 87.32
is the decimal type value stored in the SQLite database)
Can somebody tell me what is causing this rounding error or point me in the right direction?
Thanks for any help.
By the way, this is my database schema:
create_table "invoices", :force => true do |t|
t.decimal "amount_payable", :precision => 8, :scale => 2
end
Upvotes: 0
Views: 606
Reputation: 40277
You should never store Money as floats --- what's going on here is floating point arithmetic, and it's very common for calculations with money.
A better point is to store everything as integer for cents. So, you may have in your database a "amount_in_cents" column that is an integer.
Then, you add a getter/setter for 'amount' so you can use #amount
all over the place.
class Payment < ActiveRecord::Base
def amount
amount_in_cents / 100.0
end
def amount=(other)
self.amount_in_cents = (other * 100).to_i
end
end
Upvotes: 5