Интеграция интернет-магазина с 1С: REST API, вебхуки, синхронизация
Введение: Почему ручной ввод данных убивает рентабельность интернет-магазина
Типичная картина в малом и среднем интернет-магазине: менеджер получает заказ на сайте, заходит в 1С, вручную создаёт документ, распечатывает накладную, относит на склад. Кладовщик собирает заказ, возвращает менеджеру, тот отмечает в 1С как отгруженный, затем заходит на сайт и меняет статус заказа. На каждый заказ уходит 10-15 минут рабочего времени, плюс риск ошибок: перепутал артикул, не туда отправил, забыл обновить остатки.
При 50 заказах в день это 8-12 часов ручной работы. Месяц — 160-240 часов. Год — 2000-3000 часов. Это полная ставка сотрудника, который только и делает, что перекладывает данные из одной системы в другую. И это без учёта ошибок, которые приводят к отправке не того товара, двойной продаже одного и того же, рассинхронизации остатков.
Автоматическая интеграция сайта с 1С решает все эти проблемы. Но не ту интеграцию, которая предлагают большинство разработчиков («раз в час выгружаем CSV-файл»), а реальную синхронизацию в реальном времени через API.
В этой статье мы разберём, как построить такую интеграцию: от архитектуры обмена данными до обработки исключительных ситуаций. Мы будем использовать современные подходы: REST API, очереди сообщений, idempotency keys, компенсирующие транзакции.
Часть 1: Архитектура интеграции: от файлового обмена к real-time API
Эволюция интеграционных подходов
Уровень 0: Ручной ввод — всё делается руками. Время на заказ: 15 минут. Ошибки: 5-10%.
Уровень 1: Файловый обмен (CSV/XML) — раз в час/день сайт выгружает файл с заказами, 1С забирает, обрабатывает. Время: 5-10 минут с задержкой. Ошибки: 2-5%.
Уровень 2: Веб-сервисы (SOAP) — 1С предоставляет SOAP-сервис, сайт вызывает методы. Время: 1-2 минуты. Ошибки: 1-2%.
Уровень 3: REST API — современный подход, двусторонняя связь в реальном времени. Время: секунды. Ошибки: 0.1-0.5%.
Почему REST API, а не SOAP или файлы
1. Real-time синхронизация: Заказ сразу попадает в 1С, сразу резервируется товар.
2. Двусторонняя связь: Не только сайт → 1С, но и 1С → сайт (остатки, цены, статусы).
3. Надёжность: Подтверждение получения, повторные попытки при ошибках.
4. Масштабируемость: Легко добавить новые типы данных (заказы, клиенты, товары).
5. Стандартизация: REST — современный стандарт, с которым работают все разработчики.
Архитектурная схема
┌─────────────────┐ REST API ┌─────────────────┐
│ │ ┌───────────────► │ │
│ Интернет- │ │ Заказы, │ 1С │
│ магазин │ │ Клиенты │ │
│ (Django) │ │ │ (через HTTP- │
│ │ │ Остатки, │ сервисы или │
│ │ │ Цены, │ внешние │
│ │ │ Статусы ◄─────┘ обработки) │
└─────────────────┘ └─────────────────┘
│ │
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Очередь │ │ База данных │
│ сообщений │ │ 1С │
│ (RabbitMQ/ │ │ │
│ Redis) │ │ │
└─────────────────┘ └─────────────────┘
Часть 2: Настройка 1С для работы с REST API
Создание HTTP-сервиса в 1С
В 1С 8.3.10+ появилась возможность создавать полноценные REST API через механизм HTTP-сервисов. Вот пошаговая инструкция:
Шаг 1: Создаём обработку «Внешний обработчик HTTP-сервисов»
// В конфигураторе 1С создаём новую обработку
// Называем её, например, "ИнтеграцияССайтом"
Перем Лог;
Процедура ПриСозданииНаСервере()
Лог = Новый ЛогФайл;
Лог.Открыть("C:\\Logs\\1C_Integration.log", "UTF-8");
КонецПроцедуры
// Основной метод, который будет вызываться при HTTP-запросе
Функция ОбработатьВызов(Запрос, Ответ, ПараметрыЗапроса) Экспорт
// Логируем запрос
Лог.ЗаписатьСтроку("Получен запрос: " + Запрос.Метод + " " + Запрос.АдресРесурса);
// Определяем метод и путь
Если Запрос.Метод = "POST" И Запрос.АдресРесурса = "/api/orders" Тогда
Возврат ОбработатьСозданиеЗаказа(Запрос, Ответ);
ИначеЕсли Запрос.Метод = "GET" И Запрос.АдресРесурса = "/api/products" Тогда
Возврат ОбработатьПолучениеТоваров(Запрос, Ответ);
ИначеЕсли Запрос.Метод = "PUT" И Запрос.АдресРесурса НачинаетсяС("/api/orders/") Тогда
Возврат ОбработатьОбновлениеЗаказа(Запрос, Ответ);
КонецЕсли;
// Если маршрут не найден
Ответ.УстановитьТелоИзСтроки("{\"error\": \"Not found\"}");
Ответ.КодСостояния = 404;
Возврат Истина;
КонецФункции
Шаг 2: Реализуем метод создания заказа
Функция ОбработатьСозданиеЗаказа(Запрос, Ответ)
Попытка
// Читаем тело запроса (JSON)
ТелоЗапроса = Запрос.ПолучитьТелоКакСтроку("UTF-8");
Лог.ЗаписатьСтроку("Тело запроса: " + ТелоЗапроса);
// Парсим JSON
ПарсерJSON = Новый ПарсерJSON;
ДанныеЗаказа = ПарсерJSON.Прочитать(ТелоЗапроса);
// Проверяем обязательные поля
Если Не ДанныеЗаказа.Свойство("id")
Или Не ДанныеЗаказа.Свойство("items") Тогда
Ответ.УстановитьТелоИзСтроки("{\"error\": \"Missing required fields\"}");
Ответ.КодСостояния = 400;
Возврат Истина;
КонецЕсли;
// Проверяем, не создан ли уже заказ с таким ID (idempotency)
ПоискЗаказа = Документы.ЗаказыПокупателей.НайтиПоНавигационнойСсылке(
ДанныеЗаказа.id
);
Если Не ПоискЗаказа.Пустой() Тогда
// Заказ уже существует, возвращаем его данные
СуществующийЗаказ = ПоискЗаказа.Получить();
ОтветJSON = СформироватьОтветПоЗаказу(СуществующийЗаказ);
Ответ.УстановитьТелоИзСтроки(ОтветJSON);
Ответ.КодСостояния = 200;
Возврат Истина;
КонецЕсли;
// Создаём новый заказ
НовыйЗаказ = Документы.ЗаказыПокупателей.СоздатьДокумент();
НовыйЗаказ.Номер = ДанныеЗаказа.id;
НовыйЗаказ.Дата = ТекущаяДата();
// Заполняем контрагента
Если ДанныеЗаказа.Свойство("customer") Тогда
Контрагент = НайтиИлиСоздатьКонтрагента(ДанныеЗаказа.customer);
НовыйЗаказ.Контрагент = Контрагент;
КонецЕсли;
// Добавляем товары
Для Каждого ЭлементЗаказа Из ДанныеЗаказа.items Цикл
СтрокаЗаказа = НовыйЗаказ.Товары.Добавить();
Номенклатура = Справочники.Номенклатура.НайтиПоКоду(
ЭлементЗаказа.sku
);
Если Не Номенклатура.Пустой() Тогда
СтрокаЗаказа.Номенклатура = Номенклатура.Получить();
СтрокаЗаказа.Количество = ЭлементЗаказа.quantity;
СтрокаЗаказа.Цена = ЭлементЗаказа.price;
КонецЕсли;
КонецЦикла;
// Проверяем наличие товаров
Если Не ПроверитьНаличиеТоваров(НовыйЗаказ) Тогда
Ответ.УстановитьТелоИзСтроки("{\"error\": \"Insufficient stock\"}");
Ответ.КодСостояния = 409; // Conflict
Возврат Истина;
КонецЕсли;
// Проводим заказ
НовыйЗаказ.Записать();
НовыйЗаказ.Провести();
// Формируем ответ
ОтветJSON = СформироватьОтветПоЗаказу(НовыйЗаказ);
Ответ.УстановитьТелоИзСтроки(ОтветJSON);
Ответ.КодСостояния = 201; // Created
Лог.ЗаписатьСтроку("Заказ успешно создан: " + НовыйЗаказ.Номер);
Исключение
// Обработка ошибок
Лог.ЗаписатьСтроку("Ошибка при создании заказа: " + ОписаниеОшибки());
Ответ.УстановитьТелоИзСтроки("{\"error\": \"Internal server error\"}");
Ответ.КодСостояния = 500;
КонецПопытки;
Возврат Истина;
КонецФункции
Настройка публикации HTTP-сервиса
- В конфигураторе: Сервис → Публикация веб-сервисов на веб-сервере
- Добавляем новую публикацию
- Указываем путь, например:
/api/integration - Выбираем созданную обработку
- Настраиваем аутентификацию (Basic Auth или токены)
- Публикуем
Теперь 1С доступна по адресу: http://1c-server.example.com/api/integration
Часть 3: Реализация клиента на стороне Django
Модель для синхронизации
class OneCSyncStatus(models.TextChoices):
PENDING = 'pending', 'Ожидает синхронизации'
SYNCING = 'syncing', 'Синхронизируется'
SUCCESS = 'success', 'Успешно синхронизировано'
ERROR = 'error', 'Ошибка синхронизации'
RETRY = 'retry', 'Повторная попытка'
class OneCSyncLog(models.Model):