Почему не работает сравнение перечислений в обработчике "Перед выгрузкой" в Конвертации данных 2.1?

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

При настройке правил обмена в "Конвертации данных 2.1" часто возникает задача проверить какое-либо значение перед выгрузкой объекта. Стандартная ситуация: нужно сравнить значение реквизита-перечисления из базы-источника с конкретным значением перечисления в правилах. Однако, простой код, который отлично работает в обычной обработке, в обработчике события Перед выгрузкой внезапно перестает работать, и сравнение всегда возвращает "Ложь".

Рассмотрим типичный пример: при переносе документа из 1С:ERP в 1С:УПП необходимо проанализировать ставки НДС из табличной части документа-источника. Если все ставки одинаковы, это значение нужно записать в реквизит шапки документа-приемника. Проблема в том, что условие вида Если МоеЗначение = Перечисления.СтавкиНДС.НДС20 не срабатывает. Разберемся, почему так происходит и как это исправить.

Выясняем причину: магия COM-объектов

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

Этот COMОбъект является "оберткой" для нашего значения. Визуально в отладчике он может выглядеть точно так же, как и нужное нам значение перечисления, но с точки зрения системы 1С, его тип данных совершенно другой. Поэтому, когда мы выполняем прямое сравнение, платформа сопоставляет COMОбъект и ПеречислениеСсылка, которые никогда не будут равны.

Именно это несоответствие типов и является корнем проблемы, из-за которой прямое сравнение всегда возвращает Ложь.

Решение 1: Сравнение строковых представлений (рекомендуемый способ)

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

Давайте адаптируем исходный код из примера на форуме. Вместо прямого сравнения объектов перечислений будем сравнивать их имена.

Исходный проблемный код:


Если МассивСтавкиНДС.Количество() = 1 Тогда
    // ... прочие проверки
    // ЭТО УСЛОВИЕ НЕ РАБОТАЕТ
    ИначеЕсли МассивСтавкиНДС[0] = Перечисления.СтавкиНДС.НДС20 Тогда
        ИмяСтавкаНДС = "НДС20";
    // ...
    КонецЕсли;
КонецЕсли;

Правильный код с использованием Строка():


Если МассивСтавкиНДС.Количество() = 1 Тогда
    // Получаем строковое представление значения из источника
    ИмяСтавкиИзИсточника = XMLСтрока(МассивСтавкиНДС[0]); // Используем XMLСтрока, т.к. Строка() может дать представление "20%"

    // А сравниваем с именем значения перечисления
    Если ИмяСтавкиИзИсточника = "НДС20" Тогда
        ИмяСтавкаНДС = "НДС20";
    ИначеЕсли ИмяСтавкиИзИсточника = "БезНДС" Тогда
        ИмяСтавкаНДС = "БезНДС";
    // и так далее для всех нужных ставок
    КонецЕсли;
    
    // Если ИмяСтавкаНДС не пустая, то передаем ее в приемник
    Если НЕ ПустаяСтрока(ИмяСтавкаНДС) Тогда
        ВходящиеДанные.Вставить("СтавкаНДС", ИмяСтавкаНДС);
    КонецЕсли;
КонецЕсли;

Важный момент: функция Строка() для перечисления может вернуть его представление для пользователя (например, "20%"). А для сравнения нам нужно системное имя ("НДС20"). В таких случаях лучше использовать функцию XMLСтрока(), которая вернет именно имя значения перечисления. Другой вариант - получить имя через метаданные, но это более громоздко. Проще всего сравнить строковое представление значения из приемника: Если XMLСтрока(МассивСтавкиНДС[0]) = XMLСтрока(Перечисления.СтавкиНДС.НДС20) Тогда.

Этот подход является наиболее правильным, так как он не зависит от внутренних механизмов "Конвертации данных" и будет работать стабильно.

Решение 2: Оптимизация сбора уникальных значений

Первоначальная задача автора темы — проверить, все ли ставки НДС в табличной части одинаковы. Для этого был написан цикл, который перебирает все строки. Этот подход рабочий, но не оптимальный, особенно для документов с большим количеством строк. Есть более изящный и производительный способ.

Можно выгрузить всю колонку со ставками НДС в таблицу значений, а затем свернуть ее для получения уникальных значений. Если после сворачивания в таблице осталась одна строка, значит, все ставки были одинаковыми.

Посмотрим на пример кода:


// Выгружаем только колонку "СтавкаНДС" в новую ТаблицуЗначений
ТаблицаСтавок = Источник.Расходы.ВыгрузитьКолонку("СтавкаНДС");

// Сворачиваем таблицу по этой же колонке. Останутся только уникальные значения
ТаблицаСтавок.Свернуть("СтавкаНДС");

// Если после сворачивания осталась только одна строка, значит все ставки одинаковые
Если ТаблицаСтавок.Количество() = 1 Тогда
    
    // Получаем единственное уникальное значение ставки НДС
    ЕдинственнаяСтавка = ТаблицаСтавок[0].СтавкаНДС;
    
    // Теперь используем надежный метод сравнения через строку
    ИмяСтавкиНДС = XMLСтрока(ЕдинственнаяСтавка);
    
    // Передаем данные в приемник
    ВходящиеДанные.Вставить("УчитыватьНДС", Истина);
    ВходящиеДанные.Вставить("СтавкаНДС", ИмяСтавкиНДС);
    
Иначе
    // Если ставок несколько или их нет, ничего не делаем. 
    // Поле в приемнике останется пустым, как и требовалось.
КонецЕсли;

Этот код не только решает проблему сравнения, но и делает логику проверки более читаемой, лаконичной и быстрой.

Решение 3: Специфичный синтаксис "Конвертации данных"

В ходе обсуждения на форуме автор нашел еще одно рабочее решение, которое использует недокументированную особенность движка конвертации. Оказалось, что к COM-объекту можно обратиться через специальное свойство, чтобы получить исходное значение.

Код сравнения в этом случае выглядит так:


// Этот код тоже работает, но он менее очевиден
Если Источник.СтавкаНДС.ПеречислениеСтавкаНДС = Перечисления.СтавкиНДС.НДС20 Тогда
    // ...
КонецЕсли;

Хотя этот способ работает, он имеет существенные недостатки:

  1. Неочевидность: такой синтаксис не является стандартным для языка 1С и его сложно понять без знания контекста "Конвертации данных".
  2. Надежность: это внутренняя особенность движка, которая может измениться в будущих версиях, что приведет к поломке обмена.

Поэтому данный метод использовать не рекомендуется. Предпочтение следует отдавать универсальному и понятному сравнению через строковые представления.

← На главную