Failz
Failz
AEAsh Elixir
Created by Failz on 5/25/2025 in #support
ash_double_entry and updating transfers
looks happy!
40 replies
AEAsh Elixir
Created by Failz on 5/25/2025 in #support
ash_double_entry and updating transfers
you're a step ahead of me, I was trying to think through what gets broken if I set atomic to false. thank you for the help here!
40 replies
AEAsh Elixir
Created by Failz on 5/25/2025 in #support
ash_double_entry and updating transfers
40 replies
AEAsh Elixir
Created by Failz on 5/25/2025 in #support
ash_double_entry and updating transfers
correct:
defmodule App.Ledger do
use Ash.Domain,
otp_app: :app,
extensions: [AshPhoenix]

resources do
resource App.Ledger.Account do
define :list_accounts, action: :read
define :get_account, action: :read, get_by: [:id]
define :open_account, action: :open
define :update_account, action: :update
define :delete_account, action: :destroy
end

resource App.Ledger.Balance

resource App.Ledger.Transfer do
define :list_transfers, action: :read
define :get_transfer, action: :read, get_by: [:id]
define :create_transfer, action: :transfer
define :update_transfer, action: :update
end
end
end
defmodule App.Ledger do
use Ash.Domain,
otp_app: :app,
extensions: [AshPhoenix]

resources do
resource App.Ledger.Account do
define :list_accounts, action: :read
define :get_account, action: :read, get_by: [:id]
define :open_account, action: :open
define :update_account, action: :update
define :delete_account, action: :destroy
end

resource App.Ledger.Balance

resource App.Ledger.Transfer do
define :list_transfers, action: :read
define :get_transfer, action: :read, get_by: [:id]
define :create_transfer, action: :transfer
define :update_transfer, action: :update
end
end
end
and
defmodule App.Ledger.Transfer do
use Ash.Resource,
domain: Elixir.App.Ledger,
data_layer: AshPostgres.DataLayer,
extensions: [AshDoubleEntry.Transfer]

transfer do
account_resource App.Ledger.Account
balance_resource App.Ledger.Balance
end

postgres do
table "ledger_transfers"
repo App.Repo
end

actions do
defaults [:read]

create :transfer do
accept [:amount, :timestamp, :from_account_id, :to_account_id]
end

update :update do
accept [:amount]
end
end

attributes do
attribute :id, AshDoubleEntry.ULID do
primary_key? true
allow_nil? false
default &AshDoubleEntry.ULID.generate/0
end

attribute :amount, :money do
allow_nil? false
end

timestamps()
end

relationships do
belongs_to :from_account, App.Ledger.Account do
attribute_writable? true
end

belongs_to :to_account, App.Ledger.Account do
attribute_writable? true
end

has_many :balances, App.Ledger.Balance
end
end
defmodule App.Ledger.Transfer do
use Ash.Resource,
domain: Elixir.App.Ledger,
data_layer: AshPostgres.DataLayer,
extensions: [AshDoubleEntry.Transfer]

transfer do
account_resource App.Ledger.Account
balance_resource App.Ledger.Balance
end

postgres do
table "ledger_transfers"
repo App.Repo
end

actions do
defaults [:read]

create :transfer do
accept [:amount, :timestamp, :from_account_id, :to_account_id]
end

update :update do
accept [:amount]
end
end

attributes do
attribute :id, AshDoubleEntry.ULID do
primary_key? true
allow_nil? false
default &AshDoubleEntry.ULID.generate/0
end

attribute :amount, :money do
allow_nil? false
end

timestamps()
end

relationships do
belongs_to :from_account, App.Ledger.Account do
attribute_writable? true
end

belongs_to :to_account, App.Ledger.Account do
attribute_writable? true
end

has_many :balances, App.Ledger.Balance
end
end
40 replies
AEAsh Elixir
Created by Failz on 5/25/2025 in #support
ash_double_entry and updating transfers
I added back in the inspects
40 replies
AEAsh Elixir
Created by Failz on 5/25/2025 in #support
ash_double_entry and updating transfers
now seeing the :not_atomic coming back
vscode ➜ /workspaces/yakgo.money (main ✗) $ iex -S mix
Erlang/OTP 27 [erts-15.2.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit]

Interactive Elixir (1.18.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> transfer = App.Ledger.get_transfer!("01JW3TNT6M4PW7QWSTY5KSPT19")
[debug] %Postgrex.Query{ref: nil, name: "ecto_1154", statement: "SELECT l0.\"id\", l0.\"timestamp\", l0.\"amount\", l0.\"updated_at\", l0.\"inserted_at\", l0.\"from_account_id\", l0.\"to_account_id\" FROM \"ledger_transfers\" AS l0 WHERE (l0.\"id\"::bytea::bytea = $1::bytea::bytea)", param_oids: nil, param_formats: nil, param_types: nil, columns: nil, result_oids: nil, result_formats: nil, result_types: nil, types: nil, cache: :reference} uses unknown oid(s) 16792forcing us to reload type information from the database. This is expected behaviour whenever you migrate your database.
[debug] QUERY OK source="ledger_transfers" db=0.2ms queue=3.0ms idle=315.9ms
SELECT l0."id", l0."timestamp", l0."amount", l0."updated_at", l0."inserted_at", l0."from_account_id", l0."to_account_id" FROM "ledger_transfers" AS l0 WHERE (l0."id"::bytea::bytea = $1::bytea::bytea) ["01JW3TNT6M4PW7QWSTY5KSPT19"]
↳ anonymous fn/3 in AshPostgres.DataLayer.run_query/2, at: lib/data_layer.ex:786
%App.Ledger.Transfer{
balances: #Ash.NotLoaded<:relationship, field: :balances>,
to_account: #Ash.NotLoaded<:relationship, field: :to_account>,
from_account: #Ash.NotLoaded<:relationship, field: :from_account>,
__meta__: #Ecto.Schema.Metadata<:loaded, "ledger_transfers">,
id: "01JW3TNT6M4PW7QWSTY5KSPT19",
amount: Money.new(:USD, "20"),
inserted_at: ~U[2025-05-25 13:38:50.709890Z],
updated_at: ~U[2025-05-25 13:38:50.709890Z],
timestamp: ~U[2025-05-25 13:38:50.709837Z],
from_account_id: "0197078c-7b11-7b5f-a00b-1ec0996ba0b8",
to_account_id: "0197078f-1092-7c1e-88e9-1cf553432e7a"
}
iex(2)> App.Ledger.update_transfer(transfer, %{amount: Money.new(10, :USD)})
changeset: #Ash.Changeset<
domain: App.Ledger,
action_type: :update,
action: :update,
attributes: %{amount: Money.new(:USD, "10")},
relationships: %{},
errors: [],
data: %App.Ledger.Transfer{
balances: #Ash.NotLoaded<:relationship, field: :balances>,
to_account: #Ash.NotLoaded<:relationship, field: :to_account>,
from_account: #Ash.NotLoaded<:relationship, field: :from_account>,
__meta__: #Ecto.Schema.Metadata<:loaded, "ledger_transfers">,
id: "01JW3TNT6M4PW7QWSTY5KSPT19",
amount: Money.new(:USD, "20"),
inserted_at: ~U[2025-05-25 13:38:50.709890Z],
updated_at: ~U[2025-05-25 13:38:50.709890Z],
timestamp: ~U[2025-05-25 13:38:50.709837Z],
from_account_id: "0197078c-7b11-7b5f-a00b-1ec0996ba0b8",
to_account_id: "0197078f-1092-7c1e-88e9-1cf553432e7a"
},
valid?: true
>
opts: []
context: %Ash.Resource.Change.Context{
actor: nil,
tenant: nil,
authorize?: true,
tracer: nil,
bulk?: false
}
{:error,
%Ash.Error.framework{
errors: [
%Ash.Error.Framework.MustBeAtomic{
resource: App.Ledger.Transfer,
action: :update,
reason: "Cannot destroy a transfer atomically if balances must be destroyed manually, or if balance is being updated",
splode: Ash.Error,
bread_crumbs: [],
vars: [],
path: [],
stacktrace: #Splode.Stacktrace<>,
class: :framework
}
]
}}
iex(3)>
vscode ➜ /workspaces/yakgo.money (main ✗) $ iex -S mix
Erlang/OTP 27 [erts-15.2.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit]

Interactive Elixir (1.18.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> transfer = App.Ledger.get_transfer!("01JW3TNT6M4PW7QWSTY5KSPT19")
[debug] %Postgrex.Query{ref: nil, name: "ecto_1154", statement: "SELECT l0.\"id\", l0.\"timestamp\", l0.\"amount\", l0.\"updated_at\", l0.\"inserted_at\", l0.\"from_account_id\", l0.\"to_account_id\" FROM \"ledger_transfers\" AS l0 WHERE (l0.\"id\"::bytea::bytea = $1::bytea::bytea)", param_oids: nil, param_formats: nil, param_types: nil, columns: nil, result_oids: nil, result_formats: nil, result_types: nil, types: nil, cache: :reference} uses unknown oid(s) 16792forcing us to reload type information from the database. This is expected behaviour whenever you migrate your database.
[debug] QUERY OK source="ledger_transfers" db=0.2ms queue=3.0ms idle=315.9ms
SELECT l0."id", l0."timestamp", l0."amount", l0."updated_at", l0."inserted_at", l0."from_account_id", l0."to_account_id" FROM "ledger_transfers" AS l0 WHERE (l0."id"::bytea::bytea = $1::bytea::bytea) ["01JW3TNT6M4PW7QWSTY5KSPT19"]
↳ anonymous fn/3 in AshPostgres.DataLayer.run_query/2, at: lib/data_layer.ex:786
%App.Ledger.Transfer{
balances: #Ash.NotLoaded<:relationship, field: :balances>,
to_account: #Ash.NotLoaded<:relationship, field: :to_account>,
from_account: #Ash.NotLoaded<:relationship, field: :from_account>,
__meta__: #Ecto.Schema.Metadata<:loaded, "ledger_transfers">,
id: "01JW3TNT6M4PW7QWSTY5KSPT19",
amount: Money.new(:USD, "20"),
inserted_at: ~U[2025-05-25 13:38:50.709890Z],
updated_at: ~U[2025-05-25 13:38:50.709890Z],
timestamp: ~U[2025-05-25 13:38:50.709837Z],
from_account_id: "0197078c-7b11-7b5f-a00b-1ec0996ba0b8",
to_account_id: "0197078f-1092-7c1e-88e9-1cf553432e7a"
}
iex(2)> App.Ledger.update_transfer(transfer, %{amount: Money.new(10, :USD)})
changeset: #Ash.Changeset<
domain: App.Ledger,
action_type: :update,
action: :update,
attributes: %{amount: Money.new(:USD, "10")},
relationships: %{},
errors: [],
data: %App.Ledger.Transfer{
balances: #Ash.NotLoaded<:relationship, field: :balances>,
to_account: #Ash.NotLoaded<:relationship, field: :to_account>,
from_account: #Ash.NotLoaded<:relationship, field: :from_account>,
__meta__: #Ecto.Schema.Metadata<:loaded, "ledger_transfers">,
id: "01JW3TNT6M4PW7QWSTY5KSPT19",
amount: Money.new(:USD, "20"),
inserted_at: ~U[2025-05-25 13:38:50.709890Z],
updated_at: ~U[2025-05-25 13:38:50.709890Z],
timestamp: ~U[2025-05-25 13:38:50.709837Z],
from_account_id: "0197078c-7b11-7b5f-a00b-1ec0996ba0b8",
to_account_id: "0197078f-1092-7c1e-88e9-1cf553432e7a"
},
valid?: true
>
opts: []
context: %Ash.Resource.Change.Context{
actor: nil,
tenant: nil,
authorize?: true,
tracer: nil,
bulk?: false
}
{:error,
%Ash.Error.framework{
errors: [
%Ash.Error.Framework.MustBeAtomic{
resource: App.Ledger.Transfer,
action: :update,
reason: "Cannot destroy a transfer atomically if balances must be destroyed manually, or if balance is being updated",
splode: Ash.Error,
bread_crumbs: [],
vars: [],
path: [],
stacktrace: #Splode.Stacktrace<>,
class: :framework
}
]
}}
iex(3)>
40 replies
AEAsh Elixir
Created by Failz on 5/25/2025 in #support
ash_double_entry and updating transfers
do I need to set either of these?
# balance.ex
postgres do
table "balances"
repo YourApp.Repo

references do
reference :transfer, on_delete: :delete
end
end

# or
# transfer.ex
transfer do
account_resource App.Ledger.Account
balance_resource App.Ledger.Balance
destroy_balances? true
end
# balance.ex
postgres do
table "balances"
repo YourApp.Repo

references do
reference :transfer, on_delete: :delete
end
end

# or
# transfer.ex
transfer do
account_resource App.Ledger.Account
balance_resource App.Ledger.Balance
destroy_balances? true
end
40 replies
AEAsh Elixir
Created by Failz on 5/25/2025 in #support
ash_double_entry and updating transfers
where does :skip_balance_updates come from? I don't see this in my IO.inspect output
changeset.context[:ash_double_entry][:skip_balance_updates]
changeset.context[:ash_double_entry][:skip_balance_updates]
40 replies
AEAsh Elixir
Created by Failz on 5/25/2025 in #support
ash_double_entry and updating transfers
vscode ➜ /workspaces/yakgo.money (main ✗) $ mix hex.outdated
Dependency Current Latest Status
ash 3.5.12 3.5.12 Up-to-date
ash_admin 0.13.5 0.13.5 Up-to-date
ash_authentication 4.8.7 4.8.7 Up-to-date
ash_authentication_phoenix 2.7.0 2.7.0 Up-to-date
ash_double_entry 1.0.14 1.0.14 Up-to-date
ash_money 0.2.0 0.2.0 Up-to-date
ash_paper_trail 0.5.3 0.5.3 Up-to-date
ash_phoenix 2.3.2 2.3.2 Up-to-date
ash_postgres 2.5.22 2.5.22 Up-to-date
bandit 1.6.11 1.6.11 Up-to-date
bcrypt_elixir 3.3.2 3.3.2 Up-to-date
dns_cluster 0.1.3 0.2.0 Update not possible
ecto_sql 3.12.1 3.12.1 Up-to-date
esbuild 0.9.0 0.9.0 Up-to-date
ex_money_sql 1.11.0 1.11.0 Up-to-date
finch 0.19.0 0.19.0 Up-to-date
floki 0.37.1 0.37.1 Up-to-date
gettext 0.26.2 0.26.2 Up-to-date
igniter 0.6.2 0.6.2 Up-to-date
jason 1.4.4 1.4.4 Up-to-date
live_debugger 0.2.3 0.2.3 Up-to-date
phoenix 1.7.21 1.7.21 Up-to-date
phoenix_ecto 4.6.4 4.6.4 Up-to-date
phoenix_html 4.2.1 4.2.1 Up-to-date
phoenix_live_dashboard 0.8.7 0.8.7 Up-to-date
phoenix_live_reload 1.6.0 1.6.0 Up-to-date
phoenix_live_view 1.0.12 1.0.12 Up-to-date
picosat_elixir 0.2.3 0.2.3 Up-to-date
postgrex 0.20.0 0.20.0 Up-to-date
sourceror 1.10.0 1.10.0 Up-to-date
swoosh 1.19.1 1.19.1 Up-to-date
tailwind 0.2.4 0.3.1 Update not possible
telemetry_metrics 1.1.0 1.1.0 Up-to-date
telemetry_poller 1.2.0 1.2.0 Up-to-date
tidewave 0.1.6 0.1.7 Update possible
vscode ➜ /workspaces/yakgo.money (main ✗) $ mix hex.outdated
Dependency Current Latest Status
ash 3.5.12 3.5.12 Up-to-date
ash_admin 0.13.5 0.13.5 Up-to-date
ash_authentication 4.8.7 4.8.7 Up-to-date
ash_authentication_phoenix 2.7.0 2.7.0 Up-to-date
ash_double_entry 1.0.14 1.0.14 Up-to-date
ash_money 0.2.0 0.2.0 Up-to-date
ash_paper_trail 0.5.3 0.5.3 Up-to-date
ash_phoenix 2.3.2 2.3.2 Up-to-date
ash_postgres 2.5.22 2.5.22 Up-to-date
bandit 1.6.11 1.6.11 Up-to-date
bcrypt_elixir 3.3.2 3.3.2 Up-to-date
dns_cluster 0.1.3 0.2.0 Update not possible
ecto_sql 3.12.1 3.12.1 Up-to-date
esbuild 0.9.0 0.9.0 Up-to-date
ex_money_sql 1.11.0 1.11.0 Up-to-date
finch 0.19.0 0.19.0 Up-to-date
floki 0.37.1 0.37.1 Up-to-date
gettext 0.26.2 0.26.2 Up-to-date
igniter 0.6.2 0.6.2 Up-to-date
jason 1.4.4 1.4.4 Up-to-date
live_debugger 0.2.3 0.2.3 Up-to-date
phoenix 1.7.21 1.7.21 Up-to-date
phoenix_ecto 4.6.4 4.6.4 Up-to-date
phoenix_html 4.2.1 4.2.1 Up-to-date
phoenix_live_dashboard 0.8.7 0.8.7 Up-to-date
phoenix_live_reload 1.6.0 1.6.0 Up-to-date
phoenix_live_view 1.0.12 1.0.12 Up-to-date
picosat_elixir 0.2.3 0.2.3 Up-to-date
postgrex 0.20.0 0.20.0 Up-to-date
sourceror 1.10.0 1.10.0 Up-to-date
swoosh 1.19.1 1.19.1 Up-to-date
tailwind 0.2.4 0.3.1 Update not possible
telemetry_metrics 1.1.0 1.1.0 Up-to-date
telemetry_poller 1.2.0 1.2.0 Up-to-date
tidewave 0.1.6 0.1.7 Update possible
40 replies
AEAsh Elixir
Created by Failz on 5/25/2025 in #support
ash_double_entry and updating transfers
40 replies
AEAsh Elixir
Created by Failz on 5/21/2025 in #support
enabling multi tenant for graphql
To help my understanding of the differences between set_tenant I need to write my own plug that leverages Ash.PlugHelpers.set_tenant; something to this effect:
defp set_tenant_from_user(conn, _opts) do
case conn.assigns[:current_user] do
%{organization_id: organization_id} when not is_nil(organization_id) ->
Ash.PlugHelpers.set_tenant(conn, organization_id)

_ ->
conn
end
end
defp set_tenant_from_user(conn, _opts) do
case conn.assigns[:current_user] do
%{organization_id: organization_id} when not is_nil(organization_id) ->
Ash.PlugHelpers.set_tenant(conn, organization_id)

_ ->
conn
end
end
And then plug AshGraphql.Plug will take the tenant that I've set in my custom plug and place it in the appropriate place when the resource action/code_interface is called?
19 replies
AEAsh Elixir
Created by Failz on 5/21/2025 in #support
enabling multi tenant for graphql
thank you for the quick responses
19 replies
AEAsh Elixir
Created by Failz on 5/21/2025 in #support
enabling multi tenant for graphql
🤦‍♂️
19 replies
AEAsh Elixir
Created by Failz on 5/21/2025 in #support
enabling multi tenant for graphql
I'm an idiot in the highest sense. None of my crap was working because my header name that contained the token was Bearer helps if you get the right header name Authorization
19 replies
AEAsh Elixir
Created by Failz on 5/21/2025 in #support
enabling multi tenant for graphql
sorry I linked the wrong function https://hexdocs.pm/ash/3.5.9/Ash.PlugHelpers.html#set_tenant/2 I was importing Ash.PlugHelpers to set the tenant, so plug AshGraphql.Plug would then get the tenant but that's where the original question came in about does it matter which set_actor is used since import AshAuthentication.Plug.Helpers and import Ash.PlugHelpers both have set_actor
19 replies
AEAsh Elixir
Created by Failz on 5/21/2025 in #support
enabling multi tenant for graphql
19 replies