RubyMoney/money-rails

Assocation .build clears amount if association called during setup

nitsujri opened this issue · 2 comments

TL;DR

If a model association is called during .build for setting with_model_currency, the column_cents gets reset to 0.

Example

https://github.com/nitsujri/money-build-issue

To run it, load the rails console and run User.run_example.

Details

It will run through showing .build works fine if we don't call the assocation, user.currency, during with_model_currency, but then if we do call an association, amount_cents becomes zero.

The problem line is this. The only difference is a random user.currency that has no bearing on the final output.

After build, setting amount works just fine as we all would expect.

Closing

This specifically caught us during a accepts_nested_attributes_for form. I left all the deprecations in, so the output is a bit messy.

I can also get a similar situation with u=User.create; Purchase.new(user: u, amount: '222') so it's build/new initializer? I'm just guessing at this point though.

Thank you!

Thanks for this fantastic set of gems!

@semmons99 hi. This issue still persists for me. I'm on money-rails (1.15.0) . For me my price_cents gets reset to 0 in a nested attribute record creation. price_cents is a monitized field. Nested attribute structure is as such -
model A has many model B, model A accepts_nested_attributes for Bs
model B has many model C, model B accepts_nested_attributes for Cs
model C has a field price_cents, bigint, default: 0.

While record creation in a nested setting the rails build method sets price_cents to 0, however in update this doesn't happen.

I have the same issue, did anyone find a fix for that? Nested attributes ignore the price attribute, but it works with price_cents.
The price param will work if the order is being created or if the order item already exists. If the order exists and a new order item gets added, price will not work.

My workaround for now is to manually change the price to cents. For example:

order_params = params.require(:order).permit(
  :name,
  order_items_attributes: [ :id, :name, :quantity, :unit_price, :_destroy ]
)
if order_params[:order_items_attributes].present?
  order_params[:order_items_attributes].each do |key, order_item_params|
    order_item_params[:unit_price_cents] = Money.from_amount(order_item_params[:unit_price].to_f).cents
    order_item_params.delete(:unit_price)
  end
end
order_params