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