Разработка и Конфигурация · 11 мин
Как обратиться к реквизиту справочника из документа в 1С
Как обратиться к реквизиту справочника из документа в 1С: синтаксис «через точку», метод ПолучитьОбъект(), запросы, типичные ошибки и примеры кода для 8.2 и 8.3.
В документах 1С реквизиты часто хранят ссылки на элементы справочников: контрагента, номенклатуру, склад, договор. Сама ссылка — это только указатель, а данные элемента (наименование, ИНН, ставка НДС, единица измерения) лежат в базе отдельно. Чтобы получить их в коде модуля документа, нужно обратиться к реквизиту справочника через ссылку — и способов для этого несколько, у каждого свои ограничения по производительности и контексту выполнения.
Базовый приём выглядит так: СтавкаНДС = Номенклатура.СтавкаНДС; — платформа неявно обращается к базе и возвращает значение реквизита. Это работает и в обычном, и в управляемом приложении, но при массовой обработке строк табличной части такой подход порождает отдельный запрос на каждое обращение и замедляет проведение документа.
Ниже разобраны основные способы обращения к реквизитам справочника из модуля документа: обращение «через точку», работа через объект справочника, выборка запросом, обращение из табличной части и на клиенте в управляемых формах, а также типичные ошибки и способы проверить результат.
Обращение «через точку» — самый простой способ
Если реквизит документа имеет тип «СправочникСсылка», любой его реквизит доступен через точку. Платформа при первом обращении выполняет скрытый запрос к базе и кэширует данные объекта в рамках текущего обращения.
// В модуле объекта документа
ИННКонтрагента = Контрагент.ИНН;
НаименованиеСклада = Склад.Наименование;
Ставка = Номенклатура.СтавкаНДС;
Разыменование работает и по цепочке: Контрагент.ОсновнойДоговор.ВалютаВзаиморасчетов. Каждый новый уровень — потенциально отдельное обращение к базе, поэтому длинные цепочки в циклах нежелательны.
Важная особенность: если ссылка пустая, обращение к реквизиту вернёт значение по умолчанию (пустую строку, ноль, пустую дату), а не ошибку. Поэтому перед обращением к реквизитам стоит проверять заполненность ссылки через ЗначениеЗаполнено(Контрагент), иначе логика может молча отработать на пустых данных.
Обращение к реквизитам из табличной части документа
Самая частая задача — в обработчике изменения строки товаров подтянуть данные номенклатуры: единицу измерения, ставку НДС, артикул. В модуле формы документа это делается напрямую через данные строки.
&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
СтрокаТЧ = Элементы.Товары.ТекущиеДанные;
// Так на клиенте делать НЕЛЬЗЯ — разыменование недоступно:
// СтрокаТЧ.СтавкаНДС = СтрокаТЧ.Номенклатура.СтавкаНДС;
КонецПроцедуры
На тонком клиенте разыменование ссылки в общем случае недоступно: у клиента нет прямого доступа к базе. Правильный вариант — вызвать серверную процедуру, которая вернёт нужные значения, либо использовать предопределённые данные формы.
&НаСервереБезКонтекста
Функция ДанныеНоменклатуры(Номенклатура)
Результат = Новый Структура;
Результат.Вставить("СтавкаНДС", Номенклатура.СтавкаНДС);
Результат.Вставить("ЕдиницаИзмерения", Номенклатура.ЕдиницаИзмерения);
Возврат Результат;
КонецФункции
Если обработка выполняется в модуле объекта (например, при проведении), разыменование в цикле по строкам работает, но порождает запрос на каждую строку. Для документов с десятками и сотнями строк это критично — используйте запрос, описанный ниже.
Выборка реквизитов справочника запросом
Запрос — оптимальный способ, когда нужно получить реквизиты справочника сразу для всех строк документа. Ссылка из табличной части соединяется с таблицей справочника, и все данные приходят одним обращением к СУБД.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Номенклатура КАК Номенклатура,
| Товары.Количество КАК Количество,
| Спр.СтавкаНДС КАК СтавкаНДС,
| Спр.ЕдиницаИзмерения КАК ЕдиницаИзмерения
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК Товары
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Спр
| ПО Товары.Номенклатура = Спр.Ссылка
|ГДЕ
| Товары.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Выборка = Запрос.Выполнить().Выбрать();
Есть и более короткий синтаксис: в запросе допустимо обращение к реквизитам через точку прямо в полях выборки — Товары.Номенклатура.СтавкаНДС. Платформа сама построит соединение. Это удобно для одного-двух реквизитов; при большом их числе явное соединение читается лучше и даёт больше контроля.
В модуле объекта вместо параметра по ссылке можно передать саму табличную часть через Выгрузить() и использовать её как временную таблицу — так запрос не зависит от того, записан ли документ в базу.
Запрос.Текст = "ВЫБРАТЬ Т.Номенклатура, Т.Номенклатура.СтавкаНДС КАК СтавкаНДС ИЗ Документ.РеализацияТоваровУслуг.Товары КАК Т ГДЕ Т.Ссылка = &Ссылка";Изменение реквизита справочника из документа
Через точку можно только читать данные — присвоение вида Контрагент.ИНН = "7707083893" вызовет ошибку, потому что ссылка не даёт доступа к записи. Для изменения нужно получить объект справочника методом ПолучитьОбъект(), изменить реквизиты и записать.
Если ЗначениеЗаполнено(Контрагент) Тогда
КонтрагентОбъект = Контрагент.ПолучитьОбъект();
КонтрагентОбъект.Комментарий = "Обновлено из документа " + Номер;
КонтрагентОбъект.Записать();
КонецЕсли;
Такой подход требует осторожности. Запись объекта справочника из обработчика проведения документа создаёт риски: блокировки при параллельной работе пользователей, срабатывание подписок на события справочника, нарушение целостности при откате транзакции. Если документ отменит проведение, изменения в справочнике автоматически не откатятся, если запись не выполняется в той же транзакции.
Правило простое: изменять справочник из документа стоит только при явной необходимости и желательно в транзакции проведения, а для хранения изменяемых документами данных лучше использовать регистры сведений, подчинённые документу-регистратору.
Обращение на клиенте в управляемых формах
В управляемом приложении код делится на клиентский и серверный. Разыменование ссылки (Ссылка.Реквизит) доступно только на сервере — на тонком и веб-клиенте оно приведёт к ошибке исполнения. Это главное отличие от обычного приложения, где весь код выполнялся в одном контексте.
Типовые варианты решения:
- серверная функция без контекста, принимающая ссылку и возвращающая структуру с нужными реквизитами;
- общий серверный модуль с функцией получения данных справочника — удобно при повторном использовании в разных документах;
- кэширование на клиенте: если реквизиты одних и тех же элементов запрашиваются часто, сохраняйте их в соответствие на время работы формы.
Для реквизитов, которые нужны постоянно (например, ставка НДС номенклатуры), рассмотрите альтернативу: хранить копию значения в реквизите табличной части и заполнять его при изменении номенклатуры. Тогда при проведении не потребуется ни разыменование, ни запрос — данные уже в документе, и отчёты по документам построить проще.
Проверка результата и отладка
Убедиться, что обращение к реквизиту работает правильно, можно несколькими способами. Самый быстрый — отладчик: поставьте точку останова в обработчике и посмотрите значение выражения Контрагент.ИНН в табло. Если значение пустое при заполненной ссылке — проверьте, заполнен ли реквизит у самого элемента справочника.
Второй способ — консоль запросов или временное сообщение: Сообщить(Контрагент.ИНН);. Для массовой обработки полезно сравнить результат запроса с тем, что возвращает разыменование, — расхождения обычно означают ошибку в условиях соединения.
Отдельный случай — «битые» ссылки, когда элемент справочника удалён, а ссылка в документе осталась. Обращение к реквизиту такой ссылки вернёт пустые значения, а представление ссылки будет содержать текст об удалённом объекте. Проверить это можно запросом с соединением: для битых ссылок поля справочника будут NULL.
Как проверить результат
Возвращается ожидаемое значение из элемента справочника
Пустое значение при заполненной ссылке — реквизит не заполнен у элемента
ЗначениеЗаполнено(Ссылка) = Истина, элемент открывается
Ссылка пустая или ведёт на удалённый объект
Проведение занимает примерно одинаковое время при росте числа строк
Время растёт непропорционально — вероятно разыменование в цикле
Поля справочника заполнены для всех строк
NULL в полях — битая ссылка или ошибка соединения
Частые вопросы
Можно ли обратиться к реквизиту справочника без запроса к базе?
Полностью избежать чтения из базы нельзя — данные элемента хранятся там. Но можно минимизировать обращения: платформа кэширует данные объекта в рамках одного серверного вызова, поэтому повторное обращение к другому реквизиту той же ссылки обычно не порождает новый запрос. Для массовой обработки используйте один запрос вместо цикла с разыменованием.
Почему Ссылка.Реквизит возвращает пустое значение?
Три основные причины: сама ссылка пустая, у элемента справочника не заполнен этот реквизит, либо ссылка «битая» — элемент был удалён. Проверьте ссылку через ЗначениеЗаполнено() и откройте элемент справочника в режиме предприятия.
Как обратиться к табличной части справочника из документа?
Через точку табличная часть недоступна. Нужно получить объект: Объект = Ссылка.ПолучитьОбъект(), затем перебрать Объект.ИмяТабличнойЧасти в цикле. Альтернатива — запрос к таблице Справочник.ИмяСправочника.ИмяТабличнойЧасти с отбором по ссылке.
Работает ли разыменование в тонком клиенте?
Нет. На тонком и веб-клиенте прямое обращение Ссылка.Реквизит вызовет ошибку, так как клиент не имеет доступа к базе. Вынесите получение данных в серверную процедуру или функцию общего серверного модуля, возвращающую структуру со значениями.
Что быстрее: разыменование или запрос?
Для одного-двух обращений разницы практически нет. Запрос выигрывает при обработке множества строк: одно обращение к СУБД вместо запроса на каждую строку. Чем больше строк в табличной части, тем заметнее преимущество запроса.
Можно ли изменить реквизит справочника при проведении документа?
Технически да — через ПолучитьОбъект() и Записать(). Но это создаёт риски блокировок и рассогласования данных при отмене проведения. Безопаснее хранить изменяемую документами информацию в регистрах сведений, а справочник менять только при явной необходимости в транзакции проведения.
Что важно запомнить
Чтение реквизитов справочника из документа выполняется «через точку» (Ссылка.Реквизит) — но только на сервере и желательно вне циклов. Для массовой обработки строк используйте запрос с соединением по ссылке, для изменения данных — ПолучитьОбъект() с Записать() и учётом рисков блокировок. Всегда проверяйте заполненность ссылки перед обращением к реквизитам.