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