Using number_input to accept money input values

I am trying to build a template and controller to accept decimal input (monetary bids). If I use the number_input control I am restricted to entering only integer values. Is there a way to modify the behavior of this control to accept decimal input?

If not, would the best approach be to use a text_input control and parse the input to a float? I have coded it this way but I find it difficult to integrate any errors which may arise back into my template. Any suggestions would be most appreciated.

I’ve worked on a few non-elixir monetary and payment libraries before and it seems to be standard practice to always use integers and record amounts essentially as an amount of pennies. When you need to display then format it into decimals or alternatively accept decimal values and then convert decimal into integers for the backend.

Seems to be the way banking systems work.

2 Likes

I agree. I am accepting decimal input and storing cents in the DB. But my question is how to accept that decimal input in a Phoenix app. It appears that the HTML 5 numeric input control can be modified to accept decimal input, so I am wondering if the Phoenix numeric_input can be modified similarly to accept a monetary value. This would make coding way easier because I would not have to accept text value, which would have to be parsed, and opens up some error handling issues.

I just did quick test and it looks like you can add the “step” attribute which will allow users to add decimals and use up and down to cycle through at the second decimal but it does not provide any protections or validations on how many decimal places are typed in.

<%= number_input f, :amount, step: “0.01” %>

1 Like

Wow! Thanks. That is just what I was looking for. Just for the record, I tried this earlier but I must have had the syntax wrong because I received an error.

Thanks again for your help.

programming money - one of my favourite topics! But it as a few challenges, one of which is retaining precision. And since floats and precision are incompatible topics, please never convert a money amount to a float. Use a Decimal.

I personally also find using integer amounts to be insufficient since:

  1. Not all currencies use 2 fractional digits
  2. It does not allow determination of which currency is in play
  3. Now you have to make sure all calculations manage the implied fractional digits
  4. Banking systems typically use a precision of at least 8 decimal digits
  5. Rounding is usually applied only at the end of a chain of arithmetic calculations
  6. Rounding can be applied in many different ways, but most commonly “bankers rounding” which is not what Float.round/2 does

I recognise that certain payment gateways use an integral amount but that doesn’t mean that your application should use that representation internally.

4 Likes

To your original question about money input. Theres been some thoughts and ideas shared on this very topic on an issue tracker of one of my libs you might find useful: https://github.com/kipcole9/money/issues/111

3 Likes

Hrmmm…Bankers Rounding
Reminds me of the worm virus in the movie Hackers to steal pennies in each transaction of a large company :thinking:

https://wiki.c2.com/?BankersRounding

in which numbers which are equidistant from the two nearest integers are rounded to the nearest even integer. Thus, 0.5 rounds down to 0; 1.5 rounds up to 2.

I know it does make sense but pretty hard to spot a bias in an accounting table, especially in foreign currency exchange that goes to something like a 6th or 8th decimal place.

1 Like