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