При интеграции различных информационных баз 1С через COM-соединение (V8.COMConnector) разработчики часто сталкиваются с проблемой сравнения ссылочных типов, в частности — перечислений. Прямое сравнение вида Если Объект.Реквизит = COMБаза.Перечисления.Имя.Значение всегда будет возвращать Ложь. Это происходит потому, что через COM-интерфейс в полной мере передаются только примитивные типы (число, строка, дата, булево). Объекты же ссылочных типов во внешней базе являются лишь COM-объектами (прокси-объектами), которые не идентичны объектам в текущей базе, даже если они имеют одинаковые имена и значения.
В данной статье мы подробно разберем, почему возникает эта сложность, и рассмотрим несколько надежных способов ее решения, от простых строковых сравнений до использования механизмов XML и кэширования метаданных.
Проанализируем ситуацию: когда мы обращаемся к COMБаза.Перечисления.ТипыНалогообложенияНДС.ПродажаОблагаетсяНДС, мы получаем указатель на объект во внешней памяти. Ссылка в нашей текущей базе — это уникальный идентификатор (GUID) в рамках текущей таблицы базы данных. Даже если конфигурации идентичны, для платформы это два разных объекта из разных контекстов. Для корректного сопоставления нам необходимо привести оба значения к общему «знаменателю», то есть к примитивному типу.
Рассмотрим метод, который считается наиболее стабильным. Функция XMLСтрока() (или метод XMLString() у COM-объекта) возвращает внутреннее имя значения перечисления, как оно задано в конфигураторе (Имя объекта). В отличие от синонима, это имя редко меняется в процессе поддержки конфигурации.
Разберем пример кода, предложенный в обсуждении:
// ДокумИст — это документ, полученный из COM-соединения
// COMБаза — объект подключения
Если COMБаза.XMLСтрока(ДокумИст.НалогообложениеНДС) = "ПродажаОблагаетсяНДС" Тогда
НовыйДокум.УчитыватьНДС = Истина;
Иначе
НовыйДокум.УчитыватьНДС = Ложь;
КонецЕсли;
Здесь мы вызываем метод XMLСтрока именно на стороне внешней базы, чтобы получить строковый идентификатор перечисления. Если мы просто напишем Строка(ДокумИст.НалогообложениеНДС), мы получим синоним (например, "Продажа облагается НДС"), который может измениться после обновления или зависеть от языка интерфейса, что сделает код хрупким.
Посмотрим на альтернативный подход — использование порядкового номера (индекса) значения в метаданных. Индекс — это число, оно легко передается через COM. Однако у этого метода есть существенный риск: если порядок значений в конфигураторе изменится (например, кто-то воспользуется кнопками «Переместить вверх/вниз»), индексы сместятся, и логика нарушится.
Тем не менее, разберем реализацию этого метода для понимания механики:
Функция ОпределитьТипНалогообложения(ЭлементОле, COMБаза)
// Получаем индекс значения во внешней базе
ИндексОле = COMБаза.Перечисления.ТипыНалогообложенияНДС.Индекс(ЭлементОле.НалогообложениеНДС);
// Сравниваем с индексом нужного нам значения в той же внешней базе
ИндексНужногоЗначения = COMБаза.Перечисления.ТипыНалогообложенияНДС.Индекс(COMБаза.Перечисления.ТипыНалогообложенияНДС.ПродажаОблагаетсяНДС);
Если ИндексОле = ИндексНужногоЗначения Тогда
Возврат Истина;
КонецЕсли;
Возврат Ложь;
КонецФункции
Этот способ можно использовать, если вы абсолютно уверены в неизменности структуры перечислений в обеих базах.
Выясним, как сделать сопоставление более элегантным, если перечисления в двух базах называются одинаково. Мы можем использовать коллекцию Перечисления как соответствие, где ключом выступает строковое имя.
Проанализируем пример:
// Получаем имя значения из локальной базы
ИмяЗначения = XMLСтрока(Перечисления.ТипыНалогообложенияНДС.ПродажаОблагаетсяНДС);
// Пытаемся получить аналогичное значение во внешней базе по этому имени
ЗначениеВДругойБазе = COMБаза.Перечисления.ТипыНалогообложенияНДС[ИмяЗначения];
Если ДокумИст.НалогообложениеНДС = ЗначениеВДругойБазе Тогда
// Это сравнение сработает, так как оба объекта получены из контекста COMБаза
НовыйДокум.УчитыватьНДС = Истина;
КонецЕсли;
Этот метод удобен тем, что он избавляет нас от «хардкода» строковых значений в тексте модуля, используя метаданные локальной базы для поиска во внешней.
Малоизвестный, но эффективный метод — использование встроенного объекта СравнениеЗначений (ValueComparison). Он позволяет корректно сравнивать объекты, полученные через COM. Для этого объект должен быть создан внутри контекста внешней базы.
Сравнение = COMБаза.NewObject("СравнениеЗначений");
Если Сравнение.Сравнить(ДокумИст.НалогообложениеНДС, COMБаза.Перечисления.ТипыНалогообложенияНДС.ПродажаОблагаетсяНДС) = 0 Тогда
// Значения равны
КонецЕсли;
Важно помнить, что каждое обращение к COMБаза.Перечисления... внутри цикла — это «дорогостоящий» вызов, который замедляет работу системы. Если вы обрабатываете тысячи документов, постоянное получение индексов или строковых имен через COM приведет к значительным задержкам.
Рассмотрим правильную стратегию работы:
Соответствие, где ключом будет строка (имя), а значением — COM-объект.Пример кэширования:
КэшНалогообложения = Новый Соответствие;
// Заполняем кэш один раз
КэшНалогообложения.Вставить("ПродажаОблагаетсяНДС", COMБаза.Перечисления.ТипыНалогообложенияНДС.ПродажаОблагаетсяНДС);
КэшНалогообложения.Вставить("ПродажаНеОблагаетсяНДС", COMБаза.Перечисления.ТипыНалогообложенияНДС.ПродажаНеОблагаетсяНДС);
// В цикле по документам
Для Каждого ДокумИст Из ВыборкаДокументов Цикл
Если ДокумИст.НалогообложениеНДС = КэшНалогообложения["ПродажаОблагаетсяНДС"] Тогда
// Быстрое сравнение без лишних COM-вызовов
КонецЕсли;
КонецЦикла;
Подведем итоги нашего анализа. При выборе способа сравнения перечислений через COM ориентируйтесь на следующие правила:
Иначе при сравнении, чтобы зафиксировать появление новых, непредусмотренных значений перечисления в источнике данных.Выбор правильного метода позволит вам создавать отказоустойчивые интеграционные решения, которые не потребуют правок при каждом обновлении конфигурации или изменении синонимов объектов.