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