При настройке интеграции 1С с внешними системами или при написании прямых SQL-запросов к базе данных разработчики часто сталкиваются с серьезной проблемой. Нам необходимо сопоставить конкретные значения перечислений (например, Перечисления.ХозяйственныеОперации.АвансовыйОтчет) с их физическим представлением в СУБД. В отличие от справочников или документов, у объектов типа ПеречислениеСсылка в платформе 1С отсутствует стандартный метод УникальныйИдентификатор(). Из-за этого стандартные способы получения UUID не работают.
В этой статье мы подробно разберем, как устроено хранение перечислений на уровне базы данных SQL, и рассмотрим два эффективных программных метода получения UUID значений перечислений штатными средствами встроенного языка 1С без ручного анализа конфигурации.
Существует заблуждение, что перечисления хранятся исключительно в метаданных и не имеют собственных таблиц в базе данных СУБД. На самом деле в СУБД для каждого перечисления создается физическая таблица.
Давайте проанализируем структуру этой таблицы:
_Enum<n>, где <n> — это внутренний числовой идентификатор объекта метаданных. Вы можете получить его с помощью функции ПолучитьСтруктуруХраненияБазыДанных()._IDRRef — уникальный идентификатор элемента перечисления (хранится в бинарном формате binary(16))._EnumOrder — числовое значение, определяющее индекс порядка (позицию) элемента в списке.Главная сложность: В СУБД отсутствуют понятные текстовые имена (например, "АвансовыйОтчет" или "БезНДС"). Там содержатся только бинарные UUID и числовой порядок. Чтобы сопоставить текстовые имена элементов конфигурации с их бинарными идентификаторами в SQL, нам необходимо программно выгрузить связи "Имя — UUID". Ниже мы разберем, как это сделать.
Этот способ основан на использовании системной функции ЗначениеВСтрокуВнутр(). Если передать в нее ссылку на значение перечисления, мы получим строковое представление системного формата 1С.
Рассмотрим конкретный пример. Вызов функции:
Сообщить(ЗначениеВСтрокуВнутр(Перечисления.ХозяйственныеОперации.АвансовыйОтчет));
Вернет следующую строку:
{"#",ae0f3889-6e23-4a7a-bf5d-cd197ab78fb7,1523:b4ea6960515f917b461f6aa3f786c0b6}
Разберем структуру полученных данных:
ae0f3889-6e23-4a7a-bf5d-cd197ab78fb7 — это фиксированный UUID класса "ПеречислениеСсылка" в платформе 1С. Он одинаков во всех базах данных.1523 — внутренний код самого перечисления в текущей информационной базе.b4ea6960515f917b461f6aa3f786c0b6 — это 32-символьный шестнадцатеричный идентификатор значения перечисления. Однако его байты переставлены местами по специфическому алгоритму платформы 1С.Чтобы преобразовать эту строку в стандартный UUID формата 8-4-4-4-12, выполним пошаговую перестановку символов. Разобьем 32-символьный хвост b4ea6960515f917b461f6aa3f786c0b6 на 5 блоков:
b4ea6960515f917b461f6aa3f786c0b6Склеим блоки в правильной последовательности: [Блок 5]-[Блок 4]-[Блок 3]-[Блок 1]-[Блок 2]. В результате мы получаем:
f786c0b6-6aa3-461f-b4ea-6960515f917b
Этот результат полностью совпадает с UUID из XML-файла конфигурации! Напишем готовую функцию для автоматического декодирования:
Функция ПолучитьUUIDИзВнутреннейСтроки(СсылкаПеречисления) Экспорт
ВнутренняяСтрока = ЗначениеВСтрокуВнутр(СсылкаПеречисления);
// Находим позицию двоеточия, после которого идет нужный нам хвост
ПозицияДвоеточия = Найти(ВнутренняяСтрока, ":");
Если ПозицияДвоеточия = 0 Тогда
Возврат Неопределено;
КонецЕсли;
// Вырезаем 32 символа идентификатора
Хвост = Сред(ВнутренняяСтрока, ПозицияДвоеточия + 1, 32);
// Разбиваем на блоки согласно алгоритму перестановки байт 1С
Блок1 = Сред(Хвост, 1, 4);
Блок2 = Сред(Хвост, 5, 12);
Блок3 = Сред(Хвост, 17, 4);
Блок4 = Сред(Хвост, 21, 4);
Блок5 = Сред(Хвост, 25, 8);
// Собираем стандартный GUID
СтандартныйUUID = Блок5 + "-" + Блок4 + "-" + Блок3 + "-" + Блок1 + "-" + Блок2;
Возврат НРег(СтандартныйUUID);
КонецФункции
Хотя первый способ отлично работает, функция ЗначениеВСтрокуВнутр() является недокументированной, а структура возвращаемой ею строки теоретически может измениться в будущих версиях платформы.
Существует полностью легальный, безопасный и крайне элегантный способ получить UUID значения перечисления — использовать штатные механизмы XML-сериализации. Если стандартная функция XMLСтрока(СсылкаПеречисления) возвращает лишь имя элемента (например, "БезНДС"), то принудительная выгрузка через объект ЗаписьXML возвращает полноценный UUID.
Рассмотрим пример кода, реализующий этот подход:
Функция ПолучитьUUIDЧерезXML(СсылкаПеречисления) Экспорт
// Создаем объект записи XML и настраиваем вывод в строку
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
// Записываем ссылку перечисления в XML-формате
ЗаписатьXML(ЗаписьXML, СсылкаПеречисления);
СтрокаXML = ЗаписьXML.Закрыть();
// На выходе получаем строку вида:
// <Value xsi:type="EnumRef.ХозяйственныеОперации">f786c0b6-6aa3-461f-b4ea-6960515f917b</Value>
// Извлекаем UUID с помощью простых строковых функций
ПозицияНачала = Найти(СтрокаXML, ">") + 1;
ПозицияКонца = Найти(СтрокаXML, "</");
Если ПозицияНачала > 1 И ПозицияКонца > ПозицияНачала Тогда
Возврат Сред(СтрокаXML, ПозицияНачала, ПозицияКонца - ПозицияНачала);
КонецЕсли;
Возврат "";
КонецФункции
Этот метод гарантирует абсолютную стабильность решения, так как использует стандартные механизмы платформы 1С, которые не изменятся при обновлениях.
Теперь решим главную практическую задачу: как автоматически сформировать таблицу соответствий "Имя значения" — "UUID" для перечислений, содержащих сотни элементов (например, ХозяйственныеОперации), чтобы использовать ее при построении SQL-запросов или выгрузке во внешнюю СУБД.
Напишем процедуру, которая обходит все значения метаданных перечисления и заполняет результирующую таблицу значений:
Функция СформироватьТаблицуСоответствийПеречисления(ИмяПеречисления) Экспорт
// Создаем таблицу значений для хранения результатов
ТаблицаСоответствий = Новый ТаблицаЗначений;
ТаблицаСоответствий.Колонки.Добавить("ИмяЭлемента", Новый ОписаниеТипов("Строка"));
ТаблицаСоответствий.Колонки.Добавить("UUID", Новый ОписаниеТипов("Строка"));
// Проверяем существование перечисления в метаданных
ОбъектМетаданных = Метаданные.Перечисления[ИмяПеречисления];
Если ОбъектМетаданных = Неопределено Тогда
ВызватьИсключение "Указанное перечисление не найдено в метаданных конфигурации!";
КонецЕсли;
// Обходим все значения перечисления
Для Каждого ЗначениеИзМетаданных Из ОбъектМетаданных.ЗначенияПеречисления Цикл
ИмяЭлемента = ЗначениеИзМетаданных.Имя;
СсылкаПеречисления = Перечисления[ИмяПеречисления][ИмяЭлемента];
// Получаем UUID надежным методом XML-сериализации
СтрокаUUID = ПолучитьUUIDЧерезXML(СсылкаПеречисления);
Если ЗначениеЗаполнено(СтрокаUUID) Тогда
НоваяСтрока = ТаблицаСоответствий.Добавить();
НоваяСтрока.ИмяЭлемента = ИмяЭлемента;
НоваяСтрока.UUID = СтрокаUUID;
КонецЕсли;
КонецЦикла;
Возврат ТаблицаСоответствий;
КонецФункции
Полученную таблицу соответствий вы можете:
JOIN в прямых SQL-запросах для связи таблиц документов или регистров с таблицей перевода UUID в человекочитаемые имена, полностью отказавшись от громоздких конструкций CASE WHEN ... THEN.Использование XML-сериализации позволяет динамически обновлять данные соответствий при каждом запуске системы или по регламентному заданию, что исключает рассинхронизацию при добавлении новых значений перечисления разработчиками 1С.