Почему программный вывод отчета СКД в ДеревоЗначений пустой и как это исправить?

Программист 1С v8.3 (Управляемые формы) 1С:Зарплата и Управление Персоналом IT и автоматизация бизнеса
← На главную

При работе с Системой Компоновки Данных (СКД) в 1С:Предприятии 8 мы часто сталкиваемся с необходимостью программно получать данные отчетов для дальнейшей обработки, например, для формирования XML-файлов или передачи в другие системы. Одна из распространенных задач — вывод результата компоновки в ДеревоЗначений. Однако иногда, несмотря на, казалось бы, корректный код, полученное дерево остается пустым. Давайте разберем, почему это происходит, и как мы можем исправить эту ситуацию, основываясь на опыте коллег и лучших практиках.

Разбираем исходную проблему: почему ДеревоЗначений остается пустым?

Мы видим, что автор столкнулся с распространенной проблемой: при попытке программно сформировать отчет СКД "Анализ начислений и удержаний" и вывести его результат в ДеревоЗначений, последнее оставалось пустым. Отчет при этом, формируемый напрямую из пользовательского интерфейса, работал корректно.

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


СхемаКомпоновкиДанных = Отчеты.АнализНачисленийИУдержаний.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
КомпоновщикНастроекДанныхОтчета = Новый КомпоновщикНастроекКомпоновкиДанных;
КомпоновщикНастроекДанныхОтчета.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных (СхемаКомпоновкиДанных));
КомпоновщикНастроекДанныхОтчета.ЗагрузитьНастройки(СхемаКомпоновкиДанных.ВариантыНастроек.Найти("РасчетныйЛисток").Настройки);

ПараметрыДанных = КомпоновщикНастроекДанныхОтчета.Настройки.ПараметрыДанных.Элементы;
ЭлементПериода = ПараметрыДанных.Найти("Период");
ЭлементПериода.Использование = Истина;
ЭлементПериода.Значение = Период;

ОтборКомпоновщика = КомпоновщикНастроекДанныхОтчета.Настройки.Отбор;
Для Каждого ЭлементОтбора ИЗ ОтборКомпоновщика.Элементы Цикл
    Если Строка(ЭлементОтбора.ЛевоеЗначение) = "Организация" Тогда
        ЭлементОтбора.Использование = Истина;
        ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
        ЭлементОтбора.ПравоеЗначение = Организация;
    КонецЕсли;
    Если ЗначениеЗаполнено(Подразделение) Тогда
        Если Строка(ЭлементОтбора.ЛевоеЗначение) = "ПодразделениеГоловногоСотрудникаНаКонецПериода" Тогда
            ЭлементОтбора.Использование = Истина;
            ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
            ЭлементОтбора.ПравоеЗначение = Подразделение;
        КонецЕсли;
    ИначеЕсли Строка(ЭлементОтбора.ЛевоеЗначение) = "ПодразделениеГоловногоСотрудникаНаКонецПериода" Тогда
        ОтборКомпоновщика.Элементы.Удалить(ЭлементОтбора);
    КонецЕсли;
    Если ЗначениеЗаполнено(Должность) Тогда
        Если Строка(ЭлементОтбора.ЛевоеЗначение) = "Должность" Тогда
            ЭлементОтбора.Использование = Истина;
            ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
            ЭлементОтбора.ПравоеЗначение = Должность;
        КонецЕсли;
    ИначеЕсли Строка(ЭлементОтбора.ЛевоеЗначение) = "Должность" Тогда
        ОтборКомпоновщика.Элементы.Удалить(ЭлементОтбора);
    КонецЕсли;
    Если Строка(ЭлементОтбора.ЛевоеЗначение) = "ФизическоеЛицо" Тогда
        ЭлементОтбора.Использование = Истина;
        ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
        ЭлементОтбора.ПравоеЗначение = Сотрудник;
    КонецЕсли;
КонецЦикла;

ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроекДанныхОтчета.Настройки, ДанныеРасшифровки, , Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));

Если КомпоновщикНастроекДанныхОтчета.Настройки.ДополнительныеСвойства.Свойство("ДанныеДокумента") Тогда
    НаборыВнешнихДанных = КомпоновщикНастроекДанныхОтчета.Настройки.ДополнительныеСвойства.ДанныеДокумента;
Иначе
    НаборыВнешнихДанных = ЗарплатаКадрыОтчеты.НаборыВнешнихДанныхАнализНачисленийИУдержаний;
КонецЕсли;

ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, НаборыВнешнихДанных, ДанныеРасшифровки);

ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ДеревоДанных = Новый ДеревоЗначений;
ПроцессорВывода.УстановитьОбъект(ДеревоДанных);
ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);

Код, на первый взгляд, выглядит довольно стандартно: мы получаем схему СКД, загружаем нужный вариант настроек, устанавливаем параметры и отборы, затем компонуем макет, инициализируем процессор компоновки и выводим результат в ДеревоЗначений. Это стандартный алгоритм программного формирования отчета СКД. Мы выполняем следующие шаги:

  1. Получение схемы компоновки данных: Мы загружаем основную схему отчета АнализНачисленийИУдержаний.
  2. Инициализация компоновщика настроек: Создаем и инициализируем КомпоновщикНастроекКомпоновкиДанных.
  3. Загрузка варианта настроек: Мы загружаем конкретный вариант настроек "РасчетныйЛисток".
  4. Установка параметров и отборов: Динамически устанавливаем значения параметров Период, Организация, Подразделение, Должность, ФизическоеЛицо и активируем их использование.
  5. Компоновка макета: Выполняем компоновку макета, указывая тип генератора для коллекций значений.
  6. Определение внешних наборов данных: Получаем внешние наборы данных, которые могут использоваться отчетом.
  7. Инициализация процессора компоновки данных: Создаем и инициализируем ПроцессорКомпоновкиДанных.
  8. Вывод результата: Используем ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений для заполнения ДереваЗначений.

Однако, несмотря на это, данные отсутствуют, что указывает на некоторые неочевидные, но критически важные моменты при работе с СКД в типовых конфигурациях, особенно таких как 1С:Зарплата и Управление Персоналом (ЗУП).

Решение проблемы: ключевые шаги по инициализации СКД для ЗУП

Обратимся к экспертному совету из темы, который оказался решающим. Проблема пустого ДереваЗначений часто связана с тем, что типовые конфигурации 1С используют дополнительные механизмы для подготовки данных СКД, которые не запускаются автоматически при программном формировании отчета.

  1. Инициализация общих запросов для НаборовДанных.

    Конфигурации, особенно ЗУП, часто используют сложные механизмы для динамического формирования запросов, добавления виртуальных таблиц и других настроек, которые скрыты от разработчика и выполняются специальными служебными функциями. Эти функции обычно вызываются перед началом работы с СКД, чтобы "наполнить" ее необходимым контекстом.

    Для отчетов в ЗУП, таких как "Анализ начислений и удержаний", критически важным шагом является вызов метода ЗарплатаКадрыОбщиеНаборыДанных.ЗаполнитьОбщиеЗапросы(СхемаКомпоновкиДанных.НаборыДанных). Этот метод отвечает за инициализацию специфических для ЗУП общих запросов и представлений, которые используются в наборах данных СКД. Без этого вызова многие наборы данных останутся без фактического источника данных, и отчет будет пустым.

    Где добавить этот вызов:

    
    // ...
    СхемаКомпоновкиДанных = Отчеты.АнализНачисленийИУдержаний.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
    // ********************************************************************************
    // ВАЖНО: Добавляем инициализацию общих запросов для наборов данных ЗУП
    ЗарплатаКадрыОбщиеНаборыДанных.ЗаполнитьОбщиеЗапросы(СхемаКомпоновкиДанных.НаборыДанных); 
    // ********************************************************************************
    КомпоновщикНастроекДанныхОтчета = Новый КомпоновщикНастроекКомпоновкиДанных;
    // ...
    
  2. Разрешение использования внешних функций в вычисляемых полях.

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

    Четвертый параметр метода ПроцессорКомпоновкиДанных.Инициализировать() отвечает за разрешение использования внешних функций в вычисляемых полях. Его необходимо установить в значение Истина.

    Как правильно инициализировать процессор:

    
    // ...
    ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
    // ********************************************************************************
    // ВАЖНО: Разрешаем использование внешних функций в вычисляемых полях (четвертый параметр)
    ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, НаборыВнешнихДанных, ДанныеРасшифровки, Истина); 
    // ********************************************************************************
    // ...
    

    Передавая значение Истина в качестве четвертого параметра, мы даем разрешение процессору компоновки данных выполнять внешние функции, которые могут быть задействованы в вычисляемых полях отчета СКД. Это особенно важно для сложных типовых отчетов, где часто используются такие функции для форматирования, вычислений или получения дополнительных сведений.

Применяем эти два исправления к коду автора, чтобы получить полностью работоспособный вариант:


СхемаКомпоновкиДанных = Отчеты.АнализНачисленийИУдержаний.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
// *** ИСПРАВЛЕНИЕ 1: Инициализация общих запросов для ЗУП ***
ЗарплатаКадрыОбщиеНаборыДанных.ЗаполнитьОбщиеЗапросы(СхемаКомпоновкиДанных.НаборыДанных);

КомпоновщикНастроекДанныхОтчета = Новый КомпоновщикНастроекКомпоновкиДанных;
КомпоновщикНастроекДанныхОтчета.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных (СхемаКомпоновкиДанных));
КомпоновщикНастроекДанныхОтчета.ЗагрузитьНастройки(СхемаКомпоновкиДанных.ВариантыНастроек.Найти("РасчетныйЛисток").Настройки);

ПараметрыДанных = КомпоновщикНастроекДанныхОтчета.Настройки.ПараметрыДанных.Элементы;
ЭлементПериода = ПараметрыДанных.Найти("Период");
ЭлементПериода.Использование = Истина;
ЭлементПериода.Значение = Период;

ОтборКомпоновщика = КомпоновщикНастроекДанныхОтчета.Настройки.Отбор;
Для Каждого ЭлементОтбора ИЗ ОтборКомпоновщика.Элементы Цикл
    Если Строка(ЭлементОтбора.ЛевоеЗначение) = "Организация" Тогда
        ЭлементОтбора.Использование = Истина;
        ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
        ЭлементОтбора.ПравоеЗначение = Организация;
    КонецЕсли;
    Если ЗначениеЗаполнено(Подразделение) Тогда
        Если Строка(ЭлементОтбора.ЛевоеЗначение) = "ПодразделениеГоловногоСотрудникаНаКонецПериода" Тогда
            ЭлементОтбора.Использование = Истина;
            ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
            ЭлементОтбора.ПравоеЗначение = Подразделение;
        КонецЕсли;
    ИначеЕсли Строка(ЭлементОтбора.ЛевоеЗначение) = "ПодразделениеГоловногоСотрудникаНаКонецПериода" Тогда
        ОтборКомпоновщика.Элементы.Удалить(ЭлементОтбора);
    КонецЕсли;
    Если ЗначениеЗаполнено(Должность) Тогда
        Если Строка(ЭлементОтбора.ЛевоеЗначение) = "Должность" Тогда
            ЭлементОтбора.Использование = Истина;
            ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
            ЭлементОтбора.ПравоеЗначение = Должность;
        КонецЕсли;
    ИначеЕсли Строка(ЭлементОтбора.ЛевоеЗначение) = "Должность" Тогда
        ОтборКомпоновщика.Элементы.Удалить(ЭлементОтбора);
    КонецЕсли;
    Если Строка(ЭлементОтбора.ЛевоеЗначение) = "ФизическоеЛицо" Тогда
        ЭлементОтбора.Использование = Истина;
        ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
        ЭлементОтбора.ПравоеЗначение = Сотрудник;
    КонецЕсли;
КонецЦикла;

ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроекДанныхОтчета.Настройки, ДанныеРасшифровки, , Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));

Если КомпоновщикНастроекДанныхОтчета.Настройки.ДополнительныеСвойства.Свойство("ДанныеДокумента") Тогда
    НаборыВнешнихДанных = КомпоновщикНастроекДанныхОтчета.Настройки.ДополнительныеСвойства.ДанныеДокумента;
Иначе
    НаборыВнешнихДанных = ЗарплатаКадрыОтчеты.НаборыВнешнихДанныхАнализНачисленийИУдержаний;
КонецЕсли;

ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
// *** ИСПРАВЛЕНИЕ 2: Разрешение внешних функций ***
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, НаборыВнешнихДанных, ДанныеРасшифровки, Истина);

ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ДеревоДанных = Новый ДеревоЗначений;
ПроцессорВывода.УстановитьОбъект(ДеревоДанных);
ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);

Отладка и верификация: Проверка вывода в ТабличныйДокумент

Прежде чем погружаться в сложные причины отсутствия данных, всегда полезно убедиться, что отчет в принципе формирует какие-либо данные с заданными настройками. Отладочный вывод в ТабличныйДокумент — это отличный способ проверить, есть ли данные, и как они выглядят, прежде чем пытаться преобразовать их в ДеревоЗначений.

Шаги для временного вывода результата отчета в ТабличныйДокумент:

  1. Замените тип генератора макета.

    В строке, где мы вызываем КомпоновщикМакета.Выполнить(), измените последний параметр с Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений") на Тип("ГенераторМакетаКомпоновкиДанных"). Это позволит сгенерировать макет, пригодный для вывода в обычный табличный документ.

    
    // ...
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроекДанныхОтчета.Настройки, ДанныеРасшифровки, , Тип("ГенераторМакетаКомпоновкиДанных")); // Изменено
    // ...
    
  2. Используйте ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент.

    После инициализации ПроцессораКомпоновкиДанных, вместо ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений, используйте ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент для отображения результата.

    
    // ...
    ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, НаборыВнешнихДанных, ДанныеРасшифровки, Истина);
    
    // *** ВРЕМЕННЫЙ ВЫВОД ДЛЯ ОТЛАДКИ ***
    ТабДок = Новый ТабличныйДокумент;
    ПроцессорВыводаВТабДок = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВыводаВТабДок.УстановитьДокумент(ТабДок);
    ПроцессорВыводаВТабДок.Вывести(ПроцессорКомпоновкиДанных);
    ТабДок.Показать();
    // **********************************
    // После отладки, этот блок можно заменить на вывод в ДеревоЗначений:
    // ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
    // ДеревоДанных = Новый ДеревоЗначений;
    // ПроцессорВывода.УстановитьОбъект(ДеревоДанных);
    // ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
    

Если после этого в табличном документе появились данные, значит, настройки СКД и параметры были заданы корректно, и проблема заключалась именно в специфике вывода в ДеревоЗначений или в упомянутых ранее инициализациях. Мы подтверждаем, что данные для отчета существуют.

Если же и в табличном документе отчет пуст, это указывает на более фундаментальную проблему: возможно, неверно заданы отборы, период, организация или другие параметры, которые приводят к отсутствию данных в источнике. В этом случае следует внимательнее проанализировать установленные отборы и значения параметров, а также убедиться, что в базе данных есть записи, соответствующие этим условиям.

Дополнительные аспекты, влияющие на получение данных из СКД

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

  1. Корректность настроек отбора и параметров.

    Мы убеждаемся, что все параметры (такие как Период, Организация, Подразделение, Должность, ФизическоеЛицо) не только установлены, но и их свойство Использование задано как Истина. Если Использование = Ложь, то СКД проигнорирует заданное значение параметра или условие отбора.

    Особое внимание уделите динамическому удалению элементов отбора, как это сделано в исходном коде для ПодразделениеГоловногоСотрудникаНаКонецПериода и Должность. Если вы удаляете элемент отбора, который должен был применяться, это может изменить результат. Мы всегда проверяем логику удаления, чтобы убедиться, что она соответствует нашим ожиданиям.

  2. Внешние наборы данных (НаборыВнешнихДанных).

    В коде используется переменная НаборыВнешнихДанных, которая может быть получена либо из дополнительных свойств настроек отчета, либо из функции ЗарплатаКадрыОтчеты.НаборыВнешнихДанныхАнализНачисленийИУдержаний. Если СКД опирается на внешние наборы данных (то есть данные, которые не получены напрямую запросом СКД, а переданы ей извне), мы должны убедиться, что эти внешние наборы данных правильно сформированы и содержат актуальную информацию. Если эти внешние данные пусты или некорректны, отчет также будет пустым.

  3. Структура вывода СКД и ДеревоЗначений.

    ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений заполняет ДеревоЗначений в соответствии с иерархией, определенной в настройках СКД (группировки, детальные записи). Если в настройках отчета нет группировок или детальных записей, которые могли бы сформировать строки дерева, то дерево останется пустым или будет содержать только корневой элемент без потомков.

    Мы должны убедиться, что в выбранном варианте отчета "РасчетныйЛисток" (или в измененных настройках) есть поля и группировки, которые можно представить в виде иерархического дерева. Если отчет содержит только общие итоги, то ДеревоЗначений может быть неполным или неинформативным.

  4. Права доступа.

    Мы всегда убеждаемся, что пользователь, под которым выполняется код, имеет все необходимые права для доступа к данным, которые используются в отчете. Отсутствие прав на чтение регистров, справочников или других объектов, участвующих в формировании отчета, приведет к пустым данным.

  5. Версия платформы и конфигурации.

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

  6. Пошаговая отладка.

    Мы активно используем точку останова и пошаговое выполнение кода. Это позволяет нам отследить значения переменных на каждом этапе, особенно после выполнения КомпоновщикМакета.Выполнить() и ПроцессорКомпоновкиДанных.Инициализировать(). Мы проверяем, что МакетКомпоновки не пуст, а также что ПроцессорКомпоновкиДанных корректно инициализировался. Это помогает локализовать проблему и понять, на каком именно шаге данные перестают формироваться или теряются.

Заключение

Программное формирование отчетов СКД в 1С — мощный инструмент, но требующий внимательности к деталям, особенно при работе с типовыми конфигурациями, такими как 1С:ЗУП. Мы выяснили, что основной причиной пустого ДереваЗначений в данном случае были две неочевидные, но критически важные операции: инициализация общих запросов через ЗарплатаКадрыОбщиеНаборыДанных.ЗаполнитьОбщиеЗапросы() и разрешение использования внешних функций в вычисляемых полях через четвертый параметр ПроцессорКомпоновкиДанных.Инициализировать().

Следуя этим рекомендациям, мы успешно решаем проблему получения пустых данных. Мы всегда помним о важности тщательной отладки, проверки всех параметров и отборов, а также понимания внутренней структуры отчета СКД и особенностей работы с ним в контексте конкретной конфигурации. Эти шаги помогут нам избежать подобных проблем в будущем и эффективно работать с СКД.

← На главную