From 17fd5e13d50dc0d51fbb8d64ac0b2859c24f35d9 Mon Sep 17 00:00:00 2001 From: Moritz Date: Mon, 23 Feb 2026 22:10:51 +0100 Subject: [PATCH] Member: validate configurable and Vereinfacht-required fields Add validation for required member fields from settings and for Vereinfacht-required fields when integration is configured. --- lib/membership/member.ex | 47 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/lib/membership/member.ex b/lib/membership/member.ex index 7b70e89..114814a 100644 --- a/lib/membership/member.ex +++ b/lib/membership/member.ex @@ -544,6 +544,43 @@ defmodule Mv.Membership.Member do "Unable to validate required custom fields. Please try again or contact support."} end end + + # Validate member fields that are marked as required in settings or by Vereinfacht + validate fn changeset, _context -> + case Mv.Membership.get_settings() do + {:ok, settings} -> + required_config = settings.member_field_required || %{} + normalized = VisibilityConfig.normalize(required_config) + vereinfacht_required? = Mv.Config.vereinfacht_configured?() + + required_fields = + Enum.filter(Mv.Constants.member_fields(), fn field -> + field == :email || + (vereinfacht_required? && Mv.Constants.vereinfacht_required_field?(field)) || + Map.get(normalized, field, false) + end) + + missing = + Enum.filter(required_fields, fn field -> + value = Ash.Changeset.get_attribute(changeset, field) + not member_field_value_present?(field, value) + end) + + if Enum.empty?(missing) do + :ok + else + # Return first missing field error (Ash shows one at a time per field) + field = hd(missing) + + {:error, + field: field, message: Gettext.dgettext(MvWeb.Gettext, "default", "can't be blank")} + end + + {:error, _} -> + # If settings cannot be loaded, skip this validation (e.g. bootstrap) + :ok + end + end end attributes do @@ -1420,4 +1457,14 @@ defmodule Mv.Membership.Member do defp value_present?(_value, :email), do: false defp value_present?(_value, _type), do: false + + # Used by member-field-required validation (settings-driven required fields) + defp member_field_value_present?(_field, nil), do: false + + defp member_field_value_present?(_, value) when is_binary(value), + do: String.trim(value) != "" + + defp member_field_value_present?(_, %Date{}), do: true + defp member_field_value_present?(_, value) when is_struct(value, Date), do: true + defp member_field_value_present?(_, _), do: false end