Make seed script deterministic and idempotent for fee type assignments
Fix update action name from :update to :update_member for Member resource
This commit is contained in:
parent
3241dd7d96
commit
a03056e6ae
1 changed files with 48 additions and 17 deletions
|
|
@ -129,7 +129,12 @@ Accounts.create_user!(%{email: "admin@mv.local"}, upsert?: true, upsert_identity
|
||||||
|> Ash.update!()
|
|> Ash.update!()
|
||||||
|
|
||||||
# Load all membership fee types for assignment
|
# Load all membership fee types for assignment
|
||||||
all_fee_types = MembershipFeeType |> Ash.read!() |> Enum.to_list()
|
# Sort by name to ensure deterministic order
|
||||||
|
all_fee_types =
|
||||||
|
MembershipFeeType
|
||||||
|
|> Ash.Query.sort(name: :asc)
|
||||||
|
|> Ash.read!()
|
||||||
|
|> Enum.to_list()
|
||||||
|
|
||||||
# Create sample members for testing - use upsert to prevent duplicates
|
# Create sample members for testing - use upsert to prevent duplicates
|
||||||
# Member 1: Hans - All cycles paid
|
# Member 1: Hans - All cycles paid
|
||||||
|
|
@ -195,17 +200,32 @@ Enum.each(member_attrs_list, fn member_attrs ->
|
||||||
member_attrs_without_status = Map.delete(member_attrs, :cycle_status)
|
member_attrs_without_status = Map.delete(member_attrs, :cycle_status)
|
||||||
|
|
||||||
# Use upsert to prevent duplicates based on email
|
# Use upsert to prevent duplicates based on email
|
||||||
|
# First create/update member without membership_fee_type_id to avoid overwriting existing assignments
|
||||||
|
member_attrs_without_fee_type = Map.delete(member_attrs_without_status, :membership_fee_type_id)
|
||||||
|
|
||||||
member =
|
member =
|
||||||
Membership.create_member!(member_attrs_without_status,
|
Membership.create_member!(member_attrs_without_fee_type,
|
||||||
upsert?: true,
|
upsert?: true,
|
||||||
upsert_identity: :unique_email
|
upsert_identity: :unique_email
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
member
|
||||||
|
|> Ash.Changeset.for_update(:update_member, %{
|
||||||
|
membership_fee_type_id: member_attrs_without_status.membership_fee_type_id
|
||||||
|
})
|
||||||
|
|> Ash.update!()
|
||||||
|
else
|
||||||
|
member
|
||||||
|
end
|
||||||
|
|
||||||
# Generate cycles if member has a fee type
|
# Generate cycles if member has a fee type
|
||||||
if member.membership_fee_type_id do
|
if final_member.membership_fee_type_id do
|
||||||
# Load member with cycles to check if they already exist
|
# Load member with cycles to check if they already exist
|
||||||
member_with_cycles =
|
member_with_cycles =
|
||||||
member
|
final_member
|
||||||
|> Ash.load!(:membership_fee_cycles)
|
|> Ash.load!(:membership_fee_cycles)
|
||||||
|
|
||||||
# Only generate if no cycles exist yet (to avoid duplicates on re-run)
|
# Only generate if no cycles exist yet (to avoid duplicates on re-run)
|
||||||
|
|
@ -213,7 +233,7 @@ Enum.each(member_attrs_list, fn member_attrs ->
|
||||||
if Enum.empty?(member_with_cycles.membership_fee_cycles) do
|
if Enum.empty?(member_with_cycles.membership_fee_cycles) do
|
||||||
# Generate cycles
|
# Generate cycles
|
||||||
{:ok, new_cycles, _notifications} =
|
{:ok, new_cycles, _notifications} =
|
||||||
CycleGenerator.generate_cycles_for_member(member.id, skip_lock?: true)
|
CycleGenerator.generate_cycles_for_member(final_member.id, skip_lock?: true)
|
||||||
|
|
||||||
new_cycles
|
new_cycles
|
||||||
else
|
else
|
||||||
|
|
@ -311,36 +331,47 @@ Enum.with_index(linked_members)
|
||||||
user = member_attrs.user
|
user = member_attrs.user
|
||||||
member_attrs_without_user = Map.delete(member_attrs, :user)
|
member_attrs_without_user = Map.delete(member_attrs, :user)
|
||||||
|
|
||||||
# Round-robin assignment: continue cycling through fee types
|
# Use upsert to prevent duplicates based on email
|
||||||
# Start from where previous members ended
|
# First create/update member without membership_fee_type_id to avoid overwriting existing assignments
|
||||||
fee_type_index = rem(3 + index, length(all_fee_types))
|
member_attrs_without_fee_type = Map.delete(member_attrs_without_user, :membership_fee_type_id)
|
||||||
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
|
# Check if user already has a member
|
||||||
member =
|
member =
|
||||||
if user.member_id == nil do
|
if user.member_id == nil do
|
||||||
# User is free, create member and link - use upsert to prevent duplicates
|
# User is free, create member and link - use upsert to prevent duplicates
|
||||||
Membership.create_member!(
|
Membership.create_member!(
|
||||||
Map.put(member_attrs_with_fee_type, :user, %{id: user.id}),
|
Map.put(member_attrs_without_fee_type, :user, %{id: user.id}),
|
||||||
upsert?: true,
|
upsert?: true,
|
||||||
upsert_identity: :unique_email
|
upsert_identity: :unique_email
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
# User already has a member, just create the member without linking - use upsert to prevent duplicates
|
# User already has a member, just create the member without linking - use upsert to prevent duplicates
|
||||||
Membership.create_member!(member_attrs_with_fee_type,
|
Membership.create_member!(member_attrs_without_fee_type,
|
||||||
upsert?: true,
|
upsert?: true,
|
||||||
upsert_identity: :unique_email
|
upsert_identity: :unique_email
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Only set membership_fee_type_id if member doesn't have one yet (idempotent)
|
||||||
|
final_member =
|
||||||
|
if is_nil(member.membership_fee_type_id) do
|
||||||
|
# Assign deterministically using round-robin
|
||||||
|
# Start from where previous members ended (3 members before this)
|
||||||
|
fee_type_index = rem(3 + index, length(all_fee_types))
|
||||||
|
fee_type = Enum.at(all_fee_types, fee_type_index)
|
||||||
|
|
||||||
|
member
|
||||||
|
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id})
|
||||||
|
|> Ash.update!()
|
||||||
|
else
|
||||||
|
member
|
||||||
|
end
|
||||||
|
|
||||||
# Generate cycles for linked members
|
# Generate cycles for linked members
|
||||||
if member.membership_fee_type_id do
|
if final_member.membership_fee_type_id do
|
||||||
# Load member with cycles to check if they already exist
|
# Load member with cycles to check if they already exist
|
||||||
member_with_cycles =
|
member_with_cycles =
|
||||||
member
|
final_member
|
||||||
|> Ash.load!(:membership_fee_cycles)
|
|> Ash.load!(:membership_fee_cycles)
|
||||||
|
|
||||||
# Only generate if no cycles exist yet (to avoid duplicates on re-run)
|
# Only generate if no cycles exist yet (to avoid duplicates on re-run)
|
||||||
|
|
@ -348,7 +379,7 @@ Enum.with_index(linked_members)
|
||||||
if Enum.empty?(member_with_cycles.membership_fee_cycles) do
|
if Enum.empty?(member_with_cycles.membership_fee_cycles) do
|
||||||
# Generate cycles
|
# Generate cycles
|
||||||
{:ok, new_cycles, _notifications} =
|
{:ok, new_cycles, _notifications} =
|
||||||
CycleGenerator.generate_cycles_for_member(member.id, skip_lock?: true)
|
CycleGenerator.generate_cycles_for_member(final_member.id, skip_lock?: true)
|
||||||
|
|
||||||
new_cycles
|
new_cycles
|
||||||
else
|
else
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue