Каждый разработчик на платформе 1С:Предприятие рано или поздно сталкивается с парадоксальной ситуацией: написанный запрос идеально отрабатывает в Консоли запросов, выводя нужные строки, но при запуске того же самого кода в конфигурации программа либо выдает «ошибку чтения», либо возвращает пустой результат.
В этой статье мы подробно разберем, почему возникает такое поведение, проанализируем ключевые причины и покажем, как шаг за шагом исправить логику работы программы.
Это наиболее распространенная причина расхождения результатов. Разберем ситуацию детально. Консоль запросов разработчик почти всегда запускает под учетной записью с «Полными правами». В этом режиме платформа полностью игнорирует любые ограничения прав доступа. Напротив, в режиме отладки или при работе пользователя сеанс может быть запущен под учетной записью с ограниченными правами (например, менеджер по продажам или бухгалтер), у которой настроен механизм RLS (Row-Level Security) по организациям, складам или контрагентам.
Если в коде выполняется запрос к таблице, на которую у пользователя наложены ограничения прав, система поведет себя следующим образом:
ВЫБРАТЬ, а у пользователя нет прав на чтение определенных записей (или всей таблицы), платформа выбросит исключение «Ошибка чтения» или «Нарушение прав доступа».Для исправления этого поведения проанализируем два пути решения:
Способ А. Использование ключевого слова РАЗРЕШЕННЫЕ
Добавление конструкции РАЗРЕШЕННЫЕ указывает платформе, что из базы данных нужно выбрать только те записи, на чтение которых у текущего пользователя есть права. Ошибка чтения при этом исчезнет, а запрос вернет только доступные данные.
Посмотрим на пример измененного текста запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| Продажи.Количество,
| Продажи.Стоимость,
| Продажи.НДС
|ИЗ
| РегистрНакопления.Продажи КАК Продажи
|ГДЕ
| Продажи.Период МЕЖДУ &ДатаНач И &ДатаКон";
Способ Б. Выполнение кода в привилегированном режиме
Если бизнес-логика требует, чтобы запрос гарантированно прочитал все данные (например, при проведении документа или расчете себестоимости на сервере), выполнение кода необходимо временно перевести в привилегированный режим. В этом случае RLS будет временно отключен.
Для этого воспользуемся методом УстановитьПривилегированныйРежим:
УстановитьПривилегированныйРежим(Истина);
Запрос = Новый Запрос;
// Настройка параметров и выполнение запроса...
РезультатЗапроса = Запрос.Выполнить();
УстановитьПривилегированныйРежим(Ложь);
При работе в Консоли запросов разработчик заполняет параметры интерактивно: выбирает конкретный элемент из справочника или вводит точную дату в календаре. В программном коде параметры передаются динамически из переменных, реквизитов формы или строк табличных частей, что таит в себе скрытые ловушки.
Несохраненные объекты (Ссылки нового)
Представим ситуацию, когда в качестве параметра &ДоговорКонтрагента передается ссылка из только что созданного на форме договора. Если объект еще не был физически записан в базу данных, ссылка на него уже существует в памяти (так называемая «Ссылка нового»), но в таблицах базы данных СУБД такой записи еще нет. Запрос вернет пустой результат, в то время как в консоли вы гарантированно выбирали уже сохраненный в базе договор.
Проблемы с границами дат
В консоли разработчик интуитивно может выставить дату конца периода как 31.12.2023 23:59:59. В коде же значение ДоговорКонтрагента.СрокДействия часто хранится без времени (значение времени по умолчанию равно 00:00:00).
Если в коде написать:
Запрос.УстановитьПараметр("ДатаКон", ДоговорКонтрагента.СрокДействия);
То все продажи, совершенные в день окончания договора после полуночи (например, в 12:00 дня), в результат запроса не попадут, так как условие МЕЖДУ ограничит выборку началом этого дня. Для решения этой проблемы рекомендуется использовать функцию КонецДня:
Запрос.УстановитьПараметр("ДатаКон", КонецДня(ДоговорКонтрагента.СрокДействия));
Значения Неопределено и NULL
В коде параметр может принять значение Неопределено или Null (например, если строка таблицы НайденнаяСтрока.Номенклатура оказалась пустой). При отладке обязательно проверяйте типы передаваемых параметров с помощью Табло или Вычисления выражения.
В исходном коде автора запроса выборка данных происходит напрямую из физической таблицы РегистрНакопления.Продажи:
ИЗ
РегистрНакопления.Продажи КАК Продажи
ГДЕ
Продажи.Период МЕЖДУ &ДатаНач И &ДатаКон
И Продажи.Номенклатура = &Номенклатура
Это грубейшая ошибка проектирования запросов в 1С. Прямое обращение к физической таблице заставляет СУБД сканировать весь массив записей регистра. На копии базы или в консоли при отсутствии нагрузки это может работать быстро, но в реальной многопользовательской среде такой запрос приведет к избыточным блокировкам таблиц и ошибкам чтения (таймаутам блокировок).
Для исправления этой архитектурной проблемы всегда используйте виртуальные таблицы оборотов или остатков. Намного эффективнее передавать параметры отбора напрямую в параметры виртуальной таблицы:
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| ПродажиОбороты.КоличествоОборот КАК Количество,
| ПродажиОбороты.СтоимостьОборот КАК Стоимость,
| ПродажиОбороты.НДСОборот КАК НДС
|ИЗ
| РегистрНакопления.Продажи.Обороты(&ДатаНач, &ДатаКон, , Номенклатура = &Номенклатура И ДоговорКонтрагента = &ДоговорКонтрагента) КАК ПродажиОбороты";
Этот подход позволяет платформе 1С построить оптимальный SQL-запрос, который обратится к предварительно рассчитанным итогам СУБД, что ускорит выполнение операции в десятки раз и предотвратит ошибки чтения под нагрузкой.
Иногда код работает некорректно из-за устаревшего локального кэша метаданных. Особенно часто это проявляется при разработке внешних обработок. Если обработка аварийно завершила работу или была изменена в конфигураторе, платформа 1С может продолжать запускать старую версию обработки из временной папки кэша приложений.
Рекомендуется выполнить следующие действия:
/ClearCache для очистки кэша конфигурации платформы.Используя приведенные методики, мы гарантируем стабильное выполнение запросов в кодовой среде конфигурации, полностью соответствующее результатам тестирования в Консоли запросов.