При разработке расширений конфигурации часто возникает задача точечного изменения логики работы программы. Классический сценарий: в типовой конфигурации есть процедура общего модуля, которая используется множеством документов. Нам необходимо изменить ее поведение только для одного конкретного документа (например, "Авансовый отчет"), но при этом гарантировать, что для всех остальных документов (накладных, заказов и т.д.) будет работать типовой код без изменений.
В этой статье мы подробно разберем, как правильно реализовать такую логику, избежав распространенных ошибок с контекстом вызова и перехватом управления.
Рассмотрим ситуацию: вы перенесли две процедуры из общего модуля в расширение. Ваша цель — добавить несколько строк кода или изменить алгоритм, но сделать это условно. Если код запускается из документа определенного типа, должны сработать ваши изменения. Во всех остальных случаях должна отработать "родная" логика конфигурации.
Первая ошибка, с которой сталкиваются разработчики в таких случаях (и о которой говорилось в обсуждении), — попытка использования переменной ЭтотОбъект внутри общего модуля.
Давайте разберемся, почему так делать нельзя:
ЭтотОбъект, если вы находитесь не в модуле объекта или формы.Для решения задачи полной или частичной подмены логики в расширениях используется аннотация &Вместо (в английском синтаксисе &Around). Эта аннотация говорит платформе: "Не выполняй стандартный метод, выполняй мой метод".
Однако, если мы просто напишем свой код, то типовой код не выполнится никогда. Нам же нужно сохранить его для большинства случаев. Здесь на помощь приходит метод ПродолжитьВызов().
Алгоритм действий будет следующим:
&Вместо.Предположим, у нас есть процедура общего модуля УправлениеПечатью.ОбработатьДокумент(ДокументСсылка, ПараметрыПечати). Мы хотим изменить ее поведение только для документа "АвансовыйОтчет".
Давайте напишем правильный код для расширения:
&Вместо("ОбработатьДокумент")
Процедура Расш1_ОбработатьДокумент(ДокументСсылка, ПараметрыПечати)
// 1. Проверяем тип входящего параметра.
// Важно: проверяем именно параметр процедуры, а не ЭтотОбъект
Если ТипЗнч(ДокументСсылка) = Тип("ДокументСсылка.АвансовыйОтчет") Тогда
// 2. Здесь пишем нашу уникальную логику для Авансового отчета
// Например, меняем параметры печати или выполняем другие действия
ПараметрыПечати.Вставить("ДополнительнаяОпция", Истина);
// Если нужно полностью заменить типовой код для этого документа,
// то на этом выполнение процедуры можно завершить.
Иначе
// 3. Для всех остальных документов (Реализация, Заказ и т.д.)
// вызываем метод основной конфигурации.
ПродолжитьВызов(ДокументСсылка, ПараметрыПечати);
КонецЕсли;
КонецПроцедуры
Обратите внимание на ключевой момент: метод ПродолжитьВызов() обязательно должен принимать те же параметры, которые пришли в процедуру. Если вы не передадите параметры, типовой код может сработать некорректно или вызвать ошибку.
В обсуждении был предложен отличный способ добавления кода в расширение — использование режима "Изменить и контролировать". Давайте разберем, почему это рекомендуется.
Когда вы добавляете метод в расширение, вы можете просто скопировать его или использовать специальную команду контекстного меню (в окне конфигурации расширения или при добавлении метода из основной конфигурации).
При выборе варианта "Изменить и контролировать":
&Вместо (или &ИзменениеИКонтроль в новых версиях платформы, что позволяет использовать теги вставки).Зачем это нужно? Если в следующем обновлении поставщик конфигурации (например, фирма 1С) исправит ошибку в этой процедуре, то при запуске программы вы увидите предупреждение, что версия метода в расширении не соответствует версии в основной конфигурации. Это сигнал для программиста: "Внимание, типовой код изменился, нужно проверить, актуальны ли ваши доработки".
Если ваша задача состоит не в замене логики, а в дополнении (например, нужно выполнить типовой код, но с измененными параметрами), схема с ПродолжитьВызов() становится еще более гибкой.
Пример: для Авансового отчета нужно сначала выполнить типовой код, а потом что-то дописать.
&Вместо("ОбработатьДокумент")
Процедура Расш1_ОбработатьДокумент(ДокументСсылка, ПараметрыПечати)
// Сначала даем отработать типовой логике для ВСЕХ документов
ПродолжитьВызов(ДокументСсылка, ПараметрыПечати);
// А теперь добавляем свою логику только для нужного типа
Если ТипЗнч(ДокументСсылка) = Тип("ДокументСсылка.АвансовыйОтчет") Тогда
// Дополнительные действия после отработки стандарта
ЗаписьЖурналаРегистрации("Обработка Авансового отчета завершена");
КонецЕсли;
КонецПроцедуры
Чтобы заставить код расширения срабатывать в зависимости от типа документа, выполните следующие шаги:
ЭтотОбъект в общих модулях. Анализируйте параметры, передаваемые в процедуру.Если ТипЗнч(Параметр) = Тип("...") Тогда ... КонецЕсли.Иначе с вызовом метода ПродолжитьВызов(Параметры...).Такой подход обеспечивает минимальное вмешательство в типовой код и гарантирует стабильную работу системы для всех объектов, кроме тех, которые вы явно хотите модифицировать.