Как сравнить Тип и ОписаниеТипов в запросе 1С при проверке субконто?

Программист 1С v8.3 (Управляемые формы) 1C:Бухгалтерия Бухгалтерский учет IT и автоматизация бизнеса
← На главную

Часто перед разработчиками встает задача контроля корректности данных. Разберем интересную ситуацию: нам необходимо запросом вытянуть записи из регистра бухгалтерии, где фактический тип значения, установленного в качестве субконто, не соответствует допустимым типам, указанным для данного вида субконто в Плане видов характеристик (ПВХ).

На первый взгляд, задача кажется тривиальной. Мы берем виртуальную или физическую таблицу значений субконто регистра бухгалтерии, получаем ВидСубконто и его ТипЗначения (которое содержит ОписаниеТипов). Затем берем фактическое Значение субконто, определяем его тип с помощью функции ТИПЗНАЧЕНИЯ() и пытаемся сравнить их прямо в тексте запроса. Проанализируем, почему этот прямой подход обречен на провал и как правильно решить эту задачу.

Ограничения языка запросов 1С

Давайте выясним причину, почему мы не можем написать условие вида ГДЕ ТИПЗНАЧЕНИЯ(ХозрасчетныйСубконто.Значение) В (ХозрасчетныйСубконто.Вид.ТипЗначения). Здесь мы сталкиваемся с фундаментальными платформенными ограничениями:

  1. Особенности хранения Описания типов: Поле ТипЗначения в таблицах ПВХ хранится в базе данных в виде бинарных данных (сериализованный внутренний объект 1С). Язык запросов воспринимает это поле как единый атомарный объект. В SQL-движке 1С нет встроенных функций для "разворачивания" этого объекта в список доступных типов прямо "на лету" внутри запроса. Правая часть выражения не является набором строк или списком значений для оператора В (IN).
  2. Запрет на сравнение: Обратимся к официальной документации 1С. В правилах сравнения значений четко сказано: не допускается сравнение полей неограниченной длины (в том числе хранилищ значений и поля ТИПЗНАЧЕНИЯ из таблицы планов видов характеристик). Попытка прямого сравнения вызовет ошибку.
  3. Разные сущности: Функция ТИПЗНАЧЕНИЯ(Субконто) возвращает объект типа Тип (метаданные). А в ПВХ хранится ОписаниеТипов — это сложный объект-контейнер, включающий в себя массив типов, квалификаторы строк, чисел и дат. Сравнивать их напрямую некорректно.

Мы также не можем использовать конструкцию приведения типа ВЫРАЗИТЬ(Выражение КАК ТипЗначения) для динамической проверки. В языке запросов 1С тип в этой конструкции всегда должен быть задан жестко (литералом), мы не можем подставить туда поле из выборки. Для решения смежных задач с ограничениями оператора ВЫРАЗИТЬ и несовместимыми типами подойдёт методика сравнения строки с представлением ссылки в запросе.

Правильное решение: использование Временных Таблиц

Рассмотрим подробнее единственно верный и системный подход. Раз мы не можем "распаковать" ОписаниеТипов внутри запроса, мы должны сделать это программно до выполнения запроса, подготовить данные и передать их в запрос.

Разберем алгоритм по шагам:

  1. Перед выполнением запроса мы обходим все необходимые элементы ВидыСубконто (например, запросом или выборкой по справочнику/ПВХ).
  2. Для каждого вида извлекаем массив допустимых типов с помощью метода ВидСубконто.ТипЗначения.Типы().
  3. Формируем программную ТаблицаЗначений. В ней будет две колонки: ВидСубконто (ссылка на элемент ПВХ) и ДопустимыйТип (сам тип или пустая ссылка данного типа).
  4. Передаем эту таблицу значений в параметры нашего основного запроса проверки и помещаем её во Временную Таблицу (ВТ).
  5. В основном запросе выполняем ЛЕВОЕ СОЕДИНЕНИЕ таблицы регистра бухгалтерии с нашей ВТ по двум условиям: равенству вида субконто и совпадению типа фактического значения с полем ДопустимыйТип.
  6. Если в результате соединения мы получаем NULL со стороны ВТ, значит фактический тип значения субконто не входит в список разрешенных.

Посмотрим на примерный код программной подготовки данных:


// Шаг 1-3. Подготовка таблицы разрешенных типов
ТаблицаТипов = Новый ТаблицаЗначений;
ТаблицаТипов.Колонки.Добавить("ВидСубконто");
ТаблицаТипов.Колонки.Добавить("ДопустимыйТип");

// Получаем виды субконто, которые нужно проверить
ЗапросВиды = Новый Запрос("ВЫБРАТЬ Ссылка, ТипЗначения ИЗ ПланВидовХарактеристик.ВидыСубконтоХозрасчетные");
ВыборкаВидов = ЗапросВиды.Выполнить().Выбрать();

Пока ВыборкаВидов.Следующий() Цикл
    // Разворачиваем ОписаниеТипов в массив типов
    МассивТипов = ВыборкаВидов.ТипЗначения.Типы();
    Для Каждого ТекТип Из МассивТипов Цикл
        НоваяСтрока = ТаблицаТипов.Добавить();
        НоваяСтрока.ВидСубконто = ВыборкаВидов.Ссылка;
        // Записываем сам тип для дальнейшего сравнения в запросе через ТИПЗНАЧЕНИЯ()
        НоваяСтрока.ДопустимыйТип = ТекТип; 
    КонецЦикла;
КонецЦикла;

// Шаг 4-6. Выполнение основного запроса проверки
ЗапросПроверки = Новый Запрос;
ЗапросПроверки.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
ЗапросПроверки.УстановитьПараметр("ТаблицаДопустимыхТипов", ТаблицаТипов);

ЗапросПроверки.Текст = 
"ВЫБРАТЬ
|    ТабТипов.ВидСубконто КАК ВидСубконто,
|    ТабТипов.ДопустимыйТип КАК ДопустимыйТип
|ПОМЕСТИТЬ ВТ_РазрешенныеТипы
|ИЗ
|    &ТаблицаДопустимыхТипов КАК ТабТипов
|;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|    ХозрасчетныйСубконто.Регистратор КАК Документ,
|    ХозрасчетныйСубконто.НомерСтроки КАК НомерСтроки,
|    ХозрасчетныйСубконто.Вид КАК ВидСубконто,
|    ХозрасчетныйСубконто.Значение КАК ОшибочноеЗначение
|ИЗ
|    РегистрБухгалтерии.Хозрасчетный.Субконто КАК ХозрасчетныйСубконто
|        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_РазрешенныеТипы КАК ВТ_РазрешенныеТипы
|        ПО ХозрасчетныйСубконто.Вид = ВТ_РазрешенныеТипы.ВидСубконто
|            И (ТИПЗНАЧЕНИЯ(ХозрасчетныйСубконто.Значение) = ВТ_РазрешенныеТипы.ДопустимыйТип)
|ГДЕ
|    ВТ_РазрешенныеТипы.ВидСубконто ЕСТЬ NULL";

Результат = ЗапросПроверки.Выполнить().Выгрузить();

Этот подход гарантирует универсальность обработки. Если в конфигурацию будут добавлены новые виды субконто, наш код автоматически подхватит их при следующем запуске без необходимости переписывать текст запроса. Это оптимальное решение, которое элегантно обходит архитектурные ограничения платформы.

← На главную