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