При разработке отчетов без использования схемы компоновки данных (СКД) начинающие разработчики часто сталкиваются с ситуацией: запрос отрабатывает без ошибок, данные в отладчике присутствуют, но сформированный табличный документ содержит пустые ячейки (или выводятся только заголовки и подразделения без числовых показателей). Разберем по шагам, почему это происходит, какие типичные ошибки допускаются в программном коде и как правильно передавать результаты запроса в параметры областей макета.
Проанализируем метод Параметры.Заполнить(). Этот метод сопоставляет имена параметров в области макета с именами свойств переданного объекта (выборки из результата запроса, структуры или строки таблицы значений). Заполнение происходит только при полном совпадении имен с учетом регистра или без него.
Посмотрим на пример ошибки. В запросе вычисляются поля с псевдонимами:
| СУММА(...) КАК ФАКТ,
| СУММА(...) КАК ВД,
| СУММА(...) КАК ВР,
| СУММА(...) КАК Сов,
| СУММА(...) КАК Пост
А в макете ячейки названы с разделением по категориям сотрудников: СпФ, СпП, СпС (для специалистов) и РФ, РП, РС (для руководителей). Команда ОснБлок.Параметры.Заполнить(Выборка) в данном случае заполнит только параметр Подразделение, так как имена числовых показателей просто не совпадают. Остальные колонки останутся пустыми.
Если параметры макета названы специфично, их необходимо присваивать явно либо переименовать поля в запросе:
// Явное присвоение параметров области из полей выборки
ОснБлок.Параметры.СпФ = Выборка.ФАКТ;
ОснБлок.Параметры.СпП = Выборка.Пост;
ОснБлок.Параметры.СпС = Выборка.Сов;
Часто в коде пытаются сопоставить категорию из строки выборки со строковым литералом или с не объявленной заранее переменной:
// Ошибка 1: переменная Специалисты не определена
Если строка.Категория = Специалисты Тогда
// Ошибка 2: поле Категория хранит Ссылку, а сравнение идет со строкой
Если строка.Категория = "Специалисты" Тогда
Поле Категория в регистрах сведений и кадровых документах типовых конфигураций (например, в 1С:ЗУП) практически всегда имеет тип ссылки на справочник (например, Справочники.КатегорииСотрудников). Сравнение ссылки со строкой всегда возвращает Ложь, из-за чего ветка заполнения параметров никогда не выполняется.
Также вызов метода тз.Найти("Специалисты", "Категория") возвращает либо строку таблицы значений, либо значение Неопределено. Проверка конструкции Если тз.Найти(...) = Истина Тогда принципиально неверна.
Разберем правильный подход: ссылки на нужные категории следует получить до начала цикла один раз, чтобы не перегружать базу лишними обращениями к данным:
// Получаем ссылки до входа в цикл обработки данных
КатегорияСпециалисты = Справочники.КатегорииСотрудников.НайтиПоНаименованию("Специалисты", Истина);
КатегорияРуководители = Справочники.КатегорииСотрудников.НайтиПоНаименованию("Руководители", Истина);
КатегорияРабочие = Справочники.КатегорииСотрудников.НайтиПоНаименованию("Вспомогательные рабочие", Истина);
Для Каждого СтрТЗ Из ТаблицаДанных Цикл
Если СтрТЗ.Категория = КатегорияСпециалисты Тогда
ОснБлок.Параметры.СпФ = СтрТЗ.Факт;
ОснБлок.Параметры.СпП = СтрТЗ.Пост;
ОснБлок.Параметры.СпС = СтрТЗ.Совм;
ИначеЕсли СтрТЗ.Категория = КатегорияРуководители Тогда
ОснБлок.Параметры.РФ = СтрТЗ.Факт;
ОснБлок.Параметры.РП = СтрТЗ.Пост;
ОснБлок.Параметры.РС = СтрТЗ.Совм;
КонецЕсли;
КонецЦикла;
Выясним еще одну распространенную причину: вызов ТабличныйДокумент.Вывести(ОснБлок) внутри цикла по категориям. Если по одному подразделению в запросе возвращается три строки (руководители, специалисты, рабочие), то при таком подходе в печатный документ выводятся три отдельные строки на одно подразделение. При этом в первой строке заполнены только колонки руководителей (а остальные пустые), во второй — только специалистов, в третьей — только рабочих.
Кроме того, если область ОснБлок была получена один раз до цикла, неперезаписанные параметры сохраняют значения из предыдущей итерации, что приводит к некорректному дублированию цифр между строками.
Наиболее надежный и производительный путь — перенести логику разбивки по колонкам непосредственно в текст запроса с помощью конструкции ВЫБОР. В этом случае запрос вернет ровно одну строку на каждое подразделение, а имена колонок можно сделать полностью идентичными именам параметров макета.
Рассмотрим пример построения запроса и вывода данных:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТекущиеКадровые.ТекущееПодразделение КАК Подразделение,
| // Колонки для специалистов
| СУММА(ВЫБОР
| КОГДА ДопРеквизиты.Категория = &КатегорияСпециалисты
| И Состояния.Состояние = ЗНАЧЕНИЕ(Перечисление.СостоянияСотрудника.Факт)
| ТОГДА 1
| ИНАЧЕ 0
| КОНЕЦ) КАК СпФ,
| СУММА(ВЫБОР
| КОГДА ДопРеквизиты.Категория = &КатегорияСпециалисты
| И ДопРеквизиты.ХарактерРаботы = ЗНАЧЕНИЕ(Перечисление.ХарактерРаботы.РаботающийНаПостояннойРаботе)
| ТОГДА 1
| ИНАЧЕ 0
| КОНЕЦ) КАК СпП,
| // Колонки для руководителей
| СУММА(ВЫБОР
| КОГДА ДопРеквизиты.Категория = &КатегорияРуководители
| И Состояния.Состояние = ЗНАЧЕНИЕ(Перечисление.СостоянияСотрудника.Факт)
| ТОГДА 1
| ИНАЧЕ 0
| КОНЕЦ) КАК РФ,
| СУММА(ВЫБОР
| КОГДА ДопРеквизиты.Категория = &КатегорияРуководители
| И ДопРеквизиты.ХарактерРаботы = ЗНАЧЕНИЕ(Перечисление.ХарактерРаботы.РаботающийНаПостояннойРаботе)
| ТОГДА 1
| ИНАЧЕ 0
| КОНЕЦ) КАК РП
|ИЗ
| РегистрСведений.ТекущиеКадровыеДанныеСотрудников КАК ТекущиеКадровые
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СостоянияСотрудников.СрезПоследних(&Период, ) КАК Состояния
| ПО ТекущиеКадровые.Сотрудник = Состояния.Сотрудник
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДополнительныеРеквизиты.СрезПоследних(&Период, ) КАК ДопРеквизиты
| ПО ТекущиеКадровые.Сотрудник = ДопРеквизиты.Сотрудник
|ГДЕ
| ТекущиеКадровые.ТекущееПодразделение В ИЕРАРХИИ(&ТекущееПодразделение)
|СГРУППИРОВАТЬ ПО
| ТекущиеКадровые.ТекущееПодразделение
|УПОРЯДОЧИТЬ ПО
| ТекущиеКадровые.ТекущееПодразделение.Наименование";
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("ТекущееПодразделение", ВыбранноеПодразделение);
Запрос.УстановитьПараметр("КатегорияСпециалисты", Справочники.КатегорииСотрудников.НайтиПоКоду("000000001"));
Запрос.УстановитьПараметр("КатегорияРуководители", Справочники.КатегорииСотрудников.НайтиПоКоду("000000002"));
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
ТабДок.Очистить();
ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
ТабДок.Вывести(ОбластьШапка);
ОбластьСтрока = Макет.ПолучитьОбласть("ОснБлок");
Пока Выборка.Следующий() Цикл
// Заполняем все параметры одной командой за счет совпадения имен полей и параметров
ОбластьСтрока.Параметры.Заполнить(Выборка);
ТабДок.Вывести(ОбластьСтрока);
КонецЦикла;
ТабДок.Показать();
Если запрос изменить нельзя и он возвращает данные с группировкой по подразделениям и категориям построчно, то перед выводом строки в отчет необходимо объединить данные категорий для каждого подразделения. Разберем по шагам алгоритм на базе таблицы значений:
ТЗ = РезультатЗапроса.Выгрузить().ТЗПодразделений = ТЗ.Скопировать(); ТЗПодразделений.Свернуть("Подразделение");.ТЗ.НайтиСтроки(), заполняем область макета данными по всем категориям и только после этого выводим сформированную строку в ТабличныйДокумент.Посмотрим на практическую реализацию этого метода:
ТЗДанных = РезультатЗапроса.Выгрузить();
// Получаем список уникальных подразделений
ТЗПодразделения = ТЗДанных.Скопировать();
ТЗПодразделения.Свернуть("Подразделение");
КатСпециалисты = Справочники.КатегорииСотрудников.НайтиПоКоду("000000001");
КатРуководители = Справочники.КатегорииСотрудников.НайтиПоКоду("000000002");
Для Каждого СтрПодр Из ТЗПодразделения Цикл
// Получаем область заново, чтобы сбросить значения параметров с прошлой строки
ОбластьСтрока = Макет.ПолучитьОбласть("ОснБлок");
ОбластьСтрока.Параметры.Подразделение = СтрПодр.Подразделение;
Отбор = Новый Структура("Подразделение", СтрПодр.Подразделение);
СтрокиПодразделения = ТЗДанных.НайтиСтроки(Отбор);
Для Каждого СтрокаКатегории Из СтрокиПодразделения Цикл
Если СтрокаКатегории.Категория = КатСпециалисты Тогда
ОбластьСтрока.Параметры.СпФ = СтрокаКатегории.Факт;
ОбластьСтрока.Параметры.СпП = СтрокаКатегории.Пост;
ОбластьСтрока.Параметры.СпС = СтрокаКатегории.Сов;
ИначеЕсли СтрокаКатегории.Категория = КатРуководители Тогда
ОбластьСтрока.Параметры.РФ = СтрокаКатегории.Факт;
ОбластьСтрока.Параметры.РП = СтрокаКатегории.Пост;
ОбластьСтрока.Параметры.РС = СтрокаКатегории.Сов;
КонецЕсли;
КонецЦикла;
// Выводим строку ровно один раз на каждое подразделение
ТабДок.Вывести(ОбластьСтрока);
КонецЦикла;
ТабДок.Показать();
Соблюдение этих правил гарантирует корректное сопоставление типов данных, исключает дублирование и обеспечивает правильный вывод числовых показателей в печатную форму отчета.