# ADR-006: изменение сделки с проверкой актуального состояния **Статус:** Принято **Дата:** 2026-07-23 ## Контекст Состояние сделки может измениться в Битрикс24 после формирования карточки в Telegram, но до нажатия callback-кнопки. Используемый метод `crm.deal.update` не предоставляет условный `UPDATE`, поэтому перед изменением требуется проверить, что показанное пользователю состояние остаётся актуальным. ## Решение ### Назначение ответственного и изменение стадии Callback-кнопка назначения содержит не только id сделки, но и `ASSIGNED_BY_ID`, который был показан пользователю: `deal:assign::`. Перед изменением сервис повторно загружает сделку и сравнивает фактического ответственного с ожидаемым. Если карточка устарела, REST-обновление не выполняется. Внутри одного процесса операции по паре `(member_id, deal_id)` последовательно выполняются под `asyncio.Lock`. После `crm.deal.update` сервис повторно читает сделку и убеждается, что ответственным стал `bitrix_user_id` привязанного пользователя. ![Диаграмма последовательности взятия сделки в работу](assets/report/deal-assignment-sequence.png) *Рисунок 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-текст и клавиатуру возврата к карточке или списку. ![Диаграмма последовательности планирования звонка и просмотра истории](assets/report/reminder-history-sequence.png) *Рисунок ADR-006/2. Диаграмма последовательности планирования звонка и просмотра истории* ## Последствия Локальный `asyncio.Lock` защищает только один процесс `bot`. При горизонтальном масштабировании на несколько контейнеров потребуется распределённая блокировка либо серверная условная операция. Повторная проверка REST-результата сохраняет защиту от внешних изменений, но не делает два удалённых вызова одной транзакцией Битрикс24.