Почему ТаблицаЗначений СКД пуста при программном формировании отчета в 1С:Предприятии 8.3?

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

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

Понимание жизненного цикла СКД и роли настроек

Прежде чем погрузиться в детали, давайте вспомним основные этапы программного использования СКД:

  1. Мы получаем СхемаКомпоновкиДанных (обычно из макета).
  2. Создаем КомпоновщикНастроекКомпоновкиДанных и инициализируем его схемой.
  3. Загружаем или формируем НастройкиКомпоновкиДанных (отборы, параметры, выбранные поля, структура).
  4. Создаем КомпоновщикМакетаКомпоновкиДанных и выполняем его, получая МакетКомпоновкиДанных.
  5. Инициализируем ПроцессорКомпоновкиДанных полученным макетом.
  6. Используем ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений (или другой процессор вывода) для получения результата в ТаблицаЗначений.

Центральную роль здесь играет объект КомпоновщикНастроекКомпоновкиДанных и его свойство Настройки. Именно в этом объекте собираются все параметры, которые будут влиять на окончательный вид и содержимое нашего отчета.

Причина возникновения пустой ТаблицыЗначений

Давайте рассмотрим типовой фрагмент кода, который приводит к проблеме:


Если СтрокаНастройкиОтбора.Заполнено Тогда
    КомпоновщикНастроек = Новый КомпоновщикНастроекКомпоновкиДанных;
    ВнешнийМакетСКД = Справочники.НастройкиОбменовСМаркетплейсами.ПолучитьМакет(СтрокаНастройкиОтбора.ИмяОтбора);
    КомпоновщикНастроек.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(ВнешнийМакетСКД));
    
    НастройкиСКД = ОбменСМаркетплейсамиУТ11КлиентСервер.ДеСериализоватьНастройкиОтбора(СтрокаНастройкиОтбора.НастройкаОтбораXML);
    КомпоновщикНастроек.ЗагрузитьНастройки(НастройкиСКД); // <--- Вероятное место возникновения проблемы
    
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетВывода = КомпоновщикМакета.Выполнить(ВнешнийМакетСКД, КомпоновщикНастроек.ПолучитьНастройки,,, Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
    
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетВывода);
    
    РезультатОтбора = Новый ТаблицаЗначений;
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
    ПроцессорВывода.УстановитьОбъект(РезультатОтбора);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецЕсли;

Основная причина, по которой РезультатОтбора может оказаться пустой, кроется в строке КомпоновщикНастроек.ЗагрузитьНастройки(НастройкиСКД);. Разберем, что здесь происходит:

  1. КомпоновщикНастроек.Инициализировать(...): На этом этапе КомпоновщикНастроек загружает все настройки по умолчанию из нашей СхемаКомпоновкиДанных (в данном случае, ВнешнийМакетСКД). Это включает в себя не только возможные стандартные отборы и параметры, но и обязательно ВыбранныеПоля (поля, которые мы хотим видеть в отчете) и Структура (как эти поля должны быть сгруппированы, детализированы и т.д.).
  2. НастройкиСКД = ...ДеСериализоватьНастройкиОтбора(...): Мы десериализуем настройки из XML-строки. Если этот XML содержит только информацию об отборах, а части, отвечающие за ВыбранныеПоля и Структура, в нем отсутствуют или пусты, то это приведет к проблеме.
  3. КомпоновщикНастроек.ЗагрузитьНастройки(НастройкиСКД);: Эта строка берет настройки из объекта НастройкиСКД и применяет их к КомпоновщикНастроек. При этом происходит перезапись соответствующих частей. Если в НастройкиСКД поля ВыбранныеПоля и Структура пусты, то они перезапишут те, что были установлены по умолчанию при инициализации.

В результате, после загрузки настроек, КомпоновщикНастроек может оказаться с пустыми ВыбранныеПоля и/или пустой Структура. Если СКД не знает, какие поля выводить и в какой структуре, она просто не сможет сформировать результат, и наша ТаблицаЗначений останется пустой.

Как проверить настройки в отладчике

Для диагностики этой проблемы нам необходимо посмотреть, что находится в объекте Настройки компоновщика после загрузки. Разберем шаги:

  1. Установите точку останова: Поставьте точку останова сразу после строки КомпоновщикНастроек.ЗагрузитьНастройки(НастройкиСКД);.
  2. Запустите отладку: Выполните ваш код в режиме отладки.
  3. Исследуйте объект КомпоновщикНастроек: В окне отладчика найдите переменную КомпоновщикНастроек.
  4. Перейдите к свойству Настройки: Разверните свойство Настройки объекта КомпоновщикНастроек.
  5. Проверьте ВыбранныеПоля и Структура: Особое внимание уделите коллекциям ВыбранныеПоля и Структура. Если вы видите, что эти коллекции пусты (Количество() = 0), то это и есть корень проблемы. Для просмотра содержимого коллекций, особенно в старых версиях отладчика, может потребоваться нажать на кнопку "Показать значение в отдельном окне", как было отмечено в обсуждении.

Этот шаг поможет нам подтвердить, что именно происходит с настройками после их загрузки.

Решение проблемы: Обеспечиваем полноту настроек

Существует несколько подходов к решению этой проблемы, в зависимости от того, как мы планируем использовать сохраненные настройки.

1. Гарантируем полноту XML-настроек

Наиболее чистый подход — это убедиться, что XML, который мы десериализуем в НастройкиСКД, всегда содержит все необходимые части: отборы, параметры, а также ВыбранныеПоля и Структура. Если вы формируете этот XML из конструктора СКД или из пользовательских настроек, всегда сохраняйте их целиком. Когда пользователь редактирует только отборы, вы должны сохранять *все* текущие настройки компоновщика, а не только измененные отборы.

Это означает, что метод ОбменСМаркетплейсамиУТ11КлиентСервер.ДеСериализоватьНастройкиОтбора должен быть универсальным и десериализовывать полный объект настроек, а не только его часть, связанную с отборами. И, соответственно, метод, который сериализует настройки в XML (не показанный в примере), должен сериализовывать их целиком.

2. Программное дополнение настроек после загрузки

Если по какой-то причине мы не можем гарантировать полноту XML (например, если он создавался вручную или из внешнего источника, где хранились только отборы), мы можем программно дополнить ВыбранныеПоля и Структуру после загрузки отбора.

Рассмотрим, как это можно сделать:


Если СтрокаНастройкиОтбора.Заполнено Тогда
    КомпоновщикНастроек = Новый КомпоновщикНастроекКомпоновкиДанных;
    ВнешнийМакетСКД = Справочники.НастройкиОбменовСМаркетплейсами.ПолучитьМакет(СтрокаНастройкиОтбора.ИмяОтбора);
    КомпоновщикНастроек.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(ВнешнийМакетСКД));
    
    НастройкиСКД = ОбменСМаркетплейсамиУТ11КлиентСервер.ДеСериализоватьНастройкиОтбора(СтрокаНастройкиОтбора.НастройкаОтбораXML);
    
    // Загружаем настройки, которые могут быть неполными
    КомпоновщикНастроек.ЗагрузитьНастройки(НастройкиСКД);
    
    // *************************************************************
    // Дополнение настроек: Проверяем и заполняем ВыбранныеПоля
    // *************************************************************
    Если КомпоновщикНастроек.Настройки.ВыбранныеПоля.Количество() = 0 Тогда
        // Пример заполнения: добавляем несколько полей, которые нам точно нужны
        КомпоновщикНастроек.Настройки.ВыбранныеПоля.Добавить(Новый ВыбранноеПолеКомпоновкиДанных("Номенклатура"));
        КомпоновщикНастроек.Настройки.ВыбранныеПоля.Добавить(Новый ВыбранноеПолеКомпоновкиДанных("Характеристика"));
        КомпоновщикНастроек.Настройки.ВыбранныеПоля.Добавить(Новый ВыбранноеПолеКомпоновкиДанных("Количество"));
        КомпоновщикНастроек.Настройки.ВыбранныеПоля.Добавить(Новый ВыбранноеПолеКомпоновкиДанных("Цена"));
        
        // Или, если у нас есть "основной вариант" настроек, мы можем взять поля из него
        // Например, если ВнешнийМакетСКД содержит "основной" вариант настроек
        // КомпоновщикНастроек.Настройки.ВыбранныеПоля.ЗагрузитьНастройки(
        //     КомпоновщикНастроек.НастройкиПоУмолчанию.ВыбранныеПоля);
    КонецЕсли;

    // *************************************************************
    // Дополнение настроек: Проверяем и заполняем Структуру вывода
    // *************************************************************
    Если КомпоновщикНастроек.Настройки.Структура.Количество() = 0 Тогда
        // Добавляем простую структуру: детальные записи или группировку
        ЭлементСтруктуры = КомпоновщикНастроек.Настройки.Структура.Добавить(Тип("ЭлементСтруктурыКомпоновкиДанныхТаблица")); // Или Тип("ЭлементСтруктурыКомпоновкиДанныхГруппировка")
        ЭлементСтруктуры.Использование = Истина;
        
        // Если это таблица или детальные записи, то просто добавляем выбранные поля
        Для Каждого ВыбранноеПоле Из КомпоновщикНастроек.Настройки.ВыбранныеПоля Цикл
            ЭлементСтруктуры.ВыбранныеПоля.Добавить(ВыбранноеПоле);
        КонецЦикла;

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

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

Обратите внимание на возможность использования КомпоновщикНастроек.НастройкиПоУмолчанию, чтобы взять стандартные поля или структуру из схемы, если это применимо.

3. Ручное слияние настроек (для отборов)

В некоторых случаях, если мы хотим применить только отборы из внешнего источника, не затрагивая остальные настройки, вместо полного вызова ЗагрузитьНастройки() можно вручную очистить и заполнить коллекцию отборов. Этот подход более сложный, но дает полный контроль:


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

Этот подход позволяет нам применить только специфические изменения (например, пользовательские отборы), сохраняя при этом все остальные настройки по умолчанию из схемы компоновки данных.

Дополнительные аспекты и рекомендации

Особенности вывода в ТаблицаЗначений (КоллекциюЗначений)

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

Убедитесь, что ваша структура вывода соответствует этим ограничениям, если вы стремитесь получить плоскую ТаблицаЗначений.

Отладка итогового запроса СКД

Иногда даже при правильно настроенных ВыбранныхПолях и Структуре результат может быть пустым, если сам запрос СКД не возвращает данные. Это может произойти из-за сложных отборов, отсутствия данных или неожиданной "оптимизации" запроса самой СКД. Для анализа итогового запроса мы можем использовать:

  1. Консоль компоновки данных: Это стандартный инструмент 1С, который позволяет визуально настроить отчет и увидеть сгенерированный запрос.
  2. Инструменты разработчика: Существуют внешние обработки (например, "Консоль запросов и СКД") которые предоставляют более широкие возможности для анализа запроса СКД на разных этапах.

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

Обработка NULL-значений в запросе

Если в вашем запросе СКД используются агрегатные функции (ресурсы) или поля, которые могут содержать NULL, это может привести к неожиданным результатам, вплоть до отсутствия строк. Например, если вы считаете сумму по полю, которое часто бывает NULL, и не обрабатываете это. Рекомендуется использовать функцию ЕСТЬNULL() в запросе для корректной обработки таких значений, приводя их к желаемому виду (например, к 0 для числовых полей):


ВЫБРАТЬ
    Номенклатура.Наименование КАК Номенклатура,
    ЕСТЬNULL(ТаблицаОстатков.Количество, 0) КАК КоличествоНаСкладе,
    ЕСТЬNULL(ТаблицаЦен.Цена, 0) КАК Цена
ИЗ
    Справочник.Номенклатура КАК Номенклатура
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТоваров.Остатки КАК ТаблицаОстатков
    ПО Номенклатура.Ссылка = ТаблицаОстатков.Номенклатура
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК ТаблицаЦен
    ПО Номенклатура.Ссылка = ТаблицаЦен.Номенклатура

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

Проверка макета компоновки

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

Заключение

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

← На главную