fix: improve get_last_completed_cycle and fix test helpers

- Fix get_last_completed_cycle to find most recent completed cycle
- Fix create_cycle helpers to delete auto-generated cycles first
- Fix Ash.destroy return value handling
- Fix form selectors to use specific IDs
- Fix URL parameter names for filters
- Fix Ash.read_one return value expectations in tests
This commit is contained in:
Moritz 2025-12-16 14:11:15 +01:00
parent ab7fa38010
commit 128c712dbc
Signed by: moritz
GPG key ID: 1020A035E5DD0824
12 changed files with 177 additions and 43 deletions

View file

@ -102,6 +102,9 @@ defmodule Mv.Membership.Member do
where [changing(:user)]
end
# Auto-assign default membership fee type if not explicitly set
change Mv.Membership.Member.Changes.SetDefaultMembershipFeeType
# Auto-calculate membership_fee_start_date if not manually set
# Requires both join_date and membership_fee_type_id to be present
change Mv.MembershipFees.Changes.SetMembershipFeeStartDate

View file

@ -0,0 +1,37 @@
defmodule Mv.Membership.Member.Changes.SetDefaultMembershipFeeType do
@moduledoc """
Ash change that automatically assigns the default membership fee type to new members
if no membership_fee_type_id is explicitly provided.
This change reads the default_membership_fee_type_id from global settings and
assigns it to the member if membership_fee_type_id is nil.
"""
use Ash.Resource.Change
def change(changeset, _opts, _context) do
# Only set default if membership_fee_type_id is not already set
current_type_id = Ash.Changeset.get_attribute(changeset, :membership_fee_type_id)
if is_nil(current_type_id) do
case Mv.Membership.get_settings() do
{:ok, settings} ->
if settings.default_membership_fee_type_id do
Ash.Changeset.force_change_attribute(
changeset,
:membership_fee_type_id,
settings.default_membership_fee_type_id
)
else
changeset
end
{:error, _error} ->
# If settings can't be loaded, continue without default
# This prevents member creation from failing if settings are misconfigured
changeset
end
else
changeset
end
end
end

View file

@ -126,11 +126,17 @@ defmodule MvWeb.Helpers.MembershipFeeHelpers do
fee_type ->
cycles = member.membership_fee_cycles || []
cycles
|> Enum.filter(fn cycle ->
CalendarCycles.last_completed_cycle?(cycle.cycle_start, fee_type.interval, today)
end)
|> List.first()
# Get all completed cycles (cycle_end < today)
completed_cycles =
cycles
|> Enum.filter(fn cycle ->
cycle_end = CalendarCycles.calculate_cycle_end(cycle.cycle_start, fee_type.interval)
Date.compare(today, cycle_end) == :gt
end)
# Return the most recent completed cycle (highest cycle_start)
completed_cycles
|> Enum.max_by(& &1.cycle_start, Date, fn -> nil end)
end
end

View file

@ -442,7 +442,9 @@ defmodule MvWeb.MemberLive.Show.MembershipFeesComponent do
case Decimal.parse(amount_str) do
{amount, _} when is_struct(amount, Decimal) ->
case Ash.update(cycle, :update, %{amount: amount}) do
case cycle
|> Ash.Changeset.for_update(:update, %{amount: amount})
|> Ash.update() do
{:ok, updated_cycle} ->
updated_cycles = replace_cycle(socket.assigns.cycles, updated_cycle)
@ -489,6 +491,16 @@ defmodule MvWeb.MemberLive.Show.MembershipFeesComponent do
|> assign(:deleting_cycle, nil)
|> put_flash(:info, gettext("Cycle deleted"))}
{:ok, _destroyed} ->
# Handle case where return_destroyed? is true
updated_cycles = Enum.reject(socket.assigns.cycles, &(&1.id == cycle_id))
{:noreply,
socket
|> assign(:cycles, updated_cycles)
|> assign(:deleting_cycle, nil)
|> put_flash(:info, gettext("Cycle deleted"))}
{:error, error} ->
{:noreply,
socket