61 lines
4.3 KiB
Markdown
61 lines
4.3 KiB
Markdown
# ADR-006: изменение сделки с проверкой актуального состояния
|
||
|
||
**Статус:** Принято
|
||
**Дата:** 2026-07-23
|
||
|
||
## Контекст
|
||
|
||
Состояние сделки может измениться в Битрикс24 после формирования карточки в
|
||
Telegram, но до нажатия callback-кнопки. Используемый метод `crm.deal.update` не
|
||
предоставляет условный `UPDATE`, поэтому перед изменением требуется проверить,
|
||
что показанное пользователю состояние остаётся актуальным.
|
||
|
||
## Решение
|
||
|
||
### Назначение ответственного и изменение стадии
|
||
|
||
Callback-кнопка назначения содержит не только id сделки, но и `ASSIGNED_BY_ID`,
|
||
который был показан пользователю:
|
||
`deal:assign:<deal_id>:<expected_responsible_id>`. Перед изменением сервис
|
||
повторно загружает сделку и сравнивает фактического ответственного с ожидаемым.
|
||
Если карточка устарела, REST-обновление не выполняется.
|
||
|
||
Внутри одного процесса операции по паре `(member_id, deal_id)` последовательно
|
||
выполняются под `asyncio.Lock`. После `crm.deal.update` сервис повторно читает
|
||
сделку и убеждается, что ответственным стал `bitrix_user_id` привязанного
|
||
пользователя.
|
||
|
||

|
||
|
||
*Рисунок ADR-006/1. Диаграмма последовательности взятия сделки в работу*
|
||
|
||
При обновлении одновременно передаются `ASSIGNED_BY_ID` связанного пользователя,
|
||
рабочая `STAGE_ID` и параметр `REGISTER_HISTORY_EVENT=Y`. После REST-запроса
|
||
выполняется контрольное чтение сделки.
|
||
|
||
### Планирование звонка и просмотр истории
|
||
|
||
Действие «Позвонить позже» создаёт в Битрикс24 дело типа `todo` с крайним сроком
|
||
через один час. Владельцем является сделка (`ownerTypeId=2`), а ответственным —
|
||
связанный пользователь Битрикс24. Массив `pingOffsets=[0]` включает напоминание
|
||
в момент наступления срока.
|
||
|
||
История загружается методом `crm.stagehistory.list` с фильтром `OWNER_ID` и
|
||
сортировкой по убыванию идентификатора. В интерфейс выводятся первые пять
|
||
событий. Для каждого события идентификатор стадии преобразуется в название с
|
||
учётом `CATEGORY_ID`, после чего `DealFormatter` формирует защищённый HTML-текст
|
||
и клавиатуру возврата к карточке или списку.
|
||
|
||

|
||
|
||
*Рисунок ADR-006/2. Диаграмма последовательности планирования звонка и просмотра
|
||
истории*
|
||
|
||
## Последствия
|
||
|
||
Локальный `asyncio.Lock` защищает только один процесс `bot`. При горизонтальном
|
||
масштабировании на несколько контейнеров потребуется распределённая блокировка
|
||
либо серверная условная операция. Повторная проверка REST-результата сохраняет
|
||
защиту от внешних изменений, но не делает два удалённых вызова одной транзакцией
|
||
Битрикс24.
|