При разработке правил обмена данными в конфигурации «Конвертация данных 2.0» (КД 2.0) одной из самых частых задач является фильтрация выгружаемых объектов. Для этого разработчики используют обработчик событий Перед выгрузкой в Правилах конвертации объектов (ПКО), устанавливая параметр Отказ = Истина при невыполнении определенных условий. Однако на этом этапе легко совершить логическую ошибку, из-за которой фильтр либо перестает работать, либо начинает пропускать лишние данные.
В этой статье мы подробно разберем типичную ошибку при работе со сложными отрицаниями, изучим законы алгебры логики применительно к коду 1С, рассмотрим правильные и лаконичные варианты написания условий, а также обсудим неочевидные подводные камни использования механизма Отказ = Истина, влияющие на производительность и корректность обмена.
Рассмотрим реальный пример. Перед нами стоит задача: выгружать из базы-источника только тех контрагентов, которые одновременно удовлетворяют двум критериям:
Источник.ВидКонтрагента = Перечисления.ВидыКонтрагентов.Покупатель).Источник.ФормаСобственности = Перечисления.ФормыСобственности.ЮридическоеЛицо).Для всех остальных контрагентов выгрузка должна быть отменена. Первоначально разработчик написал в обработчике ПКО следующий код:
Если НЕ Источник.ВидКонтрагента = Перечисления.ВидыКонтрагентов.Покупатель И НЕ Источник.ФормаСобственности = Перечисления.ФормыСобственности.ЮридическоеЛицо Тогда
Отказ = Истина;
КонецЕсли;
На первый взгляд код кажется логичным: «Если не покупатель и не юрлицо — отказываем в выгрузке». Однако при запуске обмена система начинает выгружать абсолютно всех контрагентов, полностью игнорируя это условие. Почему так происходит? Давайте проанализируем ситуацию.
Для того чтобы понять, почему первоначальный код работал некорректно, обратимся к математической логике, а именно к законам Де Моргана. Нам нужно отсечь (установить Отказ = Истина) все объекты, которые НЕ соответствуют целевой группе.
Пусть:
Нам подходят только те контрагенты, у которых одновременно выполняются оба условия: А И B. Следовательно, отказ должен срабатывать во всех остальных случаях, то есть когда истинно отрицание этого выражения: НЕ (А И B).
Согласно закону Де Моргана, отрицание конъюнкции (операции «И») эквивалентно дизъюнкции (операции «ИЛИ») отрицаний:
НЕ (А И B) = НЕ А ИЛИ НЕ B
Посмотрим, что написал разработчик в исходном коде: НЕ А И НЕ B. В булевой алгебре это выражение эквивалентно отрицанию дизъюнкции: НЕ (А ИЛИ B).
Давайте разберем, как это работало на практике для разных контрагентов:
НЕ А = Истина.НЕ B = Ложь.Истина И Ложь. Результат вычисления — Ложь.Если не выполняется, программа проходит мимо, и объект выгружается, хотя он является поставщиком!НЕ А = Ложь.НЕ B = Истина.Ложь И Истина. Результат вычисления — Ложь.Если снова не выполняется, и объект выгружается, хотя он не является юрлицом!Таким образом, отказ срабатывал только тогда, когда контрагент одновременно не являлся ни покупателем, ни юридическим лицом (например, ИП-Поставщик). Во всех остальных случаях код пропускал объекты на выгрузку.
Рассмотрим несколько вариантов решения этой задачи, начиная от простых и заканчивая наиболее профессиональными и изящными с точки зрения стандартов разработки 1С.
Этот вариант интуитивно понятен и именно к нему в итоге пришел автор темы на форуме:
Если Источник.ВидКонтрагента = Перечисления.ВидыКонтрагентов.Покупатель
И Источник.ФормаСобственности = Перечисления.ФормыСобственности.ЮридическоеЛицо Тогда
Отказ = Ложь; // Если удовлетворяет обоим условиям — выгружаем
Иначе
Отказ = Истина; // В противном случае — отказываем
КонецЕсли;
Данный код абсолютно работоспособен, однако он является избыточным. В среде разработчиков 1С принято избегать лишних конструкций Если ... Тогда ... Иначе, если результат можно присвоить напрямую булевой переменной.
Мы можем использовать закон Де Моргана напрямую, применив группировку условий с помощью скобок. Это самый надежный способ избежать путаницы с приоритетами логических операций:
Отказ = НЕ (Источник.ВидКонтрагента = Перечисления.ВидыКонтрагентов.Покупатель
И Источник.ФормаСобственности = Перечисления.ФормыСобственности.ЮридическоеЛицо);
Этот код читается очень легко: «Отказ равен Истине, если НЕ выполняется условие, что контрагент является покупателем и одновременно юридическим лицом».
Если мы раскроем скобки по правилам алгебры логики, то получим следующее выражение, использующее операторы неравенства <>:
Отказ = (Источник.ВидКонтрагента <> Перечисления.ВидыКонтрагентов.Покупатель)
ИЛИ (Источник.ФормаСобственности <> Перечисления.ФормыСобственности.ЮридическоеЛицо);
Здесь отказ сработает, если контрагент не является покупателем ИЛИ если он не является юридическим лицом. Это математически точное и лаконичное решение задачи.
Настройка логических условий в обработчике — это лишь половина дела. При работе с Конвертацией данных 2.0 крайне важно понимать, как именно платформа обрабатывает директиву Отказ = Истина под капотом. Рассмотрим два критически важных нюанса.
Даже если вы напишете идеальное и безошибочное условие отбора в ПКО контрагентов, некоторые «нежелательные» контрагенты все равно могут быть выгружены в файл обмена!
Это происходит в тех случаях, когда на отсекаемого контрагента ссылается другой выгружаемый объект — например, документ «Реализация товаров и услуг» или договор контрагента. Если в ПКО для контрагентов включен режим выгрузки по умолчанию («Выгружать при необходимости»), КД 2.0 проигнорирует ваш отказ в ПКО, чтобы не нарушать ссылочную целостность выгружаемого документа. Платформа создаст объект-ссылку в файле обмена, заполнив его ключевыми полями (например, УИД, Код или ИНН/КПП).
Как решить эту проблему?
Перед выгрузкой свойства (ПКС) у ссылающихся объектов (например, в ПКС «Контрагент» у документа продажи) и принудительно очищать или заменять ссылку, либо отменять выгрузку самого документа.Фильтрация через Отказ = Истина в обработчике ПКО происходит в оперативной памяти на этапе обхода выборки данных. Проанализируем следующую ситуацию:
Допустим, в вашей базе-источнике содержится 500 000 контрагентов, а нужных вам покупателей-юрлиц — всего 5 000. Если вы используете стандартную выгрузку по правилам, КД 2.0 сделает запрос ко всей таблице контрагентов, прочитает все 500 000 записей, для каждой из них инициализирует интерпретатор кода 1С, выполнит ваше условие в обработчике и в 495 000 случаях установит отказ.
Это приведет к колоссальным потерям процессорного времени и перерасходу оперативной памяти на сервере.
Как оптимизировать этот процесс?
Для создания надежных и быстрых правил обмена в КД 2.0 всегда соблюдайте следующие правила:
Если ... Иначе.