Skip to content

Commit

Permalink
improvement: don't destroy balances by default
Browse files Browse the repository at this point in the history
  • Loading branch information
zachdaniel committed Jun 23, 2024
1 parent b9955d8 commit 16721bc
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 18 deletions.
2 changes: 1 addition & 1 deletion documentation/dsls/DSL:-AshDoubleEntry.Transfer.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ An extension for creating a double entry ledger transfer. See the getting starte
| [`account_resource`](#transfer-account_resource){: #transfer-account_resource .spark-required} | `module` | | The resource to use for account balances |
| [`pre_check_identities_with`](#transfer-pre_check_identities_with){: #transfer-pre_check_identities_with } | `module` | | A domain to use to precheck generated identities. Required by certain data layers. |
| [`balance_resource`](#transfer-balance_resource){: #transfer-balance_resource } | `module` | | The resource being used for balances |
| [`create_accept`](#transfer-create_accept){: #transfer-create_accept } | `atom \| list(atom)` | | Additional attributes to accept when creating a transfer |
| [`create_accept`](#transfer-create_accept){: #transfer-create_accept } | `atom \| list(atom)` | `[]` | Additional attributes to accept when creating a transfer |



Expand Down
16 changes: 16 additions & 0 deletions documentation/tutorials/getting-started-with-ash-double-entry.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ defmodule YourApp.Ledger.Transfer do
# configure the other resources it will interact with
account_resource YourApp.Ledger.Account
balance_resource YourApp.Ledger.Balance

# you only need this if you are using `postgres`
# and so cannot add the `references` block shown below

# destroy_balances? true
end
end
```
Expand Down Expand Up @@ -122,6 +127,10 @@ defmodule YourApp.Ledger.Balance do
postgres do
table "balances"
repo YourApp.Repo

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

balance do
Expand Down Expand Up @@ -161,6 +170,13 @@ defmodule YourApp.Ledger.Balance do
end
```

> ### cascading destroys {: .warning}
>
> If you are not using a data layer capable of automatic cascade
> deletion, you must add `destroy_balances? true` to the `transfer`
> resource! We do this with the `references` block in `ash_postgres`
> as shown above.
#### What does this extension do?

- Adds the following attributes:
Expand Down
39 changes: 22 additions & 17 deletions lib/transfer/changes/verify_transfer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ defmodule AshDoubleEntry.Transfer.Changes.VerifyTransfer do
use Ash.Resource.Change
require Ash.Query

def atomic(changeset, opts, context) do
if AshDoubleEntry.Transfer.Info.transfer_destroy_balances?(changeset.resource) do
{:error, "Cannot destroy a transfer atomically if balances must be destroyed manually"}
else
{:ok, change(changeset, opts, context)}
end
end

def change(changeset, _opts, context) do
if changeset.action.type == :update and
Enum.any?(
Expand All @@ -19,24 +27,21 @@ defmodule AshDoubleEntry.Transfer.Changes.VerifyTransfer do
"Cannot modify a transfer's from_account_id, to_account_id, or id"
)
else
changeset
|> Ash.Changeset.before_action(fn changeset ->
if changeset.action.type == :create do
timestamp = Ash.Changeset.get_attribute(changeset, :timestamp)

timestamp =
case timestamp do
nil -> System.system_time(:millisecond)
timestamp -> DateTime.to_unix(timestamp, :millisecond)
end
if changeset.action.type == :create do
timestamp = Ash.Changeset.get_attribute(changeset, :timestamp)

ulid = AshDoubleEntry.ULID.generate(timestamp)
timestamp =
case timestamp do
nil -> System.system_time(:millisecond)
timestamp -> DateTime.to_unix(timestamp, :millisecond)
end

Ash.Changeset.force_change_attribute(changeset, :id, ulid)
else
changeset
end
end)
ulid = AshDoubleEntry.ULID.generate(timestamp)

Ash.Changeset.force_change_attribute(changeset, :id, ulid)
else
changeset
end
|> maybe_destroy_balances(context)
|> Ash.Changeset.after_action(fn changeset, result ->
from_account_id = Ash.Changeset.get_attribute(changeset, :from_account_id)
Expand Down Expand Up @@ -192,7 +197,7 @@ defmodule AshDoubleEntry.Transfer.Changes.VerifyTransfer do
)
|> case do
%Ash.BulkResult{status: :success} -> changeset
%Ash.BulkResult{errors: errors} -> {:error, Ash.Changeset.add_error(changeset, errors)}
%Ash.BulkResult{errors: errors} -> Ash.Changeset.add_error(changeset, errors)
end
end)
else
Expand Down
5 changes: 5 additions & 0 deletions lib/transfer/transfer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ defmodule AshDoubleEntry.Transfer do
type: {:wrap_list, :atom},
default: [],
doc: "Additional attributes to accept when creating a transfer"
],
destroy_balances?: [
type: :boolean,
doc: "Whether or not balances must be manually destroyed. See the getting started guide for more.",
default: false
]
]
}
Expand Down
6 changes: 6 additions & 0 deletions lib/transfer/transformers/add_structure.ex
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ defmodule AshDoubleEntry.Transfer.Transformers.AddStructure do
AshDoubleEntry.Transfer.Info.transfer_account_resource!(dsl),
attribute_writable?: true
)
|> Ash.Resource.Builder.add_new_relationship(
:has_many,
:balances,
AshDoubleEntry.Transfer.Info.transfer_balance_resource!(dsl)
)
|> Ash.Resource.Builder.add_action(:create, :transfer,
accept:
[:amount, :timestamp, :from_account_id, :to_account_id] ++
Expand All @@ -46,6 +51,7 @@ defmodule AshDoubleEntry.Transfer.Transformers.AddStructure do
pagination: Ash.Resource.Builder.build_pagination(keyset?: true)
)
|> Ash.Resource.Builder.add_change({AshDoubleEntry.Transfer.Changes.VerifyTransfer, []},
only_when_valid?: true,
on: [:create, :update, :destroy]
)
end
Expand Down

0 comments on commit 16721bc

Please sign in to comment.