Refactor cycle generator and update translations
All checks were successful
continuous-integration/drone/push Build is passing

Extract error handling into separate functions to reduce nesting depth.
This commit is contained in:
Moritz 2025-12-26 21:00:52 +01:00
parent 1bb03b52c9
commit a8ea121800
Signed by: moritz
GPG key ID: 1020A035E5DD0824
5 changed files with 82 additions and 42 deletions

View file

@ -379,25 +379,10 @@ defmodule Mv.MembershipFees.CycleGenerator do
status: :unpaid status: :unpaid
} }
case Ash.create(MembershipFeeCycle, attrs, return_notifications?: true) do handle_cycle_creation_result(
{:ok, cycle, notifications} when is_list(notifications) -> Ash.create(MembershipFeeCycle, attrs, return_notifications?: true),
{:ok, cycle, notifications} cycle_start
)
{: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
end) end)
{successes, skips, errors} = {successes, skips, errors} =
@ -419,9 +404,7 @@ defmodule Mv.MembershipFees.CycleGenerator do
successful_cycles = Enum.map(successes, fn {:ok, cycle, _notifications} -> cycle end) successful_cycles = Enum.map(successes, fn {:ok, cycle, _notifications} -> cycle end)
if Enum.any?(skips) do if Enum.any?(skips) do
Logger.debug( Logger.debug("Skipped #{length(skips)} cycles that already exist for member #{member_id}")
"Skipped #{length(skips)} cycles that already exist for member #{member_id}"
)
end end
{:ok, successful_cycles, all_notifications} {:ok, successful_cycles, all_notifications}
@ -433,4 +416,45 @@ defmodule Mv.MembershipFees.CycleGenerator do
{:error, {:partial_failure, errors}} {:error, {:partial_failure, errors}}
end end
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 end

View file

@ -39,7 +39,6 @@ msgstr "Stadt"
#: lib/mv_web/live/custom_field_live/index_component.ex #: 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/index.html.heex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex #: 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 #: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete" 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" msgstr "Tipp: E-Mail-Adressen ins BCC-Feld einfügen für Datenschutzkonformität"
#: lib/mv_web/components/core_components.ex #: lib/mv_web/components/core_components.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "This field cannot be empty" msgid "This field cannot be empty"
msgstr "Dieses Feld darf nicht leer bleiben" msgstr "Dieses Feld darf nicht leer bleiben"
@ -1322,11 +1322,6 @@ msgstr "Textfeld"
msgid "Yes/No-Selection" msgid "Yes/No-Selection"
msgstr "Ja/Nein-Auswahl" 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 #: lib/mv_web/live/member_live/index.html.heex
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Copy email addresses" msgid "Copy email addresses"
@ -1754,7 +1749,7 @@ msgstr "Löschen"
#: lib/mv_web/live/member_live/show/membership_fees_component.ex #: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Delete All Cycles" msgid "Delete All Cycles"
msgstr "Zyklus löschen" msgstr "Alle Zyklen löschen"
#: lib/mv_web/live/member_live/show/membership_fees_component.ex #: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
@ -1816,6 +1811,16 @@ msgstr "Aktueller Zyklus Zahlungsstatus"
msgid "Last Cycle Payment Status" msgid "Last Cycle Payment Status"
msgstr "Letzter Zyklus Zahlungsstatus" 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 #~ #: lib/mv_web/live/components/payment_filter_component.ex
#~ #, elixir-autogen, elixir-format #~ #, elixir-autogen, elixir-format
#~ msgid "All payment statuses" #~ 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/custom_field_live/index_component.ex
#: lib/mv_web/live/member_live/index.html.heex #: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex #: 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 #: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete" msgid "Delete"
@ -780,6 +779,7 @@ msgid "Tip: Paste email addresses into the BCC field for privacy compliance"
msgstr "" msgstr ""
#: lib/mv_web/components/core_components.ex #: lib/mv_web/components/core_components.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "This field cannot be empty" msgid "This field cannot be empty"
msgstr "" msgstr ""
@ -1323,11 +1323,6 @@ msgstr ""
msgid "Yes/No-Selection" msgid "Yes/No-Selection"
msgstr "" 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 #: lib/mv_web/live/member_live/index.html.heex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Copy email addresses" msgid "Copy email addresses"
@ -1816,3 +1811,13 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Last Cycle Payment Status" msgid "Last Cycle Payment Status"
msgstr "" 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/custom_field_live/index_component.ex
#: lib/mv_web/live/member_live/index.html.heex #: lib/mv_web/live/member_live/index.html.heex
#: lib/mv_web/live/member_live/show/membership_fees_component.ex #: 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 #: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete" msgid "Delete"
@ -780,6 +779,7 @@ msgid "Tip: Paste email addresses into the BCC field for privacy compliance"
msgstr "" msgstr ""
#: lib/mv_web/components/core_components.ex #: lib/mv_web/components/core_components.ex
#: lib/mv_web/live/membership_fee_type_live/form.ex
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "This field cannot be empty" msgid "This field cannot be empty"
msgstr "" msgstr ""
@ -1323,11 +1323,6 @@ msgstr ""
msgid "Yes/No-Selection" msgid "Yes/No-Selection"
msgstr "" 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 #: lib/mv_web/live/member_live/index.html.heex
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Copy email addresses" msgid "Copy email addresses"
@ -1817,6 +1812,16 @@ msgstr "Current Cycle Payment Status"
msgid "Last Cycle Payment Status" msgid "Last Cycle Payment Status"
msgstr "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 #~ #: lib/mv_web/live/components/payment_filter_component.ex
#~ #, elixir-autogen, elixir-format #~ #, elixir-autogen, elixir-format
#~ msgid "All payment statuses" #~ 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) # Only set membership_fee_type_id if member doesn't have one yet (idempotent)
final_member = 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 member
|> Ash.Changeset.for_update(:update_member, %{ |> Ash.Changeset.for_update(:update_member, %{
membership_fee_type_id: member_attrs_without_status.membership_fee_type_id membership_fee_type_id: member_attrs_without_status.membership_fee_type_id