Как очистить или сделать пустым «РезультатЗапроса» в 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет IT и автоматизация бизнеса
← На главную

При разработке расширений для типовых конфигураций 1С («ERР Управление предприятием», «Комплексная автоматизация», «Управление торговлей») программисты часто сталкиваются с задачей точечного отключения стандартных проверок. Например, когда необходимо разрешить проведение документа без предоплаты для определенных видов номенклатуры (услуг), обойдя жестко зашитую в коде валидацию.

В процессе перехвата управления через расширение возникает техническая сложность: в процедуру валидации передается переменная с типом РезультатЗапроса. Пытаясь обойти проверку «в лоб», разработчики ищут способ очистить эту переменную, чтобы последующий цикл выборки данных просто не выполнился. В этой статье мы подробно разберем, почему стандартные методы очистки здесь не работают, как решить задачу быстро и как реализовать ее наиболее правильно с точки зрения архитектуры и производительности.

Почему в 1С нельзя просто очистить «РезультатЗапроса»?

Объект типа РезультатЗапроса в платформе 1С:Предприятие спроектирован исключительно как контейнер для чтения данных, полученных из СУБД. Он обладает следующими особенностями:

Проанализируем практические способы решения этой проблемы.

Способ 1. Быстрый обход через «холостой» запрос (Решение с форума)

Если нам критически важно сохранить структуру вызова с директивой &Перед в расширении и подменить переменную РезультатЗапроса на абсолютно пустую, но валидную структуру, мы можем переопределить ее с помощью выполнения специального «пустого» запроса к СУБД.

Рассмотрим пример реализации такого перехвата:


&Перед("СообщитьОбОшибкахПроведенияПоРегиструРасчетыСКлиентами")
Процедура Мой_СообщитьОбОшибкахПроведенияПоРегиструРасчетыСКлиентами(Объект, Отказ, РезультатЗапроса)
    
    // Создаем новый запрос, который заведомо вернет пустой результат
    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ 0 ГДЕ Ложь";
    
    // Переопределяем входящую переменную типом РезультатЗапроса
    РезультатЗапроса = Запрос.Выполнить();
    
КонецПроцедуры

Выясним, почему этот код работает. Конструкция ВЫБРАТЬ 0 ГДЕ Ложь формирует запрос, который не обращается ни к одной физической таблице базы данных, а жесткое логическое ограничение ГДЕ Ложь гарантирует, что СУБД вернет ровно 0 строк. При этом метод Выполнить() возвращает полноценный, но пустой объект РезультатЗапроса.

Когда управление передается в основную (типовую) процедуру, исполняется следующий код:


Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
    // Этот цикл ни разу не выполнится, так как выборка пуста
    // Ошибки проведения не запишутся, флаг Отказ останется в значении Ложь
КонецЦикла;

Существенный минус данного подхода: Несмотря на кажущуюся простоту, это решение неоптимально. Даже для выполнения конструкции ВЫБРАТЬ 0 ГДЕ Ложь платформа 1С выполняет полноценную трансляцию запроса на уровень СУБД (MS SQL, PostgreSQL и др.). База данных компилирует план запроса и возвращает ответ. При групповом проведении документов или частом вызове этой проверки система будет совершать тысячи бесполезных сетевых и вычислительных операций, снижая общую производительность программы.

Способ 2. Архитектурно правильное решение через директиву &Вместо

Чтобы избежать лишней нагрузки на СУБД и сделать код чистым, откажемся от использования директивы &Перед в пользу &Вместо. Вопреки распространенному заблуждению, директива &Вместо не обязывает нас полностью дублировать весь сложный код типовой конфигурации. Она позволяет гибко управлять вызовом оригинального метода с помощью метода ПродолжитьВызов().

Разберем по шагам, как реализовать этот алгоритм:

  1. Создаем метод в расширении с аннотацией &Вместо.
  2. Описываем условие, при котором проверку проводить не нужно (например, если в табличной части документа присутствуют только услуги).
  3. Если условие выполняется, мы просто вызываем оператор Возврат. При этом стандартная процедура проверки не выполняется, а в базу данных не отправляется никаких лишних запросов.
  4. Если условия обхода не выполняются (документ содержит стандартные товары), мы передаем выполнение оригинальному коду через ПродолжитьВызов().

Посмотрим на пример практической реализации:


&Вместо("СообщитьОбОшибкахПроведенияПоРегиструРасчетыСКлиентами")
Процедура Мой_СообщитьОбОшибкахПроведенияПоРегиструРасчетыСКлиентами(Объект, Отказ, РезультатЗапроса)
    
    // Проверяем наше кастомное условие обхода ошибки проведения
    Если ДокументСодержитТолькоУслуги(Объект) Тогда
        // Просто выходим из процедуры. 
        // Стандартная процедура НЕ выполнится, Отказ не установится в Истина.
        Возврат; 
    КонецЕсли;
    
    // В обычных случаях продолжаем выполнение типовой логики 1С
    ПродолжитьВызов(Объект, Отказ, РезультатЗапроса);
    
КонецПроцедуры

Этот способ является наиболее производительным и лаконичным, так как исключает саму необходимость как-либо модифицировать системный тип РезультатЗапроса.

Способ 3. Использование механизма «&ИзменениеИКонтроль»

Если версия вашей платформы 1С:Предприятие 8.3.15 или выше, наиболее прогрессивным методом доработки логики является использование аннотации &ИзменениеИКонтроль. Этот подход позволяет не заменять всю процедуру целиком, а точечно модифицировать поведение программы непосредственно внутри оригинального кода.

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

Подводя итог, отметим: если вам необходимо быстро решить локальную задачу отладки, метод подмены через ВЫБРАТЬ 0 ГДЕ Ложь вполне жизнеспособен. Однако для рабочих («production») систем всегда следует отдавать приоритет использованию аннотации &Вместо с условным вызовом ПродолжитьВызов(), так как это гарантирует максимальное быстродействие вашей базы данных.

← На главную