Как настроить обмен справочниками между базами 1С через Web-сервис и XDTO?

Программист 1С v8.3 (Управляемые формы)
← На главную

При настройке обмена данными между двумя идентичными базами 1С через Web-сервис часто возникает задача по синхронизации справочников, например, «Номенклатура». Стандартный подход с использованием сериализации XDTO может вызывать трудности: ошибки при чтении данных, проблемы с созданием новых элементов и обновлением существующих. Проанализируем типичные ошибки и разберем надежные способы их решения.

Основная проблема заключается в том, что при попытке десериализовать массив ссылок на объекты, полученный через Web-сервис, система выдает ошибку «Отсутствует отображение для типа '{http://v8.1c.ru/8.1/data/enterprise/current-config}CatalogRef.Номенклатура'». Кроме того, если элемент новый, в базе-приемнике возникает ошибка «Объект не найден», а для измененных элементов не видны новые данные.

Давайте по шагам разберемся, почему так происходит и как правильно организовать такой обмен.

Причина ошибок: передача ссылки вместо данных

Первоначальная ошибка кроется в том, что именно мы пытаемся передать. Рассмотрим типичный код на стороне источника:


Ответ = Новый Массив;
// ... (код запроса для получения ссылок)
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
    Ответ.Добавить(ВыборкаДетальныеЗаписи.Ссылка);
КонецЦикла;
    
Возврат СериализаторXDTO.ЗаписатьXDTO(Ответ);

В этом коде мы добавляем в массив не сами объекты номенклатуры с их реквизитами, а только ссылки на них (тип CatalogRef.Номенклатура). При сериализации в XML такая ссылка превращается лишь в ее уникальный идентификатор (GUID). Выглядит это примерно так:



    5e5a7257-6896-11e8-8780-b06ebf2faaf6

Как видим, здесь нет ни наименования, ни кода, ни других реквизитов. База-приемник получает только GUID и пытается найти по нему объект у себя. Если объект уже существует, она его находит. Если это новый элемент — возникает ошибка «Объект не найден». А если элемент был изменен в источнике, приемник все равно видит свои старые данные, так как никакой информации для обновления он не получил.

Теперь рассмотрим два эффективных способа решения этой задачи.

Решение 1: Передача полных данных объекта через XDTO

Чтобы передать не только ссылку, но и все данные элемента, нужно перед сериализацией получить сам объект. Для этого изменим код на стороне источника, используя метод ПолучитьОбъект().

Код на стороне источника:


Ответ = Новый Массив;
// ... (код запроса)
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
    // Получаем сам объект вместо ссылки и добавляем его в массив
    Ответ.Добавить(ВыборкаДетальныеЗаписи.Ссылка.ПолучитьОбъект());
КонецЦикла;
    
Возврат СериализаторXDTO.ЗаписатьXDTO(Ответ);

В этом случае в XML попадут все реквизиты объекта «Номенклатура». На стороне приемника вы получите объект XDTO, из которого можно будет прочитать все необходимые данные для создания или обновления элемента.

Плюсы:

Минусы:

Решение 2: Передача таблицы значений через ХранилищеЗначения (рекомендуемый способ)

Это более гибкий, простой и производительный метод, который в итоге и был реализован автором в теме. Суть в том, чтобы на стороне источника сформировать таблицу значений только с нужными данными, поместить ее в хранилище значений и передать через Web-сервис.

Разберем реализацию по шагам.

Шаг 1. Настройка Web-сервиса на источнике

В конфигураторе, в операции Web-сервиса, укажите тип возвращаемого значения: ValueStorage (пространство имен http://v8.1c.ru/8.1/data/core).

Шаг 2. Код на стороне источника

Вместо массива ссылок сформируем таблицу значений со всеми необходимыми полями: ссылкой (для идентификации), кодом, наименованием и другими реквизитами. Затем выгрузим результат запроса в эту таблицу и поместим в ХранилищеЗначения.


// Создаем запрос с нужными полями
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Номенклатура.Ссылка КАК Ссылка,
    |    Номенклатура.Код КАК Код,
    |    Номенклатура.Наименование КАК Наименование,
    |    Номенклатура.КатегорияНоменклатуры КАК КатегорияНоменклатуры
    |ИЗ
    |    Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |    НЕ Номенклатура.ПометкаУдаления";

РезультатЗапроса = Запрос.Выполнить();
    
// Выгружаем результат в ТаблицуЗначений и помещаем ее в ХранилищеЗначения
Возврат Новый ХранилищеЗначения(РезультатЗапроса.Выгрузить());

Шаг 3. Код на стороне приемника

На стороне приемника получить данные становится очень просто. Платформа автоматически десериализует ХранилищеЗначения. Нам нужно лишь вызвать метод Получить(), чтобы извлечь из него нашу исходную таблицу значений.


// Настройки подключения к Web-сервису
Определение = Новый WSОпределения("http://localhost/MM/ws/Directories.1cws?wsdl", Логин, Пароль);
Прокси = Новый WSПрокси(Определение, "http://192.168.1.62/Directories", "Directories", "DirectoriesSoap");
Прокси.Пользователь = Логин;
Прокси.Пароль = Пароль;      

// Получаем данные от Web-сервиса
Данные = Прокси.GetData();

// Извлекаем ТаблицуЗначений из ХранилищаЗначения
ТЗ = Данные.Получить();

// Теперь в переменной ТЗ у нас готовая таблица для обработки
Для Каждого СтрокаТЗ Из ТЗ Цикл
    // Здесь будет код поиска, создания и обновления элементов
КонецЦикла;

Шаг 4. Обработка данных в приемнике: создание и обновление элементов

Имея на руках таблицу значений, мы можем организовать логику синхронизации. Для каждой строки таблицы нужно выполнить следующие действия.

  1. Поиск элемента. Искать элемент в базе-приемнике нужно по его уникальному идентификатору (GUID), который содержится в колонке Ссылка. Это самый надежный способ.
    
    НайденнаяСсылка = Справочники.Номенклатура.ПолучитьСсылку(СтрокаТЗ.Ссылка.УникальныйИдентификатор());
    
  2. Создание нового элемента. Если ссылка пустая (НайденнаяСсылка.Пустая()), значит, такого элемента в базе-приемнике нет, и его нужно создать. Важный момент: чтобы в будущем система могла сопоставлять этот элемент с элементом из базы-источника, нужно присвоить ему тот же самый GUID. Для этого используется метод УстановитьСсылкуНового().
    
    Если НайденнаяСсылка.Пустая() Тогда
        // Элемент не найден, создаем новый
        НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
        
        // Присваиваем GUID из источника - это ключевой шаг!
        НовыйЭлемент.УстановитьСсылкуНового(СтрокаТЗ.Ссылка); 
        
        // Заполняем реквизиты из полученной таблицы
        НовыйЭлемент.Код = СтрокаТЗ.Код;
        НовыйЭлемент.Наименование = СтрокаТЗ.Наименование;
        // ... другие реквизиты
        
        НовыйЭлемент.Записать();
    КонецЕсли;
    
  3. Обновление существующего элемента. Если элемент найден, нужно сравнить его реквизиты с данными из таблицы значений и при необходимости обновить.
    
    // ... код после проверки на пустую ссылку
    Иначе 
        // Элемент найден, проверяем, нужно ли его обновлять
        ОбъектДляПроверки = НайденнаяСсылка.ПолучитьОбъект();
        
        Если ОбъектДляПроверки.Наименование <> СтрокаТЗ.Наименование 
            Или ОбъектДляПроверки.Код <> СтрокаТЗ.Код Тогда
            
            // Есть расхождения, обновляем данные
            ОбъектДляПроверки.Наименование = СтрокаТЗ.Наименование;
            ОбъектДляПроверки.Код = СтрокаТЗ.Код;
            // ... другие реквизиты
            
            ОбъектДляПроверки.Записать();
        КонецЕсли;
    КонецЕсли;
    

Альтернатива: использование Планов обмена

Стоит отметить, что для регулярной и надежной синхронизации данных между базами 1С существует встроенный механизм — Планы обмена. Он автоматически отслеживает измененные и новые объекты, формирует сообщения обмена в формате XML и обеспечивает гарантированную доставку данных. Хотя настройка планов обмена сложнее, чем создание простого Web-сервиса, для серьезных задач синхронизации это более предпочтительное и масштабируемое решение.

← На главную