При проектировании систем прав доступа, механизмов запрета редактирования или различных матриц настроек в 1С:Предприятии разработчики часто сталкиваются с необходимостью привязать определенные правила к конкретным объектам метаданных — например, к определенным справочникам. В современных конфигурациях, созданных на базе БСП (Библиотеки Стандартных Подсистем), эта задача легко решается с помощью стандартного справочника ИдентификаторыОбъектовМетаданных. Но что делать, если перед нами старая конфигурация (например, старые редакции УПП, Комплексной автоматизации или самописные конфигурации на обычных формах), где БСП попросту нет?
В данной статье мы подробно проанализируем, почему стандартный подход с хранением имен объектов в виде обычных строк является опасным, и детально разберем три альтернативных технических решения с примерами реализации.
На первый взгляд, самое простое решение — создать в регистре сведений измерение с типом Строка и записывать туда имя справочника в конфигураторе (например, "Номенклатура" или "Контрагенты"). Однако этот подход таит в себе серьезные архитектурные риски:
УдалитьНоменклатура, а его синоним помечается как «Не используется». Если ваша логика завязана на строковое значение "Номенклатура", проверка перестанет работать. Старый справочник окажется незащищенным, а попытка обратиться к нему по старому имени в коде может вызвать критическую ошибку выполнения.Подразделения будет переименован в СтруктурныеЕдиницы), все строковые связи в базе данных «ослепнут». База данных никак не отреагирует на это переименование, и вам придется вручную искать и переписывать все строки в регистре сведений.Для создания отказоустойчивой архитектуры нам необходимы решения, которые будут автоматически отслеживать изменения метаданных или сводить ручную поддержку к минимуму.
Давайте рассмотрим первый надежный платформенный метод. Вместо хранения текстовой строки мы настраиваем измерение нашего регистра сведений как составной тип данных, в который включаем ссылки на все справочники, требующие контроля.
При записи настройки в измерение регистра мы будем помещать не заполненный элемент, а пустую ссылку нужного типа (например, значение, возвращаемое методом Справочники.Номенклатура.ПустаяСсылка()).
Как этот механизм работает «под капотом» платформы:
Платформа 1С:Предприятие берет на себя всю работу по реструктуризации базы данных на уровне СУБД. Если в конфигураторе справочник переименовывается, платформа автоматически обновляет описание типов во всех таблицах. Наша ссылка внутри регистра гарантированно не сломается и продолжит указывать на нужную таблицу СУБД (даже если она теперь называется УдалитьНоменклатура).
Для проверки прав или ограничений в коде нам достаточно сопоставить тип проверяемого объекта с типом, хранящимся в регистре. Разберем пример реализации такой проверки:
// Функция проверяет, установлен ли запрет редактирования для объекта
Функция ОбъектЗапрещенКРедактированию(ОбъектПроверки) Экспорт
ТипОбъекта = ТипЗнч(ОбъектПроверки);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗапрещенныеСправочники.ОбъектМетаданных КАК ОбъектМетаданных
|ИЗ
| РегистрСведений.ЗапрещенныеСправочники КАК ЗапрещенныеСправочники";
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
Возврат Ложь;
КонецЕсли;
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Сравниваем тип проверяемого объекта с типом пустой ссылки из регистра
Если ТипЗнч(Выборка.ОбъектМетаданных) = ТипОбъекта Тогда
Возврат Истина;
КонецЕсли;
КонецЦикла;
Возврат Ложь;
КонецФункции
Преимущества метода:
Недостатки метода:
Если вы хотите избежать изменения метаданных регистра при добавлении каждого нового контролируемого объекта, проанализируем другой вариант — создание собственного облегченного аналога механизма БСП. Мы создадим специальный справочник, который будет выступать «прослойкой» между метаданными конфигурации и записями в регистре сведений.
Создадим справочник с именем НашиИдентификаторыОбъектовМетаданных. В регистре сведений измерение будет иметь единственный тип — ссылку на наш новый справочник. Существует два пути реализации сопоставления.
В справочнике НашиИдентификаторыОбъектовМетаданных мы вручную создаем предопределенные элементы, имена которых соответствуют справочникам в конфигураторе (например, предопределенный элемент Справочник_Номенклатура).
Если разработчик переименовывает справочник Номенклатура, он обязан одновременно переименовать и предопределенный элемент нашего справочника. Поскольку связь в регистре сведений держится на уникальном GUID предопределенного элемента, сами данные в регистре при этом не пострадают.
Чтобы исключить ручную работу в конфигураторе, мы можем написать процедуру автоматического обновления нашего справочника, которая будет сканировать метаданные при запуске системы или при обновлении информационной базы. Для этого добавим в наш справочник реквизиты ИмяОбъекта (Строка) и Синоним (Строка).
Давайте разберем алгоритм, который автоматически находит изменения, переименовывает записи в нашей таблице и сохраняет ссылки нетронутыми:
Процедура ОбновитьНашиИдентификаторыМетаданных() Экспорт
// 1. Создадим соответствие для ручной обработки известных переименований от "1С"
ТаблицаПереименований = Новый Соответствие;
ТаблицаПереименований.Вставить("Справочник.Номенклатура", "Справочник.УдалитьНоменклатура");
// Сюда разработчик может добавлять новые пары при обновлениях релизов
НачатьТранзакцию();
Попытка
// Обходим все справочники в конфигурации
Для Каждого МетаданныеСправочника Из Метаданные.Справочники Цикл
ПолноеИмяМетаданных = "Справочник." + МетаданныеСправочника.Имя;
СинонимОбъекта = ?(ЗначениеЗаполнено(МетаданныеСправочника.Синоним), МетаданныеСправочника.Синоним, МетаданныеСправочника.Имя);
// Ищем, есть ли уже такой идентификатор в нашем справочнике
СсылкаНаИдентификатор = Справочники.НашиИдентификаторыОбъектовМетаданных.НайтиПоРеквизиту("ИмяОбъекта", ПолноеИмяМетаданных);
Если СсылкаНаИдентификатор.Пустая() Тогда
// Проверяем, возможно объект был переименован
СтароеИмя = "";
Для Каждого КлючИЗначение Из ТаблицаПереименований Цикл
Если КлючИЗначение.Значение = ПолноеИмяМетаданных Тогда
СтароеИмя = КлючИЗначение.Ключ;
Прервать;
КонецЕсли;
КонецЦикла;
Если ЗначениеЗаполнено(СтароеИмя) Тогда
// Объект переименован! Найдем старый элемент по его старому имени и актуализируем его
СсылкаНаСтарый = Справочники.НашиИдентификаторыОбъектовМетаданных.НайтиПоРеквизиту("ИмяОбъекта", СтароеИмя);
Если Не СсылкаНаСтарый.Пустая() Тогда
ОбъектИдентификатора = СсылкаНаСтарый.ПолучитьОбъект();
ОбъектИдентификатора.ИмяОбъекта = ПолноеИмяМетаданных;
ОбъектИдентификатора.Наименование = СинонимОбъекта;
ОбъектИдентификатора.Записать();
Продолжить;
КонецЕсли;
КонецЕсли;
// Если это действительно новый справочник, просто создаем элемент
НовыйИдентификатор = Справочники.НашиИдентификаторыОбъектовМетаданных.СоздатьЭлемент();
НовыйИдентификатор.Наименование = СинонимОбъекта;
НовыйИдентификатор.ИмяОбъекта = ПолноеИмяМетаданных;
НовыйИдентификатор.Записать();
Иначе
// Если элемент найден, обновим синоним, если он изменился
ОбъектИдентификатора = СсылкаНаИдентификатор.ПолучитьОбъект();
Если ОбъектИдентификатора.Наименование <> СинонимОбъекта Тогда
ОбъектИдентификатора.Наименование = СинонимОбъекта;
ОбъектИдентификатора.Записать();
КонецЕсли;
КонецЕсли;
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ЗаписатьЛогСобытий("Ошибка обновления идентификаторов объектов метаданных: " + ОписаниеОшибки());
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
Преимущества метода:
Рассмотрим еще один интересный низкоуровневый механизм платформы. У каждого класса объектов в 1С:Предприятии существует внутренний уникальный идентификатор типа данных. Его можно извлечь из пустой ссылки программно с помощью системной функции ЗначениеВСтрокуВнутр().
Если мы передадим в эту функцию пустую ссылку справочника Номенклатура:
Результат = ЗначениеВСтрокуВнутр(Справочники.Номенклатура.ПустаяСсылка());
Платформа вернет нам строковое системное представление объекта следующего вида:
{"#",a86305d7-b8a1-4312-901f-2ade3efdb351,135:00000000000000000000000000000000}
Разберем структуру этой строки:
a86305d7-b8a1-4312-901f-2ade3efdb351 — уникальный системный идентификатор класса "СправочникСсылка". Он является постоянным и абсолютно одинаковым во всех базах данных 1С любого релиза.135 — это внутренний числовой идентификатор таблицы конкретного справочника (в нашем случае Номенклатура) в текущей базе данных.Важное предостережение при использовании этого метода:
Числовой идентификатор таблицы (в нашем примере — 135) генерируется платформой динамически при создании объекта метаданных в конфигураторе. В вашей рабочей базе он может быть равен 135, а в тестовой базе или в базе-приемнике при обмене данными через РИБ (распределенную информационную базу) этот же справочник может получить идентификатор 142.
Поэтому хранить подобные системные строки во внутренних регистрах настроек категорически не рекомендуется. Настройки гарантированно «слетят» при переносе данных, выгрузке/загрузке базы в формате XML или при использовании механизмов синхронизации данных.
Выбор оптимального архитектурного решения зависит от масштаба вашей конфигурации и частоты внесения изменений: