При доработке типовых универсальных отчетов, построенных на базе объекта ПостроительОтчета (часто встречающихся в конфигурациях «Управление торговлей 10.3», «Управление производственным предприятием 1.3» и «Комплексная автоматизация 1.1»), разработчики сталкиваются с задачей добавления измерений строк. Если стандартный реквизит объекта добавляется тривиально — например, через обращение Контрагент.Наименование, — то группировка по дополнительному свойству (значению из плана видов характеристик СвойстваОбъектов) требует понимания внутреннего механизма формирования псевдонимов полей.
Разберем детально, как устроен этот механизм, как сформировать системное имя поля группировки и каких распространенных ошибок следует избегать при программной настройке отчета.
В типовом универсальном отчете за подключение дополнительных реквизитов отвечает процедура ДобавитьВТекстЗапросаСвойстваИКатегории. Механизм динамически модифицирует исходный текст запроса: к таблицам объектов добавляются левые соединения с регистром сведений ЗначенияСвойствОбъектов.
Проанализируем фрагмент запроса, который формируется системой автоматически при включенном режиме свойств:
{ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияСвойствОбъектов КАК КонтрагентСвойство392584bb9ca211e49e1b0015172a9709
ПО КонтрагентСвойство392584bb9ca211e49e1b0015172a9709.Объект = Взаиморасчеты.Контрагент
И КонтрагентСвойство392584bb9ca211e49e1b0015172a9709.Свойство = &ПараметрКонтрагентСвойство392584bb9ca211e49e1b0015172a9709}
В секцию полей выборки при этом добавляются следующие конструкции:
КонтрагентСвойство392584bb9ca211e49e1b0015172a9709.Значение КАК КонтрагентСвойство392584bb9ca211e49e1b0015172a9709Значение,
ПРЕДСТАВЛЕНИЕ(КонтрагентСвойство392584bb9ca211e49e1b0015172a9709.Значение) КАК КонтрагентСвойство392584bb9ca211e49e1b0015172a9709ЗначениеПредставление
Обратим внимание на имя поля: оно формируется путем слияния имени исходного измерения, префикса Свойство, шестнадцатеричного строкового представления УникальныйИдентификатор() без дефисов и постфикса Значение.
Хардкодить фиксированный GUID свойства напрямую в коде не рекомендуется, так как при переносе настроек между базами или обновлении справочника идентификатор может измениться. Построим универсальный алгоритм, который динамически находит ссылку на свойство и генерирует правильное имя измерения.
Разберем реализацию по шагам:
// 1. Поиск нужного свойства
СвойствоСсылка = ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоНаименованию("ОтветственныйЗаПлатежи");
Если ЗначениеЗаполнено(СвойствоСсылка) Тогда
// 2. Очистка GUID от символов дефиса
СтрГуид = СтрЗаменить(Строка(СвойствоСсылка.УникальныйИдентификатор()), "-", "");
// 3. Формирование системного имени поля
ИмяПоляСвойства = "КонтрагентСвойство" + СтрГуид + "Значение";
// 4. Добавление группировки в универсальный отчет
ОтчетОстатки.УниверсальныйОтчет.ДобавитьИзмерениеСтроки(ИмяПоляСвойства, ТипИзмеренияПостроителяОтчета.Элементы);
КонецЕсли;
Частая ошибка, с которой сталкиваются разработчики — исключение при вызове метода ДобавитьИзмерениеСтроки или отсутствие группировки в итоговом результате. Выясним причину этой проблемы: она заключается в нарушении последовательности этапов подготовки отчета.
Чтобы построитель увидел сформированные динамические поля, необходимо соблюдать четкую последовательность действий:
УниверсальныйОтчет.ДобавитьСвойстваИКатегорииДляПоля(...) с указанием значения ПланыВидовХарактеристик.НазначенияСвойствКатегорийОбъектов.Справочник_Контрагенты.ДобавитьВТекстЗапросаСвойстваИКатегории), после чего результирующий текст помещается в свойство ПостроительОтчета.Текст.ПостроительОтчета.ЗаполнитьНастройки(), чтобы построитель перечитал поля выборки.ДобавитьИзмерениеСтроки(...).Если в отчете требуется группировка исключительно по одному конкретному свойству и динамический выбор остальных реквизитов не нужен, типовой механизм с генерацией GUID можно обойти. Это позволяет существенно упростить код и повысить производительность запроса.
Рассмотрим, как связать данные напрямую в запросе построителя:
ТекстЗапроса =
"ВЫБРАТЬ
| Взаиморасчеты.Контрагент КАК Контрагент,
| ЕСТЬNULL(СвойстваКонтрагентов.Значение, НЕОПРЕДЕЛЕНО) КАК ОтветственныйЗаПлатежи,
| Взаиморасчеты.СуммаОстаток КАК СуммаОстаток
|{ВЫБРАТЬ
| Контрагент.*,
| ОтветственныйЗаПлатежи.*,
| СуммаОстаток}
|ИЗ
| РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки(&Период, ) КАК Взаиморасчеты
| {ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияСвойствОбъектов КАК СвойстваКонтрагентов
| ПО СвойстваКонтрагентов.Объект = Взаиморасчеты.Контрагент
| И СвойстваКонтрагентов.Свойство = &СвойствоОтветственный}
|{ГДЕ
| Взаиморасчеты.Контрагент.*,
| СвойстваКонтрагентов.Значение.* КАК ОтветственныйЗаПлатежи}
|{ИТОГИ И ГРУППИРОВАТЬ ПО
| Контрагент.*,
| ОтветственныйЗаПлатежи.*}";
После установки параметра &СвойствоОтветственный добавление измерения в код становится максимально прозрачным:
ОтчетОстатки.УниверсальныйОтчет.ПостроительОтчета.Параметры.Вставить(
"СвойствоОтветственный",
ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоНаименованию("ОтветственныйЗаПлатежи")
);
ОтчетОстатки.УниверсальныйОтчет.ДобавитьИзмерениеСтроки("ОтветственныйЗаПлатежи");
Такой подход избавляет от громоздких динамических псевдонимов, упрощает отладку и предотвращает падение производительности СУБД, возникающее при автоматическом подключении десятков неиспользуемых свойств.