Как программно настроить отбор по типу документа в Универсальном отчете 1С?

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

При работе со старыми типовыми конфигурациями (например, 1С:Управление торговлей 10.3, Управление производственным предприятием 1.3 или Комплексная автоматизация 1.1) разработчики часто сталкиваются с необходимостью кастомизации отчетов. Одним из самых популярных инструментов в этих конфигурациях является Универсальный отчет, построенный на базе встроенного объекта платформы ПостроительОтчета.

Типичная задача: исключить из ведомости по денежным средствам или взаиморасчетам движения, сделанные определенным видом документа (например, исключить корректировки долга или регламентные операции). Интерактивно в режиме «1С:Предприятие» пользователь может настроить такой фильтр, но при попытке реализовать это программно при открытии отчета стандартные методы сравнения часто дают сбой. В этой статье мы подробно разберем, почему так происходит, как устроена работа СУБД с составными типами данных и как правильно решить задачу программной фильтрации.

В чем сложность работы с полем «Регистратор»?

Поле Регистратор в регистрах накопления и бухгалтерии имеет составной тип данных. Это означает, что в одной и той же колонке таблицы СУБД могут храниться ссылки на совершенно разные таблицы документов (например, на «Платежное поручение», «Расходный кассовый ордер», «Корректировка долга» и т. д.).

Когда вы пытаетесь установить программный отбор следующим образом:


УниверсальныйОтчет.ДобавитьОтбор("Регистратор", Истина, ВидСравнения.НеРавно, Документы.КорректировкаДолга.ПустаяСсылка());

Платформа транслирует этот код в SQL-запрос как проверку равенства конкретного уникального идентификатора (GUID) пустой ссылки документа типа «Корректировка долга». Проанализируем результат выполнения такого отбора:

  1. СУБД проверяет каждую запись регистра.
  2. Если документ в поле Регистратор проведен и заполнен (то есть имеет заполненную ссылку на «Корректировка долга»), его GUID не равен GUID пустой ссылки.
  3. В итоге под условие «Не равно пустой ссылке» попадают все существующие проведенные документы корректировки долга. Они успешно выводятся в отчет.
  4. Отсекаются исключительно «битые» или незаполненные ссылки этого типа, которые физически отсутствуют в базе.

Именно поэтому простая подстановка пустой ссылки типа документа в программный отбор по полю Регистратор не решает задачу фильтрации.

Анализ интерактивной «магии» составных отборов

Иногда разработчики замечают странное поведение: если в пользовательском режиме установить два отбора одновременно, то отчет начинает работать правильно:

  1. Регистратор Не Равно [Пустое поле типа Документ.КорректировкаДолга]
  2. Регистратор.ВидОперации Не Равно [Какое-то значение]

Выясним причину этой программной аномалии. Дело в том, что когда ПостроительОтчета или СУБД видит обращение к реквизиту составного поля через точку (в нашем примере это Регистратор.ВидОперации), платформа автоматически выполняет операцию LEFT JOIN (левое соединение) со всеми таблицами документов, у которых теоретически может быть данный реквизит.

Для всех документов других типов, не имеющих реквизита ВидОперации, СУБД вернет значение NULL. Из-за особенностей обработки значений NULL при сравнении в SQL-запросах, а также под влиянием первого отбора по пустой ссылке, платформа жестко сужает выборку только до одного типа документов, отсекая все лишнее.

Однако полагаться на такое поведение крайне опасно. Это нестабильный «костыль», который может перестать работать при обновлении платформы «1С:Предприятие» или при переходе с одной СУБД на другую (например, с MS SQL Server на PostgreSQL).

Правильное решение: использование функции ТИПЗНАЧЕНИЯ в запросе

Чтобы гарантированно и чисто решить проблему, мы должны явно дать понять ПостроителюОтчета, по какому именно типу данных мы хотим производить фильтрацию. Для этого необходимо модифицировать исходный запрос Универсального отчета.

Разберем этот процесс по шагам.

Шаг 1. Модификация текста запроса в модуле отчета

Найдем в модуле отчета или связанной с ним обработки процедуру инициализации настроек (обычно это УстановитьНачальныеНастройки()). В этой процедуре формируется текст запроса для УниверсальныйОтчет.ПостроительОтчета. Нам необходимо добавить в секцию ВЫБРАТЬ получение типа значения регистратора.

Добавим в текст запроса следующую конструкцию:


"ВЫБРАТЬ
|   ...
|   ТИПЗНАЧЕНИЯ(ПрефиксТаблицы.Регистратор) КАК ТипРегистратора,
|   ..."

Также важно сделать это поле доступным для наложения условий в секции построителя (в фигурных скобках {}):


"|{ГДЕ
|   ТИПЗНАЧЕНИЯ(ПрефиксТаблицы.Регистратор) КАК ТипРегистратора,
|   ...}"

Благодаря этому изменению СУБД вернет метаданные типа для каждого регистратора, а сам ПостроительОтчета получит типизированное поле для наложения условий.

Шаг 2. Программная установка отбора при открытии

Теперь, когда поле ТипРегистратора добавлено в схему запроса Универсального отчета, мы можем легко и надежно установить фильтр в модуле формы отчета (например, в процедуре ПриОткрытии()):


// Добавляем новое поле в список доступных настроек Универсального отчета
УниверсальныйОтчет.ДобавитьОтбор("ТипРегистратора");

// Получаем доступ к настройкам Построителя отчета
НастройкаОтбора = УниверсальныйОтчет.ПостроительОтчета.Отбор.ТипРегистратора;

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

Этот код абсолютно прозрачен для платформы. СУБД выполнит эффективную фильтрацию по типу метаданных, отбросив все документы типа «Корректировка долга», но сохранив при этом все остальные документы движения в ведомости.

Альтернативное решение: Использование конструкции ССЫЛКА в запросе

Если по каким-то причинам вы используете старый релиз платформы, который некорректно обрабатывает функцию ТИПЗНАЧЕНИЯ в условиях Построителя, можно применить текстовый маркер через условное выражение в запросе.

Рассмотрим пример реализации такого подхода в тексте запроса:


"ВЫБРАТЬ
|   ...
|   ВЫБОР 
|       КОГДА ПрефиксТаблицы.Регистратор ССЫЛКА Документ.КорректировкаДолга 
|           ТОГДА ""Исключить"" 
|       ИНАЧЕ ""Оставить"" 
|   КОНЕЦ КАК СтатусИсключения,
|   ..."

В секции отборов построителя описываем доступность поля:


"|{ГДЕ
|   (ВЫБОР 
|       КОГДА ПрефиксТаблицы.Регистратор ССЫЛКА Документ.КорректировкаДолга 
|           ТОГДА ""Исключить"" 
|       ИНАЧЕ ""Оставить"" 
|   КОНЕЦ) КАК СтатусИсключения}"

После этого программная установка отбора при открытии формы сводится к простому сравнению строк:


УниверсальныйОтчет.ДобавитьОтбор("СтатусИсключения");

НастройкаОтбора = УниверсальныйОтчет.ПостроительОтчета.Отбор.СтатусИсключения;
НастройкаОтбора.Использование = Истина;
НастройкаОтбора.ВидСравнения = ВидСравнения.НеРавно;
НастройкаОтбора.Значение = "Исключить";

Этот способ работает безотказно на любых версиях платформы 1С v8, хотя с точки зрения производительности СУБД конструкция ТИПЗНАЧЕНИЯ предпочтительнее.

Как эта задача решается на современных конфигурациях с СКД?

Если вы столкнулись с подобной задачей в современных конфигурациях 1С (таких как Управление торговлей 11 или ERP Управление предприятием), то там «Универсальный отчет» уже давно функционирует на базе Системы компоновки данных (СКД).

В СКД проблема работы с типами составных полей решена на уровне ядра платформы. Для любого поля составного типа (включая Регистратор) СКД автоматически генерирует вложенное системное поле Регистратор.Тип.

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

← На главную