При разработке внешних обработок в среде 1С:Предприятие часто возникает задача валидации данных перед их записью или проведением расчетов. Особые трудности у начинающих разработчиков вызывает проверка заполнения реквизитов табличных частей. Платформа предоставляет стандартный механизм декларативной проверки, однако при попытке применить его к табличным частям с использованием условий на конкретные строки код часто начинает вести себя непредсказуемо или возвращает ошибки компиляции.
В этой статье мы подробно разберем, почему стандартный метод проверки заполнения в табличных частях может работать «не так, как ожидается», проанализируем типичные ошибки разработчиков и реализуем надежное решение для выборочной проверки строк.
---
Рассмотрим ситуацию, с которой столкнулся автор темы. В обработчике модуля объекта ОбработкаПроверкиЗаполнения он пытается динамически добавлять реквизит табличной части в проверку:
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
Если Маршрутки.Количество() Тогда
Если Маршрутки[0].Номенклатура = Справочники.Номенклатура.НайтиПоКоду("0000063079").Ссылка
И ЭлементыФормы.ТаблСостояний.ТекущиеДанные.Состояние = Справочники.Состояния.НайтиПоКоду("57")
И ЭлементыФормы.TablОпераций.ТекущиеДанные.ВидОперации = Перечисления.ВидыОпераций.Изготовление Тогда
ПроверяемыеРеквизиты.Добавить("Маршрутки.СостояниеВходногоКонтроля");
Иначе
// Удаление из проверки...
КонецЕсли;
КонецЕсли;
КонецПроцедуры
В данном фрагменте кода кроются сразу две серьезные методологические ошибки, из-за которых программа отказывается работать.
Ошибка первая: Нарушение контекста (попытка работы с формой на сервере).
Обратите внимание на конструкцию ЭлементыФормы.ТаблСостояний... в коде. Процедура ОбработкаПроверкиЗаполнения располагается в Модуле объекта внешней обработки. Этот модуль компилируется и выполняется исключительно на стороне сервера. Объектная модель на сервере ничего не знает о существовании интерфейсных элементов, таких как ЭлементыФормы (в обычных формах) или Элементы (в управляемых формах). Попытка обращения к ним вызовет критическую ошибку выполнения «Переменная не определена».
Ошибка вторая: Ограничение декларативной проверки табличных частей.
Когда мы добавляем в массив ПроверяемыеРеквизиты путь к реквизиту табличной части в формате "ИмяТабличнойЧасти.ИмяРеквизита", платформа 1С воспринимает это как директиву: «Проверить заполнение этого реквизита абсолютно во всех строках данной табличной части».
Но у автора условие накладывается выборочно (например, только для определенной номенклатуры в первой строке). Если мы воспользуемся стандартным массивом, то система начнет требовать заполнения реквизита СостояниеВходногоКонтроля во всех строках без исключения, что нарушает бизнес-логику процесса.
---
Если перед нами стоит простая задача — сделать так, чтобы определенная колонка табличной части всегда была заполнена во всех строках без каких-либо сложных условий, мы можем использовать штатный механизм платформы.
Для этого выполним следующие шаги:
После этого при вызове метода ПроверитьЗаполнение() в форме или при выполнении стандартных команд записи система автоматически проверит все строки табличной части. Нам даже не потребуется писать код в модуле объекта.
Если же эту проверку нужно включать или отключать динамически, мы можем управлять ей в процедуре ОбработкаПроверкиЗаполнения:
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
// Если определенное условие выполняется, то включаем проверку для всей колонки ТЧ
Если НужнаПроверкаТаблицы Тогда
ПроверяемыеРеквизиты.Добавить("Маршрутки.СостояниеВходногоКонтроля");
Иначе
// Если проверка не нужна, удаляем реквизит из массива проверяемых
ИндексРеквизита = ПроверяемыеРеквизиты.Найти("Маршрутки.СостояниеВходногоКонтроля");
Если ИндексРеквизита <> Неопределено Тогда
ПроверяемыеРеквизиты.Удалить(ИндексРеквизита);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
---
Если нам необходимо проверять заполнение реквизита только в определенных строках табличной части (например, в зависимости от выбранной номенклатуры или других условий в строке), стандартный массив ПроверяемыеРеквизиты нам не поможет. Здесь нужно применить ручной программный обход строк и точечный вывод ошибок с помощью объекта СообщениеПользователю.
Рассмотрим по шагам, как реализовать этот алгоритм, избежав ошибок контекста формы.
Поскольку в модуле объекта мы не можем напрямую обратиться к активным строкам на форме, нам нужно передать необходимые значения (например, текущее состояние и вид операции) через свойство ДополнительныеСвойства объекта обработки.
В коде формы (например, в процедуре при нажатии кнопки «Записать» или «Выполнить») перед вызовом проверки напишем следующий код:
&НаКлиенте
Процедура ЗаписатьДанные(Команда)
ВыполнитьЗаписьНаСервере();
КонецПроцедуры
&НаСервере
Процедура ВыполнитьЗаписьНаСервере()
// Получаем объект обработки из реквизита формы
ОбъектОбработки = РеквизитФормыВЗначение("Объект");
// Передаем параметры формы во временную структуру объекта
ОбъектОбработки.ДополнительныеСвойства.Вставить("ТекущееСостояние", Элементы.ТаблСостояний.ТекущиеДанные.Состояние);
ОбъектОбработки.ДополнительныеСвойства.Вставить("ВидОперации", Элементы.ТаблОпераций.ТекущиеДанные.ВидОперации);
// Запускаем проверку заполнения
Если Не ОбъектОбработки.ПроверитьЗаполнение() Тогда
Возврат; // Проверка не пройдена, прерываем операцию
КонецЕсли;
// Код дальнейшей записи данных...
КонецПроцедуры
Теперь в модуле объекта в предопределенной процедуре ОбработкаПроверкиЗаполнения мы можем безопасно извлечь переданные параметры и выполнить обход строк табличной части. При обнаружении ошибок мы будем использовать адресный вывод сообщений, чтобы платформа подсветила конкретную ячейку в конкретной строке таблицы.
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
// 1. Первым делом исключаем реквизит из стандартной автоматической проверки,
// так как мы будем проверять его вручную по собственной логике.
ИндексРеквизита = ПроверяемыеРеквизиты.Найти("Маршрутки.СостояниеВходногоКонтроля");
Если ИндексРеквизита <> Неопределено Тогда
ПроверяемыеРеквизиты.Удалить(ИндексРеквизита);
КонецЕсли;
// 2. Извлекаем параметры формы из Дополнительных Свойств
ПараметрСостояние = Неопределено;
ПараметрВидОперации = Неопределено;
ДополнительныеСвойства.Свойство("ТекущееСостояние", ПараметрСостояние);
ДополнительныеСвойства.Свойство("ВидОперации", ПараметрВидОперации);
// Задаем искомые эталонные значения для проверки
НужнаяНоменклатура = Справочники.Номенклатура.НайтиПоКоду("0000063079");
НужноеСостояние = Справочники.Состояния.НайтиПоКоду("57");
НужныйВидОперации = Перечисления.ВидыОпераций.Изготовление;
// 3. Проверяем внешние условия, переданные из формы
Если ПараметрСостояние = НужноеСостояние И ПараметрВидОперации = НужныйВидОперации Тогда
// Обходим строки табличной части по индексу для точного позиционирования ошибок
Для Индекс = 0 По Маршрутки.Количество() - 1 Цикл
ТекущаяСтрока = Маршрутки[Индекс];
// Проверяем условие для конкретной строки табличной части
Если ТекущаяСтрока.Номенклатура = НужнаяНоменклатура Тогда
// Если условие сошлось, проверяем заполнение целевого реквизита
Если Не ЗначениеЗаполнено(ТекущаяСтрока.СостояниеВходногоКонтроля) Тогда
// 4. Формируем сообщение пользователю с привязкой к конкретной ячейке табличной части
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "В строке № " + (Индекс + 1) + " для номенклатуры """ + ТекущаяСтрока.Номенклатура + """ не заполнено состояние входного контроля!";
// Указываем путь к полю на форме для автоматической активации ячейки при клике на ошибку
Сообщение.Поле = "Объект.Маршрутки[" + Формат(Индекс, "ЧГ=0") + "].СостояниеВходногоКонтроля";
// Связываем сообщение с текущим объектом обработки
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Сообщить();
// Устанавливаем флаг отказа, чтобы прервать процесс
Отказ = Истина;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
---
Использование ручного обхода и объекта СообщениеПользователю дает ряд существенных преимуществ:
Поле в формате "Объект.ИмяТЧ[ИндексСтроки].ИмяРеквизита", пользователь в интерфейсе программы сможет просто кликнуть дважды по сообщению об ошибке внизу экрана, и система автоматически сфокусирует курсор на проблемной ячейке в табличной части.Таким образом, мы успешно обошли ограничения стандартного декларативного механизма платформы и решили проблему контекста, обеспечив стабильную работу проверки заполнения табличной части нашей внешней обработки.