Здравствуйте! Дали тестовое задание на создание внешней обработки на выгрузку и загрузку любого документа в конфигурации в текстовый файл. Формат выгрузки нужно реализовать самому, пользоваться функциями "ЗначениеВСтрокуВнутр()" и "УникальныйИдентификатор" нельзя. Нельзя делать выгрузку в форматах XML и JSON. Обработка должна быть выполнена в бухгалтерии.
Необходимо выгружать всю необходимую информацию, чтобы при загрузке создавалась копия исходного документа. У меня получилось выгрузить все реквизиты документа и все реквизиты табличных частей, за исключением одного, "Субконто". Насколько я понял, это дополнительный реквизит. На форме указано, что его тип - "Характеристика.ВидыСубконтаХозрасчетные", но при проведении обработки он возвращает тип "Перечисление.ЭлементыЗатрат", а значение "Основная Номенклатурная Группа". Подскажите, как такой реквизит надо загружать и как его заполнить в последствии?

```1c
    //Код для выгрузки реквизитов в файл:
    &НаСервере
    Процедура ВыгрузитьОбъектНаСервере()
    Если Не ЗначениеЗаполнено(ВыбФайл) Тогда
    СообщениеТест = Новый СообщениеПользователю;
    СообщениеТест.Текст = "Поле "Файл" не заполнено";
    СообщениеТест.Поле = "Файл";
    СообщениеТест.Сообщить();
    Возврат;
    КонецЕсли;
    Если Не ЗначениеЗаполнено(Документ) Тогда
    СообщениеТест = Новый СообщениеПользователю;
    СообщениеТест.Текст = "Поле "Документ" не заполнено";
    СообщениеТест.Поле = "Документ";
    СообщениеТест.Сообщить();
    Возврат;
    КонецЕсли;
    Текст = Новый ТекстовыйДокумент;
    СтрокаИмяИДата = Документ.Метаданные().Имя + ";" + Документ.Дата;
    Текст.ДобавитьСтроку(СтрокаИмяИДата);
    Для каждого Реквизит Из Документ.Метаданные().Реквизиты Цикл
    Если Не Реквизит.Имя = "Номер" Тогда
    ТипРеквизита = ОпределениеТипаРеквизита(Реквизит);
    СтрокаТекст = " + ТипРеквизита + ";" + Реквизит.Имя + ";" + Документ[Реквизит.Имя];
    Текст.ДобавитьСтроку(СтрокаТекст);
    КонецЕсли;
    КонецЦикла;
    Текст.ДобавитьСтроку("ТабличныеЧастиДокумента");
    Для каждого ТабличнаяЧасть Из Документ.Метаданные().ТабличныеЧасти Цикл
    ИмяТабЧасти = Документ[ТабличнаяЧасть.Имя];
    Текст.ДобавитьСтроку("ТабличнаяЧасть;" + ИмяТабЧасти);
    Таблица = ИмяТабЧасти.Выгрузить();
    СтрокаНомер = 0;
    Для каждого Строка из Таблица Цикл
    Для каждого Реквизит из ТабличнаяЧасть.Реквизиты Цикл
    ТипРеквизита = ОпределениеТипаРеквизита(Реквизит);
    СтрокаТекст = " + ТипРеквизита + ";" + Реквизит.Имя + ";" + ИмяТабЧасти[СтрокаНомер][Реквизит.Имя] + ";" + СтрокаНомер;
    Текст.ДобавитьСтроку(СтрокаТекст);
    КонецЦикла;
    СтрокаНомер = СтрокаНомер + 1;
    КонецЦикла;
    КонецЦикла;
    Текст.Записать(ВыбФайл);
    Сообщить("Реквизиты документа сохранены");
    КонецПроцедуры

    &НаСервере
    Функция ОпределениеТипаРеквизита(Реквизит)
    ТипыРеквизита = Реквизит.Тип.Типы();
    Для каждого ТипР из ТипыРеквизита Цикл
    Если Метаданные.НайтиПоТипу(ТипР) <> Неопределено Тогда
    ТипРеквизита = Метаданные.НайтиПоТипу(ТипР).ПолноеИмя();
    Иначе
    ТипРеквизита = Реквизит.Тип;
    КонецЕсли;
    КонецЦикла;
    Возврат(ТипРеквизита);
    КонецФункции

    //Код для загрузки документа:

    &НаСервере
    Процедура ЗагрузитьОбъектНаСервере()
    Текст = Новый ТекстовыйДокумент;
    Текст.Прочитать(ВыбФайл);

    МассивИМениИДаты = СтрРазделить(Текст.ПолучитьСтроку(1), ";");
    ИмяДокумента = МассивИмениИДаты[0];
    ДатаДокумента = Дата(МассивИмениИДаты[1]);
    НовыйДокумент = Документы[ИмяДокумента].СоздатьДокумент();
    НовыйДокумент.Дата = ДатаДокумента;
    Для НомерСтроки = 2 По Текст.КоличествоСтрок() Цикл
    ТекСтрока = Текст.ПолучитьСтроку(НомерСтроки);
    Если ТекСтрока = "ТабличныеЧастиДокумента" Тогда
    НомерТабличныхЧастей = НомерСтроки;
    Прервать;
    КонецЕсли;
    МассивСлов = СтрРазделить(ТекСтрока, ";");
    Если МассивСлов.Количество() < 3 Тогда
    Продолжить;
    КонецЕсли;
    МассивПараметрыРеквизита = ПоискРеквизита(МассивСлов);
    СтрукутураПараметрыРеквизита = МассивПараметрыРеквизита[0];
    РеквизитНазвание = СтрукутураПараметрыРеквизита.РеквизитНазвание;
    РеквизитЗначение = СтрукутураПараметрыРеквизита.РеквизитЗначение;
    НовыйДокумент[РеквизитНазвание] = РеквизитЗначение;
    КонецЦикла;

    СтрокаНомерТот = -1;
    Для НомерСтроки = НомерТабличныхЧастей По Текст.КоличествоСтрок() Цикл
    ТекСтрока = Текст.ПолучитьСтроку(НомерСтроки);
    МассивСлов = СтрРазделить(ТекСтрока, ";");
    Если МассивСлов[0] = "ТабличнаяЧасть" Тогда
    МассивИмени = СтрРазделить(МассивСлов[1], ".");
    ИмяТаблицы = МассивИмени[МассивИмени.Количество() - 1];
    //ИскомаяТаблица = НовыйДокумент.Метаданные().ТабличныеЧасти[ИмяТаблицы];
    ИскомаяТаблица = НовыйДокумент[ИмяТаблицы];
    Продолжить;
    КонецЕсли;
    МассивСловПроверка = СтрРазделить(ТекСтрока, ";", Ложь);
    Если МассивСловПроверка.Количество() < 4 Тогда
    Продолжить;
    КонецЕсли;
    МассивПараметрыРеквизита = ПоискРеквизита(МассивСлов);
    СтрукутураПараметрыРеквизита = МассивПараметрыРеквизита[0];
    РеквизитНазвание = СтрукутураПараметрыРеквизита.РеквизитНазвание;
    РеквизитЗначение = СтрукутураПараметрыРеквизита.РеквизитЗначение;
    СтрокаНомерЭтот = Число(МассивСлов[3]);
    Если СтрокаНомерЭтот <> СтрокаНомерТот Тогда
    НоваяСтрока = ИскомаяТаблица.Добавить();
    КонецЕсли;
    СтрокаНомерТот = СтрокаНомерЭтот;
    НоваяСтрока[РеквизитНазвание] = РеквизитЗначение;
    //ИскомаяТаблица[СтрокаНомер][РеквизитНазвание] = РеквизитЗначение;
    КонецЦикла;
    НовыйДокумент.Записать();
    КонецПроцедуры

    &НаСервере
    Функция ПоискРеквизита(МассивСлов)
    РеквизитНазвание = МассивСлов[1];
    Если МассивСлов[0] = "Число" Тогда
    РеквизитЗначение = Число(СтрЗаменить(МассивСлов[2]," ","));
    ИначеЕсли МассивСлов[0] = "Строка" Тогда
    РеквизитЗначение = Строка(МассивСлов[2]);
    ИначеЕсли МассивСлов[0] = "Дата" Тогда
    РеквизитЗначение = Дата(МассивСлов[2]);
    ИначеЕсли МассивСлов[0] = "Булево" Тогда
    Если МассивСлов[2] = "Да" Тогда
    РеквизитЗначение = Истина;
    Иначе
    РеквизитЗначение = Ложь;
    КонецЕсли;
    ИначеЕсли МассивСлов[1] = "Субконто" Тогда
    //ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.ЭлементыЗатрат
    МассивСубконто = СтрРазделить(МассивСлов[0],".");
    РеквизитЗначение = ПредопределенноеЗначение(ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные[МассивСубконто[1]]);
    Иначе
    ИскомыйОбъект = Метаданные.НайтиПоПолномуИмени(МассивСлов[0]);
    Если Метаданные.Справочники.Содержит(ИскомыйОбъект) Тогда
    РеквизитЗначение = Справочники[ИскомыйОбъект.Имя].НайтиПоНаименованию(МассивСлов[2]);
    ИначеЕсли Метаданные.Документы.Содержит(ИскомыйОбъект) Тогда
    ИскомыйДокумент = Документы[ИскомыйОбъект.Имя];
    МассивПервый = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(МассивСлов[2], " от "); //отрезаю дату
    МассивВторой = СтрРазделить(МассивПервый[0], " "); //отрезаю все остальное
    ИскомыйНомер = МассивВторой[МассивВторой.Количество() - 1]; //это и есть номер(по идее)
    ИскомаяДата = Дата(МассивПервый[1]);
    РеквизитЗначение = ИскомыйДокумент.НайтиПоНомеру(ИскомыйНомер, ИскомаяДата);
    ИначеЕсли Метаданные.Перечисления.Содержит(ИскомыйОбъект) Тогда
    РеквизитЗначение = Перечисления[ИскомыйОбъект.Имя][СтрЗаменить(МассивСлов[2]," ",")];
    ИначеЕсли Метаданные.ПланыСчетов.Содержит(ИскомыйОбъект) Тогда
    РеквизитЗначение = ПланыСчетов[ИскомыйОбъект.Имя].НайтиПоНаименованию(МассивСлов[2]);
    КонецЕсли;
    КонецЕсли;
    МассивПараметрыРеквизита = Новый Массив;
    СтруктураПараметрыРеквизита = Новый Структура;
    СтруктураПараметрыРеквизита.Вставить("РеквизитНазвание", РеквизитНазвание);
    СтруктураПараметрыРеквизита.Вставить("РеквизитЗначение", РеквизитЗначение);
    МассивПараметрыРеквизита.Добавить(СтруктураПараметрыРеквизита);
    Возврат(МассивПараметрыРеквизита);
    КонецФункции
```
---
Опять Alexrovich студентов набирает, предыдущих пожрал и переварил успешно, видимо...

(1) Ты вообще не туда воюешь. Типы из метаданных тебе нахрен не нужны. Из метаданных надо взять только имена реквизитов и имена табличных частей. После этого обходишь по этим именам документ, берешь оттуда фактические значения, как-то кодируешь их фактический тип и пишешь в файл: имя реквизита (с учетом возможного номера строки), какой-то придуманный идентификатор типа, имена реквизитов для поиска этого значения (например имя перечисления, код справочника, наименование справочника, номер документа, дата документа...) и значения этих реквизитов. Для простых типов реквизиты для поиска ессно не пишешь, там сразу значение.

При чтении делаешь наоборот - читаешь полное имя реквизита (с номером строки) и его идентификатор типа. Если он простой, то сразу берешь значение. Если же тип подразумевает поиск по реквизитам - дочитываешь их имена и значения, и ищешь по ним в нужном объекте, который ты знаешь по типу. Найденное просто кладешь в документ по полному имени реквизита.
---
У нас не принято код без тегов постить. Исправьте, тогда желающие помочь могут появиться.
---
Исправил
---
В данном случае достаточно знать настоящий тип объекта и найти значение. Субконто в некоторых случаях может содержать несколько значений. Например у документа расчетов это может быть как платежный документ (поступление ДС, списание ДС, ПКО, РКО) так и товарный (поступление товаров, реализация товаров, возвраты). Поэтому в файле кроме вида субконто, нужно чтобы было и тип объекта в этом субконто.
---
Окей, как получить настоящий тип объекта? В моей обработке я сделал отдельную функцию для определения типа, но, походу, придется снова её менять
---
ТипЗнч(Значение).
Ваш К.О.
---
И, возвращаясь к теме субконто, я все ещё не понял, как его загружать и выгружать.
---
Опять Alexrovich студентов набирает, предыдущих пожрал и переварил успешно, видимо...

(1) Ты вообще не туда воюешь. Типы из метаданных тебе нахрен не нужны. Из метаданных надо взять только имена реквизитов и имена табличных частей. После этого обходишь по этим именам документ, берешь оттуда фактические значения, как-то кодируешь их фактический тип и пишешь в файл: имя реквизита (с учетом возможного номера строки), какой-то придуманный идентификатор типа, имена реквизитов для поиска этого значения (например имя перечисления, код справочника, наименование справочника, номер документа, дата документа...) и значения этих реквизитов. Для простых типов реквизиты для поиска ессно не пишешь, там сразу значение.

При чтении делаешь наоборот - читаешь полное имя реквизита (с номером строки) и его идентификатор типа. Если он простой, то сразу берешь значение. Если же тип подразумевает поиск по реквизитам - дочитываешь их имена и значения, и ищешь по ним в нужном объекте, который ты знаешь по типу. Найденное просто кладешь в документ по полному имени реквизита.
---
Получил больше, чем просил. Спасибо! Теперь надо всё это переварить и сделать правильно.
---
фактический тип
Попробовал подставить "ТипЗнч", но в таком случае каждый тип выдает значение "Объект метаданных". Как получить фактический тип и фактическое значение? Что вообще означает фактический тип? Как он может по умолчанию быть не фактическим?
---
выдает значение "Объект метаданных".Да отцепись ты от этих метаданных уже.

Пусть у тебя есть документ с реквизитом шапки "Заказчик". Реквизит составного типа, например "СправочникСсылка.Контрагенты" и "СправочникСсылка.Партнеры".

То, что этот реквизит есть и у него имя "Заказчик", ты узнаешь по метаданным документа. После этого забываешь про метаданные и берешь фактическое значение из экземпляра документа: Значение = МойДокумент1["Заказчик"]. Потом смотришь тип этого фактического значения. Получаешь, например ТипЗнч(Значение) = Тип("СправочникСсылка.Контрагенты"). Соображаешь, что контрагентов ты ищешь по его рекивизиту "ИНН". Пишешь в файл что-то вроде "Заказчик:Справочник.Контрагенты:ИНН:7714434140"

При чтении понимаешь, что это справочник контрагентов, и искать надо по значению ИНН = 7714434140. Находишь, втыкаешь в реквизит "Заказчик". Всё.

Для второго документа там партнер. Берешь опять фактическое значение из экземпляра документа: Значение = МойДокумент2["Заказчик"], получаешь ТипЗнч(Значение) = Тип("СправочникСсылка.Партнеры"). Соображаешь, что партнеров ты ищешь по коду. Пишешь в файл что-то вроде "Заказчик:Справочник.Партнеры:Код:1234"

При чтении понимаешь, что это справочник партнеров, ищешь по код = 1234, втыкаешь в реквизит "Заказчик". Опять всё.

Единственный момент когда возможно придется еще раз лезть в метаданные, это получение информации о конкретном значении, чтобы закодировать его тип.Что-то Я бы написал Метаданные.НайтиПоТипу и взял бы полное имя. Ну, перед этим проверить на число, строку и прочее неопределено.
---
ТипЗнч(Значение) = Тип("СправочникСсылка.Контрагенты")
ТипЗнч() возвращает не "СправочникСсылка.Контрагенты", а просто "Контрагент". Что я делаю не так?
---
Если на клетке слона написано тигр - не верь глазам своим.
---
А как тогда получить значение "СправочникСсылка..."?
---
XMLТип
---
По заданию XML нельзя использовать
---
ТипЗнч() возвращает не "СправочникСсылка.Контрагенты", а просто "Контрагент"Видит всевышний, я сделал все что мог. Иди, учи основы.
---
Окей, я понял, ТипЗнч() не возвращает мне тип. Чтобы найти конкретный тип надо сравнить его с функцией Тип(). Но в моем задании может быть использован любой документ и, соответственно, любой реквизит. Неужели нужно будет делать перебор?
---
Сделал перебор, всё отобралось правильно. Как сдам задание, поделюсь рабочим кодом. Юзеру1936667 и Рустаму спасибо за терпение и объяснения.
---
Короче я сначала перебираю все названия всех метаданных и запихиваю их в массив

```1c
    МасМассивов = Новый Массив;
    МасСправочники = Новый Массив; //МассивИменСправочников
    Для Каждого ЭлСпр из Метаданные.Справочники Цикл
    МасСправочники.Добавить(ЭлСпр.Имя);
    КонецЦикла;
    МасМассивов.Добавить(МасСправочники);
    МасДокументы = Новый Массив; //МассивИменДокументов
    Для Каждого ЭлДок из Метаданные.Документы Цикл
    МасДокументы.Добавить(ЭлДок.Имя);
    КонецЦикла;
    МасМассивов.Добавить(МасДокументы);
    МасПеречисления = Новый Массив; //МассивИменПеречислений
    Для Каждого ЭлПчсл из Метаданные.Перечисления Цикл
    МасПеречисления.Добавить(ЭлПчсл.Имя);
    КонецЦикла;
    МасМассивов.Добавить(МасПеречисления);
    МасПланыВидовРасчета = Новый Массив; //МассивИменПлановВидовРасчета
    Для Каждого ЭлПВР из Метаданные.ПланыВидовРасчета Цикл
    МасПланыВидовРасчета.Добавить(ЭлПВР.Имя);
    КонецЦикла;
    МасМассивов.Добавить(МасПланыВидовРасчета); //МассивВсехИмен
```

Потом сверяю каждый из реквизитов с с каждым названием метаданных

```1c
    &НаСервере
    Функция ОпределениеТипаРеквизитаДеЛюкс(Реквизит, ТипОб, МасМассивов)
    //ТипОб = ТипЗнч(Документ[Реквизит.Имя])
    Для Каждого ЭлСпр из МасМассивов[0] Цикл //Справочник
    Если ТипОб = Тип("СправочникСсылка." + ЭлСпр) Тогда
    Возврат ("Справочник." + ЭлСпр);
    КонецЕсли;
    КонецЦикла;
    Для Каждого ЭлДок из МасМассивов[1] Цикл //Документ
    Если ТипОб = Тип("ДокументСсылка." + ЭлДок) Тогда
    Возврат ("Документ." + ЭлДок);
    КонецЕсли;
    КонецЦикла;
    Для Каждого ЭлПчсл из МасМассивов[2] Цикл //Перечисление
    Если ТипОб = Тип("ПеречислениеСсылка." + ЭлПчсл) Тогда
    Возврат ("Перечисление." + ЭлПчсл);
    КонецЕсли;
    КонецЦикла;
    Для Каждого ЭлПВР из МасМассивов[3] Цикл //ПланыВидовРасчета
    Если ТипОб = Тип("ПланВидовРасчетаСсылка." + ЭлПВР) Тогда
    Возврат ("ПланВидовРасчета." + ЭлПВР);
    КонецЕсли;
    КонецЦикла;
    Если ТипОб = Тип("ПланСчетовСсылка.Хозрасчетный") Тогда //ПланыСчетов
    Возврат ("ПланСчетов.Хозрасчетный");
    КонецЕсли;
    Возврат Реквизит.Тип; //Все остальное типа булево, число, строка и т.д.
    КонецФункции
```

Функция возвращает тот самый "фактический" тип в текстовом формате для записи в текстовый файл.
ПланСчетов ведет на прямую в "хозрасчетный" потому что это единственный план счетов в моей конфигурации.
Всем спасибо и удачи с вашим кодом!!
---
```1c
    Ссылка.Метаданные().ПолноеИмя();
```