При разработке универсальных механизмов, таких как загрузка данных из внешних файлов (Excel, CSV, XML) или групповая обработка реквизитов, программист часто сталкивается с ситуацией, когда у него есть ОписаниеТипов (TypeDescription) реквизита и строковое представление значения. Задача состоит в том, чтобы превратить строку "НДС10" или "00001" в полноценную ссылку ПеречислениеСсылка.СтавкиНДС или СправочникСсылка.Номенклатура. Рассмотрим детально, как реализовать этот поиск наиболее эффективно.
Если мы точно знаем, что работаем с перечислением, и строка во внешнем файле совпадает с именем значения перечисления в конфигураторе (не с синонимом, а именно с внутренним именем), мы можем использовать коллекцию Перечисления. Проанализируем пример кода:
// Предположим, ВидРеквизита - это имя реквизита "СтавкаНДС"
// СтрокаЗначения - это "НДС18"
ИмяПеречисления = "СтавкиНДС"; // Его можно получить через метаданные
НужноеЗначение = Перечисления[ИмяПеречисления][СтрокаЗначения];
Этот способ самый быстрый, но он требует точного совпадения имен. Если пользователь в Excel написал синоним (например, "Без НДС"), данный метод вызовет ошибку, так как в коллекции Перечисления поиск идет по идентификатору.
Рассмотрим более универсальный системный механизм. Платформа 1С предоставляет мощную функцию XMLЗначение, которая умеет преобразовывать строковое XML-представление в значение нужного типа. Разберем по шагам, как это работает:
// Допустим, у нас есть тип и строковое значение
ТипОбъекта = Метаданные.Справочники.Номенклатура.Реквизиты.СтавкаНДС.Тип.Типы()[0];
СтрокаИзФайла = "НДС20";
Попытка
ПриведенноеЗначение = XMLЗначение(ТипОбъекта, СтрокаИзФайла);
Исключение
ПриведенноеЗначение = Неопределено;
КонецПопытки;
Этот метод отлично работает для перечислений (по имени), примитивных типов (числа, даты в формате XML, булево) и даже для ссылочных типов, если в строке передан уникальный идентификатор (GUID). Для интерактивной проверки таких данных подойдёт обработка для поиска ссылок по GUID.
Если нам нужно найти ссылку в справочнике по наименованию или коду, имея только ОписаниеТипов, алгоритм усложняется. Проанализируем ситуацию: мы не знаем заранее, справочник это, документ или перечисление. Нам нужно динамически определить менеджер объекта. Для неточного сопоставления строковых данных можно использовать обработку для поиска ссылок по схожести наименований.
Выполним следующие шаги:
ОписаниеТипов.Метаданные.НайтиПоТипу() для получения объекта описания метаданных.Посмотрим на пример универсальной функции:
Функция НайтиЗначениеПоТипуИСтроке(ОписаниеТиповРеквизита, СтрокаПоиска)
// 1. Получаем массив типов из описания
МассивТипов = ОписаниеТиповРеквизита.Типы();
Если МассивТипов.Количество() = 0 Тогда
Возврат Неопределено;
КонецЕсли;
// Для простоты рассмотрим первый тип (в случае не составного типа)
ТекущийТип = МассивТипов[0];
ОбъектМетаданных = Метаданные.НайтиПоТипу(ТекущийТип);
Если ОбъектМетаданных = Неопределено Тогда
// Это может быть примитивный тип (строка, число)
Возврат ОписаниеТиповРеквизита.ПривестиЗначение(СтрокаПоиска);
КонецЕсли;
// 2. Определяем класс объекта (Справочник, Перечисление и т.д.)
ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
ЧастиИмени = СтрРазделить(ПолноеИмя, ".");
КлассОбъекта = ЧастиИмени[0]; // Справочник
ИмяОбъекта = ЧастиИмени[1]; // Номенклатура
// 3. Выполняем поиск в зависимости от класса
Если КлассОбъекта = "Справочник" Тогда
// Сначала пробуем по коду, потом по наименованию
Ссылка = Справочники[ИмяОбъекта].НайтиПоНаименованию(СтрокаПоиска, Истина);
Если Ссылка.Пустая() Тогда
Ссылка = Справочники[ИмяОбъекта].НайтиПоКоду(СтрокаПоиска);
КонецЕсли;
Возврат Ссылка;
ИначеЕсли КлассОбъекта = "Перечисление" Тогда
// Поиск перечисления по синонимам значений
Для Каждого ЗначениеИзМетаданных Из ОбъектМетаданных.ЗначенияПеречисления Цикл
Если ЗначениеИзМетаданных.Синоним = СтрокаПоиска Или ЗначениеИзМетаданных.Имя = СтрокаПоиска Тогда
Индекс = ОбъектМетаданных.ЗначенияПеречисления.Индекс(ЗначениеИзМетаданных);
Возврат Перечисления[ИмяОбъекта].Получить(Индекс);
КонецЕсли;
КонецДля;
КонецЕсли;
Возврат Неопределено;
КонецФункции
Часто реквизиты имеют составной тип (например, Владелец в справочнике Договоры может быть и Контрагентом, и Организацией). В этом случае ОписаниеТипов.ПривестиЗначение() не поможет найти объект в базе данных, оно лишь очистит значение, если оно не соответствует типам.
Проанализируем стратегию работы с такими данными:
Нужно перебирать все доступные типы в цикле и пытаться найти совпадение в каждом из них. Как только поиск в одном из справочников (или перечислений) вернул непустую ссылку, мы прекращаем поиск. Это единственный надежный способ при загрузке данных из "плоских" файлов, где не указан конкретный тип объекта.
Не забываем про типизацию простых значений. Метод ПривестиЗначение() отлично справляется с преобразованием типов, если на вход подается строка, а в описании типов указано Число или Булево.
// Если ОписаниеТипа содержит "Число", строка "123.45" превратится в число 123.45
// Если "Булево", строка "Да" или "Истина" станет логическим Истина
Результат = ОписаниеТипа.ПривестиЗначение(СтрокаИзФайла);
Для реализации качественного поиска значений по описанию типа мы рекомендуем комбинировать подходы:
ОписаниеТипов.ПривестиЗначение().XMLЗначение для поиска по внутренним именам.Справочники[Имя] с последующим вызовом НайтиПоНаименованию() или НайтиПоКоду().XMLЗначение, если есть возможность передавать данные в стандартных форматах платформы.Такой системный подход позволит создать отказоустойчивый механизм загрузки данных, который не будет "падать" при малейшем изменении структуры справочников или форматов входных файлов.