Обход реквизитов объекта в 1С: получение данных и привилегированный режим

· 9 мин

Содержание

За фразой «обойти реквизиты объекта» в 1С почти всегда стоит одна из двух задач. Либо получить значение реквизита, не загружая в память весь объект, — ради скорости. Либо прочитать данные, которые закрыты правами доступа, — из служебного кода. Это разные вещи, и путать их опасно.

Короткий ориентир: значение одного реквизита по ссылке берут функцией ОбщегоНазначения.ЗначениеРеквизитаОбъекта, несколько — ЗначенияРеквизитовОбъекта, а доступ в обход RLS и ролей даёт УстановитьПривилегированныйРежим(Истина) на сервере. Ниже разберём каждый приём, границы его применения и то, чем оборачивается небрежный обход.

Статья для платформы 8.3 (интерфейс «Такси»). Код универсален и не зависит от конфигурации, пути в примерах даны для типовых БП 3.0, ЗУП 3.1 и УТ 11.

Что значит «обойти реквизиты» и когда это оправдано

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

Обход оправдан в трёх ситуациях:

  • 📉 Производительность. Нужны одно-два поля, а объект тяжёлый. Запрос или специальная функция читают только то, что просили.
  • 🔐 Служебная логика. Фоновое задание, регламентная операция или сервисный расчёт обязаны прочитать данные независимо от прав текущего пользователя.
  • 🧩 Универсальный код. Обработчик не знает заранее имя реквизита — оно приходит строкой или вычисляется по метаданным.

А вот чего обход делать не должен: показывать пользователю то, что скрыто от него настройками прав. Технически это возможно, по сути — дыра в безопасности. Разделяйте «код читает данные для своих нужд» и «данные уходят на экран в обход ограничений».

Получить реквизит без чтения всего объекта

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

Один реквизит:

Склад = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаДокумент, "Склад");

Несколько сразу — одним запросом вместо нескольких обращений:

Реквизиты = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
    СсылкаНаДокумент, "Склад, Организация, Ответственный");
Склад = Реквизиты.Склад;
Орг    = Реквизиты.Организация;

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

Обе функции выполняются на сервере — из клиентского кода их вызывают через серверный метод или общий серверный модуль. Если передать пустую ссылку, вернётся Неопределено: отдельной проверки «ссылка заполнена» это не заменяет, но от аварийного завершения спасает. Имя реквизита передаётся как есть, с учётом регистра метаданных.

Если БСП в конфигурации нет, тот же результат даёт обычный запрос: ВЫБРАТЬ Склад, Организация ИЗ Документ.РеализацияТоваровУслуг ГДЕ Ссылка = &Ссылка. Это и есть то, что делают функции БСП внутри.

Через точку реквизит можно получить и по «цепочке»: Ссылка.Контрагент.ОсновнойБанковскийСчёт.Банк. Удобно, но каждое звено — отдельное чтение объекта в кэш. Если цепочка длинная, а вызовов много, соберите те же данные одним запросом с соединениями: платформа возьмёт всё за один проход.

Когда уместно именно чтение через точку (Ссылка.Склад)? Когда реквизитов нужно много и объект всё равно понадобится дальше. Кэш объектов живёт до изменения объекта, и повторные обращения к той же ссылке уже не идут в базу. Для разовой выборки одного поля точка — проигрышный вариант.

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

Если имя реквизита заранее неизвестно, к нему обращаются по строке. У объекта (не у ссылки) работает индексное обращение:

ИмяРеквизита = "Комментарий";
Значение = ОбъектДокумента[ИмяРеквизита];

Это и есть «обход» статического имени: код не привязан к конкретному реквизиту. На таком приёме строят универсальные обработки — заполнение по правилам, выгрузки, сравнение версий.

Чтобы перебрать все реквизиты объекта, идут по коллекции метаданных:

МетаданныеДок = Метаданные.Документы.РеализацияТоваровУслуг;
Для Каждого Реквизит Из МетаданныеДок.Реквизиты Цикл
    Значение = ОбъектДокумента[Реквизит.Имя];
    // обработка Значение
КонецЦикла;

Коллекция .Реквизиты содержит только собственные реквизиты. Стандартные (Номер, Дата, Ссылка, Проведён) в неё не входят — их добавляют отдельно, если нужны. Табличные части лежат в коллекции .ТабличныеЧасти, у каждой свои .Реквизиты.

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

Для Каждого Реквизит Из Метаданные.Документы.РеализацияТоваровУслуг.Реквизиты Цикл
    Имя = Реквизит.Имя;
    ОбъектПриёмник[Имя] = ОбъектИсточник[Имя];
КонецЦикла;

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

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

Через метаданные удобно собирать и состав реквизитов для формы или запроса, не перечисляя их руками. Но помните: это рефлексия, она медленнее прямого обращения. На горячих участках имена лучше знать заранее.

Обход прав доступа: привилегированный режим и RLS

Права в 1С работают на двух уровнях. Роли задают, какие объекты и действия доступны. RLS (ограничение доступа на уровне записей) сужает видимость до конкретных строк — например, пользователь видит только свою организацию.

Когда служебному коду нужно прочитать данные независимо от этих ограничений, включают привилегированный режим:

УстановитьПривилегированныйРежим(Истина);
// здесь права ролей и RLS не проверяются
Данные = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Сумма");
УстановитьПривилегированныйРежим(Ложь);

Что важно знать про этот режим:

  • ▶️ Работает только на сервере. На клиенте вызов бессмысленен.
  • 🔁 Включения вложенные: сколько раз включили, столько и нужно выключить. Поэтому выключение ставят парно, рядом с включением.
  • 👁️ Проверить текущее состояние можно функцией ПривилегированныйРежим() — она вернёт Истина, если режим активен.

Где это законно и нужно: фоновые задания, обмен данными, служебные расчёты, формирование печатных форм по регламенту — там, где код действует «от имени системы», а не показывает пользователю лишнее. Пример: регламентное задание пересчитывает скидки по всем организациям, а запустивший его пользователь по RLS видит только одну. Без привилегированного режима задание прочитает данные частично и посчитает неверно.

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

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

Обход контроля при записи объекта

Отдельная задача — записать объект, не запуская его бизнес-логику: проверки заполнения, обработчики ПередЗаписью, контроль проведения. Для этого перед записью ставят флаг загрузки:

Объект.ОбменДанными.Загрузка = Истина;
Объект.Записать();

В режиме загрузки платформа пропускает часть проверок и не инициирует стандартный сценарий проведения. Так загружают данные из обменов и исправляют записи техническими обработками.

Это мощный и опасный приём. Пропуская ПередЗаписью, вы можете записать объект в состоянии, которое обычная логика никогда бы не допустила: неверные остатки, несведённые движения, нарушенные связи. Применяйте только когда точно понимаете, что именно отключаете, и обязательно на копии базы сначала.

Проведение в обход формы делают методом Записать(РежимЗаписиДокумента.Проведение) — но это уже штатный путь, а не обход. Полное отключение логики через ОбменДанными.Загрузка оставляйте для загрузки и ремонта данных.

Чем заменить обход, когда он не нужен

Часто «обход» — симптом того, что задача решается не тем инструментом. Прежде чем лезть в привилегированный режим или читать объекты в цикле, проверьте альтернативы.

Что хочется сделатьЧто лучше использовать
Прочитать поля у многих ссылокОдин запрос с соединением, а не цикл с обращением по точке
Получить остатки, обороты, итогиВиртуальные таблицы регистров (Остатки, Обороты)
Постоянно читать один и тот же справочникКэш: повторный доступ к ссылке уже не идёт в базу
Прочитать данные для фонового расчётаПривилегированный режим — но осознанно и локально

Главная замена цикла — запрос. Если нужно собрать реквизиты у списка документов, не перебирайте их обращением Ссылка.Поле: один запрос с условием ГДЕ Ссылка В (&МассивСсылок) отдаст всё за одно обращение к базе.

Как получить значение реквизита в вашем случае?

Приёмы для сложных случаев

Когда штатных средств не хватает, в ход идут более тонкие инструменты. Коротко о тех, что реально работают.

Расширения конфигурации . Доступны с платформы 8.3.6. Позволяют добавить реквизит , изменить поведение формы или модуль, не трогая основную конфигурацию и не снимая её с поддержки. Это легальный способ «дотянуться» до логики объекта там, где править типовую нельзя.

Чтение через запрос к виртуальным таблицам. Для регистров данные почти всегда правильнее брать из РегистрНакопления.ТоварыНаСкладах.Остатки, а не собирать по документам-регистраторам. Это и быстрее, и устойчивее к изменениям учёта.

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

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

Чем оборачивается небрежный обход

Каждый приём выше при неаккуратном применении создаёт свой класс проблем.

  • 🔓 Утечка данных. Привилегированный режим, оставленный включённым, или его результат, выведенный на форму, открывают пользователю то, что закрыто правами. Самая частая и самая серьёзная ошибка.
  • 🐌 Деградация производительности. Чтение объектов в цикле вместо запроса на больших объёмах превращает секунды в минуты. Обход через точку здесь — главный виновник.
  • 🧨 Порча данных. Запись с ОбменДанными.Загрузка = Истина в обход проверок оставляет в базе объекты в невозможном состоянии: движения есть, а документ не проведён, или наоборот.
  • 🧱 Хрупкий код. Обращение по имени реквизита падает, если реквизит переименовали или удалили. Без проверки по метаданным такой код ломается при обновлении конфигурации.

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

Чем `ЗначениеРеквизитаОбъекта` лучше, чем `Ссылка.Реквизит`?
Функция читает один реквизит запросом, не поднимая объект в память. Обращение через точку загружает объект целиком в кэш. Для одного-двух полей функция быстрее, для многих полей разница стирается.
Привилегированный режим расширяет права пользователя?
Нет. Он отключает проверку прав и RLS для кода на сервере. Пользователь при этом остаётся с теми же правами — меняется только то, что проверка не выполняется. Поэтому выводить такие данные в интерфейс нельзя.
Как обратиться к реквизиту, имя которого известно только во время выполнения?
Через индексное обращение по строке: Объект[ИмяРеквизита]. Перед этим проверьте, что реквизит с таким именем есть в коллекции метаданных объекта, иначе будет ошибка.
Можно ли так же обойти реквизиты в табличной части?
Да. Табличные части лежат в коллекции ТабличныеЧасти, у каждой свои Реквизиты. К значению строки обращаются так же по имени: СтрокаТЧ[ИмяРеквизита].
Почему нельзя читать данные 1С напрямую из базы SQL?
Платформа держит собственный кэш, блокировки и контроль ссылочной целостности. Прямой доступ к таблицам СУБД обходит всё это, грозит рассинхронизацией и не поддерживается фирмой 1С.