При настройке обмена данными между двумя идентичными базами 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 и пытается найти по нему объект у себя. Если объект уже существует, она его находит. Если это новый элемент — возникает ошибка «Объект не найден». А если элемент был изменен в источнике, приемник все равно видит свои старые данные, так как никакой информации для обновления он не получил.
Теперь рассмотрим два эффективных способа решения этой задачи.
Чтобы передать не только ссылку, но и все данные элемента, нужно перед сериализацией получить сам объект. Для этого изменим код на стороне источника, используя метод ПолучитьОбъект().
Код на стороне источника:
Ответ = Новый Массив;
// ... (код запроса)
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Получаем сам объект вместо ссылки и добавляем его в массив
Ответ.Добавить(ВыборкаДетальныеЗаписи.Ссылка.ПолучитьОбъект());
КонецЦикла;
Возврат СериализаторXDTO.ЗаписатьXDTO(Ответ);
В этом случае в XML попадут все реквизиты объекта «Номенклатура». На стороне приемника вы получите объект XDTO, из которого можно будет прочитать все необходимые данные для создания или обновления элемента.
Плюсы:
Минусы:
Это более гибкий, простой и производительный метод, который в итоге и был реализован автором в теме. Суть в том, чтобы на стороне источника сформировать таблицу значений только с нужными данными, поместить ее в хранилище значений и передать через Web-сервис.
Разберем реализацию по шагам.
В конфигураторе, в операции Web-сервиса, укажите тип возвращаемого значения: ValueStorage (пространство имен http://v8.1c.ru/8.1/data/core).
Вместо массива ссылок сформируем таблицу значений со всеми необходимыми полями: ссылкой (для идентификации), кодом, наименованием и другими реквизитами. Затем выгрузим результат запроса в эту таблицу и поместим в ХранилищеЗначения.
// Создаем запрос с нужными полями
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Код КАК Код,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.КатегорияНоменклатуры КАК КатегорияНоменклатуры
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ПометкаУдаления";
РезультатЗапроса = Запрос.Выполнить();
// Выгружаем результат в ТаблицуЗначений и помещаем ее в ХранилищеЗначения
Возврат Новый ХранилищеЗначения(РезультатЗапроса.Выгрузить());
На стороне приемника получить данные становится очень просто. Платформа автоматически десериализует ХранилищеЗначения. Нам нужно лишь вызвать метод Получить(), чтобы извлечь из него нашу исходную таблицу значений.
// Настройки подключения к Web-сервису
Определение = Новый WSОпределения("http://localhost/MM/ws/Directories.1cws?wsdl", Логин, Пароль);
Прокси = Новый WSПрокси(Определение, "http://192.168.1.62/Directories", "Directories", "DirectoriesSoap");
Прокси.Пользователь = Логин;
Прокси.Пароль = Пароль;
// Получаем данные от Web-сервиса
Данные = Прокси.GetData();
// Извлекаем ТаблицуЗначений из ХранилищаЗначения
ТЗ = Данные.Получить();
// Теперь в переменной ТЗ у нас готовая таблица для обработки
Для Каждого СтрокаТЗ Из ТЗ Цикл
// Здесь будет код поиска, создания и обновления элементов
КонецЦикла;
Имея на руках таблицу значений, мы можем организовать логику синхронизации. Для каждой строки таблицы нужно выполнить следующие действия.
Ссылка. Это самый надежный способ.
НайденнаяСсылка = Справочники.Номенклатура.ПолучитьСсылку(СтрокаТЗ.Ссылка.УникальныйИдентификатор());
НайденнаяСсылка.Пустая()), значит, такого элемента в базе-приемнике нет, и его нужно создать. Важный момент: чтобы в будущем система могла сопоставлять этот элемент с элементом из базы-источника, нужно присвоить ему тот же самый GUID. Для этого используется метод УстановитьСсылкуНового().
Если НайденнаяСсылка.Пустая() Тогда
// Элемент не найден, создаем новый
НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
// Присваиваем GUID из источника - это ключевой шаг!
НовыйЭлемент.УстановитьСсылкуНового(СтрокаТЗ.Ссылка);
// Заполняем реквизиты из полученной таблицы
НовыйЭлемент.Код = СтрокаТЗ.Код;
НовыйЭлемент.Наименование = СтрокаТЗ.Наименование;
// ... другие реквизиты
НовыйЭлемент.Записать();
КонецЕсли;
// ... код после проверки на пустую ссылку
Иначе
// Элемент найден, проверяем, нужно ли его обновлять
ОбъектДляПроверки = НайденнаяСсылка.ПолучитьОбъект();
Если ОбъектДляПроверки.Наименование <> СтрокаТЗ.Наименование
Или ОбъектДляПроверки.Код <> СтрокаТЗ.Код Тогда
// Есть расхождения, обновляем данные
ОбъектДляПроверки.Наименование = СтрокаТЗ.Наименование;
ОбъектДляПроверки.Код = СтрокаТЗ.Код;
// ... другие реквизиты
ОбъектДляПроверки.Записать();
КонецЕсли;
КонецЕсли;
Стоит отметить, что для регулярной и надежной синхронизации данных между базами 1С существует встроенный механизм — Планы обмена. Он автоматически отслеживает измененные и новые объекты, формирует сообщения обмена в формате XML и обеспечивает гарантированную доставку данных. Хотя настройка планов обмена сложнее, чем создание простого Web-сервиса, для серьезных задач синхронизации это более предпочтительное и масштабируемое решение.