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