Как получить данные из подчиненного поля стандартного реквизита Регистратор в отчетах 1С

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

При доработке типовых отчетов в старых редакциях конфигураций (например, «Управление торговлей 10.3» или «Управление производственным предприятием 1.3») разработчики часто сталкиваются с необходимостью вывести дополнительные данные из документов-регистраторов. Типичный пример — вывод поля Комментарий в отчете «Ведомость по денежным средствам». Однако при попытке добавить такое поле через настройки группировок Универсального отчета система нередко выбрасывает ошибку: «Поле не найдено ИсточникДанных.Регистратор.Комментарий».

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

Почему возникает ошибка «Поле не найдено»?

Для того чтобы понять природу ошибки, давайте проанализируем, как работает Универсальный отчет под капотом. В основе старых универсальных отчетов лежит объект ПостроительОтчета. Запрос для него пишется к виртуальным таблицам (например, к таблице ОстаткиИОбороты регистра накопления ДенежныеСредства).

Выясним главные причины возникновения описываемой ошибки:

  1. Особенности виртуальных таблиц регистров. Поле Регистратор физически присутствует в таблице ОстаткиИОбороты только тогда, когда в параметрах этой таблицы явно указана периодичность Регистратор (или Авто). Если в текущих настройках компоновщика или построителя группировка по регистратору не выбрана пользователем, то оптимизатор запросов СУБД исключает это поле из выборки. Попытка обратиться к нему через точку вызывает ошибку на уровне выполнения запроса.
  2. Составной тип данных. Реквизит Регистратор имеет составной тип, который включает в себя десятки различных документов. При обращении через точку вида Регистратор.Комментарий система 1С вынуждена выполнять левое соединение со всеми таблицами документов, входящих в этот тип. Если построитель запроса формирует текст динамически, он не всегда может корректно разыменовать свойства составного типа без предварительного приведения типов или жесткого указания полей в схеме.
  3. Ограничение на длину строк в группировках. Поле Комментарий у большинства документов имеет тип «Строка неограниченной длины». Системы управления базами данных (СУБД) накладывают жесткое ограничение: такие поля нельзя использовать в секции группировки (GROUP BY). Даже использование конструкции ВЫРАЗИТЬ(... КАК СТРОКА(300)) не спасает, если построитель отчета накладывает выражение динамически поверх уже оптимизированного запроса.

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


Способ 1. Модернизация исходного запроса Универсального отчета

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

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


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

Чтобы избежать ошибки «Поле не найдено», мы должны убедиться, что выражение приведения типа жестко зафиксировано в самом тексте запроса, который передается построителю. Для этого найдем в модуле отчета или в его макете исходный текст запроса и добавим выражение приведения типа в блок описания полей построителя (в секцию {ВЫБРАТЬ...}).

Разберем, как должен выглядеть фрагмент запроса в схеме построителя:


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ИсточникДанных.Организация КАК Организация,
    ИсточникДанных.Регистратор КАК Регистратор,
    // Явно выражаем тип комментария до группировки
    ВЫРАЗИТЬ(ИсточникДанных.Регистратор.Комментарий КАК СТРОКА(300)) КАК РегистраторКомментарий
{ВЫБРАТЬ
    Организация.*,
    Регистратор.*,
    РегистраторКомментарий КАК РегистраторКомментарий}
ИЗ
    РегистрНакопления.ДенежныеСредства.ОстаткиИОбороты(&ДатаНач, &ДатаКон, Регистратор, , ) КАК ИсточникДанных

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


Способ 2. Использование механизма дополнительных полей

Если нам не требуется группировать данные по самому комментарию (то есть нам не нужно, чтобы одинаковые комментарии объединялись в одну строку с подсчетом итогов), то использовать группировку для этого поля не имеет смысла. Правильнее и эффективнее вывести его как дополнительное поле рядом с полем Регистратор.

Механизм дополнительных полей выводит информацию только для детальных записей и не накладывает на них обязательную группировку СГРУППИРОВАТЬ ПО на уровне СУБД. Это позволяет полностью обойти проблему с ограничением длины строки без использования функции ВЫРАЗИТЬ.

Проанализируем программную настройку вывода дополнительного поля:


// Вместо добавления поля группировки:
// УниверсальныйОтчет.ДобавитьПолеГруппировка("Комментарий", ...);

// Добавляем поле как дополнительное (оно будет выводиться в колонке рядом с Регистратором)
УниверсальныйОтчет.ДобавитьДополнительноеПоле("Регистратор.Комментарий", НомерРазмещения, Положение);

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


Способ 3. Перевод отчета на Систему компоновки данных (СКД)

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

Рассмотрим, как красиво решить задачу получения комментария регистратора с помощью СКД:

  1. Автоматическое разыменование полей. В СКД вам достаточно написать простой запрос к виртуальной таблице остатков и оборотов. Система сама поймет, когда пользователь захочет вывести Регистратор.Комментарий, и выполнит оптимальное левое соединение только с теми таблицами документов, которые реально присутствуют в выборке.
  2. Использование ролей полей. Для поля Регистратор в наборе данных СКД необходимо настроить роль «Период» с типом «Дополнительный период». Это гарантирует корректный расчет остатков и оборотов при любой выбранной пользователем детализации.
  3. Вывод комментария через функции общего модуля. Чтобы полностью исключить вероятность ошибок СУБД, связанных со строками неограниченной длины в запросах, мы можем перенести получение комментария в код на стороне сервера 1С.

Для этого в СКД на вкладке «Вычисляемые поля» добавим новое поле КомментарийРегистратора. В колонке «Выражение» укажем вызов функции общего модуля:


// Выражение для вычисляемого поля в СКД
ОбщийМодульОтчетов.ПолучитьКомментарийДокумента(Регистратор)

В самом общем модуле (например, создадим или используем существующий неглобальный модуль ОбщийМодульОтчетов с флагами «Сервер», «Вызов сервера») пропишем простую экспортную функцию:


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

Этот метод полностью изолирует базу данных от тяжелых операций соединения строк неограниченной длины в SQL-запросах, обеспечивая максимальную скорость формирования отчета и 100% стабильность его работы.

Резюме

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

← На главную