Vereinfacht accounting software API closes #431 #432

Merged
moritz merged 31 commits from feature/vereinfacht_api into main 2026-02-23 21:18:46 +01:00
Showing only changes of commit 140e4a9054 - Show all commits

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