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