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