При работе со старыми типовыми конфигурациями (например, 1С:Управление торговлей 10.3, Управление производственным предприятием 1.3 или Комплексная автоматизация 1.1) разработчики часто сталкиваются с необходимостью кастомизации отчетов. Одним из самых популярных инструментов в этих конфигурациях является Универсальный отчет, построенный на базе встроенного объекта платформы ПостроительОтчета.
Типичная задача: исключить из ведомости по денежным средствам или взаиморасчетам движения, сделанные определенным видом документа (например, исключить корректировки долга или регламентные операции). Интерактивно в режиме «1С:Предприятие» пользователь может настроить такой фильтр, но при попытке реализовать это программно при открытии отчета стандартные методы сравнения часто дают сбой. В этой статье мы подробно разберем, почему так происходит, как устроена работа СУБД с составными типами данных и как правильно решить задачу программной фильтрации.
Поле Регистратор в регистрах накопления и бухгалтерии имеет составной тип данных. Это означает, что в одной и той же колонке таблицы СУБД могут храниться ссылки на совершенно разные таблицы документов (например, на «Платежное поручение», «Расходный кассовый ордер», «Корректировка долга» и т. д.).
Когда вы пытаетесь установить программный отбор следующим образом:
УниверсальныйОтчет.ДобавитьОтбор("Регистратор", Истина, ВидСравнения.НеРавно, Документы.КорректировкаДолга.ПустаяСсылка());
Платформа транслирует этот код в SQL-запрос как проверку равенства конкретного уникального идентификатора (GUID) пустой ссылки документа типа «Корректировка долга». Проанализируем результат выполнения такого отбора:
Регистратор проведен и заполнен (то есть имеет заполненную ссылку на «Корректировка долга»), его GUID не равен GUID пустой ссылки.Именно поэтому простая подстановка пустой ссылки типа документа в программный отбор по полю Регистратор не решает задачу фильтрации.
Иногда разработчики замечают странное поведение: если в пользовательском режиме установить два отбора одновременно, то отчет начинает работать правильно:
Регистратор Не Равно [Пустое поле типа Документ.КорректировкаДолга]Регистратор.ВидОперации Не Равно [Какое-то значение]Выясним причину этой программной аномалии. Дело в том, что когда ПостроительОтчета или СУБД видит обращение к реквизиту составного поля через точку (в нашем примере это Регистратор.ВидОперации), платформа автоматически выполняет операцию LEFT JOIN (левое соединение) со всеми таблицами документов, у которых теоретически может быть данный реквизит.
Для всех документов других типов, не имеющих реквизита ВидОперации, СУБД вернет значение NULL. Из-за особенностей обработки значений NULL при сравнении в SQL-запросах, а также под влиянием первого отбора по пустой ссылке, платформа жестко сужает выборку только до одного типа документов, отсекая все лишнее.
Однако полагаться на такое поведение крайне опасно. Это нестабильный «костыль», который может перестать работать при обновлении платформы «1С:Предприятие» или при переходе с одной СУБД на другую (например, с MS SQL Server на PostgreSQL).
Чтобы гарантированно и чисто решить проблему, мы должны явно дать понять ПостроителюОтчета, по какому именно типу данных мы хотим производить фильтрацию. Для этого необходимо модифицировать исходный запрос Универсального отчета.
Разберем этот процесс по шагам.
Найдем в модуле отчета или связанной с ним обработки процедуру инициализации настроек (обычно это УстановитьНачальныеНастройки()). В этой процедуре формируется текст запроса для УниверсальныйОтчет.ПостроительОтчета. Нам необходимо добавить в секцию ВЫБРАТЬ получение типа значения регистратора.
Добавим в текст запроса следующую конструкцию:
"ВЫБРАТЬ
| ...
| ТИПЗНАЧЕНИЯ(ПрефиксТаблицы.Регистратор) КАК ТипРегистратора,
| ..."
Также важно сделать это поле доступным для наложения условий в секции построителя (в фигурных скобках {}):
"|{ГДЕ
| ТИПЗНАЧЕНИЯ(ПрефиксТаблицы.Регистратор) КАК ТипРегистратора,
| ...}"
Благодаря этому изменению СУБД вернет метаданные типа для каждого регистратора, а сам ПостроительОтчета получит типизированное поле для наложения условий.
Теперь, когда поле ТипРегистратора добавлено в схему запроса Универсального отчета, мы можем легко и надежно установить фильтр в модуле формы отчета (например, в процедуре ПриОткрытии()):
// Добавляем новое поле в список доступных настроек Универсального отчета
УниверсальныйОтчет.ДобавитьОтбор("ТипРегистратора");
// Получаем доступ к настройкам Построителя отчета
НастройкаОтбора = УниверсальныйОтчет.ПостроительОтчета.Отбор.ТипРегистратора;
// Задаем параметры фильтрации
НастройкаОтбора.Использование = Истина;
НастройкаОтбора.ВидСравнения = ВидСравнения.НеРавно;
НастройкаОтбора.Значение = Тип("ДокументСсылка.КорректировкаДолга");
Этот код абсолютно прозрачен для платформы. СУБД выполнит эффективную фильтрацию по типу метаданных, отбросив все документы типа «Корректировка долга», но сохранив при этом все остальные документы движения в ведомости.
Если по каким-то причинам вы используете старый релиз платформы, который некорректно обрабатывает функцию ТИПЗНАЧЕНИЯ в условиях Построителя, можно применить текстовый маркер через условное выражение в запросе.
Рассмотрим пример реализации такого подхода в тексте запроса:
"ВЫБРАТЬ
| ...
| ВЫБОР
| КОГДА ПрефиксТаблицы.Регистратор ССЫЛКА Документ.КорректировкаДолга
| ТОГДА ""Исключить""
| ИНАЧЕ ""Оставить""
| КОНЕЦ КАК СтатусИсключения,
| ..."
В секции отборов построителя описываем доступность поля:
"|{ГДЕ
| (ВЫБОР
| КОГДА ПрефиксТаблицы.Регистратор ССЫЛКА Документ.КорректировкаДолга
| ТОГДА ""Исключить""
| ИНАЧЕ ""Оставить""
| КОНЕЦ) КАК СтатусИсключения}"
После этого программная установка отбора при открытии формы сводится к простому сравнению строк:
УниверсальныйОтчет.ДобавитьОтбор("СтатусИсключения");
НастройкаОтбора = УниверсальныйОтчет.ПостроительОтчета.Отбор.СтатусИсключения;
НастройкаОтбора.Использование = Истина;
НастройкаОтбора.ВидСравнения = ВидСравнения.НеРавно;
НастройкаОтбора.Значение = "Исключить";
Этот способ работает безотказно на любых версиях платформы 1С v8, хотя с точки зрения производительности СУБД конструкция ТИПЗНАЧЕНИЯ предпочтительнее.
Если вы столкнулись с подобной задачей в современных конфигурациях 1С (таких как Управление торговлей 11 или ERP Управление предприятием), то там «Универсальный отчет» уже давно функционирует на базе Системы компоновки данных (СКД).
В СКД проблема работы с типами составных полей решена на уровне ядра платформы. Для любого поля составного типа (включая Регистратор) СКД автоматически генерирует вложенное системное поле Регистратор.Тип.
Пользователю или разработчику достаточно выбрать это поле в системных настройках компоновки и указать вид сравнения Не равно, выбрав нужный тип документа прямо из списка метаданных. Никакого написания кода или изменения запросов в СКД для этого не требуется.