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