Как правильно настроить односторонний обмен данными между 1С:КА 1.1 и 1С:БП 3.0 без COM-соединения?

Программист 1С v8.3 (Обычные формы) 1С:Комплексная автоматизация IT и автоматизация бизнеса
← На главную

При интеграции устаревших конфигураций на обычных формах (таких как 1С:Комплексная автоматизация 1.1) с современными базами (например, 1С:Бухгалтерия предприятия 3.0) разработчики часто сталкиваются с необходимостью настройки надежного автоматического обмена. Стандартное COM-соединение постепенно уходит в прошлое из-за проблем с производительностью, требовательности к лицензиям и привязки к платформе Windows.

В этой статье мы подробно разберем, как построить архитектурно правильный («по феншую») односторонний обмен данными. Мы исключим использование COM-соединения, задействуем планы обмена для регистрации изменений, организуем передачу через современные HTTP-сервисы и реализуем надежную схему гарантированной доставки данных (квитирование).

Архитектура «правильного» обмена: ключевые принципы

Чтобы обмен работал стабильно, не терял данные при сбоях сети и не нагружал систему избыточной выгрузкой, мы должны опираться на три важных столпа:

  1. Использование Планов обмена для регистрации изменений. Вместо выгрузки документов «за период» (которая неизбежно приводит к перезаписи неизмененных объектов и пропускам при изменении задним числом), мы задействуем механизм регистрации изменений платформы 1С.
  2. Квитирование (подтверждение доставки). Мы никогда не очищаем регистрацию изменений сразу после выгрузки. Очистка должна происходить только тогда, когда база-приемник пришлет ответ: «Пакет успешно получен и записан».
  3. HTTP-сервисы вместо Web-сервисов (SOAP) и COM. HTTP-сервисы (REST) работают быстрее, легче масштабируются и позволяют передавать сжатые zip-архивы данных прямо в теле запроса, минимизируя нагрузку на сеть.
  4. Конвертация данных 2.0 (КД2) против EnterpriseData (КД3). Мы проанализируем обе технологии. КД2 идеальна для быстрого написания кастомных правил под конкретные измененные конфигурации. КД3 (EnterpriseData) удобна тем, что БП 3.0 уже готова принимать данные в этом формате без изменения ее конфигурации.

Шаг 1. Настройка плана обмена на стороне источника (КА 1.1)

План обмена необходим для фиксации измененных объектов. Разберем, как его правильно настроить. Создадим новый план обмена в конфигураторе КА 1.1 (например, ПланОбменаОбменСБухгалтерией). В составе плана обмена укажем те документы и справочники, которые мы планируем переносить в БП 3.0. Авторегистрацию для этих объектов можно установить в режим «Разрешить».

Главная ошибка начинающих разработчиков — очищать регистрацию изменений сразу после генерации файла выгрузки. Если в этот момент произойдет сбой сети, данные в приемник не попадут, а источник будет «думать», что всё отправлено. Правильный алгоритм строится на номерах сообщений.

Проанализируем программный код выгрузки изменений с использованием номеров сообщений на стороне КА 1.1:


// Определяем узел обмена
УзелОбмена = ПланыОбмена.НайтиПоКоду("БП");
Если УзелОбмена.Пустая() Тогда
    ВызватьИсключение "Не найден узел обмена для Бухгалтерии!";
КонецЕсли;

// Увеличиваем номер отправляемого сообщения на 1
НовыйНомерСообщения = УзелОбмена.НомерОтправленного + 1;

// Начинаем выборку измененных данных
ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(УзелОбмена, НовыйНомерСообщения);

// Создаем объект для выгрузки по правилам КД2
ОбработкаОбмена = Обработки.УниверсальныйОбменДаннымиXML.Создать();
ОбработкаОбмена.ИмяФайлаПравилОбмена = "C:\Rules\Rules_KA_BP.xml"; // Путь к правилам конвертации
ОбработкаОбмена.РежимОбмена = "Выгрузка";

// Вместо записи в файл на диск, пишем XML-данные прямо в строку
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();

// Выполняем выгрузку зарегистрированных изменений
ОбработкаОбмена.ВыполнитьВыгрузку(ЗаписьXML, ВыборкаИзменений);
СтрокаXML = ЗаписьXML.Закрыть();

Обратите внимание: метод ПланыОбмена.ВыбратьИзменения() не просто выбирает данные, но и закрепляет за каждым выгруженным объектом переданный НовыйНомерСообщения. Если объект изменится снова во время сеанса обмена, платформа зарегистрирует его повторно, но уже без привязки к текущему номеру сообщения.

Шаг 2. Сжатие данных и отправка через HTTP-сервис

Передавать «сырой» XML-текст большого объема по сети неэффективно. Намного правильнее сжать его в ZIP-архив. Рассмотрим, как упаковать полученную XML-строку в архив и отправить её получателю с помощью объекта HTTPСоединение:


// Сжатие данных в ZIP-архив во временный файл
ИмяВременногоФайлаXML = ПолучитьИмяВременногоФайла("xml");
ТекстовыйДокумент = Новый ТекстовыйДокумент;
ТекстовыйДокумент.УстановитьТекст(СтрокаXML);
ТекстовыйДокумент.Записать(ИмяВременногоФайлаXML, КодировкаТекста.UTF8);

ИмяВременногоАрхива = ПолучитьИмяВременногоФайла("zip");
Архив = Новый ЗаписьZipФайла(ИмяВременногоАрхива);
Архив.Добавить(ИмяВременногоФайлаXML, МетодСжатияZIP.Сжатие, КодированиеИменФайловZIP.UTF8);
Архив.Записать();

// Читаем архив в двоичные данные для отправки
ДвоичныеДанныеАрхива = Новый ДвоичныеДанные(ИмяВременногоАрхива);

// Удаляем временные файлы с диска источника
УдалитьФайлы(ИмяВременногоФайлаXML);
УдалитьФайлы(ИмяВременногоАрхива);

// Настройка HTTP-соединения с БП 3.0
Сервер = "192.168.1.100"; // Адрес сервера БП
Порт = 80;
Пользователь = "ExchangeUser";
Пароль = "StrongPassword";

Соединение = Новый HTTPСоединение(Сервер, Порт, Пользователь, Пароль);
Запрос = Новый HTTPЗапрос("/bp_base/hs/exchange/v1/import");
Запрос.УстановитьТелоИзДвоичныхДанных(ДвоичныеДанныеАрхива);

Попытка
    Ответ = Соединение.ОтправитьДляОбработки(Запрос); // Отправляем POST-запрос
    
    Если Ответ.КодСостояния = 200 Тогда
        // Квитирование: Сервер успешно обработал пакет
        // Только теперь мы можем очистить регистрацию изменений в источнике!
        ПланыОбмена.УдалитьРегистрациюИзменений(УзелОбмена, НовыйНомерСообщения);
        
        // Фиксируем новый номер отправленного сообщения в узле
        ОбъектУзла = УзелОбмена.ПолучитьОбъект();
        ОбъектУзла.НомерОтправленного = НовыйНомерСообщения;
        ОбъектУзла.Записать();
        
        ЗаписьЖурналаРегистрации("Обмен данными", УровеньЖурналаРегистрации.Информация, , , 
            "Обмен успешно завершен. Номер сообщения: " + НовыйНомерСообщения);
    Иначе
        ЗаписьЖурналаРегистрации("Обмен данными", УровеньЖурналаРегистрации.Ошибка, , , 
            "Ошибка отправки данных. Код ответа сервера: " + Ответ.КодСостояния);
    КонецЕсли;
Исключение
    ЗаписьЖурналаРегистрации("Обмен данными", УровеньЖурналаРегистрации.Ошибка, , , 
        "Не удалось связаться с базой-приемником: " + ОписаниеОшибки());
КонецПопытки;

Метод ПланыОбмена.УдалитьРегистрациюИзменений() удалит регистрацию только для тех объектов, чей номер сообщения меньше или равен переданному НовыйНомерСообщения. Если во время загрузки произошел сбой, очистка не выполнится, и при следующем запуске регламентного задания все эти данные будут повторно выгружены.

Шаг 3. Создание HTTP-сервиса на стороне приемника (БП 3.0)

Теперь переместимся в базу-приемник 1С:Бухгалтерия предприятия 3.0. Здесь нам необходимо создать HTTP-сервис, который будет принимать ZIP-архив, распаковывать его и загружать данные через встроенную обработку УниверсальныйОбменДаннымиXML.

Выполним следующие шаги в конфигураторе БП 3.0:

  1. Создадим новый объект конфигурации HTTP-сервисы с именем ОбменДанными.
  2. Установим корневой URL: exchange.
  3. Добавим шаблон URL с именем Импорт и шаблоном пути: /v1/import.
  4. Для шаблона добавим метод POST и назначим ему обработчик (функцию в модуле HTTP-сервиса).

Проанализируем код обработчика POST-запроса на стороне БП 3.0:


Функция ИмпортДанныхPOST(Запрос)
    
    // Получаем двоичные данные архива из тела запроса
    ДвоичныеДанныеАрхива = Запрос.ПолучитьТелоКакДвоичныеДанные();
    Если ДвоичныеДанныеАрхива = Неопределено Или ДвоичныеДанныеАрхива.Размер() = 0 Тогда
        Ответ = Новый HTTPСервисОтвет(400);
        Ответ.УстановитьТелоИспользуяСтроку("Пустой запрос");
        Возврат Ответ;
    КонецЕсли;
    
    // Сохраняем пришедший архив во временный файл
    ИмяВременногоАрхива = ПолучитьИмяВременногоФайла("zip");
    ДвоичныеДанныеАрхива.Записать(ИмяВременногоАрхива);
    
    // Распаковываем архив во временный каталог
    КаталогРаспаковки = ПолучитьИмяВременногоФайла("");
    Архив = Новый ЧтениеZipФайла(ИмяВременногоАрхива);
    Архив.ИзвлечьВсе(КаталогРаспаковки, РежимВосстановленияПутейФайловZIP.НеВосстанавливать);
    Архив.Закрыть();
    
    // Полагаем, что внутри архива один файл данных XML
    НайденныеФайлы = НайтиФайлы(КаталогРаспаковки, "*.xml");
    Если НайденныеФайлы.Количество() = 0 Тогда
        УдалитьФайлы(ИмяВременногоАрхива);
        УдалитьФайлы(КаталогРаспаковки);
        
        Ответ = Новый HTTPСервисОтвет(400);
        Ответ.УстановитьТелоИспользуяСтроку("Файл данных не найден внутри архива");
        Возврат Ответ;
    КонецЕсли;
    
    ПутьКФайлуДанных = НайденныеФайлы[0].ПолноеИмя;
    
    // Инициализируем обработку УниверсальныйОбменДаннымиXML в режиме загрузки
    ОбработкаЗагрузки = Обработки.УниверсальныйОбменДаннымиXML.Создать();
    ОбработкаЗагрузки.РежимОбмена = "Загрузка";
    ОбработкаЗагрузки.ИмяФайлаОбмена = ПутьКФайлуДанных;
    
    Попытка
        ОбработкаЗагрузки.ВыполнитьЗагрузку();
        
        // Очищаем временные папки и файлы
        УдалитьФайлы(ИмяВременногоАрхива);
        УдалитьФайлы(КаталогРаспаковки);
        
        // Возвращаем успешный ответ источнику
        Ответ = Новый HTTPСервисОтвет(200);
        Ответ.УстановитьТелоИспользуяСтроку("Пакет успешно обработан и загружен");
        Возврат Ответ;
        
    Исключение
        // В случае ошибки логируем ее и удаляем временные файлы
        ЗаписьЖурналаРегистрации("Загрузка данных", УровеньЖурналаРегистрации.Ошибка, , , 
            "Ошибка при загрузке XML: " + ОписаниеОшибки());
            
        УдалитьФайлы(ИмяВременногоАрхива);
        УдалитьФайлы(КаталогРаспаковки);
        
        Ответ = Новый HTTPСервисОтвет(500);
        Ответ.УстановитьТелоИспользуяСтроку("Внутренняя ошибка сервера при записи объектов: " + ОписаниеОшибки());
        Возврат Ответ;
    КонецПопытки;
    
КонецФункции

Альтернативный вариант: Использование EnterpriseData (КД3)

Если вы хотите избежать написания правил обмена в Конвертации Данных 2.0 и последующей их поддержки при обновлениях конфигурации 1С:Бухгалтерия 3.0, имеет смысл посмотреть в сторону формата EnterpriseData.

Начиная с релизов серии 1.1.110, фирма «1С» встроила базовую поддержку универсального формата обмена данными (EnterpriseData версии 1.5) прямо в старую конфигурацию «Комплексная автоматизация 1.1».

Рассмотрим особенности этого подхода:

  1. Стандартизация. БП 3.0 уже содержит встроенные механизмы для приема данных в формате EnterpriseData. Вам не придется переписывать правила конвертации со стороны приемника при обновлениях БП.
  2. Использование Конвертации Данных 3.0. КД3 работает принципиально иначе, чем КД2. Правила настраиваются не напрямую между базами, а для каждой конфигурации отдельно по отношению к промежуточному XDTO-пакету.
  3. Ограничения версий. Поскольку в КА 1.1 встроен формат старой версии (1.5), а современная БП 3.0 оперирует более новыми версиями (1.15 и выше), при переносе специфичных или новых реквизитов могут возникать коллизии. Если ваши конфигурации сильно кастомизированы, классический вариант на КД 2.0 через HTTP-сервис окажется более гибким и предсказуемым в разработке.

Подводя итог, можно сказать, что связка План обмена (с квитированием) + КД 2.0 + HTTP-сервис (с ZIP-архивацией в памяти) является наиболее производительным, надежным и контролируемым решением для одностороннего обмена данными. Это позволяет полностью отказаться от нестабильных COM-соединений и гарантирует доставку каждого документа.

← На главную