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