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