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

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

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

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

Анализ проблемы и ошибок в исходном коде

Рассмотрим фрагмент кода, с которым возникла проблема при заполнении области макета:


Пока ВыборкаНоменклатура.Следующий() Цикл
    ОбластьСтрока = Макет.ПолучитьОбласть("СтрокаТЧ|Наименование");
    ОбластьСтрока.Параметры.Номенклатура = ВыборкаНоменклатура.Номенклатура;

    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    |    Номенклатура.КодИгма
    |ИЗ
    |    Справочник.Номенклатура КАК Номенклатура";
    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();

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

В данном фрагменте допущены сразу три критические ошибки:

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

Правильное решение: обращение к реквизитам через точку в основном запросе

В языке запросов 1С реализован механизм разыменования ссылочных полей («через точку»). Платформа автоматически выполняет неявное левое соединение со справочником в СУБД. Поэтому все необходимые данные по номенклатуре следует получать сразу в первом основном запросе функции формирования печатной формы.

Разберем по шагам, как исправить исходный алгоритм.

Шаг 1. Добавление реквизита в текст основного запроса

Вместо создания дополнительных запросов обратимся к реквизиту КодИгма через поле номенклатуры в основном тексте выборки данных:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|    ЗаказПокупателяСводныйТовары.Маршрут КАК Маршрут,
|    ЗаказПокупателяСводныйТовары.Номенклатура КАК Номенклатура,
|    ЗаказПокупателяСводныйТовары.Номенклатура.КодИгма КАК КодИгма,
|    ЗаказПокупателяСводныйТовары.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|    ЗаказПокупателяСводныйТовары.КоличествоЗаказано КАК КоличествоЗаказано,
|    ЗаказПокупателяСводныйТовары.ХарактеристикаНоменклатуры.Наименование КАК ХарактеристикаНоменклатурыНаименование
|ИЗ
|    Документ.ЗаказПокупателяСводный.Товары КАК ЗаказПокупателяСводныйТовары
|ГДЕ
|    ЗаказПокупателяСводныйТовары.Ссылка = &Ссылка
|СГРУППИРОВАТЬ ПО
|    ЗаказПокупателяСводныйТовары.Маршрут,
|    ЗаказПокупателяСводныйТовары.Номенклатура,
|    ЗаказПокупателяСводныйТовары.Номенклатура.КодИгма,
|    ЗаказПокупателяСводныйТовары.ХарактеристикаНоменклатуры,
|    ЗаказПокупателяСводныйТовары.КоличествоЗаказано,
|    ЗаказПокупателяСводныйТовары.ХарактеристикаНоменклатуры.Наименование
|ИТОГИ ПО
|    Маршрут,
|    Номенклатура";

Запрос.УстановитьПараметр("Ссылка", Ссылка);
РезультатЗапроса = Запрос.Выполнить();

Шаг 2. Заполнение параметров и вывод области без вложенных запросов

Теперь значение КодИгма доступно напрямую в объекте ВыборкаНоменклатура. Полностью убираем лишние вложенные запросы и циклы:


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

Дополнительные рекомендации по оптимизации кода

Помимо устранения вложенного запроса, обратим внимание на сопутствующие скрытые ошибки и возможности оптимизации:

  1. Получение данных шапки (Транспорт и водитель): В коде заполнения шапки также не следует делать отдельный запрос к справочнику Справочник.ТранспортныеСредства. Данные о водителе и автомобиле должны быть связаны с документом или маршрутом и выбираться в первом запросе через соединение таблиц либо браться напрямую из реквизитов документа.
  2. Использование агрегатных функций в итогах: При построении иерархических выборок с ключевым словом ИТОГИ ПО числовые поля (например, КоличествоЗаказано) рекомендуется агрегировать функцией СУММА(), чтобы избежать избыточной группировки в секции СГРУППИРОВАТЬ ПО.
  3. Быстрый поиск колонок: Для сопоставления данных в цикле вместо вложенного перебора Для Каждого по таблице значений эффективнее использовать метод ТаблицаХарактеристик.Найти() или предварительно заполненную коллекцию Соответствие.
← На главную