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


/////////////////////////////////////////////////////////////////////////////
// Расширение общего модуля менеджера обмена (БП 3.0.191, формат 1.22)
//
// Назначение: несколько документов "Реализация товаров и услуг" с видом
// операции "Передача на комиссию", пришедших из УТ за один сеанс обмена,
// загружаются в БП как ОДИН документ.
//
// Механика: первый документ группы грузится штатно. У второго и последующих
// в ПОД подменяются ключевые свойства (Ссылка/Дата/Номер) на значения первого,
// поэтому штатная идентификация находит уже записанный документ и обновляет
// его. Табличная часть при этом не замещается, а дополняется.
//
// Перехват идёт через экспортный диспетчер ВыполнитьПроцедуруМодуляМенеджера,
// сами обработчики ПОД/ПКО в модуле не экспортные.
////////////////////////////////////////////////////////////////////////////////

#Область НастройкиОтбора

// ИНН комиссионера, передачи к которому нужно сворачивать.
// Если комиссионеров несколько — заменить на список.
Функция КМ_ИННКомиссионера()
	Возврат "9714053621"; // ООО "РВБ" (Wildberries)
КонецФункции

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

#Область Перехваты

&Перед("ВыполнитьПроцедуруМодуляМенеджера")
Процедура КМ_Перед_ВыполнитьПроцедуруМодуляМенеджера(ИмяПроцедуры, Параметры)

	Если ИмяПроцедуры <> "ПКО_Документ_РеализацияТоваровУслуг_ПередЗаписьюПолученныхДанных" Тогда
		Возврат;
	КонецЕсли;

	// ДанныеИБ = Неопределено означает, что документа в базе ещё нет:
	// это первый документ группы, накапливать нечего.
	Если Параметры.ДанныеИБ = Неопределено Тогда
		Возврат;
	КонецЕсли;

	// Запоминаем строки, которые сейчас лежат в базе, — типовой обработчик
	// ниже по стеку их затрёт строками нового документа.
	Хранилище = КМ_Хранилище(Параметры.КомпонентыОбмена);
	Хранилище.Вставить("НакопленныеТовары", Параметры.ДанныеИБ.Товары.Выгрузить());

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

&После("ВыполнитьПроцедуруМодуляМенеджера")
Процедура КМ_После_ВыполнитьПроцедуруМодуляМенеджера(ИмяПроцедуры, Параметры)

	Если ИмяПроцедуры = "ПОД_Документ_РеализацияТоваровУслуг_ПриОбработке" Тогда

		КМ_ПодменитьКлючевыеСвойства(Параметры.ОбъектОбработки, Параметры.КомпонентыОбмена);

	ИначеЕсли ИмяПроцедуры = "ПКО_Документ_РеализацияТоваровУслуг_ПередЗаписьюПолученныхДанных" Тогда

		КМ_ОбъединитьТовары(Параметры.ДанныеИБ, Параметры.КомпонентыОбмена);

	КонецЕсли;

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

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

#Область ПодменаКлючевыхСвойств

Процедура КМ_ПодменитьКлючевыеСвойства(ДанныеXDTO, КомпонентыОбмена)

	// Загрузка объекта как значения чужого свойства — трогать нельзя
	// (аналог типовой ЭтоЗагрузкаКлючевыхСвойств).
	Если ДанныеXDTO.Количество() = 1 Тогда
		Возврат;
	КонецЕсли;

	Если Не КМ_ЭтоСворачиваемаяПередача(ДанныеXDTO) Тогда
		Возврат;
	КонецЕсли;

	КлючГруппы = КМ_КлючГруппы(ДанныеXDTO);
	Если Не ЗначениеЗаполнено(КлючГруппы) Тогда
		Возврат;
	КонецЕсли;

	Хранилище = КМ_Хранилище(КомпонентыОбмена);
	Лидер = Хранилище.Лидеры.Получить(КлючГруппы);

	Если Лидер = Неопределено Тогда
		// Первый документ группы. Грузится штатно, запоминаем его ключевые свойства.
		Лидер = Новый Структура("Ссылка, Дата, Номер",
			ДанныеXDTO.КлючевыеСвойства.Ссылка,
			ДанныеXDTO.КлючевыеСвойства.Дата,
			ДанныеXDTO.КлючевыеСвойства.Номер);

		// Корневая Ссылка — именно её берёт СтруктураОбъектаXDTOВДанныеИБ
		// как ОригинальныйУИДСтрокой. Это ключевая подмена.
		КорневаяСсылка = Неопределено;
		Если ДанныеXDTO.Свойство("Ссылка", КорневаяСсылка) Тогда
			Лидер.Вставить("КорневаяСсылка", КорневаяСсылка);
		КонецЕсли;

		Хранилище.Лидеры.Вставить(КлючГруппы, Лидер);
		Возврат;
	КонецЕсли;

	// Второй и последующие: выдаём себя за лидера.
	//
	// ГЛАВНОЕ — подменить УИД. Идентификация по полям поиска (Дата, Номер)
	// здесь НЕ поможет: СсылкаОбъектаПоСвойствамОбъектаXDTO добавляет в запрос
	// условие "ПубличныеИдентификаторы.Ссылка is null", то есть намеренно
	// исключает уже сопоставленные объекты. Документ-лидер получает публичный
	// идентификатор сразу при записи и становится невидим для этого поиска.
	//
	// С подменённым УИД срабатывает первая ветка идентификации —
	// по публичному идентификатору, — и находит документ лидера.
	Если Лидер.Свойство("КорневаяСсылка") И ДанныеXDTO.Свойство("Ссылка") Тогда
		ДанныеXDTO.Ссылка = Лидер.КорневаяСсылка;
	КонецЕсли;

	// Ключевые свойства подменяем для консистентности шапки
	// (номер и дата документа берутся отсюда через ПКС).
	ДанныеXDTO.КлючевыеСвойства.Ссылка = Лидер.Ссылка;
	ДанныеXDTO.КлючевыеСвойства.Дата   = Лидер.Дата;
	ДанныеXDTO.КлючевыеСвойства.Номер  = Лидер.Номер;

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

Функция КМ_ЭтоСворачиваемаяПередача(ДанныеXDTO)

	Если Не ДанныеXDTO.Свойство("ВидОперации")
		Или ДанныеXDTO.ВидОперации = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;

	Если ДанныеXDTO.ВидОперации.Значение <> "ПередачаНаКомиссию" Тогда
		Возврат Ложь;
	КонецЕсли;

	Если Не ДанныеXDTO.Свойство("Контрагент")
		Или ДанныеXDTO.Контрагент = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;

	ИНН = "";
	ДанныеXDTO.Контрагент.Свойство("ИНН", ИНН);

	Возврат СокрЛП(ИНН) = КМ_ИННКомиссионера();

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

// Ключ группировки: день + договор. Организация и контрагент уже
// зафиксированы отбором выше.
Функция КМ_КлючГруппы(ДанныеXDTO)

	Если Не ДанныеXDTO.КлючевыеСвойства.Свойство("Дата") Тогда
		Возврат "";
	КонецЕсли;

	ИдентификаторДоговора = "";
	Если ДанныеXDTO.Свойство("ДанныеВзаиморасчетов")
		И ДанныеXDTO.ДанныеВзаиморасчетов <> Неопределено
		И ДанныеXDTO.ДанныеВзаиморасчетов.Свойство("Договор")
		И ДанныеXDTO.ДанныеВзаиморасчетов.Договор <> Неопределено Тогда

		СсылкаДоговора = Неопределено;
		ДанныеXDTO.ДанныеВзаиморасчетов.Договор.Свойство("Ссылка", СсылкаДоговора);
		Если СсылкаДоговора <> Неопределено Тогда
			ИдентификаторДоговора = Строка(СсылкаДоговора);
		КонецЕсли;
	КонецЕсли;

	Возврат Формат(НачалоДня(ДанныеXDTO.КлючевыеСвойства.Дата), "ДФ=yyyyMMdd")
		+ "|" + ИдентификаторДоговора;

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

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

#Область ОбъединениеТабличнойЧасти

Процедура КМ_ОбъединитьТовары(ДанныеИБ, КомпонентыОбмена)

	Если ДанныеИБ = Неопределено Тогда
		Возврат;
	КонецЕсли;

	Хранилище = КМ_Хранилище(КомпонентыОбмена);
	Накопленные = Неопределено;
	Хранилище.Свойство("НакопленныеТовары", Накопленные);

	Если Накопленные = Неопределено Или Накопленные.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;

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

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

	Хранилище.Вставить("НакопленныеТовары", Неопределено);

	// Сумма шапки была перенесена из полученных данных и относится
	// только к последнему документу — пересчитываем.
	ДанныеИБ.СуммаДокумента = ДанныеИБ.Товары.Итог("Сумма");

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

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

#Область Служебные

// Хранилище состояния на время одного сеанса обмена.
// ПараметрыКонвертации живут ровно столько, сколько нужно.
Функция КМ_Хранилище(КомпонентыОбмена)

	ПараметрыКонвертации = КомпонентыОбмена.ПараметрыКонвертации;

	Хранилище = Неопределено;
	Если Не ПараметрыКонвертации.Свойство("КМ_СводныеПередачи", Хранилище)
		Или Хранилище = Неопределено Тогда

		Хранилище = Новый Структура;
		Хранилище.Вставить("Лидеры", Новый Соответствие);
		Хранилище.Вставить("НакопленныеТовары", Неопределено);
		ПараметрыКонвертации.Вставить("КМ_СводныеПередачи", Хранилище);
	КонецЕсли;

	Возврат Хранилище;

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

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

////////////////////////////////////////////////////////////////////////////////
// ПОЧЕМУ ПОДМЕНЫ НОМЕРА НЕДОСТАТОЧНО
//
// ВариантИдентификации = "СначалаПоУникальномуИдентификаторуПотомПоПолямПоиска",
// ПоляПоиска = "Дата,Номер". Кажется, что достаточно подменить номер и дату —
// но поиск по полям в СсылкаОбъектаПоСвойствамОбъектаXDTO строит запрос с
// условием "ПубличныеИдентификаторы.Ссылка is null" и отбрасывает объекты,
// уже сопоставленные с этим узлом. Документ-лидер получает публичный
// идентификатор при записи, поэтому по номеру он больше не находится,
// и создаётся дубль с тем же номером.
//
// Работает только подмена корневой ДанныеXDTO.Ссылка (УИД).
//
// ПЕРЕД ЗАПУСКОМ
//
// Точка останова в КМ_ПодменитьКлючевыеСвойства: убедиться, что корневое
// свойство "Ссылка" существует и что присваивание целиком (а не через
// .Значение) проходит. Если в структуре его нет, а УИД лежит только в
// КлючевыеСвойства — сообщить, логику подмены надо будет строить иначе.
////////////////////////////////////////////////////////////////////////////////