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