Мы часто сталкиваемся с задачей, когда хотим, чтобы изменение значения одного параметра в Системе Компоновки Данных (СКД) автоматически влияло на другой параметр. Например, чтобы при выборе элемента справочника в одном параметре, другой параметр автоматически заполнялся значением его реквизита. Эта функциональность значительно повышает удобство использования отчетов, но ее реализация в СКД имеет свои нюансы. Давайте разберем, как реализовать такую логику в 1С:Предприятии, учитывая возможности и ограничения СКД.
&Параметр.РеквизитИзначально может показаться, что проблему можно решить прямо в настройках СКД, используя выражение вида &ИмяПараметраИсточника.ИмяРеквизита для значения параметра-приемника. Например, если у нас есть параметр ПараметрИсточник, который ссылается на какой-либо объект (допустим, элемент справочника "Номенклатура"), и мы хотим, чтобы ПараметрПриемник автоматически принимал значение реквизита Артикул этого элемента, мы могли бы попробовать прописать в поле "Значение" для ПараметрПриемник следующее:
&ПараметрИсточник.Артикул
Однако, как показывает практика и подтверждают обсуждения на форумах, такой подход имеет существенные ограничения. Платформа 1С поддерживает использование выражений &Параметр.Реквизит только для очень ограниченного числа типов данных, чаще всего для примитивных типов или специфических случаев, когда платформа может однозначно разрешить реквизит. Для большинства ссылочных типов (справочники, документы, планы видов характеристик и т.д.) это выражение не сработает напрямую в настройках СКД и приведет к ошибке при попытке сформировать отчет или даже при открытии формы настроек.
Причина в том, что СКД не всегда может корректно интерпретировать и получить значение реквизита объекта по ссылке в момент настройки или предварительного расчета. Система компоновки данных предназначена в первую очередь для декларативного описания запросов и структуры отчета, а не для сложной логики извлечения данных из объектов базы в интерактивном режиме или на этапе настройки параметров.
Если наша цель – обеспечить автоматическое заполнение параметра-приемника значением реквизита параметра-источника непосредственно перед формированием отчета, то наиболее надежным и распространенным способом является программная реализация. Мы можем использовать для этого событие ПриКомпоновкеРезультата в модуле объекта отчета. В этом обработчике у нас будет доступ к настройкам компоновщика, и мы сможем получить значение параметра-источника, извлечь из него нужный реквизит и установить его для параметра-приемника.
Для получения значения реквизита объекта по его ссылке удобно использовать стандартные функции из Библиотеки Стандартных Подсистем (БСП), которые присутствуют в большинстве современных конфигураций, таких как 1С:ERP Управление предприятием, 1С:Комплексная автоматизация и других. Одной из таких функций является ОбщегоНазначения.ЗначениеРеквизитаОбъекта(). Эта функция позволяет безопасно получить значение любого реквизита по ссылке на объект, обрабатывая при этом возможные ошибки, если реквизита не существует.
Давайте рассмотрим пример, как это можно реализовать в модуле объекта отчета:
// В модуле объекта отчета
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
// Получаем текущее значение параметра-источника из настроек компоновщика.
// Важно, чтобы ПараметрИсточник был определен в СКД как параметр данных.
ПараметрИсточникаСКД = КомпоновщикНастроек.Настройки.ПараметрыДанных.ПолучитьПараметр("ПараметрИсточник");
ЗначениеПараметраИсточника = Неопределено;
Если ПараметрИсточникаСКД <> Неопределено Тогда
ЗначениеПараметраИсточника = ПараметрИсточникаСКД.Значение;
КонецЕсли;
// Проверяем, что ПараметрИсточник заполнен и является ссылочным типом, а не пустым значением.
Если ЗначениеПараметраИсточника <> Неопределено И ЗначениеПараметраИсточника <> Null И НЕ ЗначениеПараметраИсточника.Пустая() Тогда
// Определяем имя реквизита, который мы хотим извлечь.
// Укажите здесь актуальное имя реквизита вашего объекта.
ИмяРеквизита = "Реквизит";
ЗначениеРеквизита = Неопределено;
// Используем функцию из общего модуля для безопасного получения реквизита.
// В конфигурациях на БСП это часто ОбщегоНазначения.ЗначениеРеквизитаОбъекта.
// Если такой функции нет, можно реализовать аналогичную свою или получить через
// МенеджерОбъекта.ПолучитьОбъект().<ИмяРеквизита> после проверки типа объекта.
Попытка
// Пример использования БСП-функции:
ЗначениеРеквизита = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ЗначениеПараметраИсточника, ИмяРеквизита);
Исключение
// Обработка ошибки, если реквизит не найден, не существует или функция недоступна.
Сообщить("Не удалось получить значение реквизита " + ИмяРеквизита + " из объекта типа " + ТипЗнч(ЗначениеПараметраИсточника) + ": " + ОписаниеОшибки());
// Как альтернатива, если вы точно знаете тип объекта и реквизит:
// Если ТипЗнч(ЗначениеПараметраИсточника) = Тип("СправочникСсылка.Номенклатура") Тогда
// ЗначениеРеквизита = ЗначениеПараметраИсточника.ПолучитьОбъект().Реквизит;
// КонецЕсли;
КонецПопытки;
// Получаем объект параметра-приемника СКД.
ПараметрПриемникСКД = КомпоновщикНастроек.Настройки.ПараметрыДанных.ПолучитьПараметр("ПараметрПриемник");
Если ПараметрПриемникСКД <> Неопределено Тогда
// Устанавливаем полученное значение в ПараметрПриемник.
Если ЗначениеРеквизита <> Неопределено Тогда
ПараметрПриемникСКД.УстановитьЗначение(ЗначениеРеквизита);
Иначе
// Если реквизит не найден или пуст, можно очистить ПараметрПриемник.
ПараметрПриемникСКД.УстановитьЗначение(Null);
КонецЕсли;
КонецЕсли;
Иначе
// Если ПараметрИсточник не заполнен, очищаем ПараметрПриемник.
ПараметрПриемникСКД = КомпоновщикНастроек.Настройки.ПараметрыДанных.ПолучитьПараметр("ПараметрПриемник");
Если ПараметрПриемникСКД <> Неопределено Тогда
ПараметрПриемникСКД.УстановитьЗначение(Null);
КонецЕсли;
КонецЕсли;
// Важно: если вы полностью перехватываете компоновку, можете установить СтандартнаяОбработка = Ложь.
// В большинстве случаев, когда вы только меняете параметры, оставляйте СтандартнаяОбработка = Истина.
КонецПроцедуры
В этом примере мы получаем объект, который был выбран в ПараметрИсточник, затем с помощью функции ОбщегоНазначения.ЗначениеРеквизитаОбъекта извлекаем из него нужное нам поле (реквизит) и присваиваем его ПараметруПриемнику. Важно понимать, что эта логика выполняется каждый раз при формировании отчета, то есть перед тем, как СКД начнет строить запросы и выводить данные. Пользователь увидит обновленное значение только после нажатия кнопки "Сформировать".
Мы должны убедиться, что для параметров ПараметрИсточник и ПараметрПриемник в настройках СКД установлены соответствующие типы данных, чтобы система могла корректно работать со значениями. Если ПараметрИсточник является ссылочным типом (например, "СправочникСсылка.Номенклатура"), а ПараметрПриемник должен принимать значение строкового реквизита (например, "Строка"), то это необходимо указать в свойствах параметров СКД на вкладке "Параметры".
Изначальный вопрос часто подразумевает не только установку значения при формировании отчета, но и немедленное интерактивное обновление значения параметра-приемника прямо в пользовательской форме настроек отчета при изменении параметра-источника. Например, чтобы пользователь выбрал контрагента, и поле "Договор" сразу же заполнилось основным договором этого контрагента, даже до нажатия кнопки "Сформировать". Это создает более динамичный и удобный интерфейс для пользователя.
Такая возможность не может быть реализована только средствами СКД через декларативные настройки. Для этого требуется более глубокая программная доработка. Вам понадобится:
ПриИзменении: Для элемента формы, который связан с ПараметромИсточником (например, поле ввода для выбора контрагента), необходимо подключить обработчик события ПриИзменении. Этот обработчик будет срабатывать каждый раз, когда пользователь меняет значение в этом поле.ПараметромИсточником.ПараметромПриемником, чтобы пользователь сразу увидел изменение.КомпоновщикНастроек.Настройки.ПараметрыДанных, чтобы это изменение было учтено при дальнейшей компоновке отчета и его формировании.Рассмотрим пример псевдокода для клиентского обработчика и серверной функции:
// В модуле формы отчета
// Обработчик события ПриИзменении для элемента формы, связанного с ПараметромИсточником
&НаКлиенте
Процедура ПараметрИсточникПриИзменении(Элемент)
// Получаем значение из элемента формы, который пользователь только что изменил.
ЗначениеИсточника = Элементы.ПолеВводаПараметрИсточник.Значение; // Используем имя элемента формы
// Если значение заполнено, вызываем серверный метод для получения реквизита.
Если ЗначениеИсточника <> Неопределено И ЗначениеИсточника <> Null И НЕ ЗначениеИсточника.Пустая() Тогда
// Вызов серверной функции для получения реквизита.
// Здесь мы передаем ссылку на объект и имя нужного реквизита.
ЗначениеРеквизита = ПолучитьРеквизитОбъектаНаСервере(ЗначениеИсточника, "Реквизит");
// Устанавливаем полученное значение в элемент формы, связанный с ПараметромПриемником.
Элементы.ПолеВводаПараметрПриемник.Значение = ЗначениеРеквизита;
// Также необходимо обновить значение в параметрах компоновщика настроек.
// Это гарантирует, что при формировании отчета СКД будет использовать это новое значение.
ПараметрПриемникСКД = КомпоновщикНастроек.Настройки.ПараметрыДанных.ПолучитьПараметр("ПараметрПриемник");
Если ПараметрПриемникСКД <> Неопределено Тогда
ПараметрПриемникСКД.УстановитьЗначение(ЗначениеРеквизита);
КонецЕсли;
Иначе
// Если ПараметрИсточник очищен, очищаем и ПараметрПриемник.
Элементы.ПолеВводаПараметрПриемник.Значение = Null;
ПараметрПриемникСКД = КомпоновщикНастроек.Настройки.ПараметрыДанных.ПолучитьПараметр("ПараметрПриемник");
Если ПараметрПриемникСКД <> Неопределено Тогда
ПараметрПриемникСКД.УстановитьЗначение(Null);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
// Серверная функция для безопасного получения реквизита объекта по ссылке.
// Эта функция вызывается с клиента и выполняется на сервере.
&НаСервере
Функция ПолучитьРеквизитОбъектаНаСервере(ОбъектСсылка, ИмяРеквизита)
Если ОбъектСсылка = Неопределено ИЛИ ОбъектСсылка = Null ИЛИ ОбъектСсылка.Пустая() Тогда
Возврат Null;
КонецЕсли;
// Пример использования БСП-функции ОбщегоНазначения.ЗначениеРеквизитаОбъекта.
// Если вашей конфигурации нет БСП или этой функции, реализуйте свою логику.
Попытка
Возврат ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ОбъектСсылка, ИмяРеквизита);
Исключение
Сообщить("Ошибка при получении реквизита '" + ИмяРеквизита + "' для объекта типа " + ТипЗнч(ОбъектСсылка) + ": " + ОписаниеОшибки());
Возврат Null;
КонецПопытки;
КонецФункции
Обратите внимание: для получения реквизита ссылочного типа с клиента обычно требуется вызов серверной функции, так как клиент не имеет прямого доступа к данным объектов базы данных. Клиентская часть формы работает с представлением данных, а не с самими объектами базы данных. Серверная функция позволяет безопасно и эффективно получить нужную информацию.
Мы выяснили, что прямая декларативная установка значения параметра СКД из реквизита другого параметра через выражение &Параметр.Реквизит имеет ограниченную применимость и в большинстве случаев не работает для ссылочных типов. Для решения этой задачи существуют два основных программных подхода:
ПриКомпоновкеРезультата в модуле объекта отчета. Этот метод является серверным и хорошо подходит для случаев, когда интерактивное обновление в форме не является критичным.ПриИзменении для соответствующих полей.Выбор конкретного подхода зависит от требований к отчету и его удобству для конечного пользователя. В большинстве случаев комбинация этих подходов позволяет добиться необходимой функциональности.