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