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


/ Остатки складов поставщиков (FBS со склада Берга).
//
// Остатки Берга не проходят через документы и регистры УТ: регламентное задание
// кор_ЗагрузкаОстатковБерга спрашивает их по API и перезаписывает регистр
// кор_ОстаткиПоставщиков, а БИТ.УМ при выгрузке остатков на маркетплейсы берёт их оттуда
// (вставка BSLR в топ_ОбщийМодульСервер.ОстаткиFBS_ПолучитьТекстЗапроса_ВТ_Остатки
// вызывает ДополнитьЗапросОстатковFBS). Какие склады УТ считаются складами поставщика и
// каким складам Берга они соответствуют, задаёт регистр кор_СкладыПоставщиков.
//
// ВОССТАНОВЛЕНО из текста с вырезанными символами. Операторы сравнения, которые
// пришлось угадывать по смыслу, помечены комментарием "// [восст.]".

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

// Обработчик регламентного задания кор_ЗагрузкаОстатковБерга.
//
Процедура ЗагрузитьОстаткиБерга() Экспорт

	ОбщегоНазначения.ПриНачалеВыполненияРегламентногоЗадания(
		Метаданные.РегламентныеЗадания.кор_ЗагрузкаОстатковБерга);

	ЗагрузитьОстаткиБергаВРегистр();

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

// Спрашивает у Берга остатки по номенклатуре, выставленной на маркетплейсы со складов
// поставщика, и перезаписывает по этим складам регистр кор_ОстаткиПоставщиков.
// Можно вызывать и вне регламентного задания - например, для отладки.
//
// Возвращаемое значение:
//  Структура:
//    * Позиций       - Число - сколько позиций спрошено у Берга.
//    * Найдено       - Число - позиций, по которым Берг вернул ровно один товар.
//    * Неоднозначно  - Число - позиций, по которым вернулось несколько товаров; остаток по ним не пишется.
//    * ПачекСОшибкой - Число
//    * Текст         - Строка - итог для журнала регистрации.
//
Функция ЗагрузитьОстаткиБергаВРегистр() Экспорт

	Итоги = Новый Структура("Позиций, Найдено, Неоднозначно, ПачекСОшибкой, Текст", 0, 0, 0, 0, "");

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

	УстановитьПривилегированныйРежим(Истина);

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

	Если НашиСклады.Количество() = 0 Тогда
		Итоги.Текст = НСтр("ru = 'Загрузка остатков Берга пропущена: не заполнен регистр «Склады поставщиков».'");
		ЗаписатьВЖурнал(УровеньЖурналаРегистрации.Предупреждение, Итоги.Текст);
		Возврат Итоги;
	КонецЕсли;

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

	ПозицииAPI = Новый Массив;
	Для Индекс = 0 По Позиции.Количество() - 1 Цикл
		Позиция = Позиции[Индекс];
		ПозицииAPI.Добавить(Новый Структура("Индекс, ResourceID, Артикул, BrandID, Бренд",
			Индекс + 1, Позиция.ИдентификаторТовара, Позиция.Артикул, 0, Позиция.Бренд));
	КонецЦикла;

	// Момент берётся до запроса: заказы, созданные, пока идёт опрос, Берг ещё не видит,
	// и запрос выгрузки на МП вычтет их из остатка (см. ТекстЗапросаОстатковПоставщиков).
	МоментЗагрузки = ТекущаяДатаСеанса();
	Ответ = кор_HTTPКоннектор.ПолучитьОстатки(ПараметрыПодключения, ПозицииAPI);

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

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

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

	// По позициям из пачек с ошибкой ответа нет: оставляем прежние записи с прежней датой,
	// чтобы товар не обнулился на маркетплейсе из-за одного сбойного запроса.
	Если Ответ.ИндексыСОшибкой.Количество() > 0 Тогда // [восст.]
		ДобавитьПрежниеЗаписи(Записи, Позиции, Ответ.ИндексыСОшибкой, НашиСклады);
	КонецЕсли;

	ЗаписатьОстатки(Записи, НашиСклады);

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

	Возврат Итоги;

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

// Подменяет в запросе остатков FBS БИТ.УМ остатки складов поставщиков: для них вместо
// РаспределениеЗапасов берётся регистр кор_ОстаткиПоставщиков за вычетом наших заказов,
// о которых поставщик ещё не знает. Вызывается из вставки BSLR в
// топ_ОбщийМодульСервер.ОстаткиFBS_ПолучитьТекстЗапроса_ВТ_Остатки.
//
// Параметры:
//  ТекстыЗапроса - Массив из Строка - пакет запроса БИТ.УМ; последний элемент - запрос,
//                  помещающий остатки во временную таблицу ВТ_ОстаткиПредварительная
//                  (Номенклатура, Характеристика, Склад, Остаток). Массив меняется на месте.
//
Процедура ДополнитьЗапросОстатковFBS(ТекстыЗапроса) Экспорт

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

	Запрос = Новый Запрос(
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК ЕстьСклады
	|ИЗ
	|	РегистрСведений.кор_СкладыПоставщиков КАК СкладыПоставщиков");
	Если Запрос.Выполнить().Пустой() Тогда
		Возврат;
	КонецЕсли;

	ИмяТиповойТаблицы = "ВТ_ОстаткиПредварительнаяБезПоставщиков";
	Индекс = ТекстыЗапроса.ВГраница();
	ТекстыЗапроса[Индекс] = СтрЗаменить(ТекстыЗапроса[Индекс],
		"ПОМЕСТИТЬ ВТ_ОстаткиПредварительная", "ПОМЕСТИТЬ " + ИмяТиповойТаблицы);
	ТекстыЗапроса.Добавить(ТекстЗапросаОстатковПоставщиков(ИмяТиповойТаблицы));
	ТекстыЗапроса.Добавить("УНИЧТОЖИТЬ " + ИмяТиповойТаблицы);

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

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

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

#Область ЗапросВыгрузкиНаМП

Функция ТекстЗапросаОстатковПоставщиков(ИмяТиповойТаблицы)

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

	// Параметры в этот пакет не передать: запрос собирает и выполняет БИТ.УМ.
	Текст = СтрЗаменить(Текст, "&ИмяТиповойТаблицы", ИмяТиповойТаблицы);
	Текст = СтрЗаменить(Текст, "&ТекущаяДата", ЛитералДаты(ТекущаяДатаСеанса()));

	Возврат Текст;

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

Функция ЛитералДаты(Дата)

	Возврат СтрШаблон("ДАТАВРЕМЯ(%1, %2, %3, %4, %5, %6)",
		XMLСтрока(Год(Дата)), XMLСтрока(Месяц(Дата)), XMLСтрока(День(Дата)),
		XMLСтрока(Час(Дата)), XMLСтрока(Минута(Дата)), XMLСтрока(Секунда(Дата)));

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

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

#Область ЗагрузкаОстатков

// Возвращаемое значение:
//  Соответствие - ключ: идентификатор склада Берга (Строка), значение: СправочникСсылка.Склады.
//
Функция СкладыПоставщика()

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

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

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

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

// Номенклатура, сопоставленная с товарами маркетплейсов в тех личных кабинетах,
// где склад поставщика указан среди складов FBS. Только её и имеет смысл спрашивать
// у Берга: целого прайса API не отдаёт, а каждая позиция - это часть запроса.
//
// Возвращаемое значение:
//  ТаблицаЗначений - Номенклатура, Характеристика, Артикул, Бренд, ИдентификаторТовара.
//
Функция ПозицииДляЗапроса(НашиСклады)

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

	Результат = Новый ТаблицаЗначений;
	Результат.Колонки.Добавить("Номенклатура");
	Результат.Колонки.Добавить("Характеристика");
	Результат.Колонки.Добавить("Артикул", Новый ОписаниеТипов("Строка"));
	Результат.Колонки.Добавить("Бренд", Новый ОписаниеТипов("Строка"));
	Результат.Колонки.Добавить("ИдентификаторТовара", Новый ОписаниеТипов("Число"));

	Выборка = Запрос.Выполнить().Выбрать();
	Пока Выборка.Следующий() Цикл
		Если ПустаяСтрока(Выборка.Артикул) И Выборка.ИдентификаторТовара = 0 Тогда
			Продолжить;
		КонецЕсли;
		Строка = Результат.Добавить();
		ЗаполнитьЗначенияСвойств(Строка, Выборка, "Номенклатура, Характеристика, ИдентификаторТовара");
		Строка.Артикул = СокрЛП(Выборка.Артикул);
		// Как во внешней обработке: бренд Берга - наш производитель, если его нет - марка.
		Строка.Бренд = ?(ЗначениеЗаполнено(Выборка.Производитель), Выборка.Производитель, Выборка.Марка);
	КонецЦикла;

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

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

Функция НоваяТаблицаЗаписей()

	Таблица = Новый ТаблицаЗначений;
	Таблица.Колонки.Добавить("Склад");
	Таблица.Колонки.Добавить("Номенклатура");
	Таблица.Колонки.Добавить("Характеристика");
	Таблица.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
	Таблица.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число"));
	Таблица.Колонки.Добавить("ДоступноБольше", Новый ОписаниеТипов("Булево"));
	Таблица.Колонки.Добавить("ИдентификаторТовараПоставщика", Новый ОписаниеТипов("Число"));
	Таблица.Колонки.Добавить("ИдентификаторыСкладовПоставщика", Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("ДатаОбновления", Новый ОписаниеТипов("Дата"));

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

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

// Заводит по строке записи на каждый наш склад для каждой позиции, по которой Берг вернул
// ровно один товар, - с нулевым остатком. Нулевая запись нужна: она хранит id товара Берга
// для следующих запросов и явно обнуляет товар на маркетплейсе.
// Позиции, по которым товаров несколько, пропускаются: чей остаток брать, непонятно.
//
// Возвращаемое значение:
//  Соответствие - ключ: Индекс позиции, значение: Соответствие (Склад - СтрокаТаблицыЗначений).
//
Функция СтрокиЗаписейПоПозициям(Предложения, Позиции, НашиСклады, МоментЗагрузки, Записи, Итоги)

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

	СтрокиЗаписей = Новый Соответствие;
	Для Каждого КлючИЗначение Из ТоварыПозиций Цикл

		Позиция = Позиции[КлючИЗначение.Ключ - 1];
		Товары = КлючИЗначение.Значение;

		Если Товары.Количество() > 1 Тогда // [восст.]
			Итоги.Неоднозначно = Итоги.Неоднозначно + 1;
			ЗаписатьВЖурнал(УровеньЖурналаРегистрации.Предупреждение, СтрШаблон(
				НСтр("ru = 'Берг вернул несколько товаров по позиции %1 (артикул %2, бренд %3): %4. Остаток не загружен - уточните бренд.'"),
				Позиция.Номенклатура, Позиция.Артикул, Позиция.Бренд, СтрСоединить(Товары, ", ")));
			Продолжить;
		КонецЕсли;

		Итоги.Найдено = Итоги.Найдено + 1;
		СтрокиПоСкладам = Новый Соответствие;
		Для Каждого Склад Из НашиСклады Цикл
			Запись = Записи.Добавить();
			Запись.Склад = Склад;
			Запись.Номенклатура = Позиция.Номенклатура;
			Запись.Характеристика = Позиция.Характеристика;
			Запись.ИдентификаторТовараПоставщика = Товары[0];
			Запись.ДатаОбновления = МоментЗагрузки;
			СтрокиПоСкладам.Вставить(Склад, Запись);
		КонецЦикла;
		СтрокиЗаписей.Вставить(КлючИЗначение.Ключ, СтрокиПоСкладам);

	КонецЦикла;

	Возврат СтрокиЗаписей;

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

// Складывает предложения складов Берга в записи наших складов по регистру кор_СкладыПоставщиков.
// Если одному нашему складу соответствует несколько складов Берга, количество суммируется,
// а цена берётся минимальная.
//
Процедура ЗаполнитьЗаписиПоПредложениям(СтрокиЗаписей, Предложения, СкладыПоставщика)

	Для Каждого Предложение Из Предложения Цикл

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

		ИдентификаторСклада = XMLСтрока(Предложение.СкладID);
		Склад = СкладыПоставщика.Получить(ИдентификаторСклада);
		Если Склад = Неопределено Тогда
			Продолжить;
		КонецЕсли;

		Запись = СтрокиПоСкладам.Получить(Склад);
		Запись.Количество = Запись.Количество + Предложение.Количество;
		Если Предложение.Цена > 0 И (Запись.Цена = 0 Или Предложение.Цена < Запись.Цена) Тогда // [восст.]
			Запись.Цена = Предложение.Цена;
		КонецЕсли;
		Запись.ДоступноБольше = Запись.ДоступноБольше Или Предложение.БольшеДоступно;

		Если СтрНайти("," + Запись.ИдентификаторыСкладовПоставщика + ",", "," + ИдентификаторСклада + ",") = 0 Тогда
			Запись.ИдентификаторыСкладовПоставщика = ?(ПустаяСтрока(Запись.ИдентификаторыСкладовПоставщика),
				ИдентификаторСклада, Запись.ИдентификаторыСкладовПоставщика + "," + ИдентификаторСклада);
		КонецЕсли;

	КонецЦикла;

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

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

	ПозицииСОшибкой = Новый ТаблицаЗначений;
	ПозицииСОшибкой.Колонки.Добавить("Номенклатура", Метаданные.РегистрыСведений.кор_ОстаткиПоставщиков.Измерения.Номенклатура.Тип);
	ПозицииСОшибкой.Колонки.Добавить("Характеристика", Метаданные.РегистрыСведений.кор_ОстаткиПоставщиков.Измерения.Характеристика.Тип);
	Для Каждого Индекс Из ИндексыСОшибкой Цикл
		ЗаполнитьЗначенияСвойств(ПозицииСОшибкой.Добавить(), Позиции[Индекс - 1]);
	КонецЦикла;

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

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

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

// Перезаписывает регистр по каждому нашему складу целиком: позиции, которых больше нет
// в ответе Берга (или на маркетплейсах), уходят из регистра, и остаток по ним становится 0.
//
Процедура ЗаписатьОстатки(Записи, НашиСклады)

	Для Каждого Склад Из НашиСклады Цикл

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

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

	КонецЦикла;

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

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

	ЗаписьЖурналаРегистрации(кор_HTTPКоннектор.ИмяСобытияЖурнала(), Уровень,
		Метаданные.РегистрыСведений.кор_ОстаткиПоставщиков,, Текст);

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

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

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