Чтение реквизитов и свойств в 1С 8.3: от точки до запроса
Содержание
Значение свойства в 1С чаще всего получают через точку: Документ.Контрагент, Ссылка.Наименование. Если имя свойства известно только во время выполнения, пишут Объект["ИмяРеквизита"]. Для чтения одного-двух реквизитов по ссылке в типовых конфигурациях есть функции БСП ОбщегоНазначения.ЗначениеРеквизитаОбъекта() и ЗначенияРеквизитовОбъекта(), а для массовой выборки — запрос.
Проблемы начинаются, когда реквизит пустой, ссылка равна Неопределено, код выполняется на клиенте или одно и то же поле читается в цикле тысячи раз. Ниже — каждый способ с примерами, ограничениями и ошибками, которые он вызывает. Всё написано для платформы 8.3 и управляемых форм; встроенный язык общий для всех конфигураций.
Обращение через точку: самый короткий путь
Точка работает для любого значения объектного типа: объекта справочника или документа, ссылки, строки табличной части, структуры, строки таблицы значений.
// На сервере
Контрагент = ДокументОбъект.Контрагент;
ИНН = ДокументОбъект.Контрагент.ИНН;
Дата = ДокументСсылка.Дата;
Что происходит «под капотом» при обращении через ссылку:
- 🔹
ДокументСсылка.Дата— платформа читает объект документа из базы целиком, а не одно поле; - 🔹 каждая следующая точка (
Контрагент.ИНН) — ещё одно чтение, уже объекта контрагента; - 🔹 прочитанные данные кэшируются на время сеанса, но кэш не бесконечный, и на больших объектах с табличными частями это ощутимо.
На клиенте ссылку разыменовать нельзя. Код Объект.Контрагент.ИНН в клиентской процедуре формы вызовет ошибку: на клиенте ссылка — это просто идентификатор без данных. Читайте реквизиты в процедуре с директивой &НаСервере или через серверный общий модуль.
Квадратные скобки вместо точки
Тот же доступ можно записать через строку с именем:
ИмяРеквизита = "Контрагент";
Значение = ДокументОбъект[ИмяРеквизита];
Это удобно, когда имя реквизита приходит из настроек, параметра или списка. Скобки поддерживают объекты, ссылки, структуры, строки таблиц значений и табличных частей. Работает быстрее и безопаснее, чем Вычислить("ДокументОбъект." + ИмяРеквизита): строка не исполняется как код, значит, в неё нельзя подсунуть лишнее.
Проверка перед чтением: ЗначениеЗаполнено() и сравнение с Неопределено
Самая частая авария — цепочка точек через пустое значение. Если в документе не выбран договор, Документ.Договор.Организация вернёт пустую ссылку, а дальше всё зависит от типа:
| Что лежит в реквизите | Что будет при Реквизит.Поле |
|---|---|
| Пустая ссылка нужного типа | Поле вернётся пустым, ошибки нет |
Неопределено (составной тип, ничего не выбрано) | Ошибка «Значение не является значением объектного типа» |
| Значение другого типа из составного | Ошибка «Поле объекта не обнаружено» |
Поэтому перед переходом по цепочке проверяйте значение:
Если ЗначениеЗаполнено(Документ.Договор) Тогда
Организация = Документ.Договор.Организация;
Иначе
Организация = Справочники.Организации.ПустаяСсылка();
КонецЕсли;
ЗначениеЗаполнено() возвращает Ложь для Неопределено, Null, пустой ссылки, пустой строки, нуля, пустой даты и Ложь для булева. Последний пункт ловит многих: для реквизита типа «Булево» функция не отличает «не заполнено» от «выключено».
Для составного типа одной проверки на заполненность мало — нужно ещё убедиться в типе:
Если ТипЗнч(Документ.Контрагент) = Тип("СправочникСсылка.Контрагенты") Тогда
ИНН = Документ.Контрагент.ИНН;
КонецЕсли;
Функции БСП для чтения реквизитов по ссылке
Почти все типовые конфигурации на БСП (Бухгалтерия 3.0, ЗУП 3.1, УТ 11, ERP, УНФ) содержат в общем модуле ОбщегоНазначения готовые функции. Они делают маленький запрос и читают только нужные поля, а не весь объект.
| Функция | Что возвращает | Когда брать |
|---|---|---|
ЗначениеРеквизитаОбъекта(Ссылка, "Имя") | Значение одного реквизита | Нужно одно поле |
ЗначенияРеквизитовОбъекта(Ссылка, "Имя1, Имя2") | Структуру с ключами-реквизитами | Нужно несколько полей одного объекта |
ЗначенияРеквизитовОбъектов(МассивСсылок, "Имя1, Имя2") | Соответствие «ссылка → структура» | Поля по многим объектам за один запрос |
Пример:
// В серверной процедуре
Реквизиты = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Договор, "Организация, ВалютаВзаиморасчетов, Владелец");
Если ЗначениеЗаполнено(Реквизиты.Организация) Тогда
// ...
КонецЕсли;
Имена можно задавать через точку, например "Владелец.ИНН": функция сама построит соединение в запросе. Тогда ключом в структуре станет имя без точек, а при желании можно задать свой псевдоним — его описание есть в комментарии к функции в модуле.
💡 Не помните точную сигнатуру в своём релизе — откройте общий модуль ОбщегоНазначения в конфигураторе и найдите функцию поиском по модулю (Ctrl+F). Над каждой функцией БСП подробно расписаны параметры и возвращаемое значение.
Модуль ОбщегоНазначения серверный. С клиента эти функции напрямую не вызвать: сделайте серверную процедуру формы или свою функцию в серверном модуле с флажком «Вызов сервера».
Дополнительные реквизиты и сведения
В типовых конфигурациях «свойствами» часто называют дополнительные реквизиты, которые пользователь добавил сам. Через точку их не прочитать — в метаданных объекта их нет. Хранятся они в двух местах:
- 📁 дополнительные реквизиты — в табличной части
ДополнительныеРеквизитысамого объекта (колонкиСвойствоиЗначение); - 📁 дополнительные сведения
— в регистре сведений
ДополнительныеСведения(измеренияОбъектиСвойство, ресурсЗначение).
Для их чтения в БСП есть модуль УправлениеСвойствами, функция ЗначенияСвойств() возвращает таблицу значений по списку объектов. Либо читайте напрямую запросом:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДопРеквизиты.Значение КАК Значение
|ИЗ
| Справочник.Номенклатура.ДополнительныеРеквизиты КАК ДопРеквизиты
|ГДЕ
| ДопРеквизиты.Ссылка = &Ссылка
| И ДопРеквизиты.Свойство = &Свойство";
Запрос.УстановитьПараметр("Ссылка", Номенклатура);
Запрос.УстановитьПараметр("Свойство", Свойство);
Выборка = Запрос.Выполнить().Выбрать();
Значение = ?(Выборка.Следующий(), Выборка.Значение, Неопределено);
Свойство здесь — элемент плана видов характеристик ДополнительныеРеквизитыИСведения. Найти его можно по полю Имя или по наименованию.
Значение свойства через запрос
Запрос выгоднее всего, когда полей много или объектов много. Разыменование в запросе тоже пишется через точку, но превращается в соединение таблиц на стороне СУБД.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Реализация.Ссылка КАК Документ,
| Реализация.Контрагент.ИНН КАК ИНН,
| ПРЕДСТАВЛЕНИЕ(Реализация.Договор) КАК Договор
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Ссылка В (&Документы)";
Запрос.УстановитьПараметр("Документы", МассивДокументов);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Строка(Выборка.Документ) + ": " + Выборка.ИНН);
КонецЦикла;
Нюансы, о которых стоит помнить:
- 🔸 если реквизит пустой,
Контрагент.ИННв запросе вернётNULL, а не ошибку — оберните вЕСТЬNULL(..., ""), если результат пойдёт в расчёты или строки; - 🔸 для составного типа длинные цепочки точек порождают соединения со всеми таблицами из состава типа — уточняйте тип через
ВЫРАЗИТЬ(Поле КАК Справочник.Контрагенты); - 🔸
ПРЕДСТАВЛЕНИЕ()берите только для вывода: сравнивать по нему нельзя.
Свойства коллекций: Структура, Соответствие, Массив, таблица значений
У каждой коллекции свой способ безопасного чтения. Путать их — источник половины «загадочных» ошибок в обработках.
| Коллекция | Чтение | Безопасная проверка | Если ключа или индекса нет |
|---|---|---|---|
Структура | Стр.Ключ, Стр["Ключ"] | Стр.Свойство("Ключ", Значение) | Ошибка «Поле объекта не обнаружено» |
ФиксированнаяСтруктура | так же | так же | так же |
Соответствие | Соотв[Ключ], Соотв.Получить(Ключ) | сравнить результат с Неопределено | Вернёт Неопределено |
Массив | Массив[Индекс], индекс с нуля | Индекс < Массив.Количество() | Ошибка «Индекс находится за границами диапазона» |
ТаблицаЗначений | Строка.Колонка, Строка["Колонка"] | Таблица.Колонки.Найти("Колонка") <> Неопределено | Ошибка «Поле объекта не обнаружено» |
Метод Свойство() у структуры одновременно проверяет наличие ключа и кладёт значение во второй параметр:
Параметры = Новый Структура("Склад, Дата", Склад, ТекущаяДата());
Значение = Неопределено;
Если Параметры.Свойство("Организация", Значение) И ЗначениеЗаполнено(Значение) Тогда
Организация = Значение;
КонецЕсли;
У соответствия ключом может быть ссылка, число или строка. Если в соответствии реально хранится Неопределено как значение, отличить его от отсутствующего ключа через Получить() нельзя — в таком случае храните значения в структуре-обёртке или проверяйте перебором.
Последний элемент массива берут по индексу Массив.Количество() - 1. Для пустого массива это -1, поэтому проверку на количество делайте до обращения.
Имя свойства неизвестно заранее: метаданные и ЗаполнитьЗначенияСвойств
В 1С нет рефлексии в привычном для Java или C# смысле, но её роль играют три инструмента.
1. Перебор реквизитов через метаданные
МетаОбъект = Ссылка.Метаданные();
Для Каждого Реквизит Из МетаОбъект.Реквизиты Цикл
Сообщить(Реквизит.Имя + " = " + Строка(Ссылка[Реквизит.Имя]));
КонецЦикла;
Реквизиты содержит только реквизиты, добавленные в конфигурации. Код, наименование, дата, номер, пометка удаления лежат в коллекции СтандартныеРеквизиты, табличные части — в ТабличныеЧасти.
2. Проверка, есть ли такой реквизит у объекта
Через метаданные:
ЕстьРеквизит = (Ссылка.Метаданные().Реквизиты.Найти("Склад") <> Неопределено);
Приём практиков для любых объектов, включая строки табличных частей и данные формы, — проверка через структуру с маркером:
Функция ЕстьСвойство(Источник, ИмяСвойства) Экспорт
Маркер = Новый УникальныйИдентификатор;
Проверка = Новый Структура(ИмяСвойства, Маркер);
ЗаполнитьЗначенияСвойств(Проверка, Источник);
Возврат Проверка[ИмяСвойства] <> Маркер;
КонецФункции
ЗаполнитьЗначенияСвойств() копирует только совпадающие по имени свойства и молча пропускает отсутствующие. Если маркер остался на месте — свойства у источника нет.
3. Копирование группы свойств одним вызовом
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаИсточник, "Номенклатура, Количество, Цена");
Третий параметр — список свойств через запятую, четвёртый — исключения. Это быстрее и короче, чем десяток строк присваивания, и не падает, если в приёмнике нет какого-то поля.
Вычислить() и Выполнить() ради чтения свойства не нужны. Скобки Объект[Имя] решают ту же задачу, а выполнение строки как кода в безопасном режиме и в облачных базах запрещено или ограничено.
Ошибки при чтении свойств и как их снять
Как читать свойства быстро: практика для обработок и отчётов
Медленный код почти всегда выглядит невинно — точка внутри цикла:
// Плохо: на каждой итерации читается объект контрагента
Для Каждого Строка Из ТаблицаДокументов Цикл
Строка.ИНН = Строка.Документ.Контрагент.ИНН;
КонецЦикла;
На тысяче документов это тысячи чтений объектов из базы. Правильнее одним вызовом:
Документы = ТаблицаДокументов.ВыгрузитьКолонку("Документ");
Данные = ОбщегоНазначения.ЗначенияРеквизитовОбъектов(Документы, "Контрагент.ИНН");
Для Каждого Строка Из ТаблицаДокументов Цикл
Строка.ИНН = Данные[Строка.Документ].КонтрагентИНН;
КонецЦикла;
Ключ в структуре здесь — имя без точки. Если сомневаетесь в формате ключа в вашем релизе БСП, задайте псевдоним явно или выберите данные обычным запросом с В (&Документы) — результат будет тот же.
Короткий свод правил:
- Одно-два поля по ссылке —
ЗначениеРеквизитаОбъекта()илиЗначенияРеквизитовОбъекта(). - Поля по многим объектам — один запрос или
ЗначенияРеквизитовОбъектов(), но не точка в цикле. - Объект уже получен через
ПолучитьОбъект()и всё равно будет записываться — читайте прямо из него, лишний запрос не нужен. - На клиенте — только реквизиты формы и данные, переданные с сервера. Ссылки не разыменовываются.
- Имя поля из переменной —
Объект[Имя], а неВычислить(). - Реквизиты, которые нужны форме постоянно, кладите в реквизиты формы при создании на сервере, чтобы не гонять серверные вызовы.