Разберем детально процесс настройки обмена данными между учетной системой на базе 1С и государственной информационной системой AITS (Animal Identification Traceability System) Республики Беларусь. Эта система обязательна к использованию для предприятий агропромышленного комплекса и мясокомбинатов. Рассмотрим особенности архитектуры, методы авторизации, способы отправки XML-файлов и типовые проблемы, с которыми сталкиваются разработчики при внедрении.
Система AITS состоит из нескольких подсистем, среди которых нас в первую очередь интересует «AITS-Прослеживаемость». Она отвечает за учет движения сырья, производство готовой продукции и отгрузки. Важно понимать, что жесткие требования к заполнению данных продиктованы необходимостью бесшовной интеграции с российской системой «Меркурий» через шлюз обмена данными.
Проанализируем основные идентификаторы, которые нам потребуются для корректного формирования запросов:
Если цепочка «сырье — готовая продукция — отгрузка» не будет строго связана в AITS, система не сможет автоматически сгенерировать электронный ветеринарный сертификат для экспорта.
Посмотрим на пример реализации авторизации на платформе 1С:Предприятие 8. Для получения токена (AuthToken) нам необходимо выполнить GET-запрос к сервису авторизации.
Разберем код, который отправляет HTTP-запрос и парсит полученный JSON-ответ средствами платформы:
// Работа с JSON
&НаКлиенте
Функция jsonПрочитатьПлатформой(Значение)
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(Значение);
Результат = Неопределено;
СформироватьДерево(ЧтениеJSON, Результат);
ЧтениеJSON.Закрыть;
Возврат Результат;
КонецФункции
&НаКлиенте
Процедура СформироватьДерево(ЧтениеJSON, Дерево)
ИмяСвойства = Неопределено;
Пока ЧтениеJSON.Прочитать() Цикл
TипJSON = ЧтениеJSON.ТипТекущегоЗначения;
Если TипJSON = ТипЗначенияJSON.НачалоОбъекта ИЛИ TипJSON = ТипЗначенияJSON.НачалоМассива Тогда
НовыйОбъект = ?(TипJSON = ТипЗначенияJSON.НачалоОбъекта, Новый Соответствие, Новый Массив);
Если ТипЗнч(Дерево) = Тип("Массив") Тогда
Дерево.Добавить(НовыйОбъект);
ИначеЕсли ТипЗнч(Дерево) = Тип("Соответствие") И ЗначениеЗаполнено(ИмяСвойства) Тогда
Дерево.Вставить(ИмяСвойства, НовыйОбъект);
КонецЕсли;
СформироватьДерево(ЧтениеJSON, НовыйОбъект);
Если Дерево = Неопределено Тогда
Дерево = НовыйОбъект;
КонецЕсли;
ИначеЕсли TипJSON = ТипЗначенияJSON.ИмяСвойства Тогда
ИмяСвойства = ЧтениеJSON.ТекущееЗначение;
ИначеЕсли TипJSON = ТипЗначенияJSON.Число ИЛИ TипJSON = ТипЗначенияJSON.Строка ИЛИ TипJSON = ТипЗначенияJSON.Булево ИЛИ TипJSON = ТипЗначенияJSON.Null Тогда
Если ТипЗнч(Дерево) = Тип("Массив") Тогда
Дерево.Добавить(ЧтениеJSON.ТекущееЗначение);
ИначеЕсли ТипЗнч(Дерево) = Тип("Соответствие") Тогда
Дерево.Вставить(ИмяСвойства, ЧтениеJSON.ТекущееЗначение);
КонецЕсли;
Иначе
Возврат;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
// Функция отправки HTTP-запроса
Функция ОтправитьHTTPЗапрос(Хост, Ресурс) Экспорт
Соединение = Новый HTTPСоединение(Хост);
Заголовки = Новый Соответствие;
Заголовки.Вставить("host", Хост);
Запрос = Новый HTTPЗапрос(Ресурс, Заголовки);
Ответ = Неопределено;
Попытка
Ответ = Соединение.Получить(Запрос);
Исключение
Сообщить(ОписаниеОшибки());
КонецПопытки;
Возврат Ответ;
КонецФункции
// Процедура авторизации
Процедура ВыполнитьАвторизацию(СтруктураНастроек, Токен) Экспорт
Порт = СтруктураНастроек.ПортСервера;
Хост = СтруктураНастроек.АдресСервера + ":" + Порт;
Логин = СтруктураНастроек.Логин;
Пароль = СтруктураНастроек.Пароль;
Ресурс = "/trace/services/Auth.svc/auth?login=" + Логин + "&pwd=" + Пароль;
Ответ = ОтправитьHTTPЗапрос(Хост, Ресурс);
Если Ответ <> Неопределено Тогда
СтруктурированныйОтвет = jsonПрочитатьПлатформой(Ответ.ПолучитьТелоКакСтроку());
ПредставлениеОтвета = СтруктурированныйОтвет.Получить("Status");
КодОтвета = СтруктурированныйОтвет.Получить("Code");
ТокенДанные = СтруктурированныйОтвет.Получить("Data");
Если ПредставлениеОтвета = "OK" Тогда
// ВАЖНО: Обработка бага API со слешами в токене
Токен = СтрЗаменить(ТокенДанные, "\/", "/");
Сообщить("Авторизация пройдена успешно");
Иначе
Сообщить ("Авторизация не пройдена, Код ошибки: " + КодОтвета);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Важное замечание: API системы AITS иногда возвращает токен, в котором экранированы символы косой черты (\/). Если отправить такой токен обратно, сервер вернет ошибку «Некорректный AuthToken». Именно поэтому перед использованием токена мы применяем функцию СтрЗаменить(ТокенДанные, "\/", "/"). Также стоит учитывать, что сервис может работать нестабильно, поэтому иногда авторизацию приходится повторять в цикле (до 3-5 попыток), если сервер отвергает правильный токен.
Рассмотрим метод загрузки XML-файлов с данными о партиях (сырье, продукция, отгрузки) через POST-запрос с использованием WinHttp.WinHttpRequest.5.1 и ADODB.Stream. Данный подход универсален и работает как в 1С 7.7, так и в более новых версиях платформы (через вызов Новый COMОбъект).
Процедура ОтправитьФайлВАИТС(ПутьКФайлу, Токен, АдресСайта)
// Чтение файла в правильной кодировке
oADOStream = Новый COMОбъект("ADODB.Stream"); // Для 7.7: СоздатьОбъект("ADODB.Stream")
oADOStream.Type = 1;
oADOStream.Open();
oADOStream.LoadFromFile(ПутьКФайлу);
// Инициализация HTTP-соединения
Соединение = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
Соединение.Option(2, "utf-8");
Соединение.Open("POST", АдресСайта, 0);
// Установка обязательных заголовков
Соединение.SetRequestHeader("AuthToken", Токен);
Соединение.SetRequestHeader("Content-Type", "text/xml");
Соединение.SetRequestHeader("isinsert", 0); // 0 - обновление, 1 - вставка
// Отправка файла
Попытка
Соединение.Send(oADOStream);
Исключение
Сообщить("Ошибка отправки запроса!");
Возврат;
КонецПопытки;
// Проверка статуса ответа
Если Соединение.Status = 200 Тогда
Сообщить("Ответ сервера: " + Соединение.ResponseText);
Иначе
Сообщить("HTTP Ошибка: " + Соединение.Status + " - " + Соединение.StatusText);
КонецЕсли;
КонецПроцедуры
Обратим внимание на обязательные HTTP-заголовки. Если не передать Content-Type: text/xml, сервер вернет внутреннюю ошибку или ошибку формата данных. Заголовок isinsert указывает системе, создаем ли мы новую запись или обновляем существующую.
Для предприятий с большим документооборотом отправка файлов по одному вручную неэффективна. Разберем алгоритм автоматизированной пакетной отправки ТТН и производственных документов:
requestGuid (он должен быть уникальным при каждом POST-запросе, даже если мы обновляем данные).Пауза(1000) между отправками, чтобы не перегрузить сервер AITS).requestGuid. Если ответ положительный, помечаем документ как успешно переданный.Выясним причину частой проблемы: как выгрузить партию, если мы предприятие-посредник, и поставщик не передал нам GUIN партии сырья? В системе AITS существует недокументированный функционал. Если GUIN партии неизвестен, разработчики рекомендуют вместо него передавать тег GLN производителя. В этом случае сервер будет осуществлять поиск и привязку по связке: дата партии + номер партии + GLN производителя.
В случае, если система постоянно выдает ошибку «Идентификатор запроса 'RequestGuid' не является уникальным» при попытке обновить данные (параметр UPDATE), убедитесь, что вы генерируете новый RequestGuid именно для сессии связи, а не берете старый GUID самого документа или исходного файла.