Как правильно выводить динамические секции и объединять ячейки в отчетах 1С 7.7?

Программист 1С v7.7 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При формировании сложных или шахматных (cross-tab) отчетов в платформе 1С 7.7 разработчики часто сталкиваются с нестандартным поведением печатных форм. Типичные проблемы включают потерю последней строки при смене группировок, смещение динамических колонок, а также неожиданные результаты расчета остатков при использовании запросов с временными группировками.

Рассмотрим по шагам основные нюансы построения подобных отчетов, разберем причины возникновения ошибок и проанализируем правильные алгоритмы работы с объектом Таблица.

Причина потери последней строки в цикле и её решение

Выясним причину, по которой при переборе таблицы значений или результата запроса теряется финальная позиция. Классический алгоритм вывода группировок строится на сравнении текущего значения номенклатуры с предыдущим. Когда цикл доходит до конца, последняя группа формируется в памяти, но условие смены номенклатуры Если Номен <> Номенклатура больше не срабатывает, так как цикл завершился.

Проанализируем пример кода с логической ошибкой:


Для К = 1 По ТЗВспом.КоличествоСтрок() Цикл
    Номенклатура = ТЗВспом.ПолучитьЗначение(К, "Номенклатура");
    Если Номен <> Номенклатура Тогда 
        // Здесь выводится итоговая секция по ПРЕДЫДУЩЕЙ номенклатуре
        Для с = 1 По ТЗ1.КоличествоСтрок() Цикл
            Товар = ТЗ1.ПолучитьЗначение(с, "Номенклатура");
            Если Товар = Номен Тогда
                Таб.ПрисоединитьСекцию("Строка|Прав");
            КонецЕсли;
        КонецЦикла;
        Номен = Номенклатура;
    КонецЕсли;
    Таб.ВывестиСекцию("Строка|Лев");
КонецЦикла;

В приведенном фрагменте для самой последней позиции из ТЗВспом итоговая секция Строка|Прав выведена не будет. Чтобы устранить эту проблему, необходимо дублировать блок вывода итоговых секций сразу после завершения цикла Для ... КонецЦикла.

Разберем исправленный алгоритм работы:


Номен = "";
Для К = 1 По ТЗВспом.КоличествоСтрок() Цикл
    Номенклатура = ТЗВспом.ПолучитьЗначение(К, "Номенклатура");
    Если (Номен <> Номенклатура) И (ПустаяСтрока(Номен) = 0) Тогда 
        // Выводим итоги для предыдущей номенклатуры
        ВывестиИтогиПоНоменклатуре(Таб, ТЗ1, Номен);
    КонецЕсли;
    
    Если Номен <> Номенклатура Тогда
        Номен = Номенклатура;
        Таб.ВывестиСекцию("Строка|Лев");
    КонецЕсли;
    
    Таб.ПрисоединитьСекцию("Строка|Сред");
КонецЦикла;

// Обязательный вывод итоговой секции для последней номенклатуры после цикла!
Если ПустаяСтрока(Номен) = 0 Тогда
    ВывестиИтогиПоНоменклатуре(Таб, ТЗ1, Номен);
КонецЕсли;

Динамическое объединение ячеек шапки отчета

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

Рассмотрим пример формирования динамической шапки с объединением колонок по филиалам:


Таб = СоздатьОбъект("Таблица");
Таб.ИсходнаяТаблица("Таблица");
Таб.Опции(0, 0, Таб.ВысотаТаблицы(), 0);

Таб.ВывестиСекцию("Шапка|Лев");
Высота = Таб.ВысотаТаблицы(); // Фиксируем номер строки шапки

Для н = 1 По СписокФилиалов.РазмерСписка() Цикл
    НачалоКолонки = Таб.ШиринаТаблицы() + 1;
    
    // Вложенный цикл вывода колонок периодов для текущего филиала
    Для к = 1 По КолПериодов Цикл
        НачДата = ТЗПериод.ПолучитьЗначение(к, "Период_С");
        КонДата = ТЗПериод.ПолучитьЗначение(к, "Период_По"); 
        Периодтек = "С " + НачДата + " по " + КонДата;
        
        Таб.ПрисоединитьСекцию("Шапка|Сред");
    КонецЦикла;
    
    КонецКолонки = Таб.ШиринаТаблицы();
    
    // Объединяем ячейки сформированного диапазона колонок
    Таб.Область(Высота, НачалоКолонки, Высота, КонецКолонки).Объединить();
КонецЦикла;

Таб.ПрисоединитьСекцию("Шапка|Прав");

Обратите внимание на ключевые шаги этого решения:

  1. Координата НачалоКолонки вычисляется как текущая ширина таблицы плюс 1 перед входом во внутренний цикл.
  2. После завершения внутреннего цикла присоединения секций считывается КонецКолонки через метод ШиринаТаблицы().
  3. Метод Область(Верх, Лево, Низ, Право).Объединить() связывает ячейки строго в пределах полученного диапазона.

Особенности расчета остатков в запросах с временными группировками

Посмотрим на распространенную ошибку при написании текстов запросов. Если в запросе 1С 7.7 одновременно используются функция КонОст() и временная группировка (например, Группировка Месяц; или Группировка День;), платформа рассчитывает остаток на конец каждого периода группировки, а не на конечную дату отчета в целом.

Рассмотрим проблемный запрос:


ТекстЗапроса = 
"//{{ЗАПРОС(Сформировать)
|Период с НачДата по КонДата;
|Номенклатура = Регистр.ОстаткиТМЦ.Номенклатура;
|Склад        = Регистр.ОстаткиТМЦ.Склад;
|Остаток      = Регистр.ОстаткиТМЦ.Количество;
|Функция КоличествоКонОст = КонОст(Остаток);
|Группировка Номенклатура без групп;
|Группировка Склад;
|Группировка Месяц; // Вызывает расчет КонОст на конец каждого месяца!
|"//}}ЗАПРОС
;

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

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

Оптимизация работы с таблицами значений

При построении сложных отчетов часто выполняется множественная выгрузка и перебор данных во вложенных циклах. Для обеспечения высокой скорости выполнения обработки придерживайтесь следующих правил:

  1. Избегайте выгрузки данных на форму: Заполнение элемента управления ТаблицаЗначений на форме работает значительно медленнее, чем обработка объекта в памяти. Если таблица на форме необходима для отладки, отключайте её отображение перед заполнением:
    
    Форма.ТЗ.Видимость(0);
    // Выполнение операций с ТЗ
    Форма.ТЗ.Видимость(1);
    
  2. Выносите операции выгрузки из внутренних циклов: Такие методы, как ТЗ.Выгрузить() или создание объектов внутри повторно исполняемых блоков кода, вызывают сильное замедление. Формируйте итоговую плоскую таблицу значений заранее, а при выводе печатной формы выполняйте простой линейный выбор данных.
← На главную