I am having the following warning from ash_double_entry
extension. adjust_balance function is in the AshDoubleEntry extension. How do I set require_atomic to false?
`Kamaro.Ledger.Resources.Balance.adjust_balance` cannot be done atomically, because the changes `[Ash.Resource.Change.AfterAction]` cannot be done atomically
You must either address the issue or set `require_atomic? false` on `Kamaro.Ledger.Resources.Balance.adjust_balance`.
lib/kamaro/ledger/resources/balance.ex:1
are you on the latest version of ash_double_entry
? It defines an atomic callback and should be compatible with atomic updates.
I have the version recommended in the docs {:ash_double_entry, "~> 1.0.0-rc.0"}
. I will install the latest version: 1.0.3 and try again.
Pushing up a fix to the docs.
1 Like
Even with the latest version I am still getting the same error.
warning: [Kamaro.Ledger.Resources.Balance]
actions -> adjust_balance:
`Kamaro.Ledger.Resources.Balance.adjust_balance` cannot be done atomically, because the changes `[Ash.Resource.Change.AfterAction]` cannot be done atomically
You must either address the issue or set `require_atomic? false` on `Kamaro.Ledger.Resources.Balance.adjust_balance`.
lib/kamaro/ledger/resources/balance.ex:1: Kamaro.Ledger.Resources.Balance.__verify_spark_dsl__/1
Compilation failed due to warnings while using the --warnings-as-errors option
are you on the latest version of ash
as well?
I’m using it atomically in a personal app of mine, so it should definitely be compatible…
Yes. I am on the latest version of ash
. See my mix deps
below. Unless if you are referring to ash
main branch instead of 3.0.16
Have you added any global changes to your transaction resource?
I haven’t added any global changes to the transaction resource.
Here are the resources
defmodule Kamaro.Ledger.Resources.Transfer do
use Ash.Resource,
domain: Kamaro.Ledger,
data_layer: AshPostgres.DataLayer,
extensions: [AshDoubleEntry.Transfer]
postgres do
table "transfers"
repo Kamaro.Repo
end
transfer do
# configure the other resources it will interact with
account_resource(Kamaro.Ledger.Resources.Account)
balance_resource(Kamaro.Ledger.Resources.Balance)
end
end
defmodule Kamaro.Ledger.Resources.Balance do
use Ash.Resource,
domain: Kamaro.Ledger,
data_layer: AshPostgres.DataLayer,
extensions: [AshDoubleEntry.Balance]
postgres do
table "balances"
repo Kamaro.Repo
end
balance do
# configure the other resources it will interact with
transfer_resource(Kamaro.Ledger.Resources.Transfer)
account_resource(Kamaro.Ledger.Resources.Account)
end
actions do
read :read do
primary? true
# configure keyset pagination for streaming
pagination keyset?: true, required?: false
end
end
changes do
# add custom behavior. In this case, we're preventing certain balances from being less than zero
change after_action(&validate_balance/2)
end
defp validate_balance(_changeset, result) do
account = result |> Ash.load!(:account) |> Map.get(:account)
if account.allow_zero_balance == false && Money.negative?(result.balance) do
{:error,
Ash.Error.Changes.InvalidAttribute.exception(
value: result.balance,
field: :balance,
message: "balance cannot be negative"
)}
else
{:ok, result}
end
end
end
defmodule Kamaro.Ledger.Resources.Account do
use Ash.Resource,
domain: Kamaro.Ledger,
data_layer: AshPostgres.DataLayer,
extensions: [AshDoubleEntry.Account]
postgres do
table "accounts"
repo Kamaro.Repo
end
account do
# configure the other resources it will interact with
transfer_resource(Kamaro.Ledger.Resources.Transfer)
balance_resource(Kamaro.Ledger.Resources.Balance)
# accept custom attributes in the autogenerated `open` create action
open_action_accept([:account_number])
end
attributes do
# Add custom attributes
attribute :account_number, :string do
allow_nil? false
end
end
end
changes do
# add custom behavior. In this case, we're preventing certain balances from being less than zero
change after_action(&validate_balance/2)
end
defp validate_balance(_changeset, result) do
account = result |> Ash.load!(:account) |> Map.get(:account)
if account.allow_zero_balance == false && Money.negative?(result.balance) do
{:error,
Ash.Error.Changes.InvalidAttribute.exception(
value: result.balance,
field: :balance,
message: "balance cannot be negative"
)}
else
{:ok, result}
end
end
that change does not have an atomic implementation, and therefore cannot be done atomically.
Here is an example from my app that does the same logic atomically:
defmodule Webuilt.Ledger.Balance.Validations.RequiresPositiveBalance do
@moduledoc "Validates that an account requires a positive balance"
use Ash.Resource.Validation
@impl true
def validate(changeset, _, _) do
account_id = Ash.Changeset.get_attribute(changeset, :account_id)
if Webuilt.Ledger.get_account!(account_id, authorize?: false).allow_negative_balance do
{:error,
Ash.Error.Changes.InvalidRelationship.exception(
relationship: :account,
message: "Account must require positive balance"
)}
else
:ok
end
end
@impl true
def atomic(_changeset, _, _) do
{:atomic, [:account], expr(account.allow_negative_balance == false),
expr(
error(Ash.Error.Changes.InvalidRelationship,
relationship: :account,
message: "Account must require positive balance"
)
)}
end
end
# in the balance resource
validations do
validate compare(:balance, greater_than_or_equal_to: 0),
where: [Webuilt.Ledger.Balance.Validations.RequiresPositiveBalance],
message: "balance cannot be negative"
end
Just realized that you’re using an example from the guide. I will adjust to make the guide illustrate this
EDIT: I’ve published latest fixes and removal of that example. Better examples can be added later.
1 Like