Skip to main content
Version: v1.0.0(int)

Contacts

Shared contacts pool — people linked polymorphically to customers, vendors, locations, etc. Each contact can carry multiple methods (email / phone) and can be linked to multiple entities.

Tables owned​

TablePurpose
org.contactsContact records (first/last name, title, notes).
org.contact_methodsEmail / phone methods attached to a contact, with label.
org.entity_contactsPolymorphic linker. (entity_type, entity_id, contact_id) with role + notes. entity_id is application-enforced (no DB FK).

contact_methods is owned here too — it's a subordinate of contacts with no independent consumers. Implicit in the table list above.

Operations​

Writes — Contacts​

  • createContact(conn, input) → Contact. input: { firstName, lastName?, title?, notes? }.
  • updateContact(conn, contactId, patch) → Contact. Partial update.
  • deleteContact(conn, contactId) → void. Cascades to methods and entity_contact links.

Writes — Contact methods​

  • addMethod(conn, contactId, { type, value, label? }) → ContactMethod. type: 'email' | 'phone'.
  • updateMethod(conn, methodId, patch) → ContactMethod.
  • deleteMethod(conn, methodId) → void.
  • linkToEntity(conn, { contactId, entityType, entityId, role?, notes? }) → EntityContact. entityType is the entity_type enum.
  • updateLink(conn, linkId, patch) → EntityContact. Edits role / notes.
  • unlink(conn, linkId) → void. Removes the association without deleting the contact.

Reads — Contacts​

  • listContacts(conn, filters?) → Contact[]. Filters: { q? } (name/email search).
  • getContactById(conn, contactId) → Contact | null.
  • getContactsByIds(conn, contactIds) → Contact[]. Batch hydration.

Reads — Methods​

  • listMethodsForContact(conn, contactId) → ContactMethod[].
  • listContactsByEntity(conn, entityType, entityId) → Array<{ link: EntityContact, contact: Contact, methods: ContactMethod[] }>. The hydrated view used on Customer/Vendor detail pages.
  • listEntityContactsByEntity(conn, entityType, entityId) → EntityContact[]. Link rows only (for L3 to compose its own hydration).
  • getLinkById(conn, linkId) → EntityContact | null.

Notes​

  • The component never resolves entity_id back to its source table. That's L3's job (it knows which component owns the entity).
  • entity_type values are enum-controlled. Adding a new entity type requires a migration on the enum, not a code change here.