feat(export): include Fee Type in CSV export

Payload and column_order when visible; allowlist, load, sort;
MembersCSV cell for :membership_fee_type.
This commit is contained in:
Moritz 2026-02-23 23:53:51 +01:00
parent 68ceaced0c
commit f3b213ecec
Signed by: moritz
GPG key ID: 1020A035E5DD0824
2 changed files with 63 additions and 11 deletions

View file

@ -59,6 +59,13 @@ defmodule Mv.Membership.MembersCSV do
if is_binary(value), do: value, else: "" if is_binary(value), do: value, else: ""
end end
defp cell_value(member, %{kind: :membership_fee_type, key: :membership_fee_type}) do
case Map.get(member, :membership_fee_type) do
%{name: name} when is_binary(name) -> name
_ -> ""
end
end
defp cell_value(member, %{kind: :groups, key: :groups}) do defp cell_value(member, %{kind: :groups, key: :groups}) do
groups = Map.get(member, :groups) || [] groups = Map.get(member, :groups) || []
format_groups(groups) format_groups(groups)

View file

@ -19,7 +19,7 @@ defmodule MvWeb.MemberExportController do
use Gettext, backend: MvWeb.Gettext use Gettext, backend: MvWeb.Gettext
@member_fields_allowlist (Mv.Constants.member_fields() |> Enum.map(&Atom.to_string/1)) ++ @member_fields_allowlist (Mv.Constants.member_fields() |> Enum.map(&Atom.to_string/1)) ++
["groups"] ["membership_fee_type", "groups"]
@computed_export_fields ["membership_fee_status"] @computed_export_fields ["membership_fee_status"]
@custom_field_prefix Mv.Constants.custom_field_prefix() @custom_field_prefix Mv.Constants.custom_field_prefix()
@ -238,6 +238,7 @@ defmodule MvWeb.MemberExportController do
parsed.computed_fields != [] and "membership_fee_status" in parsed.computed_fields parsed.computed_fields != [] and "membership_fee_status" in parsed.computed_fields
need_groups = "groups" in parsed.member_fields need_groups = "groups" in parsed.member_fields
need_membership_fee_type = "membership_fee_type" in parsed.member_fields
query = query =
Member Member
@ -246,6 +247,7 @@ defmodule MvWeb.MemberExportController do
|> load_custom_field_values_query(parsed.custom_field_ids) |> load_custom_field_values_query(parsed.custom_field_ids)
|> maybe_load_cycles(need_cycles, parsed.show_current_cycle) |> maybe_load_cycles(need_cycles, parsed.show_current_cycle)
|> maybe_load_groups(need_groups) |> maybe_load_groups(need_groups)
|> maybe_load_membership_fee_type(need_membership_fee_type)
query = query =
if parsed.selected_ids != [] do if parsed.selected_ids != [] do
@ -296,6 +298,12 @@ defmodule MvWeb.MemberExportController do
Ash.Query.load(query, groups: [:id, :name]) Ash.Query.load(query, groups: [:id, :name])
end end
defp maybe_load_membership_fee_type(query, false), do: query
defp maybe_load_membership_fee_type(query, true) do
Ash.Query.load(query, membership_fee_type: [:id, :name])
end
# Adds computed field values to members (e.g. membership_fee_status) # Adds computed field values to members (e.g. membership_fee_status)
defp add_computed_fields(members, computed_fields, show_current_cycle) do defp add_computed_fields(members, computed_fields, show_current_cycle) do
if "membership_fee_status" in computed_fields do if "membership_fee_status" in computed_fields do
@ -343,26 +351,45 @@ defmodule MvWeb.MemberExportController do
defp maybe_sort_export(query, field, order) when is_binary(field) do defp maybe_sort_export(query, field, order) when is_binary(field) do
cond do cond do
field == "groups" -> field == "groups" ->
# Groups sort → in-memory nach dem Read (wie Tabelle)
{query, true} {query, true}
field == "membership_fee_type" ->
apply_membership_fee_type_sort_export(query, order)
custom_field_sort?(field) -> custom_field_sort?(field) ->
# Custom field sort → in-memory nach dem Read (wie Tabelle)
{query, true} {query, true}
true -> true ->
field_atom = String.to_existing_atom(field) apply_member_field_sort_export(query, field, order)
if field_atom in (Mv.Constants.member_fields() -- [:notes]) do
{Ash.Query.sort(query, [{field_atom, String.to_existing_atom(order)}]), false}
else
{query, false}
end
end end
rescue rescue
ArgumentError -> {query, false} ArgumentError -> {query, false}
end end
defp apply_membership_fee_type_sort_export(query, order) do
order_atom = if order == "desc", do: :desc, else: :asc
{Ash.Query.sort(query, membership_fee_type_id: order_atom), false}
end
defp apply_member_field_sort_export(query, field, order) do
field_atom = String.to_existing_atom(field)
sortable =
field_atom in (Mv.Constants.member_fields() -- [:notes]) or
field_atom == :membership_fee_type
if sortable do
order_atom = if order == "desc", do: :desc, else: :asc
sort_field =
if field_atom == :membership_fee_type, do: :membership_fee_type_id, else: field_atom
{Ash.Query.sort(query, [{sort_field, order_atom}]), false}
else
{query, false}
end
end
defp custom_field_sort?(field), do: String.starts_with?(field, @custom_field_prefix) defp custom_field_sort?(field), do: String.starts_with?(field, @custom_field_prefix)
# ------------------------------------------------------------------ # ------------------------------------------------------------------
@ -488,6 +515,19 @@ defmodule MvWeb.MemberExportController do
} }
end) end)
membership_fee_type_col =
if "membership_fee_type" in parsed.member_fields do
[
%{
header: membership_fee_type_field_header(conn),
kind: :membership_fee_type,
key: :membership_fee_type
}
]
else
[]
end
groups_col = groups_col =
if "groups" in parsed.member_fields do if "groups" in parsed.member_fields do
[ [
@ -519,7 +559,8 @@ defmodule MvWeb.MemberExportController do
end) end)
|> Enum.reject(&is_nil/1) |> Enum.reject(&is_nil/1)
member_cols ++ computed_cols ++ groups_col ++ custom_cols # Table order: ... membership_fee_start_date, membership_fee_type, membership_fee_status, groups, custom
member_cols ++ membership_fee_type_col ++ computed_cols ++ groups_col ++ custom_cols
end end
# --- headers: use MemberFields.label for translations --- # --- headers: use MemberFields.label for translations ---
@ -559,6 +600,10 @@ defmodule MvWeb.MemberExportController do
cf.name cf.name
end end
defp membership_fee_type_field_header(_conn) do
MemberFields.label(:membership_fee_type)
end
defp groups_field_header(_conn) do defp groups_field_header(_conn) do
MemberFields.label(:groups) MemberFields.label(:groups)
end end