К статье
Практический материал

Разработка и Конфигурация · 11 мин

Как обратиться к реквизиту справочника из документа в 1С

Как обратиться к реквизиту справочника из документа в 1С: синтаксис «через точку», метод ПолучитьОбъект(), запросы, типичные ошибки и примеры кода для 8.2 и 8.3.

В документах 1С реквизиты часто хранят ссылки на элементы справочников: контрагента, номенклатуру, склад, договор. Сама ссылка — это только указатель, а данные элемента (наименование, ИНН, ставка НДС, единица измерения) лежат в базе отдельно. Чтобы получить их в коде модуля документа, нужно обратиться к реквизиту справочника через ссылку — и способов для этого несколько, у каждого свои ограничения по производительности и контексту выполнения.

Базовый приём выглядит так: СтавкаНДС = Номенклатура.СтавкаНДС; — платформа неявно обращается к базе и возвращает значение реквизита. Это работает и в обычном, и в управляемом приложении, но при массовой обработке строк табличной части такой подход порождает отдельный запрос на каждое обращение и замедляет проведение документа.

Ниже разобраны основные способы обращения к реквизитам справочника из модуля документа: обращение «через точку», работа через объект справочника, выборка запросом, обращение из табличной части и на клиенте в управляемых формах, а также типичные ошибки и способы проверить результат.

Обращение «через точку» — самый простой способ

Если реквизит документа имеет тип «СправочникСсылка», любой его реквизит доступен через точку. Платформа при первом обращении выполняет скрытый запрос к базе и кэширует данные объекта в рамках текущего обращения.

// В модуле объекта документа

ИННКонтрагента = Контрагент.ИНН;

НаименованиеСклада = Склад.Наименование;

Ставка = Номенклатура.СтавкаНДС;

Разыменование работает и по цепочке: Контрагент.ОсновнойДоговор.ВалютаВзаиморасчетов. Каждый новый уровень — потенциально отдельное обращение к базе, поэтому длинные цепочки в циклах нежелательны.

Важная особенность: если ссылка пустая, обращение к реквизиту вернёт значение по умолчанию (пустую строку, ноль, пустую дату), а не ошибку. Поэтому перед обращением к реквизитам стоит проверять заполненность ссылки через ЗначениеЗаполнено(Контрагент), иначе логика может молча отработать на пустых данных.

Обращение к реквизитам из табличной части документа

Самая частая задача — в обработчике изменения строки товаров подтянуть данные номенклатуры: единицу измерения, ставку НДС, артикул. В модуле формы документа это делается напрямую через данные строки.

&НаКлиенте

Процедура ТоварыНоменклатураПриИзменении(Элемент)

СтрокаТЧ = Элементы.Товары.ТекущиеДанные;

// Так на клиенте делать НЕЛЬЗЯ — разыменование недоступно:

// СтрокаТЧ.СтавкаНДС = СтрокаТЧ.Номенклатура.СтавкаНДС;

КонецПроцедуры

На тонком клиенте разыменование ссылки в общем случае недоступно: у клиента нет прямого доступа к базе. Правильный вариант — вызвать серверную процедуру, которая вернёт нужные значения, либо использовать предопределённые данные формы.

&НаСервереБезКонтекста

Функция ДанныеНоменклатуры(Номенклатура)

Результат = Новый Структура;

Результат.Вставить("СтавкаНДС", Номенклатура.СтавкаНДС);

Результат.Вставить("ЕдиницаИзмерения", Номенклатура.ЕдиницаИзмерения);

Возврат Результат;

КонецФункции

Если обработка выполняется в модуле объекта (например, при проведении), разыменование в цикле по строкам работает, но порождает запрос на каждую строку. Для документов с десятками и сотнями строк это критично — используйте запрос, описанный ниже.

Выборка реквизитов справочника запросом

Запрос — оптимальный способ, когда нужно получить реквизиты справочника сразу для всех строк документа. Ссылка из табличной части соединяется с таблицей справочника, и все данные приходят одним обращением к СУБД.

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Товары.Номенклатура КАК Номенклатура,

| Товары.Количество КАК Количество,

| Спр.СтавкаНДС КАК СтавкаНДС,

| Спр.ЕдиницаИзмерения КАК ЕдиницаИзмерения

|ИЗ

| Документ.РеализацияТоваровУслуг.Товары КАК Товары

| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Спр

| ПО Товары.Номенклатура = Спр.Ссылка

|ГДЕ

| Товары.Ссылка = &Ссылка";

Запрос.УстановитьПараметр("Ссылка", Ссылка);

Выборка = Запрос.Выполнить().Выбрать();

Есть и более короткий синтаксис: в запросе допустимо обращение к реквизитам через точку прямо в полях выборки — Товары.Номенклатура.СтавкаНДС. Платформа сама построит соединение. Это удобно для одного-двух реквизитов; при большом их числе явное соединение читается лучше и даёт больше контроля.

В модуле объекта вместо параметра по ссылке можно передать саму табличную часть через Выгрузить() и использовать её как временную таблицу — так запрос не зависит от того, записан ли документ в базу.

bsl
Запрос.Текст = "ВЫБРАТЬ Т.Номенклатура, Т.Номенклатура.СтавкаНДС КАК СтавкаНДС ИЗ Документ.РеализацияТоваровУслуг.Товары КАК Т ГДЕ Т.Ссылка = &Ссылка";

Изменение реквизита справочника из документа

Через точку можно только читать данные — присвоение вида Контрагент.ИНН = "7707083893" вызовет ошибку, потому что ссылка не даёт доступа к записи. Для изменения нужно получить объект справочника методом ПолучитьОбъект(), изменить реквизиты и записать.

Если ЗначениеЗаполнено(Контрагент) Тогда

КонтрагентОбъект = Контрагент.ПолучитьОбъект();

КонтрагентОбъект.Комментарий = "Обновлено из документа " + Номер;

КонтрагентОбъект.Записать();

КонецЕсли;

Такой подход требует осторожности. Запись объекта справочника из обработчика проведения документа создаёт риски: блокировки при параллельной работе пользователей, срабатывание подписок на события справочника, нарушение целостности при откате транзакции. Если документ отменит проведение, изменения в справочнике автоматически не откатятся, если запись не выполняется в той же транзакции.

Правило простое: изменять справочник из документа стоит только при явной необходимости и желательно в транзакции проведения, а для хранения изменяемых документами данных лучше использовать регистры сведений, подчинённые документу-регистратору.

Обращение на клиенте в управляемых формах

В управляемом приложении код делится на клиентский и серверный. Разыменование ссылки (Ссылка.Реквизит) доступно только на сервере — на тонком и веб-клиенте оно приведёт к ошибке исполнения. Это главное отличие от обычного приложения, где весь код выполнялся в одном контексте.

Типовые варианты решения:

  • серверная функция без контекста, принимающая ссылку и возвращающая структуру с нужными реквизитами;
  • общий серверный модуль с функцией получения данных справочника — удобно при повторном использовании в разных документах;
  • кэширование на клиенте: если реквизиты одних и тех же элементов запрашиваются часто, сохраняйте их в соответствие на время работы формы.

Для реквизитов, которые нужны постоянно (например, ставка НДС номенклатуры), рассмотрите альтернативу: хранить копию значения в реквизите табличной части и заполнять его при изменении номенклатуры. Тогда при проведении не потребуется ни разыменование, ни запрос — данные уже в документе, и отчёты по документам построить проще.

Типичные ошибки
01
Разыменование ссылки на клиенте в управляемой форме — ошибка исполнения; перенесите обращение на сервер
02
Обращение к реквизиту пустой ссылки без проверки — молча возвращаются пустые значения и логика искажается
03
Присвоение значения через точку (Ссылка.Реквизит = ...) — ошибка; для записи нужен ПолучитьОбъект()
04
Разыменование в цикле по сотням строк — резкое замедление проведения; используйте запрос
05
Обращение к реквизиту, которого нет в справочнике конфигурации, — ошибка компиляции модуля

Проверка результата и отладка

Убедиться, что обращение к реквизиту работает правильно, можно несколькими способами. Самый быстрый — отладчик: поставьте точку останова в обработчике и посмотрите значение выражения Контрагент.ИНН в табло. Если значение пустое при заполненной ссылке — проверьте, заполнен ли реквизит у самого элемента справочника.

Второй способ — консоль запросов или временное сообщение: Сообщить(Контрагент.ИНН);. Для массовой обработки полезно сравнить результат запроса с тем, что возвращает разыменование, — расхождения обычно означают ошибку в условиях соединения.

Отдельный случай — «битые» ссылки, когда элемент справочника удалён, а ссылка в документе осталась. Обращение к реквизиту такой ссылки вернёт пустые значения, а представление ссылки будет содержать текст об удалённом объекте. Проверить это можно запросом с соединением: для битых ссылок поля справочника будут NULL.

Контрольные точки

Как проверить результат

Значение реквизита в отладчике
Норма

Возвращается ожидаемое значение из элемента справочника

Тревожный признак

Пустое значение при заполненной ссылке — реквизит не заполнен у элемента

Ссылка на справочник
Норма

ЗначениеЗаполнено(Ссылка) = Истина, элемент открывается

Тревожный признак

Ссылка пустая или ведёт на удалённый объект

Скорость проведения документа
Норма

Проведение занимает примерно одинаковое время при росте числа строк

Тревожный признак

Время растёт непропорционально — вероятно разыменование в цикле

Результат запроса
Норма

Поля справочника заполнены для всех строк

Тревожный признак

NULL в полях — битая ссылка или ошибка соединения

Частые вопросы

Можно ли обратиться к реквизиту справочника без запроса к базе?

Полностью избежать чтения из базы нельзя — данные элемента хранятся там. Но можно минимизировать обращения: платформа кэширует данные объекта в рамках одного серверного вызова, поэтому повторное обращение к другому реквизиту той же ссылки обычно не порождает новый запрос. Для массовой обработки используйте один запрос вместо цикла с разыменованием.

Почему Ссылка.Реквизит возвращает пустое значение?

Три основные причины: сама ссылка пустая, у элемента справочника не заполнен этот реквизит, либо ссылка «битая» — элемент был удалён. Проверьте ссылку через ЗначениеЗаполнено() и откройте элемент справочника в режиме предприятия.

Как обратиться к табличной части справочника из документа?

Через точку табличная часть недоступна. Нужно получить объект: Объект = Ссылка.ПолучитьОбъект(), затем перебрать Объект.ИмяТабличнойЧасти в цикле. Альтернатива — запрос к таблице Справочник.ИмяСправочника.ИмяТабличнойЧасти с отбором по ссылке.

Работает ли разыменование в тонком клиенте?

Нет. На тонком и веб-клиенте прямое обращение Ссылка.Реквизит вызовет ошибку, так как клиент не имеет доступа к базе. Вынесите получение данных в серверную процедуру или функцию общего серверного модуля, возвращающую структуру со значениями.

Что быстрее: разыменование или запрос?

Для одного-двух обращений разницы практически нет. Запрос выигрывает при обработке множества строк: одно обращение к СУБД вместо запроса на каждую строку. Чем больше строк в табличной части, тем заметнее преимущество запроса.

Можно ли изменить реквизит справочника при проведении документа?

Технически да — через ПолучитьОбъект() и Записать(). Но это создаёт риски блокировок и рассогласования данных при отмене проведения. Безопаснее хранить изменяемую документами информацию в регистрах сведений, а справочник менять только при явной необходимости в транзакции проведения.

Итог

Что важно запомнить

Чтение реквизитов справочника из документа выполняется «через точку» (Ссылка.Реквизит) — но только на сервере и желательно вне циклов. Для массовой обработки строк используйте запрос с соединением по ссылке, для изменения данных — ПолучитьОбъект() с Записать() и учётом рисков блокировок. Всегда проверяйте заполненность ссылки перед обращением к реквизитам.