Yes, I am using latest versions for all Ash packages.
ash_archival 1.0.4
ash_authentication 4.3.9
ash_authentication_phoenix 2.4.4
ash_double_entry 1.0.6
ash_money 0.1.15
ash_paper_trail 0.4.0
ash_postgres 2.4.20
ash_sql 0.2.43
ash 3.4.50 3.4.51
ash_phoenix 2.1.13
REPRODUCTION
Failing Tests
defmodule MyApp.Ledger.LedgerAccountsLiveTest do
alias MyApp.Ledger.{Account, AccountType, Transfer}
use MyAppWeb.ConnCase, async: false
import LedgerCase
def get_accounts(tenant) do
case Ash.read(Account, tenant: tenant, authorize?: false) do
{:ok, []} -> create_accounts(tenant)
{:ok, accounts} -> accounts
end
end
def create_accounts(tenant) do
# Account type are automatically seeded when testing starts.
account_type = Ash.read_first!(AccountType, tenant: tenant, authorize?: false)
attrs = [
%{
identifier: "1000",
account_number: "1000",
name: "CASH and BANK",
currency: :USD,
account_type_id: account_type.id
},
%{
identifier: "1010",
account_number: "1010",
currency: :USD,
name: "CASH and BANK - NCBA",
account_type_id: account_type.id
},
%{
identifier: "1020",
account_number: "1020",
currency: :USD,
name: "CASH and BANK - OPEN FLOAT",
account_type_id: account_type.id
}
]
Ash.Seed.seed!(Account, attrs, tenant: tenant)
end
describe "Ledger Accounts" do
test "Transfer between 2 accounts happens successfully", %{user: actor} do
accounts = get_accounts(actor.current_tenant)
account_1 = Enum.at(accounts, 1)
account_2 = Enum.at(accounts, 2)
attrs = %{
amount: Money.new!(20, :USD),
from_account_id: account_1.id,
to_account_id: account_2.id
}
opts = [actor: actor, tenant: actor.current_tenant]
Transfer
|> Ash.Changeset.for_create(:transfer, attrs, opts)
|> Ash.create()
|> dbg()
end
end
end
Account Resource
defmodule MyApp.Ledger.Account do
use Ash.Resource,
domain: MyApp.Ledger,
authorizers: [Ash.Policy.Authorizer],
data_layer: AshPostgres.DataLayer,
notifiers: [Ash.Notifier.PubSub],
extensions: [AshPaperTrail.Resource, AshDoubleEntry.Account]
postgres do
table "ledger_accounts"
repo MyApp.Repo
end
paper_trail do
belongs_to_actor :user, MyApp.Auth.User, domain: MyApp.Auth
store_action_name? true
end
account do
transfer_resource(MyApp.Ledger.Transfer)
balance_resource(MyApp.Ledger.Balance)
open_action_accept([:account_number, :name, :account_type_id])
end
policies do
policy always() do
access_type :strict
authorize_if MyApp.Auth.Checks.Can
end
end
pub_sub do
module MyAppWeb.Endpoint
prefix "ledger_accounts"
publish_all :update, [[:id, nil]]
publish_all :create, [[:id, nil]]
publish_all :destroy, [[:id, nil]]
end
preparations do
prepare MyApp.Preparations.SetTenantFromActor
end
changes do
change MyApp.Changes.SetTenantFromActor
end
multitenancy do
strategy :context
end
attributes do
attribute :account_number, :string, allow_nil?: false
attribute :name, :string, allow_nil?: false
attribute :tax_rate, :decimal, default: 0
end
relationships do
belongs_to :account_type, MyApp.Ledger.AccountType do
source_attribute :account_type_id
end
belongs_to :parent_account, MyApp.Ledger.Account do
source_attribute :parent_account_id
destination_attribute :id
allow_nil? true
end
has_many :sub_accounts, MyApp.Ledger.Account do
destination_attribute :parent_account_id
source_attribute :id
end
end
end
Transfer resource
defmodule MyApp.Ledger.Transfer do
use Ash.Resource,
domain: MyApp.Ledger,
authorizers: [Ash.Policy.Authorizer],
data_layer: AshPostgres.DataLayer,
notifiers: [Ash.Notifier.PubSub],
extensions: [AshPaperTrail.Resource, AshDoubleEntry.Transfer]
postgres do
table "ledger_transfers"
repo MyApp.Repo
end
paper_trail do
belongs_to_actor :user, MyApp.Auth.User, domain: MyApp.Auth
store_action_name? true
end
transfer do
account_resource(MyApp.Ledger.Account)
balance_resource(MyApp.Ledger.Balance)
destroy_balances?(true)
end
policies do
policy always() do
access_type :strict
authorize_if MyApp.Auth.Checks.Can
end
end
pub_sub do
module MyAppWeb.Endpoint
prefix "ledger_transfers"
publish_all :update, [[:id, nil]]
publish_all :create, [[:id, nil]]
publish_all :destroy, [[:id, nil]]
end
preparations do
prepare MyApp.Preparations.SetTenantFromActor
end
changes do
change MyApp.Changes.SetTenantFromActor
end
multitenancy do
strategy :context
end
end