diff --git a/lib/mv_web/live/member_live/show/membership_fees_component.ex b/lib/mv_web/live/member_live/show/membership_fees_component.ex index e839d16..d074ffa 100644 --- a/lib/mv_web/live/member_live/show/membership_fees_component.ex +++ b/lib/mv_web/live/member_live/show/membership_fees_component.ex @@ -457,7 +457,7 @@ defmodule MvWeb.MemberLive.Show.MembershipFeesComponent do |> assign(:cycles, []) |> assign( :available_fee_types, - get_available_fee_types(updated_member, current_actor(socket)) + get_available_fee_types(updated_member, actor) ) |> assign(:interval_warning, nil) |> put_flash(:info, gettext("Membership fee type removed"))} @@ -488,13 +488,9 @@ defmodule MvWeb.MemberLive.Show.MembershipFeesComponent do if interval_warning do {:noreply, assign(socket, :interval_warning, interval_warning)} else - actor = current_actor(socket) - case update_member_fee_type(member, fee_type_id, actor) do {:ok, updated_member} -> # Reload member with cycles - actor = current_actor(socket) - updated_member = updated_member |> Ash.load!( @@ -520,7 +516,7 @@ defmodule MvWeb.MemberLive.Show.MembershipFeesComponent do |> assign(:cycles, cycles) |> assign( :available_fee_types, - get_available_fee_types(updated_member, current_actor(socket)) + get_available_fee_types(updated_member, actor) ) |> assign(:interval_warning, nil) |> put_flash(:info, gettext("Membership fee type updated. Cycles regenerated."))} @@ -730,61 +726,31 @@ defmodule MvWeb.MemberLive.Show.MembershipFeesComponent do confirmation = String.trim(String.downcase(socket.assigns.delete_all_confirmation)) expected = String.downcase(gettext("Yes")) - if confirmation != expected do + if confirmation == expected do + member = socket.assigns.member + actor = current_actor(socket) + cycles = socket.assigns.cycles + + reset_modal = fn s -> + s + |> assign(:deleting_all_cycles, false) + |> assign(:delete_all_confirmation, "") + end + + if can?(actor, :destroy, MembershipFeeCycle) do + do_delete_all_cycles(socket, member, actor, cycles, reset_modal) + else + {:noreply, + socket + |> reset_modal.() + |> put_flash(:error, format_error(%Ash.Error.Forbidden{}))} + end + else {:noreply, socket |> assign(:deleting_all_cycles, false) |> assign(:delete_all_confirmation, "") |> put_flash(:error, gettext("Confirmation text does not match"))} - else - member = socket.assigns.member - - # Delete all cycles atomically using Ecto query - import Ecto.Query - - deleted_count = - Mv.Repo.delete_all( - from c in Mv.MembershipFees.MembershipFeeCycle, - where: c.member_id == ^member.id - ) - - if deleted_count > 0 do - # Reload member to get updated cycles - actor = current_actor(socket) - - updated_member = - member - |> Ash.load!( - [ - :membership_fee_type, - membership_fee_cycles: [:membership_fee_type] - ], - actor: actor - ) - - updated_cycles = - Enum.sort_by( - updated_member.membership_fee_cycles || [], - & &1.cycle_start, - {:desc, Date} - ) - - send(self(), {:member_updated, updated_member}) - - {:noreply, - socket - |> assign(:member, updated_member) - |> assign(:cycles, updated_cycles) - |> assign(:deleting_all_cycles, false) - |> assign(:delete_all_confirmation, "") - |> put_flash(:info, gettext("All cycles deleted"))} - else - {:noreply, - socket - |> assign(:deleting_all_cycles, false) - |> assign(:delete_all_confirmation, "") - |> put_flash(:info, gettext("No cycles to delete"))} - end end end @@ -903,6 +869,55 @@ defmodule MvWeb.MemberLive.Show.MembershipFeesComponent do # Helper functions + defp do_delete_all_cycles(socket, member, actor, cycles, reset_modal) do + result = + Enum.reduce_while(cycles, {:ok, 0}, fn cycle, {:ok, count} -> + case Ash.destroy(cycle, domain: MembershipFees, actor: actor) do + :ok -> {:cont, {:ok, count + 1}} + {:ok, _} -> {:cont, {:ok, count + 1}} + {:error, error} -> {:halt, {:error, error}} + end + end) + + case result do + {:ok, deleted_count} when deleted_count > 0 -> + updated_member = + member + |> Ash.load!( + [:membership_fee_type, membership_fee_cycles: [:membership_fee_type]], + actor: actor + ) + + updated_cycles = + Enum.sort_by( + updated_member.membership_fee_cycles || [], + & &1.cycle_start, + {:desc, Date} + ) + + send(self(), {:member_updated, updated_member}) + + {:noreply, + socket + |> assign(:member, updated_member) + |> assign(:cycles, updated_cycles) + |> reset_modal.() + |> put_flash(:info, gettext("All cycles deleted"))} + + {:ok, _} -> + {:noreply, + socket + |> reset_modal.() + |> put_flash(:info, gettext("No cycles to delete"))} + + {:error, error} -> + {:noreply, + socket + |> reset_modal.() + |> put_flash(:error, format_error(error))} + end + end + defp get_available_fee_types(member, actor) do all_types = MembershipFeeType