В разработке на платформе 1С часто возникает задача: при выборе значения в одном поле формы необходимо автоматически заполнить другое. Например, при выборе контрагента подставить его основной договор, или, как в нашем случае, при выборе предмета задачи установить периодичность ее выполнения. Давайте разберем, как правильно реализовать такой механизм в управляемых формах, какие события использовать и почему стандартный подход "в лоб" не всегда работает.
Проанализируем ситуацию: разработчик пытается установить значение реквизита Периодичность в зависимости от реквизита ПредметВзаимодействия в форме бизнес-процесса. Первая мысль — использовать событие ПриСозданииНаСервере. Однако в этот момент реквизит ПредметВзаимодействия еще не заполнен пользователем, и, соответственно, брать из него данные для анализа бессмысленно. Выясним, какое событие подходит для этой задачи лучше всего.
Наиболее правильный и логичный способ решить нашу задачу — использовать событие ПриИзменении для того поля, от которого зависит заполнение другого. В нашем случае это поле ПредметВзаимодействия. Это событие срабатывает на клиенте каждый раз, когда пользователь меняет значение в поле. Алгоритм действий будет следующим:
ПриИзменении для поля ПредметВзаимодействия на форме.Объект) должна происходить на сервере.Объект.ПредметВзаимодействия и на основе этого анализа присвоить нужное значение реквизиту Объект.Периодичность.Посмотрим на пример реализации. Сначала создадим клиентский обработчик события.
В модуле формы это будет выглядеть так:
&НаКлиенте
Процедура ПредметВзаимодействияПриИзменении(Элемент)
// Вызываем серверную процедуру для установки значения
УстановитьПериодичностьНаСервере();
КонецПроцедуры
Почему мы вызываем сервер? Хотя значение ПредметВзаимодействия доступно на клиенте, изменять основной реквизит объекта (Объект.Периодичность) следует на сервере, чтобы обеспечить целостность данных и выполнение всех серверных проверок. Разделение логики на клиентскую и серверную — это хорошая практика в 1С.
Теперь напишем саму серверную процедуру. Директива &НаСервереБезКонтекста используется, когда процедуре не нужен полный контекст формы, что немного оптимизирует вызов. Если же вам нужны данные формы (не только реквизиты объекта), используйте &НаСервере.
&НаСервереБезКонтекста
Процедура УстановитьПериодичностьНаСервере()
// Здесь должна быть ваша логика.
// Например, мы можем проверить, что предмет является ссылкой на определенный справочник,
// и установить значение по умолчанию.
// В простом примере, как в обсуждении на форуме, можно просто присвоить значение.
// В реальной задаче здесь будет анализ значения Объект.ПредметВзаимодействия.
Объект.Периодичность = Перечисления.Периодичность.РазВГод;
// Если логика сложнее, она может выглядеть так:
// Если ТипЗнч(Объект.ПредметВзаимодействия) = Тип("СправочникСсылка.Проекты") Тогда
// Объект.Периодичность = Объект.ПредметВзаимодействия.ОсновнаяПериодичностьЗадач;
// Иначе
// Объект.Периодичность = ПредопределенноеЗначение("Перечисление.Периодичность.ПустаяСсылка");
// КонецЕсли;
КонецПроцедуры
Этот подход гарантирует, что каждый раз, когда пользователь выберет или изменит "Предмет взаимодействия", поле "Периодичность" автоматически обновится согласно заложенной логике.
Что если нам нужно, чтобы значение поля Периодичность было установлено не только при изменении, но и при открытии уже существующей задачи? Например, если логика заполнения изменилась, и мы хотим при открытии старой задачи показать актуальное значение по умолчанию.
В этом случае нам поможет событие ПриЧтенииНаСервере. Оно срабатывает один раз, когда форма открывается для уже существующего в базе данных объекта. Данные объекта уже прочитаны, и мы можем их модифицировать или использовать для заполнения других реквизитов формы.
&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
// Проверяем, если периодичность еще не заполнена
Если ТекущийОбъект.Периодичность = Перечисления.Периодичность.ПустаяСсылка Тогда
// и при этом предмет взаимодействия уже есть,
Если ЗначениеЗаполнено(ТекущийОбъект.ПредметВзаимодействия) Тогда
// то выполняем логику заполнения
// (логика может быть вынесена в отдельную общую процедуру)
УстановитьПериодичностьПоПредмету(ТекущийОбъект);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Процедура УстановитьПериодичностьПоПредмету(ОбъектЗадачи)
// Здесь ваша логика, аналогичная той, что в процедуре УстановитьПериодичностьНаСервере()
// ...
ОбъектЗадачи.Периодичность = Перечисления.Периодичность.РазВГод;
КонецПроцедуры
Важный момент: Код в ПриЧтенииНаСервере изменяет данные реквизитов формы (Объект), но не записывает их в базу данных. Изменения будут сохранены только тогда, когда пользователь нажмет кнопку "Записать" или "Провести".
Рассмотрим еще один частый случай: мы создаем задачу не интерактивно, а программно, например, из формы другого документа, и хотим сразу передать в новую форму задачи нужный ПредметВзаимодействия.
Для этого используется механизм параметров формы. При вызове метода ОткрытьФорму() мы можем передать структуру с начальными значениями.
Шаг 1. Код вызова формы:
Предположим, у нас есть кнопка на форме документа "Заказ клиента", которая создает новую задачу.
&НаКлиенте
Процедура СоздатьЗадачу(Команда)
// Создаем структуру параметров
ПараметрыФормы = Новый Структура;
// Вставляем в нее ключ "ПредметВзаимодействия" и значение - ссылку на текущий документ
ПараметрыФормы.Вставить("ПредметВзаимодействия", Объект.Ссылка);
// Открываем форму нового бизнес-процесса "Задача", передавая параметры
ОткрытьФорму("БизнесПроцесс.Задача.Форма.ФормаБизнесПроцесса", ПараметрыФормы);
КонецПроцедуры
Шаг 2. Код приема параметров в форме задачи:
Теперь в форме задачи, в событии ПриСозданииНаСервере, нам нужно эти параметры "поймать" и обработать. Это событие идеально подходит для первоначального заполнения, так как оно выполняется до отображения формы пользователю.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Проверяем, был ли передан параметр "ПредметВзаимодействия"
Если Параметры.Свойство("ПредметВзаимодействия") Тогда
// Если да, то заполняем реквизит объекта этим значением
Объект.ПредметВзаимодействия = Параметры.ПредметВзаимодействия;
// И сразу же можем вызвать нашу логику для установки периодичности
УстановитьПериодичностьНаСервере();
КонецЕсли;
КонецПроцедуры
Таким образом, мы добиваемся того, что при программном создании задачи форма открывается с уже предзаполненным предметом и корректной периодичностью, что значительно упрощает работу пользователя.
Давайте подведем итог и систематизируем, какое событие для чего использовать:
<strong>ПриСозданииНаСервере</strong>: Используйте для первоначального заполнения реквизитов новой формы. Идеально для обработки параметров, переданных при открытии, или для установки значений по умолчанию, не зависящих от действий пользователя на форме.<strong>ПриЧтенииНаСервере</strong>: Используйте при открытии существующего объекта для дополнительной обработки прочитанных данных или заполнения реквизитов формы (не объекта), которые не хранятся в базе.<strong>ПриИзменении</strong>: Это ключевое событие для реализации динамической логики на форме. Оно срабатывает на клиенте в ответ на действия пользователя и является основным инструментом для создания взаимосвязанных полей.Для решения исходной задачи о зависимом заполнении полей наиболее подходящим является событие ПриИзменении, так как оно реагирует именно на ввод данных пользователем.