# 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-@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. Cycles are only generated when none exist yet; the status # is (re-)applied on every run so a run interrupted between generation and the # status update still converges (idempotent — the update is a no-op once set). if not is_nil(member.membership_fee_type_id) do member = Ash.load!(member, :membership_fee_cycles, actor: admin_user_with_role) cycles = if Enum.empty?(member.membership_fee_cycles) do {:ok, generated, _} = CycleGenerator.generate_cycles_for_member(member.id, skip_lock?: true, actor: admin_user_with_role ) generated else member.membership_fee_cycles end 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 if rem(i, 50) == 0, do: IO.puts(" … #{i}/#{count}") end) IO.puts("✅ Bulk seed complete: #{count} members.")