kamaroly
How to Use Ash Calculations to Sum total of list items Without Hitting the Data Layer
I want to calculate the sub_total for a sale (sum of line_items.line_total) without querying the data layer. how can I achieve that?
Here’s my current setup for the sub_total calculation in the Sale resource:
calculations do
calculate :sub_total, :decimal, expr(sum(line_items.line_total)) do
description "Sub total amount for the sale before discounts or taxes"
public? true
end
end`
I’d like to calculate the sub_total using Ash.calculate!/3 by passing line_items data in-memory, like this:
sale_attrs = %{line_items: [%{line_total: 12}, %{line_total: 15}]}
Ash.calculate!(Sale, :sub_total, refs: sale_attrs)`
For context, I’m already doing something similar for line_total in the SaleLineItem resource, which works without hitting the data layer:
calculations do
calculate :line_total, :decimal, expr(quantity * price) do
public? true
description "Total amount for the line item"
end
end`
# I can compute line_total like this:
line_item_attrs = %{quantity: 2, price: 10}
Ash.calculate!(SaleLineItem, :line_total, refs: line_item_attrs)`
How can I configure the sub_total calculation to work with in-memory data (sale_attrs) and avoid hitting the data layer, similar to my line_total calculation?
Marked As Solved
kamaroly
It was simpler than I thought! I followed the contract of extracting a calculation in its module and it worked.
# MyApp.Sales.Sale resource
calculations do
calculate :sub_total, :decimal, MyApp.Sales.Sale.Calculations.SubTotal
end
Calculation Module
defmodule MyApp.Sales.Sale.Calculations.SubTotal do
use Ash.Resource.Calculation
def description, do: "Calculates the sub total of a sale."
def public?, do: true
@impl true
def load(_query, opts, _context) do
[line_items: [:line_total, :quantity, :price]]
end
@impl true
def calculate(sales, _opts, _arguments) do
Enum.map(sales, &calculate_sub_total/1)
end
defp calculate_sub_total(%{line_items: line_items}) do
line_items
|> Enum.map(& &1.line_total)
|> Enum.reduce(Decimal.new(0), &Decimal.add/2)
end
end
Now I am able to successful calculate subtotal without hitting the DB
sale_attrs = %{line_items: [%{line_total: 12}, %{line_total: 15}]}
Ash.calculate!(Zippiker.Sales.Sale, :sub_total, refs: sale_attrs)
# Decimal.new("27")
Also Liked
kamaroly
I just wrote an article about this. You can read it here: https://medium.com/@lambert.kamaro/how-to-reuse-business-logic-with-ash-calculations-andsave-time-like-a-pro-5eb95388c80d
zachdaniel
calculate :sub_total, :decimal, expr(sum(line_items, field: :line_total)) do
description "Sub total amount for the sale before discounts or taxes"
public? true
end
the field is separate from the relationship path
zachdaniel
You can set tenant when running calculations, even if its not actually necessary for the calculation to run.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









