Загрузка и выгрузка документов из текстового формата. Выгрузка дополнительных реквизитов (Субконто)

1. KurilovNikita01 11.10.23 15:35 Сейчас в теме
Здравствуйте! Дали тестовое задание на создание внешней обработки на выгрузку и загрузку любого документа в конфигурации в текстовый файл. Формат выгрузки нужно реализовать самому, пользоваться функциями "ЗначениеВСтрокуВнутр()" и "УникальныйИдентификатор" нельзя. Нельзя делать выгрузку в форматах XML и JSON. Обработка должна быть выполнена в бухгалтерии.
Необходимо выгружать всю необходимую информацию, чтобы при загрузке создавалась копия исходного документа. У меня получилось выгрузить все реквизиты документа и все реквизиты табличных частей, за исключением одного, "Субконто". Насколько я понял, это дополнительный реквизит. На форме указано, что его тип - "Характеристика.ВидыСубконтаХозрасчетные", но при проведении обработки он возвращает тип "Перечисление.ЭлементыЗатрат", а значение "Основная Номенклатурная Группа". Подскажите, как такой реквизит надо загружать и как его заполнить в последствии?
//Код для выгрузки реквизитов в файл:
&НаСервере
Процедура ВыгрузитьОбъектНаСервере()      
    Если Не ЗначениеЗаполнено(ВыбФайл) Тогда
        СообщениеТест = Новый СообщениеПользователю;
        СообщениеТест.Текст = "Поле ""Файл"" не заполнено";
        СообщениеТест.Поле = "Файл";
        СообщениеТест.Сообщить();
        Возврат;
    КонецЕсли;
    Если Не ЗначениеЗаполнено(Документ) Тогда
        СообщениеТест = Новый СообщениеПользователю;
        СообщениеТест.Текст = "Поле ""Документ"" не заполнено";
        СообщениеТест.Поле = "Документ";
        СообщениеТест.Сообщить();
        Возврат;
    КонецЕсли;
    Текст = Новый ТекстовыйДокумент;    
    СтрокаИмяИДата = Документ.Метаданные().Имя + ";" + Документ.Дата;
    Текст.ДобавитьСтроку(СтрокаИмяИДата);    
    Для каждого Реквизит Из Документ.Метаданные().Реквизиты Цикл
        Если Не Реквизит.Имя = "Номер" Тогда
            ТипРеквизита = ОпределениеТипаРеквизита(Реквизит);
            СтрокаТекст = "" + ТипРеквизита + ";" + Реквизит.Имя + ";" + Документ[Реквизит.Имя];
            Текст.ДобавитьСтроку(СтрокаТекст);
        КонецЕсли;
    КонецЦикла;
    Текст.ДобавитьСтроку("ТабличныеЧастиДокумента");
    Для каждого ТабличнаяЧасть Из Документ.Метаданные().ТабличныеЧасти Цикл
        ИмяТабЧасти = Документ[ТабличнаяЧасть.Имя];
        Текст.ДобавитьСтроку("ТабличнаяЧасть;" + ИмяТабЧасти);
        Таблица = ИмяТабЧасти.Выгрузить();
        СтрокаНомер = 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]);
        КонецЕсли;
    КонецЕсли;
    МассивПараметрыРеквизита = Новый Массив;
    СтруктураПараметрыРеквизита = Новый Структура;
    СтруктураПараметрыРеквизита.Вставить("РеквизитНазвание", РеквизитНазвание);
    СтруктураПараметрыРеквизита.Вставить("РеквизитЗначение", РеквизитЗначение);
    МассивПараметрыРеквизита.Добавить(СтруктураПараметрыРеквизита);
    Возврат(МассивПараметрыРеквизита);
КонецФункции
Показать
По теме из базы знаний
Найденные решения
6. user1936667 11.10.23 18:27 Сейчас в теме
Опять Alexrovich студентов набирает, предыдущих пожрал и переварил успешно, видимо...

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

При чтении делаешь наоборот - читаешь полное имя реквизита (с номером строки) и его идентификатор типа. Если он простой, то сразу берешь значение. Если же тип подразумевает поиск по реквизитам - дочитываешь их имена и значения, и ищешь по ним в нужном объекте, который ты знаешь по типу. Найденное просто кладешь в документ по полному имени реквизита.
Остальные ответы
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
2. RustamZz 11.10.23 16:32 Сейчас в теме
(1) У нас не принято код без тегов постить. Исправьте, тогда желающие помочь могут появиться.
3. KurilovNikita01 11.10.23 16:45 Сейчас в теме
4. RustamZz 11.10.23 17:01 Сейчас в теме
(3) В данном случае достаточно знать настоящий тип объекта и найти значение. Субконто в некоторых случаях может содержать несколько значений. Например у документа расчетов это может быть как платежный документ (поступление ДС, списание ДС, ПКО, РКО) так и товарный (поступление товаров, реализация товаров, возвраты). Поэтому в файле кроме вида субконто, нужно чтобы было и тип объекта в этом субконто.
5. KurilovNikita01 11.10.23 17:12 Сейчас в теме
(4) Окей, как получить настоящий тип объекта? В моей обработке я сделал отдельную функцию для определения типа, но, походу, придется снова её менять
7. user1936667 11.10.23 18:28 Сейчас в теме
(5) ТипЗнч(Значение).
Ваш К.О.
15. KurilovNikita01 13.10.23 16:07 Сейчас в теме
(7)И, возвращаясь к теме субконто, я все ещё не понял, как его загружать и выгружать.
6. user1936667 11.10.23 18:27 Сейчас в теме
Опять Alexrovich студентов набирает, предыдущих пожрал и переварил успешно, видимо...

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

При чтении делаешь наоборот - читаешь полное имя реквизита (с номером строки) и его идентификатор типа. Если он простой, то сразу берешь значение. Если же тип подразумевает поиск по реквизитам - дочитываешь их имена и значения, и ищешь по ним в нужном объекте, который ты знаешь по типу. Найденное просто кладешь в документ по полному имени реквизита.
8. KurilovNikita01 11.10.23 18:38 Сейчас в теме
(6) Получил больше, чем просил. Спасибо! Теперь надо всё это переварить и сделать правильно.
9. KurilovNikita01 12.10.23 18:58 Сейчас в теме
(6)
фактический тип

Попробовал подставить "ТипЗнч", но в таком случае каждый тип выдает значение "Объект метаданных". Как получить фактический тип и фактическое значение? Что вообще означает фактический тип? Как он может по умолчанию быть не фактическим?
10. user1936667 13.10.23 08:57 Сейчас в теме
(9)
выдает значение "Объект метаданных".
Да отцепись ты от этих метаданных уже.

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

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

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

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

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

Единственный момент когда возможно придется еще раз лезть в метаданные, это получение информации о конкретном значении, чтобы закодировать его тип.Что-то Я бы написал Метаданные.НайтиПоТипу и взял бы полное имя. Ну, перед этим проверить на число, строку и прочее неопределено.
11. KurilovNikita01 13.10.23 14:24 Сейчас в теме
(10)
ТипЗнч(Значение) = Тип("СправочникСсылка.Контрагенты")

ТипЗнч() возвращает не "СправочникСсылка.Контрагенты", а просто "Контрагент". Что я делаю не так?
Прикрепленные файлы:
12. RustamZz 13.10.23 14:34 Сейчас в теме
(11) Если на клетке слона написано тигр - не верь глазам своим.
Прикрепленные файлы:
13. KurilovNikita01 13.10.23 14:55 Сейчас в теме
(12) А как тогда получить значение "СправочникСсылка..."?
14. RustamZz 13.10.23 15:47 Сейчас в теме
17. KurilovNikita01 13.10.23 17:41 Сейчас в теме
(14) По заданию XML нельзя использовать
16. user1936667 13.10.23 17:33 Сейчас в теме
(11)
ТипЗнч() возвращает не "СправочникСсылка.Контрагенты", а просто "Контрагент"
Видит всевышний, я сделал все что мог. Иди, учи основы.
18. KurilovNikita01 13.10.23 17:55 Сейчас в теме
(16) Окей, я понял, ТипЗнч() не возвращает мне тип. Чтобы найти конкретный тип надо сравнить его с функцией Тип(). Но в моем задании может быть использован любой документ и, соответственно, любой реквизит. Неужели нужно будет делать перебор?
19. KurilovNikita01 13.10.23 19:45 Сейчас в теме
(18) Сделал перебор, всё отобралось правильно. Как сдам задание, поделюсь рабочим кодом. Юзеру1936667 и Рустаму спасибо за терпение и объяснения.
20. KurilovNikita01 17.10.23 17:05 Сейчас в теме
Короче я сначала перебираю все названия всех метаданных и запихиваю их в массив
МасМассивов = Новый Массив;
МасСправочники = Новый Массив; //МассивИменСправочников
Для Каждого ЭлСпр из Метаданные.Справочники Цикл
        МасСправочники.Добавить(ЭлСпр.Имя);
КонецЦикла;
МасМассивов.Добавить(МасСправочники);
МасДокументы = Новый Массив; //МассивИменДокументов
Для Каждого ЭлДок из Метаданные.Документы Цикл
        МасДокументы.Добавить(ЭлДок.Имя);
КонецЦикла;
МасМассивов.Добавить(МасДокументы);
МасПеречисления = Новый Массив; //МассивИменПеречислений
Для Каждого ЭлПчсл из Метаданные.Перечисления Цикл
        МасПеречисления.Добавить(ЭлПчсл.Имя);
КонецЦикла;
МасМассивов.Добавить(МасПеречисления);
МасПланыВидовРасчета = Новый Массив; //МассивИменПлановВидовРасчета
Для Каждого ЭлПВР из Метаданные.ПланыВидовРасчета Цикл
	МасПланыВидовРасчета.Добавить(ЭлПВР.Имя);	
КонецЦикла;
МасМассивов.Добавить(МасПланыВидовРасчета); //МассивВсехИмен
Показать

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

Функция возвращает тот самый "фактический" тип в текстовом формате для записи в текстовый файл.
ПланСчетов ведет на прямую в "хозрасчетный" потому что это единственный план счетов в моей конфигурации.
Всем спасибо и удачи с вашим кодом!!
21. PowerBoy 3377 18.10.23 08:12 Сейчас в теме
(20)
Ссылка.Метаданные().ПолноеИмя();
Оставьте свое сообщение

Для получения уведомлений об ответах подключите телеграм бот:
Инфостарт бот