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