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

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

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

Анализ ошибки: почему значение перезаписывается

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


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

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

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

Решение 1: Получение всех данных в основном запросе печатной формы (Рекомендуемое)

Оптимальный способ решения задачи — исключить любые дополнительные запросы из цикла и получать необходимые данные об упаковках и коэффициентах напрямую во втором запросе пакета печатной формы.

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


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ТаблицаТовары.НомерСтроки КАК НомерСтроки,
    ТаблицаТовары.Номенклатура КАК Номенклатура,
    ТаблицаТовары.Номенклатура.Представление КАК Товар,
    ТаблицаТовары.Номенклатура.НаименованиеПолное КАК НоменклатураПредставление,
    ПРЕДСТАВЛЕНИЕ(ТаблицаТовары.Характеристика) КАК ХарактеристикаПредставление,
    ПРЕДСТАВЛЕНИЕ(ТаблицаТовары.Номенклатура.ЕдиницаИзмерения) КАК ПредставлениеБазовойЕдиницыИзмерения,
    ВЫБОР
        КОГДА ТаблицаТовары.Упаковка <> ЗНАЧЕНИЕ(Справочник.УпаковкиНоменклатуры.ПустаяСсылка)
            ТОГДА ПРЕДСТАВЛЕНИЕ(ТаблицаТовары.Упаковка.ЕдиницаИзмерения)
        ИНАЧЕ ПРЕДСТАВЛЕНИЕ(ТаблицаТовары.Номенклатура.ЕдиницаИзмерения)
    КОНЕЦ КАК ЕдиницаИзмерения,
    ТаблицаТовары.Количество КАК Количество,
    ТаблицаТовары.КоличествоУпаковок КАК КоличествоУпаковок,
    // Получаем коэффициент выбранной упаковки или 1 для базовой единицы
    ВЫБОР
        КОГДА ТаблицаТовары.Упаковка = ЗНАЧЕНИЕ(Справочник.УпаковкиНоменклатуры.ПустаяСсылка)
            ТОГДА 1
        ИНАЧЕ ЕСТЬNULL(ТаблицаТовары.Упаковка.Коэффициент, 1)
    КОНЕЦ КАК КоличествоУпак,
    ТаблицаТовары.Сумма КАК Сумма,
    ТаблицаТовары.Ссылка КАК Ссылка
ИЗ
    Документ.ЗаказПокупателя.Товары КАК ТаблицаТовары
ГДЕ
    ТаблицаТовары.Ссылка В(&МассивОбъектов)
    И ТаблицаТовары.Ссылка.Проведен
УПОРЯДОЧИТЬ ПО
    Ссылка,
    НомерСтроки
ИТОГИ ПО
    Ссылка

Теперь разберем, как упрощается цикл вывода строк в табличный документ:


Пока ВыборкаПоСтрокамТЧ.Следующий() Цикл
    Если НЕ ЗначениеЗаполнено(ВыборкаПоСтрокамТЧ.Номенклатура) Тогда
        Продолжить;
    КонецЕсли;

    ОбластьСтрокаНомера.Параметры.Заполнить(ВыборкаПоСтрокамТЧ);
    ТабличныйДокумент.Вывести(ОбластьСтрокаНомера);

    // Автоматическое заполнение всех параметров строки, включая КоличествоУпак
    ОбластьСтрокаДанных.Параметры.Заполнить(ВыборкаПоСтрокамТЧ);
    ОбластьСтрокаДанных.Параметры.Товар = ФормированиеПечатныхФормСервер.ПолучитьПредставлениеНоменклатурыДляПечати(
        ВыборкаПоСтрокамТЧ.НоменклатураПредставление,
        ВыборкаПоСтрокамТЧ.ХарактеристикаПредставление
    );
    ТабличныйДокумент.Присоединить(ОбластьСтрокаДанных);

    ОбластьСтрокаСкидок.Параметры.Заполнить(ВыборкаПоСтрокамТЧ);
    ТабличныйДокумент.Присоединить(ОбластьСтрокаСкидок);

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

Преимущества данного подхода:

  1. Метод ОбластьСтрокаДанных.Параметры.Заполнить() автоматически подставляет значение поля запроса КоличествоУпак в одноименный параметр ячейки макета.
  2. Полностью отсутствуют обращения к базе данных внутри цикла, что обеспечивает максимальную скорость формирования документа при печати списков объектов через МассивОбъектов.

Решение 2: Использование предварительно выгруженной таблицы значений и поиска

Если данные невозможно получить напрямую через реквизиты строки в основном запросе (например, требуются сложные расчеты или обращение к сторонним регистрам), правильным решением будет однократное выполнение вспомогательного запроса до начала циклов печати с последующим быстрым поиском по результатам.

Разберем по шагам реализацию через поиск в таблице значений:


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

ЗапросУпаковок.УстановитьПараметр("МассивОбъектов", МассивОбъектов);
ТаблицаКоэффициентов = ЗапросУпаковок.Выполнить().Выгрузить();
// Устанавливаем индекс для быстрого поиска
ТаблицаКоэффициентов.Индексы.Добавить("Ссылка, НомерСтроки");

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

Особенности очистки параметров областей макета

Следует помнить, что если переменная области (например, ОбластьСтрокаДанных) получена из макета до входа в цикл, платформа сохраняет в ней значения параметров предыдущей итерации. Если для текущей строки параметр не был явно перезаписан или отсутствовал в выборке метода Заполнить(), в макет попадет значение из предыдущей строки.

Для исключения эффекта «фантомных» данных необходимо:

  1. Гарантировать заполнение всех параметров в каждой итерации цикла (например, передавать значения по умолчанию при отсутствии данных).
  2. Либо получать область заново на каждой итерации вызовом Макет.ПолучитьОбласть("Строка|Данные"), если макет формируется динамически с изменяющимся составом колонок.
← На главную