Загрузка данных


//++Варнин А.А. Задача №72300 - 27.07.26
#Область РегламентноеЗадание_кор_ВыгрузкаЗакупочныхЦен

Процедура ВыполнитьВыгрузкуЗакупочныхЦен() Экспорт

	ИмяСобытия = "Выгрузка закупочных цен";
	НачалоРаботы = ТекущаяУниверсальнаяДатаВМиллисекундах();

	ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Информация, , , "Старт выгрузки.");

	ЦеныТЗ = СформироватьТаблицуЦен();

	Если ЦеныТЗ.Количество() = 0 Тогда
		ТекстОшибки = "Запрос не вернул ни одной строки. Выгрузка не выполнена.";
		ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка, , , ТекстОшибки);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;

	ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Информация, , ,
		СтрШаблон("Сформировано строк: %1. Время запроса: %2 мс.",
			ЦеныТЗ.Количество(), ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоРаботы));

	РезультатОтправки = СформироватьОтправитьВKafka(ЦеныТЗ);
	
	ЦеныДляФайла = РазвернутьПоКарточкамМаркетплейсов(ЦеныТЗ);

	ВыгрузитьВExcel(ЦеныДляФайла);

	ЗаписьЖурналаРегистрации(ИмяСобытия,
		?(РезультатОтправки.Успех, УровеньЖурналаРегистрации.Информация, УровеньЖурналаРегистрации.Ошибка),
		, , СтрШаблон("Финиш. %1 Общее время: %2 мс.",
			РезультатОтправки.Текст, ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоРаботы));

	Если НЕ РезультатОтправки.Успех Тогда
		ВызватьИсключение РезультатОтправки.Текст;
	КонецЕсли;

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

Функция СформироватьТаблицуЦен()

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

	СвойствоOEM = НайтиДополнительныйРеквизит("OEMНомер");
	СвойствоАртикулМП = НайтиДополнительныйРеквизит("АртикулМаркетплейсов");

	Запрос = Новый Запрос(ТекстЗапросаЦены());
	Запрос.УстановитьПараметр("ВидЦены", ВидЦены);
	Запрос.УстановитьПараметр("КонецПериода", ТекущаяДатаСеанса());
	Запрос.УстановитьПараметр("СвойствоOEM", СвойствоOEM);
	Запрос.УстановитьПараметр("СвойствоАртикулМП", СвойствоАртикулМП);
 	Запрос.УстановитьПараметр("МаскаНоменклатуры", "%не использовать%");
	
	//// Раскомментить, если понадобиться брать номенклатуру с остатком
	//Запрос.Текст = 
	//	СтрЗаменить(Запрос.Текст,"РегистрНакопления.ТоварыНаСкладах КАК Товары",
	//	"		РегистрНакопления.ТоварыНаСкладах.Остатки(&КонецПериода, ) КАК Товары
	//	|	ГДЕ
	//	|		Товары.ВНаличииОстаток > 0");

	Результаты = Запрос.ВыполнитьПакет();

	ЦеныТЗ  = Результаты[Результаты.Количество() - 2].Выгрузить();
	СоставТЗ = Результаты[Результаты.Количество() - 1].Выгрузить();
	
	Колонки = СтрРазделить("СредняяЦена,Цена,СредняяЦенаБезНДС,ЦенаБезНДС", ","); 
	
  	Для Каждого СтрЦена Из ЦеныТЗ Цикл
		Для Каждого ИмяКолонки Из Колонки Цикл
			СтрЦена[ИмяКолонки] = Окр(СтрЦена[ИмяКолонки], 2, РежимОкругления.Окр15как20);
		КонецЦикла;
	КонецЦикла;
		
	ДополнитьЦенамиНаборов(ЦеныТЗ, СоставТЗ); 
	
	ЦеныТЗ.Сортировать("Код");

	Возврат ЦеныТЗ;

КонецФункции

Функция РазвернутьПоКарточкамМаркетплейсов(ЦеныТЗ)

	КодыПоНоменклатуре = КодыПоставщикаПоНоменклатуре();

	Результат = ЦеныТЗ.СкопироватьКолонки();

	Результат.Колонки.Добавить("АртикулМаркетплейсаРасширенный", Новый ОписаниеТипов("Строка"));

	Для Каждого СтрЦена Из ЦеныТЗ Цикл

		КодыКарточек = КодыПоНоменклатуре[СтрЦена.Номенклатура];

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

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

	КонецЦикла;

	Возврат Результат;

КонецФункции

Функция КодыПоставщикаПоНоменклатуре()

	Запрос = Новый Запрос(
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	СоответствиеТоваров.Номенклатура КАК Номенклатура,
		|	ТоварыТП.КодПоставщика КАК КодПоставщика
		|ИЗ
		|	РегистрСведений.топ_СоответствиеТоваров КАК СоответствиеТоваров
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.топ_ТоварыТорговыхПлощадок КАК ТоварыТП
		|		ПО СоответствиеТоваров.Товар = ТоварыТП.Ссылка
		|ГДЕ
		|	НЕ ТоварыТП.ПометкаУдаления
		|	И ТоварыТП.КодПоставщика <> """"
		|	И СоответствиеТоваров.Номенклатура <> ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
		|
		|УПОРЯДОЧИТЬ ПО
		|	КодПоставщика");

	Результат = Новый Соответствие;

	Выборка = Запрос.Выполнить().Выбрать();

	Пока Выборка.Следующий() Цикл

		КодПоставщика = СокрЛП(Выборка.КодПоставщика);
		Если ПустаяСтрока(КодПоставщика) Тогда
			Продолжить;
		КонецЕсли;

		КодыКарточек = Результат[Выборка.Номенклатура];
		Если КодыКарточек = Неопределено Тогда
			КодыКарточек = Новый Массив;
			Результат.Вставить(Выборка.Номенклатура, КодыКарточек);
		КонецЕсли;

		Если КодыКарточек.Найти(КодПоставщика) = Неопределено Тогда
			КодыКарточек.Добавить(КодПоставщика);
		КонецЕсли;

	КонецЦикла;

	Возврат Результат;

КонецФункции

Процедура ДополнитьЦенамиНаборов(ЦеныТЗ, СоставТЗ)
		
	Если СоставТЗ.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	ИзвестныеЦены = Новый Соответствие;
	Для Каждого СтрЦена Из ЦеныТЗ Цикл
		ИзвестныеЦены.Вставить(СтрЦена.Номенклатура,
			Новый Структура("СредняяЦена, Цена, СредняяЦенаБезНДС, ЦенаБезНДС", 
				СтрЦена.СредняяЦена, СтрЦена.Цена, СтрЦена.СредняяЦенаБезНДС, СтрЦена.ЦенаБезНДС));
	КонецЦикла;
	
	РеквизитыНаборов = Новый Соответствие;
	СоставНабора = Новый Соответствие;
	
	Для Каждого СтрСостав Из СоставТЗ Цикл
		
		Если РеквизитыНаборов[СтрСостав.Набор] = Неопределено Тогда
			Реквизиты = Новый Структура("Наименование, Код, Артикул, Марка, OEMНомер, АртикулМаркетплейса");
			ЗаполнитьЗначенияСвойств(Реквизиты, СтрСостав);
			РеквизитыНаборов.Вставить(СтрСостав.Набор, Реквизиты);
			СоставНабора.Вставить(СтрСостав.Набор, Новый Массив);
		КонецЕсли;
		
		СоставНабора[СтрСостав.Набор].Добавить(
			Новый Структура("Компонент, Количество", СтрСостав.Компонент, СтрСостав.Количество));
		
	КонецЦикла;
	
	МассивНаборов = Новый Массив;
	Для Каждого КлючЗначение Из СоставНабора Цикл
		МассивНаборов.Добавить(КлючЗначение.Ключ);
	КонецЦикла;
	
	Для Каждого Набор Из МассивНаборов Цикл
		
		СредняяИтог = 0;
		ЦенаИтог = 0;
		СредняяБезНДСИтог = 0;
		ЦенаБезНДСИтог = 0;
				
		Для Каждого Строка Из СоставНабора[Набор] Цикл
			
			ЦеныКомпонента = ИзвестныеЦены[Строка.Компонент];
			Если ЦеныКомпонента <> Неопределено Тогда
				Цена = ЦеныКомпонента.Цена;
				ЦенаБезНДС = ЦеныКомпонента.ЦенаБезНДС;
				СредняяЦена = ЦеныКомпонента.СредняяЦена; 
				СредняяЦенаБезНДС = ЦеныКомпонента.СредняяЦенаБезНДС;
				Количество = Строка.Количество;
			Иначе 
				СредняяИтог = 0;
				ЦенаИтог = 0;
				СредняяБезНДСИтог = 0;
				ЦенаБезНДСИтог = 0; 			
				Прервать;
			КонецЕсли;
			
			СредняяИтог		  = СредняяИтог + СредняяЦена * Количество;
			ЦенаИтог    	  = ЦенаИтог + Цена * Количество;
			СредняяБезНДСИтог = СредняяБезНДСИтог + СредняяЦенаБезНДС * Количество;
			ЦенаБезНДСИтог    = ЦенаБезНДСИтог + ЦенаБезНДС * Количество;
		
		КонецЦикла;
		
		СредняяИтог 	  = Окр(СредняяИтог, 2, РежимОкругления.Окр15как20);
		ЦенаИтог    	  = Окр(ЦенаИтог, 2, РежимОкругления.Окр15как20);
		СредняяБезНДСИтог = Окр(СредняяБезНДСИтог, 2, РежимОкругления.Окр15как20);
		ЦенаБезНДСИтог    = Окр(ЦенаБезНДСИтог, 2, РежимОкругления.Окр15как20);
		
		НоваяСтрока = ЦеныТЗ.Добавить();
		ЗаполнитьЗначенияСвойств(НоваяСтрока, РеквизитыНаборов[Набор]);
		НоваяСтрока.Номенклатура 	  	 = Набор;
		НоваяСтрока.СредняяЦена 	  	 = СредняяИтог;
		НоваяСтрока.Цена        	  	 = ЦенаИтог; 
		НоваяСтрока.СредняяЦенаБезНДС 	 = СредняяБезНДСИтог;
		НоваяСтрока.ЦенаБезНДС       	 = ЦенаБезНДСИтог;
		НоваяСтрока.КоличествоДокументов = 0;
		
		ИзвестныеЦены.Вставить(Набор, Новый Структура("СредняяЦена, Цена, СредняяЦенаБезНДС, ЦенаБезНДС", 
			СредняяИтог, ЦенаИтог, СредняяБезНДСИтог, ЦенаБезНДСИтог));
		
	КонецЦикла;
	
КонецПроцедуры

Функция НайтиДополнительныйРеквизит(ИдентификаторДляФормул)

	Свойство = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту(
		"ИдентификаторДляФормул", ИдентификаторДляФормул);

	Если НЕ ЗначениеЗаполнено(Свойство) Тогда
		ЗаписьЖурналаРегистрации("Выгрузка закупочных цен",
			УровеньЖурналаРегистрации.Предупреждение, , ,
			"Не найден дополнительный реквизит с идентификатором """ + ИдентификаторДляФормул + """.");
	КонецЕсли;

	Возврат Свойство;

КонецФункции

Функция ТекстЗапросаЦены()

	Возврат	
		"ВЫБРАТЬ
		|	Вариант.Владелец КАК Набор,
		|	Вариант.Ссылка КАК Вариант,
		|	Вариант.Наименование КАК ВариантНаименование,
		|	СоставТЧ.НомерСтроки КАК НомерСтроки,
		|	СоставТЧ.Номенклатура КАК Компонент,
		|	СоставТЧ.Количество КАК Количество
		|ПОМЕСТИТЬ ВТ_СоставНаборов
		|ИЗ
		|	Справочник.ВариантыКомплектацииНоменклатуры.Товары КАК СоставТЧ
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ВариантыКомплектацииНоменклатуры КАК Вариант
		|		ПО СоставТЧ.Ссылка = Вариант.Ссылка
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Наборы
		|		ПО (Вариант.Владелец = Наборы.Ссылка)
		|ГДЕ
		|	НЕ Вариант.ПометкаУдаления
		|	И НЕ Наборы.ПометкаУдаления
		|	И Наборы.ТипНоменклатуры = ЗНАЧЕНИЕ(Перечисление.ТипыНоменклатуры.Набор)
		|	И СоставТЧ.Количество > 0
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	Набор,
		|	Компонент
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ВсяНоменклатура.Номенклатура КАК Номенклатура
		|ПОМЕСТИТЬ ВТ_НоменклатураКРасчету
		|ИЗ
		|	(ВЫБРАТЬ
		|		Товары.Номенклатура КАК Номенклатура
		|	ИЗ
		|		РегистрНакопления.ТоварыНаСкладах КАК Товары
		|	
		|	ОБЪЕДИНИТЬ ВСЕ
		|	
		|	ВЫБРАТЬ
		|		ВТ_СоставНаборов.Компонент
		|	ИЗ
		|		ВТ_СоставНаборов КАК ВТ_СоставНаборов
		|	
		|	ОБЪЕДИНИТЬ ВСЕ
		|	
		|	ВЫБРАТЬ
		|		ВТ_СоставНаборов.Набор
		|	ИЗ
		|		ВТ_СоставНаборов КАК ВТ_СоставНаборов) КАК ВсяНоменклатура
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	Номенклатура
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	УстановкаЦен.ДокументОснование КАК Основание,
		|	ЦеныНоменклатуры25.Номенклатура КАК Номенклатура,
		|	МАКСИМУМ(ЦеныНоменклатуры25.Период) КАК ПериодУстановки
		|ПОМЕСТИТЬ ВТ_ДокУстановкаЦен
		|ИЗ
		|	РегистрСведений.ЦеныНоменклатуры25 КАК ЦеныНоменклатуры25
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.УстановкаЦенНоменклатуры КАК УстановкаЦен
		|		ПО ЦеныНоменклатуры25.Регистратор = УстановкаЦен.Ссылка
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_НоменклатураКРасчету КАК ВТ_Номенклатура
		|		ПО ЦеныНоменклатуры25.Номенклатура = ВТ_Номенклатура.Номенклатура
		|ГДЕ
		|	УстановкаЦен.Проведен
		|	И УстановкаЦен.ДокументОснование ССЫЛКА Документ.ПриобретениеТоваровУслуг
		|	И ЦеныНоменклатуры25.ВидЦены = &ВидЦены
		|	И ЦеныНоменклатуры25.Цена > 0
		|
		|СГРУППИРОВАТЬ ПО
		|	УстановкаЦен.ДокументОснование,
		|	ЦеныНоменклатуры25.Номенклатура
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	Номенклатура,
		|	Основание
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	ДокТовары.Номенклатура КАК Номенклатура,
		|	Док.Ссылка КАК Ссылка,
		|	ВТ_ДокУстановкаЦен.ПериодУстановки КАК ПериодУстановки,
		|	СУММА(ВЫБОР
		|			КОГДА Док.ЦенаВключаетНДС
		|				ТОГДА ДокТовары.Сумма - ДокТовары.СуммаНДС
		|			ИНАЧЕ ДокТовары.Сумма
		|		КОНЕЦ) КАК СуммаБезНДС,
		|	СУММА(ВЫБОР
		|			КОГДА Док.ЦенаВключаетНДС
		|				ТОГДА ДокТовары.Сумма
		|			ИНАЧЕ ДокТовары.Сумма + ДокТовары.СуммаНДС
		|		КОНЕЦ) КАК СуммаСНДС,
		|	СУММА(ДокТовары.Количество) КАК Количество
		|ПОМЕСТИТЬ ВТ_ПоДокументам
		|ИЗ
		|	Документ.ПриобретениеТоваровУслуг.Товары КАК ДокТовары
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ПриобретениеТоваровУслуг КАК Док
		|		ПО ДокТовары.Ссылка = Док.Ссылка
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ДокУстановкаЦен КАК ВТ_ДокУстановкаЦен
		|		ПО (Док.Ссылка = ВТ_ДокУстановкаЦен.Основание)
		|			И ДокТовары.Номенклатура = ВТ_ДокУстановкаЦен.Номенклатура
		|ГДЕ
		|	Док.Проведен
		|	И ДокТовары.Количество > 0
		|	И ДокТовары.Сумма > 0
		|
		|СГРУППИРОВАТЬ ПО
		|	ДокТовары.Номенклатура,
		|	Док.Ссылка,
		|	ВТ_ДокУстановкаЦен.ПериодУстановки
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	Номенклатура
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	ВТ_ПоДокументам.Номенклатура КАК Номенклатура,
		|	ВТ_ПоДокументам.Ссылка КАК Ссылка,
		|	ВТ_ПоДокументам.ПериодУстановки КАК ПериодУстановки,
		|	ВТ_ПоДокументам.СуммаБезНДС / ВТ_ПоДокументам.Количество КАК ЦенаДокумента,
		|	ВТ_ПоДокументам.СуммаСНДС / ВТ_ПоДокументам.Количество КАК ЦенаДокументаСНДС
		|ПОМЕСТИТЬ ВТ_ЦеныДокументов
		|ИЗ
		|	ВТ_ПоДокументам КАК ВТ_ПоДокументам
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	Номенклатура,
		|	ПериодУстановки
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	ВТ_ЦеныДокументов.Номенклатура КАК Номенклатура,
		|	СРЕДНЕЕ(ВТ_ЦеныДокументов.ЦенаДокумента) КАК СредняяЦена,
		|	СРЕДНЕЕ(ВТ_ЦеныДокументов.ЦенаДокументаСНДС) КАК СредняяЦенаСНДС,
		|	КОЛИЧЕСТВО(ВТ_ЦеныДокументов.Ссылка) КАК КоличествоДокументов,
		|	МАКСИМУМ(ВТ_ЦеныДокументов.ПериодУстановки) КАК ПериодУстановки
		|ПОМЕСТИТЬ ВТ_СредняяЦена
		|ИЗ
		|	ВТ_ЦеныДокументов КАК ВТ_ЦеныДокументов
		|
		|СГРУППИРОВАТЬ ПО
		|	ВТ_ЦеныДокументов.Номенклатура
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	Номенклатура,
		|	ПериодУстановки
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	ВТ_ЦеныДокументов.Номенклатура КАК Номенклатура,
		|	СРЕДНЕЕ(ВТ_ЦеныДокументов.ЦенаДокумента) КАК ТекущаяЦенаБезНДС,
		|	СРЕДНЕЕ(ВТ_ЦеныДокументов.ЦенаДокументаСНДС) КАК ТекущаяЦенаСНДС
		|ПОМЕСТИТЬ ВТ_ПоследняяЦена
		|ИЗ
		|	ВТ_ЦеныДокументов КАК ВТ_ЦеныДокументов
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_СредняяЦена КАК ВТ_СредняяЦена
		|		ПО ВТ_ЦеныДокументов.Номенклатура = ВТ_СредняяЦена.Номенклатура
		|			И ВТ_ЦеныДокументов.ПериодУстановки = ВТ_СредняяЦена.ПериодУстановки
		|
		|СГРУППИРОВАТЬ ПО
		|	ВТ_ЦеныДокументов.Номенклатура
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	Номенклатура
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////		
		|ВЫБРАТЬ
		|	ЦеныСрез.Номенклатура КАК Номенклатура,
		|	ЦеныСрез.Цена КАК Цена
		|ПОМЕСТИТЬ ВТ_ЦеныБезПриобретений
		|ИЗ
		|	РегистрСведений.ЦеныНоменклатуры25.СрезПоследних(&КонецПериода, ВидЦены = &ВидЦены) КАК ЦеныСрез
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_НоменклатураКРасчету КАК ВТ_Номенклатура
		|		ПО ЦеныСрез.Номенклатура = ВТ_Номенклатура.Номенклатура
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Ном
		|		ПО ЦеныСрез.Номенклатура = Ном.Ссылка
		|		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_СредняяЦена КАК ВТ_СредняяЦена
		|		ПО ЦеныСрез.Номенклатура = ВТ_СредняяЦена.Номенклатура
		|ГДЕ
		|	ВТ_СредняяЦена.Номенклатура ЕСТЬ NULL
		|	И ЦеныСрез.Цена > 0
		|	И НЕ Ном.ПометкаУдаления
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	Номенклатура
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	ВТ_СредняяЦена.Номенклатура КАК Номенклатура,
		|	ВТ_СредняяЦена.СредняяЦена КАК СредняяБезНДС,
		|	ВТ_СредняяЦена.СредняяЦенаСНДС КАК СредняяСНДС,
		|	ВТ_ПоследняяЦена.ТекущаяЦенаБезНДС КАК ПоследняяБезНДС,
		|	ВТ_ПоследняяЦена.ТекущаяЦенаСНДС КАК ПоследняяСНДС,
		|	ВТ_СредняяЦена.КоличествоДокументов КАК КоличествоДокументов
		|ПОМЕСТИТЬ ВТ_ИтоговыеЦены
		|ИЗ
		|	ВТ_СредняяЦена КАК ВТ_СредняяЦена
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ПоследняяЦена КАК ВТ_ПоследняяЦена
		|		ПО ВТ_СредняяЦена.Номенклатура = ВТ_ПоследняяЦена.Номенклатура
		|
		|ОБЪЕДИНИТЬ ВСЕ
		|
		|ВЫБРАТЬ
		|	ВТ_Цены.Номенклатура,
		|	ВТ_Цены.Цена * 0.8,
		|	ВТ_Цены.Цена,
		|	ВТ_Цены.Цена * 0.8,
		|	ВТ_Цены.Цена,
		|	0
		|ИЗ
		|	ВТ_ЦеныБезПриобретений КАК ВТ_Цены
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	Номенклатура
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	ДопРеквизиты.Ссылка КАК Номенклатура,
		|	МАКСИМУМ(ВЫБОР
		|			КОГДА ДопРеквизиты.Свойство = &СвойствоOEM
		|				ТОГДА ВЫРАЗИТЬ(ДопРеквизиты.Значение КАК СТРОКА(150))
		|			ИНАЧЕ """"
		|		КОНЕЦ) КАК OEMНомер,
		|	МАКСИМУМ(ВЫБОР
		|			КОГДА ДопРеквизиты.Свойство = &СвойствоАртикулМП
		|				ТОГДА ВЫРАЗИТЬ(ДопРеквизиты.Значение КАК СТРОКА(150))
		|			ИНАЧЕ """"
		|		КОНЕЦ) КАК АртикулМаркетплейса
		|ПОМЕСТИТЬ ВТ_ДопРеквизиты
		|ИЗ
		|	Справочник.Номенклатура.ДополнительныеРеквизиты КАК ДопРеквизиты
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_НоменклатураКРасчету КАК ВТ_Номенклатура
		|		ПО ДопРеквизиты.Ссылка = ВТ_Номенклатура.Номенклатура
		|ГДЕ
		|	ДопРеквизиты.Свойство В (&СвойствоOEM, &СвойствоАртикулМП)
		|
		|СГРУППИРОВАТЬ ПО
		|	ДопРеквизиты.Ссылка
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	Номенклатура
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	ПРЕДСТАВЛЕНИЕ(Ном.Марка) КАК Марка,
		|	Ном.Артикул КАК Артикул,
		|	ЕСТЬNULL(ВТ_ДопРеквизиты.АртикулМаркетплейса, """") КАК АртикулМаркетплейса,
		|	Ном.Код КАК Код,
		|	ЕСТЬNULL(ВТ_ДопРеквизиты.OEMНомер, """") КАК OEMНомер,
		|	Ном.Ссылка КАК Номенклатура,
		|	ПРЕДСТАВЛЕНИЕ(Ном.Ссылка) КАК Наименование,
		|	ВТ_ИтоговыеЦены.ПоследняяБезНДС КАК ЦенаБезНДС,
		|	ВТ_ИтоговыеЦены.СредняяБезНДС КАК СредняяЦенаБезНДС,
		|	ВТ_ИтоговыеЦены.ПоследняяСНДС КАК Цена,
		|	ВТ_ИтоговыеЦены.СредняяСНДС КАК СредняяЦена,
		|	ВТ_ИтоговыеЦены.КоличествоДокументов КАК КоличествоДокументов
		|ИЗ
		|	ВТ_ИтоговыеЦены КАК ВТ_ИтоговыеЦены
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Ном
		|		ПО ВТ_ИтоговыеЦены.Номенклатура = Ном.Ссылка
		|		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ДопРеквизиты КАК ВТ_ДопРеквизиты
		|		ПО ВТ_ИтоговыеЦены.Номенклатура = ВТ_ДопРеквизиты.Номенклатура
		|ГДЕ
		|	НЕ Ном.ПометкаУдаления
		|	И ВТ_ИтоговыеЦены.СредняяСНДС > 0
		|	И НЕ ВРЕГ(Ном.Наименование) ПОДОБНО &МаскаНоменклатуры
		|	И НЕ ВРЕГ(Ном.Артикул) ПОДОБНО &МаскаНоменклатуры
		|	И Ном.ТипНоменклатуры <> ЗНАЧЕНИЕ(Перечисление.ТипыНоменклатуры.Набор)
		|
		|УПОРЯДОЧИТЬ ПО
		|	Артикул
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	ВТ_СоставНаборов.Набор КАК Набор,
		|	ПРЕДСТАВЛЕНИЕ(Ном.Ссылка) КАК Наименование,
		|	Ном.Код КАК Код,
		|	Ном.Артикул КАК Артикул,
		|	ПРЕДСТАВЛЕНИЕ(Ном.Марка) КАК Марка,
		|	ЕСТЬNULL(ВТ_ДопРеквизиты.OEMНомер, """") КАК OEMНомер,
		|	ЕСТЬNULL(ВТ_ДопРеквизиты.АртикулМаркетплейса, """") КАК АртикулМаркетплейса,
		|	ВТ_СоставНаборов.Вариант КАК Вариант,
		|	ВТ_СоставНаборов.ВариантНаименование КАК ВариантНаименование,
		|	ВТ_СоставНаборов.НомерСтроки КАК НомерСтроки,
		|	ВТ_СоставНаборов.Компонент КАК Компонент,
		|	ВТ_СоставНаборов.Количество КАК Количество
		|ИЗ
		|	ВТ_СоставНаборов КАК ВТ_СоставНаборов
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Ном
		|		ПО ВТ_СоставНаборов.Набор = Ном.Ссылка
		|		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ДопРеквизиты КАК ВТ_ДопРеквизиты
		|		ПО ВТ_СоставНаборов.Набор = ВТ_ДопРеквизиты.Номенклатура
		|
		|УПОРЯДОЧИТЬ ПО
		|	Набор,
		|	ВариантНаименование,
		|	НомерСтроки";

КонецФункции

Функция СформироватьОтправитьВKafka(ЦеныТЗ)

	Сообщения = Новый Массив;

	Для Каждого СтрЦена Из ЦеныТЗ Цикл

		Элемент = Новый Структура;
		Элемент.Вставить("name",       СокрЛП(СтрЦена.Наименование));
		Элемент.Вставить("code",       СокрЛП(СтрЦена.Код));
		Элемент.Вставить("brand",      СокрЛП(СтрЦена.Марка));
		Элемент.Вставить("article",    СокрЛП(СтрЦена.Артикул));
		Элемент.Вставить("article_mp", СокрЛП(СтрЦена.АртикулМаркетплейса));
		Элемент.Вставить("oem",        СокрЛП(СтрЦена.OEMНомер));
		Элемент.Вставить("last_price", СтрЦена.Цена);
		Элемент.Вставить("avg_price",  СтрЦена.СредняяЦена);

		Сообщение = Новый Структура("Ключ, Тело",
			Строка(СтрЦена.Номенклатура.УникальныйИдентификатор()),
			ОбщегоНазначения.ЗначениеВJSON(Элемент));

		Сообщения.Добавить(Сообщение);

	КонецЦикла;

	Возврат ОтправитьВKafka(Сообщения);

КонецФункции

Процедура ВыгрузитьВExcel(ЦеныТЗ)
	
	Каталог = "C:\Выгрузка закупочных цен";
//	Каталог = СтрЗаменить(Каталог,"Выгрузка закупочных цен","Тестовая Папка");	
	Ключи = "Имя, Заголовок, Ширина, Формат, Переносить";
	
	Колонки = Новый Массив;
	Колонки.Добавить(Новый Структура(Ключи, "Марка",               			  "Марка",                	  14, "", Ложь));
	Колонки.Добавить(Новый Структура(Ключи, "Артикул",             			  "Артикул",              	  18, "", Истина));
	Колонки.Добавить(Новый Структура(Ключи, "АртикулМаркетплейса", 			  "Артикул МП",           	  18, "", Истина));
	Колонки.Добавить(Новый Структура(Ключи, "АртикулМаркетплейсаРасширенный", "Артикул МП (расширенный)", 18, "", Истина));
	Колонки.Добавить(Новый Структура(Ключи, "Код",                 			  "Код",                  	  12, "", Ложь));
	Колонки.Добавить(Новый Структура(Ключи, "OEMНомер",            			  "OEM номер",            	  22, "", Истина)); 
	Колонки.Добавить(Новый Структура(Ключи, "Номенклатура",        			  "Номенклатура",         	  50, "", Истина));
	Колонки.Добавить(Новый Структура(Ключи, "СредняяЦена",         			  "Средняя цена с НДС",   	  16, "ЧДЦ=2; ЧН=0; ЧРГ=", Ложь));
	Колонки.Добавить(Новый Структура(Ключи, "Цена",                			  "Последняя цена с НДС", 	  16, "ЧДЦ=2; ЧН=0; ЧРГ=", Ложь));
 	Колонки.Добавить(Новый Структура(Ключи, "СредняяЦенаБезНДС",   			  "Средняя цена без НДС", 	  16, "ЧДЦ=2; ЧН=0; ЧРГ=", Ложь));
	Колонки.Добавить(Новый Структура(Ключи, "ЦенаБезНДС",          			  "Последняя цена без НДС",   16, "ЧДЦ=2; ЧН=0; ЧРГ=", Ложь));
	
	Попытка

		ТабДок = Новый ТабличныйДокумент;

		Для НомерКолонки = 1 По Колонки.Количество() Цикл
			Описание = Колонки[НомерКолонки - 1];
			Ячейка = ТабДок.Область(1, НомерКолонки, 1, НомерКолонки);
			Ячейка.Текст = Описание.Заголовок;
			Ячейка.Шрифт = Новый Шрифт(, , Истина); 
			Ячейка.РазмещениеТекста = ТипРазмещенияТекстаТабличногоДокумента.Переносить;
			Ячейка.ВертикальноеПоложение = ВертикальноеПоложение.Центр;		
			Ячейка.ГоризонтальноеПоложение = ГоризонтальноеПоложение.Центр;
			Ячейка.ЦветФона = Новый Цвет(230, 230, 230);
			ТабДок.Область(, НомерКолонки, , НомерКолонки).ШиринаКолонки = Описание.Ширина;
		КонецЦикла;

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

		ИмяФайла = Каталог
			+ ?(Прав(Каталог, 1) = ПолучитьРазделительПути(), "", ПолучитьРазделительПути())
			+ "Цены_" + Формат(ТекущаяДатаСеанса(), "ДФ=yyyyMMdd_HHmmss") + ".xlsx";

		ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.XLSX);

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

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

Процедура ВывестиМетодикуРасчета(ТабДок, ПоследняяСтрока, КоличествоКолонок)

	ТекстМетодики =
		"Методика расчета: Изначально берется период в 6 месяцев, по которому смотрятся все документы
		|приобретения товаров и услуг и установки цен. Рассчитываются значения с НДС и без НДС.
		|Если в какой-то из месяцев приобретений товаров и услуг не было, то в рассчетах данный месяц
		|не присутствует. Если за последние полгода не было ни одного приобретения товаров и услуг,
		|то промежуток увеличивается до 9 месяцев, если все еще нет, то уже до 12 месяцев.
		|Номенклатура, в которой цена не была рассчитана (или не рассчитаны отдельные составляющие
		|набора) выводится с нулевым значением. Номенклатура, у которой есть документ установки цен,
		|но нет приобретений товаров и услуг также присутствует в рассчетах.";
	
	ТекстМетодики = СтрЗаменить(ТекстМетодики, Символы.ПС, " ");
	СтрокаНачала = ПоследняяСтрока + 2;
	СтрокаОкончания = СтрокаНачала + 7;

	Область = ТабДок.Область(СтрокаНачала, 1, СтрокаОкончания, КоличествоКолонок);
	Область.Объединить();
	Область.Текст = ТекстМетодики;
	Область.Шрифт = Новый Шрифт(, , Истина);
	Область.РазмещениеТекста = ТипРазмещенияТекстаТабличногоДокумента.Переносить;
	Область.ГоризонтальноеПоложение = ГоризонтальноеПоложение.Центр;
	Область.ВертикальноеПоложение = ВертикальноеПоложение.Центр;

	Линия = Новый Линия(ТипЛинииЯчейкиТабличногоДокумента.Сплошная);
	Область.Обвести(Линия, Линия, Линия, Линия);

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

#КонецОбласти


// https://github.com/NuclearAPK/Simple-Kafka_Adapter
// Общий модуль: кор_HTTPКоннектор
//
// Настройки хранятся в константах:
//   кор_KafkaБрокеры      (Строка) - "host:port,host:port,..."
//   кор_KafkaТопик        (Строка) - имя топика
//   кор_KafkaКаталогЛогов (Строка) - каталог логов компоненты
//
#Область Кафка

#Область ПрограммныйИнтерфейс

Функция ПолучитьСтруктуруПодключения() Экспорт

	СтруктураПодключения = Новый Структура;
	СтруктураПодключения.Вставить("Брокеры",      СокрЛП(Константы.кор_KafkaБрокеры.Получить()));
	СтруктураПодключения.Вставить("ИмяТопика",    СокрЛП(Константы.кор_KafkaТопик.Получить()));
	СтруктураПодключения.Вставить("КаталогЛогов", СокрЛП(Константы.кор_KafkaКаталогЛогов.Получить()));

	Возврат СтруктураПодключения;

КонецФункции

// Отправляет поток сообщений в Kafka.
//
// Параметры:
//   Сообщения - Массив структур - каждый элемент: Ключ (Строка), Тело (Строка JSON)
//
// Возвращаемое значение:
//   Структура - Отправлено (Число), Ошибок (Число), Успех (Булево), Текст (Строка)
//
Функция ОтправитьВKafka(Сообщения) Экспорт

	ИмяСобытия = "Kafka. Отправка цен";
	Результат = Новый Структура("Отправлено, Ошибок, Успех, Текст", 0, 0, Ложь, "");

	Если ТипЗнч(Сообщения) <> Тип("Массив") ИЛИ Сообщения.Количество() = 0 Тогда
		Результат.Текст = "Нет данных для отправки.";
		Возврат Результат;
	КонецЕсли;

	Подключение = ПолучитьСтруктуруПодключения();

	Если НЕ ЗначениеЗаполнено(Подключение.Брокеры) Тогда
		Результат.Текст = "Не заполнена константа кор_KafkaБрокеры.";
		ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка, , , Результат.Текст);
		Возврат Результат;
	КонецЕсли;

	Если НЕ ЗначениеЗаполнено(Подключение.ИмяТопика) Тогда
		Результат.Текст = "Не заполнена константа кор_KafkaТопик.";
		ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка, , , Результат.Текст);
		Возврат Результат;
	КонецЕсли;

	Компонента = СоздатьКомпонентуКафкаКлиент();
		
	Если Компонента = Неопределено Тогда
		Результат.Текст = "Не удалось создать компоненту Kafka.";
		Возврат Результат;
	КонецЕсли;
		
	Попытка
		Компонента.УстановитьТаймаутОчисткиПродюсера(60000);
	Исключение
	КонецПопытки;
	
	Если НЕ Компонента.ИнициализироватьПродюсера(Подключение.Брокеры) Тогда
		Результат.Текст = "Не удалось инициализировать продюсера. Брокеры: " + Подключение.Брокеры
			+ ". " + Компонента.ПолучитьСообщениеОбОшибке();
		ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка, , , Результат.Текст);
		Компонента = Неопределено;
		Возврат Результат;
	КонецЕсли;

	Всего = Сообщения.Количество();

	Попытка

		ОтправитьПакетами(Компонента, Сообщения, Подключение.ИмяТопика, Результат, ИмяСобытия);

	Исключение
		Результат.Текст = "Прервано на " + (Результат.Отправлено + Результат.Ошибок) + " из " + Всего
			+ ". " + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
		ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка, , , Результат.Текст);

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

	Компонента = Неопределено;

	Результат.Успех = (Результат.Ошибок = 0) И ОчередьОчищена;
	Результат.Текст = СтрШаблон("Отправлено: %1, ошибок: %2, всего: %3.",
		Результат.Отправлено, Результат.Ошибок, Всего);

	ЗаписьЖурналаРегистрации(ИмяСобытия,
		?(Результат.Успех, УровеньЖурналаРегистрации.Информация, УровеньЖурналаРегистрации.Предупреждение),
		, , Результат.Текст);

	Возврат Результат;

КонецФункции

Функция ОстановитьПродюсераБезопасно(Компонента, ИмяСобытия)

	Попытка
		Возврат Компонента.ОстановитьПродюсера();
	Исключение
	КонецПопытки;

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

КонецФункции

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

Процедура ОтправитьПакетами(Компонента, Сообщения, Топик, Результат, ИмяСобытия)

	РазмерПачки = 1000;
	Пачка = Новый Массив;

	Для Каждого ЭлементСообщения Из Сообщения Цикл

		Ключ = ?(ЭлементСообщения.Свойство("Ключ"), Строка(ЭлементСообщения.Ключ), "");
		Тело = ?(ЭлементСообщения.Свойство("Тело"), Строка(ЭлементСообщения.Тело), "");

		Если НЕ ЗначениеЗаполнено(Тело) Тогда
			Результат.Ошибок = Результат.Ошибок + 1;
			Продолжить;
		КонецЕсли;

		СообщениеКомпоненты = Новый Структура;
		СообщениеКомпоненты.Вставить("message", Тело);
		Если ЗначениеЗаполнено(Ключ) Тогда
			СообщениеКомпоненты.Вставить("key", Ключ);
		КонецЕсли;

		Пачка.Добавить(СообщениеКомпоненты);

		Если Пачка.Количество() >= РазмерПачки Тогда
			ОтправитьОднуПачку(Компонента, Пачка, Топик, Результат, ИмяСобытия);
			Пачка = Новый Массив;
		КонецЕсли;

	КонецЦикла;

	Если Пачка.Количество() > 0 Тогда
		ОтправитьОднуПачку(Компонента, Пачка, Топик, Результат, ИмяСобытия);
	КонецЕсли;

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

Процедура ОтправитьОднуПачку(Компонента, Пачка, Топик, Результат, ИмяСобытия)

	ЗаписьJSON = Новый ЗаписьJSON;
	ЗаписьJSON.УстановитьСтроку();
	ЗаписатьJSON(ЗаписьJSON, Пачка);
	ПачкаJSON = ЗаписьJSON.Закрыть();

	ОтветJSON = "";
	ЕстьПакетныйМетод = Истина;

	Попытка
		ОтветJSON = Компонента.ОтправитьПакетСРезультатом(ПачкаJSON, Топик, 60000);
	Исключение
		ЕстьПакетныйМетод = Ложь;
	КонецПопытки;

	Если НЕ ЕстьПакетныйМетод Тогда
		ОтправитьПоштучно(Компонента, Пачка, Топик, Результат, ИмяСобытия);
		Возврат;
	КонецЕсли;

	Если ПустаяСтрока(ОтветJSON) Тогда
		Результат.Ошибок = Результат.Ошибок + Пачка.Количество();
		ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка, , ,
			"Пакет не отправлен: " + Компонента.ПолучитьСообщениеОбОшибке());
		Возврат;
	КонецЕсли;

	ЧтениеJSON = Новый ЧтениеJSON;
	ЧтениеJSON.УстановитьСтроку(ОтветJSON);
	ОтветПакета = ПрочитатьJSON(ЧтениеJSON);
	ЧтениеJSON.Закрыть();

	Доставлено = Число(ОтветПакета["success_count"]);
	НеДоставлено = Число(ОтветПакета["failed_count"]);

	Результат.Отправлено = Результат.Отправлено + Доставлено;
	Результат.Ошибок = Результат.Ошибок + НеДоставлено;

	Если НеДоставлено > 0 Тогда
		ЗаписатьОшибкиПакета(ОтветПакета, ИмяСобытия);
	КонецЕсли;

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

Процедура ЗаписатьОшибкиПакета(ОтветПакета, ИмяСобытия)

	СтрокиОшибок = Новый Массив;

	Для Каждого СтрокаРезультата Из ОтветПакета["results"] Цикл
		Если СтрокаРезультата["delivered"] = "true" Тогда
			Продолжить;
		КонецЕсли;
		СтрокиОшибок.Добавить(СтрШаблон("Ключ: %1. %2",
			СтрокаРезультата["key"], СтрокаРезультата["error"]));
			
		Если СтрокиОшибок.Количество() >= 50 Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;

	ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка, , ,
		"Недоставленные сообщения:" + Символы.ПС + СтрСоединить(СтрокиОшибок, Символы.ПС));

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

Процедура ОтправитьПоштучно(Компонента, Пачка, Топик, Результат, ИмяСобытия)

	Для Каждого СообщениеКомпоненты Из Пачка Цикл

		Ключ = "";
		СообщениеКомпоненты.Свойство("key", Ключ);
		Тело = СообщениеКомпоненты["message"];

		РезультатОтправки = Компонента.ОтправитьСообщение(Тело, Топик, -1, Ключ);

		Если РезультатОтправки = -1 Тогда
			Результат.Ошибок = Результат.Ошибок + 1;
			ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка, , ,
				"Не поставлено в очередь. Ключ: " + Ключ + ". " + Компонента.ПолучитьСообщениеОбОшибке());
		Иначе
			Результат.Отправлено = Результат.Отправлено + 1;
		КонецЕсли;

	КонецЦикла;

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

Функция СоздатьКомпонентуКафкаКлиент() Экспорт

	ИмяСобытия = "Kafka. Подключение компоненты";

	Попытка
		Подключено = ПодключитьВнешнююКомпоненту(
			"ОбщийМакет.SimpleKafkaAdapter64",
			"KafkaClient",
			ТипВнешнейКомпоненты.Native,
			ТипПодключенияВнешнейКомпоненты.Изолированно);
	Исключение
		ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка, , ,
			ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
		Возврат Неопределено;
	КонецПопытки;

	Если НЕ Подключено Тогда
		ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка, , ,
			"Не удалось подключить компоненту из общего макета SimpleKafkaAdapter64.");
		Возврат Неопределено;
	КонецЕсли;

	Попытка
		Компонента = Новый("AddIn.KafkaClient.simpleKafka1C");
	Исключение
		ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка, , ,
			"Компонента подключилась, но объект не создался: "
				+ ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
		Возврат Неопределено;
	КонецПопытки;

	Возврат Компонента;

КонецФункции

#КонецОбласти

#КонецОбласти

#Область API_Берга

// Клиент API Берга (https://api.berg.ru, версия 1.0): остатки, цены, справочник брендов.

#Область РегламентноеЗадание_кор_ЗагрузкаОстатковБерга

Процедура ЗагрузитьОстаткиБерга() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);

	ПараметрыПодключения = НовыеПараметрыПодключения();
	ПараметрыПодключения.Ключ = Константы.кор_БергКлючAPI.Получить(); 
	ПараметрыПодключения.ТипыСкладов.Добавить(1);
	
	Если ПустаяСтрока(ПараметрыПодключения.Ключ) Тогда
		Текст = НСтр("ru = 'Загрузка остатков Берга пропущена: API-ключ не сохранён.'");
		ЗаписатьВЖурнал(УровеньЖурналаРегистрации.Предупреждение, Текст);
	КонецЕсли;

	Позиции = ПозицииДляЗапроса();
	Если Позиции.Количество() = 0 Тогда
		Текст = НСтр("ru = 'Загрузка остатков Берга: нет товаров, подходящих под отбор.'");
		ЗаписатьВЖурнал(УровеньЖурналаРегистрации.Информация, Текст);
	КонецЕсли;
	
	МоментЗагрузки = ТекущаяДатаСеанса();
	Ответ = ПолучитьОстатки(ПараметрыПодключения, Позиции.ВыгрузитьКолонку("Артикул"));

	ПачекСОшибкой = Ответ.Ошибки.Количество();
	Для Каждого ТекстОшибки Из Ответ.Ошибки Цикл
		ЗаписатьВЖурнал(УровеньЖурналаРегистрации.Ошибка, ТекстОшибки);
	КонецЦикла;

	Если Ответ.КоличествоПачек > 0 И Ответ.Ошибки.Количество() = Ответ.КоличествоПачек Тогда
		Текст = СтрШаблон(
			НСтр("ru = 'Загрузка остатков Берга: ни одна из %1 пачек не загрузилась, регистр не изменён.'"),
			Ответ.КоличествоПачек);
		ЗаписатьВЖурнал(УровеньЖурналаРегистрации.Ошибка, Текст);
	КонецЕсли;
		
	ЗаполнитьПозиции(Ответ, Позиции);
	
 	Если Ответ.АртикулыСОшибкой.Количество() > 0 Тогда
		ДобавитьПрежниеЗаписи(Позиции, Ответ.АртикулыСОшибкой);
	КонецЕсли;

	ЗаписатьОстатки(Позиции);

	Текст = СтрШаблон(
		НСтр("ru = 'Загрузка остатков Берга: загружено %1, пачек с ошибкой %2.'"),
			Позиции.Количество(), ПачекСОшибкой);
	ЗаписатьВЖурнал(УровеньЖурналаРегистрации.Информация, Текст);

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

Процедура ЗаполнитьПозиции(Ответ, Позиции)
		
	Предложения = Ответ.Предложения;
	МассивКУдалению = Новый Массив;
	
	Для Каждого Позиция Из Позиции Цикл
		МассивСтрок = Предложения.НайтиСтроки(Новый Структура("Артикул", Позиция.Артикул));
		Если МассивСтрок.Количество() = 1 Тогда
			Строка = МассивСтрок[0];
			ЗаполнитьЗначенияСвойств(Позиция,Строка);
		ИначеЕсли МассивСтрок.Количество() > 1 Тогда
			КоличествоСтрок = МассивСтрок.Количество();
			Количество = 0;
			Цена = 0;		
			Для Каждого Стр Из МассивСтрок Цикл
				Количество = Количество + Стр.Количество;
				Цена = Цена + Стр.Цена;
			КонецЦикла;
			Позиция.Количество = Количество;
			Если Цена <> 0 Тогда
				Позиция.Цена = Окр((Цена/КоличествоСтрок),2);
			КонецЕсли;
		Иначе
			МассивКУдалению.Добавить(Позиция);			
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого СтрКУдалению Из МассивКУдалению Цикл
		Позиции.Удалить(СтрКУдалению);	
	КонецЦикла;

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

Функция ПозицииДляЗапроса()

	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	Номенклатура.Ссылка КАК Номенклатура,
	|	Номенклатура.Артикул КАК Артикул,
	|	Номенклатура.Марка.Наименование КАК Марка,
	|   0 КАК Количество,
	|   0 КАК Цена,
	|	ДАТАВРЕМЯ(1, 1, 1) КАК ДатаОбновления
	|ИЗ
	|	Справочник.Номенклатура КАК Номенклатура
	|ГДЕ
	|	НЕ Номенклатура.ЭтоГруппа
	|	И НЕ Номенклатура.ПометкаУдаления
	|	И Номенклатура.Артикул <> """"
	|	И НЕ Номенклатура.Артикул ПОДОБНО ""%не использовать%""
	|	И Номенклатура.Марка = &Марка
	|	И НЕ Номенклатура.Наименование ПОДОБНО ""%не использовать%""
	|
	|УПОРЯДОЧИТЬ ПО
	|	Номенклатура.Наименование"; 
	
	Запрос.УстановитьПараметр("Марка", Справочники.Марки.НайтиПоНаименованию("CTR"));

	Возврат Запрос.Выполнить().Выгрузить();

КонецФункции

Процедура ДобавитьПрежниеЗаписи(Позиции, АртикулыСОшибкой)

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

	Выборка = Запрос.Выполнить().Выбрать();
	Пока Выборка.Следующий() Цикл
		ЗаполнитьЗначенияСвойств(Позиции, Выборка);
	КонецЦикла;

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

Процедура ЗаписатьОстатки(Записи)
	
	Набор = РегистрыСведений.кор_ОстаткиПоставщиков.СоздатьНаборЗаписей();
	Для Каждого Строка Из Записи Цикл
		НоваяЗапись = Набор.Добавить();
		НоваяЗапись.ДатаОбновления = ТекущаяДатаСеанса();
		ЗаполнитьЗначенияСвойств(НоваяЗапись, Строка);
	КонецЦикла;
	Попытка
		Набор.Записать();
	Исключение
		ЗаписатьВЖурнал(УровеньЖурналаРегистрации.Ошибка, СтрШаблон(
			НСтр("ru = 'Не удалось записать остатки: %1'"),
		 ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())));
	КонецПопытки;

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

Процедура ЗаписатьВЖурнал(Уровень, Текст)

	ЗаписьЖурналаРегистрации("Берг.API", Уровень,
		Метаданные.РегистрыСведений.кор_ОстаткиПоставщиков,, Текст);

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

#КонецОбласти

#Область ПрограммныйИнтерфейс

// Параметры подключения по умолчанию.
//
// Возвращаемое значение:
//  Структура:
//   * Ключ        - Строка - API-ключ из личного кабинета Берга (передаётся в заголовке X-Berg-API-Key).
//   * Сервер      - Строка
//   * Версия      - Строка - версия API в пути запроса.
//   * Таймаут     - Число  - секунды.
//   * ТипыСкладов - Массив из Число - 1 - склад филиала, 2 - ЦС Берга, 3 - дополнительные;
//                   пустой массив - все типы складов.
//
Функция НовыеПараметрыПодключения() Экспорт

	Параметры = Новый Структура;
	Параметры.Вставить("Ключ", "");
	Параметры.Вставить("Сервер", "api.berg.ru");
	Параметры.Вставить("Версия", "v1.0");
	Параметры.Вставить("Таймаут", 60);
	Параметры.Вставить("ТипыСкладов", Новый Массив);

	Возврат Параметры;

КонецФункции

Функция ПолучитьОстатки(ПараметрыПодключения, Позиции) Экспорт
  
	Результат = Новый Структура;
	Результат.Вставить("Предложения", НоваяТаблицаПредложений());
	Результат.Вставить("Ошибки", Новый Массив);
	Результат.Вставить("АртикулыСОшибкой", Новый Массив);
	Результат.Вставить("КоличествоПачек", 0);
	Результат.Вставить("ОтветыСервера", Новый Массив);

	РазмерПачки = 50;
	Пачка = Новый Массив;

	Для Каждого Позиция Из Позиции Цикл
		Пачка.Добавить(Позиция);
		Если Пачка.Количество() = РазмерПачки Тогда
			Результат.КоличествоПачек = Результат.КоличествоПачек + 1;
			ЗагрузитьПачку(ПараметрыПодключения, Пачка, Результат.КоличествоПачек, Результат);
			Пачка = Новый Массив;
		КонецЕсли;
	КонецЦикла;

	Если Пачка.Количество() > 0 Тогда
		Результат.КоличествоПачек = Результат.КоличествоПачек + 1;
		ЗагрузитьПачку(ПараметрыПодключения, Пачка, Результат.КоличествоПачек, Результат);
	КонецЕсли;

	Возврат Результат;

КонецФункции

Функция НоваяТаблицаПредложений() Экспорт

	ТипЧисло  = Новый ОписаниеТипов("Число");
	ТипСтрока = Новый ОписаниеТипов("Строка");
	ТипБулево = Новый ОписаниеТипов("Булево");

	Таблица = Новый ТаблицаЗначений;
	Таблица.Колонки.Добавить("Артикул", ТипСтрока);
	Таблица.Колонки.Добавить("Наименование", ТипСтрока);
	Таблица.Колонки.Добавить("Количество", ТипЧисло);
	Таблица.Колонки.Добавить("Цена", ТипЧисло);                 

	Возврат Таблица;

КонецФункции

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

Процедура ЗагрузитьПачку(ПараметрыПодключения, Пачка, НомерПачки, Результат)
	
   	Items = Новый Массив;
	Для Каждого Позиция Из Пачка Цикл
		Элемент = Новый Соответствие;
        Элемент.Вставить("resource_article", СокрЛП(Позиция));
		Элемент.Вставить("brand_name", "CTR");	
		Items.Добавить(Элемент);
	КонецЦикла;

	Тело = Новый Соответствие;
	Тело.Вставить("analogs", 0);
	Тело.Вставить("items", Items);
 	Если ПараметрыПодключения.ТипыСкладов.Количество() > 0 Тогда
		Тело.Вставить("warehouse_types", ПараметрыПодключения.ТипыСкладов);
	КонецЕсли;

	Ответ = ВыполнитьЗапрос(ПараметрыПодключения, "POST", "ordering/get_stock", Тело);
	Результат.ОтветыСервера.Добавить(СтрШаблон("Пачка %1, HTTP %2" + Символы.ПС + "%3",
		НомерПачки, Ответ.КодСостояния, Ответ.Текст));

	Если ЗначениеЗаполнено(Ответ.Ошибка) Тогда
		Результат.Ошибки.Добавить(СтрШаблон(НСтр("ru = 'Пачка %1: %2'"), НомерПачки, Ответ.Ошибка));
		Для Каждого Позиция Из Пачка Цикл
			Результат.АртикулыСОшибкой.Добавить(Позиция);
		КонецЦикла;
		Возврат;
	КонецЕсли;

	Для Каждого Товар Из МассивИзОтвета(Ответ.Данные, "resources") Цикл
		
		Предложения = Товар.Получить("offers");
		
		Если ТипЗнч(Предложения) <> Тип("Массив") Тогда
			Продолжить;
		КонецЕсли;

		Для Каждого Предложение Из Предложения Цикл
			Строка = Результат.Предложения.Добавить();
			Строка.Артикул      = Товар.Получить("article");
			Строка.Наименование = Товар.Получить("name");
			Строка.Количество   = Предложение.Получить("quantity");
			Строка.Цена         = Предложение.Получить("price");
		КонецЦикла;
	КонецЦикла;

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

Функция ВыполнитьЗапрос(ПараметрыПодключения, Метод, Ресурс, Тело = Неопределено)

	Результат = Новый Структура("КодСостояния, Текст, Данные, Ошибка", 0, "", Неопределено, "");

	Если ПустаяСтрока(ПараметрыПодключения.Ключ) Тогда
		Результат.Ошибка = НСтр("ru = 'Не указан API-ключ Берга.'");
		Возврат Результат;
	КонецЕсли;

	Адрес = СтрШаблон("/%1/%2.json", ПараметрыПодключения.Версия, Ресурс);

	HTTPЗапрос = Новый HTTPЗапрос(Адрес);
	HTTPЗапрос.Заголовки.Вставить("X-Berg-API-Key", ПараметрыПодключения.Ключ);
	HTTPЗапрос.Заголовки.Вставить("Accept", "application/json");
	Если Тело <> Неопределено Тогда
		HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
		HTTPЗапрос.УстановитьТелоИзСтроки(ОбщегоНазначения.ЗначениеВJSON(Тело), КодировкаТекста.UTF8,
			ИспользованиеByteOrderMark.НеИспользовать);
	КонецЕсли;

	Попытка
		Соединение = Новый HTTPСоединение(ПараметрыПодключения.Сервер, 443,,,
			ПолучениеФайловИзИнтернета.ПолучитьПрокси("https"),
			ПараметрыПодключения.Таймаут,
			ОбщегоНазначенияКлиентСервер.НовоеЗащищенноеСоединение());
		Если Метод = "POST" Тогда
			HTTPОтвет = Соединение.ОтправитьДляОбработки(HTTPЗапрос);
		Иначе
			HTTPОтвет = Соединение.Получить(HTTPЗапрос);
		КонецЕсли;
	Исключение
		Результат.Ошибка = СтрШаблон(НСтр("ru = 'Не удалось выполнить запрос к %1: %2'"),
			ПараметрыПодключения.Сервер, КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
		ЗаписьЖурналаРегистрации("Берг.API", УровеньЖурналаРегистрации.Ошибка,,,
			Адрес + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
		Возврат Результат;
	КонецПопытки;

	Результат.КодСостояния = HTTPОтвет.КодСостояния;
	Результат.Текст = HTTPОтвет.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);

	Попытка
		Результат.Данные = ОбщегоНазначения.JSONВЗначение(Результат.Текст);
	Исключение
		Результат.Данные = Неопределено;
	КонецПопытки;

	ОписаниеОшибок = ОписаниеОшибокAPI(Результат.Данные);
	Если Результат.КодСостояния <> 200 Или ЗначениеЗаполнено(ОписаниеОшибок) Тогда
		Результат.Ошибка = СтрШаблон(НСтр("ru = 'HTTP %1. %2'"), Результат.КодСостояния,
			?(ЗначениеЗаполнено(ОписаниеОшибок), ОписаниеОшибок, Лев(Результат.Текст, 500)));
		ЗаписьЖурналаРегистрации("Берг.API", УровеньЖурналаРегистрации.Ошибка,,,
			Адрес + Символы.ПС + Результат.Ошибка);
	ИначеЕсли Результат.Данные = Неопределено Тогда
		Результат.Ошибка = НСтр("ru = 'Ответ сервера не является JSON.'");
	КонецЕсли;

	Возврат Результат;

КонецФункции

Функция ОписаниеОшибокAPI(Данные)

	Тексты = Новый Массив;
	Для Каждого Ошибка Из МассивИзОтвета(Данные, "errors") Цикл
		Тексты.Добавить(СтрШаблон("%1: %2",ЗначениеПоля(Ошибка, "code", "code"),Ошибка.Получить("text")));
	КонецЦикла;

	Возврат СтрСоединить(Тексты, "; ");

КонецФункции

Функция МассивИзОтвета(Данные, ИмяПоля)

	Результат = Новый Массив;
	Если ТипЗнч(Данные) <> Тип("Соответствие") Тогда
		Возврат Результат;
	КонецЕсли;

	Значение = Данные.Получить(ИмяПоля);
	Если ТипЗнч(Значение) <> Тип("Массив") Тогда
		Возврат Результат;
	КонецЕсли;

	Для Каждого Элемент Из Значение Цикл
		Если ТипЗнч(Элемент) = Тип("Соответствие") Тогда
			Результат.Добавить(Элемент);
		КонецЕсли;
	КонецЦикла;

	Возврат Результат;

КонецФункции

Функция ЗначениеПоля(Данные, ИмяПоля, ЗапасноеИмяПоля)

	Значение = Данные.Получить(ИмяПоля);
	Если Значение = Неопределено Тогда
		Значение = Данные.Получить(ЗапасноеИмяПоля);
	КонецЕсли;

	Возврат Значение;

КонецФункции

#КонецОбласти

#КонецОбласти