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


//++Варнин А.А. Задача №72300 - редакция 2, наборы по вариантам комплектации
//
// ВНИМАНИЕ, проверить в конфигураторе три имени метаданных — я их взял по типовой УТ 11.5:
//   1. Перечисление.ТипыНоменклатуры.Набор            - значение типа номенклатуры "Набор"
//   2. Справочник.ВариантыКомплектацииНоменклатуры    - справочник вариантов комплектации
//   3. ...ВариантыКомплектацииНоменклатуры.Содержание - имя табличной части с составом
// Если у вас ТЧ называется иначе (встречается "Комплектующие") — поправить в ТекстЗапроса.
//
// Изменения относительно редакции 1:
//  - Убран фильтр по "не использовать" (позиции переводятся в тип "Набор").
//  - Якорь расчёта расширен: остатки + компоненты наборов + сами наборы.
//    Раньше всё висело на ТоварыНаСкладах > 0, и наборы (у которых остатка нет)
//    не попали бы в выгрузку в принципе, а компоненты с нулём — выпали бы.
//  - Наборы исключены из расчёта по поступлениям и считаются вторым проходом
//    как сумма уже посчитанных цен компонентов с учётом количества.
//  - Порядок арифметики как в постановке: компонент округляется до копеек,
//    затем умножается на количество, затем всё складывается.
//  - Поддержаны наборы из наборов (итеративные проходы) и защита от зацикливания.

#Область РегламентноеЗадание_кор_ВыгрузкаЗакупочныхЦен

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	// Два последних запроса пакета: [-2] — цены обычной номенклатуры, [-1] — состав наборов.
	// Если будете добавлять запросы в пакет — добавляйте ВЫШЕ этих двух.
	ЦеныТЗ  = Результаты[Результаты.Количество() - 2].Выгрузить();
	СоставТЗ = Результаты[Результаты.Количество() - 1].Выгрузить();

	// Единственное округление обычных позиций. Наборы округляются в ДополнитьЦенамиНаборов.
	Для Каждого СтрЦена Из ЦеныТЗ Цикл
		СтрЦена.СредняяЦена = Окр(СтрЦена.СредняяЦена, 2, РежимОкругления.Окр15как20);
		СтрЦена.Цена        = Окр(СтрЦена.Цена, 2, РежимОкругления.Окр15как20);
	КонецЦикла;

	ДополнитьЦенамиНаборов(ЦеныТЗ, СоставТЗ);

	ЦеныТЗ.Сортировать("Код");

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

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

// Досчитывает цены наборов как сумму цен компонентов с учётом количества.
//
// Порядок арифметики соответствует постановке:
//   цена компонента (уже округлённая) * количество, затем сумма, затем округление итога.
//
// Наборы, вложенные в наборы, разбираются последовательными проходами:
// на каждом проходе считаются те наборы, все компоненты которых уже известны.
//
Процедура ДополнитьЦенамиНаборов(ЦеныТЗ, СоставТЗ)

	ИмяСобытия = "Выгрузка закупочных цен. Наборы";

	Если СоставТЗ.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;

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

	// У набора может быть несколько вариантов комплектации.
	// СоставТЗ упорядочен по набору и представлению варианта, поэтому берём первый
	// и предупреждаем, если вариантов больше одного.
	ВыбранныйВариант = Новый Соответствие;
	НаборыСНесколькимиВариантами = Новый Массив;

	Для Каждого СтрСостав Из СоставТЗ Цикл
		Вариант = ВыбранныйВариант[СтрСостав.Набор];
		Если Вариант = Неопределено Тогда
			ВыбранныйВариант.Вставить(СтрСостав.Набор, СтрСостав.Вариант);
		ИначеЕсли Вариант <> СтрСостав.Вариант
			И НаборыСНесколькимиВариантами.Найти(СтрСостав.Набор) = Неопределено Тогда
			НаборыСНесколькимиВариантами.Добавить(СтрСостав.Набор);
		КонецЕсли;
	КонецЦикла;

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

	// Реквизиты наборов и их состав в разрезе выбранного варианта.
	РеквизитыНаборов = Новый Соответствие;
	СоставНабора = Новый Соответствие;

	Для Каждого СтрСостав Из СоставТЗ Цикл

		Если СтрСостав.Вариант <> ВыбранныйВариант[СтрСостав.Набор] Тогда
			Продолжить;
		КонецЕсли;

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

		СоставНабора[СтрСостав.Набор].Добавить(
			Новый Структура("Компонент, Количество", СтрСостав.Компонент, СтрСостав.Количество));

	КонецЦикла;

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

	НомерПрохода = 0;
	ПоследняяПричина = Новый Соответствие;

	Пока Осталось.Количество() > 0 Цикл

		НомерПрохода = НомерПрохода + 1;
		ПосчитаноЗаПроход = 0;
		НеПосчитанные = Новый Массив;

		Для Каждого Набор Из Осталось Цикл

			СредняяИтог = 0;
			ЦенаИтог = 0;
			НедостающийКомпонент = Неопределено;

			Для Каждого Строка Из СоставНабора[Набор] Цикл

				ЦеныКомпонента = ИзвестныеЦены[Строка.Компонент];
				Если ЦеныКомпонента = Неопределено Тогда
					НедостающийКомпонент = Строка.Компонент;
					Прервать;
				КонецЕсли;

				СредняяИтог = СредняяИтог + ЦеныКомпонента.СредняяЦена * Строка.Количество;
				ЦенаИтог    = ЦенаИтог    + ЦеныКомпонента.Цена        * Строка.Количество;

			КонецЦикла;

			Если НедостающийКомпонент <> Неопределено Тогда
				НеПосчитанные.Добавить(Набор);
				ПоследняяПричина.Вставить(Набор, НедостающийКомпонент);
				Продолжить;
			КонецЕсли;

			Если СредняяИтог <= 0 ИЛИ ЦенаИтог <= 0 Тогда
				ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Предупреждение, , ,
					"Набор " + Строка(Набор) + ": итоговая цена нулевая, в выгрузку не попал.");
				Продолжить;
			КонецЕсли;

			СредняяИтог = Окр(СредняяИтог, 2, РежимОкругления.Окр15как20);
			ЦенаИтог    = Окр(ЦенаИтог, 2, РежимОкругления.Окр15как20);

			НоваяСтрока = ЦеныТЗ.Добавить();
			ЗаполнитьЗначенияСвойств(НоваяСтрока, РеквизитыНаборов[Набор]);
			НоваяСтрока.Номенклатура = Набор;
			НоваяСтрока.СредняяЦена  = СредняяИтог;
			НоваяСтрока.Цена         = ЦенаИтог;
			НоваяСтрока.КоличествоДокументов = 0; // у набора своих поступлений нет

			ИзвестныеЦены.Вставить(Набор, Новый Структура("СредняяЦена, Цена", СредняяИтог, ЦенаИтог));
			ПосчитаноЗаПроход = ПосчитаноЗаПроход + 1;

		КонецЦикла;

		Осталось = НеПосчитанные;

		// Ни один набор не сдвинулся — дальше только вложенные циклы либо
		// действительно недостающие компоненты. Выходим.
		Если ПосчитаноЗаПроход = 0 Тогда
			Прервать;
		КонецЕсли;

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

	КонецЦикла;

	Если Осталось.Количество() > 0 Тогда
		СтрокиЛога = Новый Массив;
		Для Каждого Набор Из Осталось Цикл
			СтрокиЛога.Добавить(Строка(Набор) + " — нет цены по компоненту: "
				+ Строка(ПоследняяПричина[Набор]));
			Если СтрокиЛога.Количество() >= 50 Тогда
				Прервать;
			КонецЕсли;
		КонецЦикла;
		ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка, , ,
			СтрШаблон("Не посчитано наборов: %1. Они не попали в выгрузку.%2%3",
				Осталось.Количество(), Символы.ПС, СтрСоединить(СтрокиЛога, Символы.ПС)));
	КонецЕсли;

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

Функция ПредставлениеСписка(Массив, МаксимумСтрок)

	Строки = Новый Массив;
	Для Каждого Элемент Из Массив Цикл
		Строки.Добавить(Строка(Элемент));
		Если Строки.Количество() >= МаксимумСтрок Тогда
			Строки.Добавить("... и ещё " + (Массив.Количество() - МаксимумСтрок));
			Прервать;
		КонецЕсли;
	КонецЦикла;

	Возврат СтрСоединить(Строки, Символы.ПС);

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

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

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

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

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

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

Функция ТекстЗапросаБезНДС()

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

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

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

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

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

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

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

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

	КонецЦикла;

	Возврат кор_HTTPКоннектор.ОтправитьВКафку(Сообщения);

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

Процедура ВыгрузитьВExcel(ЦеныТЗ)

	Если НЕ Константы.кор_ВыгружатьЦеныВФайл.Получить() Тогда
		Возврат;
	КонецЕсли;

	Каталог = СокрЛП(Константы.кор_КаталогВыгрузкиЦен.Получить());
	Если НЕ ЗначениеЗаполнено(Каталог) Тогда
		ЗаписьЖурналаРегистрации("Выгрузка закупочных цен",
			УровеньЖурналаРегистрации.Предупреждение, , ,
			"Не заполнена константа кор_КаталогВыгрузкиЦен, файл не сохранён.");
		Возврат;
	КонецЕсли;

	КолонкиФайла = "Код,Артикул,Марка,OEMНомер,АртикулМаркетплейса,СредняяЦена,Цена";

	Попытка

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

		ТабДок = Новый ТабличныйДокумент;
		Построитель = Новый ПостроительОтчета;
		Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТЗДляФайла);
		Построитель.Выполнить();
		Построитель.Вывести(ТабДок);

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

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

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

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

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