Rework member overview #553

Open
simon wants to merge 27 commits from issue/mitgliederverwaltung-547 into main
2 changed files with 134 additions and 0 deletions
Showing only changes of commit 95c25142f0 - Show all commits

View file

@ -30,6 +30,11 @@ reset-database:
seed-database:
mix run priv/repo/seeds.exs
# Seed a large batch of members for load / infinite-scroll testing (opt-in; NOT
# part of `seed-database`). Set BULK_MEMBER_COUNT to change the count (default 300).
seed-members-bulk:
mix run priv/repo/seeds_bulk_members.exs
start-database:
docker compose up -d

View file

@ -0,0 +1,129 @@
# Optional BULK member seed for load / infinite-scroll testing.
#
# This is NOT part of the default dev seed (`mix run priv/repo/seeds.exs`), so the
# normal dev database stays small. Run it explicitly when you need many members:
#
# just seed-members-bulk
# BULK_MEMBER_COUNT=500 mix run priv/repo/seeds_bulk_members.exs
#
# Requires the bootstrap seeds (admin user + fee types) to have run first. Members
# are upserted by email ("bulk-member-<n>@example.de"), so re-running is idempotent
# and does not duplicate members or their fee cycles.
alias Mv.Accounts
alias Mv.Membership
alias Mv.MembershipFees.CycleGenerator
alias Mv.MembershipFees.MembershipFeeType
require Ash.Query
admin_email = System.get_env("ADMIN_EMAIL") || "admin@localhost"
admin_user_with_role =
case Accounts.User
|> Ash.Query.filter(email == ^admin_email)
|> Ash.read_one(domain: Mv.Accounts, authorize?: false) do
{:ok, user} when not is_nil(user) ->
user |> Ash.load!(:role, authorize?: false)
_ ->
raise "Bulk seed requires bootstrap: admin user not found (#{admin_email})"
end
all_fee_types =
MembershipFeeType
|> Ash.Query.sort(name: :asc)
|> Ash.read!(actor: admin_user_with_role, domain: Mv.MembershipFees)
|> Enum.to_list()
count =
case Integer.parse(System.get_env("BULK_MEMBER_COUNT") || "300") do
{n, _} when n > 0 -> n
_ -> 300
end
first_names =
~w(Anna Bruno Clara David Elena Felix Greta Henrik Ines Jakob Laura Max Nina
Oliver Paula Quirin Rosa Stefan Thea Uwe Vera Wolf Xenia Yannick Zoe Ada
Ben Carl Dora Emil)
last_names =
~w(Schmidt Müller Fischer Weber Wagner Becker Schulz Hoffmann Koch Richter Klein
Wolf Neumann Schwarz Zimmermann Braun Krüger Hartmann Lange Werner Krause
Lehmann Köhler Herrmann Walter)
cities = [
{"München", "80331"},
{"Hamburg", "20095"},
{"Berlin", "10435"},
{"Köln", "50667"},
{"Frankfurt", "60313"},
{"Stuttgart", "70173"},
{"Düsseldorf", "40212"},
{"Dortmund", "44137"},
{"Essen", "45127"},
{"Dresden", "01069"},
{"Nürnberg", "90402"},
{"Bremen", "28195"}
]
statuses = [:paid, :unpaid, :suspended]
IO.puts("Seeding #{count} bulk members (set BULK_MEMBER_COUNT to change)…")
Enum.each(1..count, fn i ->
{city, postal_code} = Enum.at(cities, rem(i, length(cities)))
attrs = %{
first_name: Enum.at(first_names, rem(i, length(first_names))),
last_name: Enum.at(last_names, rem(div(i, length(first_names)) + i, length(last_names))),
email: "bulk-member-#{i}@example.de",
join_date: Date.add(~D[2020-01-01], rem(i * 7, 2000)),
city: city,
street: "Teststraße",
house_number: Integer.to_string(rem(i, 200) + 1),
postal_code: postal_code,
country: "Deutschland"
}
attrs =
case all_fee_types do
[] -> attrs
types -> Map.put(attrs, :membership_fee_type_id, Enum.at(types, rem(i, length(types))).id)
end
member =
Membership.create_member!(attrs,
upsert?: true,
upsert_identity: :unique_email,
actor: admin_user_with_role
)
# Give the member fee cycles + a round-robin status so the fee-status column is
# populated at scale. Only generate when none exist yet (idempotent re-runs).
if not is_nil(member.membership_fee_type_id) do
member = Ash.load!(member, :membership_fee_cycles, actor: admin_user_with_role)
if Enum.empty?(member.membership_fee_cycles) do
{:ok, cycles, _} =
CycleGenerator.generate_cycles_for_member(member.id,
skip_lock?: true,
actor: admin_user_with_role
)
status = Enum.at(statuses, rem(i, length(statuses)))
Enum.each(cycles, fn cycle ->
if cycle.status != status do
cycle
|> Ash.Changeset.for_update(:update, %{status: status})
|> Ash.update!(actor: admin_user_with_role, domain: Mv.MembershipFees)
end
end)
end
end
if rem(i, 50) == 0, do: IO.puts("#{i}/#{count}")
end)
IO.puts("✅ Bulk seed complete: #{count} members.")