Search code examples
ruby-on-railsrubymoney-rails

Using money-rails gem, switching instance currency to Euro breaks updates


I have a model named Widget that has a single monetized attribute named advance.

Each instance of a Widget may have an advance attribute that stores a unique currency and amount.

Despite setting up everything based on the gem documentation, and having the ability to switch between the default currency USD and switching to other currencies, such as JPY, CSD, CNY, switching to EUR throws errors and prevents updating the advance_currency and advance_cents values.

Gem File

ruby "3.1.2"
gem "rails", "7.0.2.3"
gem "money-rails", "1.15.0"

Initializer File

Money.locale_backend = :currency

MoneyRails.configure do |config|
  config.default_currency = :usd
  config.include_validations = true
  config.rounding_mode = BigDecimal::ROUND_HALF_UP
end

Migration Setup

create_table :widgets, force: :cascade do |t|

  ...

  t.monetize :advance, amount: { limit: 8 }
end

Schema

create_table "widgets", force: :cascade do |t|

    ...

    t.bigint "advance_cents", default: 0, null: false
    t.string "advance_currency", default: "USD", null: false

    ...

end

Model Setup

class Widget < ApplicationRecord

  ...

  monetize :advance_cents, with_model_currency: :advance_currency

  ...

end

Edit Form View Snippet

  <div class="form-floating">
    <%= select_tag("advance_currency", options_for_select(CurrencyTypes.options_for_select, @instance.advance_currency), prompt: "Select a currency", class: "form-select") %>
    <label for="advance_currency" class="form-label">Advance Currency</label>
  </div>
  <div class="form-floating">
    <input type="number" step="any" class="form-control" id="advance" name="advance" value="<%= number_to_currency(@instance.advance, unit: "", separator: ".") %>">
    <label for="advance" class="form-label">Advance Amount</label>
  </div>

Controller Update Action

def update
  instance = controller_class.find(params[:id])
  instance.update(update_params)

  ...

end

private

def update_params
  params.permit(

    ...

    :advance,
    :advance_currency,

    ...

  )
end

Messages Switching to Yen (Works)

Params Check

#<ActionController::Parameters {

  ...

  "advance_currency"=>"JPY",
  "advance"=>"987.65", 
} permitted: false>

Debugging Update

> instance.update(update_params)
> instance.save

true

Debugging Errors

> instance.errors

#<ActiveModel::Errors []>

Error Message(s) Switching to Euro (Doesn't Work)

Params Check

#<ActionController::Parameters {

  ...

  "advance_currency"=>"EUR",
  "advance"=>"123.45", 
} permitted: false>

Debugging Update

> instance.update(update_params)
> instance.save

false

Debugging Errors

> instance.errors

#<ActiveModel::Errors [#<ActiveModel::Error attribute=advance, type=invalid_currency, options={:thousands=>".", :decimal=>",", :currency=>"123.45", :attribute=>"Advance"}>]>

What doesn't make any sense is that when switching to Euro as the currency, the advance amount seems to be entered into the currency value instead of EUR.

I have no idea why one currency throws off the money-rails gem operations while the others work correctly.


Solution

  • It seems like the use of the number type input is the problem.

    You can only submit values in Euro with the 123.456,78 format instead of 123,456.78 for the other currencies.

    Since a comma is not allowed in the number input type, you must switch to a type of text

    Not Working

    <div class="form-floating">
      <input type="number" class="form-control" id="advance" name="advance" value="<%= @instance.advance %>">
      <label for="advance" class="form-label">Advance Amount</label>
    </div>
    

    Working

    <div class="form-floating">
      <input type="text" class="form-control" id="advance" name="advance" value="<%= @instance.advance %>">
      <label for="advance" class="form-label">Advance Amount</label>
    </div>