При разработке внешних обработок заполнения часто возникает ситуация, когда нам необходимо загрузить данные в табличную часть существующего документа (например, Заказ клиента). Однако простое добавление строк в программном коде не инициирует выполнение логики, которая прописана в форме документа в процедурах типа ТоварыНоменклатураПриИзменении или ТоварыЦенаПриИзменении. Платформа 1С:Предприятие разделяет интерактивные действия пользователя и программное изменение данных. Рассмотрим детально, как решить эту задачу, сохранив целостность данных и не дублируя код.
Прежде всего, проанализируем ситуацию с точки зрения архитектуры платформы. Событие ПриИзменении привязано к элементу управления на форме (полю ввода), а не к самому реквизиту данных. Когда мы пишем код НоваяСтрока.Количество = 10;, мы меняем данные напрямую в памяти, минуя визуальный интерфейс. Платформа «не видит» в этом действия пользователя, поэтому события формы не генерируются. Это стандартное поведение, защищающее от бесконечных рекурсивных вызовов при программной обработке.
Самый правильный и «чистый» с точки зрения стандартов разработки 1С метод — это избавление от «технологического долга» путем переноса бизнес-логики из модуля формы. Рассмотрим, как это реализовать по шагам.
Шаг 1. Перенос кода в модуль объекта или общий модуль. Мы должны вынести расчеты (цены, суммы, налоги) в экспортную процедуру модуля объекта документа или в общий модуль (например, с префиксом Сервер). Проанализируем пример кода в модуле объекта:
// В модуле объекта Документ.ЗаказКлиента
Процедура РассчитатьПараметрыСтрокиТЧ(СтрокаТЧ) Экспорт
// Здесь размещается вся логика пересчета суммы, НДС и т.д.
СтрокаТЧ.Сумма = СтрокаТЧ.Количество * СтрокаТЧ.Цена;
// ... остальная логика
КонецПроцедуры
Шаг 2. Вызов из формы. Теперь в обработчике формы мы просто вызываем этот метод объекта:
&НаСервере
Процедура ТоварыКоличествоПриИзмененииНаСервере()
ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
ДокОбъект = РеквизитВЗначение("Объект");
ДокОбъект.РассчитатьПараметрыСтрокиТЧ(ТекущаяСтрока);
ЗначениеВДанныеФормы(ДокОбъект, Объект);
КонецПроцедуры
Шаг 3. Вызов из внешней обработки. Теперь ваша обработка загрузки может просто вызвать ту же самую экспортную процедуру после добавления строки, и логика будет идентичной.
Если глубокий рефакторинг сейчас невозможен, разберем более быстрый, но менее элегантный способ. Мы можем объявить процедуры формы как Экспорт. Однако помните, что вызвать их можно только в том случае, если форма документа открыта и у вас есть ссылка на её контекст.
Экспорт для нужных обработчиков.ПолучитьФорму().Важно: Этот метод работает только на клиенте, что накладывает серьезные ограничения на производительность при больших объемах данных.
В современных конфигурациях, таких как УТ 11, ERP или КА, большинство расчетов в табличных частях уже вынесено в специализированные общие модули. Вместо того чтобы пытаться вызвать события формы, проанализируем, какие процедуры БСП вызываются внутри формы. Часто это модули типа ОбработкаТабличныхЧастейКлиентСервер. Использование готовых функций БСП для расчета цен и сумм — это самый современный подход, который гарантирует корректность учета во всей системе.
Рассмотрим специфический метод, который позволяет «обмануть» платформу и заставить её запустить цепочку событий, как если бы пользователь нажимал кнопки. Этот способ крайне нестабилен и не рекомендуется для промышленного использования, но полезно знать о его существовании для экстренных случаев.
Суть заключается в использовании метода ОповеститьОВыборе при установленном фокусе на конкретном элементе формы. Проанализируем пример кода:
&НаКлиенте
Процедура ЗаполнитьСЭмуляцией(ФормаВладелец)
Товары = ФормаВладелец.Объект.Товары;
// Добавляем строку через элементы формы
ФормаВладелец.Элементы.Товары.ДобавитьСтроку();
НоваяСтрока = Товары[Товары.Количество() - 1];
// Эмулируем изменение цены
НоваяСтрока.Цена = 1500;
ФормаВладелец.ТекущийЭлемент = ФормаВладелец.Элементы.ТоварыЦена;
// Этот вызов может спровоцировать срабатывание обработчиков выбора и изменения
ФормаВладелец.ОповеститьОВыборе(НоваяСтрока.Цена);
ФормаВладелец.Элементы.Товары.ЗакончитьРедактированиеСтроки(Ложь);
КонецПроцедуры
Внимание: Этот метод сильно зависит от того, какое окно активно в данный момент. Если в процессе работы скрипта пользователь переключится на другое приложение, фокус собьется, и эмуляция не сработает или приведет к ошибке.
Если логика расчетов сложная и завязана на методы объекта, выясним, как правильно обработать данные на сервере без закрытия формы. Мы можем использовать преобразование контекста формы в объект документа, выполнить все программные действия и вернуть результат обратно в форму.
Разберем алгоритм:
Объект (данные формы) на сервер.ДокОбъект = ДанныеФормыВЗначение(Объект, Тип("ДокументОбъект.ЗаказКлиента"));.ДокОбъект.ЗначениеВДанныеФормы(ДокОбъект, Объект);.Этот подход позволяет использовать всю мощь серверного кода модуля объекта, при этом форма пользователя обновится автоматически, отобразив все рассчитанные суммы и реквизиты, хотя формальные события ПриИзменении так и не будут вызваны.
Подводя итог, можно сказать следующее: стремление «просто вызвать процедуру формы» обычно свидетельствует об ошибке в архитектуре документа. Бизнес-логика должна быть отделена от интерфейса.
Для решения вашей задачи мы рекомендуем:
Такой подход сделает ваш код надежным, легко тестируемым и соответствующим стандартам разработки 1С.