diff --git a/lib/mv/accounts/user/validations/email_not_used_by_other_member.ex b/lib/mv/accounts/user/validations/email_not_used_by_other_member.ex index cf3c624..d3cb776 100644 --- a/lib/mv/accounts/user/validations/email_not_used_by_other_member.ex +++ b/lib/mv/accounts/user/validations/email_not_used_by_other_member.ex @@ -1,9 +1,7 @@ defmodule Mv.Accounts.User.Validations.EmailNotUsedByOtherMember do @moduledoc """ - Validates that the user's email is not already used by another member - (unless that member is linked to this user). - - This prevents email conflicts when syncing between users and members. + Validates that the user's email is not already used by another member. + Allows syncing with linked member (excludes member_id from check). """ use Ash.Resource.Validation @@ -11,42 +9,32 @@ defmodule Mv.Accounts.User.Validations.EmailNotUsedByOtherMember do def validate(changeset, _opts, _context) do case Ash.Changeset.fetch_change(changeset, :email) do {:ok, new_email} -> - check_email_not_used_by_other_member(changeset, new_email) + member_id = Ash.Changeset.get_attribute(changeset, :member_id) + check_email_uniqueness(new_email, member_id) :error -> - # Email not being changed :ok end end - defp check_email_not_used_by_other_member(changeset, new_email) do - member_id = Ash.Changeset.get_attribute(changeset, :member_id) - - # Check if any member has this email - # Exclude the member linked to this user (if any) + defp check_email_uniqueness(new_email, exclude_member_id) do query = Mv.Membership.Member |> Ash.Query.filter(email == ^to_string(new_email)) - |> then(fn q -> - if member_id do - Ash.Query.filter(q, id != ^member_id) - else - q - end - end) + |> maybe_exclude_id(exclude_member_id) case Ash.read(query) do {:ok, []} -> - # No conflicting member found :ok - {:ok, members} when is_list(members) and length(members) > 0 -> - # Email is already used by another member + {:ok, _} -> {:error, field: :email, message: "is already used by another member", value: new_email} {:error, _} -> - # Error reading members - be safe and allow :ok end end + + defp maybe_exclude_id(query, nil), do: query + defp maybe_exclude_id(query, id), do: Ash.Query.filter(query, id != ^id) end diff --git a/lib/mv/membership/member/validations/email_not_used_by_other_user.ex b/lib/mv/membership/member/validations/email_not_used_by_other_user.ex index f48613b..6c544b5 100644 --- a/lib/mv/membership/member/validations/email_not_used_by_other_user.ex +++ b/lib/mv/membership/member/validations/email_not_used_by_other_user.ex @@ -1,9 +1,7 @@ defmodule Mv.Membership.Member.Validations.EmailNotUsedByOtherUser do @moduledoc """ - Validates that the member's email is not already used by another user - (unless that user is linked to this member). - - This prevents email conflicts when syncing between users and members. + Validates that the member's email is not already used by another user. + Allows syncing with linked user (excludes linked user from check). """ use Ash.Resource.Validation @@ -11,49 +9,39 @@ defmodule Mv.Membership.Member.Validations.EmailNotUsedByOtherUser do def validate(changeset, _opts, _context) do case Ash.Changeset.fetch_change(changeset, :email) do {:ok, new_email} -> - check_email_not_used_by_other_user(changeset, new_email) + linked_user_id = get_linked_user_id(changeset.data) + check_email_uniqueness(new_email, linked_user_id) :error -> - # Email not being changed :ok end end - defp check_email_not_used_by_other_user(changeset, new_email) do - # Load the user relationship to check if this member is linked to a user - member_with_user = - case Ash.load(changeset.data, :user) do - {:ok, loaded} -> loaded - {:error, _} -> changeset.data - end - - linked_user_id = if member_with_user.user, do: member_with_user.user.id, else: nil - - # Check if any user has this email (case-insensitive) - # Exclude the user linked to this member (if any) + defp check_email_uniqueness(new_email, exclude_user_id) do query = Mv.Accounts.User |> Ash.Query.filter(email == ^new_email) - |> then(fn q -> - if linked_user_id do - Ash.Query.filter(q, id != ^linked_user_id) - else - q - end - end) + |> maybe_exclude_id(exclude_user_id) case Ash.read(query) do {:ok, []} -> - # No conflicting user found :ok - {:ok, users} when is_list(users) and length(users) > 0 -> - # Email is already used by another user + {:ok, _} -> {:error, field: :email, message: "is already used by another user", value: new_email} {:error, _} -> - # Error reading users - be safe and allow :ok end end + + defp maybe_exclude_id(query, nil), do: query + defp maybe_exclude_id(query, id), do: Ash.Query.filter(query, id != ^id) + + defp get_linked_user_id(member_data) do + case Ash.load(member_data, :user) do + {:ok, %{user: %{id: id}}} -> id + _ -> nil + end + end end