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