### Complete AshMoney Setup Example Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/CONFIGURATION.md Example demonstrating the complete setup for AshMoney, including dependencies, configuration, repository setup with extensions, and resource definition with money attributes. ```elixir # mix.exs def deps do [ {:ash, "~> 3.0"}, {:ash_money, "~> 0.2"}, {:ash_postgres, "~> 2.0"}, {:ex_money, "~> 6.0"}, {:ex_money_sql, "~> 2.0"}, {:ash_graphql, "~> 1.0"}, ] end # config/config.exs import Config config :ash, known_types: [AshMoney.Types.Money], custom_types: [money: AshMoney.Types.Money] config :localize, locale: "en", timezone: "Etc/UTC" # lib/my_app/repo.ex defmodule MyApp.Repo do use AshPostgres.Repo, otp_app: :my_app def installed_extensions do [AshMoney.AshPostgresExtension] end end # Usage in resource defmodule MyApp.Account do use Ash.Resource, domain: MyApp.Domain attributes do uuid_primary_key :id attribute :balance, :money do constraints [ min: Decimal.new("0"), max: Decimal.new("1000000"), storage_type: :money_with_currency ] end end end ``` -------------------------------- ### Install AshMoney with Igniter Source: https://github.com/ash-project/ash_money/blob/main/documentation/tutorials/getting-started-with-ash-money.md Use the Igniter mix task to install the ash_money dependency. ```bash mix igniter.install ash_money ``` -------------------------------- ### Install AshMoney Dependency Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/README.md Add the AshMoney dependency to your `mix.exs` file to include it in your project. ```elixir def deps do [ {:ash_money, "~> 0.2.6"} ] end ``` -------------------------------- ### Basic AshMoney Setup Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/QUICK_REFERENCE.md Steps to add AshMoney to your project and configure it for use with Ash resources. This includes updating mix.exs and config.exs. ```elixir # 1. Add to mix.exs {:ash_money, "~> 0.2.6"} # 2. Configure in config.exs config :ash, :known_types, [AshMoney.Types.Money] config :ash, :custom_types, money: AshMoney.Types.Money # 3. Use in resource attribute :balance, :money ``` -------------------------------- ### Install AshPostgresExtension in Repo Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/API_REFERENCE.md To use the AshPostgresExtension, include it in the `installed_extensions` list within your AshPostgres Repo configuration. ```elixir defmodule YourApp.Repo do use AshPostgres.Repo def installed_extensions do [ AshMoney.AshPostgresExtension, # ... other extensions ] end end ``` -------------------------------- ### AshMoney Install Mix Task Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/MODULE_STRUCTURE.md Task to add AshMoney to an Ash project. Requires the 'igniter' package. Adds Money types to Ash configuration and optionally runs AshPostgres setup. ```elixir defmodule Mix.Tasks.AshMoney.Install do @moduledoc """ Installation task for adding AshMoney to an Ash project. ## Installation ```bash mix igniter.install ash_money ``` """ @shortdoc "Installs AshMoney" @recursive true @impl true def igniter(args) do Mix.Tasks.AshMoney.Install.run(args) end @impl true def run(args) do AshMoney.Install.run(args) end end ``` -------------------------------- ### Install AshPostgres Extension Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/CONFIGURATION.md Add the AshMoney.AshPostgresExtension to your Repo module to enable PostgreSQL specific features for monetary types. ```elixir defmodule YourApp.Repo do use AshPostgres.Repo, otp_app: :your_app def installed_extensions do [ AshMoney.AshPostgresExtension, # ... other extensions ] end end ``` -------------------------------- ### Test Money Operations Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/ADVANCED_AND_TROUBLESHOOTING.md This example demonstrates how to test various money operations like addition, subtraction, multiplication, and comparison within an ExUnit test. ```elixir defmodule MoneyDebugTest do use ExUnit.Case test "debug money operations" do a = Money.new(:USD, 100) b = Money.new(:USD, 50) {:ok, sum} = Money.add(a, b) IO.inspect(sum, label: "Addition result") {:ok, diff} = Money.sub(a, b) IO.inspect(diff, label: "Subtraction result") {:ok, mult} = Money.mult(a, Decimal.new("2")) IO.inspect(mult, label: "Multiplication result") cmp = Money.compare!(a, b) IO.inspect(cmp, label: "Comparison result") end end ``` -------------------------------- ### Example of Money Validation Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/USAGE_GUIDE.md Demonstrates creating a payment request with valid and invalid amounts, showing successful creation and validation errors. ```elixir # Valid {:ok, request} = PaymentRequest.create(%{ amount: Money.new(:USD, "500.00") }) # Below minimum - validation error {:error, changeset} = PaymentRequest.create(%{ amount: Money.new(:USD, "0.50") }) # Above maximum - validation error {:error, changeset} = PaymentRequest.create(%{ amount: Money.new(:USD, "15000.00") }) ``` -------------------------------- ### AshMoney test/ Directory Structure Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/MODULE_STRUCTURE.md Shows the layout of the test files for AshMoney, including comprehensive tests and test setup. ```text test/ ├── ash_money_test.exs (Comprehensive tests) └── test_helper.exs (Test setup) ``` -------------------------------- ### Money Addition (+) Example Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/EXPRESSIONS_AND_OPERATORS.md Adds two Money values with matching currencies. Ensure currencies match to avoid errors. ```elixir left: %Money{amount: Decimal.new("100"), currency: :USD} right: %Money{amount: Decimal.new("50"), currency: :USD} result: %Money{amount: Decimal.new("150"), currency: :USD} ``` ```elixir import Ash.Expr # In filters filter expr(balance1 + balance2 > Decimal.new("1000")) # In calculations Ash.Query.select(query, [ total: expr(revenue + refunds) ]) ``` ```elixir left = Money.new(:USD, 100) right = Money.new(:EUR, 50) # Returns :unknown (currency mismatch) evaluate_operator(%Ash.Query.Operator.Basic.Plus{left: left, right: right}) # => :unknown ``` -------------------------------- ### Sum Money in AshPostgres Source: https://github.com/ash-project/ash_money/blob/main/documentation/tutorials/getting-started-with-ash-money.md Example of summing monetary values in AshPostgres, filtering by currency code. ```elixir sum :sum_of_usd_balances, :accounts, :balance do filter expr(balance[:currency_code] == "USD") end ``` -------------------------------- ### Calculate Total with Fees Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/EXPRESSIONS_AND_OPERATORS.md Perform arithmetic operations on Money values to calculate totals including fees. This example adds a 5% fee. ```elixir import Ash.Expr Ash.Query.select(query, [ total_with_fees: expr(amount + (amount * Decimal.new("0.05"))) ]) ``` -------------------------------- ### Run Ash Postgres Migrations Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/ADVANCED_AND_TROUBLESHOOTING.md After updating dependencies, run the necessary mix tasks to get dependencies, compile, create a new migration, and apply all pending migrations. ```bash mix deps.get mix deps.compile mix ash_postgres.create_migration mix ash_postgres.migrate ``` -------------------------------- ### Run AshPostgres Migrations Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/ADVANCED_AND_TROUBLESHOOTING.md Generate and run AshPostgres migrations after installing the extension to ensure database functions for the Money type are created. This resolves 'function public.money_gt(...) does not exist' errors. ```bash # Ensure extension is registered # (see above) # Generate and run migrations mix ash_postgres.create_migration mix ash_postgres.migrate ``` -------------------------------- ### Calculate Total Revenue with Addition Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/USAGE_GUIDE.md Perform addition on money attributes to calculate combined values like total revenue. This example selects a computed field. ```elixir import Ash.Expr # Calculate total revenue result = Ash.Query.select(query, [ total: expr(revenue + refunds) ]) ``` -------------------------------- ### Querying AshMoney Attributes Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/QUICK_REFERENCE.md Examples of how to filter and query Ash resources based on Money attributes using Ash.Query and Ash.Expr, including exact matches, comparisons, and range queries. ```elixir import Ash.Expr # Exact match Account |> Ash.Query.filter(expr(balance == Money.new(:USD, 500))) |> Ash.read!() # Comparisons Account |> Ash.Query.filter(expr(balance > Decimal.new("1000"))) |> Ash.read!() Account |> Ash.Query.filter(expr(balance <= Decimal.new("100"))) |> Ash.read!() # By currency (composite access) Account |> Ash.Query.filter(expr(balance[:currency_code] == "USD")) |> Ash.read!() # Range Account |> Ash.Query.filter(expr(balance >= Decimal.new("100") and balance <= Decimal.new("10000"))) |> Ash.read!() # Multiple conditions Account |> Ash.Query.filter(expr(balance > Decimal.new("0") and status == :active)) |> Ash.read!() ``` -------------------------------- ### API Reference Overview Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/MANIFEST.txt The API Reference provides detailed documentation for all public functions within the AshMoney library. Each function includes its full signature with types, a table of parameters (name, type, required, default, description), return type documentation, error handling notes, and complete code examples. It also documents operators, constraint options, and external integrations. ```APIDOC ## API Reference Documentation This section details the public API of the AshMoney library. ### Functions Each function is documented with: - **Full signature with types** - **Parameters**: name, type, required, default, description - **Return type documentation** - **Error handling notes** - **Complete code examples** ### Operators Documentation for supported operators including: - **Type signatures** - **Example usage** - **Error handling** - **Use cases** - **Runtime vs database evaluation** ### Constraint Options Details on available constraint options: - **Type specification** - **Default value** - **Validation rules** - **Real-world examples** - **Combination patterns** ### External Integrations Information on how AshMoney integrates with other libraries and frameworks: - **ex_money library** - **Ash framework** - **AshPostgres** - **AshGraphql** - **AshJsonApi** ### Use Cases Examples of common use cases: - **Fee calculations** - **Currency conversion** - **Money transfers** - **Aggregations (sum, min, max)** - **Property-based testing** - **GraphQL queries** - **JSON API endpoints** - **Postgres operations** ``` -------------------------------- ### Add AshPostgres Extension Source: https://github.com/ash-project/ash_money/blob/main/documentation/tutorials/getting-started-with-ash-money.md Include AshMoney.AshPostgresExtension in your repository's installed extensions to enable database support for money types. ```elixir def installed_extensions do [..., AshMoney.AshPostgresExtension] end ``` -------------------------------- ### JSON API Request Format with Money Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/USAGE_GUIDE.md Example of a JSON API request payload for creating or updating an account, including money attributes. ```json { "data": { "type": "account", "attributes": { "name": "John Doe", "balance": { "amount": "1000.00", "currency": "USD" } } } } ``` -------------------------------- ### Filter by Currency and Amount Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/EXPRESSIONS_AND_OPERATORS.md Filter Money values by both currency code and amount. This example selects USD transactions over $1000. ```elixir import Ash.Expr # USD transactions over $1000 Transaction |> Ash.Query.filter(expr( amount[:currency_code] == "USD" and amount[:amount] > Decimal.new("1000") )) |> Ash.read!() ``` -------------------------------- ### Define Money Attribute in Ash Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/API_REFERENCE.md Demonstrates how to define a 'balance' attribute using AshMoney.Types.Money. Includes examples with aliases and constraints like min/max values and formatting options. ```elixir attribute :balance, AshMoney.Types.Money # Or with alias if configured attribute :balance, :money # With constraints attribute :charge, :money do constraints [ min: Decimal.new("0"), max: Decimal.new("1000"), ex_money_opts: [no_fraction_if_integer: true, format: :short] ] end ``` -------------------------------- ### Filter by Money Amount Comparison Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/USAGE_GUIDE.md Use these examples to filter records based on whether a money attribute is greater than, less than, or within a range of specified amounts. Requires Ash.Expr import. ```elixir import Ash.Expr # Accounts with balance over $1000 Account |> Ash.Query.filter(expr(balance > Decimal.new("1000"))) |> Ash.read!() # Accounts with balance under $100 Account |> Ash.Query.filter(expr(balance < Decimal.new("100"))) |> Ash.read!() # Accounts with balance in range Account |> Ash.Query.filter(expr(balance >= Decimal.new("100") and balance <= Decimal.new("10000"))) |> Ash.read!() ``` -------------------------------- ### Money Binary Subtraction (-) Example Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/EXPRESSIONS_AND_OPERATORS.md Subtracts one Money value from another. Ensure currencies match to avoid errors. ```elixir left: %Money{amount: Decimal.new("100"), currency: :USD} right: %Money{amount: Decimal.new("30"), currency: :USD} result: %Money{amount: Decimal.new("70"), currency: :USD} ``` ```elixir import Ash.Expr # Calculate difference filter expr(balance - min_balance > Decimal.new("100")) # In calculations Ash.Query.select(query, [ profit: expr(revenue - expenses) ]) ``` ```elixir left = Money.new(:USD, 100) right = Money.new(:EUR, 50) # Currency mismatch returns :unknown evaluate_operator(%Ash.Query.Operator.Basic.Minus{left: left, right: right}) # => :unknown ``` -------------------------------- ### JSON API Response Format for Money Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/USAGE_GUIDE.md Example of how money data is represented in a JSON API response, including amount and currency. ```json { "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "balance": { "amount": "1000.00", "currency": "USD" } } } ``` -------------------------------- ### Get Available Constraints for Money Type Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/API_REFERENCE.md Retrieves the list of available constraint options for the AshMoney.Types.Money type. This function returns a keyword list specifying possible configurations. ```elixir def constraints, do: @constraints ``` -------------------------------- ### Configure AshPostgres Extension for Money Type Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/ADVANCED_AND_TROUBLESHOOTING.md Install and register the AshMoney.AshPostgresExtension in your AshPostgres Repo configuration to enable Money type support in Postgres queries. This resolves 'Unknown operator override' and 'money_gt' function errors. ```elixir # lib/your_app/repo.ex defmodule YourApp.Repo do use AshPostgres.Repo def installed_extensions do [AshMoney.AshPostgresExtension] end end # Then generate migrations mix ash_postgres.create_migration ``` -------------------------------- ### Handle Currency Mismatches in Comparisons Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/ADVANCED_AND_TROUBLESHOOTING.md Avoid errors when comparing Money values with different currencies by filtering by currency code first or performing currency conversion before comparison. This example shows filtering by 'USD' before reading transactions. ```elixir import Ash.Expr # ✗ Wrong - different currencies Transaction |> Ash.Query.filter(expr(usd_balance > eur_balance)) |> Ash.read!() # ✓ Correct - filter by currency first usd_transactions = Transaction |> Ash.Query.filter(expr(amount[:currency_code] == "USD")) |> Ash.read!() # ✓ Or perform conversion before comparison # (requires exchange rate lookup) ``` -------------------------------- ### Ash Resource with Money Attributes Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/TYPES.md An example of an Ash resource defining attributes using the :money type. It includes a primary key, a balance, and a credit limit with specific constraints. ```elixir defmodule Account do use Ash.Resource attributes do uuid_primary_key :id attribute :balance, :money attribute :credit_limit, :money do constraints min: Decimal.new("0"), max: Decimal.new("100000") end end end ``` -------------------------------- ### Directly Construct Money Value with Format Options Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/USAGE_GUIDE.md Demonstrates creating a Money value with specific formatting options applied during construction. ```elixir # With format options money = Money.new(:USD, 100, no_fraction_if_integer: true) ``` -------------------------------- ### Discount with Simple Formatting Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/CONFIGURATION.md Set up a discount amount attribute with minimum and maximum value constraints, and enable no_fraction_if_integer formatting. ```elixir attribute :discount_amount, :money do constraints [ min: Decimal.new("0"), max: Decimal.new("500.00"), ex_money_opts: [no_fraction_if_integer: true] ] end ``` -------------------------------- ### Money Unary Negation (-) Example Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/EXPRESSIONS_AND_OPERATORS.md Negates a Money value. Useful for representing debits or expenses. ```elixir value: %Money{amount: Decimal.new("100"), currency: :USD} result: %Money{amount: Decimal.new("-100"), currency: :USD} ``` ```elixir import Ash.Expr # Negate value filter expr(-balance > Decimal.new("0")) # In calculations Ash.Query.select(query, [ debit: expr(-amount) ]) ``` -------------------------------- ### Money Construction with ex_money Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/TYPES.md Demonstrates various ways to construct Money values using the ex_money library. This includes direct construction with currency and amount, and options for formatting. ```elixir # Using ex_money directly Money.new(:USD, 100) Money.new(:USD, "100.50") Money.new(:EUR, Decimal.new("50.00")) # With options Money.new(:USD, 100, no_fraction_if_integer: true) ``` -------------------------------- ### Get Money Storage Type Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/API_REFERENCE.md Returns the configured storage type for the Money type, which can be `:money_with_currency` or `:map`. ```elixir def storage_type(constraints) -> :money_with_currency | :map ``` -------------------------------- ### Creating Records with Money Attributes Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/TYPES.md Demonstrates how to create records for the Account resource, providing values for the money attributes using Money.new. ```elixir # Creating records Account.create!(%{ balance: Money.new(:USD, "1000.00"), credit_limit: Money.new(:USD, "50000.00") }) ``` -------------------------------- ### Directly Construct Money Value with String Currency Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/USAGE_GUIDE.md Demonstrates creating a Money value using an atom for the currency code and a string representing the amount. ```elixir # With string currency code money = Money.new(:USD, "100.50") ``` -------------------------------- ### AshMoney config/ Directory Structure Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/MODULE_STRUCTURE.md Details the location of build-time configuration files for AshMoney. ```text config/ └── config.exs (Build-time configuration) ``` -------------------------------- ### Directly Construct Money Value with Decimal Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/USAGE_GUIDE.md Demonstrates creating a Money value using an atom for the currency code and a Decimal for the amount. ```elixir # With Decimal money = Money.new(:USD, Decimal.new("100.50")) ``` -------------------------------- ### Directly Construct Money Value with Atom Currency Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/USAGE_GUIDE.md Demonstrates creating a Money value using an atom for the currency code and an integer amount. ```elixir # With atom currency code money = Money.new(:USD, 100) ``` -------------------------------- ### Create Resource with Money Value (Map Format) Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/USAGE_GUIDE.md Shows how to create an Ash resource record, providing a Money value using a map format with :currency and :amount keys. ```elixir # Or with map format {:ok, account} = Account.create!(%{ balance: %{currency: :USD, amount: Decimal.new("1000")} }) ``` -------------------------------- ### Money Multiplication (*) Example Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/EXPRESSIONS_AND_OPERATORS.md Multiplies a Money value by a numeric scalar (Decimal, Integer, or Float). Supports commutative multiplication. ```elixir money = %Money{amount: Decimal.new("100"), currency: :USD} scalar = Decimal.new("2") result = %Money{amount: Decimal.new("200"), currency: :USD} ``` ```elixir import Ash.Expr # Scale payment filter expr(unit_price * quantity > Decimal.new("1000")) # Calculate tax Ash.Query.select(query, [ tax: expr(amount * Decimal.new("0.10")) ]) # Discount (commutative) filter expr(Decimal.new("0.9") * price < budget) ``` ```elixir money = Money.new(:USD, 100) # Returns :unknown if multiplier is not numeric evaluate_operator(%Ash.Query.Operator.Basic.Times{left: money, right: "invalid"}) # => :unknown ``` -------------------------------- ### Map Input Formats for Money (String Keys) Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/TYPES.md Shows how to represent Money using maps with string keys, accepting string or integer for the amount. ```elixir %{"currency" => "USD", "amount" => "100.00"} ``` ```elixir %{"currency" => "USD", "amount" => 100} ``` -------------------------------- ### Create Resource with Money Value (Tuple Format) Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/USAGE_GUIDE.md Shows how to create an Ash resource record, providing a Money value using a tuple format {currency, amount}. ```elixir # Or with tuple format {:ok, account} = Account.create!(%{ balance: {:USD, "1000.00"} }) ``` -------------------------------- ### Get Composite Type Field Definitions Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/API_REFERENCE.md Retrieves the field definitions for the composite Money type, including currency and amount. ```elixir def composite_types(_constraints) -> list() # Returns: # [ {:currency, :currency_code, :string, []}, # {:amount, :decimal, []} ] ``` -------------------------------- ### Creating AshMoney Values Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/QUICK_REFERENCE.md Demonstrates various ways to create Money values in Elixir, including from atoms, strings, Decimals, and tuples, as well as how to use them when creating Ash resources. ```elixir # From atom currency Money.new(:USD, 100) Money.new(:USD, "100.50") Money.new(:USD, Decimal.new("100.50")) # With options Money.new(:USD, 100, no_fraction_if_integer: true) # In resources {:ok, account} = Account.create!(%{ balance: Money.new(:USD, "1000.00") }) # From tuple {:ok, account} = Account.create!(%{ balance: {:USD, "1000.00"} }) # From map {:ok, account} = Account.create!(%{ balance: %{currency: :USD, amount: Decimal.new("1000")} }) ``` -------------------------------- ### New Localize Configuration for ex_money 6.0+ Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/CONFIGURATION.md Configure localization using the `localize` package for ex_money version 6.0 and later. This replaces the older `ex_cldr` configuration. ```elixir config :localize, locale: "en", timezone: "Etc/UTC" ``` -------------------------------- ### Add AshPostgres Extension Mix Task Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/CONFIGURATION.md Run this task to add AshMoney's extension to AshPostgres repositories. It detects repos, adds the extension, includes the ex_money_sql dependency, and generates migrations. ```bash mix ash_money.add_to_ash_postgres ``` -------------------------------- ### Create Account with Money in Postgres Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/USAGE_GUIDE.md Insert a new account record into the database using Ash, specifying the balance with currency and amount. ```elixir {:ok, account} = Account.create!(%{ balance: Money.new(:USD, "1000.00") }) ``` -------------------------------- ### Filter by Profit Margin Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/EXPRESSIONS_AND_OPERATORS.md Filter records based on a calculated profit margin. This example finds transactions with a margin greater than 20%. ```elixir import Ash.Expr # Find transactions with > 20% margin Transaction |> Ash.Query.filter(expr( revenue > cost * Decimal.new("1.2") )) |> Ash.read!() ``` -------------------------------- ### Construct Money Values Source: https://github.com/ash-project/ash_money/blob/main/usage-rules.md Create Money values using `Money.new/2` or `Money.new!/2` with currency and amount. ```elixir Money.new(:USD, "100.00") Money.new!("EUR", Decimal.new("50")) ``` -------------------------------- ### Get GraphQL Type Name for Money Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/API_REFERENCE.md Returns the GraphQL type name for the Money type when AshGraphql is available. The default name is :money. ```elixir def graphql_type(_), do: :money ``` -------------------------------- ### Create Resource with Money Value (Atom Currency) Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/USAGE_GUIDE.md Shows how to create an Ash resource record, providing a Money value using an atom currency code and string amount for the balance. ```elixir {:ok, account} = Account.create!(%{ name: "John Doe", balance: Money.new(:USD, "1000.00") }) ``` -------------------------------- ### AshMoney lib/ Directory Structure Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/MODULE_STRUCTURE.md Illustrates the organization of the main source code for AshMoney, including the root module, types, protocols, and extensions. ```text lib/ ├── ash_money.ex (Root module) ├── ash_money/ │ ├── types/ │ │ └── money.ex (Main type implementation) │ ├── comp.ex (Comparable protocol) │ └── ash_postgres_extension.ex (Postgres support) └── mix/ └── tasks/ ├── ash_money.install.ex (Installation task) └── ash_money.add_to_ash_postgres.ex (Postgres setup task) ``` -------------------------------- ### Get GraphQL Input Type Name for Money Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/API_REFERENCE.md Returns the GraphQL input type name for the Money type when AshGraphql is available. The default name is :money_input. ```elixir def graphql_input_type(_), do: :money_input ``` -------------------------------- ### Error Handling for Currency Mismatches in Expressions Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/EXPRESSIONS_AND_OPERATORS.md Expressions involving currency comparisons that are incompatible will raise an Ash.Error.Query.InvalidExpression at query time. This example demonstrates how to catch and handle such errors. ```elixir import Ash.Expr # Currency mismatch - raises at query time try do Resource |> Ash.Query.filter(expr(usd_amount > eur_amount)) |> Ash.read!() rescue e in Ash.Error.Query.InvalidExpression -> # Handle currency incompatibility Logger.error("Currency mismatch: #{inspect(e)}") end ``` -------------------------------- ### Configure Money Formatting Options Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/CONFIGURATION.md Pass options to `Money.new/3` for custom formatting and behavior. This allows control over decimal places, format strings, and separators. ```elixir attribute :display_price, :money do constraints ex_money_opts: [no_fraction_if_integer: true, format: :short] end ``` ```elixir # Display whole numbers without cents attribute :rounded_price, :money do constraints ex_money_opts: [no_fraction_if_integer: true] end ``` ```elixir # Short format for display attribute :display_amount, :money do constraints ex_money_opts: [format: :short] end ``` ```elixir # Multiple options attribute :formatted_balance, :money do constraints ex_money_opts: [ no_fraction_if_integer: true, format: :short, separator: ".", delimiter: "," ] end ``` -------------------------------- ### AshMoney Root Module Documentation Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/MODULE_STRUCTURE.md The root module for AshMoney, providing high-level documentation about the library's purpose and integrations. ```elixir defmodule AshMoney do @moduledoc """ `AshMoney` provides a type for working with money in your Ash resources. It also provides an `AshPostgres.Extension` that can be used to add support for money types and operators to your Postgres database. """ end ``` -------------------------------- ### AshMoney Type Definition Patterns Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/QUICK_REFERENCE.md Examples of defining money attributes in Ash resources, including simple attributes, attributes with validation constraints, formatting options, and custom storage types. ```elixir # Simple attribute attribute :balance, :money # With validation attribute :price, :money do constraints [ min: Decimal.new("0.01"), max: Decimal.new("9999.99") ] end # With formatting attribute :display_price, :money do constraints ex_money_opts: [ no_fraction_if_integer: true, format: :short ] end # With custom storage attribute :balance, :money do constraints storage_type: :map end ``` -------------------------------- ### Configure Money Storage Type (Postgres Composite) Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/ADVANCED_AND_TROUBLESHOOTING.md For Postgres with composite types, ensure `ex_money_sql` is a dependency and configure the attribute to use `storage_type: :money_with_currency`. This is the default and ensures Money values are stored correctly. ```elixir # Ensure ex_money_sql is in dependencies # mix.exs {:ex_money_sql, "~> 2.0"} # Use composite storage (default) attribute :balance, :money do constraints storage_type: :money_with_currency end ``` -------------------------------- ### Create Dynamic Calculations with Money Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/ADVANCED_AND_TROUBLESHOOTING.md Define calculated attributes that perform dynamic calculations using Money operations. This example shows how to calculate tax amount and total price based on subtotal and tax rate. ```elixir defmodule Order do use Ash.Resource attributes do uuid_primary_key :id attribute :subtotal, :money attribute :tax_rate, :decimal, default: Decimal.new("0.08") end calculations do calculate :tax_amount, :money, expr(subtotal * tax_rate) calculate :total, :money, expr(subtotal + calculated(:tax_amount)) end end ``` -------------------------------- ### Money Construction in Ash Expressions Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/TYPES.md Shows how to construct a Money value within Ash expressions using a composite type representation. ```elixir composite_type(%{currency: "USD", amount: Decimal.new("100")}, :money) ``` -------------------------------- ### Account Balance with Range Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/CONFIGURATION.md Configure an account balance attribute with specific minimum and maximum values, and set the storage type to :money_with_currency for PostgreSQL integration. ```elixir attribute :account_balance, :money do constraints [ min: Decimal.new("-10000"), max: Decimal.new("1000000"), storage_type: :money_with_currency ] end ``` -------------------------------- ### Create Transaction with Negative Amount using Negation Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/USAGE_GUIDE.md Demonstrates how to represent negative monetary values for operations like refunds, either directly or using an expression with negation. Requires Ash.Expr import. ```elixir import Ash.Expr # Record refund as negative transaction Transaction.create!(%{ amount: Money.new(:USD, -50), # or amount: expr(-Money.new(:USD, 50)) }) ``` -------------------------------- ### Money Less Than Comparison Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/EXPRESSIONS_AND_OPERATORS.md Demonstrates the less than operator for Money types. Supports Money to Money and Money to Decimal comparisons. ```elixir left = Money.new(:USD, 50) right = Money.new(:USD, 100) result = true # 50 < 100 ``` ```elixir import Ash.Expr # Direct comparison filter expr(balance < minimum_required) ``` ```elixir # With literal decimal filter expr(amount < Decimal.new("100")) ``` ```elixir # Complex expressions filter expr(balance - fees < limit) ``` ```elixir # Find low balance accounts Account |> Ash.Query.filter(expr(balance < Decimal.new("100"))) |> Ash.read!() ``` ```elixir # Find accounts below their limit Account |> Ash.Query.filter(expr(balance < credit_limit)) |> Ash.read!() ``` -------------------------------- ### Efficient Bulk Operations with Expressions Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/ADVANCED_AND_TROUBLESHOOTING.md Execute efficient bulk updates using expressions for operations like applying interest to all accounts or zeroing out negative balances. Requires importing `Ash.Expr`. ```elixir import Ash.Expr # Apply interest to all accounts Account |> Ash.Query.bulk_update(:balance, expr(balance * Decimal.new("1.02"))) |> Ash.bulk_update!() ``` ```elixir import Ash.Expr # Zero out all negative balances Account |> Ash.Query.filter(expr(balance < Decimal.new("0"))) |> Ash.Query.bulk_update(:balance, Money.new(:USD, 0)) |> Ash.bulk_update!() ``` -------------------------------- ### Money Less Than or Equal Comparison Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/EXPRESSIONS_AND_OPERATORS.md Demonstrates the less than or equal to operator for Money types. Supports Money to Money and Money to Decimal comparisons. ```elixir left = Money.new(:USD, 100) right = Money.new(:USD, 100) result = true # 100 <= 100 ``` ```elixir import Ash.Expr filter expr(balance <= limit) ``` ```elixir filter expr(amount <= Decimal.new("1000")) ``` ```elixir filter expr(payment <= available_funds) ``` -------------------------------- ### Update Money Formatting Options for ex_money 6.x+ Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/ADVANCED_AND_TROUBLESHOOTING.md In ex_money 6.x and later, formatting options like locale are handled globally via `Localize.put_locale/1`. The old method of passing `locale` to `Money.new/3` is deprecated. ```elixir # Old style (ex_money 5.x) Money.new(:USD, 100, locale: "en_US") # New style (ex_money 6.x+) Localize.put_locale("en_US") Money.new(:USD, 100) # Or in constraints attribute :amount, :money do constraints ex_money_opts: [format: :short] # Locale is now global via Localize.put_locale/1 end ``` -------------------------------- ### Configure Money Formatting Options Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/ADVANCED_AND_TROUBLESHOOTING.md Control how Money attributes are displayed by specifying formatting options within the attribute definition. Options include displaying whole dollars only, using a short format for UIs, or locale-specific formatting. ```elixir attribute :display_price, :money do constraints ex_money_opts: [no_fraction_if_integer: true] end ``` ```elixir attribute :ui_amount, :money do constraints ex_money_opts: [format: :short] end ``` ```elixir attribute :local_amount, :money do constraints ex_money_opts: [locale: "en_US"] end ``` -------------------------------- ### Production Environment Configuration for AshMoney Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/CONFIGURATION.md Configure AshMoney to be recognized as a known type and a custom type in the production environment. ```elixir config :ash, :known_types, [AshMoney.Types.Money] config :ash, :custom_types, money: AshMoney.Types.Money ``` -------------------------------- ### Configure Known Types for Runtime Expressions Source: https://github.com/ash-project/ash_money/blob/main/documentation/tutorials/getting-started-with-ash-money.md Register AshMoney.Types.Money as a known type to enable runtime operations on money values. ```elixir config :ash, :known_types, [AshMoney.Types.Money] ``` -------------------------------- ### Configure Money Storage Type (Map) Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/ADVANCED_AND_TROUBLESHOOTING.md If not using Postgres or `ex_money_sql`, configure the attribute to use `storage_type: :map` for storing Money values as a map. This is an alternative to composite types. ```elixir # Use map storage attribute :balance, :money do constraints storage_type: :map end ``` -------------------------------- ### AshMoney Add to AshPostgres Mix Task Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/MODULE_STRUCTURE.md Task to set up Money support in existing AshPostgres projects. Requires the 'igniter' package. Adds the AshPostgres extension, dependency, and generates migrations. ```elixir defmodule Mix.Tasks.AshMoney.AddToAshPostgres do @moduledoc """ Postgres setup task for Money support in existing AshPostgres projects. ## Installation ```bash mix ash_money.add_to_ash_postgres [--yes] ``` ## Options * `--yes` - Skip confirmation prompts """ @shortdoc "Adds Money support to AshPostgres" @recursive true @impl true def igniter(args) do Mix.Tasks.AshMoney.AddToAshPostgres.run(args) end @impl true def run(args) do AshMoney.AddToAshPostgres.run(args) end end ``` -------------------------------- ### Ash Money Dependencies Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/CONFIGURATION.md Specify the necessary dependencies for Ash Money and its integration with AshPostgres, including ex_money_sql for :money_with_currency storage. ```elixir def deps do [ {:ash_money, "~> 0.2"}, {:ex_money_sql, "~> 2.0"}, # Required for money_with_currency storage {:ash_postgres, "~> 2.0"}, ] end ``` -------------------------------- ### Map Input Formats for Money (Atom Keys) Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/TYPES.md Represents Money using maps with atom keys for 'currency' and 'amount'. Supports both string and Decimal for the amount. ```elixir %{currency: :USD, amount: Decimal.new("100")} ``` ```elixir %{currency: :USD, amount: "100"} ``` ```elixir %{currency: :USD, amount: 100} ``` -------------------------------- ### Recompile Ash with Force Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/QUICK_REFERENCE.md Use this command to recompile Ash after configuration changes. The `--force` flag ensures a complete recompilation. ```bash mix deps.compile ash --force ``` -------------------------------- ### Configure Ash Money Postgres Extension Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/QUICK_REFERENCE.md Add the AshMoney.AshPostgresExtension to your Repo's installed_extensions. This is required for using Ash Money with Postgres. Remember to generate and run migrations after updating your Repo. ```elixir defmodule YourApp.Repo do use AshPostgres.Repo, otp_app: :your_app def installed_extensions do [AshMoney.AshPostgresExtension] end end # Generate migrations mix ash_postgres.create_migration mix ash_postgres.migrate ``` -------------------------------- ### AshMoney.AshPostgresExtension Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/MODULE_STRUCTURE.md Postgres custom extension providing database-level Money support. ```APIDOC ## AshMoney.AshPostgresExtension ### Description Provides a Postgres custom extension for database-level Money support. This extension adds Money types and operators to your Postgres database, enhancing querying and data manipulation capabilities. ### Availability Only defined when `AshPostgres.CustomExtension` is available. ### Behavior Implemented - `AshPostgres.CustomExtension` - Postgres extension protocol ### Exported Functions - `install/1`: Installs the extension by version. - `uninstall/1`: Uninstalls the extension by version. ### Extension Versions - **Version 0**: Initial release with all operators. - **Version 1**: Add multiplication. - **Version 2**: Add subtraction and negation. - **Version 3**: Fix comparison operators. - **Version 4**: Fix `>=` operator implementation. - **Version 5**: Latest stable (current). ``` -------------------------------- ### Register Custom Money Type Aliases Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/ADVANCED_AND_TROUBLESHOOTING.md Configure custom type aliases for Money in `config/config.exs` to simplify usage across your resources. This allows for predefined currency types like USD and EUR. ```elixir config :ash, :custom_types, money: AshMoney.Types.Money, usd: {:money_type, currency: :USD}, eur: {:money_type, currency: :EUR} ``` ```elixir attribute :balance, :money attribute :usd_price, :usd attribute :eur_price, :eur ``` -------------------------------- ### Development Environment Configuration for AshMoney Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/CONFIGURATION.md Configure AshMoney to be recognized as a known type and a custom type in the development environment. ```elixir config :ash, :known_types, [AshMoney.Types.Money] config :ash, :custom_types, money: AshMoney.Types.Money ``` -------------------------------- ### GraphQL Money Serialization Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/TYPES.md Illustrates the GraphQL output and input types for the Money type when ash_graphql is available. ```graphql type Money { amount: Decimal! currency: String! } ``` ```graphql input MoneyInput { amount: Decimal! currency: String! } ``` -------------------------------- ### Add New Localize Configuration for ex_money Migration Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/ADVANCED_AND_TROUBLESHOOTING.md After removing old CLDR configuration, add the new localize configuration to your config.exs, specifying the default locale and timezone. ```elixir # ✓ Add this config :localize, locale: "en", timezone: "Etc/UTC" ``` -------------------------------- ### Validated Price with Formatting Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/CONFIGURATION.md Define a monetary attribute for product price with minimum and maximum value constraints and specify short formatting options. ```elixir attribute :product_price, :money do constraints [ min: Decimal.new("0.01"), max: Decimal.new("999999.99"), ex_money_opts: [format: :short] ] end ``` -------------------------------- ### Tuple Input Formats for Money Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/TYPES.md Shows different ways to represent Money using tuples, including variations in the amount's data type (string, integer, Decimal). ```elixir {:USD, "100.00"} ``` ```elixir {:USD, 100} ``` ```elixir {:USD, Decimal.new("100.00")} ``` -------------------------------- ### AshMoney.AshPostgresExtension Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/INDEX.md Overview of the AshPostgresExtension, including database functions, operators, and version history. ```APIDOC ## AshMoney.AshPostgresExtension ### Description Provides integration with PostgreSQL, offering database functions and operators specifically designed for the Money type. This extension enhances querying and data manipulation directly within the database. ### Database Functions and Operators This section details the specific SQL functions and operators exposed by the AshPostgresExtension. These allow for efficient handling of monetary values in database queries, such as arithmetic operations, comparisons, and currency conversions directly within PostgreSQL. *(Specific functions and operators are not detailed in the provided source, but would typically include examples like `money_add(value1, value2)`, `money_compare(value1, value2)`, etc.)* ### Version History - **5 versions** of the extension are documented, indicating a history of updates and improvements. ### Examples ```sql -- Example of using a hypothetical money_add operator in PostgreSQL SELECT amount FROM accounts WHERE money_add(balance, 1000) > 5000; ``` ``` -------------------------------- ### GraphQL Mutation to Create Account with Money Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/USAGE_GUIDE.md Mutate account data by providing money details (amount and currency) in the input. ```graphql mutation { createAccount(input: { name: "John Doe" balance: { amount: "1000.00" currency: "USD" } }) { account { id balance { amount currency } } } } ``` -------------------------------- ### Apply Discount with Ash Money Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/QUICK_REFERENCE.md Calculate a discounted price by multiplying the original price by a decimal factor using `expr`. The `Decimal.new` function is used for precise decimal arithmetic. ```elixir discounted = expr(price * Decimal.new("0.9")) ``` -------------------------------- ### Test Environment Configuration for AshMoney Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/CONFIGURATION.md Configure AshMoney to be recognized as a known type in the test environment. ```elixir config :ash, :known_types, [AshMoney.Types.Money] ``` -------------------------------- ### Run Ash Postgres Migrations Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/QUICK_REFERENCE.md Applies pending database migrations generated by Ash Postgres. Ensure migrations are created before running this command. ```bash mix ash_postgres.migrate ``` -------------------------------- ### ex_money_opts Constraint for Money Attribute Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/TYPES.md Passes options to Money.new/3 for custom formatting and behavior, such as suppressing fractions for integers or using short format. ```elixir attribute :display_amount, :money do constraints ex_money_opts: [ no_fraction_if_integer: true, # "100" instead of "100.00" format: :short # "-US$100" instead of "-$100.00 USD" ] end ``` -------------------------------- ### Money Attribute Constraints Source: https://github.com/ash-project/ash_money/blob/main/usage-rules.md Apply constraints like min/max decimal bounds and pass ex_money options to the attribute definition. ```elixir attribute :charge, :money do constraints min: Decimal.new("0"), max: Decimal.new("1000") end ``` -------------------------------- ### Implement Account Balance Transfer with Ash Actions Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/USAGE_GUIDE.md Use Ash actions to define complex business logic, such as transferring funds between accounts, ensuring atomicity and data integrity. ```elixir defmodule MyApp.TransferFunds do use Ash.Resource, domain: MyApp.Domain actions do action :execute do argument :from_id, :uuid, allow_nil?: false argument :to_id, :uuid, allow_nil?: false argument :amount, AshMoney.Types.Money, allow_nil?: false run(fn input, _context -> from = Account.read!(input.arguments.from_id) to = Account.read!(input.arguments.to_id) amount = input.arguments.amount with {:ok, updated_from} <- Account.update(from, %{ balance: expr(balance - ^amount) }), {:ok, updated_to} <- Account.update(to, %{ balance: expr(balance + ^amount) }) do {:ok, %{from: updated_from, to: updated_to}} end end) end end end ``` -------------------------------- ### Configure AshMoney Types Source: https://github.com/ash-project/ash_money/blob/main/_autodocs/README.md Configure Ash to recognize the Money type by adding it to `known_types` and `custom_types` in your `config/config.exs`. ```elixir config :ash, :known_types, [AshMoney.Types.Money] config :ash, :custom_types, money: AshMoney.Types.Money ```