Update seeds: member without fee type, cycles with various statuses

Add member without membership fee type. Generate cycles for members
with fee types and set different statuses: all paid, all unpaid, and
mixed (paid/unpaid/suspended). Update tests accordingly.
This commit is contained in:
Moritz 2025-12-18 13:53:01 +01:00
parent f25e198b0e
commit 239d784f3c
Signed by: moritz
GPG key ID: 1020A035E5DD0824
2 changed files with 171 additions and 35 deletions

View file

@ -6,6 +6,7 @@
alias Mv.Membership
alias Mv.Accounts
alias Mv.MembershipFees.MembershipFeeType
alias Mv.MembershipFees.CycleGenerator
# Create example membership fee types
for fee_type_attrs <- [
@ -131,7 +132,10 @@ Accounts.create_user!(%{email: "admin@mv.local"}, upsert?: true, upsert_identity
all_fee_types = MembershipFeeType |> Ash.read!() |> Enum.to_list()
# Create sample members for testing - use upsert to prevent duplicates
# Assign each member to a fee type using round-robin distribution
# Member 1: Hans - All cycles paid
# Member 2: Greta - All cycles unpaid
# Member 3: Friedrich - Mixed cycles (paid, unpaid, suspended)
# Member 4: Marianne - No membership fee type
member_attrs_list = [
%{
first_name: "Hans",
@ -142,7 +146,9 @@ member_attrs_list = [
city: "München",
street: "Hauptstraße",
house_number: "42",
postal_code: "80331"
postal_code: "80331",
membership_fee_type_id: Enum.at(all_fee_types, 0).id,
cycle_status: :all_paid
},
%{
first_name: "Greta",
@ -154,7 +160,9 @@ member_attrs_list = [
street: "Lindenstraße",
house_number: "17",
postal_code: "20095",
notes: "Interessiert an Fortgeschrittenen-Kursen"
notes: "Interessiert an Fortgeschrittenen-Kursen",
membership_fee_type_id: Enum.at(all_fee_types, 1).id,
cycle_status: :all_unpaid
},
%{
first_name: "Friedrich",
@ -164,7 +172,9 @@ member_attrs_list = [
phone_number: "+49301122334",
city: "Berlin",
street: "Kastanienallee",
house_number: "8"
house_number: "8",
membership_fee_type_id: Enum.at(all_fee_types, 2).id,
cycle_status: :mixed
},
%{
first_name: "Marianne",
@ -175,21 +185,74 @@ member_attrs_list = [
city: "Berlin",
street: "Kastanienallee",
house_number: "8"
# No membership_fee_type_id - member without fee type
}
]
# Assign fee types to members using round-robin
Enum.with_index(member_attrs_list)
|> Enum.each(fn {member_attrs, index} ->
# Round-robin assignment: cycle through fee types
fee_type = Enum.at(all_fee_types, rem(index, length(all_fee_types)))
member_attrs_with_fee_type = Map.put(member_attrs, :membership_fee_type_id, fee_type.id)
# Create members and generate cycles
Enum.each(member_attrs_list, fn member_attrs ->
cycle_status = Map.get(member_attrs, :cycle_status)
member_attrs_without_status = Map.delete(member_attrs, :cycle_status)
# Use upsert to prevent duplicates based on email
Membership.create_member!(member_attrs_with_fee_type,
upsert?: true,
upsert_identity: :unique_email
)
member =
Membership.create_member!(member_attrs_without_status,
upsert?: true,
upsert_identity: :unique_email
)
# Generate cycles if member has a fee type
if member.membership_fee_type_id do
# Load member with cycles to check if they already exist
member_with_cycles =
member
|> Ash.load!(:membership_fee_cycles)
# Only generate if no cycles exist yet (to avoid duplicates on re-run)
cycles =
if Enum.empty?(member_with_cycles.membership_fee_cycles) do
# Generate cycles
{:ok, new_cycles, _notifications} =
CycleGenerator.generate_cycles_for_member(member.id, skip_lock?: true)
new_cycles
else
# Use existing cycles
member_with_cycles.membership_fee_cycles
end
# Set cycle statuses based on member type
if cycle_status do
cycles
|> Enum.sort_by(& &1.cycle_start, Date)
|> Enum.with_index()
|> Enum.each(fn {cycle, index} ->
status =
case cycle_status do
:all_paid ->
:paid
:all_unpaid ->
:unpaid
:mixed ->
# Mix: first paid, second unpaid, third suspended, then repeat
case rem(index, 3) do
0 -> :paid
1 -> :unpaid
2 -> :suspended
end
end
# Only update if status is different
if cycle.status != status do
cycle
|> Ash.Changeset.for_update(:update, %{status: status})
|> Ash.update!()
end
end)
end
end
end)
# Create additional users for user-member linking examples
@ -250,26 +313,64 @@ Enum.with_index(linked_members)
# Round-robin assignment: continue cycling through fee types
# Start from where previous members ended
fee_type_index = rem(length(member_attrs_list) + index, length(all_fee_types))
fee_type_index = rem(3 + index, length(all_fee_types))
fee_type = Enum.at(all_fee_types, fee_type_index)
member_attrs_with_fee_type =
Map.put(member_attrs_without_user, :membership_fee_type_id, fee_type.id)
# Check if user already has a member
if user.member_id == nil do
# User is free, create member and link - use upsert to prevent duplicates
Membership.create_member!(
Map.put(member_attrs_with_fee_type, :user, %{id: user.id}),
upsert?: true,
upsert_identity: :unique_email
)
else
# User already has a member, just create the member without linking - use upsert to prevent duplicates
Membership.create_member!(member_attrs_with_fee_type,
upsert?: true,
upsert_identity: :unique_email
)
member =
if user.member_id == nil do
# User is free, create member and link - use upsert to prevent duplicates
Membership.create_member!(
Map.put(member_attrs_with_fee_type, :user, %{id: user.id}),
upsert?: true,
upsert_identity: :unique_email
)
else
# User already has a member, just create the member without linking - use upsert to prevent duplicates
Membership.create_member!(member_attrs_with_fee_type,
upsert?: true,
upsert_identity: :unique_email
)
end
# Generate cycles for linked members
if member.membership_fee_type_id do
# Load member with cycles to check if they already exist
member_with_cycles =
member
|> Ash.load!(:membership_fee_cycles)
# Only generate if no cycles exist yet (to avoid duplicates on re-run)
cycles =
if Enum.empty?(member_with_cycles.membership_fee_cycles) do
# Generate cycles
{:ok, new_cycles, _notifications} =
CycleGenerator.generate_cycles_for_member(member.id, skip_lock?: true)
new_cycles
else
# Use existing cycles
member_with_cycles.membership_fee_cycles
end
# Set some cycles to paid for linked members (mixed status)
cycles
|> Enum.sort_by(& &1.cycle_start, Date)
|> Enum.with_index()
|> Enum.each(fn {cycle, index} ->
# Every other cycle is paid, rest unpaid
status = if rem(index, 2) == 0, do: :paid, else: :unpaid
# Only update if status is different
if cycle.status != status do
cycle
|> Ash.Changeset.for_update(:update, %{status: status})
|> Ash.update!()
end
end)
end
end)