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