В практике программирования на платформе 1С часто возникает задача по контролю структуры данных. Например, необходимо запретить пользователям создавать номенклатуру в папке "Архив", заставить их обязательно выбирать группу для нового элемента или ограничить глубину иерархии справочника, чтобы дерево не разрасталось бесконечно.
Рассмотрим подробно, как решить эти задачи, используя механизм Расширений конфигурации. Этот подход позволяет внедрить логику проверок, не снимая основную конфигурацию с поддержки.
Для того чтобы предотвратить сохранение элемента, если он не удовлетворяет нашим условиям, лучше всего подходит событие модуля объекта ПередЗаписью. Именно в этом обработчике мы можем установить параметр Отказ в значение Истина, что прервет транзакцию записи.
В расширении нам необходимо создать перехватчик этого события. Рассмотрим, как это выглядит структурно.
В модуле расширения для справочника (например, "Номенклатура") создадим процедуру с аннотацией &Перед. Это гарантирует, что наша проверка выполнится до основного кода конфигурации.
&Перед("ПередЗаписью")
Процедура Расш1_ПередЗаписью(Отказ)
// Здесь будет наш код проверки
КонецПроцедуры
Теперь разберем конкретные сценарии проверок, которые обсуждались в теме.
Частая ошибка пользователей — создание элементов в корне справочника, когда по регламенту они должны лежать в папках. Нам нужно проверить, заполнено ли поле Родитель.
Для этого мы можем использовать глобальную функцию ЗначениеЗаполнено() или метод пустой ссылки. Рассмотрим оба варианта, чтобы понимать разницу.
Вариант с проверкой на заполненность (рекомендуемый):
Если Не ЗначениеЗаполнено(Родитель) Тогда
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(
"Элемент должен находиться в группе!",,
"Родитель",
,
Отказ);
Возврат;
КонецЕсли;
Вариант с проверкой на пустую ссылку, который упоминался в обсуждении (Родитель.Пустая()), тоже сработает, но использование ЗначениеЗаполнено считается более универсальным стандартом.
Допустим, у нас есть задача: запретить создание элементов в папке "Архив" или "Удаленные".
Простой проверки на равенство ссылки (Родитель = ЗапрещеннаяПапка) может быть недостаточно, если внутри запрещенной папки есть подпапки. В таком случае элемент, созданный в подпапке, пройдет простую проверку, хотя логически он все еще находится в "Архиве".
Для проверки вхождения элемента в иерархию (на любом уровне глубины) следует использовать метод ПринадлежитЭлементу().
Посмотрим на пример кода:
// Получаем ссылку на запрещенную группу (в реальном коде лучше использовать предопределенный элемент или константу)
ЗапрещеннаяГруппа = Справочники.Номенклатура.НайтиПоНаименованию("Архив");
Если ЗначениеЗаполнено(ЗапрещеннаяГруппа)
И ЭтотОбъект.ПринадлежитЭлементу(ЗапрещеннаяГруппа) Тогда
ТекстОшибки = "Создание элементов в папке Архив запрещено!";
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстОшибки,,,, Отказ);
КонецЕсли;
Иногда требуется жестко ограничить структуру справочника, например, запретить создавать группы или элементы глубже второго уровня. В конфигураторе мы можем задать ограничение количества уровней, но это влияет на весь справочник. Если же нам нужно программно контролировать это условие (например, только для определенных видов номенклатуры), нам поможет метод Уровень().
Метод Уровень() возвращает число, соответствующее глубине вложенности элемента.
Важно помнить: нумерация уровней начинается с 0.
0 — это корень справочника.
1 — первый уровень вложенности (внутри папки корня).
2 — второй уровень вложенности.
Разберем пример, где мы запрещаем создавать группы на втором уровне вложенности (то есть глубже, чем папки в корне):
// Проверяем, является ли текущий объект группой, если задача стоит ограничить именно иерархию папок
Если ЭтотОбъект.ЭтоГруппа Тогда
// Если уровень текущего элемента больше 1 (то есть это уровень 2 и глубже)
Если ЭтотОбъект.Уровень() > 1 Тогда
ТекстОшибки = "Запрещено создавать группы глубже первого уровня!";
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстОшибки,,,, Отказ);
КонецЕсли;
КонецЕсли;
Обратите внимание на использование свойства ЭтоГруппа. Если его не проверить, код будет запрещать создание обычных элементов на 2-м уровне, что может противоречить задаче.
При внедрении любых проверок в событие ПередЗаписью существует риск сломать автоматический обмен данными (РИБ, загрузка из XML/JSON, синхронизация с сайтом).
Когда данные загружаются программно, они могут не соответствовать новым правилам (например, в базе-источнике элемент уже лежит в папке "Архив", или структура иерархии там глубже).
Если мы не предусмотрим отключение проверки при загрузке, обмен завершится с ошибкой, и данные не будут синхронизированы.
Поэтому всегда первой строкой в процедуре проверки добавляйте следующее условие:
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
Это стандарт разработки 1С. Свойство ОбменДанными.Загрузка устанавливается в Истина платформой автоматически при чтении данных из планов обмена или при программной сериализации.
Соберем все рекомендации в единый, готовый к работе блок кода, который можно поместить в модуль объекта справочника в расширении.
&Перед("ПередЗаписью")
Процедура Расш_Справочник_ПередЗаписью(Отказ)
// 1. Пропускаем проверки при служебной загрузке данных
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// 2. Пример: Элемент обязательно должен быть в папке (не в корне)
// Проверка актуальна только для новых или переносимых элементов
Если Не ЗначениеЗаполнено(Родитель) И Не ЭтоГруппа Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Элемент нельзя создавать в корне! Выберите папку.";
Сообщение.Поле = "Родитель";
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Сообщить();
Отказ = Истина;
Возврат; // Дальше проверять нет смысла
КонецЕсли;
// 3. Пример: Ограничение уровня вложенности для групп
Если ЭтоГруппа И Уровень() > 1 Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Максимальный уровень вложенности групп - 1.";
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Сообщить();
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Хотя решение через расширение и код является рабочим, стоит упомянуть и другие подходы:
ПередЗаписью является лишь "защитой от дурака". Опытный пользователь может обойти его через универсальные обработки (если там включен режим обмена). Надежная защита строится через Роли и RLS.ПараметрыВыбора или СвязиПараметровВыбора для поля "Родитель" в форме элемента. Тогда пользователь физически не увидит или не сможет выбрать запрещенные группы.Однако, для быстрых и гибких проверок бизнес-логики использование расширения и события ПередЗаписью остается самым простым и эффективным методом.