При работе в старых редакциях конфигураций, таких как 1С:Управление производственным предприятием 1.3 или Комплексная автоматизация 1.1, программисты часто сталкиваются с необходимостью доработки стандартного «Универсального отчета». Одна из типичных задач — ограничить данные отчета конкретным типом документа (например, только «Требованиями-накладными»). Однако простая вставка условия ССЫЛКА в текст запроса часто приводит к тому, что отчет становится абсолютно пустым, как только пользователь пытается добавить группировку или дополнительный отбор в интерфейсе. Рассмотрим, почему это происходит и как правильно решить задачу.
Проанализируем ситуацию, с которой столкнулся автор темы. В тексте запроса Универсального отчета было добавлено жесткое условие:
ГДЕ
ИсточникДанных.Регистратор ССЫЛКА Документ.ТребованиеНакладная
На первый взгляд, это логичное решение. Однако «Универсальный отчет» построен на базе механизма ПостроительОтчета. Этот механизм динамически перестраивает текст запроса в зависимости от настроек, выбранных пользователем (группировки, поля, отборы). Когда мы используем виртуальную таблицу ОстаткиИОбороты, система ожидает определенную структуру данных для корректного расчета итогов.
Главная причина «пустого отчета» заключается в том, что в виртуальной таблице ОстаткиИОбороты записи, отвечающие за начальные и конечные остатки, не имеют конкретного регистратора (поле Регистратор для них может быть неопределено или содержать пустую ссылку в контексте итоговых записей). Накладывая жесткое условие ССЫЛКА Документ.ТребованиеНакладная в секции ГДЕ, мы буквально заставляем систему выкинуть из результата все записи, которые не являются этим документом. В итоге записи с остатками исчезают, а если по выбранной номенклатуре не было движений именно этим типом документа в выбранном периоде, результат запроса будет полностью пустым.
Прежде чем исправлять код, необходимо понять, какой именно запрос отправляет в базу данных ПостроительОтчета. Текст, который мы видим в конфигураторе, — это лишь «шаблон». Реальный запрос формируется в момент выполнения.
Разберем, как получить финальный текст запроса для анализа. Для этого в процедуре формирования отчета (обычно это модуль объекта отчета или общие модули Универсального отчета) нужно поставить точку останова и выполнить в табло следующую команду:
УниверсальныйОтчет.ПостроительОтчета.ПолучитьЗапрос().Текст
Именно этот метод позволяет увидеть, куда построитель вставил ваши условия и как он преобразовал секции ИТОГИ и СГРУППИРОВАТЬ ПО. Часто после такого анализа становится очевидно, что жесткое условие ГДЕ конфликтует с вложенными запросами, которые генерирует построитель для расчета остатков.
Чтобы отчет работал корректно и не «ломался» при добавлении группировок, условия нужно выносить в специальные блоки фигурных скобок {}. Это дает понять Построителю, что данные условия являются необязательными и должны применяться гибко.
Рассмотрим правильную структуру секции отбора в запросе:
ИЗ
РегистрНакопления.ПартииТоваровНаСкладах.ОстаткиИОбороты(&ДатаНач, &ДатаКон, Регистратор, , ) КАК ИсточникДанных
{ГДЕ
ИсточникДанных.Регистратор.* КАК Регистратор,
ИсточникДанных.Номенклатура.* КАК Номенклатура}
Если мы хотим, чтобы тип регистратора был ограничен программно, но при этом отчет не пустел, у нас есть два пути:
ОстаткиИОбороты нельзя напрямую отфильтровать регистратор в параметрах самой таблицы (в отличие от таблицы Обороты).ГДЕ в основном теле запроса, мы убеждаемся, что поле Регистратор доступно в блоке {ГДЕ ...}.Выясним, как выполнить требование автора: сделать отбор по типу регистратора, не выводя сам регистратор в строки отчета. В Универсальном отчете это делается через интерфейс настроек:
При такой настройке Построитель отчета сам добавит в SQL-запрос условие проверки типа, но при этом корректно обработает итоги. Поскольку поле «Регистратор» добавлено только в отбор, но не выбрано в списке «Группировки строк», в самом отчете детальных записей по документам не будет — вы увидите только агрегированные данные по номенклатуре или складам, но только те, которые были сформированы выбранным типом документа.
Если вам нужно «намертво» зашить этот отбор в коде, чтобы пользователь не мог его снять, проанализируем пример кода, который можно вставить в процедуру УстановитьНачальныеНастройки:
// Находим поле Регистратор в доступных полях отбора
НовыйОтбор = УниверсальныйОтчет.ПостроительОтчета.Отбор.Добавить("Регистратор");
НовыйОтбор.Использование = Истина;
НовыйОтбор.ВидСравнения = ВидСравнения.ТипЗначания;
НовыйОтбор.Значение = Тип("ДокументСсылка.ТребованиеНакладная");
Важный момент: Если вы используете таблицу ОстаткиИОбороты, помните, что отбор по регистратору превращает ее, по сути, в таблицу оборотов. Начальные остатки будут показаны только те, которые «прошли» через этот фильтр, что с точки зрения бухгалтерского смысла может быть неверно. Если ваша задача — увидеть все остатки, но движения только по одному документу, вам придется пересмотреть логику запроса, используя объединение нескольких таблиц или расчет остатков отдельно от оборотов.
Подводя итоги, отметим основные правила работы с Универсальным отчетом в УПП:
ГДЕ на поля, которые могут быть неопределены в записях итогов виртуальных таблиц.УниверсальныйОтчет.ПостроительОтчета.ПолучитьЗапрос().Текст для отладки — это сэкономит часы поиска ошибки.Тип значения в настройках построителя.ГДЕ и динамически добавляемыми полями построителя.