При интеграции различных конфигураций 1С с помощью технологии COM-соединения разработчики часто сталкиваются с проблемой передачи и сопоставления значений перечислений. Прямое присвоение типа НовСправочник.СтавкаНДС = Перечисления.СтавкиНДС.НДС18 работать не будет, так как объекты принадлежат разным информационным базам и разным процессам в операционной системе. Локальная база ничего не знает о внутренних идентификаторах (ссылках) COM-базы, и наоборот.
В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем типичные ошибки разработчиков и рассмотрим пять надежных и производительных способов работы с перечислениями через COM-соединение в 1С:Предприятии 8.3.
Для начала выясним причину, по которой простые методы сравнения и присвоения дают сбой. Когда мы обращаемся к внешней базе через COMConnector, все возвращаемые объекты являются прокси-объектами типа COMОбъект.
Посмотрим на типичную ошибку при сравнении объектов:
Если Строка(Стр.Ссылка) = Строка(Выборка.Ссылка[строка.РеквезитВторойБазы]) Тогда
// Код не выполнится корректно
КонецЕсли;
В данном сценарии функция Строка() выполняется в контексте локальной базы. Для внешнего COM-объекта она вернет системное строковое представление вида "COM-объект". В итоге условие превратится в сравнение "COM-объект" = "COM-объект", что всегда будет давать истину, независимо от реального значения перечисления. Это приводит к некорректной записи данных.
Теперь рассмотрим правильные, протестированные практикой подходы к решению этой задачи.
Начиная с платформы 1С 8.2, в системе доступна глобальная функция ПредопределенноеЗначение(). Она прекрасно работает и во внешнем COM-соединении. Этот метод является наиболее лаконичным, производительным и безопасным, так как не требует ручного поиска в метаданных или написания запросов.
Рассмотрим пример, когда нам нужно напрямую установить значение перечисления в создаваемом справочнике внешней базы:
// Создаем элемент справочника во второй базе через COM
НовСправочник = ComObject.Справочники.Номенклатура.СоздатьЭлемент();
// Устанавливаем перечисление СтавкиНДС напрямую с помощью ПредопределенноеЗначение
НовСправочник.СтавкаНДС = ComObject.ПредопределенноеЗначение("Перечисление.СтавкиНДС.НДС18");
НовСправочник.Записать();
Этот способ работает мгновенно, поскольку платформа выполняет поиск ссылки по ее полному имени на стороне COM-сервера без лишних промежуточных вызовов.
Метод XMLString() является аналогом локальной функции XMLСтрока(), но вызывается в контексте COM-соединения. Для перечислений этот метод возвращает строковое имя значения (имя из конфигуратора, например, "НДС18"). Это позволяет легко сопоставлять перечисления между базами.
Сценарий А: Получение значения из COM-базы и поиск аналога в локальной базе
// Получаем COM-значение перечисления из внешней базы
COMЗначение = ВыборкаИзCOM.СтавкаНДС;
// Получаем строковое имя значения (например, "НДС18")
ИмяЗначения = ComObject.XMLString(COMЗначение);
// Записываем значение в локальный справочник
ЛокальныйСправочник.СтавкаНДС = Перечисления.СтавкиНДС[ИмяЗначения];
Сценарий Б: Запись значения в COM-базу по локальному значению
// Получаем имя локального перечисления
ИмяЛокальногоЗначения = XMLСтрока(ЛокальныйСправочник.СтавкаНДС);
// Записываем во внешнюю базу через квадратные скобки
НовСправочник.СтавкаНДС = ComObject.Перечисления.СтавкиНДС[ИмяЛокальногоЗначения];
Каждое значение перечисления в 1С имеет свой порядковый номер — индекс (начиная с 0), соответствующий его расположению в конфигураторе. Если структуры перечислений в обеих базах идентичны, мы можем сопоставлять их по числовому индексу. Это избавляет от строковых операций и работает максимально быстро.
Перенос значения из COM-базы в локальную:
// Получаем числовой индекс значения во внешней базе
ИндексВCOM = ComObject.Перечисления.СтавкиНДС.Индекс(COMЗначениеПеречисления);
// Находим локальное значение по полученному индексу
ЛокальноеЗначение = Перечисления.СтавкиНДС.Получить(ИндексВCOM);
Перенос значения из локальной базы в COM-базу:
// Получаем локальный индекс
ИндексЛокальный = Перечисления.СтавкиНДС.Индекс(ЛокальноеЗначение);
// Находим значение в COM-базе по индексу
НовСправочник.СтавкаНДС = ComObject.Перечисления.СтавкиНДС.Получить(ИндексЛокальный);
Важное предупреждение: Используйте этот метод только в том случае, если вы абсолютно уверены, что состав и порядок значений перечисления в обеих конфигурациях полностью совпадают.
Иногда требуется сопоставить значения по их пользовательскому представлению (синониму), например, когда имена в конфигураторе различаются, но синонимы одинаковы (например, "Без НДС"). Для корректного получения представления COM-объекта необходимо использовать метод String() самого COM-соединения.
Проанализируем следующий пример кода:
// Получаем текстовое представление значения из COM-базы
ПредставлениеCOM = ComObject.String(COMЗначениеПеречисления); // Вернет, например, "18%"
// Выполняем поиск по представлению в локальной базе
Для Каждого ЗначениеИзПеречисления Из Перечисления.СтавкиНДС Цикл
Если Строка(ЗначениеИзПеречисления) = ПредставлениеCOM Тогда
ЛокальныйСправочник.СтавкаНДС = ЗначениеИзПеречисления;
Прервать;
КонецЕсли;
КонецЦикла;
Если вам нужно перенести большой массив данных и сопоставить множество значений, перебор каждого объекта через COM-соединение в цикле вызовет серьезные задержки из-за сетевых накладных расходов. В этом случае лучшим решением будет пакетная выгрузка значений через запрос во внешней базе.
Разберем эффективный алгоритм:
// Создаем объект Запрос во внешней базе
ЗапросCOM = ComObject.NewObject("Запрос");
ЗапросCOM.Текст =
"ВЫБРАТЬ
| СтавкиНДС.Ссылка КАК Ссылка,
| Порядок(СтавкиНДС) КАК Порядок
|ИЗ
| Перечисление.СтавкиНДС КАК СтавкиНДС";
ТаблицаЗначенийCOM = ЗапросCOM.Выполнить().Выгрузить();
// Теперь мы можем быстро обойти коллекцию на клиенте/сервере локальной базы
Для Каждого СтрокаCOM Из ТаблицаЗначенийCOM Цикл
// Получаем имя значения перечисления через XMLString для сопоставления
ИмяЗначения = ComObject.XMLString(СтрокаCOM.Ссылка);
// Выполняем сопоставление с локальным перечислением
Попытка
ЛокальноеЗначение = Перечисления.СтавкиНДС[ИмяЗначения];
// Выполняем дальнейшую обработку...
Исключение
// Обработка ситуации, если в локальной базе нет такого значения
КонецПопытки;
КонецЦикла;
= в локальном коде. Сравнивайте либо их индексы, либо строковые имена, полученные через ComObject.XMLString().Соответствие), чтобы не выполнять ресурсоемкие COM-вызовы повторно.V83.COMConnector, так как оно работает значительно быстрее, чем запуск полноценного приложения V83.Application.