Клиенты, контакты и связи
Клиентский контур хранит компании, контактных лиц, реквизиты и связи между сущностями. В проектах компания отвечает за юридическую сторону клиента, контакт — за конкретного человека для коммуникации, а связи позволяют показать весь контекст вокруг клиента без ручного обхода разных модулей.
Основные сущности
Company
Компания — контрагент или клиент. В ней хранятся название, юридическое имя, ИНН/КПП, контакты общего назначения, ответственный менеджер и флаг enforce_time_for_done_tasks.
Компания может быть:
- клиентом проекта через
projects.company_id; - владельцем реквизитов через
requisites.owner_type = company; - связана с одним или несколькими контактами через
company_contacts; - узлом навигационного хаба
/api/v1/entities/company/{id}/related.
В списках компаний поле manager_id используется как владелец клиентского контура. Не-глобальный пользователь видит компании, назначенные на него и на его рекурсивных подчиненных из оргструктуры; админ, пользователи с system.global_read/system.global_admin и роли с записью справочника контрагентов «все» по матрице (финансы, юристы, руководители дивизионов и менеджеры — контрагентов ведут менеджеры) видят полный список. Свой клиентский контур остаётся у ролей с чтением «Р» (технический директор).
Contact
Контакт — конкретное контактное лицо. Контакт может иметь прямое поле company_id для базовой принадлежности и M:M-связи через company_contacts, если один человек работает с несколькими компаниями или у компании несколько ролей контактов.
Контакт может быть:
- контактным лицом проекта через
projects.contact_id; - владельцем реквизитов через
requisites.owner_type = contact; - связан с несколькими компаниями через
company_contacts; - узлом навигационного хаба
/api/v1/entities/contact/{id}/related.
В списках контактов тот же scope применяется через связанную компанию: контакт виден, если его company_id или M:M-связь company_contacts указывает на компанию из доступного набора менеджеров.
Правка контакта (PUT /api/v1/contacts/:id): необязательное поле, переданное пустой строкой (почта, телефон, должность, заметки), очищается; не переданное — не меняется. Из имени и фамилии одно остаётся обязательным. Почта у контакта необязательна; указанная — один адрес без имени и не занятый другим контактом. Отключение контакта (is_active: true → false) подчиняется тому же правилу, что DELETE: контакт активного проекта не отключить (409 contacts_contact_is_linked_to_active_projects).
CompanyContact
company_contacts — управляемая связь компании и контакта. Она хранит роль контакта в компании (role) и признак основного контакта (is_primary).
Правило: у одной компании может быть только один основной контакт. При создании или обновлении связи с is_primary = true остальные связи этой компании становятся неосновными.
Requisite
Реквизиты принадлежат компании или контакту через пару owner_type / owner_id. Основные реквизиты помечаются is_primary; endpoint set-primary переводит выбранный набор в основной для владельца.
Создание или правка набора с is_primary = true тоже снимает признак с прежнего основного набора владельца в той же транзакции (один основной набор — уникальный индекс idx_requisites_primary). PUT /requisites/:id и PUT /settings/my-company/requisites/:id перезаписывают набор целиком: отсутствующее поле, null или пустая строка очищают колонку (NULL), поэтому клиент, правящий часть полей (карточка компании), отправляет остальные поля набора обратно.
Банковский счёт компании: у архивной компании новый счёт не заводится (409 companybankaccounts_company_archived); счёт, указанный на вкладке «Счета и кассы» (payment_accounts.company_bank_account_id), не удаляется (409 companybankaccounts_bank_account_in_use).
EntityLink
entity_links хранит произвольные связи между сущностями: например проект-документ, компания-файл, контакт-договор. Для компаний, контактов, проектов и реквизитов навигационный хаб также собирает структурные связи из FK и join-таблиц.
Рабочий флоу
- Менеджер создает компанию:
POST /api/v1/companies. - Менеджер создает контакт:
POST /api/v1/contacts. - Если контакт должен быть явно связан с компанией, создается M:M-связь:
POST /api/v1/companies/{id}/contacts. - Для компании или контакта добавляются реквизиты:
POST /api/v1/requisites. - При создании проекта выбираются
company_idи, если известен,contact_id. - Для карточки компании или контакта UI запрашивает
/api/v1/entities/{type}/{id}/relatedи показывает связанные контакты, компании, проекты и произвольные связи. - Реквизиты карточки загружаются отдельно через
GET /api/v1/requisites?owner_type=...&owner_id=....
API-контур
| Сценарий | Endpoint |
|---|---|
| Список компаний | GET /api/v1/companies |
| Создать компанию | POST /api/v1/companies |
| Получить компанию | GET /api/v1/companies/{id} |
| Обновить компанию | PUT /api/v1/companies/{id} |
| Удалить компанию | DELETE /api/v1/companies/{id} |
| Список контактов | GET /api/v1/contacts |
| Создать контакт | POST /api/v1/contacts |
| Получить контакт | GET /api/v1/contacts/{id} |
| Обновить контакт | PUT /api/v1/contacts/{id} |
| Удалить контакт | DELETE /api/v1/contacts/{id} |
| Контакты компании | GET /api/v1/companies/{id}/contacts |
| Привязать контакт | POST /api/v1/companies/{id}/contacts |
| Обновить роль/основной контакт | PATCH /api/v1/companies/{id}/contacts/{contact_id} |
| Отвязать контакт | DELETE /api/v1/companies/{id}/contacts/{contact_id} |
| Компании контакта | GET /api/v1/contacts/{id}/companies |
| Реквизиты владельца | GET /api/v1/requisites?owner_type=company&owner_id=1 |
| Создать реквизиты | POST /api/v1/requisites |
| Получить реквизиты | GET /api/v1/requisites/{id} |
| Обновить реквизиты | PUT /api/v1/requisites/{id} |
| Удалить реквизиты | DELETE /api/v1/requisites/{id} |
| Сделать реквизиты основными | PATCH /api/v1/requisites/{id}/set-primary |
| Создать произвольную связь | POST /api/v1/entity-links |
| Удалить произвольную связь | DELETE /api/v1/entity-links/{id} |
| Связанные сущности | GET /api/v1/entities/{type}/{id}/related |
Точные параметры, схемы JSON и коды ответов поддерживаются в Scalar API Reference.
Навигационный хаб
GET /api/v1/entities/{type}/{id}/related возвращает единый список связанных объектов. Это основной endpoint для UI-карточек, где нужно показать "с чем связан объект".
Поддерживаемые type:
| type | Структурные связи |
|---|---|
company | контакты из company_contacts, проекты из projects.company_id |
contact | компании из company_contacts, проекты из projects.contact_id |
project | компания из projects.company_id, контакт из projects.contact_id |
requisite | владелец: компания или контакт |
Ответ содержит type, id, title, relation_type и, для произвольных связей, link_id. Благодаря title UI может отрисовать список связей без дополнительных запросов за названием каждой сущности.