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