При формировании сложных или шахматных (cross-tab) отчетов в платформе 1С 7.7 разработчики часто сталкиваются с нестандартным поведением печатных форм. Типичные проблемы включают потерю последней строки при смене группировок, смещение динамических колонок, а также неожиданные результаты расчета остатков при использовании запросов с временными группировками.
Рассмотрим по шагам основные нюансы построения подобных отчетов, разберем причины возникновения ошибок и проанализируем правильные алгоритмы работы с объектом Таблица.
Выясним причину, по которой при переборе таблицы значений или результата запроса теряется финальная позиция. Классический алгоритм вывода группировок строится на сравнении текущего значения номенклатуры с предыдущим. Когда цикл доходит до конца, последняя группа формируется в памяти, но условие смены номенклатуры Если Номен <> Номенклатура больше не срабатывает, так как цикл завершился.
Проанализируем пример кода с логической ошибкой:
Для К = 1 По ТЗВспом.КоличествоСтрок() Цикл
Номенклатура = ТЗВспом.ПолучитьЗначение(К, "Номенклатура");
Если Номен <> Номенклатура Тогда
// Здесь выводится итоговая секция по ПРЕДЫДУЩЕЙ номенклатуре
Для с = 1 По ТЗ1.КоличествоСтрок() Цикл
Товар = ТЗ1.ПолучитьЗначение(с, "Номенклатура");
Если Товар = Номен Тогда
Таб.ПрисоединитьСекцию("Строка|Прав");
КонецЕсли;
КонецЦикла;
Номен = Номенклатура;
КонецЕсли;
Таб.ВывестиСекцию("Строка|Лев");
КонецЦикла;
В приведенном фрагменте для самой последней позиции из ТЗВспом итоговая секция Строка|Прав выведена не будет. Чтобы устранить эту проблему, необходимо дублировать блок вывода итоговых секций сразу после завершения цикла Для ... КонецЦикла.
Разберем исправленный алгоритм работы:
Номен = "";
Для К = 1 По ТЗВспом.КоличествоСтрок() Цикл
Номенклатура = ТЗВспом.ПолучитьЗначение(К, "Номенклатура");
Если (Номен <> Номенклатура) И (ПустаяСтрока(Номен) = 0) Тогда
// Выводим итоги для предыдущей номенклатуры
ВывестиИтогиПоНоменклатуре(Таб, ТЗ1, Номен);
КонецЕсли;
Если Номен <> Номенклатура Тогда
Номен = Номенклатура;
Таб.ВывестиСекцию("Строка|Лев");
КонецЕсли;
Таб.ПрисоединитьСекцию("Строка|Сред");
КонецЦикла;
// Обязательный вывод итоговой секции для последней номенклатуры после цикла!
Если ПустаяСтрока(Номен) = 0 Тогда
ВывестиИтогиПоНоменклатуре(Таб, ТЗ1, Номен);
КонецЕсли;
При выводе колонок в цикле через ПрисоединитьСекцию() часто требуется объединять заголовки по периодам или филиалам верхнего уровня. Попытка применить объединение ячеек прямо в процессе присоединения приводит к искажению структуры таблицы. Правильный подход — зафиксировать координаты начальной и конечной колонок, а затем применить метод Объединить() к области печатного документа.
Рассмотрим пример формирования динамической шапки с объединением колонок по филиалам:
Таб = СоздатьОбъект("Таблица");
Таб.ИсходнаяТаблица("Таблица");
Таб.Опции(0, 0, Таб.ВысотаТаблицы(), 0);
Таб.ВывестиСекцию("Шапка|Лев");
Высота = Таб.ВысотаТаблицы(); // Фиксируем номер строки шапки
Для н = 1 По СписокФилиалов.РазмерСписка() Цикл
НачалоКолонки = Таб.ШиринаТаблицы() + 1;
// Вложенный цикл вывода колонок периодов для текущего филиала
Для к = 1 По КолПериодов Цикл
НачДата = ТЗПериод.ПолучитьЗначение(к, "Период_С");
КонДата = ТЗПериод.ПолучитьЗначение(к, "Период_По");
Периодтек = "С " + НачДата + " по " + КонДата;
Таб.ПрисоединитьСекцию("Шапка|Сред");
КонецЦикла;
КонецКолонки = Таб.ШиринаТаблицы();
// Объединяем ячейки сформированного диапазона колонок
Таб.Область(Высота, НачалоКолонки, Высота, КонецКолонки).Объединить();
КонецЦикла;
Таб.ПрисоединитьСекцию("Шапка|Прав");
Обратите внимание на ключевые шаги этого решения:
НачалоКолонки вычисляется как текущая ширина таблицы плюс 1 перед входом во внутренний цикл.КонецКолонки через метод ШиринаТаблицы().Область(Верх, Лево, Низ, Право).Объединить() связывает ячейки строго в пределах полученного диапазона.Посмотрим на распространенную ошибку при написании текстов запросов. Если в запросе 1С 7.7 одновременно используются функция КонОст() и временная группировка (например, Группировка Месяц; или Группировка День;), платформа рассчитывает остаток на конец каждого периода группировки, а не на конечную дату отчета в целом.
Рассмотрим проблемный запрос:
ТекстЗапроса =
"//{{ЗАПРОС(Сформировать)
|Период с НачДата по КонДата;
|Номенклатура = Регистр.ОстаткиТМЦ.Номенклатура;
|Склад = Регистр.ОстаткиТМЦ.Склад;
|Остаток = Регистр.ОстаткиТМЦ.Количество;
|Функция КоличествоКонОст = КонОст(Остаток);
|Группировка Номенклатура без групп;
|Группировка Склад;
|Группировка Месяц; // Вызывает расчет КонОст на конец каждого месяца!
|"//}}ЗАПРОС
;
Чтобы получить корректный остаток на дату окончания отчета независимо от интервалов группировки, рекомендуется разделять получение оборотов и остатков:
Месяц на фиксированную дату КонДата, либо считывать их из регистров напрямую.При построении сложных отчетов часто выполняется множественная выгрузка и перебор данных во вложенных циклах. Для обеспечения высокой скорости выполнения обработки придерживайтесь следующих правил:
ТаблицаЗначений на форме работает значительно медленнее, чем обработка объекта в памяти. Если таблица на форме необходима для отладки, отключайте её отображение перед заполнением:
Форма.ТЗ.Видимость(0);
// Выполнение операций с ТЗ
Форма.ТЗ.Видимость(1);
ТЗ.Выгрузить() или создание объектов внутри повторно исполняемых блоков кода, вызывают сильное замедление. Формируйте итоговую плоскую таблицу значений заранее, а при выводе печатной формы выполняйте простой линейный выбор данных.