Как программно добавить внешний набор данных в СКД и корректно настроить связи в типовых конфигурациях?

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

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

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

Симптомы проблемы

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

  1. Создается внешний набор данных.
  2. В него добавляются поля.
  3. Создается и заполняется ТаблицаЗначений.
  4. Настраивается связь между основным набором данных (Запросом) и новым внешним набором.

Пример кода, который часто пишут ошибочно (обращаясь к Макету, а не Схеме):


// ОШИБОЧНЫЙ ПОДХОД
НоваяСвязь = МакетКомпоновкиДанных.СвязиНаборовДанных.Добавить();
НоваяСвязь.ВыражениеИсточник = "НачисленияУдержанияОбъединенные.Сотрудник";
НоваяСвязь.ВыражениеПриемник = "ВнешнийНаборДанных.ПолеДляСоединения";
// ... настройки наборов ...

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

Фундаментальная ошибка: Схема против Макета

Чтобы понять причину проблемы, нужно четко разделять два понятия в архитектуре СКД, которые часто путают новички (иногда в одном отчете могут даже сосуществовать две разные схемы):

  1. Схема компоновки данных (DataCompositionSchema) — это "чертеж" отчета. Это то, что мы видим в конфигураторе. Она описывает, какие данные мы хотим получить и как они логически связаны. В схеме поля имеют имена и типы.
  2. Макет компоновки данных (DataCompositionTemplate) — это результат работы КомпоновщикаМакета. Это техническое задание для процессора компоновки. В макете логические имена часто заменяются на внутренние псевдонимы (например, П1, П2), а структура оптимизируется для выполнения.

В обсуждаемой теме проблема заключалась в том, что разработчик пытался добавить набор данных непосредственно в Макет (НаборДанныхОбъектМакетаКомпоновкиДанных), минуя Схему. Для упрощения таких манипуляций можно использовать библиотеки-обертки над СКД.

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

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

Важность типизации Таблицы Значений

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

Если вы связываете наборы по полю Сотрудник, то в вашей ТЗ колонка должна иметь тип СправочникСсылка.Сотрудники. Если тип не указан (составной тип, произвольный) или не совпадает, связь не сработает.

Правильный пример создания ТЗ:


ТЗ = Новый ТаблицаЗначений;
// Обязательно указываем типы!
ТЗ.Колонки.Добавить("ПолеДляСоединения", Новый ОписаниеТипов("СправочникСсылка.Сотрудники"));
ТЗ.Колонки.Добавить("ПолеТест", Новый ОписаниеТипов("Число"));

// Заполнение данными
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.ПолеДляСоединения = СсылкаНаСотрудника;
НоваяСтрока.ПолеТест = 100;

При добавлении полей в Схему (если мы делаем это программно), также важно указывать тип значения:


НовоеПоле = ВнешнийНабор.Поля.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
НовоеПоле.Заголовок = "Тестовое поле";
НовоеПоле.Поле = "ПолеТест";
НовоеПоле.ПутьКДанным = "ПолеТест";
НовоеПоле.ТипЗначения = Новый ОписаниеТипов("Число"); // Критически важно

Проблема типовых конфигураций (ЗУП, ERP, КА)

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

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

Сценарий провала:

  1. Вы программно добавляете внешний набор в СхемаКомпоновкиДанных.
  2. Вызывается типовая процедура формирования из общего модуля.
  3. Типовая процедура загружает "чистую" схему из макета по умолчанию, затирая ваш новый набор.
  4. Отчет формируется по старой схеме, ваши данные игнорируются.

Решение проблемы

Существует два основных способа решения этой задачи. Проанализируем их.

Способ 1: Использование Расширения (Рекомендуемый)

Самый надежный способ внедриться в процесс формирования отчета в современной платформе 1С — это использование Расширений конфигурации. Вместо того чтобы пытаться "протиснуться" в модуле самого отчета до вызова общих процедур, нужно переопределить поведение общего модуля, который управляет компоновкой.

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

Это позволяет "подсунуть" измененную Схему (с добавленным НаборДанныхОбъект) непосредственно перед тем, как БСП начнет создавать Макет компоновки.

Способ 2: Полный перехват события ПриКомпоновкеРезультата

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

Рассмотрим пошаговый алгоритм такого решения:


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

Ключевые моменты для запоминания

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

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

← На главную