gifts2017

Пример обработки загрузки данных из XML-файла в облаке 1С:Fresh

Опубликовал Дмитрий Кашпаров (laperuz) в раздел Программирование - Практика программирования

Внешняя обработка, адаптированная для работы в УНФ, размещенной в облаке 1С:Fresh (легко редактируется под любую другую конфигурацию)
В обработке использованы следующие механизмы (их варианты при работе в безопасном режиме):
1. Механизм выполнения сценариев дополнительных обработок.
2. Механизм отложенной записи объектов.
3. Механизм клиент-серверной передачи файлов.
4. Механизм чтения XML в безопасном режиме.
5. Механизм создания и поиска элементов с помощью UID.

Обработка загрузки прайс-листа поставщика из файла XML в конфигурацию 1С:Управление Небольшой Фирмой. Работает в файловом, клиент-серверном варианте, а также в безопасном режиме исполнения дополнительных обработок технологии 1С:Fresh.

Обработка разрабатывалась под конкретного клиента, здесь выкладывается с целью поделиться принципами работы с файлами, объектами при исполнении кода в базах 1С:Fresh.

1.    Особого внимания стоит механизм выполнения сценариев дополнительных обработок.

Суть данного механизма в том, что вся обработка представляет собой набор этапов исполнения. Каждый этап - вызов процедуры либо функции как внешней обработки, так и объектов конфигурации. При этом, передача параметров в функцию происходит не в явном виде, а с помощью своеобразного API, который представляется некоторыми общими модулями. Отличить общие модули можно по их наименованию вида "ДополнительныеОтчетыИОбработкиВБезопасномРежиме*".

Пример сценария исполнения:

        Конструктор = ДополнительныеОтчетыИОбработкиВБезопасномРежимеИнтерфейс;
	
	Сценарий = Конструктор.НовыйСценарий();
	
	Этап = Конструктор.ДобавитьМетодКонфигурации(Сценарий,
			"ДополнительныеОтчетыИОбработкиВБезопасномРежиме.ЧтениеXMLИзДвоичныхДанных", // Имя метода
			"ЧтениеXML" // Сохранение результата в переменную
		);
	Конструктор.ДобавитьЗначение(Этап, АдресФайлаДанных);
	
	Если ОтлаживатьКакВстроенную Тогда
		ИмяОбработки = Метаданные().Имя;
		Если Метаданные.НайтиПоПолномуИмени("Обработка." + ИмяОбработки) = Неопределено Тогда
			
			ТекстСообщенияОбОшибке = 
			НСтр("ru = 'Эта обработка предназначена для использования только в подсистеме ""Дополнительные отчеты и обработки"".
                  |Запускать ее на выполнение как внешюю обработку через главное меню (Файл - Открыть) нельзя.
                  |Для проверки работы необходимо:
                  |1. Добавить эту обработку в справочник ДополнительныеОтчетыИОбработки
                  |2. Запустить ее через командный интерфейс соответствующей подсистемы.
                  |Для отладки необходимо:
                  |1. Включить эту обработку в состав конфигурации
                  |2. Отладить обработку, запуская через меню ""Все функции""
                  |3. Сохранить обработку как внешнюю
                  |4. Удалить отлаженную обработку из состава конфигурации
                  |5. Использовать отлаженную внешнюю обработку в подсистеме ""Дополнительные отчеты и обработки"".'");
			ВызватьИсключение ТекстСообщенияОбОшибке;
			
		КонецЕсли;
		Этап = Конструктор.ДобавитьМетодКонфигурации(Сценарий,
				"Обработки." + ИмяОбработки + ".Создать().ЗагрузитьДанныеИзXMLФайла", // Имя метода
				"ДокументДОМ"
		);
	Иначе
		Этап = Конструктор.ДобавитьМетодОбработки(Сценарий,
				"ЗагрузитьДанныеИзXMLФайла", // Имя метода
				"ДокументДОМ"
				);
	КонецЕсли;
	
	Конструктор.ДобавитьСохраняемоеЗначение(Этап, "ЧтениеXML");
	
	Конструктор.ДобавитьЗначение(Этап, ВидЦены);
		
	Конструктор.ДобавитьПараметрВыполненияКоманды(Этап, "РезультатВыполнения");
	
	Если ОтлаживатьКакВстроенную Тогда
		ИмяОбработки = Метаданные().Имя;
		Если Метаданные.НайтиПоПолномуИмени("Обработка." + ИмяОбработки) = Неопределено Тогда
			
			ТекстСообщенияОбОшибке = 
			НСтр("ru = 'Эта обработка предназначена для использования только в подсистеме ""Дополнительные отчеты и обработки"".
                  |Запускать ее на выполнение как внешюю обработку через главное меню (Файл - Открыть) нельзя.
                  |Подробнее в справочной информации.'");
			ВызватьИсключение ТекстСообщенияОбОшибке;
			
		КонецЕсли;
		Этап = Конструктор.ДобавитьМетодКонфигурации(Сценарий,
				"Обработки." + ИмяОбработки + ".Создать().ЗагрузитьГруппы", // Имя метода
				"МассивГрупп"); // Сохранение результата
	Иначе
		Этап = Конструктор.ДобавитьМетодОбработки(Сценарий,
				"ЗагрузитьГруппы", // Имя метода
				"МассивГрупп"); // Сохранение результата
	КонецЕсли;
	Конструктор.ДобавитьСохраняемоеЗначение(Этап, "ДокументДОМ");
    	
	Этап = Конструктор.ДобавитьМетодКонфигурации(Сценарий, "ДополнительныеОтчетыИОбработкиВБезопасномРежиме.ЗаписатьОбъекты");
	Конструктор.ДобавитьКлючСессии(Этап);
	Конструктор.ДобавитьСохраняемоеЗначение(Этап, "МассивГрупп");
	Конструктор.ДобавитьЗначение(Этап, Неопределено);

Обращу внимание на вызовы 

Конструктор.ДобавитьСохраняемоеЗначение(Этап, "МассивГрупп");
Конструктор.ДобавитьЗначение(Этап, Неопределено);

Первый метод позволяет передать в следующую процедуру или функцию значение, сохраненное ранее на прошлых этапах.
Причем стоит заметить, что в качестве второго параметра выступает строка - имя переменной.
На самом деле при исполнении этапа вместо строки будет реальное значение переменной с данным именем. 

Второй метод позволяет передать в процедуру или функцию некоторое значение, полученное, например, с клиента. Это может быть, например, реквизит, который заполняется пользователем вручную выбором из списка

После формирования сценария с помощью вызова процедуры

ДополнительныеОтчетыИОбработкиВБезопасномРежимеВызовСервера.ВыполнитьСценарийВБезопасномРежиме(
		КлючСессии,
		АдресСценария,
		Результат
	);

сценарий начинает исполняться.

Данный метод рекомендуем специалистами 1С для написания внешних обработок. 

 

2.    С недавнего времени в облаке нельзя напрямую записать объект(будь то документ, справочник или набор записей) с помощью метода Записать().

Вместо этого необходимо добавить этап с вызовом процедуры

ДополнительныеОтчетыИОбработкиВБезопасномРежиме.ЗаписатьОбъекты

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

3.    К сожалению, и по сей день большая часть разработчиков пишет код работы с файлами так, будто клиентская и серверная сторона находятся на одном компьютере. В обработке используется механизм корректной передачи файлов с клиента на сервер.
Для кого-то этот механизм не нов, но, думаю, часть аудитории никогда не пользовалась данным механизмом, либо пользовалась давно. Освежить память стоит.

4.    Как я уже упоминал, в облаке исполнение внешнего кода происходит в безопасном режиме, а поэтому напрямую множество потенциально небезопасных действий заблокированы. В обработке используется механизм загрузки из XML с помощью метода общего модуля

ДополнительныеОтчетыИОбработкиВБезопасномРежиме.ЧтениеXMLИзДвоичныхДанных

5.    В связи с тем, что в настоящее время в облаке действует ограничение из п.2, при загрузке данных справочника номенклатуры возникла задача, как за один обход дерева корректно загрузить всю иерархию групп справочника.

Так как в момент обработки каждого отдельного элемента его родитель, в общем случае, еще не записан в базу, то пользоваться стандартными методами НайтиПоКоду(), НайтиПоНаименованию(), НайтиПоРеквизиту() не получится. В данном случае был использован механизм создания объектов на основе UID(благо, в загружаемом XML-файле каждому элементу был присвоен свой идентификатор). Данный механизм позволил устанавливать ссылку на родительский элемент, который еще не загружен в базу, но при этом ссылка на него уже хранится в памяти.

Пример:

                НоваяСсылка = Справочники.Номенклатура.ПолучитьСсылку(Новый УникальныйИдентификатор(Ид));
		Номенклатура = НоваяСсылка.ПолучитьОбъект();
		
		Если Номенклатура = Неопределено И МассивСсылок.Найти(НоваяСсылка) = Неопределено Тогда
			Номенклатура = Справочники.Номенклатура.СоздатьЭлемент();
			Номенклатура.УстановитьСсылкуНового(НоваяСсылка);
			Если СтруктураДопРеквизитов.Свойство("НаименованиеКраткое") Тогда
				Номенклатура.Наименование = СтруктураДопРеквизитов.НаименованиеКраткое;
			Иначе
				Номенклатура.Наименование = Наименование;
			КонецЕсли; 
			Если СтруктураДопРеквизитов.Свойство("НаименованиеПолное") Тогда
				Номенклатура.НаименованиеПолное = СтруктураДопРеквизитов.НаименованиеПолное;
			Иначе
				Номенклатура.НаименованиеПолное = Номенклатура.Наименование;
			КонецЕсли; 
			Номенклатура.ТипНоменклатуры = Перечисления.ТипыНоменклатуры.Запас;
			Номенклатура.СтавкаНДС = Справочники.СтавкиНДС.НайтиПоНаименованию(Ставка);
			Номенклатура.Родитель = Справочники.Номенклатура.ПолучитьСсылку(Новый УникальныйИдентификатор(ГруппаРодительИд));
			Номенклатура.ЕдиницаИзмерения = Справочники.КлассификаторЕдиницИзмерения.НайтиПоКоду(БазоваяЕдиница);
			Если Характеристика <> Неопределено Тогда
				Номенклатура.ИспользоватьХарактеристики = Истина;
			КонецЕсли; 
			МассивЦен.Добавить(Номенклатура);
			МассивСсылок.Добавить(НоваяСсылка);
		КонецЕсли;

 Буду рад ответить на ваши вопросы и учесть замечания.

UPD 04.02.2016: 
v 1.0.2 
    - Новый механизм вывода оповещения в связи с переходом на БСП 2.3(конфигурация УНФ 1.6.* и выше)

UPD 21.10.2015:
v 1.0.1 
    - Почищен код
    - Добавлена передача в процедуру записи объектов параметров записи. 
 

Скачать файлы

Наименование Файл Версия Размер
Обработка загрузки XML в 1С:Fresh (1.0.1) 9
.epf 15,29Kb
04.02.16
9
.epf 1.0.1 15,29Kb Скачать
Обработка загрузки XML в 1С:Fresh (1.0.2) 10
.epf 15,27Kb
04.02.16
10
.epf 1.0.2 15,27Kb Скачать

См. также

Подписаться Добавить вознаграждение
Комментарии
1. Андрей Кантер (waryg) 20.10.15 12:42
Полезная тема, спасибо :)
2. Сергей Самарин (Samarin) 20.10.15 20:04
Тема новая и мало вообще про нее где можно почитать. ИТС не поспевает за всеми изменениями БСП (БТС).
На первых парах тем, кто с облаками столкнется, пригодились бы такие шаблоны выполнения таких сценариев:
- загрузка данных из файла.
- выгрузка данных в файл.
- запись объектов.

Опять все изменили в механизме сценариев - пришлось опять переписывать внешнюю обработку.
В обработке после создания объектов - отправлял оповещение, в котором параметром передавал на клиент список ссылок на созданные объекты, о чем и уведомлялись пользователи.
Теперь это дело перестало работать и как его опять наладить - пока без понятия :(
3. Дмитрий Кашпаров (laperuz) 21.10.15 04:00
(2) Samarin,

- загрузка данных из файла.


Это в обработке есть. Там, по сути, вызов одного метода общего модуля. Единственное, в связи с ограничениями безопасного режима, список форматов, из которых можно грузить, сильно ограничен.

- выгрузка данных в файл.

То же самое, что с выгрузкой, только поменять вызываемый метод, ничего сложного.

- запись объектов.

Это тоже есть, и там тоже вызов одного метода, куда передается массив - список объектов для записи.

В обработке после создания объектов - отправлял оповещение, в котором параметром передавал на клиент список ссылок на созданные объекты, о чем и уведомлялись пользователи.
Теперь это дело перестало работать и как его опять наладить - пока без понятия :(


Вызовом
Конструктор.ДобавитьПараметрВыполненияКоманды(Этап, "РезультатВыполнения");
? У меня на последнем этапе в переменной РезультатВыполнения формируется сообщение для показа на клиенте. Как вариант, через
ДобавитьСохраняемоеЗначение
туда передать массив ссылок и сформировать текст показа, который записать в РезультатВыполнения, он отобразится на клиенте.
4. Сергей Самарин (Samarin) 23.10.15 14:41
Скачал обработку - посмотрел: у меня примерно так и работало до новой БСП.
Теперь такой функции "СтандартныеПодсистемыКлиентСервер.НовыйРезультатВыполнения()" - нет и чем оно заменилось - я не знаю.
5. Дмитрий Кашпаров (laperuz) 26.10.15 13:04
(4) Samarin,
Посмотрел сейчас как в новой БСП.

Там вот такой код:
ПараметрыВыполнения = Новый Структура("РезультатВыполнения", Новый Структура);


Т.е. просто новая структура, без вызова процедуры.

Ну и далее
ПоказатьРезультатВыполненияОбработки(ПараметрыВыполнения);
6. Сергей Самарин (Samarin) 26.10.15 14:52
Вот к этой структуре и вопросы - как сделать так, чтобы оно все заработало.
Старый код обработки результата - не работает:
Результат.ВыводПредупреждения.Использование = Истина;
Результат.ВыводПредупреждения.Заголовок = НСтр("ru = 'Обработка завершена успешно'");

Конкретное место проблемы - это отработка процедуры вывода результата, которая теперь не отрабатывается и вываливается с ошибкой.
СтандартныеПодсистемыКлиент.ПоказатьРезультатВыполнения(ВладелецФормы, Результат.РезультатВыполнения);

Там теперь ничего подобного нет, теперь там используются "Шаги". Вот что пишет справка к процедуре вывода:

// * Шаги - СписокЗначений - Информация, которую необходимо вывести на клиенте.
// Шаги добавляются только при помощи процедур:
// ** СтандартныеПодсистемыКлиентСервер.ОповеститьДинамическиеСписки()
// ** СтандартныеПодсистемыКлиентСервер.ОповеститьОткрытыеФормы()
// ** СтандартныеПодсистемыКлиентСервер.РазвернутьУзлыДерева()
// ** СтандартныеПодсистемыКлиентСервер.ВывестиПредупреждение()
// ** СтандартныеПодсистемыКлиентСервер.ВывестиСообщение()
// ** СтандартныеПодсистемыКлиентСервер.ВывестиОповещение()
// ** СтандартныеПодсистемыКлиентСервер.ВывестиФорму()
7. Сергей Самарин (Samarin) 26.10.15 15:03
Все, разобрался, работает!
Теперь все через эти чертовы шаги работает.

В процедуре ОформитьРезультат(МассивОбъектов, Результат) появляется примерно такое:

СтандартныеПодсистемыКлиентСервер.ОповеститьОткрытыеФормы(
Результат,
"СозданиеДокументов",
ПоместитьВоВременноеХранилище(МассивОбъектов));

СтандартныеПодсистемыКлиентСервер.ВывестиОповещение(Результат, ЗаголовокОповещения, "ru = 'Данные успешно записаны'",, СсылкаНаДокумент);
8. Дмитрий Кашпаров (laperuz) 27.10.15 04:13
(7) Samarin,
Молодец!
Теперь буду ждать, когда УНФ на новую БСП переведут, придется переделывать.
9. Сергей Иванов (Serg1980) 28.07.16 15:53
Как работать с объектом XBASE? На веб клиенте он недоступен, а на сервере недоступны операции с файлами в безопасном режиме. Кроме как через файл, XBASE заполнить не могу.