# Best way to multiply Money values several times

Hi, hello. I’m trying to implement a formula which involves `Money` types. I’m using `ex_money` library to deal with this. I have to calculate this on my `changeset`. The formula is:

``````annual_amortization = amount * dedicated_percentage * depreciation_rate * initial_value / 10_000
``````

That should return me a `Money` value.

This is how I’m defining this:

``````schema "intangible_fixed_assets_processes" do
field :amount, :integer
field :annual_amortization, Money.Ecto.Composite.Type
field :dedicated_percentage, :decimal
field :depreciation_rate, :decimal
field :initial_value, Money.Ecto.Composite.Type

......
end
``````

My changeset:

``````def changeset(intangible_fixed_asset_process, attrs) do
...
|> calculate_annual_amortization()
end
``````

And my `calculate_annual_amortization` function:

``````def calculate_annual_amortization(changeset) do
amount = get_change(changeset, :amount)
dedicated_percentage = get_change(changeset, :dedicated_percentage)
initial_value = get_change(changeset, :initial_value)
depreciation_rate = get_change(changeset, :depreciation_rate)

result = initial_value
|> Money.mult(amount)
|> Money.mult(dedicated_percentage)
|> Money.mult(depreciation_rate)
|> Money.div!(10_000)

with {:ok, annual_amortization} <- result do
put_change(changeset, :annual_amortization, annual_amortization)
end
end
``````

But, that function gives obvious argument errors because the result of multiplying `Money` types is like this:

``````iex> Money.mult(Money.new(:USD, 200), 2)
{:ok, Money.new(:USD, 400)}    <--------------------------
``````

Actually, the argument errors will start here ` |> Money.mult(dedicated_percentage)`

I’m not very experienced with Elixir or `ex_money`. Does anyone know which is the most elegant way to calculate this? Actually, I can’t even figure out a non-elegant way either

Well, I found a way that works. I’m going to share it here in case someone needs it. What I did was to convert the `Money` types into `Decimal` types to perform the arithmetic operations well. And then convert to `Money` again. If someone has a different opinion on what is best way, feel free to discuss

``````def calculate_annual_amortization(changeset) do
amount = get_change(changeset, :amount)
dedicated_percentage = get_change(changeset, :dedicated_percentage)
initial_value = get_change(changeset, :initial_value) |> Money.to_decimal()
depreciation_rate = get_change(changeset, :depreciation_rate)

result =
initial_value
|> Decimal.mult(amount)
|> Decimal.mult(dedicated_percentage)
|> Decimal.mult(depreciation_rate)
|> Decimal.div(10_000)
|> Money.new(:CUP)

put_change(changeset, :annual_amortization, result)
end
``````

This does the job and assigns the value correctly.

1 Like

Not particularly anything on “best way” but just another idea, you were already using `with` on the initial sample so you could use it to pattern match the result of the `Money` ops. Or if `Money` has a `!` operator available then you could use that instead to return unwrapped values.

``````def calculate_annual_amortization(changeset) do
with(
amount <- get_change(changeset, :amount),
dedicated_percentage <- get_change(changeset, :dedicated_percentage),
initial_value <- get_change(changeset, :initial_value),
depreciation_rate <- get_change(changeset, :depreciation_rate),
{:ok, money_1} <- Money.mult(initial_value, amount),
{:ok, money_2} <- Money.mult(money_1, dedicated_percentage),
{:ok, money_3} <- Money.mult(money_2, depreciation_rate),
{:ok, annual_amortization} <- Money.div(money_3, 10_000)
) do
put_change(changeset, :annual_amortization, annual_amortization)

else
_ ->
add_error(changeset, :annual_amortization, "unable to calc annual_amortization")
end
end
``````

I’m assuming that the fields you’re fetching with `get_change` are always `changed` at this point, but if not or you didn’t consider it you might want to change them to `fetch_field` which always gives back the value (in order #1 the `{:changes, value}` if changed on the changeset, #2 `{:data, existing_value}` if unchanged on the changeset, and #3 `:error` if the field doesn’t exist)
On the `with` you could then use `{_, value} <- fetch_field(changeset, :amount)`.

1 Like

You could also avail yourself of the bang versions of the arithmetic functions. Your pipeline would then become quite straight forward:

``````    result = initial_value
|> Money.mult!(amount)
|> Money.mult!(dedicated_percentage)
|> Money.mult!(depreciation_rate)
|> Money.div!(10_000)
``````

But given there is already a several functions in `Money.Financial` I’ll happily add `Money.Financial.amortization/3` as well in the next version to make it even easier (I’m the author of `ex_money`).

4 Likes

OHHHH!!! How could I not see the bang! functions??? That’s the way I was looking for. Why? Because, as you say, it’s very straight forward. I think @amnu3387’s is also good without the use of !. Thanks for the feedback and the time spent

Thanks a lot. Great work with `ex_money`

Just take into account that there are different ways (formulas) to calculate amortization. In my case, that’s how they I’m being asked to calculate it but I’ve seen different ways depending on the case

Thanks for pointing me this out

``````iex> Money.Financial.