Почему в расширении 1С удваиваются проводки при использовании &ИзменениеИКонтроль и как это исправить

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

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

Суть проблемы: механизм двойного срабатывания

Рассмотрим ситуацию, с которой столкнулся автор темы. В расширении модифицируется функция модуля менеджера документа ПоступлениеТоваров, отвечающая за формирование текста временных таблиц. Для этого используется современная директива &ИзменениеИКонтроль. Программист корректно применяет инструкции #Удаление и #Вставка, чтобы заменить фрагмент запроса. Однако при проведении документа проводки фиксируются дважды: один раз с изменениями из расширения, а второй раз — в первоначальном виде из основной конфигурации.

Давайте выясним причину. Проблема кроется не в самом запросе, а в том, как вызывается процедура проведения в целом. Если в расширении одновременно используется директива &Вместо для основной процедуры проведения (например, СформироватьДвиженияПоступлениеТоваров) и внутри неё вызывается ПродолжитьВызов(), платформа выполняет оригинальный код. Если же этот оригинальный код, в свою очередь, обращается к функции, которую мы уже "подменили" через &ИзменениеИКонтроль, может возникнуть наслоение логики.

Разбор директивы &ИзменениеИКонтроль

Директива &ИзменениеИКонтроль — это мощный инструмент, который позволяет платформе буквально "пересобрать" метод в момент компиляции. В отличие от &Перед или &После, этот метод не дополняет оригинал, а полностью замещает его модифицированной версией. Рассмотрим пример того, как выглядит типичная вставка кода в запрос:


&ИзменениеИКонтроль("ТекстЗапросаВременныеТаблицыДокумента")
Функция ТекстЗапросаВременныеТаблицыДокумента(Параметры)
    ТекстЗапроса = "ВЫБРАТЬ * ИЗ Документ.ПоступлениеТоваров";
    #Удаление
    ТекстЗапроса = ТекстЗапроса + " ГДЕ Ссылка = &Ссылка";
    #КонецУдаления
    #Вставка
    ТекстЗапроса = ТекстЗапроса + " ГДЕ Ссылка = &Ссылка И Проведен";
    #КонецВставки
    Возврат ТекстЗапроса;
КонецФункции

Важно понимать: когда вы используете &ИзменениеИКонтроль, оригинальный метод в основной конфигурации для системы "исчезает", и на его месте появляется ваш код. Если вы видите удвоение, значит, выполнение логики инициируется дважды из разных точек входа.

Опасность использования ПродолжитьВызов() в методах &Вместо

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

Если ваша процедура в расширении уже выполнила часть работы (например, сформировала какие-то движения или подготовила данные), а затем вызвала ПродолжитьВызов(), управление передается оригинальной процедуре. Оригинальная процедура начинает выполнять алгоритм с начала, снова обращается к запросам (которые уже изменены расширением через &ИзменениеИКонтроль) и записывает данные в регистры еще раз.

Рассмотрим схему ошибочного кода:


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

Как исправить удвоение данных: пошаговая инструкция

Для решения проблемы нам необходимо исключить повторное выполнение одного и того же алгоритма. Разберем основные шаги:

  1. Определите стратегию изменения: Если вы используете &ИзменениеИКонтроль для глубокой модификации логики запросов или расчетов, вам, скорее всего, придется полностью отказаться от вызова оригинального метода проведения.
  2. Удалите ПродолжитьВызов(): В методе с директивой &Вместо закомментируйте или удалите строку ПродолжитьВызов(). В этом случае отработает только ваш код из расширения, который уже включает в себя все необходимые модификации через &ИзменениеИКонтроль.
  3. Проверьте чистоту кэша: Платформа 1С иногда сохраняет скомпилированные модули. Если после правки кода поведение не изменилось, попробуйте очистить кэш конфигурации или перезагрузить сервер 1С:Предприятия.
  4. Соблюдайте точность текста: Помните, что &ИзменениеИКонтроль крайне чувствительна к пробелам и переносам строк. Если текст внутри блока #Удаление не совпадает с оригиналом до символа, расширение не применится. Используйте копирование напрямую из конфигуратора основной базы.

Технические нюансы платформы 8.3.22 и выше

Проанализируем особенности новых версий платформы. Начиная с версии 8.3.15, механизм контроля за текстом стал строже. Если вы изменили текст запроса в расширении, а затем вышло обновление типовой конфигурации, где в этом же запросе изменился комментарий или добавился пробел — расширение "слетит" с ошибкой проверки. Поэтому рекомендуется:

1. Минимизировать область удаления. Не удаляйте весь запрос целиком. Старайтесь заключать в #Удаление только ту конкретную строку, которую вы меняете.

2. Использовать СтрЗаменить. В некоторых случаях (хотя это менее производительно) вместо &ИзменениеИКонтроль можно использовать &После или &Вместо, где текст запроса корректируется программно с помощью функции СтрЗаменить().

Заключение

Подведем итог: удвоение результатов в расширении — это всегда признак того, что один и тот же участок логики выполняется дважды. Чаще всего это происходит из-за комбинации &ИзменениеИКонтроль (которая подменяет низкоуровневые функции) и &Вместо с вызовом ПродолжитьВызов() (который запускает высокоуровневый процесс повторно). Выбирайте одну точку входа для ваших изменений, и механизм расширений станет предсказуемым и надежным инструментом разработки.

← На главную