Почему номенклатура приходит как NULL при обмене данными через веб-сервис 1С?

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

При интеграции информационных баз на платформе 1С:Предприятие через веб-сервисы, одной из частых и критичных проблем является корректная передача ссылочных типов данных, таких как элементы справочника Номенклатура, Контрагенты или ссылки на документы. Часто разработчики сталкиваются с ситуацией, когда в базе-источнике данные формируются правильно, но в принимающей базе ссылочные поля оказываются пустыми (Null). Разберем, почему это происходит и как можно эффективно решить эту проблему, чтобы обеспечить надежный и корректный обмен.

Суть проблемы: Ссылка вне контекста родной базы

Основная причина появления Null значений в принимающей базе при передаче ссылочных типов данных (например, Номенклатура) через веб-сервис заключается в том, что прямая передача ссылки теряет смысл вне контекста той базы, где она была создана. Каждая ссылка в 1С (это внутренний уникальный идентификатор, часто называемый УИД или GUID) уникальна только в пределах своей информационной базы. Когда вы пытаетесь передать объект-ссылку из одной базы в другую, принимающая система не может понять, что эта ссылка обозначает, поскольку ее внутренние идентификаторы не совпадают. В результате, поле, которое должно содержать ссылку на объект, остается незаполненным, и вы получаете Null.

Представим, что у нас есть база1, где хранится номенклатура с внутренним идентификатором b2c3d4e5-..., и база2. Когда мы передаем ссылку b2c3d4e5-... в базу2, она не знает, что это такое, поскольку в ее собственной структуре данных такого идентификатора нет. Это приводит к тому, что в базе2 соответствующее поле для номенклатуры остается незаполненным, отображаясь как Null.

Решение: Передача примитивных типов и идентификация объектов

Для корректной передачи ссылочных данных необходимо отказаться от прямой передачи ссылок и вместо этого использовать примитивные типы данных (строки, числа), которые однозначно идентифицируют объект в обеих базах. На стороне базы-приемника мы будем использовать эти примитивные данные для поиска и сопоставления существующего объекта или создания нового, если он отсутствует.

Подход 1: Использование уникальных идентификаторов (УИД/GUID) для синхронизированных баз

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

  1. На стороне базы-отправителя (База1):

    При выгрузке данных в веб-сервис, вместо передачи самой ссылки на объект Номенклатура, мы передаем ее уникальный идентификатор, преобразованный в строковый формат. Для этого используем метод XMLСтрока().

    Рассмотрим пример, когда нам нужно передать номенклатуру и ее количество:

    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ДокументПродажиТовары.Номенклатура КАК Номенклатура,
        |    ДокументПродажиТовары.Количество КАК Количество
        |ИЗ
        |    Документ.ДокументПродажи.Товары КАК ДокументПродажиТовары
        |ГДЕ
        |    ДокументПродажиТовары.Ссылка = &СсылкаДокумента";
    
    Запрос.УстановитьПараметр("СсылкаДокумента", МойДокументПродажи); // Например, ссылка на текущий документ
    
    РезультатЗапроса = Запрос.Выполнить().Выгрузить();
    
    // Предположим, что РезультатЗапроса - это таблица значений, которую мы хотим передать.
    // Переберем строки и заменим Номенклатуру на ее УИД в строковом представлении.
    ТаблицаДляПередачи = Новый ТаблицаЗначений;
    ТаблицаДляПередачи.Колонки.Добавить("НоменклатураУИД", Новый ОписаниеТипов("Строка"));
    ТаблицаДляПередачи.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
    
    Для Каждого СтрокаТЗ Из РезультатЗапроса Цикл
        НоваяСтрока = ТаблицаДляПередачи.Добавить();
        НоваяСтрока.НоменклатураУИД = XMLСтрока(СтрокаТЗ.Номенклатура); // Преобразуем ссылку в УИД-строку
        НоваяСтрока.Количество = СтрокаТЗ.Количество;
    КонецЦикла;
    
    // Теперь ТаблицаДляПередачи содержит УИД номенклатуры и количество.
    // Эту таблицу или ее сериализованное представление можно передать через веб-сервис.
    

    Важно помнить, что XMLСтрока() умеет работать с ссылочными типами, преобразуя их в строковый УИД. Это позволяет нам передавать уникальный идентификатор объекта, а не саму ссылку.

  2. На стороне базы-получателя (База2):

    Веб-сервис принимает данные, включая строковое представление УИД номенклатуры. Нам необходимо преобразовать эту строку обратно в ссылку на объект в текущей базе. Для этого используется метод XMLЗначение().

    
    // Предположим, что мы получили ТаблицаПринятыхДанных через веб-сервис
    // В этой таблице есть колонка "НоменклатураУИД" типа Строка.
    
    Для Каждого СтрокаПринятыхДанных Из ТаблицаПринятыхДанных Цикл
        Попытка
            // Преобразуем строковый УИД обратно в ссылку на объект
            НоменклатураСсылка = XMLЗначение(Тип("СправочникСсылка.Номенклатура"), СтрокаПринятыхДанных.НоменклатураУИД);
    
            // Проверим, существует ли объект по этой ссылке
            Если НоменклатураСсылка.Пустая() Тогда
                // Объект не найден или некорректный УИД
                Сообщить("Номенклатура с УИД " + СтрокаПринятыхДанных.НоменклатураУИД + " не найдена.");
                Продолжить;
            КонецЕсли;
    
            // Теперь у нас есть корректная ссылка на объект Номенклатура в Базе2
            // Мы можем использовать ее для дальнейших операций, например, создания документа
            // ...
        Исключение
            Сообщить("Ошибка при получении номенклатуры по УИД " + СтрокаПринятыхДанных.НоменклатураУИД + ": " + ОписаниеОшибки());
        КонецПопытки;
    КонецЦикла;
    

    Метод XMLЗначение() в сочетании с указанием типа Тип("СправочникСсылка.Номенклатура") позволяет системе найти объект по его УИД в текущей базе данных. Если объект с таким УИД существует, будет возвращена ссылка на него. Если нет – будет возвращена пустая ссылка или возникнет ошибка, в зависимости от контекста и корректности УИД.

    Важное условие: Этот метод работает только в том случае, если элементы номенклатуры в обеих базах уже синхронизированы по своим УИДам. То есть, одна и та же позиция номенклатуры в Базе1 и Базе2 должна иметь одинаковый внутренний уникальный идентификатор.

Подход 2: Использование полей поиска (Код, Наименование, Артикул) для несинхронизированных баз

Если базы не синхронизированы по УИДам (что часто бывает при первоначальной настройке обмена или при обмене с внешними системами, не на платформе 1С), то необходимо использовать поля, по которым можно однозначно идентифицировать элемент. В качестве таких полей обычно выступают Код, Артикул, Наименование или их комбинация. Этот метод более гибкий, так как не требует предварительной синхронизации GUID, но требует тщательного контроля за уникальностью полей поиска.

  1. На стороне базы-отправителя (База1):

    Вместо ссылки на объект Номенклатура, мы передаем в веб-сервис значения ее ключевых полей, которые мы будем использовать для поиска в принимающей базе. Например, это может быть Код или Наименование.

    Модифицируем предыдущий пример:

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

    Мы видим, что вместо ссылки на объект мы теперь получаем и передаем его Код и Наименование.

  2. На стороне базы-получателя (База2):

    Веб-сервис принимает данные, содержащие КодНоменклатуры и НаименованиеНоменклатуры. Далее мы должны выполнить поиск по этим полям в справочнике Номенклатура.

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

    В этом подходе крайне важно обеспечить уникальность полей поиска (Код, Артикул, Наименование) в обеих базах. Если в Базе2 существует несколько элементов номенклатуры с одинаковым кодом или наименованием, то метод НайтиПоКоду() или НайтиПоНаименованию() вернет только одну из них (обычно первую найденную), что может привести к ошибкам в обмене.

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

Общие рекомендации и лучшие практики

  1. Единая модель данных: Старайтесь поддерживать максимально схожую структуру справочников и документов в обменивающихся базах. Это значительно упрощает сопоставление данных.
  2. Механизм XDTO: Платформа 1С активно использует XDTO (XML Data Transfer Objects) для обмена данными. Этот механизм позволяет описывать структуры передаваемых данных с помощью XML-схем. При правильной настройке XDTO-пакетов и типов, 1С может автоматически выполнять сериализацию и десериализацию ссылочных объектов, особенно когда базы синхронизированы по УИДам. Если вы работаете с веб-сервисами, которые генерируются из конфигурации 1С, то XDTO уже используется "под капотом". Наша задача – правильно подготовить данные для XDTO.
  3. Логирование и обработка ошибок: Всегда реализуйте подробное логирование процесса обмена. Записывайте все успешные операции, а также детализированные сообщения об ошибках. Это поможет быстро выявлять и устранять проблемы, особенно на этапах отладки. Например, если номенклатура не найдена, запишите ее код и наименование, чтобы в дальнейшем можно было вручную или автоматически решить эту проблему.
  4. Транзакции: При записи данных в принимающей базе через веб-сервис, всегда используйте транзакции. Если в процессе записи возникает ошибка, транзакция должна быть отменена, чтобы избежать частичной записи данных и сохранения некорректного состояния.
    
    НачатьТранзакцию();
    Попытка
        // ... логика поиска и записи данных ...
        // Например, запись документа
        ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        Сообщить("Ошибка при обработке данных: " + ОписаниеОшибки(), СтатусСообщения.Ошибка);
        // Дополнительное логирование ошибки
        Возврат;
    КонецПопытки;
    
  5. Производительность: При больших объемах данных уделяйте внимание производительности. Пакетная передача данных вместо поштучной, оптимизация запросов и индексов в базах данных могут значительно ускорить процесс обмена.

Таким образом, проблема с появлением Null значений при передаче ссылочных объектов через веб-сервис в 1С решается не прямой передачей ссылок, а использованием примитивных типов данных, таких как уникальные идентификаторы (УИД/GUID) или ключевые поля (Код, Наименование, Артикул). Выбор подхода зависит от степени синхронизации и специфики обменивающихся баз. Внедряя эти методы, мы обеспечиваем корректную идентификацию и сопоставление объектов, что является основой для надежного и бесперебойного обмена данными между информационными системами.

← На главную