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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке отчетов в 1С часто возникает задача вывода данных в виде структурированных списков или описей. Например, нам необходимо вывести список всех документов, хранящихся в сейфе, сгруппировав их по конкретным папкам. На первый взгляд задача кажется простой, но начинающие разработчики часто сталкиваются с техническими трудностями: документы «слипаются» в одну кучу, привязываются только к первой найденной папке, а система выдает непонятные ошибки при попытке сравнить числовые показатели.

В этой статье мы подробно разберем, почему возникают такие проблемы, проанализируем типичные ошибки при работе с выборками запроса и научимся строить красивую, структурированную опись документов с использованием механизмов итогов и правильного обхода группировок в табличном документе.

В чем заключается основная проблема?

Рассмотрим ситуацию, описанную автором темы. Изначально разработчик пытался решить задачу с помощью двух независимых запросов и вложенных циклов. Первый запрос выбирал все документы, а второй — папки. В коде это выглядело следующим образом:


Пока ВыборкаПапок.Следующий() Цикл
    // Вывод шапки папки
    ТабДок.Вывести(ОбластьШапкаТабл);
    
    Пока Выборка.Следующий() Цикл
        // Попытка вывести документы для текущей папки
        ТабДок.Вывести(ОбластьЗначенияТабл);
    КонецЦикла; 
КонецЦикла;

При таком подходе проявляются сразу две критические ошибки:

  1. Однократный обход выборки документов. Объект типа ВыборкаИзРезультатаЗапроса в 1С является последовательным однонаправленным курсором. Когда мы первый раз входим во внутренний цикл Пока Выборка.Следующий(), система успешно обходит все документы до самого конца. На второй итерации внешнего цикла по папкам указатель выборки документов уже находится в самом конце (метод Следующий() возвращает Ложь). В результате все документы привязываются к первой же папке, а для остальных папок детальные строки просто не выводятся.
  2. Отсутствие связи между выборками. Даже если бы мы сбрасывали выборку документов в начало на каждом шаге (используя метод Сбросить()), мы бы получали полный список документов для абсолютно каждой папки, поскольку внутри запросов не было настроено отбора документов по конкретной папке.

Выясним, как решить эту задачу изящно и правильно с точки зрения стандартов разработки 1С.

Оптимизируем структуру запроса с помощью конструкции ИТОГИ ПО

Вместо выполнения нескольких запросов в цикле или создания сложных вложенных конструкций, мы объединим получение папок и документов в один оптимальный запрос. Для разделения данных по папкам мы задействуем предложение ИТОГИ ПО.

Давайте посмотрим на пример правильного построения запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Папки.Ссылка КАК Папка,
    |	Документы.РегистрационныйНомер КАК РегНомер,
    |	ЕСТЬNULL(Документы.КоличествоЭкземпляров, 0) КАК КолЭкз
    |ИЗ
    |	Справочник.ПапкиВСейфе КАК Папки
    |	ЛЕВОЕ СОЕДИНЕНИЕ Документ.ВходящийДокумент КАК Документы
    |		ПО Документы.ПапкаХранения = Папки.Ссылка
    |		И Документы.Статус = &СтатусДокумента
    |ГДЕ
    |	Папки.Владелец = &Хранилище
    |УПОРЯДОЧИТЬ ПО
    |	Папка,
    |	РегНомер
    |ИТОГИ
    |	МАКСИМУМ(КолЭкз)
    |ПО
    |	Папка";

Запрос.УстановитьПараметр("Хранилище", Отчет.Хранилище);
Запрос.УстановитьПараметр("СтатусДокумента", Перечисления.СтатусыДокументов.НаХранении);

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

Реализуем правильный многоуровневый обход выборки

Теперь разберем по шагам, как правильно обойти результат нашего запроса в коде и вывести данные в макет табличного документа. Ключевой момент здесь — использование метода Выбрать() с параметром ОбходРезультатаЗапроса.ПоГруппировкам для верхнего уровня.

Рассмотрим подробный пример реализации алгоритма вывода:


// Выполняем запрос и получаем выборку по группировке "Папка"
РезультатЗапроса = Запрос.Выполнить();
ВыборкаПапка = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

// Обходим папки первого уровня
Пока ВыборкаПапка.Следующий() Цикл
    
    // Заполняем область шапки папки в макете
    ОбластьШапкаПапки.Параметры.ИмяПапки = ВыборкаПапка.Папка;
    ТабДок.Вывести(ОбластьШапкаПапки);
    
    // Получаем детальную выборку документов ДЛЯ ТЕКУЩЕЙ ПАПКИ
    ВыборкаДетали = ВыборкаПапка.Выбрать();
    
    // Обходим документы внутри текущей папки
    Пока ВыборкаДетали.Следующий() Цикл
        
        // Проверяем, есть ли документ (исключаем вывод пустой строки для пустых папок)
        Если ЗначениеЗаполнено(ВыборкаДетали.РегНомер) Тогда
            
            // Формируем представление регистрационного номера с учетом количества экземпляров
            Если ВыборкаДетали.КолЭкз > 1 Тогда 
                ОбластьСтрокаДокумента.Параметры.РегНомер = ВыборкаДетали.RegНомер + " (" + ВыборкаДетали.КолЭкз + " экз.)";
            Иначе 
                ОбластьСтрокаДокумента.Параметры.РегНомер = ВыборкаДетали.РегНомер;
            КонецЕсли;
            
            // Выводим детальную строку документа
            ТабДок.Вывести(ОбластьСтрокаДокумента);
            
        КонецЕсли;
        
    КонецЦикла;
    
КонецЦикла;

Что изменилось в этой схеме? Мы больше не используем два независимых запроса. Вместо этого мы берем подчиненную выборку ВыборкаДетали = ВыборкаПапка.Выбрать() непосредственно из текущей строки группировки ВыборкаПапка. Это обеспечивает автоматическую фильтрацию детальных записей по текущей папке и решает проблему «перемешивания» документов раз и навсегда.

Особенности работы с макетом и табличным документом

Для создания профессиональной описи документов важно не только правильно выбрать данные, но и красиво представить их пользователю. Рассмотрим полезные приемы позиционирования областей в 1С:

Посмотрим на пример кода с контролем разбиения страниц и выводом итогового количества документов по каждой папке:


ТабДок.Очистить();

ВыборкаПапка = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

Пока ВыборкаПапка.Следующий() Цикл
    
    // Создаем массив областей для проверки возможности их вывода на текущий лист
    МассивОбластей = Новый Массив;
    МассивОбластей.Добавить(ОбластьШапкаПапки);
    
    ВыборкаДетали = ВыборкаПапка.Выбрать();
    КоличествоДокументовВПапке = 0;
    
    // Предварительно считаем количество детальных строк
    Пока ВыборкаДетали.Следующий() Цикл
        Если ЗначениеЗаполнено(ВыборкаДетали.РегНомер) Тогда
            КоличествоДокументовВПапке = КоличествоДокументовВПапке + 1;
            МассивОбластей.Добавить(ОбластьСтрокаДокумента);
        КонецЕсли;
    КонецЦикла;
    
    // Добавим в массив область подвала папки с итогами
    МассивОбластей.Добавить(ОбластьПодвалПапки);
    
    // Если вся папка целиком не влезает на страницу — принудительно переносим ее на следующий лист
    Если Не ТабДок.ПроверитьВывод(МассивОбластей) Тогда
        ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
    КонецЕсли;
    
    // Теперь производим фактический вывод данных
    ОбластьШапкаПапки.Параметры.ИмяПапки = ВыборкаПапка.Папка;
    ТабДок.Вывести(ОбластьШапкаПапки);
    
    // Сбрасываем детальную выборку в начало для повторного обхода после подсчета
    ВыборкаДетали.Сбросить();
    
    Пока ВыборкаДетали.Следующий() Цикл
        Если ЗначениеЗаполнено(ВыборкаДетали.РегНомер) Тогда
            
            Если ВыборкаДетали.КолЭкз > 1 Тогда 
                ОбластьСтрокаДокумента.Параметры.РегНомер = ВыборкаДетали.РегНомер + " " + ВыборкаДетали.КолЭкз + " экз.";
            Иначе 
                ОбластьСтрокаДокумента.Параметры.РегНомер = ВыборкаДетали.РегНомер;
            КонецЕсли;
            
            ТабДок.Вывести(ОбластьСтрокаДокумента);
            
        КонецЕсли;
    КонецЦикла;
    
    // Выводим итоги по текущей папке
    ОбластьПодвалПапки.Параметры.ВсегоДокументов = "Итого документов в папке: " + КоличествоДокументовВПапке;
    ТабДок.Вывести(ОбластьПодвалПапки);
    
КонецЦикла;

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

← На главную