Merge branch 'main' into feature/export_csv
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
carla 2026-02-06 08:02:05 +01:00
commit 36e57b24be
102 changed files with 5332 additions and 1219 deletions

View file

@ -294,6 +294,7 @@ msgstr "Beschreibung"
msgid "Edit User"
msgstr "Benutzer*in bearbeiten"
#: lib/mv_web/live/user_live/index.html.heex
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "Enabled"
@ -471,6 +472,7 @@ msgid "Include both letters and numbers"
msgstr "Buchstaben und Zahlen verwenden"
#: lib/mv_web/live/user_live/form.ex
#: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Password"
msgstr "Passwort"
@ -958,7 +960,6 @@ msgid "Last name"
msgstr "Nachname"
#: lib/mv_web/components/core_components.ex
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "None"
msgstr "Keine"
@ -1670,6 +1671,9 @@ msgstr "Profil"
#: lib/mv_web/live/role_live/form.ex
#: lib/mv_web/live/role_live/show.ex
#: lib/mv_web/live/user_live/form.ex
#: lib/mv_web/live/user_live/index.html.heex
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "Role"
msgstr "Rolle"
@ -1965,11 +1969,6 @@ msgstr "Bezahlstatus"
msgid "Reset"
msgstr "Zurücksetzen"
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format
msgid "Only administrators can regenerate cycles"
msgstr "Nur Administrator*innen können Zyklen regenerieren"
#: lib/mv_web/live/import_export_live.ex
#, elixir-autogen, elixir-format
msgid " (Field: %{field})"
@ -2307,7 +2306,7 @@ msgstr "Import/Export"
#: lib/mv_web/live/import_export_live.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "You do not have permission to access this page."
msgstr "Du hast keine Berechtigung, auf diese*n Benutzer*in zuzugreifen"
msgstr "Du hast keine Berechtigung, auf diese Seite zuzugreifen."
#: lib/mv_web/live/import_export_live.ex
#, elixir-autogen, elixir-format, fuzzy
@ -2319,21 +2318,6 @@ msgstr "Mitgliederdaten verwalten"
msgid "Use the data field name as the CSV column header in your file. Data fields must exist in Mila before importing, so they must be listed in the list of member data (like e-mail or first name). Unknown data field columns will be ignored with a warning."
msgstr "Verwende die Namen der Datenfelder als Spaltennamen in der CSV Datei. Datenfelder müssen in Mila bereits angelegt sein, damit sie importiert werden können. sie müssen in der Liste der Mitgliederdaten als Datenfeld enthalten sein (z.B. E-Mail). Spalten mit unbekannten Spaltenüberschriften werden mit einer Warnung ignoriert."
#: lib/mv_web/live/member_live/index.html.heex
#, elixir-autogen, elixir-format, fuzzy
msgid "Export members to CSV"
msgstr "Mitglieder importieren (CSV)"
#: lib/mv_web/live/member_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Export to CSV"
msgstr "Als CSV exportieren"
#: lib/mv_web/live/member_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "all"
msgstr "alle"
#~ #: lib/mv_web/live/global_settings_live.ex
#~ #, elixir-autogen, elixir-format, fuzzy
#~ msgid "Custom Fields in CSV Import"
@ -2349,7 +2333,7 @@ msgstr "alle"
#~ msgid "Individual data fields must be created in Mila before importing. Use the field name as the CSV column header. Unknown custom field columns will be ignored with a warning."
#~ msgstr "Individuelle Datenfelder müssen in Mila erstellt werden, bevor sie importiert werden können. Verwende den Namen des Datenfeldes als CSV-Spaltenüberschrift. Unbekannte Spaltenüberschriften werden mit einer Warnung ignoriert."
#~ #: lib/mv_web/live/global_settings_live.ex
#~ #: lib/mv_web/live/member_live/show/membership_fees_component.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Manage Custom Fields"
#~ msgstr "Benutzerdefinierte Felder verwalten"
#~ msgid "Only administrators can regenerate cycles"
#~ msgstr "Nur Administrator*innen können Zyklen regenerieren"

View file

@ -295,6 +295,7 @@ msgstr ""
msgid "Edit User"
msgstr ""
#: lib/mv_web/live/user_live/index.html.heex
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "Enabled"
@ -472,6 +473,7 @@ msgid "Include both letters and numbers"
msgstr ""
#: lib/mv_web/live/user_live/form.ex
#: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Password"
msgstr ""
@ -959,7 +961,6 @@ msgid "Last name"
msgstr ""
#: lib/mv_web/components/core_components.ex
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "None"
msgstr ""
@ -1671,6 +1672,9 @@ msgstr ""
#: lib/mv_web/live/role_live/form.ex
#: lib/mv_web/live/role_live/show.ex
#: lib/mv_web/live/user_live/form.ex
#: lib/mv_web/live/user_live/index.html.heex
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "Role"
msgstr ""
@ -1966,11 +1970,6 @@ msgstr ""
msgid "Reset"
msgstr ""
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format
msgid "Only administrators can regenerate cycles"
msgstr ""
#: lib/mv_web/live/import_export_live.ex
#, elixir-autogen, elixir-format
msgid " (Field: %{field})"
@ -2334,3 +2333,50 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "all"
msgstr ""
#: lib/mv/membership/member/validations/email_change_permission.ex
#, elixir-autogen, elixir-format
msgid "Only administrators or the linked user can change the email for members linked to users"
msgstr ""
#: lib/mv_web/live/user_live/form.ex
#, elixir-autogen, elixir-format
msgid "Select role..."
msgstr ""
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format
msgid "You are not allowed to perform this action."
msgstr ""
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format
msgid "Select a membership fee type"
msgstr ""
#: lib/mv_web/live/user_live/index.html.heex
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "Linked"
msgstr ""
#: lib/mv_web/live/user_live/index.html.heex
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "OIDC"
msgstr ""
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "Not linked"
msgstr ""
#: lib/mv_web/live/user_live/form.ex
#, elixir-autogen, elixir-format
msgid "SSO / OIDC user"
msgstr ""
#: lib/mv_web/live/user_live/form.ex
#, elixir-autogen, elixir-format
msgid "This user is linked via SSO (Single Sign-On). A password set or changed here only affects login with email and password in this application. It does not change the password in your identity provider (e.g. Authentik). To change the SSO password, use the identity provider or your organization's IT."
msgstr ""

View file

@ -295,6 +295,7 @@ msgstr ""
msgid "Edit User"
msgstr ""
#: lib/mv_web/live/user_live/index.html.heex
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "Enabled"
@ -472,6 +473,7 @@ msgid "Include both letters and numbers"
msgstr ""
#: lib/mv_web/live/user_live/form.ex
#: lib/mv_web/live/user_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Password"
msgstr ""
@ -959,7 +961,6 @@ msgid "Last name"
msgstr ""
#: lib/mv_web/components/core_components.ex
#: lib/mv_web/live/member_live/form.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "None"
msgstr ""
@ -1671,6 +1672,9 @@ msgstr ""
#: lib/mv_web/live/role_live/form.ex
#: lib/mv_web/live/role_live/show.ex
#: lib/mv_web/live/user_live/form.ex
#: lib/mv_web/live/user_live/index.html.heex
#: lib/mv_web/live/user_live/show.ex
#, elixir-autogen, elixir-format
msgid "Role"
msgstr ""
@ -1966,11 +1970,6 @@ msgstr ""
msgid "Reset"
msgstr ""
#: lib/mv_web/live/member_live/show/membership_fees_component.ex
#, elixir-autogen, elixir-format
msgid "Only administrators can regenerate cycles"
msgstr ""
#: lib/mv_web/live/import_export_live.ex
#, elixir-autogen, elixir-format
msgid " (Field: %{field})"
@ -2350,7 +2349,7 @@ msgstr ""
#~ msgid "Individual data fields must be created in Mila before importing. Use the field name as the CSV column header. Unknown custom field columns will be ignored with a warning."
#~ msgstr ""
#~ #: lib/mv_web/live/global_settings_live.ex
#~ #: lib/mv_web/live/member_live/show/membership_fees_component.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Manage Custom Fields"
#~ msgid "Only administrators can regenerate cycles"
#~ msgstr ""

View file

@ -10,7 +10,7 @@ alias Mv.MembershipFees.CycleGenerator
require Ash.Query
# Create example membership fee types
# Create example membership fee types (no admin user yet; skip authorization for bootstrap)
for fee_type_attrs <- [
%{
name: "Standard (Jährlich)",
@ -39,7 +39,12 @@ for fee_type_attrs <- [
] do
MembershipFeeType
|> Ash.Changeset.for_create(:create, fee_type_attrs)
|> Ash.create!(upsert?: true, upsert_identity: :unique_name)
|> Ash.create!(
upsert?: true,
upsert_identity: :unique_name,
authorize?: false,
domain: Mv.MembershipFees
)
end
for attrs <- [
@ -127,8 +132,15 @@ for attrs <- [
)
end
# Get admin email from environment variable or use default
# Admin email: default for dev/test so seed_admin has a target
admin_email = System.get_env("ADMIN_EMAIL") || "admin@localhost"
System.put_env("ADMIN_EMAIL", admin_email)
# In dev/test, set fallback password so seed_admin creates the admin user when none is set
if Mix.env() in [:dev, :test] and is_nil(System.get_env("ADMIN_PASSWORD")) and
is_nil(System.get_env("ADMIN_PASSWORD_FILE")) do
System.put_env("ADMIN_PASSWORD", "testpassword")
end
# Create all authorization roles (idempotent - creates only if they don't exist)
# Roles are created using create_role_with_system_flag to allow setting is_system_role
@ -209,39 +221,9 @@ if is_nil(admin_role) do
raise "Failed to create or find admin role. Cannot proceed with member seeding."
end
# Assign admin role to user with ADMIN_EMAIL (if user exists)
# This handles both existing users (e.g., from OIDC) and newly created users
case Accounts.User
|> Ash.Query.filter(email == ^admin_email)
|> Ash.read_one(domain: Mv.Accounts, authorize?: false) do
{:ok, existing_admin_user} when not is_nil(existing_admin_user) ->
# User already exists (e.g., via OIDC) - assign admin role
# Use authorize?: false for bootstrap - this is initial setup
existing_admin_user
|> Ash.Changeset.for_update(:update, %{})
|> Ash.Changeset.manage_relationship(:role, admin_role, type: :append_and_remove)
|> Ash.update!(authorize?: false)
{:ok, nil} ->
# User doesn't exist - create admin user with password
# Use authorize?: false for bootstrap - no admin user exists yet to use as actor
Accounts.create_user!(%{email: admin_email},
upsert?: true,
upsert_identity: :unique_email,
authorize?: false
)
|> Ash.Changeset.for_update(:admin_set_password, %{password: "testpassword"})
|> Ash.update!(authorize?: false)
|> then(fn user ->
user
|> Ash.Changeset.for_update(:update, %{})
|> Ash.Changeset.manage_relationship(:role, admin_role, type: :append_and_remove)
|> Ash.update!(authorize?: false)
end)
{:error, error} ->
raise "Failed to check for existing admin user: #{inspect(error)}"
end
# Create/update admin user via Release.seed_admin (uses ADMIN_EMAIL, ADMIN_PASSWORD / ADMIN_PASSWORD_FILE).
# Reduces duplication and exercises the same path as production entrypoint.
Mv.Release.seed_admin()
# Load admin user with role for use as actor in member operations
# This ensures all member operations have proper authorization
@ -299,12 +281,12 @@ case Accounts.User
IO.puts("SystemActor will fall back to admin user (#{admin_email})")
end
# Load all membership fee types for assignment
# Load all membership fee types for assignment (admin actor for authorization)
# Sort by name to ensure deterministic order
all_fee_types =
MembershipFeeType
|> Ash.Query.sort(name: :asc)
|> Ash.read!()
|> Ash.read!(actor: admin_user_with_role, domain: Mv.MembershipFees)
|> Enum.to_list()
# Create sample members for testing - use upsert to prevent duplicates
@ -452,7 +434,8 @@ Enum.each(member_attrs_list, fn member_attrs ->
end
end)
# Create additional users for user-member linking examples
# Create additional users for user-member linking examples (no password by default)
# Only admin gets a password (admin_set_password when created); all other users have no password.
additional_users = [
%{email: "hans.mueller@example.de"},
%{email: "greta.schmidt@example.de"},
@ -462,15 +445,12 @@ additional_users = [
created_users =
Enum.map(additional_users, fn user_attrs ->
# Use admin user as actor for additional user creation (not bootstrap)
user =
Accounts.create_user!(user_attrs,
upsert?: true,
upsert_identity: :unique_email,
actor: admin_user_with_role
)
|> Ash.Changeset.for_update(:admin_set_password, %{password: "testpassword"})
|> Ash.update!(actor: admin_user_with_role)
# Reload user to ensure all fields (including member_id) are loaded
Accounts.User
@ -744,7 +724,14 @@ IO.puts("📝 Created sample data:")
IO.puts(" - Global settings: club_name = #{default_club_name}")
IO.puts(" - Membership fee types: 4 types (Yearly, Half-yearly, Quarterly, Monthly)")
IO.puts(" - Custom fields: 12 fields (String, Date, Boolean, Email, + 8 realistic fields)")
IO.puts(" - Admin user: #{admin_email} (password: testpassword)")
password_configured =
System.get_env("ADMIN_PASSWORD") != nil or System.get_env("ADMIN_PASSWORD_FILE") != nil
IO.puts(
" - Admin user: #{admin_email} (password: #{if password_configured, do: "set", else: "not set"})"
)
IO.puts(" - Sample members: Hans, Greta, Friedrich")
IO.puts(