При разработке и доработке документов часто возникает необходимость проконтролировать корректность ввода данных пользователем. Рассмотрим практическую задачу: в документе ОтчетПроизводстваЗаСмену конфигурации «Управление производственным предприятием» (УПП 1.3) требуется проверить, заполнен ли реквизит табличной части ЗаказРезерв.
Разберем различные подходы к реализации этой проверки, начиная от стандартных функций платформы и заканчивая оптимизированными алгоритмами для больших объемов строк.
Самым простым и универсальным способом проверки конкретного значения является использование глобальной контекстной функции ЗначениеЗаполнено(). Она появилась еще в платформе 8.1 и остается базовым инструментом разработчика.
Проанализируем важный нюанс: реквизиты резервирования или заказов в производственных документах почти всегда имеют составной тип данных (например, ссылки на документы ЗаказПокупателя и ЗаказНаПроизводство). Функция ЗначениеЗаполнено() идеально подходит для таких полей, поскольку она автоматически возвращает Ложь, если в переменной находится:
Неопределено или Null.0001-01-01.Именно из-за составного типа данных стандартный метод поиска строк ТабличнаяЧасть.НайтиСтроки() в этой ситуации неприменим: пустая ссылка одного документа не будет эквивалентна пустой ссылке другого типа или значению Неопределено.
Посмотрим на пример базового обхода табличной части в цикле:
Для Каждого СтрПродукция Из Продукция Цикл
Если Не ЗначениеЗаполнено(СтрПродукция.ЗаказРезерв) Тогда
// Реквизит не заполнен, выполняем необходимые действия
Сообщить("В строке " + СтрПродукция.НомерСтроки + " не указан заказ резерва!");
КонецЕсли;
КонецЦикла;
В дереве метаданных у любого реквизита табличной части можно настроить свойство Проверка заполнения, выставив значение Выдавать ошибку. В этом случае платформа автоматически выполнит контроль при проведении или записи документа.
Однако выясним причину, почему в типовых конфигурациях вроде УПП 1.3 этот механизм подходит далеко не всегда. Платформенное свойство метаданных требует безусловного заполнения реквизита во всех строках документа без исключения. В реальном производственном учете выпуск продукции может оформляться как под конкретный заказ, так и на склад (без резерва) в рамках одного документа. Если включить жесткую проверку в метаданных, документ перестанет проводиться для бескомпромиссных складских выпусков.
Для реализации гибкого и интерактивного контроля разберем стандартный механизм платформы — процедуру ОбработкаПроверкиЗаполнения, расположенную в модуле объекта документа. Этот механизм позволяет программно управлять списком обязательных полей и формировать удобные для пользователя сообщения.
Если реквизит табличной части должен проверяться только при выполнении определенных условий (например, если заполнен определенный вид выпуска или статья затрат), действуем по следующему алгоритму:
ПроверяемыеРеквизиты.СообщениеПользователю, указывая путь к конкретной ячейке табличной части. Это позволит пользователю перейти к проблемной строке простым кликом по тексту ошибки.Посмотрим на программную реализацию в модуле объекта:
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
// Исключаем системную проверку, если она была задана в метаданных
ПозицияРеквизита = ПроверяемыеРеквизиты.Найти("Продукция.ЗаказРезерв");
Если ПозицияРеквизита <> Неопределено Тогда
ПроверяемыеРеквизиты.Удалить(ПозицияРеквизита);
КонецЕсли;
// Выполняем выборочный контроль строк
Для Индекс = 0 По Продукция.Количество() - 1 Цикл
Стр = Продукция[Индекс];
// Например, проверяем только если продукция требует резервирования
Если Стр.ТребуетРезерва И Не ЗначениеЗаполнено(Стр.ЗаказРезерв) Тогда
Отказ = Истина;
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Не заполнен заказ резерва в строке " + (Индекс + 1);
Сообщение.Поле = "Продукция[" + Индекс + "].ЗаказРезерв";
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Сообщить();
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Если в документе присутствуют сотни или тысячи строк, последовательный перебор коллекции в цикле на клиенте или сервере может вызывать задержки. Проанализируем оптимизированный вариант: валидацию с помощью запроса к табличной части документа.
Запрос мгновенно возвращает номера строк, где реквизит не указан, без перебора объектов памяти:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТЧ.НомерСтроки КАК НомерСтроки
|ИЗ
| Документ.ОтчетПроизводстваЗаСмену.Продукция КАК ТЧ
|ГДЕ
| ТЧ.Ссылка = &Ссылка
| И (ТЧ.ЗаказРезерв ЕСТЬ NULL
| ИЛИ ТЧ.ЗаказРезерв = ЗНАЧЕНИЕ(Документ.ЗаказПокупателя.ПустаяСсылка)
| ИЛИ ТЧ.ЗаказРезерв = ЗНАЧЕНИЕ(Документ.ЗаказНаПроизводство.ПустаяСсылка))";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Результат = Запрос.Выполнить();
Если Не Результат.Пустой() Тогда
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
// Обрабатываем найденные ошибки
КонецЦикла;
КонецЕсли;
Выясним важное правило архитектуры 1С: в конфигурациях уровня УПП 1.3 документы часто формируются программно без открытия визуального интерфейса (например, с помощью пакетных обработок, обменов по правилам конвертации данных или регламентных заданий).
ПередЗаписью или ПриИзменении формы срабатывают только при интерактивном вводе данных пользователем. При программной записи документа эти обработчики игнорируются.ОбработкаПроверкиЗаполнения, ПередЗаписью или ОбработкаПроведения модуля объекта гарантирует целостность учетных данных независимо от того, каким способом был создан документ.