Пример условный. В компании-дистрибьюторе руководитель согласовал заявку на закупку, и система автоматизации отправляет её в ERP как заказ поставщику. ERP заказ сохраняет, но соединение обрывается раньше, чем ответ доходит до интеграции. В журнале процесса появляется «тайм-аут».
Простых реакций две, и обе плохие. Можно нажать «повторить» и получить второй заказ у того же поставщика. Можно пометить задачу как сбой, и тогда сотрудник заведёт заказ вручную, то есть опять второй.
Закупка сорвалась? Не обязательно. Передача завершена? Пока не проверено. Тайм-аут говорит лишь о том, что ответ не пришёл.
Сам механизм дубля при повторной отправке мы коротко описывали в статье о заявках в CRM. Здесь разберём, как устроить передачу так, чтобы неизвестный результат не превращался ни в дубль, ни в потерянный заказ.
Тайм-аут означает «неизвестно»
Для случая, когда ответа нет, у операции передачи должно быть отдельное состояние: не «успех» и не «ошибка», а «результат неизвестен». Пока операция в нём находится, создавать новый заказ нельзя, а считать закупку сорванной рано.
Важно договориться и о том, что считается завершением. «Готово» в этой операции означает: заказ в ERP создан, и его номер получен. Это не значит, что поставщик принял заказ, товар приехал или счёт оплачен. У этих шагов свои подтверждения.

Что нужно операции ещё до отправки
Постоянный идентификатор операции
Идентификатор присваивается один раз, в момент согласования заявки, и не меняется при повторах. Если интеграция на каждой попытке генерирует новый номер, для ERP каждая попытка выглядит новым заказом. Новый идентификатор нужен только действительно новому заказу.
Согласованная версия данных
Вместе с идентификатором сохраните ту версию заказа, которую согласовали: поставщика, позиции, количества, цены. Если заявку изменили после согласования, это уже другая версия, и вопрос об отправке решается заново. Повтор должен передавать ровно то, что было согласовано.
Защита от повторов на стороне ERP
Если API вашей ERP поддерживает ключ идемпотентности, передавайте в нём идентификатор операции и соблюдайте правила этой системы. У таких ключей бывает ограниченный срок хранения, а повтор с тем же ключом, но с другими данными может быть отклонён.
Если такой поддержки нет, записывайте идентификатор операции в сам заказ, например в поле внешнего номера документа, если оно есть. Тогда по нему можно будет искать. Учтите, что поле с названием «ключ идемпотентности» само по себе ничего не гарантирует. Значение имеет то, что система реально делает при повторе, и это нужно проверить на тестовом стенде, а не только прочитать в документации.
Transactional outbox: намерение отправить не должно потеряться
Вторая ловушка находится внутри вашей собственной системы. Процесс выполняет два действия: отмечает у себя «заказ отправлен» и действительно отправляет запрос в ERP. Если сбой случится между ними, вы получите одно из двух: отметка есть, а отправки не было, или отправка была, а отметки нет.
Шаблон transactional outbox решает это так. В одной транзакции локальной базы сохраняются и новое состояние заявки, и запись «нужно отправить заказ» в отдельную таблицу, которую и называют outbox. Отправкой занимается отдельный процесс: он берёт записи из outbox, передаёт их в ERP и фиксирует результат. Если он упадёт на середине, запись никуда не денется и будет обработана снова.
У этого есть обратная сторона: одна и та же запись может уйти больше одного раза. Поэтому outbox работает только вместе с защитой от повторов на стороне получателя или со сверкой, о которой речь ниже.
Шаблон подходит, когда у процесса есть собственная база данных, которой вы управляете. Если автоматизация собрана в облачном конструкторе без такой базы, нужен аналог: надёжное место, где намерение отправить фиксируется до отправки и не пропадает при сбое.
«Не найдено» ещё не значит «не создано»
После тайм-аута естественно заглянуть в ERP и поискать заказ с вашим идентификатором. Но пустой результат поиска не доказывает, что заказа нет:
- поиск может отставать от записи: документ уже сохранён, но ещё не виден в списке или отчёте;
- поиск может быть неполным: искали не по тому полю или под учётной записью, которой не видны документы другого склада или юрлица;
- первый запрос может ещё выполняться: проверка обгоняет операцию, которая пока не завершилась.
Поэтому проверка после тайм-аута ищет по идентификатору операции, под учётной записью с доступом ко всем нужным документам, и повторяется через согласованный интервал. Если и после этого результат не удаётся подтвердить ни в одну сторону, операция остаётся в состоянии «неизвестно». Тихий повтор с новым идентификатором в такой ситуации — самый короткий путь к дублю.
Три исхода, которые не пересекаются
Каждая операция передачи должна закончиться ровно одним из трёх исходов.
- Подтверждение получено. ERP вернула номер заказа, интеграция проверила, что заказ существует и находится в ожидаемом состоянии. Номер сохранён рядом с идентификатором операции.
- Ответ потерян, заказ найден. Сверка нашла заказ по идентификатору операции. Операция связывается с этим заказом и закрывается, новый заказ не создаётся.
- Результат подтвердить не удалось. Операция переходит к ответственному за восстановление вместе с доказательствами: что отправлено, когда, с каким идентификатором и что показали проверки. Ответственный решает, что делать дальше, и записывает своё решение.
Отчёт о передачах лучше строить по этим трём исходам, а не по схеме «успех или ошибка». Тогда третий исход виден как отдельная работа и не теряется среди прочих сбоев.
Ответственный и регулярная сверка
У третьего исхода есть имя
Ответственный за восстановление — конкретный человек или роль, например руководитель закупок или администратор учётной системы. Ему нужна очередь, где видны идентификатор операции, согласованная версия, попытки, результаты проверок и следующее действие. Полный доступ ко всем данным ERP для этого не нужен. Договоритесь и о том, как быстро он разбирает очередь. Это зависит от того, насколько срочно поставщик должен получить заказ.
Сверка по расписанию
Кроме разбора отдельных случаев нужна регулярная сверка, то есть задача, которая сравнивает операции на вашей стороне с документами в ERP и ищет:
- операции, которые слишком долго остаются в состоянии «отправлено» или «неизвестно»;
- заказы в ERP с вашим идентификатором, которые не привязаны ни к одной операции;
- два заказа с одним идентификатором: признак того, что защита от повторов не сработала.
Однозначные случаи, как во втором исходе, сверка может закрыть сама. Всё остальное попадает в очередь ответственного.
Счета поставщиков: тот же случай
Та же логика нужна при проведении входящих счетов. Если связь оборвалась после того, как ERP уже сохранила счёт, повторное создание без проверки даст дубль документа. Постоянный идентификатор, поиск по нему и три исхода работают здесь так же, как с заказами.
Чек-лист перед запуском
- У операции есть идентификатор, который присваивается при согласовании и не меняется при повторах.
- Согласованная версия данных сохраняется, и повтор отправляет именно её.
- Известно, как ERP обрабатывает повтор: ключ идемпотентности и срок его хранения или поиск по внешнему номеру.
- Намерение отправить фиксируется вместе с изменением состояния: через outbox или его аналог.
- В процессе есть состояние «результат неизвестен», отдельное от успеха и ошибки.
- Проверка после тайм-аута ищет по идентификатору операции, с достаточными правами и с повтором через интервал.
- Каждая операция заканчивается одним из трёх исходов, и отчёт строится по ним.
- Назначен ответственный за восстановление, и у него есть очередь с доказательствами.
- Регулярная сверка находит зависшие операции, непривязанные заказы и дубли.
- В тестовой среде подтверждение намеренно потеряно, и восстановление нашло существующий заказ, а не создало новый.
С чего начать
Возьмите одну передачу, например «согласованная заявка становится заказом поставщику», и ответьте на два вопроса: что доказывает, что передача прошла, и кто отвечает за результат, когда доказательства нет. Затем добавьте состояние «неизвестно», назначьте ответственного и проведите на тестовом стенде один тест с потерянным подтверждением. Остальные стрелки на схеме процесса проверяются так же.
Если хотите так проверить одну из своих передач в ERP, опишите её нам, и мы вместе разберём, что сейчас происходит у вас при тайм-ауте.
