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