Как вывести данные табличной части документа в разные таблицы печатной формы с помощью одного запроса и итогов

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

При разработке печатных форм в 1С часто возникает задача разделить данные одной табличной части на несколько визуальных таблиц. Типичный пример — документ ОказаниеУслуги, где в общем списке перемешаны и товары (материалы), и услуги. Чтобы печатная форма выглядела профессионально, их нужно сгруппировать. Мы разберем, как сделать это максимально эффективно — с помощью всего одного запроса и механизма обхода итогов, как того требуют стандарты разработки 1С.

Понимаем задачу и структуру данных

Представим ситуацию: у нас есть табличная часть ПереченьНоменклатуры. В ней хранятся строки, у каждой из которых есть реквизит Номенклатура, а у номенклатуры — ВидНоменклатуры (например, "Товар" и "Услуга"). Наша цель — вывести сначала заголовок "Материалы" и таблицу с ними, а затем заголовок "Услуги" и таблицу с услугами. Главное условие — использовать один запрос и обход итогов, чтобы избежать лишних обращений к базе данных и итераций в циклах.

Шаг 1. Создание запроса с итогами

Рассмотрим структуру запроса. Чтобы система поняла, что данные нужно сгруппировать, мы используем предложение ИТОГИ ПО. Давайте проанализируем пример текста запроса:


ВЫБРАТЬ
    ОказаниеУслугиПереченьНоменклатуры.Ссылка.Клиент КАК Клиент,
    ОказаниеУслугиПереченьНоменклатуры.Ссылка.Номер КАК Номер,
    ОказаниеУслугиПереченьНоменклатуры.Ссылка.Дата КАК Дата,
    ОказаниеУслугиПереченьНоменклатуры.Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры,
    ОказаниеУслугиПереченьНоменклатуры.Номенклатура КАК Номенклатура,
    ОказаниеУслугиПереченьНоменклатуры.Количество КАК Количество,
    ОказаниеУслугиПереченьНоменклатуры.Цена КАК Цена,
    ОказаниеУслугиПереченьНоменклатуры.Сумма КАК Сумма
ИЗ
    Документ.ОказаниеУслуги.ПереченьНоменклатуры КАК ОказаниеУслугиПереченьНоменклатуры
ГДЕ
    ОказаниеУслугиПереченьНоменклатуры.Ссылка В(&Ссылка)
ИТОГИ
    СУММА(Количество),
    СУММА(Сумма),
    МАКСИМУМ(Цена)
ПО
    ВидНоменклатуры

Важный нюанс: обратите внимание на секцию ИТОГИ. Для числовых полей, которые мы хотим видеть в детальных записях, мы обязательно указываем агрегатные функции. Для Количество и Сумма логично использовать СУММА(), а для Цена — МАКСИМУМ() или МИНИМУМ(). Если этого не сделать, на уровне группировки значения этих полей могут оказаться пустыми, что иногда путает начинающих разработчиков при просмотре результата в консоли запросов.

Шаг 2. Использование конструктора запроса с обработкой результата

Чтобы не писать весь код вручную и не ошибиться в синтаксисе обхода, в 1С есть отличный помощник. Разберем, как им воспользоваться:

  1. В модуле менеджера (или объекта) нажмите правую кнопку мыши.
  2. Выберите пункт "Конструктор запроса с обработкой результата".
  3. Выберите тип обработки — "Вывод в табличный документ".
  4. На вкладке "Запрос" вставьте наш текст.
  5. На вкладке "Итоги" выберите поле ВидНоменклатуры в левое окно "Группировки", а числовые поля — в правое окно "Итоги".

После нажатия кнопки "ОК" система сама создаст шаблон кода с правильной структурой циклов, которую мы сейчас детально изучим.

Шаг 3. Алгоритм двойного цикла (обход по группировкам)

Когда мы используем итоги, результат запроса перестает быть "плоской таблицей". Он становится иерархическим. Для корректного вывода нам понадобится механизм вложенных выборок. Проанализируем, как это выглядит в коде:


РезультатЗапроса = Запрос.Выполнить();

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

Пока ВыборкаВидНоменклатуры.Следующий() Цикл
    
    // Здесь мы находимся на уровне "Услуга" или "Материал"
    // Можно вывести заголовок таблицы, например: "Тип: Услуги"
    ОбластьЗаголовокГруппы.Параметры.ТекстЗаголовка = ВыборкаВидНоменклатуры.ВидНоменклатуры;
    ТабДок.Вывести(ОбластьЗаголовокГруппы);
    
    // 2. Внутри создаем вложенную выборку для детальных записей этой группы
    ВыборкаДетальныеЗаписи = ВыборкаВидНоменклатуры.Выбрать();
    
    Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
        // Здесь мы выводим конкретные строки: "Консультация", "Болт", "Гайка"
        ОбластьСтрока.Параметры.Заполнить(ВыборкаДетальныеЗаписи);
        ТабДок.Вывести(ОбластьСтрока);
    КонецЦикла;
    
    // Здесь можно вывести подвал конкретной таблицы (например, Итого по разделу)
КонецЦикла;

Выясним причину, почему это работает быстро: метод РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам) говорит системе, что первый цикл должен идти только по уникальным значениям ВидНоменклатуры. А внутренний вызов ВыборкаВидНоменклатуры.Выбрать() получает только те строки, которые относятся к текущему виду. Это и есть отсутствие "лишних итераций".

Шаг 4. Оформление в макете

Для реализации этой логики в макете печатной формы (объект Макет) стоит создать несколько областей:

Рассмотрим порядок вывода: во внешнем цикле (где ВидНоменклатуры) мы выводим ЗаголовокТаблицы. Во внутреннем цикле — многократно выводим Строку. После завершения внутреннего цикла, но еще внутри внешнего, выводим ПодвалТаблицы. Таким образом, сколько бы видов номенклатуры ни было в документе, программа сама сформирует нужное количество аккуратных таблиц.

Почему это лучше, чем два разных запроса?

Проанализируем ситуацию с точки зрения производительности. Если делать два запроса (один для услуг, другой для материалов), СУБД придется дважды сканировать одну и ту же таблицу документа на диске. В случае с одним запросом и итогами, 1С получает все данные за один раз, а разделение происходит уже в оперативной памяти сервера приложений. Это критически важно для высоконагруженных систем.

Совет: Если вы видите в консоли запросов пустые поля в строках итогов — не пугайтесь. Это особенность отображения иерархии. В программном коде, при обращении к ВыборкаДетальныеЗаписи, все данные будут на своих местах, если они правильно описаны в основной секции ВЫБРАТЬ.

← На главную