При разработке механизмов обмена данными или систем резервного копирования часто возникает задача получения строкового представления типа реквизита. Например, вместо простого типа СправочникСсылка нам нужно получить полное техническое имя, такое как СправочникСсылка.Контрагенты. Ситуация усложняется, если реквизит имеет составной тип данных или если нам запрещено использовать стандартные средства сериализации (XML, JSON, УИД).
В этой статье мы подробно разберем, как работать с метаданными для извлечения информации о типах, как корректно обрабатывать составные типы и как реализовать выгрузку документа на основе "естественных ключей" для последующего восстановления.
Для начала проанализируем ситуацию: у нас есть объект метаданных реквизита, и мы хотим получить его строковую идентификацию. Если мы используем функцию ТипЗнч() на конкретном значении, мы получим тип текущего значения, но не всегда это даст нам путь к метаданным. Чтобы получить строку вида СправочникСсылка.ИмяОбъекта, необходимо обратиться к глобальному менеджеру метаданных.
Рассмотрим пример кода, который позволяет преобразовать тип в его полное имя:
// Получаем тип значения реквизита
ТипТекущегоЗначения = ТипЗнч(ЗначениеРеквизита);
// Ищем объект метаданных по этому типу
ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипТекущегоЗначения);
Если ОбъектМетаданных <> Неопределено Тогда
ТипРеквизитаСтрокой = ОбъектМетаданных.ПолноеИмя();
Иначе
// Для примитивных типов (Строка, Число, Дата, Булево)
ТипРеквизитаСтрокой = Строка(ТипТекущегоЗначения);
КонецЕсли;
Метод ПолноеИмя() возвращает строку, которая идеально подходит для последующей загрузки, так как она содержит и класс объекта (Справочник, Документ), и его имя в конфигурации.
Выясним причину сложностей при работе с составными типами. Составной тип (объект ОписаниеТипов) может содержать в себе десятки возможных ссылок. При выгрузке нам важно зафиксировать не просто "какие типы допустимы", а "какой тип выбран сейчас".
Разберем по шагам алгоритм обработки реквизита в цикле:
Метаданные().Реквизиты.Посмотрим на пример реализации обхода реквизитов с учетом этих нюансов:
Для Каждого Реквизит Из ДокументОбъект.Метаданные().Реквизиты Цикл
ИмяРеквизита = Реквизит.Имя;
ЗначениеРеквизита = ДокументОбъект[ИмяРеквизита];
// Определяем тип конкретного значения
ТекущийТип = ТипЗнч(ЗначениеРеквизита);
МетаДанныеТипа = Метаданные.НайтиПоТипу(ТекущийТип);
Если МетаДанныеТипа <> Неопределено Тогда
ТипРеквизитаСтрокой = МетаДанныеТипа.ПолноеИмя();
Иначе
ТипРеквизитаСтрокой = Строка(ТекущийТип);
КонецЕсли;
// Формируем строку для файла
СтрокаВыгрузки = "#Реквизит|" + ИмяРеквизита + "|" + ТипРеквизитаСтрокой + "|" + ЗначениеРеквизита;
ФайлТХТ.ДобавитьСтроку(СтрокаВыгрузки);
КонецЦикла;
Согласно условию задачи, мы не можем использовать Уникальный Идентификатор (УИД). Это означает, что нам нужно разработать систему "естественных ключей" для поиска объектов в базе при загрузке. Проанализируем, что делает объекты уникальными:
Дата + Номер.Код или Наименование. Для подчиненных справочников — дополнительно Владелец, для иерархических — Родитель.При выгрузке ссылочного реквизита (например, "Контрагент") мы не можем просто записать его наименование. Нам нужно выгрузить данные, достаточные для его поиска. Рассмотрим пример структуры выгрузки ссылки:
#Реквизит|Контрагент|СправочникСсылка.Контрагенты|ИНН=7701010101;Наименование=ООО Ромашка;Код=00001
Не забудем, что документ состоит не только из шапки, но и из табличных частей. Проанализируем ситуацию: каждая табличная часть — это коллекция строк, а каждая строка содержит свои реквизиты. Нам нужно организовать вложенный цикл.
Для Каждого МетаТЧ Из ДокументОбъект.Метаданные().ТабличныеЧасти Цикл
ФайлТХТ.ДобавитьСтроку("@НачалоТЧ|" + МетаТЧ.Имя);
ТЧ = ДокументОбъект[МетаТЧ.Имя];
Для Каждого СтрокаТЧ Из ТЧ Цикл
ДанныеСтроки = "";
Для Каждого Колонка Из МетаТЧ.Реквизиты Цикл
ЗначениеКолонки = СтрокаТЧ[Колонка.Имя];
// Здесь должна быть логика получения типа и поиска ключа, аналогичная шапке
ДанныеСтроки = ДанныеСтроки + Колонка.Имя + ":" + ЗначениеКолонки + "|";
КонецЦикла;
ФайлТХТ.ДобавитьСтроку(ДанныеСтроки);
КонецЦикла;
ФайлТХТ.ДобавитьСтроку("@КонецТЧ|" + МетаТЧ.Имя);
КонецЦикла;
При загрузке данных из текстового файла нам придется выполнить обратную операцию. Разберем основные шаги восстановления объекта:
Документы[ИмяДокумента].СоздатьДокумент().НайтиПоКоду() или НайтиПоНаименованию().ОписаниеТипов. Если мы прочитали из файла тип СправочникСсылка.Контрагенты, мы можем создать описание типов: Новый ОписаниеТипов("СправочникСсылка.Контрагенты") и привести считанное строковое значение к этому типу.ДокументОбъект.Записать().ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение) в блоке Попытка...Исключение.Важный момент: Так как номер документа в 1С обычно присваивается автоматически при записи, в задании указано не восстанавливать старый номер, чтобы избежать конфликтов нумерации. Это упрощает задачу, так как нам не нужно заботиться о контроле уникальности номеров в целевой базе.
Таким образом, комбинируя работу с коллекциями метаданных (Метаданные.НайтиПоТипу) и строковые функции, можно создать гибкий механизм выгрузки, который будет работать даже в условиях жестких ограничений на использование стандартных форматов обмена.