What is the best way to convert dollars and cents into just cents in a Phoenix Framework app?
Background
I’m storing currencies in the smallest denomination (cents) to avoid floating point math (everything is an integer). The user will still input currencies in dollars and cents.
e.g., User inputs $75.25 → stored as 7525
in the database.
LiveView Form Component
I have a form_component.ex
with the following text input (using Alpine.js Mask for formatting):
<.input
x-mask:dynamic="$money($input)"
field={@form[:fee]}
type="text"
placeholder="0.00"
label="Fee"
/>
and the following functions (also in form_component.ex
), which converts the string “75.25” into the integer 7525
both on validation and on save:
def handle_event("validate", %{"assignments" => assignments_params}, socket) do
assignments_params =
Map.update!(assignments_params, "fee", fn fee -> dollars_to_cents(fee) end)
changeset =
socket.assigns.assignments
|> Assigner.change_assignments(assignments_params)
|> Map.put(:action, :validate)
{:noreply, assign_form(socket, changeset)}
end
def handle_event("save", %{"assignments" => assignments_params}, socket) do
assignments_params =
Map.update!(assignments_params, "fee", fn fee -> dollars_to_cents(fee) end)
save_assignments(socket, socket.assigns.action, assignments_params)
end
defp dollars_to_cents(""), do: nil
defp dollars_to_cents(dollars_and_cents_string) do
{dollars_and_cents_float, _} = Float.parse(dollars_and_cents_string)
trunc(dollars_and_cents_float * 100)
end
Questions
- Where should I put the
dollars_to_cents
helper function? I may use currencies elsewhere in the app and don’t want to duplicate code. - Similarly, is there a way to convert the
fee
to cents in a central place (perhaps the schema?) to avoid having to convert before validation and before saving?- I’ve considered making a new “currency”
input
type but am unsure how that would work when saving
- I’ve considered making a new “currency”
Thanks for any advice!