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


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

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

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

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

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

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

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

	РезультатОтправки = СформироватьОтправитьВKafka(ЦеныТЗ);

	ВыгрузитьВExcel(ЦеныТЗ);

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

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

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

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

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

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

	Запрос = Новый Запрос(ТекстЗапросаЦены());
	Запрос.УстановитьПараметр("ВидЦены", ВидЦены);
	Запрос.УстановитьПараметр("КонецПериода", ТекущаяДатаСеанса());
	Запрос.УстановитьПараметр("СвойствоOEM", СвойствоOEM);
	Запрос.УстановитьПараметр("СвойствоАртикулМП", СвойствоАртикулМП);
 	Запрос.УстановитьПараметр("МаскаНоменклатуры", "%не использовать%");

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

	ЦеныТЗ  = Результаты[Результаты.Количество() - 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
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	Набор,
		|	Компонент
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ВсяНоменклатура.Номенклатура КАК Номенклатура
		|ПОМЕСТИТЬ ВТ_НоменклатураКРасчету
		|ИЗ
		|	(ВЫБРАТЬ
		|		ОстаткиТоваров.Номенклатура КАК Номенклатура
		|	ИЗ
		|		РегистрНакопления.ТоварыНаСкладах.Остатки(&КонецПериода, ) КАК ОстаткиТоваров
		|	ГДЕ
		|		ОстаткиТоваров.ВНаличииОстаток > 0
		|	
		|	ОБЪЕДИНИТЬ ВСЕ
		|	
		|	ВЫБРАТЬ
		|		ВТ_СоставНаборов.Компонент
		|	ИЗ
		|		ВТ_СоставНаборов КАК ВТ_СоставНаборов
		|	
		|	ОБЪЕДИНИТЬ ВСЕ
		|	
		|	ВЫБРАТЬ
		|		ВТ_СоставНаборов.Набор
		|	ИЗ
		|		ВТ_СоставНаборов КАК ВТ_СоставНаборов) КАК ВсяНоменклатура
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	Номенклатура
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	УстановкаЦен.ДокументОснование КАК Основание,
		|	ЦеныНоменклатуры25.Номенклатура КАК Номенклатура,
		|	МАКСИМУМ(ЦеныНоменклатуры25.Период) КАК ПериодУстановки
		|ПОМЕСТИТЬ ВТ_ДокУстановкаЦен
		|ИЗ
		|	РегистрСведений.ЦеныНоменклатуры25 КАК ЦеныНоменклатуры25
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.УстановкаЦенНоменклатуры КАК УстановкаЦен
		|		ПО ЦеныНоменклатуры25.Регистратор = УстановкаЦен.Ссылка
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_НоменклатураКРасчету КАК ВТ_Номенклатура
		|		ПО ЦеныНоменклатуры25.Номенклатура = ВТ_Номенклатура.Номенклатура
		|ГДЕ
		|	УстановкаЦен.Проведен
		|	И УстановкаЦен.ДокументОснование ССЫЛКА Документ.ПриобретениеТоваровУслуг
		|	И ЦеныНоменклатуры25.ВидЦены = &ВидЦены
		|	И ЦеныНоменклатуры25.Цена > 0
		|
		|СГРУППИРОВАТЬ ПО
		|	УстановкаЦен.ДокументОснование,
		|	ЦеныНоменклатуры25.Номенклатура
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	Номенклатура,
		|	Основание
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	ДокТовары.Номенклатура КАК Номенклатура,
		|	Док.Ссылка КАК Ссылка,
		|	ВТ_ДокУстановкаЦен.ПериодУстановки КАК ПериодУстановки,
		|	СУММА(ВЫБОР
		|			КОГДА Док.ЦенаВключаетНДС
		|				ТОГДА ДокТовары.Сумма - ДокТовары.СуммаНДС
		|			ИНАЧЕ ДокТовары.Сумма
		|		КОНЕЦ) КАК СуммаБезНДС,
		|	СУММА(ВЫБОР
		|			КОГДА Док.ЦенаВключаетНДС
		|				ТОГДА ДокТовары.Сумма
		|			ИНАЧЕ ДокТовары.Сумма + ДокТовары.СуммаНДС
		|		КОНЕЦ) КАК СуммаСНДС,
		|	СУММА(ДокТовары.Количество) КАК Количество
		|ПОМЕСТИТЬ ВТ_ПоДокументам
		|ИЗ
		|	Документ.ПриобретениеТоваровУслуг.Товары КАК ДокТовары
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ПриобретениеТоваровУслуг КАК Док
		|		ПО ДокТовары.Ссылка = Док.Ссылка
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ДокУстановкаЦен КАК ВТ_ДокУстановкаЦен
		|		ПО (Док.Ссылка = ВТ_ДокУстановкаЦен.Основание)
		|			И ДокТовары.Номенклатура = ВТ_ДокУстановкаЦен.Номенклатура
		|ГДЕ
		|	Док.Проведен
		|	И ДокТовары.Количество > 0
		|	И ДокТовары.Сумма > 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:\Выгрузка закупочных цен";
//	Каталог = "C:\Тестовая Папка";	
	КолонкиФайла = "Марка,Артикул,АртикулМаркетплейса,Код,OEMНомер,Номенклатура,Цена,СредняяЦена"; 
	Ключи = "Имя, Заголовок, Ширина, Формат, Переносить";
	
	Колонки = Новый Массив;
	Колонки.Добавить(Новый Структура(Ключи, "Марка",               "Марка",                	14, "", Ложь));
	Колонки.Добавить(Новый Структура(Ключи, "Артикул",             "Артикул",              	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);

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

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

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


// 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");
	Исключение
		ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка, , ,
			"Компонента подключилась, но объект не создался: "
				+ ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
		Возврат Неопределено;
	КонецПопытки;

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

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

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

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