PhoenixKitCRM.Contacts (PhoenixKitCRM v0.2.4)

Copy Markdown View Source

Context for CRM contacts — CRUD, soft-delete, the (v1 single) company membership, and the optional login-user connection.

The user connection mirrors phoenix_kit_staff's flow but is opt-in: a contact has no user_uuid until connect_user/2 is called (driven by the form's "allow login" checkbox). It uses find-or-create — an existing user by email is linked; if none exists a placeholder is registered (tagged custom_fields.source = "crm_contact"), which the person can later claim by registering / signing in with that email.

Summary

Functions

Connects a contact to a login user by email (staff-style find-or-create). Existing user by email → linked; otherwise a placeholder user is registered. Rolls back a just-created placeholder if the link fails. No-op-safe to call on an already-linked contact (re-links).

Permanently deletes a contact (cascades memberships + interactions).

Disconnects a contact from its login user (unlinks only; never deletes the user).

Finds an existing user by email, or registers a placeholder with no usable password (tagged custom_fields.source = "crm_contact").

The (at most one) contact linked to a given login user, or nil.

Contacts for the given uuids (any status) — for comment back-link resolution.

Lists contacts. Excludes trashed by default; preloads the primary company membership (with company) and the linked user.

The contact's primary company membership (or the first), or nil.

Searches contacts by name/email (case-insensitive) for the parties picker. Excludes trashed and any uuids in exclude_uuids (e.g. the contact whose page the interaction is being logged on — they're already the subject).

Sets the contact's primary company membership to the given company, with free-form role + department. v1 manages exactly one company per contact via the form, so this replaces the contact's membership set. A blank/nil company clears it.

Soft-deletes a contact (status → trashed, stashing the prior status).

Functions

change_contact(contact, attrs \\ %{})

@spec change_contact(PhoenixKitCRM.Schemas.Contact.t(), map()) :: Ecto.Changeset.t()

connect_user(contact, email)

@spec connect_user(PhoenixKitCRM.Schemas.Contact.t(), String.t()) ::
  {:ok, PhoenixKitCRM.Schemas.Contact.t(), :existing | :created}
  | {:error, atom() | Ecto.Changeset.t()}

Connects a contact to a login user by email (staff-style find-or-create). Existing user by email → linked; otherwise a placeholder user is registered. Rolls back a just-created placeholder if the link fails. No-op-safe to call on an already-linked contact (re-links).

count_contacts(opts \\ [])

@spec count_contacts(keyword()) :: non_neg_integer()

create_contact(attrs)

@spec create_contact(map()) ::
  {:ok, PhoenixKitCRM.Schemas.Contact.t()} | {:error, Ecto.Changeset.t()}

delete_contact(contact)

@spec delete_contact(PhoenixKitCRM.Schemas.Contact.t()) ::
  {:ok, PhoenixKitCRM.Schemas.Contact.t()} | {:error, Ecto.Changeset.t()}

Permanently deletes a contact (cascades memberships + interactions).

disconnect_user(contact)

@spec disconnect_user(PhoenixKitCRM.Schemas.Contact.t()) ::
  {:ok, PhoenixKitCRM.Schemas.Contact.t()} | {:error, Ecto.Changeset.t()}

Disconnects a contact from its login user (unlinks only; never deletes the user).

find_or_create_user_by_email(email)

@spec find_or_create_user_by_email(String.t()) ::
  {:ok, PhoenixKit.Users.Auth.User.t(), :existing | :created}
  | {:error, atom() | Ecto.Changeset.t()}

Finds an existing user by email, or registers a placeholder with no usable password (tagged custom_fields.source = "crm_contact").

get_by_user_uuid(user_uuid)

@spec get_by_user_uuid(UUIDv7.t() | String.t() | nil) ::
  PhoenixKitCRM.Schemas.Contact.t() | nil

The (at most one) contact linked to a given login user, or nil.

get_contact(uuid)

@spec get_contact(UUIDv7.t() | String.t() | nil) ::
  PhoenixKitCRM.Schemas.Contact.t() | nil

list_by_uuids(uuids)

@spec list_by_uuids([binary()]) :: [PhoenixKitCRM.Schemas.Contact.t()]

Contacts for the given uuids (any status) — for comment back-link resolution.

list_contacts(opts \\ [])

@spec list_contacts(keyword()) :: [PhoenixKitCRM.Schemas.Contact.t()]

Lists contacts. Excludes trashed by default; preloads the primary company membership (with company) and the linked user.

primary_membership(contact)

The contact's primary company membership (or the first), or nil.

restore_contact(contact)

@spec restore_contact(PhoenixKitCRM.Schemas.Contact.t()) ::
  {:ok, PhoenixKitCRM.Schemas.Contact.t()}
  | {:error, atom() | Ecto.Changeset.t()}

search_contacts(query, limit \\ 8, exclude_uuids \\ [])

@spec search_contacts(String.t(), pos_integer(), [binary()]) :: [
  PhoenixKitCRM.Schemas.Contact.t()
]

Searches contacts by name/email (case-insensitive) for the parties picker. Excludes trashed and any uuids in exclude_uuids (e.g. the contact whose page the interaction is being logged on — they're already the subject).

set_primary_company(contact, company_uuid, role, department)

@spec set_primary_company(
  PhoenixKitCRM.Schemas.Contact.t(),
  UUIDv7.t() | String.t() | nil,
  String.t() | nil,
  String.t() | nil
) ::
  {:ok, PhoenixKitCRM.Schemas.CompanyMembership.t() | nil}
  | {:error, Ecto.Changeset.t()}

Sets the contact's primary company membership to the given company, with free-form role + department. v1 manages exactly one company per contact via the form, so this replaces the contact's membership set. A blank/nil company clears it.

trash_contact(contact)

@spec trash_contact(PhoenixKitCRM.Schemas.Contact.t()) ::
  {:ok, PhoenixKitCRM.Schemas.Contact.t()}
  | {:error, atom() | Ecto.Changeset.t()}

Soft-deletes a contact (status → trashed, stashing the prior status).

update_contact(contact, attrs)

@spec update_contact(PhoenixKitCRM.Schemas.Contact.t(), map()) ::
  {:ok, PhoenixKitCRM.Schemas.Contact.t()} | {:error, Ecto.Changeset.t()}