Membership Fee 6 - UI Components & LiveViews closes #280 #304

Merged
moritz merged 79 commits from feature/280_membership_fee_ui into main 2025-12-26 23:14:50 +01:00
5 changed files with 82 additions and 42 deletions
Showing only changes of commit a8ea121800 - Show all commits

View file

@ -379,25 +379,10 @@ defmodule Mv.MembershipFees.CycleGenerator do
status: :unpaid
}
case Ash.create(MembershipFeeCycle, attrs, return_notifications?: true) do
{:ok, cycle, notifications} when is_list(notifications) ->
{:ok, cycle, notifications}
{:ok, cycle} ->
{:ok, cycle, []}
{:error, %Ash.Error.Invalid{errors: [%Ash.Error.Changes.InvalidAttribute{private_vars: %{constraint: constraint, constraint_type: :unique}}]}} = error ->
# Cycle already exists (unique constraint violation) - skip it silently
# This makes the function idempotent and prevents errors on server restart
if constraint == "membership_fee_cycles_unique_cycle_per_member_index" do
{:skip, cycle_start}
else
{:error, {cycle_start, error}}
end
{:error, reason} ->
{:error, {cycle_start, reason}}
end
handle_cycle_creation_result(
Ash.create(MembershipFeeCycle, attrs, return_notifications?: true),
cycle_start
)
end)
{successes, skips, errors} =
@ -419,9 +404,7 @@ defmodule Mv.MembershipFees.CycleGenerator do
successful_cycles = Enum.map(successes, fn {:ok, cycle, _notifications} -> cycle end)
if Enum.any?(skips) do
Logger.debug(
"Skipped #{length(skips)} cycles that already exist for member #{member_id}"
)
Logger.debug("Skipped #{length(skips)} cycles that already exist for member #{member_id}")
end
{:ok, successful_cycles, all_notifications}
@ -433,4 +416,45 @@ defmodule Mv.MembershipFees.CycleGenerator do
{:error, {:partial_failure, errors}}
end
end
defp handle_cycle_creation_result({:ok, cycle, notifications}, _cycle_start)
when is_list(notifications) do
{:ok, cycle, notifications}
end
defp handle_cycle_creation_result({:ok, cycle}, _cycle_start) do
{:ok, cycle, []}
end
defp handle_cycle_creation_result(
{:error,
%Ash.Error.Invalid{
errors: [
%Ash.Error.Changes.InvalidAttribute{
private_vars: %{constraint: constraint, constraint_type: :unique}
}
]
}} = error,
cycle_start
) do
# Cycle already exists (unique constraint violation) - skip it silently
# This makes the function idempotent and prevents errors on server restart
handle_unique_constraint_violation(constraint, cycle_start, error)
end
defp handle_cycle_creation_result({:error, reason}, cycle_start) do
{:error, {cycle_start, reason}}
end
defp handle_unique_constraint_violation(
"membership_fee_cycles_unique_cycle_per_member_index",
cycle_start,
_error
) do
{:skip, cycle_start}
end
defp handle_unique_constraint_violation(_constraint, cycle_start, error) do
{:error, {cycle_start, error}}
end
end

View file

@ -39,7 +39,6 @@ msgstr "Stadt"
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Delete"
@ -779,6 +778,7 @@ msgid "Tip: Paste email addresses into the BCC field for privacy compliance"
msgstr "Tipp: E-Mail-Adressen ins BCC-Feld einfügen für Datenschutzkonformität"
#: lib/mv_web/components/core_components.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "This field cannot be empty"
msgstr "Dieses Feld darf nicht leer bleiben"
@ -1322,11 +1322,6 @@ msgstr "Textfeld"
msgid "Yes/No-Selection"
msgstr "Ja/Nein-Auswahl"
#: lib/mv_web/live/components/payment_filter_component.ex
#, elixir-autogen, elixir-format
msgid "All payment statuses"
msgstr "Jeder Zahlungs-Zustand"
#: lib/mv_web/live/member_live/index.html.heex
#, elixir-autogen, elixir-format, fuzzy
msgid "Copy email addresses"
@ -1754,7 +1749,7 @@ msgstr "Löschen"
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Delete All Cycles"
msgstr "Zyklus löschen"
msgstr "Alle Zyklen löschen"
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format, fuzzy
@ -1816,6 +1811,16 @@ msgstr "Aktueller Zyklus Zahlungsstatus"
msgid "Last Cycle Payment Status"
msgstr "Letzter Zyklus Zahlungsstatus"
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Delete membership fee type"
msgstr ""
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Edit membership fee type"
msgstr "Mitgliedsbeitragsart bearbeiten"
#~ #: lib/mv_web/live/components/payment_filter_component.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "All payment statuses"

View file

@ -40,7 +40,6 @@ msgstr ""
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Delete"
@ -780,6 +779,7 @@ msgid "Tip: Paste email addresses into the BCC field for privacy compliance"
msgstr ""
#: lib/mv_web/components/core_components.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format
msgid "This field cannot be empty"
msgstr ""
@ -1323,11 +1323,6 @@ msgstr ""
msgid "Yes/No-Selection"
msgstr ""
#: lib/mv_web/live/components/payment_filter_component.ex
#, elixir-autogen, elixir-format
msgid "All payment statuses"
msgstr ""
#: lib/mv_web/live/member_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Copy email addresses"
@ -1816,3 +1811,13 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Last Cycle Payment Status"
msgstr ""
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Delete membership fee type"
msgstr ""
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Edit membership fee type"
msgstr ""

View file

@ -40,7 +40,6 @@ msgstr ""
#: lib/mv_web/live/custom_field_live/index_component.ex
#: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#: lib/mv_web/live/membership_fee_type_live/index.ex
#: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Delete"
@ -780,6 +779,7 @@ msgid "Tip: Paste email addresses into the BCC field for privacy compliance"
msgstr ""
#: lib/mv_web/components/core_components.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "This field cannot be empty"
msgstr ""
@ -1323,11 +1323,6 @@ msgstr ""
msgid "Yes/No-Selection"
msgstr ""
#: lib/mv_web/live/components/payment_filter_component.ex
#, elixir-autogen, elixir-format
msgid "All payment statuses"
msgstr ""
#: lib/mv_web/live/member_live/index.html.heex
#, elixir-autogen, elixir-format, fuzzy
msgid "Copy email addresses"
@ -1817,6 +1812,16 @@ msgstr "Current Cycle Payment Status"
msgid "Last Cycle Payment Status"
msgstr "Last Cycle Payment Status"
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format
msgid "Delete membership fee type"
msgstr ""
#: lib/mv_web/live/membership_fee_type_live/index.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Edit membership fee type"
msgstr ""
#~ #: lib/mv_web/live/components/payment_filter_component.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "All payment statuses"

View file

@ -211,7 +211,8 @@ Enum.each(member_attrs_list, fn member_attrs ->
# Only set membership_fee_type_id if member doesn't have one yet (idempotent)
final_member =
if is_nil(member.membership_fee_type_id) and Map.has_key?(member_attrs_without_status, :membership_fee_type_id) do
if is_nil(member.membership_fee_type_id) and
Map.has_key?(member_attrs_without_status, :membership_fee_type_id) do
member
|> Ash.Changeset.for_update(:update_member, %{
membership_fee_type_id: member_attrs_without_status.membership_fee_type_id