Как прочитать реквизит объекта в 1С: точка, запрос и БСП
Содержание
Чаще всего реквизит читают через точку: Ссылка.ИмяРеквизита. Если у вас в переменной Контрагент лежит ссылка на элемент справочника, то его ИНН вы получите выражением Контрагент.ИНН, а полное наименование — Контрагент.НаименованиеПолное. Это основной способ, и для одиночного чтения он правильный.
Проблема начинается, когда таких обращений много или они внутри цикла: каждая точка к реквизиту через ссылку — это отдельное чтение из базы. Ниже разберу, как получить значение реквизита объекта в 1С 8.3 правильно в каждой ситуации: от точечной нотации до запроса и штатных функций библиотеки стандартных подсистем, с проверкой типа и разбором ошибок, на которых спотыкаются даже опытные разработчики.
Точечная нотация: обращение к реквизиту через точку
Точка после ссылки или объекта возвращает значение одноимённого реквизита. Синтаксис одинаков для справочников, документов, регистров и перечислений:
Значение = Номенклатура.Артикул;
Валюта = Договор.ВалютаДокумента;
Поставщик = Поступление.Контрагент;
Точки можно выстраивать в цепочку — платформа пройдёт по ссылкам до конечного реквизита:
ИНН = Заказ.Контрагент.ИНН;
Страна = Товар.Производитель.СтранаРегистрации.Наименование;
Каждое звено цепочки Заказ.Контрагент.ИНН — это обращение к базе: сначала читается Контрагент, потом из него ИНН. Одно выражение — несколько скрытых чтений. В отчёте на тысячу строк такая цепочка внутри цикла превращается в тысячи запросов.
Точка работает и для чтения свойств системных объектов: ТекущаяДата(), Строка.НомерСтроки, Выборка.Количество. Но у реквизитов объекта есть особенность — платформа подгружает их «лениво», по первому обращению, а не целиком при получении ссылки.
Через ссылку или через объект: в чём разница
У ссылки (СправочникСсылка, ДокументСсылка) и у объекта (СправочникОбъект, ДокументОбъект) набор реквизитов один, но ведут они себя по-разному.
Чтение через ссылку — для случаев, когда нужно только прочитать:
Код = Контрагент.Код; // Контрагент здесь — ссылка
Платформа сама обращается к базе за значением реквизита. Это дёшево для одиночного чтения и не блокирует объект.
Получение объекта нужно, когда вы собираетесь менять данные или читать сразу много реквизитов:
Объект = Контрагент.ПолучитьОбъект();
Объект.Комментарий = "Проверен";
Объект.Записать();
ПолучитьОбъект() целиком считывает элемент в память — все реквизиты уже загружены, повторных обращений к базе на точку не будет.
Правило простое: нужно только прочитать одно-два значения — работайте со ссылкой. Нужно читать десятки реквизитов одного элемента или записывать — берите объект через ПолучитьОбъект().
Штатные функции БСП для чтения реквизитов
В типовых конфигурациях на библиотеке стандартных подсистем (БП 3.0, ЗУП 3.1, УТ 11, ERP) есть готовые функции общего модуля ОбщегоНазначения. Они читают реквизиты одним запросом и обрабатывают пустые ссылки:
// один реквизит
Валюта = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Договор, "ВалютаДокумента");
// несколько реквизитов за одно обращение к базе
Реквизиты = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
Контрагент, "ИНН, КПП, НаименованиеПолное");
ИНН = Реквизиты.ИНН;
КПП = Реквизиты.КПП;
ЗначенияРеквизитовОбъекта возвращает структуру, где ключи — имена реквизитов. Одно обращение к базе вместо трёх точек — это заметно быстрее в циклах и обработках. Если конфигурация на БСП, предпочитайте эти функции точечной цепочке в коде, который выполняется многократно.
Реквизиты табличных частей и строк документа
Табличная часть документа — это коллекция строк. У каждой строки свои реквизиты (колонки), читаются они той же точкой.
Перебор всех строк:
Для Каждого Строка Из Поступление.Товары Цикл
Номенклатура = Строка.Номенклатура;
Количество = Строка.Количество;
Сумма = Строка.Сумма;
КонецЦикла;
Обращение к конкретной строке по индексу (нумерация с нуля):
ПерваяСтрока = Поступление.Товары[0];
Первый = ПерваяСтрока.Номенклатура;
Полезные методы табличной части для выборки значений без ручного цикла:
| Метод | Что возвращает |
|---|---|
Товары.Количество() | число строк |
Товары.Итог("Сумма") | сумму по колонке |
Товары.Найти(Значение, "Номенклатура") | первую подходящую строку или Неопределено |
Товары.НайтиСтроки(Отбор) | массив строк по структуре отбора |
Товары.ВыгрузитьКолонку("Сумма") | массив значений одной колонки |
Товары.Выгрузить() | всю табличную часть как таблицу значений |
Пример выборки строк по условию:
Отбор = Новый Структура("Номенклатура", НужнаяНоменклатура);
НайденныеСтроки = Поступление.Товары.НайтиСтроки(Отбор);
Для Каждого Строка Из НайденныеСтроки Цикл
Сообщить(Строка.Количество);
КонецЦикла;
Найти возвращает Неопределено, если ничего не нашлось. Перед обращением к реквизиту результата проверяйте: Если Строка <> Неопределено Тогда .... Иначе получите ошибку обращения к реквизиту неопределённого значения.
Чтение реквизитов запросом: когда это быстрее точки
Когда нужно получить реквизиты у множества объектов, запрос почти всегда выигрывает у цикла с точками: он забирает все данные за одно обращение к базе.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Ссылка,
| Контрагенты.ИНН КАК ИНН,
| Контрагенты.НаименованиеПолное КАК Наименование
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.ЮридическоеФизическоеЛицо = &ВидЛица";
Запрос.УстановитьПараметр("ВидЛица",
Перечисления.ЮридическоеФизическоеЛицо.ЮридическоеЛицо);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.ИНН + " — " + Выборка.Наименование);
КонецЦикла;
В запросе точка тоже работает — ей удобно подниматься по ссылкам, но уже на стороне СУБД, без скрытых обращений из кода:
ВЫБРАТЬ
Заказы.Ссылка,
Заказы.Контрагент.ИНН КАК ИНН,
Заказы.Контрагент.ОсновнойМенеджер.Наименование КАК Менеджер
ИЗ
Документ.ЗаказПокупателя КАК Заказы
Результат запроса можно сразу выгрузить в таблицу значений:
ТаблицаКонтрагентов = Запрос.Выполнить().Выгрузить();
Когда из запроса нужно забрать единственное значение, выгружать таблицу необязательно:
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
ИНН = Выборка.ИНН;
КонецЕсли;
Ориентир по выбору способа: одно значение у одного объекта — точка через ссылку; несколько реквизитов у одного объекта — ЗначенияРеквизитовОбъекта или объект; реквизиты у многих объектов — запрос.
Реквизиты предопределённых и найденных элементов
Нередко реквизит нужно взять не из переменной, а у конкретного элемента справочника. Ссылку на него получают через менеджер:
// по коду или наименованию
Валюта = Справочники.Валюты.НайтиПоКоду("840");
Если ЗначениеЗаполнено(Валюта) Тогда
Курс = РегистрыСведений.КурсыВалют... // дальше по данным
КонецЕсли;
// предопределённый элемент — обращение по имени из конфигуратора
СтавкаНДС = Перечисления.СтавкиНДС.НДС20;
НайтиПоКоду и НайтиПоНаименованию возвращают пустую ссылку, если ничего не нашли, поэтому результат тоже проверяют ЗначениеЗаполнено(). У предопределённых элементов имена фиксированы в конфигурации — их видно в дереве объектов, на вкладке Прочее → Предопределённые.
Данные из таблицы значений и временного хранилища
Таблица значений — универсальная коллекция в памяти. Её строки читаются как строки табличной части:
Для Каждого Строка Из ТаблицаЗначений Цикл
Значение = Строка.ИмяКолонки;
КонецЦикла;
// по индексу
ПервоеЗначение = ТаблицаЗначений[0].ИмяКолонки;
// поиск, итоги, выгрузка колонки
НайденнаяСтрока = ТаблицаЗначений.Найти(Искомое, "Код");
Итог = ТаблицаЗначений.Итог("Сумма");
МассивКодов = ТаблицаЗначений.ВыгрузитьКолонку("Код");
Перебрать имена колонок, когда структура таблицы заранее неизвестна:
Для Каждого Колонка Из ТаблицаЗначений.Колонки Цикл
Сообщить(Колонка.Имя);
КонецЦикла;
Временное хранилище — не источник реквизитов, а способ передать данные между клиентом и сервером (а также между серверными вызовами). В хранилище кладут готовое значение — таблицу, структуру, двоичные данные — и получают его по адресу:
// сервер: положили
Адрес = ПоместитьВоВременноеХранилище(ТаблицаЗначений, УникальныйИдентификатор);
// позже: достали
Данные = ПолучитьИзВременногоХранилища(Адрес);
Значение = Данные[0].ИмяКолонки;
Прочитать реквизит прямо «из хранилища» нельзя: сначала получаете объект из него, потом работаете с его реквизитами обычной точкой. Если передаёте идентификатор формы, значение живёт до закрытия формы; с Новый УникальныйИдентификатор — до конца серверного вызова.
Чтение реквизита элемента на управляемой форме
На форме «реквизит объекта» и «реквизит формы» — разные вещи. Главный объект формы доступен через реквизит Объект, его реквизиты читаются точкой:
&НаКлиенте
Процедура ПриИзмененииКонтрагента(Элемент)
ТекущийКонтрагент = Объект.Контрагент;
КонецПроцедуры
Значение, которое пользователь видит в поле ввода, — это именно реквизит формы, а не элемент управления. Элемент (Элементы.ИмяПоля) отвечает за отображение: доступность, видимость, заголовок. Данные лежат в связанном с ним реквизите.
Строку табличной части, на которой стоит курсор, берут через текущие данные таблицы:
&НаКлиенте
Процедура ТоварыПриАктивизацииСтроки(Элемент)
ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
Если ТекущаяСтрока <> Неопределено Тогда
Номенклатура = ТекущаяСтрока.Номенклатура;
КонецЕсли;
КонецПроцедуры
Чтобы на сервере поработать с реквизитом формы как с полноценным объектом (например, выгрузить таблицу формы в таблицу значений), применяют преобразование данных формы:
&НаСервере
Процедура ЗаполнитьНаСервере()
ТаблицаЗначений = ДанныеФормыВЗначение(Товары, Тип("ТаблицаЗначений"));
Итог = ТаблицаЗначений.Итог("Сумма");
КонецПроцедуры
ТекущиеДанные возвращают Неопределено, когда в таблице нет выделенной строки (список пуст или курсор снят). Проверяйте результат перед обращением к его реквизитам.
Проверка типа и приведение значения реквизита
Реквизит может быть составного типа (например, «Контрагент или Физлицо») или оказаться пустым. Прежде чем идти по нему дальше, тип стоит проверить.
Определение типа значения:
Если ТипЗнч(Значение) = Тип("СправочникСсылка.Контрагенты") Тогда
ИНН = Значение.ИНН;
КонецЕсли;
Проверка на заполненность и на пустую ссылку:
Если ЗначениеЗаполнено(Контрагент) Тогда
// ссылка не пустая и не Неопределено
КонецЕсли;
Если НЕ Контрагент.Пустая() Тогда
Код = Контрагент.Код;
КонецЕсли;
ЗначениеЗаполнено() удобна тем, что одинаково отрабатывает и пустую ссылку, и Неопределено, и пустую строку, и нулевую дату.
Приведение к нужному типу делают явными функциями, а не в надежде на автоматику:
ЧислоСуммы = Число(СтрокаСуммы);
СтрокаКода = Строка(Контрагент.Код);
ДатаДок = Дата(ЗначениеДаты);
Автоматического приведения числа к строке при конкатенации в 1С нет — выражение "Итог: " + 1500 вызовет ошибку типов. Приводите явно: "Итог: " + Формат(1500, "ЧГ=0") или через СтрШаблон("Итог: %1", 1500).
Частые ошибки при чтении реквизитов и отладка
Разберём, что именно ломается и как это увидеть.
Неопределено. Частый случай — результат Найти() не проверили на Неопределено.ЗначениеЗаполнено() до чтения.Ссылка.Реквизит.Реквизит внутри цикла на много строк. Каждая строка — несколько запросов к базе. Перенесите чтение в запрос или используйте ЗначенияРеквизитовОбъекта.Инструменты отладки, которыми это ловят:
Сообщить(Значение)илиСообщить(ТипЗнч(Значение))— быстро посмотреть, что в переменной и какого она типа.- Точка останова (F9) и пошаговое выполнение (F11) в конфигураторе — остановить код перед проблемной строкой.
- Табло и окно «Вычислить выражение» (Shift+F9) в режиме отладки — раскрыть ссылку и увидеть все её реквизиты и значения прямо во время остановки.
ПредставлениеОшибки(ИнформацияОбОшибке())в блокеПопытка … Исключение— получить понятный текст сбоя вместо аварийного завершения.
Попытка
Значение = Объект.СомнительныйРеквизит;
Исключение
Сообщить(ПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
Если не уверены, как точно называется реквизит, не угадывайте в коде — откройте объект в дереве конфигурации (Справочники → ваш справочник → ветка Реквизиты) и возьмите имя оттуда. Регистр в именах реквизитов не важен, но опечатка в букве — это ошибка «Поле объекта не обнаружено».
Частые вопросы
Чем отличается чтение реквизита через ссылку от чтения через объект?
ПолучитьОбъект() считывает элемент в память полностью, поэтому выгоден, когда нужно много реквизитов сразу или запись.Почему обращение к реквизиту в цикле тормозит отчёт?
ЗначенияРеквизитовОбъекта, а в цикле уже работайте с готовой выборкой.Что вернёт точка, если ссылка пустая?
ЗначениеЗаполнено() или Пустая(), а не по факту пустого результата.Как прочитать реквизит, если он составного типа?
ТипЗнч(Значение) = Тип("..."), затем обращайтесь к реквизитам этого типа. У разных типов составного реквизита наборы реквизитов отличаются, поэтому без проверки можно получить «Поле объекта не обнаружено».Можно ли получить реквизит напрямую из временного хранилища?
ПолучитьИзВременногоХранилища, а затем читайте реквизиты полученного объекта или строки таблицы обычной точкой.