hudsonbay

hudsonbay

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 :frowning:

Marked As Solved

kip

kip

ex_cldr Core Team

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).

Also Liked

kip

kip

ex_cldr Core Team

I have already implemented:

iex> Money.Financial.
future_value/2               future_value/3               
interest_rate/3              internal_rate_of_return/1    
net_present_value/2          net_present_value/3          
net_present_value/4          payment/3                    
periods/3                    present_value/2              
present_value/3 

I’ll check against what you require.

hudsonbay

hudsonbay

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 :slight_smile:

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.

amnu3387

amnu3387

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).

Where Next?

Popular in Questions Top

vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
Kagamiiiii
Student &amp; New to elixir. Nice language. I want to convert a english character, e.g. “a”, which is stored in a variable, to it’s asci...
New
dokuzbir
Hello, I am trying to convert my lists to string without losing brackets.For start i have 3 map. They look like these buyer = %{ id: ...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
kostonstyle
Hi all I want to have a unix time, from the current time plus 1 hour. DateTime.now + 1 hour How to get it in elixir? Thanks
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
idi527
I’ve been re-reading swift book again and noticed that multiline strings there don’t have a trailing line break, unlike in elixir iex(2)...
New

Other popular topics Top

lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New

We're in Beta

About us Mission Statement