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