Remove future date validation for join_date

Allow join_date to be set in the future. Only validation remaining
is that exit_date must be after join_date.
This commit is contained in:
Moritz 2025-12-16 17:24:24 +01:00
parent 8f8c3f258a
commit 9a1f0fbfa6
Signed by: moritz
GPG key ID: 1020A035E5DD0824
2 changed files with 59 additions and 10 deletions

View file

@ -242,6 +242,63 @@ defmodule Mv.Membership.Member do
{:ok, member} {:ok, member}
end end
end) end)
# Trigger cycle regeneration when join_date or exit_date changes
# Regenerates cycles based on new dates
# Note: Cycle generation runs synchronously in test environment, asynchronously in production
# CycleGenerator uses advisory locks and transactions internally to prevent race conditions
change after_action(fn changeset, member, _context ->
join_date_changed = Ash.Changeset.changing_attribute?(changeset, :join_date)
exit_date_changed = Ash.Changeset.changing_attribute?(changeset, :exit_date)
if (join_date_changed || exit_date_changed) && member.membership_fee_type_id do
if Application.get_env(:mv, :sql_sandbox, false) do
# Run synchronously in test environment for DB sandbox compatibility
# Use skip_lock?: true to avoid nested transactions (after_action runs within action transaction)
# Return notifications to Ash so they are sent after commit
case Mv.MembershipFees.CycleGenerator.generate_cycles_for_member(
member.id,
today: Date.utc_today(),
skip_lock?: true
) do
{:ok, _cycles, notifications} ->
{:ok, member, notifications}
{:error, reason} ->
require Logger
Logger.warning(
"Failed to regenerate cycles for member #{member.id}: #{inspect(reason)}"
)
{:ok, member}
end
else
# Run asynchronously in other environments
# Send notifications explicitly since they cannot be returned via after_action
Task.start(fn ->
case Mv.MembershipFees.CycleGenerator.generate_cycles_for_member(member.id) do
{:ok, _cycles, notifications} ->
# Send notifications manually for async case
if Enum.any?(notifications) do
Ash.Notifier.notify(notifications)
end
{:error, reason} ->
require Logger
Logger.warning(
"Failed to regenerate cycles for member #{member.id}: #{inspect(reason)}"
)
end
end)
{:ok, member}
end
else
{:ok, member}
end
end)
end end
# Action to handle fuzzy search on specific fields # Action to handle fuzzy search on specific fields
@ -395,11 +452,6 @@ defmodule Mv.Membership.Member do
end end
end end
# Join date not in the future
validate compare(:join_date, less_than_or_equal_to: &Date.utc_today/0),
where: [present(:join_date)],
message: "cannot be in the future"
# Exit date not before join date # Exit date not before join date
validate compare(:exit_date, greater_than: :join_date), validate compare(:exit_date, greater_than: :join_date),
where: [present([:join_date, :exit_date])], where: [present([:join_date, :exit_date])],

View file

@ -58,12 +58,9 @@ defmodule Mv.Membership.MemberTest do
assert {:ok, _member} = Membership.create_member(attrs2) assert {:ok, _member} = Membership.create_member(attrs2)
end end
test "Join date is optional but must not be in the future" do test "Join date can be in the future" do
attrs = Map.put(@valid_attrs, :join_date, Date.utc_today() |> Date.add(1)) attrs = Map.put(@valid_attrs, :join_date, Date.utc_today() |> Date.add(1))
assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) assert {:ok, _member} = Membership.create_member(attrs)
assert error_message(errors, :join_date) =~ "cannot be in the future"
attrs2 = Map.delete(@valid_attrs, :join_date)
assert {:ok, _member} = Membership.create_member(attrs2)
end end
test "Exit date is optional but must not be before join date if both are specified" do test "Exit date is optional but must not be before join date if both are specified" do