Обход реквизитов объекта в 1С: получение данных и привилегированный режим
Содержание
За фразой «обойти реквизиты объекта» в 1С почти всегда стоит одна из двух задач. Либо получить значение реквизита, не загружая в память весь объект, — ради скорости. Либо прочитать данные, которые закрыты правами доступа, — из служебного кода. Это разные вещи, и путать их опасно.
Короткий ориентир: значение одного реквизита по ссылке берут функцией ОбщегоНазначения.ЗначениеРеквизитаОбъекта, несколько — ЗначенияРеквизитовОбъекта, а доступ в обход RLS и ролей даёт УстановитьПривилегированныйРежим(Истина) на сервере. Ниже разберём каждый приём, границы его применения и то, чем оборачивается небрежный обход.
Статья для платформы 8.3 (интерфейс «Такси»). Код универсален и не зависит от конфигурации, пути в примерах даны для типовых БП 3.0, ЗУП 3.1 и УТ 11.
Что значит «обойти реквизиты» и когда это оправдано
Прямое обращение Ссылка.Склад выглядит просто, но за точкой скрывается чтение всего объекта в кэш. Если нужен один реквизит из документа с двадцатью табличными частями, платформа всё равно поднимет объект целиком. На выборке в тысячи строк это заметная потеря.
Обход оправдан в трёх ситуациях:
- 📉 Производительность. Нужны одно-два поля, а объект тяжёлый. Запрос или специальная функция читают только то, что просили.
- 🔐 Служебная логика. Фоновое задание, регламентная операция или сервисный расчёт обязаны прочитать данные независимо от прав текущего пользователя.
- 🧩 Универсальный код. Обработчик не знает заранее имя реквизита — оно приходит строкой или вычисляется по метаданным.
А вот чего обход делать не должен: показывать пользователю то, что скрыто от него настройками прав. Технически это возможно, по сути — дыра в безопасности. Разделяйте «код читает данные для своих нужд» и «данные уходят на экран в обход ограничений».
Получить реквизит без чтения всего объекта
Самый аккуратный способ — функции из Библиотеки стандартных подсистем (БСП). Они выполняют одиночный запрос и возвращают только значение, не трогая объект целиком.
Один реквизит:
Склад = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаДокумент, "Склад");
Несколько сразу — одним запросом вместо нескольких обращений:
Реквизиты = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
СсылкаНаДокумент, "Склад, Организация, Ответственный");
Склад = Реквизиты.Склад;
Орг = Реквизиты.Организация;
Функция возвращает структуру, ключи — имена запрошенных реквизитов. Второй вариант предпочтителен всегда, когда полей больше одного: он экономит обращения к базе.
Обе функции выполняются на сервере — из клиентского кода их вызывают через серверный метод или общий серверный модуль. Если передать пустую ссылку, вернётся Неопределено: отдельной проверки «ссылка заполнена» это не заменяет, но от аварийного завершения спасает. Имя реквизита передаётся как есть, с учётом регистра метаданных.
Если БСП в конфигурации нет, тот же результат даёт обычный запрос: ВЫБРАТЬ Склад, Организация ИЗ Документ.РеализацияТоваровУслуг ГДЕ Ссылка = &Ссылка. Это и есть то, что делают функции БСП внутри.
Через точку реквизит можно получить и по «цепочке»: Ссылка.Контрагент.ОсновнойБанковскийСчёт.Банк. Удобно, но каждое звено — отдельное чтение объекта в кэш. Если цепочка длинная, а вызовов много, соберите те же данные одним запросом с соединениями: платформа возьмёт всё за один проход.
Когда уместно именно чтение через точку (Ссылка.Склад)? Когда реквизитов нужно много и объект всё равно понадобится дальше. Кэш объектов живёт до изменения объекта, и повторные обращения к той же ссылке уже не идут в базу. Для разовой выборки одного поля точка — проигрышный вариант.
Доступ к реквизиту по его имени через метаданные
Если имя реквизита заранее неизвестно, к нему обращаются по строке. У объекта (не у ссылки) работает индексное обращение:
ИмяРеквизита = "Комментарий";
Значение = ОбъектДокумента[ИмяРеквизита];
Это и есть «обход» статического имени: код не привязан к конкретному реквизиту. На таком приёме строят универсальные обработки — заполнение по правилам, выгрузки, сравнение версий.
Чтобы перебрать все реквизиты объекта, идут по коллекции метаданных:
МетаданныеДок = Метаданные.Документы.РеализацияТоваровУслуг;
Для Каждого Реквизит Из МетаданныеДок.Реквизиты Цикл
Значение = ОбъектДокумента[Реквизит.Имя];
// обработка Значение
КонецЦикла;
Коллекция .Реквизиты содержит только собственные реквизиты. Стандартные (Номер, Дата, Ссылка, Проведён) в неё не входят — их добавляют отдельно, если нужны. Табличные части лежат в коллекции .ТабличныеЧасти, у каждой свои .Реквизиты.
На этом приёме держится большинство универсальных обработок. Типовой сценарий — скопировать значения одноимённых реквизитов из одного объекта в другой:
Для Каждого Реквизит Из Метаданные.Документы.РеализацияТоваровУслуг.Реквизиты Цикл
Имя = Реквизит.Имя;
ОбъектПриёмник[Имя] = ОбъектИсточник[Имя];
КонецЦикла;
Код не знает состава реквизитов и не сломается, если в конфигурацию добавят новый, — он подхватит его автоматически. Платой за гибкость будет скорость: обращение по имени каждый раз ищет реквизит в структуре объекта, тогда как статическое Объект.Склад компилируется заранее.
Проверяйте, что реквизит с таким именем вообще есть, прежде чем обращаться по строке из внешнего источника. Обращение по несуществующему имени вызывает ошибку выполнения. Для проверки сверяйтесь с коллекцией метаданных, а не с предположением.
Через метаданные удобно собирать и состав реквизитов для формы или запроса, не перечисляя их руками. Но помните: это рефлексия, она медленнее прямого обращения. На горячих участках имена лучше знать заранее.
Обход прав доступа: привилегированный режим и RLS
Права в 1С работают на двух уровнях. Роли задают, какие объекты и действия доступны. RLS (ограничение доступа на уровне записей) сужает видимость до конкретных строк — например, пользователь видит только свою организацию.
Когда служебному коду нужно прочитать данные независимо от этих ограничений, включают привилегированный режим:
УстановитьПривилегированныйРежим(Истина);
// здесь права ролей и RLS не проверяются
Данные = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Сумма");
УстановитьПривилегированныйРежим(Ложь);
Что важно знать про этот режим:
- ▶️ Работает только на сервере. На клиенте вызов бессмысленен.
- 🔁 Включения вложенные: сколько раз включили, столько и нужно выключить. Поэтому выключение ставят парно, рядом с включением.
- 👁️ Проверить текущее состояние можно функцией
ПривилегированныйРежим()— она вернётИстина, если режим активен.
Где это законно и нужно: фоновые задания, обмен данными, служебные расчёты, формирование печатных форм по регламенту — там, где код действует «от имени системы», а не показывает пользователю лишнее. Пример: регламентное задание пересчитывает скидки по всем организациям, а запустивший его пользователь по RLS видит только одну. Без привилегированного режима задание прочитает данные частично и посчитает неверно.
Границу удобно держать в голове так: привилегированный режим — для чтения и записи внутри алгоритма, результат которого не раскрывает закрытые данные пользователю. Как только значение, полученное в этом режиме, попадает в реквизит формы, отчёт или сообщение, вы вышли за границу.
Где это недопустимо: выдавать в интерфейс данные, которые пользователь по правам видеть не должен. Привилегированный режим не «расширяет права пользователя» — он отключает проверку. Ответственность за то, что уйдёт на экран, целиком на разработчике.
Обход контроля при записи объекта
Отдельная задача — записать объект, не запуская его бизнес-логику: проверки заполнения, обработчики ПередЗаписью, контроль проведения. Для этого перед записью ставят флаг загрузки:
Объект.ОбменДанными.Загрузка = Истина;
Объект.Записать();
В режиме загрузки платформа пропускает часть проверок и не инициирует стандартный сценарий проведения. Так загружают данные из обменов и исправляют записи техническими обработками.
Это мощный и опасный приём. Пропуская ПередЗаписью, вы можете записать объект в состоянии, которое обычная логика никогда бы не допустила: неверные остатки, несведённые движения, нарушенные связи. Применяйте только когда точно понимаете, что именно отключаете, и обязательно на копии базы сначала.
Проведение в обход формы делают методом Записать(РежимЗаписиДокумента.Проведение) — но это уже штатный путь, а не обход. Полное отключение логики через ОбменДанными.Загрузка оставляйте для загрузки и ремонта данных.
Чем заменить обход, когда он не нужен
Часто «обход» — симптом того, что задача решается не тем инструментом. Прежде чем лезть в привилегированный режим или читать объекты в цикле, проверьте альтернативы.
| Что хочется сделать | Что лучше использовать |
|---|---|
| Прочитать поля у многих ссылок | Один запрос с соединением, а не цикл с обращением по точке |
| Получить остатки, обороты, итоги | Виртуальные таблицы регистров (Остатки, Обороты) |
| Постоянно читать один и тот же справочник | Кэш: повторный доступ к ссылке уже не идёт в базу |
| Прочитать данные для фонового расчёта | Привилегированный режим — но осознанно и локально |
Главная замена цикла — запрос. Если нужно собрать реквизиты у списка документов, не перебирайте их обращением Ссылка.Поле: один запрос с условием ГДЕ Ссылка В (&МассивСсылок) отдаст всё за одно обращение к базе.
ОбщегоНазначения.ЗначениеРеквизитаОбъекта или ЗначенияРеквизитовОбъекта. Объект целиком не читается.ГДЕ Ссылка В (&Массив). Цикл с обращением по точке — медленно.Объект[ИмяРеквизита] плюс проверка по коллекции метаданных.УстановитьПривилегированныйРежим(Истина) на сервере, выключить сразу после чтения.Приёмы для сложных случаев
Когда штатных средств не хватает, в ход идут более тонкие инструменты. Коротко о тех, что реально работают.
Расширения конфигурации . Доступны с платформы 8.3.6. Позволяют добавить реквизит , изменить поведение формы или модуль, не трогая основную конфигурацию и не снимая её с поддержки. Это легальный способ «дотянуться» до логики объекта там, где править типовую нельзя.
Чтение через запрос к виртуальным таблицам. Для регистров данные почти всегда правильнее брать из РегистрНакопления.ТоварыНаСкладах.Остатки, а не собирать по документам-регистраторам. Это и быстрее, и устойчивее к изменениям учёта.
Дополнительные свойства объекта. Коллекция ДополнительныеСвойства — не замена реквизитам, а способ передать служебные данные между обработчиками в рамках одной записи. Для постоянного хранения она не годится: значения живут только до конца транзакции.
Чего делать не стоит: обращаться к таблицам информационной базы напрямую через SQL в обход платформы. Платформа кэширует, блокирует и проверяет данные по своим правилам; прямой доступ к СУБД ломает эту механику и официально не поддерживается.
Чем оборачивается небрежный обход
Каждый приём выше при неаккуратном применении создаёт свой класс проблем.
- 🔓 Утечка данных. Привилегированный режим, оставленный включённым, или его результат, выведенный на форму, открывают пользователю то, что закрыто правами. Самая частая и самая серьёзная ошибка.
- 🐌 Деградация производительности. Чтение объектов в цикле вместо запроса на больших объёмах превращает секунды в минуты. Обход через точку здесь — главный виновник.
- 🧨 Порча данных. Запись с
ОбменДанными.Загрузка = Истинав обход проверок оставляет в базе объекты в невозможном состоянии: движения есть, а документ не проведён, или наоборот. - 🧱 Хрупкий код. Обращение по имени реквизита падает, если реквизит переименовали или удалили. Без проверки по метаданным такой код ломается при обновлении конфигурации.
Общее правило: обход — это снятие штатной защиты. Снимая её, вы берёте на себя то, что обычно делает платформа. Включайте режим на минимально короткий участок, выключайте сразу, а любые эксперименты с записью в обход логики проводите на копии базы.
Чем `ЗначениеРеквизитаОбъекта` лучше, чем `Ссылка.Реквизит`?
Привилегированный режим расширяет права пользователя?
Как обратиться к реквизиту, имя которого известно только во время выполнения?
Объект[ИмяРеквизита]. Перед этим проверьте, что реквизит с таким именем есть в коллекции метаданных объекта, иначе будет ошибка.Можно ли так же обойти реквизиты в табличной части?
ТабличныеЧасти, у каждой свои Реквизиты. К значению строки обращаются так же по имени: СтрокаТЧ[ИмяРеквизита].