При разработке отчетов в 1С часто возникает задача построить "календарь" или "табель": нам нужно вывести список всех дней за определенный период, даже если в базе данных за какие-то дни нет записей. Чтобы упростить этот процесс и сделать код более читаемым, некоторые разработчики используют СКДБилдер — специальный инструмент для программного создания схем.
Стандартный запрос SQL возвращает только те записи, которые физически существуют в таблице. Механизм Системы Компоновки Данных (СКД) имеет встроенную функцию Дополнение периода, которая умеет "дорисовывать" пустые даты. Однако при ее включении разработчики часто сталкиваются с ошибкой: Дополнение в периоде может быть выполнено только по одному полю....
Давайте разберемся, почему возникает эта ошибка, как ее исправить и какие существуют альтернативные способы получения полного календаря в отчете.
Рассмотрим ситуацию, описанную в задаче. У нас есть регистр сведений, содержащий задания сотрудникам. Поле периода в регистре обычно имеет тип Дата и содержит не только дату, но и время записи (например, 01.01.2023 09:30:00).
Когда мы настраиваем в СКД дополнение периода с периодичностью День, система пытается сгруппировать записи по дням и заполнить пропуски. Но если в запросе поле выбрано "как есть" (с временем), СКД видит множество уникальных точек на оси времени, а не четкие дни. Это вызывает конфликт, когда система не может однозначно определить границы для дополнения. В таких ситуациях бывает полезен инструмент для быстрого анализа данных, который помогает наглядно увидеть, как именно данные выбираются из базы.
Кроме того, механизм дополнения чувствителен к тому, как поле определено в настройках (ролях) и как заданы параметры границ периода.
Самый простой и эффективный способ, найденный в ходе обсуждения, — это принудительное отсечение времени у поля даты непосредственно в тексте запроса. Это делает каждую дату уникальной в пределах одних суток, что позволяет механизму дополнения СКД работать корректно.
Вместо простого выбора поля:
мЗаданияНаРаботуСписком.Период КАК Период
Необходимо использовать функцию приведения к началу периода:
НачалоПериода(мЗаданияНаРаботуСписком.Период, ДЕНЬ) КАК Период
Посмотрим на полный текст исправленного запроса, который решает проблему:
ВЫБРАТЬ
мЗаданияНаРаботуСписком.Организация КАК Организация,
мЗаданияНаРаботуСписком.Сотрудник КАК Сотрудник,
// Другие поля...
НачалоПериода(мЗаданияНаРаботуСписком.Период, ДЕНЬ) КАК Период
ИЗ
РегистрСведений.мЗаданияНаРаботуСписком КАК мЗаданияНаРаботуСписком
ГДЕ
мЗаданияНаРаботуСписком.Начало МЕЖДУ &НачалоПериода И &КонецПериода
После изменения запроса необходимо выполнить следующие действия в настройках схемы компоновки данных:
Период.Теперь СКД сможет корректно построить последовательность дней от &НачалоПериода до &КонецПериода, вставляя пустые строки там, где записей в регистре нет.
Если вы работаете в конфигурации, внедряющей Библиотеку Стандартных Подсистем (БСП), такой как "Зарплата и Управление Персоналом", "ERP" или "Комплексная Автоматизация", существует более продвинутый способ. Он позволяет сократить время разработки сложных отчетов за счет использования штатной бизнес-логики и корректной обработки производственного календаря (праздники, выходные).
Этот метод использует "виртуальные" таблицы представлений, которые подменяются на реальные данные программно перед выполнением отчета.
В тексте запроса СКД мы пишем обращение к несуществующей таблице, описывая параметры календаря:
ВЫБРАТЬ
ДАТАВРЕМЯ(1, 1, 1) КАК Период
ПОМЕСТИТЬ Представления_Периоды
ГДЕ
"НачалоИнтервала" = &НачалоПериода
И "ОкончаниеИнтервала" = &КонецПериода
И "Периодичность" = "ДЕНЬ"
И "ИспользоватьКонецПериода" = Ложь
Затем, в модуле объекта отчета в процедуре ПриКомпоновкеРезультата необходимо вызвать специальный метод общего модуля.
// Пример кода в модуле отчета
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
// Получаем настройки и параметры
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
ПараметрыДанных = Настройки.ПараметрыДанных;
// Получаем значения параметров дат
НачалоПериода = ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("НачалоПериода")).Значение;
КонецПериода = ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("КонецПериода")).Значение;
// Инициализируем запрос для замены
Запрос = Новый Запрос;
Запрос.Текст = СхемаКомпоновкиДанных.НаборыДанных.НаборДанных1.Запрос; // Берем текст из схемы
Запрос.УстановитьПараметр("НачалоПериода", НачалоПериода);
Запрос.УстановитьПараметр("КонецПериода", КонецПериода);
// Магия БСП: замена псевдо-таблиц на реальный запрос генерации дат
ЗарплатаКадрыОбщиеНаборыДанных.ЗаменитьЗапросыКПредставлениямВиртуальныхТаблиц(Запрос.Текст, Запрос);
// Далее полученный текст запроса нужно вернуть обратно в схему компоновки
// или выполнить программный вывод отчета.
КонецПроцедуры
Этот способ сложнее в реализации, так как требует программирования, но он считается более надежным для сложных промышленных отчетов, особенно когда требуется объединять две разных схемы в одном отчете СКД.
Когда мы используем дополнение периода (любым способом), мы получаем строки с датами, которых нет в регистре. В этих строках значения всех остальных полей (например, "Сотрудник", "Контрагент", числовые показатели) будут равны NULL.
Если вы попытаетесь просто вывести эти поля или произвести с ними арифметические действия, результат может быть неожиданным (пустые ячейки или ошибки вычисления). Чтобы отчет выглядел красиво, необходимо обработать NULL.
Это можно сделать двумя способами:
ЕСТЬNULL(Поле, ЗначениеЗамены).
ЕСТЬNULL(мЗаданияНаРаботуСписком.Сотрудник, "Нет сотрудника") КАК Сотрудник
ЕстьNull(Сумма, 0)
Для вывода полного списка дат в отчете СКД по регистру сведений:
НачалоПериода(..., ДЕНЬ). Это решает ошибку "Дополнение может быть выполнено только по одному полю".&НачалоПериода и &КонецПериода корректно передаются в отчет и ограничивают выборку.NULL для ресурсов, чтобы в "пустых" днях выводились нули или прочерки, а не пустота.