Membership Fee 6 - UI Components & LiveViews closes #280 #304
2 changed files with 43 additions and 9 deletions
|
|
@ -299,11 +299,15 @@ defmodule Mv.MembershipFees.CalendarCycles do
|
|||
end
|
||||
|
||||
defp quarterly_cycle_end(cycle_start) do
|
||||
case cycle_start.month do
|
||||
1 -> Date.new!(cycle_start.year, 3, 31)
|
||||
4 -> Date.new!(cycle_start.year, 6, 30)
|
||||
7 -> Date.new!(cycle_start.year, 9, 30)
|
||||
10 -> Date.new!(cycle_start.year, 12, 31)
|
||||
# Ensure cycle_start is aligned to quarter boundary
|
||||
# This handles cases where cycle_start might not be at the correct quarter start (e.g., month 12)
|
||||
aligned_start = quarterly_cycle_start(cycle_start)
|
||||
|
||||
case aligned_start.month do
|
||||
1 -> Date.new!(aligned_start.year, 3, 31)
|
||||
4 -> Date.new!(aligned_start.year, 6, 30)
|
||||
7 -> Date.new!(aligned_start.year, 9, 30)
|
||||
10 -> Date.new!(aligned_start.year, 12, 31)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -313,9 +317,13 @@ defmodule Mv.MembershipFees.CalendarCycles do
|
|||
end
|
||||
|
||||
defp half_yearly_cycle_end(cycle_start) do
|
||||
case cycle_start.month do
|
||||
1 -> Date.new!(cycle_start.year, 6, 30)
|
||||
7 -> Date.new!(cycle_start.year, 12, 31)
|
||||
# Ensure cycle_start is aligned to half-year boundary
|
||||
# This handles cases where cycle_start might not be at the correct half-year start (e.g., month 10)
|
||||
aligned_start = half_yearly_cycle_start(cycle_start)
|
||||
|
||||
case aligned_start.month do
|
||||
1 -> Date.new!(aligned_start.year, 6, 30)
|
||||
7 -> Date.new!(aligned_start.year, 12, 31)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -386,18 +386,44 @@ defmodule Mv.MembershipFees.CycleGenerator do
|
|||
{: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)
|
||||
|
||||
{successes, errors} = Enum.split_with(results, &match?({:ok, _, _}, &1))
|
||||
{successes, skips, errors} =
|
||||
Enum.reduce(results, {[], [], []}, fn
|
||||
{:ok, cycle, notifications}, {successes, skips, errors} ->
|
||||
{[{:ok, cycle, notifications} | successes], skips, errors}
|
||||
|
||||
{:skip, cycle_start}, {successes, skips, errors} ->
|
||||
{successes, [cycle_start | skips], errors}
|
||||
|
||||
{:error, error}, {successes, skips, errors} ->
|
||||
{successes, skips, [error | errors]}
|
||||
end)
|
||||
|
||||
all_notifications =
|
||||
Enum.flat_map(successes, fn {:ok, _cycle, notifications} -> notifications end)
|
||||
|
||||
if Enum.empty?(errors) 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}"
|
||||
)
|
||||
end
|
||||
|
||||
{:ok, successful_cycles, all_notifications}
|
||||
else
|
||||
Logger.warning("Some cycles failed to create: #{inspect(errors)}")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue