Как передать значение переменной из процедуры Перед в процедуру После в расширениях 1С?

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

При разработке расширений в системе 1С:Предприятие 8.3 программисты часто сталкиваются с необходимостью сохранить состояние данных перед выполнением типового кода и восстановить или обработать их сразу после завершения стандартной процедуры. Типичный пример: типовой механизм при изменении реквизита очищает табличную часть или сбрасывает значения, которые нам критически важны. Нам нужно "запомнить" эти данные в &Перед и "вернуть" их в &После.

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

Метод 1. Использование аннотации &Вместо и локальных переменных (Матрешка)

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

Разберем по шагам, как это работает. Когда мы используем &Вместо, мы полностью перехватываем управление. Вызов оригинального (типового) кода происходит только тогда, когда мы явно вызываем метод ПродолжитьВызов(). Это позволяет нам объявить локальные переменные в начале процедуры, и они будут доступны на протяжении всего времени ее выполнения — и до, и после вызова типового кода.

Посмотрим на пример кода:


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

Преимущества этого метода:

  1. Данные хранятся в стеке вызовов процедуры, они автоматически очистятся после завершения метода.
  2. Вам не нужно создавать лишние реквизиты формы или переменные модуля, которые могут конфликтовать с другими доработками.
  3. Метод работает корректно даже при рекурсивных вызовах или нескольких открытых окнах одной и той же формы.

Метод 2. Переменные модуля формы

Если по каким-то причинам вы предпочитаете использовать раздельные аннотации &Перед и &После, или если логика сохранения должна быть разнесена по разным событиям, можно использовать переменные модуля. Выясним причину, почему это не всегда удобно: переменные модуля доступны только в контекстных вызовах.

Рассмотрим пример реализации:


&НаКлиенте
Перем КонтейнерДанных; // Объявляем переменную в самом верху модуля расширения

&Перед("ТиповаяПроцедура")
Процедура Расш1_ПередТиповаяПроцедура()
    КонтейнерДанных = Объект.СуммаДокумента;
КонецПроцедуры

&После("ТиповаяПроцедура")
Процедура Расш1_ПослеТиповаяПроцедура()
    Если КонтейнерДанных > 1000 Тогда
        // Выполняем логику, используя сохраненное значение
    КонецЕсли;
КонецПроцедуры

Важное ограничение: Если типовая процедура вызывает серверный метод без контекста (&НаСервереБезКонтекста), переменные модуля формы будут недоступны. В этом случае данный метод не сработает.

Метод 3. Использование свойства ДополнительныеСвойства (Модуль объекта)

Проанализируем ситуацию, когда доработка происходит не в форме, а в модуле объекта (например, в справочнике или документе). В 1С у многих объектов есть встроенное свойство ДополнительныеСвойства, которое представляет собой структуру. Это свойство идеально подходит для передачи данных между процедурами ПередЗаписью, ПриЗаписи и ОбработкаПроверкиЗаполнения.

Разберем пример для модуля документа:


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

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

Почему это надежно: Свойство ДополнительныеСвойства живет вместе с экземпляром объекта в памяти. Даже если типовой код выполняет частичное считывание или обновление реквизитов, эта структура обычно остается нетронутой.

Метод 4. Работа с Временным хранилищем (Сложный случай)

В самых сложных ситуациях, например, при работе с бесконтекстными серверными вызовами, когда нам нужно передать данные из одного вызова в другой, мы можем использовать ВременноеХранилище. Для этого нам понадобится уникальный идентификатор (УИД) формы, чтобы не перепутать данные разных пользователей или сеансов.

Алгоритм действий будет следующим:

  1. В методе &Перед помещаем данные в хранилище: Адрес = ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор);.
  2. Передаем этот Адрес (или сохраняем его в реквизит формы).
  3. В методе &После извлекаем данные: Данные = ПолучитьИзВременногоХранилища(Адрес);.

Сравнение аннотаций: почему &Вместо часто лучше, чем &Перед и &После?

Многие разработчики задаются вопросом: зачем вообще использовать &Перед, если &Вместо с вызовом ПродолжитьВызов() делает то же самое? Выясним ключевые отличия:

1. Целостность контекста: Как мы выяснили выше, &Вместо позволяет использовать локальные переменные для передачи состояния, что невозможно при разделении логики на две разные процедуры.

2. Иерархия выполнения: Платформа 1С соблюдает строгий порядок. Сначала выполняются ВСЕ процедуры &Перед всех расширений. Затем выполняется самая приоритетная процедура &Вместо. И только в конце — ВСЕ процедуры &После. Причем процедуры &После выполняются в обратном порядке (от последнего расширения к первому).

3. Возврат значений: Если вы работаете с функцией, то &Перед не может изменить результат функции. Это может сделать только &Вместо, перехватив возвращаемое значение типового кода:


&Вместо("РассчитатьСумму")
Функция Расш1_РассчитатьСумму(Параметры)
    Результат = ПродолжитьВызов(Параметры); // Получаем результат типовой функции
    Результат = Результат * 1.1; // Увеличиваем на 10%
    Возврат Результат;
КонецФункции

Рекомендации по выбору метода

Подводя итог, выберем оптимальную стратегию для вашей задачи:

Если вы работаете в рамках одного расширения и вам нужно сохранить состояние реквизитов формы до и после вызова процедуры — используйте Метод 1 (&Вместо). Это обеспечит максимальную чистоту кода и отсутствие лишних зависимостей.

Если данные должны передаваться между разными событиями жизненного цикла объекта (например, из ПриЧтенииНаСервере в ПередЗаписью) — используйте Метод 3 (ДополнительныеСвойства) для серверной логики или Метод 2 (Переменные модуля) для клиентской.

Всегда помните о безопасности обновлений: старайтесь не заменять типовой код полностью, всегда вызывайте ПродолжитьВызов(), чтобы не потерять важные изменения, которые могут прийти с новым релизом конфигурации от поставщика.

← На главную