При работе с Системой компоновки данных (СКД) программисты часто сталкиваются с ситуациями, когда стандартных механизмов отборов или связей наборов данных недостаточно. В таких случаях возникает соблазн реализовать вывод отчета полностью программно, перехватывая каждый элемент результата и пытаясь подставить в него свои значения. Однако этот путь таит в себе множество подводных камней: от пустых колонок в итоговом документе до критического падения производительности системы. Рассмотрим подробнее, почему возникают эти проблемы и как их решить правильно.
Разберем ситуацию, представленную в коде автора. Основная логика заключается в переборе элементов результата через ПроцессорКомпоновкиДанных.Следующий(). Внутри цикла выполняется проверка на соответствие макету (например, "Макет9"), выполняется запрос для получения остатков, и результат записывается в параметр ЭлементРезультата.ЗначенияПараметров["П10"]. Несмотря на то что отладчик показывает наличие данных в параметре, в ТабличныйДокумент они не попадают. Выясним причину.
Проблема заключается в том, что элемент результата компоновки данных — это не просто строка таблицы, а сложный объект, который уже содержит в себе скомпонованные данные на основе макета. Когда мы вызываем ПроцессорВывода.ВывестиЭлемент(ЭлементРезультата), платформа берет данные, которые были подготовлены ПроцессоромКомпоновки в момент вызова метода Следующий(). Прямая модификация коллекции ЗначенияПараметров может не подхватываться процессором вывода, если макет уже был "отрисован" внутренними механизмами платформы, или если вы пытаетесь изменить параметр, который не связан напрямую с выводимой областью в данный конкретный момент итерации.
Кроме того, использование имен параметров типа "П10" крайне ненадежно. Эти имена генерируются СКД автоматически при генерации макета компоновки. Любое изменение в схеме (добавление поля, изменение группировки) приведет к тому, что "П10" превратится в "П11" или исчезнет, и ваш код перестанет работать.
Проанализируем представленный код с точки зрения быстродействия. Выполнение объекта Запрос внутри цикла обхода результата — это классическая антипаттерн-практика в 1С. Если ваш отчет выводит 500 строк номенклатуры, система выполнит 500 отдельных обращений к базе данных. Это приведет к колоссальным задержкам. Правильным подходом является получение всех необходимых данных одним запросом до начала вывода и последующая работа с подготовленной коллекцией.
Это наиболее правильный и "чистый" способ решения задачи, если стандартные связи СКД не позволяют получить нужные остатки. Вместо того чтобы вмешиваться в процесс вывода, мы подготовим данные заранее и передадим их в СКД.
Рассмотрим алгоритм реализации по шагам:
ВнешниеОстатки.ТаблицуЗначений с данными остатков.Пример программного кода для вывода с внешним набором данных:
// 1. Получаем данные одним запросом
Запрос = Новый Запрос("ВЫБРАТЬ Номенклатура, Остаток ИЗ ...");
ТаблицаОстатков = Запрос.Выполнить().Выгрузить();
// 2. Подготавливаем структуру внешних наборов
ВнешниеДанные = Новый Структура;
ВнешниеДанные.Вставить("ВнешниеОстатки", ТаблицаОстатков);
// 3. Инициализируем процессор компоновки с передачей данных
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки());
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
// Здесь передается структура ВнешниеДанные
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеДанные, ДанныеРасшифровки, Истина);
// 4. Выводим результат стандартно
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
Если логика получения остатков очень сложная и зависит от параметров каждой строки, можно воспользоваться механизмом Вычисляемых полей. Это позволит избежать ручного перебора процессора вывода и сохранит работоспособность всех отборов СКД.
Проанализируем ситуацию: нам нужно подставить значение в колонку. Для этого:
Сервер, Вызов сервера).СвободныйОстаток.МойОбщийМодуль.ПолучитьОстаток(Номенклатура, Склад, &Период).Важно: Этот метод также может быть медленным, так как функция будет вызываться для каждой строки. Используйте кэширование внутри функции или предварительную загрузку данных в привилегированный модуль.
Если вам все же необходимо модифицировать данные именно в процессе вывода (например, для сложного оформления, которое невозможно задать в СКД), убедитесь, что вы работаете с нужным типом элемента результата. Посмотрим на правильную структуру цикла:
ПроцессорВывода.НачатьВывод();
Пока Истина Цикл
ЭлементРезультата = ПроцессорКомпоновки.Следующий();
Если ЭлементРезультата = Неопределено Тогда
Прервать;
КонецЕсли;
// Проверяем, что это детальная запись или начало группировки
Если ЭлементРезультата.ТипЭлемента = ТипЭлементаРезультатаКомпоновкиДанных.НачалоГруппировкиИлиДетальныеЗаписи Тогда
// Вместо поиска по именам типа "П10", лучше искать по значению в данных
// Проверяем наличие нужного нам параметра в макете
Если ЭлементРезультата.ЗначенияПараметров.Количество() > 0 Тогда
// Логика поиска и замены значения
// ВНИМАНИЕ: Если вы меняете значение здесь,
// убедитесь, что оно привязано к полю макета.
КонецЕсли;
КонецЕсли;
ПроцессорВывода.ВывестиЭлемент(ЭлементРезультата);
КонецЦикла;
ПроцессорВывода.ЗакончитьВывод();
Чтобы данные гарантированно отобразились, иногда требуется использовать метод ПроцессорВывода.Вывести() для макета области, но это значительно усложняет код, фактически превращая его в классический построитель отчета на базе областей табличного документа, лишая вас преимуществ СКД.
Если данные в ЭлементРезультата есть, а в отчете их нет, выполните следующие действия:
Ограничение доступности.ПриКомпоновкеРезультата. Это обеспечит правильную инициализацию всех коллекций и параметров до того, как форма начнет визуализацию.Подводя итог, можно сказать: программный вывод через перебор элементов — это крайняя мера. В 99% случаев задача решается через внешние наборы данных или связи наборов внутри самой схемы СКД. Это сохраняет производительность и делает ваш код понятным для других разработчиков.