При работе с конфигурациями 1С:Предприятия 8.3, особенно в версиях 8.3.17 и выше, часто возникает необходимость автоматизировать заполнение связанных полей формы после изменения значения одного реквизита. Стандартное присвоение значения реквизиту объекта формы не всегда приводит к срабатыванию обработчика события ПриИзменении, который отвечает за каскадное обновление других полей, как это происходит при интерактивном вводе пользователем. В этой статье мы рассмотрим различные подходы и примеры решения этой задачи, а также выявим важные особенности поведения платформы.
Для модификации типового функционала без изменения основной конфигурации, что является лучшей практикой и упрощает обновление, мы рекомендуем использовать расширения конфигурации. Этот механизм, доступный с платформы 8.3.5, позволяет нам вносить изменения, не затрагивая исходный код типовых объектов.
Добавление расширения: Сначала необходимо создать новое расширение конфигурации в режиме "Конфигуратор".
Заимствование формы: В расширении заимствуем форму, в которой находится требуемый реквизит. Это позволит нам работать с модулем этой формы.
Добавление экспортной клиентской процедуры: В модуль заимствованной формы помещаем новую экспортную клиентскую процедуру. Эта процедура будет доступна для вызова извне, например, из обработок или других форм.
Вызов обработчиков событий: Уже из этой экспортной процедуры мы сможем вызывать необходимые неэкспортные обработчики событий элементов формы, которые по умолчанию срабатывают при интерактивном изменении. Это позволяет нам имитировать действия пользователя, вызывая внутреннюю логику формы.
Данный подход дает нам гибкость в управлении событиями формы, позволяя перехватывать и модифицировать их поведение "перед", "после" или "вместо" типовых обработчиков. С версии 8.3.9 возможности расширений значительно расширились, включив работу с модулями объектов, менеджерами объектов и общими модулями.
Одним из наиболее эффективных способов имитации интерактивного ввода является использование специальной функции, которая задействует внутренние механизмы платформы для оповещения формы об изменении значения. Рассмотрим подробнее этот подход.
Для программной имитации записи значения в элемент управления формы и вызова связанных с этим событием обработчиков, мы можем использовать следующую функцию:
Функция ИнтерактивноЗаписатьВЭлементУправления(ЭлементУправления, Знач Значение, Знач ФормаИнициатор = Неопределено)
// Проверяем, если ФормаИнициатор не определена, то используем временную форму-пустышку.
// Это может быть полезно, если вызов происходит из контекста, где нет прямой формы-инициатора.
// В данном примере эти строки закомментированы, что означает, что ФормаИнициатор ожидается всегда.
// Если ФормаИнициатор = Неопределено Тогда
// ФормаИнициатор = ПолучитьФорму("ОбщаяФорма.Пустышка", ЭлементУправления);
// КонецЕсли;
// Сохраняем текущие значения свойств ВладелецФормы и ЗакрыватьПриВыборе формы-инициатора,
// чтобы восстановить их после операции.
СтарыйВладелец = ФормаИнициатор.ВладелецФормы;
СтарыйЗакрыватьПриВыборе = ФормаИнициатор.ЗакрыватьПриВыборе;
// Устанавливаем ВладелецФормы на целевой ЭлементУправления.
// Это важно для корректной работы механизма оповещения выбора.
ФормаИнициатор.ВладелецФормы = ЭлементУправления;
// Устанавливаем ЗакрыватьПриВыборе в Ложь, чтобы форма-инициатор не закрывалась автоматически.
ФормаИнициатор.ЗакрыватьПриВыборе = Ложь;
// Приводим новое значение к типу, допустимому для элемента управления.
// Это обеспечивает соответствие типам данных и предотвращает ошибки.
НовоеЗначение = ЭлементУправления.ОграничениеТипа.ПривестиЗначение(Значение);
// Если приведенное значение отличается от исходного или элемент управления только для просмотра,
// то интерактивная запись невозможна, возвращаем Ложь.
Если НовоеЗначение <> Значение Или ЭлементУправления.ТолькоПросмотр Тогда
Возврат Ложь;
КонецЕсли;
// Ключевой момент: вызываем метод ОповеститьОВыборе().
// Этот метод инициирует внутренние механизмы платформы,
// которые имитируют выбор значения пользователем и запускают
// связанные обработчики событий, включая ПриИзменении.
ФормаИнициатор.ОповеститьОВыборе(Значение);
// Восстанавливаем сохраненные значения свойств ВладелецФормы и ЗакрыватьПриВыборе.
Если СтарыйЗакрыватьПриВыборе <> Неопределено Тогда
ФормаИнициатор.ВладелецФормы = СтарыйВлазыватьПриВыборе;
ФормаИнициатор.ЗакрыватьПриВыборе = СтарыйЗакрыватьПриВыборе;
КонецЕсли;
Возврат Истина; // Операция выполнена успешно
КонецФункции
Для корректной работы этой функции крайне важен правильный порядок действий. Разберем его по шагам:
Получение формы документа: Сначала необходимо получить экземпляр формы документа, с которым мы хотим работать. Это можно сделать с помощью функции ПолучитьФорму().
Форма = ПолучитьФорму("Документ.ПлатежноеПоручениеВходящее.ФормаОбъекта", Новый Структура("Ключ", ДокСсылка));
Здесь ДокСсылка — это ссылка на документ, который мы собираемся модифицировать.
Присвоение значения реквизиту объекта: Прежде чем вызывать интерактивную запись, обязательно присвойте новое значение реквизиту самого объекта формы. Это подготовит данные формы к последующему событию.
Контрагент = ПолучитьКонтрагентаПоНаименованию(СтруктураДока.ДокСтрокаИзФайла[6].Значение);
Форма.Объект.Контрагент = Контрагент;
В этом примере мы получаем контрагента по наименованию и присваиваем его реквизиту Контрагент объекта Форма.Объект.
Вызов функции ИнтерактивноЗаписатьВЭлементУправления: После присвоения значения реквизиту объекта, вызываем нашу вспомогательную функцию.
ИнтерактивноЗаписатьВЭлементУправления(Форма.Элементы.Контрагент, Форма.Объект.Контрагент, ЭтаФорма);
Форма.Элементы.Контрагент) — это сам элемент управления формы, соответствующий реквизиту.Форма.Объект.Контрагент) — значение, которое было присвоено реквизиту объекта.ЭтаФорма) — это форма-инициатор, то есть форма, из которой производится вызов.Вызов ФормаИнициатор.ОповеститьОВыборе(Значение) внутри функции является ключевым, так как именно он запускает цепочку событий, имитирующую интерактивный выбор и последующее срабатывание обработчиков ПриИзменении.
Важное замечание: Практика показывает, что на платформе 8.3.18 и выше этот механизм работает только в том случае, если форма документа уже открыта и видима пользователю. Если форма не открыта, вызов обработчиков может не произойти.
Заполнение реквизитов в табличных частях формы имеет свои особенности, так как помимо изменения самого значения, необходимо также управлять активностью строки и элемента в табличной части. Рассмотрим пошаговый алгоритм.
Предположим, мы хотим добавить новую строку в табличную часть "Товары" и заполнить поля "КоличествоУпаковок" и "Цена", ожидая, что при этом автоматически пересчитается сумма.
&НаКлиенте
Процедура ТестИнфостарт()
// 1. Получаем открытую форму документа.
// Этот код предполагает, что форма уже открыта.
ОкнаПриложения = ПолучитьОкна();
Форма = Неопределено;
Для Каждого Строка Из ОкнаПриложения Цикл
Если Лев(Строка.Заголовок, 29) = "Приобретение товаров и услуг " Тогда // Или другой заголовок вашей формы
Для Каждого Строка2 Из Строка.Содержимое Цикл
Если ТипЗнч(Строка2) = Тип("ФормаКлиентскогоПриложения") Тогда
Форма = Строка2;
Прервать;
КонецЕсли;
КонецЦикла;
Прервать;
КонецЕсли;
КонецЦикла;
Если Форма = Неопределено Тогда
Сообщить("Форма документа не найдена или не открыта.");
Возврат;
КонецЕсли;
// 2. Активизируем форму. Это критически важно для корректного срабатывания событий.
Форма.Активизировать();
// 3. Устанавливаем текущий элемент на табличную часть.
Форма.ТекущийЭлемент = Форма.Элементы.Товары;
// 4. Добавляем новую строку в табличную часть объекта.
нСтр = Форма.Объект.Товары.Добавить();
// 5. Устанавливаем текущую строку и текущий элемент внутри табличной части.
// Это имитирует переход пользователя в новую строку и поле.
Форма.Элементы.Товары.ТекущаяСтрока = нСтр.ПолучитьИдентификатор();
Форма.Элементы.Товары.ТекущийЭлемент = Форма.Элементы.Товары.ПодчиненныеЭлементы.Найти("ТоварыКоличествоУпаковок");
// 6. Вызываем ИзменитьСтроку для табличной части.
// Это может быть необходимо для инициализации редактирования строки.
Форма.Элементы.Товары.ИзменитьСтроку();
// 7. Присваиваем значения реквизитам новой строки объекта.
нСтр.КоличествоУпаковок = 14.123;
// 8. Переходим к следующему полю и присваиваем значение.
Форма.Элементы.Товары.ТекущийЭлемент = Форма.Элементы.Товары.ПодчиненныеЭлементы.Найти("ТоварыЦенаЗаказа");
Форма.Элементы.Товары.ИзменитьСтроку(); // Повторный вызов может быть полезен, если логика формы этого требует
нСтр.Цена = 154.321;
// 9. Обязательно завершаем редактирование строки.
// Без этого события ПриИзменении могут не отработать,
// и строка останется в состоянии редактирования.
Форма.Элементы.Товары.ЗакончитьРедактированиеСтроки(Ложь);
КонецПроцедуры
Как мы видим из примера, активизация формы и установка текущего элемента на табличную часть и конкретное поле являются ключевыми шагами для того, чтобы платформа восприняла программные изменения как интерактивные и корректно отработала связанные с ними обработчики.
Если прямой программный вызов событий в табличных частях оказывается слишком медленным или нестабильным, существует альтернативный "хак", который использует встроенный функционал платформы для работы с буфером обмена. Этот метод может быть особенно полезен в конфигурациях вроде "Бухгалтерии" или "Управления торговлей 11".
Программное заполнение табличной части: Сначала заполняем все необходимые строки и колонки табличной части программно, как вы делали это ранее, напрямую присваивая значения реквизитам строк. На этом этапе связанные колонки (например, сумма, которая должна пересчитаться из количества и цены) могут остаться необновленными.
Выделение и копирование строк: После заполнения, мы программно или вручную выделяем вставленные строки в табличной части формы. Затем имитируем нажатие кнопки "Копировать", которая обычно располагается над табличной частью. Это действие копирует данные строк во внутренний буфер базы данных.
Удаление и вставка строк: Далее, имитируем нажатие клавиши "Delete" для удаления выделенных строк. Сразу после этого имитируем нажатие кнопки "Вставить".
Результат: При вставке данных из внутреннего буфера платформа обрабатывает эти строки так, как будто они были введены интерактивно, вызывая все необходимые процедуры ПриИзменении для каждого поля. Этот метод часто срабатывает очень быстро и надежно, так как он использует уже оптимизированные и проверенные механизмы самой платформы для массового ввода данных.
При работе с программным вызовом событий в 1С 8.3 следует учитывать несколько важных моментов:
Версии платформы: Как показала практика, поведение механизма может меняться от версии к версии. Например, на платформе 8.3.18 было подтверждено, что для успешного срабатывания обработчиков форма должна быть открыта и видима. В то время как на 8.3.14 тот же механизм мог работать и без предварительного открытия формы.
Открытая форма: Для многих методов, имитирующих интерактивные действия, крайне желательно (а иногда и обязательно), чтобы форма, с которой мы работаем, была уже открыта и активна в клиентском приложении. Если форма не открыта, внутренние механизмы взаимодействия с элементами формы могут не функционировать.
Различие между прямым вызовом и имитацией: Важно понимать, что прямой вызов процедуры обработчика события (например, &НаКлиенте Процедура КонтрагентПриИзменении(Элемент)) не всегда приводит к такому же эффекту, как интерактивное изменение. Прямой вызов обработает только код внутри этой процедуры, но не запустит всю цепочку связанных событий и проверок, которые платформа выполняет при реальном взаимодействии пользователя. Для этого и применяются методы, использующие ОповеститьОВыборе() или манипуляции с буфером обмена.
Событийное управление формами: Платформа 1С 8.3 полностью перешла на событийное управление формами, что означает асинхронную обработку событий и необходимость использования оповещений для реагирования на закрытие модальных окон. Это усложняет прямой программный контроль над "моментальным" результатом после вызова события, требуя адаптации к асинхронному подходу.
Метод УстановитьДействие(): Для более глубокого контроля над событиями форм и их элементов можно использовать метод УстановитьДействие(). Он позволяет программно назначить, изменить или отключить любой обработчик события. Например, вы можете динамически привязать свою процедуру к событию ПриИзменении, если это необходимо для вашей логики.
Надеемся, что представленные решения и подробный анализ помогут вам эффективно автоматизировать работу с формами в 1С:Предприятии 8.3, значительно сократив объем ручного труда при заполнении документов и карточек.