SyncContact: only run when relevant attributes changed

- Sync on create; on update only when synced attrs changed or no contact_id yet
- Reduces unnecessary API calls on unrelated member updates
This commit is contained in:
Moritz 2026-02-23 19:21:18 +01:00
parent 1188320844
commit 140e4a9054
Signed by: moritz
GPG key ID: 1020A035E5DD0824

View file

@ -7,20 +7,57 @@ defmodule Mv.Vereinfacht.Changes.SyncContact do
Runs in `after_transaction` so the member is persisted first. API failures are logged Runs in `after_transaction` so the member is persisted first. API failures are logged
but do not block the member operation. Requires Vereinfacht to be configured but do not block the member operation. Requires Vereinfacht to be configured
(Mv.Config.vereinfacht_configured?/0). (Mv.Config.vereinfacht_configured?/0).
Only runs when relevant data changed: on create always; on update only when
first_name, last_name, email, street, house_number, postal_code, or city changed,
or when the member has no vereinfacht_contact_id yet (to avoid unnecessary API calls).
""" """
use Ash.Resource.Change use Ash.Resource.Change
require Logger require Logger
@synced_attributes [
:first_name,
:last_name,
:email,
:street,
:house_number,
:postal_code,
:city
]
@impl true @impl true
def change(changeset, _opts, _context) do def change(changeset, _opts, _context) do
if Mv.Config.vereinfacht_configured?() do if Mv.Config.vereinfacht_configured?() and sync_relevant?(changeset) do
Ash.Changeset.after_transaction(changeset, &sync_after_transaction/2) Ash.Changeset.after_transaction(changeset, &sync_after_transaction/2)
else else
changeset changeset
end end
end end
defp sync_relevant?(changeset) do
case changeset.action_type do
:create -> true
:update -> relevant_update?(changeset)
_ -> false
end
end
defp relevant_update?(changeset) do
any_synced_attr_changed? =
Enum.any?(@synced_attributes, &Ash.Changeset.changing_attribute?(changeset, &1))
record = changeset.data
no_contact_id_yet? = record && blank_contact_id?(record.vereinfacht_contact_id)
any_synced_attr_changed? or no_contact_id_yet?
end
defp blank_contact_id?(nil), do: true
defp blank_contact_id?(""), do: true
defp blank_contact_id?(s) when is_binary(s), do: String.trim(s) == ""
defp blank_contact_id?(_), do: false
# Ash calls after_transaction with (changeset, result) only - 2 args. # Ash calls after_transaction with (changeset, result) only - 2 args.
defp sync_after_transaction(_changeset, {:ok, member}) do defp sync_after_transaction(_changeset, {:ok, member}) do
case Mv.Vereinfacht.sync_member(member) do case Mv.Vereinfacht.sync_member(member) do