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