Как мы автоматизировали отправку УПД из 1С в СБИС через cron: разбор архитектуры коннектора

Ежедневная ручная выгрузка УПД из 1С и загрузка в СБИС отнимает у бухгалтерии до двух часов в день. Мы собрали коннектор, который делает это по cron без участия человека. В статье — полная архитектура, сложности интеграции и то, что осталось за кадром официальной документации СБИС.

Отправка универсальных передаточных документов (УПД) — одна из самых массовых операций в российском ЭДО. Для компании с сотней контрагентов и несколькими десятками документов в день ручной процесс быстро превращается в узкое горло: бухгалтер отвлекается от аналитики, появляются ошибки при повторном вводе, а задержки с отправкой влияют на сроки оплаты. Автоматизация отправки УПД из 1С в СБИС через cron решает эту проблему на уровне архитектуры, а не инструкций для сотрудников.

Коннектор — это не замена бухгалтеру. Это инфраструктурный слой, который убирает механическую работу и даёт отделу время на то, ради чего он нанят: анализ, контроль и взаимодействие с контрагентами.

Почему стандартные способы интеграции не закрывают задачу

Перед тем как писать свой коннектор, мы изучили три типовых пути: готовый модуль 1С, встроенный обмен СБИС и ручную выгрузку через веб-интерфейс. Ни один из них не подошёл под нашу инфраструктуру без критических компромиссов.

Готовый модуль интеграции 1С и СБИС

Модуль существует, он официальный и поддерживается. Но он работает внутри 1С, использует встроенный язык и требует прямого доступа к базе. В нашей ситуации база 1С уже была нагружена операционной работой, а добавление регламентных заданий по обмену с ЭДО увеличивало риск блокировок при пиковой нагрузке. Кроме того, любое обновление конфигурации 1С потенциально ломало доработки модуля — и мы не хотели завязывать жизненный цикл интеграции на цикл обновлений учётной системы.

Встроенный обмен через API СБИС напрямую из 1С

Теоретически можно написать обработку на встроенном языке 1С, которая будет ходить в API СБИС. Практически — встроенный язык не проектировался для надёжной сетевой работы: нет встроенного retry с экспоненциальным backoff, нет типизированных DTO, нет unit-тестирования. При недоступности API СБИС регламентное задание падает, и диагностировать причину приходится вручную через журнал регистрации.

Ручная работа через личный кабинет

Это то, с чего начинают почти все. Бухгалтер формирует УПД в 1С, сохраняет в файл, заходит в СБИС, загружает, проверяет, подписывает, отправляет. При десяти документах в день это терпимо. При пятидесяти — становится полноценной рутиной, которая отнимает время, концентрацию и повышает риск человеческой ошибки.

Архитектура коннектора: общая схема

Мы выделили интеграцию в отдельный сервис на Symfony. Он живёт рядом с основным приложением, но не внутри 1С. Связь с учётной системой — через OData. Связь с СБИС — через REST API. Управление потоком — через cron и очередь сообщений Symfony Messenger.

Компонент Технология Назначение
Ядро сервиса Symfony 6/7 DI-контейнер, команды, конфигурация
Клиент 1С OData + HTTP-клиент Чтение документов и статусов из 1С
Клиент СБИС REST API + OAuth2 Авторизация, отправка, проверка статуса
Очередь Symfony Messenger + Redis Асинхронная обработка и retry
Хранение PostgreSQL Локальное хранение документов и логов
Планировщик cron + Symfony Console Регулярный запуск синхронизации

Как работает поток данных

Коннектор не пытается заменить 1С или СБИС. Он выполняет роль переводчика и доставщика: забирает готовый документ из учётной системы, преобразует в формат, понятный оператору ЭДО, отправляет и следит за статусом. Вся логика разбита на три консольные команды, которые вызываются по расписанию.

Шаг 1. Синхронизация исходящих УПД

Команда sbis:sync-upd запускается по cron каждые 10 минут. Она обращается к OData-сервису 1С и запрашивает документы, у которых статус «К отправке» и дата больше последней успешной синхронизации. Для каждого найденного документа создаётся DTO, валидируется обязательная структура, и сообщение отправляется в очередь Messenger.

Почему не отправляем сразу? Потому что API СБИС может быть недоступен, а 1С — нет. Разделение чтения и отправки через очередь позволяет 1С не ждать ответа от внешнего сервиса и не блокировать своё соединение.

Шаг 2. Отправка в СБИС

Consumer очереди забирает сообщение и вызывает DocumentService::send(). Сервис формирует JSON по спецификации СБИС, авторизуется через OAuth2, выполняет POST-запрос. В ответ получает идентификатор документа в системе СБИС, который сохраняет в локальную таблицу sbis_document. Если запрос падает по таймауту или возвращает 5xx — Symfony Messenger делает retry с экспоненциальной задержкой. После исчерпания попыток сообщение уходит в dead-letter очередь для ручного разбора.

Шаг 3. Проверка статусов

Команда sbis:check-status запускается каждые 15 минут. Она выбирает из локальной таблицы все документы, отправленные за последние 48 часов, и опрашивает СБИС о текущем статусе: доставлено, подписано контрагентом, отклонено, аннулировано. Обновлённые статусы пишутся в базу коннектора и, при необходимости, синхронизируются обратно в 1С через OData PATCH.

Шаг 4. Обработка входящих

Команда sbis:fetch-incoming забирает из СБИС документы, адресованные организации: входящие УПД, акты, счета-фактуры. Они валидируются, сохраняются локально и передаются в 1С для проведения. Это закрывает полный цикл двустороннего обмена, а не только исходящий трафик.

Что скрывается за абстракциями: нюансы реализации

Архитектура на бумаге выглядит линейно. На практике каждый слой добавил неочевидных сложностей, которые не описаны в документации СБИС и не решаются одним HTTP-запросом.

OAuth2: токен живёт дольше, чем написано

Документация СБИС указывает время жизни токена. На деле токен иногда остаётся валидным дольше, а иногда сбрасывается раньше при параллельных запросах из разных сервисов. Мы реализовали клиент с ленивым обновлением: перед каждым запросом проверяем TTL, при необходимости делаем refresh, а при 401 на лету повторяем запрос с новым токеном без падения основной операции.

Формат УПД: структура и валидация

СБИС принимает УПД в JSON, но структура отличается от того, что 1С называет «УПД». Например, поля контрагента, суммы НДС, номера ГТД и маркировки имеют разную вложенность. Мы ввели промежуточный DTO UpdDto, который описывает документ в терминах бизнеса, а не в терминах конкретного API. Маппер преобразует DTO в JSON СБИС, и если завтра формат изменится — правки идут только в маппер, а не в бизнес-логику.

Retry: не всё, что 5xx, стоит повторять

Симфонийский retry из коробки работает по HTTP-статусу. Но СБИС может вернуть 200 с ошибкой внутри тела, или 400 при невалидном ИНН контрагента, который повторным запросом не исправится. Мы добавили кастомный SbisApiException, который анализирует тело ответа и помечает ошибку как retriable или fatal. Fatal-ошибки сразу уходят в dead-letter, не тратя попытки retry.

Идемпотентность: как не отправить один УПД дважды

Если cron запустится во время работы предыдущего процесса, или consumer упадёт после отправки, но до подтверждения — документ может уйти в СБИС повторно. Мы используем локальную таблицу с уникальным индексом по связке «номер документа 1С + дата + контрагент». Перед отправкой коннектор проверяет, не существует ли уже такой записи со статусом «отправлено». Это не гарантия на 100% — гарантия даёт только идемпотентный API, которого у СБИС нет — но сводит риск дублирования к минимуму.

Как выглядит cron-расписание

Мы разнесли команды по разным интервалам, чтобы не создавать пиковых нагрузок и не перегружать API СБИС:

Каждые 10 минут

sbis:sync-upd — поиск новых документов к отправке в 1С.

Каждые 15 минут

sbis:check-status — обновление статусов ранее отправленных документов.

Каждые 30 минут

sbis:fetch-incoming — забор входящих документов из СБИС.

Раз в сутки (ночью)

Ротация логов и очистка старых записей в dead-letter очереди.

Интервалы подбирались эмпирически: при 50–100 документах в день 10 минут — баланс между оперативностью и нагрузкой. При росте объёма можно уменьшить интервал или добавить параллельных consumer-процессов — Symfony Messenger масштабируется горизонтально без изменения кода.

Мониторинг: как понять, что что-то пошло не так

Автоматизация хороша, пока работает. Когда ломается — важно узнать об этом до звонка от бухгалтера или контрагента. Мы настроили три уровня наблюдаемости.

Логи и трассировка

Каждый запрос к СБИС логируется: URL, статус, время выполнения, correlation ID. При ошибке в логе остаётся полное тело ответа — это сокращает время отладки с часов до минут. Логи пишутся в stdout и забираются стеком ELK.

Метрики очереди

Количество сообщений в очереди, количество failed, время обработки — всё это экспортируется в Prometheus. Алерт срабатывает, если failed-сообщений больше пяти за час: это сигнал, что проблема массовая, а не единичная.

Проверка «здоровья»

Отдельная команда sbis:health-check проверяет доступность API СБИС, валидность токена, связь с 1С и размер очереди. Она вызывается из системы мониторинга каждую минуту. Если хотя бы один чек не проходит — алерт уходит в Telegram-канал DevOps.

Результаты внедрения: что изменилось через три месяца

Коннектор работает в продакшене с начала года. За это время мы накопили метрики, которые позволяют говорить о результатах не в терминах «стало удобнее», а в цифрах.

Показатель До внедрения После внедрения
Время на отправку УПД (в день) 1,5–2 часа бухгалтера 0 (полностью автоматически)
Задержка между созданием и отправкой От 2 часов до 1 дня До 10 минут
Ошибки при ручном вводе (дубли, неверный контрагент) 3–5 в неделю 0
Время реакции на сбой API СБИС От 4 часов (по жалобе) До 5 минут (по алерту)
Нагрузка на сервер 1С от обмена Заметная (регламентные задания) Минимальная (OData-запросы)

Кому подойдёт такая архитектура

Коннектор не является универсальным решением для любой компании. Он окупается при определённых условиях, и важно не строить оверинжиниринг ради технологий.

✅ От 30 документов в день

Меньше — ручная работа дешевле, чем поддержка отдельного сервиса. Больше — экономия времени перекрывает затраты на инфраструктуру.

✅ Есть 1С с включённым OData

Если OData недоступен — придётся добавлять дополнительный слой интеграции, что усложняет архитектуру.

✅ В штате или на аутсорсе есть PHP/Symfony-разработчик

Коннектор — это не no-code решение. Для доработки, отладки и обновлений нужен разработчик соответствующего стека.

✅ Важна скорость доставки документов

Если задержка в полдня критична для цепочки поставок или оплаты — автоматизация через cron закрывает этот риск.

Итог

Автоматизация отправки УПД из 1С в СБИС через cron — это не просто замена ручных действий скриптом. Это смена архитектурного подхода: интеграция выносится из учётной системы в отдельный сервис, получает очередь сообщений, retry, мониторинг и масштабируемость.

Такой коннектор не решает все проблемы ЭДО, но решает ключевую: он убирает рутину, снижает количество ошибок и даёт бизнесу предсказуемость в том, когда документ окажется у контрагента. Если ваш поток документов уже перерос ручное управление — это сигнал, что пора инвестировать в инфраструктуру, а не в инструкции.

Исходный код коннектора, на котором основана статья, доступен в репозитории sbis-1c-connector — он содержит реализацию клиента, DTO, сервисов и команд, описанных выше.