fix: make cycle regeneration atomic on type change

Make cycle regeneration synchronous in the same transaction as the member
update to ensure atomicity.
This commit is contained in:
Moritz 2025-12-15 12:21:00 +01:00
parent e9c53cc520
commit 1e5f84fd88
Signed by: moritz
GPG key ID: 1020A035E5DD0824

View file

@ -191,33 +191,23 @@ defmodule Mv.Membership.Member do
# Trigger cycle regeneration when membership_fee_type_id changes # Trigger cycle regeneration when membership_fee_type_id changes
# This deletes future unpaid cycles and regenerates them with the new type/amount # This deletes future unpaid cycles and regenerates them with the new type/amount
# Note: Cycle regeneration runs asynchronously to not block the action, # Note: Cycle regeneration runs synchronously in the same transaction to ensure atomicity
# but in test environment it runs synchronously for DB sandbox compatibility # CycleGenerator uses advisory locks and transactions internally to prevent race conditions
change after_action(fn changeset, member, _context -> change after_action(fn changeset, member, _context ->
fee_type_changed = fee_type_changed =
Ash.Changeset.changing_attribute?(changeset, :membership_fee_type_id) Ash.Changeset.changing_attribute?(changeset, :membership_fee_type_id)
if fee_type_changed && member.membership_fee_type_id && member.join_date do if fee_type_changed && member.membership_fee_type_id && member.join_date do
regenerate_fn = fn -> case regenerate_cycles_on_type_change(member) do
case regenerate_cycles_on_type_change(member) do :ok ->
:ok -> :ok
:ok
{:error, reason} -> {:error, reason} ->
require Logger require Logger
Logger.warning( Logger.warning(
"Failed to regenerate cycles for member #{member.id}: #{inspect(reason)}" "Failed to regenerate cycles for member #{member.id}: #{inspect(reason)}"
) )
end
end
if Application.get_env(:mv, :sql_sandbox, false) do
# Run synchronously in test environment for DB sandbox compatibility
regenerate_fn.()
else
# Run asynchronously in other environments
Task.start(regenerate_fn)
end end
end end