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