Загрузка коммерческих предложений из FTP сайта в 1С УТ 11.4 в формате XML и создание по ним документов "Заказ клиента"
Теги: 1С

# Код реализации

```1c
Процедура ЮКОЗ_ЗагрузкаКП() Экспорт

	//Инициализируем константные переменные
	КаталогВрФл 		= КаталогВременныхФайлов();
	ВидНоменклатуры 	= РегистрыСведений.ЮКОЗ_ДополнительныеНастройки.ПолучитьНастройки("ВидыНоменклатурыТовар");
	ЕдиницаИзмШТ 		= РегистрыСведений.ЮКОЗ_ДополнительныеНастройки.ПолучитьНастройки("ЕдиницаИзмерения_ШТ");
	ПриоритетСредний 	= РегистрыСведений.ЮКОЗ_ДополнительныеНастройки.ПолучитьНастройки("ПриоритетСредний");
	СкладОсновной 		= РегистрыСведений.ЮКОЗ_ДополнительныеНастройки.ПолучитьНастройки("СкладОсновной");
	ТиповоеСоглашение 	= РегистрыСведений.ЮКОЗ_ДополнительныеНастройки.ПолучитьНастройки("ТиповоеСоглашение");
	ВалютаДок 			= Константы.ВалютаРегламентированногоУчета.Получить();
	АдресFTP			= РегистрыСведений.ЮКОЗ_ДополнительныеНастройки.ПолучитьНастройки("АдресFTP");
	ЛогинFTP			= РегистрыСведений.ЮКОЗ_ДополнительныеНастройки.ПолучитьНастройки("ЛогинFTP");
	ПарольFTP			= РегистрыСведений.ЮКОЗ_ДополнительныеНастройки.ПолучитьНастройки("ПарольFTP");

	//Соединямся с FTP-сервером и ищем необработанные файлы xml
	ФТПСоединение = Новый FTPСоединение(АдресFTP,, ЛогинFTP, ПарольFTP,, Истина);
	НайдемФайлаНаФТП = ФТПСоединение.НайтиФайлы("/", "*.xml");

	//Если файлов нет, значит завершаем работу
	Если НайдемФайлаНаФТП.Количество() = 0 Тогда
		ФТПСоединение = Неопределено;
		Возврат;
	КонецЕсли;

	//Подготавливаем таблицу значений для заполнения загружаемыми данными
	ДанныеИзФайлаТовары = Новый ТаблицаЗначений();
	ДанныеИзФайлаТовары.Колонки.Добавить("Артикул");
	ДанныеИзФайлаТовары.Колонки.Добавить("ИД");
	ДанныеИзФайлаТовары.Колонки.Добавить("Производитель");
	ДанныеИзФайлаТовары.Колонки.Добавить("Номенклатура");
	ДанныеИзФайлаТовары.Колонки.Добавить("Количество");
	ДанныеИзФайлаТовары.Колонки.Добавить("Цена");
	ДанныеИзФайлаТовары.Колонки.Добавить("Размер");
	ДанныеИзФайлаТовары.Колонки.Добавить("Цвет");
	ДанныеИзФайлаТовары.Колонки.Добавить("Серия");
	ДанныеИзФайлаТовары.Колонки.Добавить("ЦенаСоСкидкой");

	//Циклом обходим каждый найденный на FTP файл xml
	Для каждого ФайлФТП из НайдемФайлаНаФТП Цикл

		//Очищаем нашу таблицу значений, чтобы она могла хранить данные только по одному файлу xml
		ДанныеИзФайлаТовары.Очистить();

		//Получаем номер коммерческого предложения из имени файла xml
		НомерКП = ФайлФТП.ИмяБезРасширения;

		//Очищаем дату коммерческого предложения кототрую мы впоследствии возьмом из тела файла xml
		ДатаКП = "";

		//Обернём код в попытку, мало ли будут ошибки при загрузке и обработке файла
		Попытка

		//Загружаем файл на локальный компьютер
		ПутьКФайлуОбъект = КаталогВрФл + ФайлФТП.Имя;
		ФТПСоединение.Получить(ФайлФТП.ПолноеИмя, ПутьКФайлуОбъект);

		//Перемещаем загруженный файл в директорию обработанных файлов на FTP
		//Специально помещаем в начале, чтобы регламентное задание в случае ошибок в этом файле каждый раз не спотыкалось об него
		ФТПСоединение.Переместить(ФайлФТП.ПолноеИмя, "OldFiles" + "\" + ФайлФТП.Имя);

		//Инициализируем чтение файла xml
		ЧтениеXML = Новый ЧтениеXML;
		ЧтениеXML.ОткрытьФайл(ПутьКФайлуОбъект);

		//Циклом обходим узлы XML файла и при совпадении с нужным именем узла записываем его значение в нашу таблицу значений
		Пока ЧтениеXML.Прочитать() Цикл
			Если ЧтениеXML.ТипУзла <> ТипУзлаXML.НачалоЭлемента Тогда Продолжить; КонецЕсли;
			ИмяУзла = ЧтениеXML.ЛокальноеИмя;
			Если ИмяУзла = "PRODUCT" Тогда
				НоваяСтрока = ДанныеИзФайлаТовары.Добавить();
				Продолжить;
			КонецЕсли;
			Если ИмяУзла = "ID" Тогда
				ЧтениеXML.Прочитать();
				НоваяСтрока.ИД = СокрЛП(ЧтениеXML.Значение);
				Продолжить;
			КонецЕсли;
			Если ИмяУзла = "ARTICLE" Тогда
				ЧтениеXML.Прочитать();
				НоваяСтрока.Артикул = СокрЛП(ЧтениеXML.Значение);
				Продолжить;
			КонецЕсли;
			Если ИмяУзла = "SIZE" Тогда
				ЧтениеXML.Прочитать();
				НоваяСтрока.Размер = СокрЛП(ЧтениеXML.Значение);
				Продолжить;
			КонецЕсли;
			Если ИмяУзла = "COLOR" Тогда
				ЧтениеXML.Прочитать();
				НоваяСтрока.Цвет = СокрЛП(ЧтениеXML.Значение);
				Продолжить;
			КонецЕсли;
			Если ИмяУзла = "NAME" И ЧтениеXML.КонтекстПространствИмен.Глубина = 5 Тогда
				ЧтениеXML.Прочитать();
				НоваяСтрока.Серия = СокрЛП(ЧтениеXML.Значение);
				Продолжить;
			КонецЕсли;
			Если ИмяУзла = "PROPERTY_PROVIDER_NAME" И ЧтениеXML.КонтекстПространствИмен.Глубина = 5 Тогда
				ЧтениеXML.Прочитать();
				НоваяСтрока.Производитель = СокрЛП(ЧтениеXML.Значение);
				Продолжить;
			КонецЕсли;
			Если ИмяУзла = "NAME" Тогда
				ЧтениеXML.Прочитать();
				НоваяСтрока.Номенклатура = СокрЛП(ЧтениеXML.Значение);
				Продолжить;
			КонецЕсли;
			Если ИмяУзла = "QUANTITY" Тогда
				ЧтениеXML.Прочитать();
				НоваяСтрока.Количество = СокрЛП(ЧтениеXML.Значение);
				Продолжить;
			КонецЕсли;
			Если ИмяУзла = "PRICE" Тогда
				ЧтениеXML.Прочитать();
				НоваяСтрока.Цена = СокрЛП(ЧтениеXML.Значение);
				Продолжить;
			КонецЕсли;
			Если ИмяУзла = "PRICE_DISCOUNT" Тогда
				ЧтениеXML.Прочитать();
				НоваяСтрока.ЦенаСоСкидкой = СокрЛП(ЧтениеXML.Значение);
				Продолжить;
			КонецЕсли;
			Если ИмяУзла = "DATE" Тогда
				ЧтениеXML.Прочитать();
				ДатаСтр = СокрЛП(ЧтениеXML.Значение);
				ДатаКП = Дата(ДатаСтр);
				Продолжить;
			КонецЕсли;
		КонецЦикла;

		//Закрываем чтение XML и удаляем ранее загруженный временный файл из нашего компьютера
		ЧтениеXML.Закрыть();
		УдалитьФайлы(ПутьКФайлуОбъект);

		//Дата коммерческого предложения обязательно должна быть заполнена, если нет, то вызываем ошибку с пояснением для журнала регистрации
		Если НЕ ЗначениеЗаполнено(ДатаКП) Тогда
			ВызватьИсключение "Пустая дата по КП " + НомерКП;
		КонецЕсли;

		//Запросом найдем уже созданный ранее заказ клиента по загружаемому файлу
		//Плохо, что запрос в цикле, но в данном случае можно ;) дабы не усложнять код.
		ЗапросСущЗаказа = Новый Запрос();
		ЗапросСущЗаказа.УстановитьПараметр("НомерКП", НомерКП);
		ЗапросСущЗаказа.УстановитьПараметр("ДатаКП", НачалоДня(ДатаКП));
		ЗапросСущЗаказа.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1
		                        |	ЗаказКлиента.Ссылка КАК Ссылка,
		                        |	ЗаказКлиента.Проведен КАК Проведен
		                        |ИЗ
		                        |	Документ.ЗаказКлиента КАК ЗаказКлиента
		                        |ГДЕ
		                        |	ЗаказКлиента.НомерПоДаннымКлиента = &НомерКП
		                        |	И ЗаказКлиента.ДатаПоДаннымКлиента = &ДатаКП";
		Результат_ЗапросСущЗаказа = ЗапросСущЗаказа.Выполнить().Выбрать();

		//Если заказ клиента найден и он проведен, то обновлять\изменять мы его уже не можем и вызываем ошибку с пояснением,
		//если еще не проведен, то получаем ссылку на документ и работаем дальше
		Если Результат_ЗапросСущЗаказа.Следующий() Тогда
			Если Результат_ЗапросСущЗаказа.Проведен Тогда
				ВызватьИсключение "Заказ уже проведен по КП " + НомерКП;
			КонецЕсли;
			ДокЗаказ = Результат_ЗапросСущЗаказа.Ссылка.ПолучитьОбъект();

		//Если заказ клиента по загружаемому xml файлу еще не было, то создаем и заполняем его
		Иначе
			ДокЗаказ = Документы.ЗаказКлиента.СоздатьДокумент();
			ДокЗаказ.Дата = ТекущаяДата();
			ДокЗаказ.ЮКОЗ_НомерКПИзСайта = НомерКП;
			ДокЗаказ.Приоритет = ПриоритетСредний;
			ДокЗаказ.ХозяйственнаяОперация = Перечисления.ХозяйственныеОперации.РеализацияКлиенту;
			ДокЗаказ.ЦенаВключаетНДС = Истина;
			ДокЗаказ.НалогообложениеНДС = Перечисления.ТипыНалогообложенияНДС.ПродажаОблагаетсяНДС;
			ДокЗаказ.НомерПоДаннымКлиента = НомерКП;
			ДокЗаказ.ДатаПоДаннымКлиента = ДатаКП;
			ДокЗаказ.Склад = СкладОсновной;
			ДокЗаказ.Статус = Перечисления.СтатусыЗаказовКлиентов.КОтгрузке;
			ДокЗаказ.ЮКОЗ_Статус = Перечисления.ЮКОЗ_СтатусыЗаказовКлиентов.Согласование;
			ДокЗаказ.Валюта = ВалютаДок;
			ДокЗаказ.Соглашение = ТиповоеСоглашение;
			ДокЗаказ.НеОтгружатьЧастями = Истина;
			ДокЗаказ.ПорядокОплаты = Перечисления.ПорядокОплатыПоСоглашениям.РасчетыВВалютеОплатаВВалюте;
			ДокЗаказ.ФормаОплаты = Перечисления.ФормыОплаты.Безналичная;
		КонецЕсли;

		//Очистим табличную часть Товары (на тот случай, если заказ клиента уже был ранее создан и там были какие-то данные)
		ДокЗаказ.Товары.Очистить();

		//Обходим данные из файла через нашу таблицу значений
		Для каждого стр из ДанныеИзФайлаТовары Цикл

			//Проверям на заполнение обязательный полей (артикул, производитель, номенклатура), если хоть что-то не заполнено, то вызываем ошибку с пояснением
			Если НЕ ЗначениеЗаполнено(стр.Артикул) И НЕ ЗначениеЗаполнено(стр.ИД) Тогда
				ВызватьИсключение "Пустой артикул и ID по КП " + НомерКП;
			КонецЕсли;
			Если НЕ ЗначениеЗаполнено(стр.Производитель) Тогда
				ВызватьИсключение "Пустой производитель по КП " + НомерКП;
			КонецЕсли;
			Если НЕ ЗначениеЗаполнено(стр.Номенклатура) Тогда
				ВызватьИсключение "Пустая номенклатура по КП " + НомерКП;
			КонецЕсли;

			//Пытаемся найти уже созданную номенклатуру по артикулу, а если он не заполнен, то по ИД.
			ЗапросСущТовара = Новый Запрос();
			ЗапросСущТовара.УстановитьПараметр("ИД", стр.ИД);
			ЗапросСущТовара.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1
			                        |	Номенклатура.Ссылка КАК Ссылка
			                        |ИЗ
			                        |	Справочник.Номенклатура КАК Номенклатура
			                        |ГДЕ" + Символы.ПС;
			Если ЗначениеЗаполнено(стр.Артикул) Тогда
				ЗапросСущТовара.Текст = ЗапросСущТовара.Текст + "Номенклатура.Артикул = &Артикул";
			Иначе
			    ЗапросСущТовара.Текст = ЗапросСущТовара.Текст + "Номенклатура.ЮКОЗ_IDИзСайта = &ИД";
			КонецЕсли;
			Результат_ЗапросСущТовара = ЗапросСущТовара.Выполнить().Выбрать();

			//Формируем представление строки описания номенклатуры (строковая характеристика)
			ДобТоварЮКОЗ_Характеристика = ?(ЗначениеЗаполнено(стр.Серия), стр.Серия + ", ", "") +
				                           ?(ЗначениеЗаполнено(стр.Размер), стр.Размер + ", ", "") +
										   ?(ЗначениеЗаполнено(стр.Цвет), стр.Цвет, "");

			//Получаем ссылку на номенклатуру если она найдена
			Если Результат_ЗапросСущТовара.Следующий() Тогда
				ТоварСсылка = Результат_ЗапросСущТовара.Ссылка;

			//Если номенклатура не найдена, то создаем её
			Иначе
				ТоварНовый = Справочники.Номенклатура.СоздатьЭлемент();

				//Каждая номенлатура должна находится в своей группе в справочнике номенклатура, где название группы - это наименование производителя
				//проверяем это запросом
				ЗапросСущГруппы = Новый Запрос();
				ЗапросСущГруппы.УстановитьПараметр("Производитель", стр.Производитель);
				ЗапросСущГруппы.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1
				                        |	Номенклатура.Ссылка КАК Ссылка
				                        |ИЗ
				                        |	Справочник.Номенклатура КАК Номенклатура
				                        |ГДЕ
				                        |	Номенклатура.Наименование = &Производитель
				                        |	И Номенклатура.ЭтоГруппа";
				Результат_ЗапросСущГруппы = ЗапросСущГруппы.Выполнить().Выбрать();

				//Если группа найдено, то берем ссылку на неё
				Если Результат_ЗапросСущГруппы.Следующий() Тогда
					ГруппаТовараСсылка = Результат_ЗапросСущГруппы.Ссылка;

				//Если группа не найдена, то создаем её
				Иначе
					ГруппаТовараСсылка = Справочники.Номенклатура.СоздатьГруппу();
					ГруппаТовараСсылка.Наименование = стр.Производитель;
					ГруппаТовараСсылка.Записать();
					ГруппаТовараСсылка = ГруппаТовараСсылка.Ссылка;
				КонецЕсли;

				//Указываем, что номенклатуру мы создаем в группе которую нашли\создали чуть ранее
				ТоварНовый.Родитель = ГруппаТовараСсылка;

				//Формируем представление наименования номенклатуры, оно должно содержать артикул, а если его нет, то ИД
				//Также записываем артикул, если его нет, то ИД вместо него.
				Если ЗначениеЗаполнено(стр.Артикул) Тогда
					ТоварНовый.Артикул = стр.Артикул;
					ТоварНовый.Наименование = Строка(?(ЗначениеЗаполнено(стр.Артикул), "(" + стр.Артикул + ") ", "") + стр.Номенклатура);
				Иначе
					ТоварНовый.Наименование = Строка(?(ЗначениеЗаполнено(стр.ИД), "(" + стр.ИД + ") ", "") + стр.Номенклатура);
			    	ТоварНовый.Артикул = стр.ИД;
				КонецЕсли;

				//Добавляем в наименование номенклатуры строковое описание номенклатуры, которое мы сформировали ранее,
				//при условии, что описание номенклатуры есть и оно уже не встречается в наименовании номенклатуры
				Если ЗначениеЗаполнено(ДобТоварЮКОЗ_Характеристика) Тогда
					Если СтрНайти(СтрЗаменить(ТоварНовый.Наименование, ",", ""),
								  СтрЗаменить(ДобТоварЮКОЗ_Характеристика, ",", "")) = 0 Тогда
						ТоварНовый.Наименование = ТоварНовый.Наименование + ", " + ДобТоварЮКОЗ_Характеристика;
					КонецЕсли;
				КонецЕсли;

				//Заполняем остальные поля номенклатуры
				ТоварНовый.ЮКОЗ_IDИзСайта = стр.ИД;
				ТоварНовый.ТипНоменклатуры = Перечисления.ТипыНоменклатуры.Товар;
				ТоварНовый.ВидНоменклатуры = ВидНоменклатуры;
  			    ТоварНовый.НаименованиеПолное = стр.Номенклатура + ?(ЗначениеЗаполнено(ДобТоварЮКОЗ_Характеристика),
																	  ", " + ДобТоварЮКОЗ_Характеристика, "");
				ТоварНовый.СтавкаНДС = Перечисления.СтавкиНДС.НДС20;
				ТоварНовый.ЕдиницаИзмерения = ЕдиницаИзмШТ;
				ТоварНовый.ЕдиницаДляОтчетов = ЕдиницаИзмШТ;
				ТоварНовый.ИспользованиеХарактеристик = Перечисления.ВариантыИспользованияХарактеристикНоменклатуры.НеИспользовать;
				ТоварНовый.ВариантОформленияПродажи = Перечисления.ВариантыОформленияПродажи.РеализацияТоваровУслуг;

				//Ищем производителя, если его нет, то создаем нового и указываем его в номенклатуре
				ПроизводительСсылка = Справочники.Производители.НайтиПоНаименованию(стр.Производитель);
				Если НЕ ЗначениеЗаполнено(ПроизводительСсылка) Тогда
					НовыйПроизводитель = Справочники.Производители.СоздатьЭлемент();
					НовыйПроизводитель.Наименование = стр.Производитель;
					НовыйПроизводитель.Записать();
					ПроизводительСсылка = НовыйПроизводитель.Ссылка;
				КонецЕсли;

				ТоварНовый.Производитель = ПроизводительСсылка;

				//Записываем новую номенклатуру и получаем ссылку на неё
				ТоварНовый.Записать();
				ТоварСсылка = ТоварНовый.Ссылка;

			КонецЕсли;

			//Добавляем в табличную часть Товары номенклатуру с заполнением данных из файла xml
			ДобТовар = ДокЗаказ.Товары.Добавить();
			ДобТовар.Номенклатура = ТоварСсылка;
			ДобТовар.Количество = стр.Количество;
			ДобТовар.КоличествоУпаковок = стр.Количество;
			ДобТовар.Упаковка = ДобТовар.Номенклатура.ЕдиницаИзмерения;
			ДобТовар.Цена = стр.Цена;
			ДобТовар.СтавкаНДС = ДобТовар.Номенклатура.СтавкаНДС;
			ДобТовар.Склад = СкладОсновной;
			ДобТовар.ЮКОЗ_Характеристика = ДобТоварЮКОЗ_Характеристика;

			//Вычисляем процент ручной скидки, если в файле xml была указана цена со скидкой и она меньше, чем обычная цена
			Если ЗначениеЗаполнено(стр.ЦенаСоСкидкой) И ЗначениеЗаполнено(стр.Цена) Тогда
				стрЦенаСоСкидкой = Число(стр.ЦенаСоСкидкой);
				стрЦена = Число(стр.Цена);
				Если стрЦенаСоСкидкой > 0 И стрЦена > 0 И стрЦена > стрЦенаСоСкидкой Тогда
					ДобТовар.ПроцентРучнойСкидки = (стрЦенаСоСкидкой / стрЦена - 1) * 100 * -1;
				КонецЕсли;
			КонецЕсли;

		КонецЦикла;

		//Посчитаем сумму строки, а также НДС
		СтруктураПересчетаСуммы = ОбработкаТабличнойЧастиКлиентСервер.ПараметрыПересчетаСуммыНДСВСтрокеТЧ(ДокЗаказ);
		СтруктураДействий = Новый Структура;
		СтруктураДействий.Вставить("ПересчитатьСумму", "КоличествоУпаковок");
		СтруктураДействий.Вставить("ПересчитатьСуммуНДС", СтруктураПересчетаСуммы);
		СтруктураДействий.Вставить("ПересчитатьСуммуСНДС", СтруктураПересчетаСуммы);
		ОбработкаТабличнойЧастиСервер.ОбработатьТЧ(ДокЗаказ.Товары, СтруктураДействий, Неопределено);

		//Запишем заказ клиента без проведения
		ДокЗаказ.Записать(РежимЗаписиДокумента.Запись);

		//Обработаем исключения в блоке попытки с записью в журнал регистрации для дальнейшего анализа
		Исключение
			ЗаписьЖурналаРегистрации("Ошибка обработки КП из сайта: " + ОписаниеОшибки(), УровеньЖурналаРегистрации.Ошибка);
		КонецПопытки;

	КонецЦикла;

	//После обработки всех файлов, закроем FTP соединение.
	ФТПСоединение = Неопределено;

КонецПроцедуры
```