feat: add membership fee status to columns and dropdown

This commit is contained in:
carla 2026-02-09 13:34:38 +01:00
parent 36e57b24be
commit e1266944b1
7 changed files with 725 additions and 514 deletions

View file

@ -14,8 +14,12 @@ defmodule MvWeb.MemberExportController do
alias Mv.Membership.CustomField
alias Mv.Membership.Member
alias Mv.Membership.MembersCSV
alias MvWeb.Translations.MemberFields
alias MvWeb.MemberLive.Index.MembershipFeeStatus
use Gettext, backend: MvWeb.Gettext
@member_fields_allowlist Mv.Constants.member_fields() |> Enum.map(&Atom.to_string/1)
@computed_export_fields ["membership_fee_status"]
@custom_field_prefix Mv.Constants.custom_field_prefix()
def export(conn, params) do
@ -58,17 +62,38 @@ defmodule MvWeb.MemberExportController do
end
defp parse_and_validate(params) do
member_fields = filter_allowed_member_fields(extract_list(params, "member_fields"))
{selectable_member_fields, computed_fields} = split_member_fields(member_fields)
%{
selected_ids: filter_valid_uuids(extract_list(params, "selected_ids")),
member_fields: filter_allowed_member_fields(extract_list(params, "member_fields")),
computed_fields: filter_existing_atoms(extract_list(params, "computed_fields")),
member_fields: member_fields,
selectable_member_fields: selectable_member_fields,
computed_fields:
computed_fields ++ filter_existing_atoms(extract_list(params, "computed_fields")),
custom_field_ids: filter_valid_uuids(extract_list(params, "custom_field_ids")),
query: extract_string(params, "query"),
sort_field: extract_string(params, "sort_field"),
sort_order: extract_sort_order(params)
sort_order: extract_sort_order(params),
show_current_cycle: extract_boolean(params, "show_current_cycle")
}
end
defp split_member_fields(member_fields) do
domain_fields = Mv.Constants.member_fields() |> Enum.map(&Atom.to_string/1)
selectable = Enum.filter(member_fields, fn f -> f in domain_fields end)
computed = Enum.filter(member_fields, fn f -> f in @computed_export_fields end)
{selectable, computed}
end
defp extract_boolean(params, key) do
case Map.get(params, key) do
true -> true
"true" -> true
_ -> false
end
end
defp filter_existing_atoms(list) when is_list(list) do
list
|> Enum.filter(&is_binary/1)
@ -198,13 +223,17 @@ defmodule MvWeb.MemberExportController do
end
defp load_members_for_export(actor, parsed, custom_fields_by_id) do
select_fields = [:id] ++ Enum.map(parsed.member_fields, &String.to_existing_atom/1)
select_fields = [:id] ++ Enum.map(parsed.selectable_member_fields, &String.to_existing_atom/1)
need_cycles =
parsed.computed_fields != [] and "membership_fee_status" in parsed.computed_fields
query =
Member
|> Ash.Query.new()
|> Ash.Query.select(select_fields)
|> load_custom_field_values_query(parsed.custom_field_ids)
|> maybe_load_cycles(need_cycles, parsed.show_current_cycle)
query =
if parsed.selected_ids != [] do
@ -232,6 +261,9 @@ defmodule MvWeb.MemberExportController do
members
end
# Calculate membership_fee_status for computed fields
members = add_computed_fields(members, parsed.computed_fields, parsed.show_current_cycle)
{:ok, members}
{:error, %Ash.Error.Forbidden{}} ->
@ -239,6 +271,31 @@ defmodule MvWeb.MemberExportController do
end
end
defp maybe_load_cycles(query, false, _show_current), do: query
defp maybe_load_cycles(query, true, show_current) do
MembershipFeeStatus.load_cycles_for_members(query, show_current)
end
# Adds computed field values to members (e.g. membership_fee_status)
defp add_computed_fields(members, computed_fields, show_current_cycle) do
if "membership_fee_status" in computed_fields do
Enum.map(members, fn member ->
status = MembershipFeeStatus.get_cycle_status_for_member(member, show_current_cycle)
status_string = format_membership_fee_status(status)
Map.put(member, :membership_fee_status, status_string)
end)
else
members
end
end
# Formats membership fee status as German string
defp format_membership_fee_status(:paid), do: gettext("paid")
defp format_membership_fee_status(:unpaid), do: gettext("unpaid")
defp format_membership_fee_status(:suspended), do: gettext("suspended")
defp format_membership_fee_status(nil), do: ""
defp load_custom_field_values_query(query, []), do: query
defp load_custom_field_values_query(query, custom_field_ids) do
@ -360,7 +417,7 @@ defmodule MvWeb.MemberExportController do
defp build_columns(conn, parsed, custom_fields_by_id) do
member_cols =
Enum.map(parsed.member_fields, fn field ->
Enum.map(parsed.selectable_member_fields, fn field ->
%{
header: member_field_header(conn, field),
kind: :member_field,
@ -373,7 +430,7 @@ defmodule MvWeb.MemberExportController do
%{
header: computed_field_header(conn, key),
kind: :computed,
key: key
key: String.to_existing_atom(key)
}
end)
@ -398,15 +455,36 @@ defmodule MvWeb.MemberExportController do
member_cols ++ computed_cols ++ custom_cols
end
# --- headers: hier solltest du idealerweise eure bestehenden "display name" Helfer verwenden ---
# --- headers: use MemberFields.label for translations ---
defp member_field_header(_conn, field) when is_binary(field) do
# TODO: hier euren bestehenden display-name helper verwenden (wie Tabelle)
humanize_field(field)
field
|> String.to_existing_atom()
|> MemberFields.label()
rescue
ArgumentError ->
# Fallback for unknown fields
humanize_field(field)
end
defp computed_field_header(_conn, key) when is_atom(key) do
# Map export-only alias to canonical UI key for translation
atom_key = if key == :payment_status, do: :membership_fee_status, else: key
MemberFields.label(atom_key)
end
defp computed_field_header(_conn, key) when is_binary(key) do
# TODO: display-name helper für computed fields verwenden
humanize_field(key)
# Map export-only alias to canonical UI key for translation
atom_key =
case key do
"payment_status" -> :membership_fee_status
_ -> String.to_existing_atom(key)
end
MemberFields.label(atom_key)
rescue
ArgumentError ->
# Fallback for unknown computed fields
humanize_field(key)
end
defp custom_field_header(_conn, cf) do
@ -417,7 +495,8 @@ defmodule MvWeb.MemberExportController do
defp humanize_field(str) do
str
|> String.replace("_", " ")
|> String.capitalize()
|> String.split()
|> Enum.map_join(" ", &String.capitalize/1)
end
defp extract_sort_value(%Ash.Union{value: value, type: type}, _),