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