Как правильно выгрузить и синхронизировать два связанных справочника через веб-сервис в 1С?

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

При разработке механизмов интеграции между различными конфигурациями «1С:Предприятие 8» программисты часто сталкиваются с необходимостью передачи связанных данных, например, справочников Номенклатура и ЕдиницыИзмерения. Основная сложность заключается не столько в самой передаче, сколько в правильной организации структуры данных, чтобы избежать избыточности и корректно восстановить связи в базе-приемнике. Разберем эту задачу подробно, проанализируем типичные ошибки и выработаем оптимальную стратегию реализации.

Анализ проблемы: почему стандартное соединение таблиц не эффективно

Рассмотрим ситуацию, представленную в исходном вопросе. Автор попытался получить данные одним запросом, используя ПОЛНОЕ СОЕДИНЕНИЕ. Проанализируем, к чему это приводит на практике. При соединении справочника Номенклатура со справочником ЕдиницыИзмерения по ссылке, мы получаем плоскую таблицу. Если у одной номенклатуры есть одна единица измерения, в таблице будет одна строка. Однако, если мы пытаемся выгрузить весь массив данных без четкой иерархии, мы получаем избыточность: наименование единицы измерения будет повторяться для каждой позиции номенклатуры, где она используется. Это увеличивает объем передаваемого трафика через веб-сервис, что критично для мобильных приложений.

Более того, использование ПОЛНОЕ СОЕДИНЕНИЕ без условий фильтрации может привести к появлению в выборке «одиноких» записей из обоих справочников, которые не связаны друг с другом, что окончательно запутает логику обработки в приемнике. Выясним, как сделать этот процесс более профессиональным.

Шаг 1. Оптимизация запроса на стороне сервера-источника

Вместо того чтобы пытаться «склеить» всё в одну таблицу, мы можем использовать более элегантные подходы. Рассмотрим первый вариант: использование двух временных таблиц и левого соединения, чтобы гарантировать получение номенклатуры вместе с ее актуальными единицами измерения.

Проанализируем пример кода, который позволит подготовить данные правильно:


Функция ПолучитьДанныеДляВыгрузки() Экспорт
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |	Номенклатура.Ссылка КАК СсылкаНоменклатуры,
    |	Номенклатура.Наименование КАК НаименованиеНоменклатуры,
    |	Номенклатура.Цена КАК Цена,
    |	ЕдиницыИзмерения.Наименование КАК НаименованиеЕдиницы,
    |	ЕдиницыИзмерения.Ссылка КАК СсылкаЕдиницы
    |ИЗ
    |	Справочник.Номенклатура КАК Номенклатура
    |		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ЕдиницыИзмерения КАК ЕдиницыИзмерения
    |		ПО Номенклатура.ЕдиницаИзмерения = ЕдиницыИзмерения.Ссылка";
    
    РезультатЗапроса = Запрос.Выполнить();
    Возврат Сериализовать(РезультатЗапроса.Выгрузить());
КонецФункции

Важный момент: если справочник ЕдиницыИзмерения является подчиненным справочнику Номенклатура, то в запросе обязательно нужно использовать связь по владельцу: ПО ЕдиницыИзмерения.Владелец = Номенклатура.Ссылка. Это гарантирует, что вы получите именно те единицы, которые принадлежат конкретной позиции.

Шаг 2. Передача данных и работа с GUID

Одной из главных ошибок при синхронизации является поиск элементов по наименованию. Рассмотрим ситуацию: в базе-источнике пользователь переименовал товар. Если в приемнике поиск идет по наименованию, система не найдет товар, создаст дубль, и у вас появится две карточки одного и того же товара с разными именами. Чтобы этого избежать, необходимо всегда передавать УникальныйИдентификатор (GUID).

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

СтрокаДанных.Идентификатор = Строка(Номенклатура.Ссылка.УникальныйИдентификатор());

В приемнике поиск будет выглядеть так:


Идентификатор = Новый УникальныйИдентификатор(ДанныеИзСети.GUID);
СсылкаНаЭлемент = Справочники.Номенклатура.ПолучитьСсылку(Идентификатор);
ОбъектНоменклатуры = СсылкаНаЭлемент.ПолучитьОбъект();

Если ОбъектНоменклатуры = Неопределено Тогда
    ОбъектНоменклатуры = Справочники.Номенклатура.СоздатьЭлемент();
    ОбъектНоменклатуры.УстановитьСсылкуНового(СсылкаНаЭлемент);
КонецЕсли;

Шаг 3. Обработка данных на стороне клиента (база-приемник)

Теперь разберем, как правильно загружать полученные данные. Проблема кода из вопроса в том, что создание элементов происходит безусловно, что порождает дубли при каждом нажатии кнопки. Нам нужно реализовать проверку существования. Рассмотрим алгоритм «Сначала ищем — потом обновляем или создаем».

Посмотрим на пример правильной обработки в цикле:


Пока Выборка.Следующий() Цикл
    // 1. Сначала работаем с Единицей Измерения
    СсылкаЕд = Справочники.ЕдиницыИзмерения.НайтиПоНаименованию(Выборка.НаименованиеЕдиницы);
    Если СсылкаЕд.Пустая() Тогда
        НоваяЕд = Справочники.ЕдиницыИзмерения.СоздатьЭлемент();
        НоваяЕд.Наименование = Выборка.НаименованиеЕдиницы;
        НоваяЕд.Записать();
        СсылкаЕд = НоваяЕд.Ссылка;
    КонецЕсли;

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

Шаг 4. Оптимизация производительности: Транзакции

Проанализируем ситуацию с точки зрения скорости. Если в вашей выборке 1000 элементов, вызов метода Записать() 1000 раз приведет к 1000 открытий и закрытий транзакций в базе данных. Это крайне медленно, особенно на мобильных устройствах. Мы должны объединить запись в одну транзакцию.

Рассмотрим, как это реализовать:


НачатьТранзакцию();
Попытка
    // Здесь располагается цикл загрузки (Шаг 3)
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    Сообщить("Ошибка при загрузке данных: " + ОписаниеОшибки());
КонецПопытки;

Шаг 5. Использование JSON вместо XML (XDTO)

Хотя веб-сервисы в 1С традиционно работают через SOAP/XDTO, для мобильных приложений и легких интеграций мы рекомендуем использовать формат JSON. Он значительно компактнее и быстрее обрабатывается. Начиная с платформы 8.3.6, в 1С реализованы мощные инструменты: ЗаписьJSON и ЧтениеJSON. Передавая через веб-сервис строку в формате JSON, вы уменьшаете нагрузку на сеть и упрощаете десериализацию данных в мобильном приложении.

Рекомендации по архитектуре обмена

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

  1. Разделяйте данные: Если справочники очень большие, не передавайте их целиком. Используйте ПланыОбмена для регистрации только измененных объектов.
  2. GUID — это закон: Никогда не синхронизируйте справочники только по наименованию. Использование УникальныйИдентификатор — единственный способ гарантировать целостность данных.
  3. Сначала владелец, потом подчиненный: Если вы передаете иерархические или подчиненные справочники, убедитесь, что в базе-приемнике сначала создается «родитель» (или владелец), а затем ему присваиваются ссылки на дочерние элементы.
  4. Порционность: При передаче больших объемов данных через веб-сервис (более 500-1000 записей), реализуйте пагинацию (выгрузку порциями), чтобы избежать ошибок переполнения памяти на мобильном клиенте.

Выполнив эти шаги, вы сможете создать надежную систему синхронизации данных, которая будет корректно обрабатывать связи между справочниками Номенклатура и ЕдиницыИзмерения, исключая дублирование и обеспечивая высокую скорость работы приложения.

← На главную