Как программно вызвать событие "ПриИзменении" для реквизитов формы в 1С:Предприятии 8.3?

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

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

Общий подход с использованием расширений конфигурации

Для модификации типового функционала без изменения основной конфигурации, что является лучшей практикой и упрощает обновление, мы рекомендуем использовать расширения конфигурации. Этот механизм, доступный с платформы 8.3.5, позволяет нам вносить изменения, не затрагивая исходный код типовых объектов.

  1. Добавление расширения: Сначала необходимо создать новое расширение конфигурации в режиме "Конфигуратор".

  2. Заимствование формы: В расширении заимствуем форму, в которой находится требуемый реквизит. Это позволит нам работать с модулем этой формы.

  3. Добавление экспортной клиентской процедуры: В модуль заимствованной формы помещаем новую экспортную клиентскую процедуру. Эта процедура будет доступна для вызова извне, например, из обработок или других форм.

  4. Вызов обработчиков событий: Уже из этой экспортной процедуры мы сможем вызывать необходимые неэкспортные обработчики событий элементов формы, которые по умолчанию срабатывают при интерактивном изменении. Это позволяет нам имитировать действия пользователя, вызывая внутреннюю логику формы.

Данный подход дает нам гибкость в управлении событиями формы, позволяя перехватывать и модифицировать их поведение "перед", "после" или "вместо" типовых обработчиков. С версии 8.3.9 возможности расширений значительно расширились, включив работу с модулями объектов, менеджерами объектов и общими модулями.

Имитация интерактивного ввода для реквизитов формы

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

Функция для интерактивной записи значения

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


Функция ИнтерактивноЗаписатьВЭлементУправления(ЭлементУправления, Знач Значение, Знач ФормаИнициатор = Неопределено)

    // Проверяем, если ФормаИнициатор не определена, то используем временную форму-пустышку.
    // Это может быть полезно, если вызов происходит из контекста, где нет прямой формы-инициатора.
    // В данном примере эти строки закомментированы, что означает, что ФормаИнициатор ожидается всегда.
    // Если ФормаИнициатор = Неопределено Тогда
    //     ФормаИнициатор = ПолучитьФорму("ОбщаяФорма.Пустышка", ЭлементУправления);
    // КонецЕсли;

    // Сохраняем текущие значения свойств ВладелецФормы и ЗакрыватьПриВыборе формы-инициатора,
    // чтобы восстановить их после операции.
    СтарыйВладелец = ФормаИнициатор.ВладелецФормы;
    СтарыйЗакрыватьПриВыборе = ФормаИнициатор.ЗакрыватьПриВыборе;

    // Устанавливаем ВладелецФормы на целевой ЭлементУправления.
    // Это важно для корректной работы механизма оповещения выбора.
    ФормаИнициатор.ВладелецФормы = ЭлементУправления;
    // Устанавливаем ЗакрыватьПриВыборе в Ложь, чтобы форма-инициатор не закрывалась автоматически.
    ФормаИнициатор.ЗакрыватьПриВыборе = Ложь;

    // Приводим новое значение к типу, допустимому для элемента управления.
    // Это обеспечивает соответствие типам данных и предотвращает ошибки.
    НовоеЗначение = ЭлементУправления.ОграничениеТипа.ПривестиЗначение(Значение);

    // Если приведенное значение отличается от исходного или элемент управления только для просмотра,
    // то интерактивная запись невозможна, возвращаем Ложь.
    Если НовоеЗначение <> Значение Или ЭлементУправления.ТолькоПросмотр Тогда
        Возврат Ложь;
    КонецЕсли;

    // Ключевой момент: вызываем метод ОповеститьОВыборе().
    // Этот метод инициирует внутренние механизмы платформы,
    // которые имитируют выбор значения пользователем и запускают
    // связанные обработчики событий, включая ПриИзменении.
    ФормаИнициатор.ОповеститьОВыборе(Значение);

    // Восстанавливаем сохраненные значения свойств ВладелецФормы и ЗакрыватьПриВыборе.
    Если СтарыйЗакрыватьПриВыборе <> Неопределено Тогда
        ФормаИнициатор.ВладелецФормы = СтарыйВлазыватьПриВыборе;
        ФормаИнициатор.ЗакрыватьПриВыборе = СтарыйЗакрыватьПриВыборе;
    КонецЕсли;

    Возврат Истина; // Операция выполнена успешно
КонецФункции

Порядок вызова функции

Для корректной работы этой функции крайне важен правильный порядок действий. Разберем его по шагам:

  1. Получение формы документа: Сначала необходимо получить экземпляр формы документа, с которым мы хотим работать. Это можно сделать с помощью функции ПолучитьФорму().

    
    Форма = ПолучитьФорму("Документ.ПлатежноеПоручениеВходящее.ФормаОбъекта", Новый Структура("Ключ", ДокСсылка));
    

    Здесь ДокСсылка — это ссылка на документ, который мы собираемся модифицировать.

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

    
    Контрагент = ПолучитьКонтрагентаПоНаименованию(СтруктураДока.ДокСтрокаИзФайла[6].Значение);
    Форма.Объект.Контрагент = Контрагент;
    

    В этом примере мы получаем контрагента по наименованию и присваиваем его реквизиту Контрагент объекта Форма.Объект.

  3. Вызов функции ИнтерактивноЗаписатьВЭлементУправления: После присвоения значения реквизиту объекта, вызываем нашу вспомогательную функцию.

    
    ИнтерактивноЗаписатьВЭлементУправления(Форма.Элементы.Контрагент, Форма.Объект.Контрагент, ЭтаФорма);
    
    • Первый параметр (Форма.Элементы.Контрагент) — это сам элемент управления формы, соответствующий реквизиту.
    • Второй параметр (Форма.Объект.Контрагент) — значение, которое было присвоено реквизиту объекта.
    • Третий параметр (ЭтаФорма) — это форма-инициатор, то есть форма, из которой производится вызов.

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

Важное замечание: Практика показывает, что на платформе 8.3.18 и выше этот механизм работает только в том случае, если форма документа уже открыта и видима пользователю. Если форма не открыта, вызов обработчиков может не произойти.

Работа с реквизитами табличных частей

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

Предположим, мы хотим добавить новую строку в табличную часть "Товары" и заполнить поля "КоличествоУпаковок" и "Цена", ожидая, что при этом автоматически пересчитается сумма.


&НаКлиенте
Процедура ТестИнфостарт()
    // 1. Получаем открытую форму документа.
    // Этот код предполагает, что форма уже открыта.
    ОкнаПриложения = ПолучитьОкна();
    Форма = Неопределено;
    Для Каждого Строка Из ОкнаПриложения Цикл
        Если Лев(Строка.Заголовок, 29) = "Приобретение товаров и услуг " Тогда // Или другой заголовок вашей формы
            Для Каждого Строка2 Из Строка.Содержимое Цикл
                Если ТипЗнч(Строка2) = Тип("ФормаКлиентскогоПриложения") Тогда
                    Форма = Строка2;
                    Прервать;
                КонецЕсли;
            КонецЦикла;
            Прервать;
        КонецЕсли;
    КонецЦикла;

    Если Форма = Неопределено Тогда
        Сообщить("Форма документа не найдена или не открыта.");
        Возврат;
    КонецЕсли;

    // 2. Активизируем форму. Это критически важно для корректного срабатывания событий.
    Форма.Активизировать();

    // 3. Устанавливаем текущий элемент на табличную часть.
    Форма.ТекущийЭлемент = Форма.Элементы.Товары;

    // 4. Добавляем новую строку в табличную часть объекта.
    нСтр = Форма.Объект.Товары.Добавить();

    // 5. Устанавливаем текущую строку и текущий элемент внутри табличной части.
    // Это имитирует переход пользователя в новую строку и поле.
    Форма.Элементы.Товары.ТекущаяСтрока = нСтр.ПолучитьИдентификатор();
    Форма.Элементы.Товары.ТекущийЭлемент = Форма.Элементы.Товары.ПодчиненныеЭлементы.Найти("ТоварыКоличествоУпаковок");

    // 6. Вызываем ИзменитьСтроку для табличной части.
    // Это может быть необходимо для инициализации редактирования строки.
    Форма.Элементы.Товары.ИзменитьСтроку();

    // 7. Присваиваем значения реквизитам новой строки объекта.
    нСтр.КоличествоУпаковок = 14.123;

    // 8. Переходим к следующему полю и присваиваем значение.
    Форма.Элементы.Товары.ТекущийЭлемент = Форма.Элементы.Товары.ПодчиненныеЭлементы.Найти("ТоварыЦенаЗаказа");
    Форма.Элементы.Товары.ИзменитьСтроку(); // Повторный вызов может быть полезен, если логика формы этого требует
    нСтр.Цена = 154.321;

    // 9. Обязательно завершаем редактирование строки.
    // Без этого события ПриИзменении могут не отработать,
    // и строка останется в состоянии редактирования.
    Форма.Элементы.Товары.ЗакончитьРедактированиеСтроки(Ложь);

КонецПроцедуры

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

Обходной путь для обновления табличных частей через буфер обмена

Если прямой программный вызов событий в табличных частях оказывается слишком медленным или нестабильным, существует альтернативный "хак", который использует встроенный функционал платформы для работы с буфером обмена. Этот метод может быть особенно полезен в конфигурациях вроде "Бухгалтерии" или "Управления торговлей 11".

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

  2. Выделение и копирование строк: После заполнения, мы программно или вручную выделяем вставленные строки в табличной части формы. Затем имитируем нажатие кнопки "Копировать", которая обычно располагается над табличной частью. Это действие копирует данные строк во внутренний буфер базы данных.

  3. Удаление и вставка строк: Далее, имитируем нажатие клавиши "Delete" для удаления выделенных строк. Сразу после этого имитируем нажатие кнопки "Вставить".

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

Важные аспекты и ограничения

При работе с программным вызовом событий в 1С 8.3 следует учитывать несколько важных моментов:

Надеемся, что представленные решения и подробный анализ помогут вам эффективно автоматизировать работу с формами в 1С:Предприятии 8.3, значительно сократив объем ручного труда при заполнении документов и карточек.

← На главную