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

marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
JulienCorb
I am trying to implement my new.html.eex file to create new posts on my website. new.html.eex: &lt;h1&gt;Create Post&lt;/h1&gt; &lt;%= ...
New
jaysoifer
Is there a way to rollback a specific migration and only that one (“skipping” all the other ones)? Would mix ecto.rollback -v 200809061...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
marick
I had some trouble figuring out how to make many-to-many associations work. Once I got it working, I wrote a blog post. Because I’m a nov...
New

Other popular topics Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 42920 311
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 41539 114
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 43622 214
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a &gt; b) do {:ok, "a"} end if (a &lt; b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36128 110
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement