РК_ЗагрузкаОстатков 1.3.1.49: подбор складов, загрузка и сопоставление остатков.

HTTP-клиент Transfer API, детальная ТЧ входящих остатков и шаг сопоставления НСИ/серий по логике BSL-переноса.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
mihailkudravcev
2026-08-11 09:46:54 +03:00
parent b0d285a87d
commit d39b1c0abb
12 changed files with 1981 additions and 8 deletions
@@ -0,0 +1,439 @@
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
#Область ПрограммныйИнтерфейс
// Заполняет ТЧ ВходящиеДанные строками остатков Transfer API (mode=combined по умолчанию).
//
Процедура ЗаполнитьВходящиеДанныеИзОтветаОстатков(ОтветAPI, ОчищатьПередЗаполнением = Истина) Экспорт
Если ОчищатьПередЗаполнением Тогда
ВходящиеДанные.Очистить();
КонецЕсли;
СтрокиОтвета = Неопределено;
Если ТипЗнч(ОтветAPI) = Тип("Структура") Или ТипЗнч(ОтветAPI) = Тип("Соответствие") Тогда
СтрокиОтвета = ЗначениеСвойства(ОтветAPI, "rows", Неопределено);
КонецЕсли;
Если ТипЗнч(СтрокиОтвета) <> Тип("Массив") Тогда
Возврат;
КонецЕсли;
Для Каждого СтрокаAPI Из СтрокиОтвета Цикл
НовСтр = ВходящиеДанные.Добавить();
ЗаполнитьСтрокуВходящихДанныхИзAPI(НовСтр, СтрокаAPI);
КонецЦикла;
КонецПроцедуры
// Сопоставление строк ТЧ с НСИ целевой ИБ + создание серий-заглушек при необходимости.
// Алгоритм — по логике scripts/generate_balances_apply_bsl.py (артикул/GUID/код/имя; серии Перенос_*).
//
// Параметры:
// РежимТест - Булево - Истина: только поиск, без создания серий/характеристик
//
// Возвращаемое значение:
// Структура - Сопоставлено, НеСопоставлено, СозданоСерий, СозданоХарактеристик
//
Функция СопоставитьВходящиеДанные(РежимТест = Истина) Экспорт
Результат = Новый Структура;
Результат.Вставить("Сопоставлено", 0);
Результат.Вставить("НеСопоставлено", 0);
Результат.Вставить("СозданоСерий", 0);
Результат.Вставить("СозданоХарактеристик", 0);
СуффиксДатыСер = Формат(?(ЗначениеЗаполнено(ДатаДокументовВвода), ДатаДокументовВвода, ДатаОстатков), "ДФ=yyyyMMdd");
Если ПустаяСтрока(СуффиксДатыСер) Тогда
СуффиксДатыСер = Формат(ТекущаяДатаСеанса(), "ДФ=yyyyMMdd");
КонецЕсли;
КэшТребуетсяСерия = Новый Соответствие;
КэшСерий = Новый Соответствие;
Для Каждого СтрТ Из ВходящиеДанные Цикл
СтрТ.Сопоставлено = Ложь;
СтрТ.КомментарийСопоставления = "";
СтрТ.Номенклатура = Справочники.Номенклатура.ПустаяСсылка();
СтрТ.Склад = Справочники.Склады.ПустаяСсылка();
СтрТ.Характеристика = Справочники.ХарактеристикиНоменклатуры.ПустаяСсылка();
СтрТ.СерияСсылка = Справочники.СерииНоменклатуры.ПустаяСсылка();
СтрТ.НомерГТДСсылка = Справочники.НомераГТД.ПустаяСсылка();
Ном = НайтиНоменклатуруСтроки(СтрТ);
Если Не ЗначениеЗаполнено(Ном) Тогда
СтрТ.КомментарийСопоставления = "Номенклатура не найдена";
Результат.НеСопоставлено = Результат.НеСопоставлено + 1;
Продолжить;
КонецЕсли;
СтрТ.Номенклатура = Ном;
СкладЦель = НайтиСкладСтроки(СтрТ);
Если Не ЗначениеЗаполнено(СкладЦель) Тогда
СтрТ.КомментарийСопоставления = "Склад не найден";
Результат.НеСопоставлено = Результат.НеСопоставлено + 1;
Продолжить;
КонецЕсли;
СтрТ.Склад = СкладЦель;
Хар = НайтиИлиСоздатьХарактеристику(СтрТ, Ном, РежимТест, Результат);
СтрТ.Характеристика = Хар;
Сер = НайтиИлиСоздатьСерию(СтрТ, Ном, СкладЦель, СуффиксДатыСер, РежимТест,
КэшТребуетсяСерия, КэшСерий, Результат);
СтрТ.СерияСсылка = Сер;
Если ЗначениеЗаполнено(СтрТ.ГТД) Тогда
КодГТД = СокрЛП(СтрТ.ГТД);
СтрТ.НомерГТДСсылка = Справочники.НомераГТД.НайтиПоКоду(КодГТД);
Если Не ЗначениеЗаполнено(СтрТ.НомерГТДСсылка) Тогда
СтрТ.НомерГТДСсылка = Справочники.НомераГТД.НайтиПоРеквизиту("РегистрационныйНомер", КодГТД);
КонецЕсли;
Если Не ЗначениеЗаполнено(СтрТ.НомерГТДСсылка) Тогда
СтрТ.НомерГТДСсылка = Справочники.НомераГТД.НайтиПоНаименованию(КодГТД, Истина);
КонецЕсли;
Если Не ЗначениеЗаполнено(СтрТ.НомерГТДСсылка) Тогда
СсылкаГТД = СсылкаПоUUID(Справочники.НомераГТД, СтрТ.ГТДИдентификатор);
Если ЗначениеЗаполнено(СсылкаГТД) И ОбщегоНазначения.СсылкаСуществует(СсылкаГТД) Тогда
СтрТ.НомерГТДСсылка = СсылкаГТД;
КонецЕсли;
КонецЕсли;
КонецЕсли;
СтрТ.Сопоставлено = Истина;
Если ПустаяСтрока(СтрТ.КомментарийСопоставления) Тогда
СтрТ.КомментарийСопоставления = "OK";
КонецЕсли;
Результат.Сопоставлено = Результат.Сопоставлено + 1;
КонецЦикла;
Возврат Результат;
КонецФункции
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
Процедура ЗаполнитьСтрокуВходящихДанныхИзAPI(НовСтр, СтрокаAPI)
НовСтр.НоменклатураИдентификатор = UUIDИзСтроки(ЗначениеСвойства(СтрокаAPI, "nomenclature_guid", ""));
НовСтр.НоменклатураНаименование = СокрЛП(Строка(ЗначениеСвойства(СтрокаAPI, "nomenclature_name", "")));
НовСтр.НоменклатураКод = СокрЛП(Строка(ЗначениеСвойства(СтрокаAPI, "nomenclature_code", "")));
НовСтр.Артикул = СокрЛП(Строка(ЗначениеСвойства(СтрокаAPI, "nomenclature_article", "")));
НовСтр.СкладИдентификатор = UUIDИзСтроки(ЗначениеСвойства(СтрокаAPI, "warehouse_guid", ""));
НовСтр.СкладНаименование = СокрЛП(Строка(ЗначениеСвойства(СтрокаAPI, "warehouse_name", "")));
НовСтр.ЕдИзмИдентификатор = UUIDИзСтроки(ЗначениеСвойства(СтрокаAPI, "unit_guid", ""));
НовСтр.ЕдИзм = СокрЛП(Строка(ЗначениеСвойства(СтрокаAPI, "unit_name", "")));
Если ПустаяСтрока(НовСтр.ЕдИзм) Тогда
НовСтр.ЕдИзм = СокрЛП(Строка(ЗначениеСвойства(СтрокаAPI, "unit_code", "")));
КонецЕсли;
НовСтр.ХарактеристикаИдентификатор = UUIDИзСтроки(ЗначениеСвойства(СтрокаAPI, "characteristic_guid", ""));
НовСтр.ХарактеристикаНаименование = СокрЛП(Строка(ЗначениеСвойства(СтрокаAPI, "characteristic_name", "")));
НовСтр.СерияИдентификатор = UUIDИзСтроки(ЗначениеСвойства(СтрокаAPI, "series_guid", ""));
НовСтр.Серия = СокрЛП(Строка(ЗначениеСвойства(СтрокаAPI, "series_name", "")));
НовСтр.ГТДИдентификатор = UUIDИзСтроки(ЗначениеСвойства(СтрокаAPI, "gtd_guid", ""));
НовСтр.ГТД = СокрЛП(Строка(ЗначениеСвойства(СтрокаAPI, "gtd_name", "")));
НовСтр.ОрганизацияИдентификатор = UUIDИзСтроки(ЗначениеСвойства(СтрокаAPI, "organization_guid", ""));
НовСтр.ОрганизацияНаименование = СокрЛП(Строка(ЗначениеСвойства(СтрокаAPI, "organization_name", "")));
НовСтр.Количество = ЧислоИзЗначения(ЗначениеСвойства(СтрокаAPI, "qty_warehouse",
ЗначениеСвойства(СтрокаAPI, "qty_org", 0)), 0);
НовСтр.КоличествоКОтгрузке = ЧислоИзЗначения(ЗначениеСвойства(СтрокаAPI, "qty_to_ship", 0), 0);
НовСтр.КоличествоОрганизации = ЧислоИзЗначения(ЗначениеСвойства(СтрокаAPI, "qty_org", 0), 0);
НовСтр.ВидНоменклатурыИдентификатор = UUIDИзСтроки(ЗначениеСвойства(СтрокаAPI, "policy_kind_guid", ""));
НовСтр.ВидНоменклатурыНаименование = СокрЛП(Строка(ЗначениеСвойства(СтрокаAPI, "policy_kind_name", "")));
НовСтр.ПолитикаСерии = СокрЛП(Строка(ЗначениеСвойства(СтрокаAPI, "policy_use_series", "")));
НовСтр.ПолитикаХарактеристик = СокрЛП(Строка(ЗначениеСвойства(СтрокаAPI, "policy_use_characteristics", "")));
НовСтр.Сопоставлено = Ложь;
НовСтр.КомментарийСопоставления = "";
КонецПроцедуры
Функция НайтиНоменклатуруСтроки(СтрТ)
Ном = Справочники.Номенклатура.ПустаяСсылка();
Арт = СокрЛП(СтрТ.Артикул);
ИмяНом = СокрЛП(СтрТ.НоменклатураНаименование);
КодНом = СокрЛП(СтрТ.НоменклатураКод);
Если ЗначениеЗаполнено(Арт) Тогда
Ном = Справочники.Номенклатура.НайтиПоРеквизиту("Артикул", Арт);
КонецЕсли;
Если Не ЗначениеЗаполнено(Ном) Тогда
СсылкаПоГуид = СсылкаПоUUID(Справочники.Номенклатура, СтрТ.НоменклатураИдентификатор);
Если ЗначениеЗаполнено(СсылкаПоГуид) И ОбщегоНазначения.СсылкаСуществует(СсылкаПоГуид) Тогда
Ном = СсылкаПоГуид;
КонецЕсли;
КонецЕсли;
Если Не ЗначениеЗаполнено(Ном) И ЗначениеЗаполнено(КодНом) Тогда
Ном = Справочники.Номенклатура.НайтиПоКоду(КодНом);
КонецЕсли;
Если Не ЗначениеЗаполнено(Ном) И ЗначениеЗаполнено(ИмяНом) Тогда
Ном = Справочники.Номенклатура.НайтиПоНаименованию(ИмяНом, Истина);
КонецЕсли;
Если ЗначениеЗаполнено(Ном) И ЗначениеЗаполнено(ИмяНом) Тогда
Если СокрЛП(Ном.Наименование) <> ИмяНом Тогда
СтрТ.КомментарийСопоставления = "Номенклатура не совпала по имени: " + ИмяНом + " → " + Ном.Наименование;
Возврат Справочники.Номенклатура.ПустаяСсылка();
КонецЕсли;
КонецЕсли;
Возврат Ном;
КонецФункции
Функция НайтиСкладСтроки(СтрТ)
СкладЦель = СсылкаПоUUID(Справочники.Склады, СтрТ.СкладИдентификатор);
Если ЗначениеЗаполнено(СкладЦель) И ОбщегоНазначения.СсылкаСуществует(СкладЦель) Тогда
Возврат СкладЦель;
КонецЕсли;
ИмяСкл = СокрЛП(СтрТ.СкладНаименование);
Если ЗначениеЗаполнено(ИмяСкл) Тогда
Возврат Справочники.Склады.НайтиПоНаименованию(ИмяСкл, Истина);
КонецЕсли;
Возврат Справочники.Склады.ПустаяСсылка();
КонецФункции
Функция НайтиИлиСоздатьХарактеристику(СтрТ, Ном, РежимТест, Результат)
Хар = Справочники.ХарактеристикиНоменклатуры.ПустаяСсылка();
ИмяХар = СокрЛП(СтрТ.ХарактеристикаНаименование);
ВидНом = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ном, "ВидНоменклатуры");
ИспХар = Ложь;
Попытка
ИспХар = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ном, "ВидНоменклатуры.ИспользоватьХарактеристики") = Истина;
Исключение
Попытка
ИспХар = ВидНом.ИспользоватьХарактеристики;
Исключение
КонецПопытки;
КонецПопытки;
Если ЗначениеЗаполнено(ИмяХар) Тогда
Хар = Справочники.ХарактеристикиНоменклатуры.НайтиПоНаименованию(ИмяХар, Истина, , Ном);
Если Не ЗначениеЗаполнено(Хар) Тогда
Хар = Справочники.ХарактеристикиНоменклатуры.НайтиПоНаименованию(ИмяХар, Истина, , ВидНом);
КонецЕсли;
КонецЕсли;
Если Не ЗначениеЗаполнено(Хар) Тогда
СсылкаПоГуид = СсылкаПоUUID(Справочники.ХарактеристикиНоменклатуры, СтрТ.ХарактеристикаИдентификатор);
Если ЗначениеЗаполнено(СсылкаПоГуид) И ОбщегоНазначения.СсылкаСуществует(СсылкаПоГуид) Тогда
ВладХар = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаПоГуид, "Владелец");
Если ВладХар = Ном Или ВладХар = ВидНом Тогда
Хар = СсылкаПоГуид;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Если Не ЗначениеЗаполнено(Хар) И ИспХар И ЗначениеЗаполнено(ИмяХар) И Не РежимТест Тогда
Попытка
НовХар = Справочники.ХарактеристикиНоменклатуры.СоздатьЭлемент();
НовХар.Владелец = ВидНом;
НовХар.Наименование = ИмяХар;
НовХар.Записать();
Хар = НовХар.Ссылка;
Результат.СозданоХарактеристик = Результат.СозданоХарактеристик + 1;
Исключение
СтрТ.КомментарийСопоставления = "Ошибка создания характеристики: " + ИмяХар;
КонецПопытки;
КонецЕсли;
Если Не ИспХар Тогда
Хар = Справочники.ХарактеристикиНоменклатуры.ПустаяСсылка();
КонецЕсли;
Возврат Хар;
КонецФункции
Функция НайтиИлиСоздатьСерию(СтрТ, Ном, СкладЦель, СуффиксДатыСер, РежимТест,
КэшТребуетсяСерия, КэшСерий, Результат)
Сер = Справочники.СерииНоменклатуры.ПустаяСсылка();
ВидНом = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ном, "ВидНоменклатуры");
КлючТребСер = Строка(Ном.УникальныйИдентификатор());
ТребуетсяСерия = КэшТребуетсяСерия.Получить(КлючТребСер);
Если ТребуетсяСерия = Неопределено Тогда
ТребуетсяСерия = Ложь;
Попытка
ТребуетсяСерия = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ном, "ВидНоменклатуры.ИспользоватьСерии") = Истина;
Исключение
Попытка
ТребуетсяСерия = ВидНом.ИспользоватьСерии;
Исключение
КонецПопытки;
КонецПопытки;
КэшТребуетсяСерия.Вставить(КлючТребСер, ТребуетсяСерия);
КонецЕсли;
ИмяСерии = СокрЛП(СтрТ.Серия);
Если Не ЗначениеЗаполнено(ИмяСерии) И ТребуетсяСерия Тогда
КодСклЗагл = "";
Попытка
КодСклЗагл = СокрЛП(Строка(ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СкладЦель, "Код")));
Исключение
КодСклЗагл = "";
КонецПопытки;
Если ПустаяСтрока(КодСклЗагл) Тогда
КодСклЗагл = Лев(СтрЗаменить(Строка(СкладЦель.УникальныйИдентификатор()), "-", ""), 8);
КонецЕсли;
ИмяСерии = "Перенос_" + КодСклЗагл + "_" + СуффиксДатыСер;
СтрТ.Серия = ИмяСерии;
КонецЕсли;
Если Не ТребуетсяСерия И Не ЗначениеЗаполнено(ИмяСерии) Тогда
Возврат Сер;
КонецЕсли;
СсылкаПоГуид = СсылкаПоUUID(Справочники.СерииНоменклатуры, СтрТ.СерияИдентификатор);
Если ЗначениеЗаполнено(СсылкаПоГуид) И ОбщегоНазначения.СсылкаСуществует(СсылкаПоГуид) Тогда
Возврат СсылкаПоГуид;
КонецЕсли;
ВидСер = Неопределено;
ИспНомерСерии = Истина;
Попытка
НастрСер = Справочники.ВидыНоменклатуры.НастройкиИспользованияСерий(ВидНом, СкладЦель);
ВидСер = НастрСер.ВладелецСерий;
ИспНомерСерии = НастрСер.ИспользоватьНомерСерии;
Исключение
ВидСер = ВидНом;
КонецПопытки;
Если Не ЗначениеЗаполнено(ВидСер) Тогда
ВидСер = ВидНом;
КонецЕсли;
КлючКэшСер = Строка(ВидСер.УникальныйИдентификатор()) + "|" + ИмяСерии;
СерИзКэш = КэшСерий.Получить(КлючКэшСер);
Если СерИзКэш <> Неопределено Тогда
Возврат СерИзКэш;
КонецЕсли;
Если ИспНомерСерии Тогда
Сер = Справочники.СерииНоменклатуры.НайтиПоРеквизиту("Номер", ИмяСерии, , ВидСер);
КонецЕсли;
Если Не ЗначениеЗаполнено(Сер) Тогда
Сер = Справочники.СерииНоменклатуры.НайтиПоНаименованию(ИмяСерии, Истина, , ВидСер);
КонецЕсли;
Если Не ЗначениеЗаполнено(Сер) И Не РежимТест И (ТребуетсяСерия Или ЗначениеЗаполнено(ИмяСерии)) Тогда
Попытка
ОбСер = Справочники.СерииНоменклатуры.ПустойОбъект(ВидСер);
Если ИспНомерСерии Тогда
НомерСер = ИмяСерии;
Если СтрНачинаетсяС(ИмяСерии, "Перенос_") Тогда
НомерСер = Сред(ИмяСерии, СтрДлина("Перенос_") + 1);
КонецЕсли;
ОбСер.Номер = НомерСер;
Иначе
Попытка
ОбСер.Наименование = ИмяСерии;
Исключение
КонецПопытки;
КонецЕсли;
ОбСер.Записать();
Сер = ОбСер.Ссылка;
Результат.СозданоСерий = Результат.СозданоСерий + 1;
Исключение
СтрТ.КомментарийСопоставления = "Ошибка создания серии: " + ИмяСерии;
КонецПопытки;
КонецЕсли;
Если ЗначениеЗаполнено(Сер) Тогда
КэшСерий.Вставить(КлючКэшСер, Сер);
ИначеЕсли ТребуетсяСерия Тогда
Если ПустаяСтрока(СтрТ.КомментарийСопоставления) Или СтрТ.КомментарийСопоставления = "OK" Тогда
СтрТ.КомментарийСопоставления = "Серия не найдена" + ?(РежимТест, " (тест)", "");
КонецЕсли;
КонецЕсли;
Возврат Сер;
КонецФункции
Функция СсылкаПоUUID(Менеджер, Идентификатор)
Если Не ЗначениеЗаполнено(Идентификатор) Тогда
Возврат Менеджер.ПустаяСсылка();
КонецЕсли;
Попытка
Возврат Менеджер.ПолучитьСсылку(Идентификатор);
Исключение
Возврат Менеджер.ПустаяСсылка();
КонецПопытки;
КонецФункции
Функция UUIDИзСтроки(Значение)
Текст = НРег(СокрЛП(Строка(Значение)));
Если ПустаяСтрока(Текст) Или Текст = "00000000-0000-0000-0000-000000000000" Тогда
Возврат Новый УникальныйИдентификатор("00000000-0000-0000-0000-000000000000");
КонецЕсли;
Попытка
Возврат Новый УникальныйИдентификатор(Текст);
Исключение
Возврат Новый УникальныйИдентификатор("00000000-0000-0000-0000-000000000000");
КонецПопытки;
КонецФункции
Функция ЗначениеСвойства(Источник, Имя, ПоУмолчанию)
Если Источник = Неопределено Тогда
Возврат ПоУмолчанию;
КонецЕсли;
Если ТипЗнч(Источник) = Тип("Структура") Или ТипЗнч(Источник) = Тип("ФиксированнаяСтруктура") Тогда
Если Источник.Свойство(Имя) Тогда
Возврат Источник[Имя];
КонецЕсли;
Возврат ПоУмолчанию;
КонецЕсли;
Если ТипЗнч(Источник) = Тип("Соответствие") Или ТипЗнч(Источник) = Тип("ФиксированноеСоответствие") Тогда
Значение = Источник.Получить(Имя);
Если Значение = Неопределено Тогда
Возврат ПоУмолчанию;
КонецЕсли;
Возврат Значение;
КонецЕсли;
Возврат ПоУмолчанию;
КонецФункции
Функция ЧислоИзЗначения(Значение, ПоУмолчанию)
Если Значение = Неопределено Или Значение = Null Тогда
Возврат ПоУмолчанию;
КонецЕсли;
Попытка
Возврат Число(Значение);
Исключение
Возврат ПоУмолчанию;
КонецПопытки;
КонецФункции
#КонецОбласти
#КонецЕсли
@@ -1,6 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<Form xmlns="http://v8.1c.ru/8.3/xcf/logform" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcssch="http://v8.1c.ru/8.1/data-composition-system/schema" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.20">
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1"/>
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1">
<ChildItems>
<Button name="ФормаПеренестиВОтбор" id="26">
<Type>CommandBarButton</Type>
<DefaultButton>true</DefaultButton>
<CommandName>Form.Command.ПеренестиВОтбор</CommandName>
<ExtendedTooltip name="ФормаПеренестиВОтборРасширеннаяПодсказка" id="27"/>
</Button>
</ChildItems>
</AutoCommandBar>
<Events>
<Event name="OnCreateAtServer">ПриСозданииНаСервере</Event>
</Events>
<ChildItems>
<CheckBoxField name="Добавление" id="23">
<DataPath>Добавление</DataPath>
@@ -146,4 +158,21 @@
</Type>
</Attribute>
</Attributes>
<Commands>
<Command name="ПеренестиВОтбор" id="1">
<Title>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Перенести в отбор</v8:content>
</v8:item>
</Title>
<ToolTip>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Перенести отмеченные склады в отбор</v8:content>
</v8:item>
</ToolTip>
<Action>ПеренестиВОтбор</Action>
</Command>
</Commands>
</Form>
@@ -0,0 +1,78 @@
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Добавление = Истина;
Подключение = Неопределено;
Если Параметры.Свойство("ПодключениеКСтороннейБазе", Подключение) Тогда
Объект.ПодключениеКСтороннейБазе = Подключение;
КонецЕсли;
ЗаполнитьСписокСкладовНаСервере();
КонецПроцедуры
&НаКлиенте
Процедура ПеренестиВОтбор(Команда)
Выбранные = Новый Массив;
Для Каждого Стр Из СписокСкладовВнешнейБазы Цикл
Если Стр.Отметка Тогда
Выбранные.Добавить(Новый Структура("Наименование, Идентификатор, Отметка",
Стр.Наименование, Стр.Идентификатор, Истина));
КонецЕсли;
КонецЦикла;
Закрыть(Новый Структура("Добавление, Склады", Добавление, Выбранные));
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьСписокСкладовНаСервере()
Если Не ЗначениеЗаполнено(Объект.ПодключениеКСтороннейБазе) Тогда
Возврат;
КонецЕсли;
Ответ = РК_ОбменСВнешнейБазой.СписокСкладовВнешнейБазы(Объект.ПодключениеКСтороннейБазе);
Строки = Неопределено;
Если ТипЗнч(Ответ) = Тип("Структура") Или ТипЗнч(Ответ) = Тип("Соответствие") Тогда
Если ТипЗнч(Ответ) = Тип("Структура") Тогда
Если Ответ.Свойство("rows") Тогда
Строки = Ответ.rows;
КонецЕсли;
Иначе
Строки = Ответ.Получить("rows");
КонецЕсли;
КонецЕсли;
Если ТипЗнч(Строки) <> Тип("Массив") Тогда
Возврат;
КонецЕсли;
СписокСкладовВнешнейБазы.Очистить();
Для Каждого СтрокаAPI Из Строки Цикл
ГуидТекст = "";
Имя = "";
Если ТипЗнч(СтрокаAPI) = Тип("Структура") Тогда
Если СтрокаAPI.Свойство("guid") Тогда
ГуидТекст = СокрЛП(Строка(СтрокаAPI.guid));
КонецЕсли;
Если СтрокаAPI.Свойство("name") Тогда
Имя = СокрЛП(Строка(СтрокаAPI.name));
КонецЕсли;
ИначеЕсли ТипЗнч(СтрокаAPI) = Тип("Соответствие") Тогда
ГуидТекст = СокрЛП(Строка(СтрокаAPI.Получить("guid")));
Имя = СокрЛП(Строка(СтрокаAPI.Получить("name")));
КонецЕсли;
Если ПустаяСтрока(ГуидТекст) Тогда
Продолжить;
КонецЕсли;
НовСтр = СписокСкладовВнешнейБазы.Добавить();
НовСтр.Наименование = Имя;
Попытка
НовСтр.Идентификатор = Новый УникальныйИдентификатор(ГуидТекст);
Исключение
Продолжить;
КонецПопытки;
НовСтр.Отметка = Ложь;
КонецЦикла;
КонецПроцедуры
@@ -6,7 +6,24 @@
<v8:content>Загрузка остатков из внешней базы</v8:content>
</v8:item>
</Title>
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1"/>
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1">
<ChildItems>
<Button name="ФормаЗагрузитьОстатки" id="113">
<Type>CommandBarButton</Type>
<DefaultButton>true</DefaultButton>
<CommandName>Form.Command.ЗагрузитьОстатки</CommandName>
<ExtendedTooltip name="ФормаЗагрузитьОстаткиРасширеннаяПодсказка" id="114"/>
</Button>
<Button name="ФормаСопоставить" id="115">
<Type>CommandBarButton</Type>
<CommandName>Form.Command.Сопоставить</CommandName>
<ExtendedTooltip name="ФормаСопоставитьРасширеннаяПодсказка" id="116"/>
</Button>
</ChildItems>
</AutoCommandBar>
<Events>
<Event name="OnCreateAtServer">ПриСозданииНаСервере</Event>
</Events>
<ChildItems>
<InputField name="ПодключениеКСтороннейБазе" id="30">
<DataPath>Объект.ПодключениеКСтороннейБазе</DataPath>
@@ -45,7 +62,7 @@
<Title>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Водящие данные</v8:content>
<v8:content>Входящие данные</v8:content>
</v8:item>
</Title>
<ToolTip>
@@ -143,6 +160,74 @@
<ContextMenu name="ВходящиеДанныеСерияКонтекстноеМеню" id="68"/>
<ExtendedTooltip name="ВходящиеДанныеСерияРасширеннаяПодсказка" id="69"/>
</InputField>
<InputField name="ВходящиеДанныеКоличество" id="80">
<DataPath>Объект.ВходящиеДанные.Количество</DataPath>
<EditMode>EnterOnInput</EditMode>
<ContextMenu name="ВходящиеДанныеКоличествоКонтекстноеМеню" id="81"/>
<ExtendedTooltip name="ВходящиеДанныеКоличествоРасширеннаяПодсказка" id="82"/>
</InputField>
<InputField name="ВходящиеДанныеАртикул" id="83">
<DataPath>Объект.ВходящиеДанные.Артикул</DataPath>
<EditMode>EnterOnInput</EditMode>
<ContextMenu name="ВходящиеДанныеАртикулКонтекстноеМеню" id="84"/>
<ExtendedTooltip name="ВходящиеДанныеАртикулРасширеннаяПодсказка" id="85"/>
</InputField>
<InputField name="ВходящиеДанныеНоменклатураКод" id="86">
<DataPath>Объект.ВходящиеДанные.НоменклатураКод</DataPath>
<EditMode>EnterOnInput</EditMode>
<ContextMenu name="ВходящиеДанныеНоменклатураКодКонтекстноеМеню" id="87"/>
<ExtendedTooltip name="ВходящиеДанныеНоменклатураКодРасширеннаяПодсказка" id="88"/>
</InputField>
<InputField name="ВходящиеДанныеХарактеристикаНаименование" id="89">
<DataPath>Объект.ВходящиеДанные.ХарактеристикаНаименование</DataPath>
<EditMode>EnterOnInput</EditMode>
<ContextMenu name="ВходящиеДанныеХарактеристикаНаименованиеКонтекстноеМеню" id="90"/>
<ExtendedTooltip name="ВходящиеДанныеХарактеристикаНаименованиеРасширеннаяПодсказка" id="91"/>
</InputField>
<InputField name="ВходящиеДанныеВидНоменклатурыНаименование" id="92">
<DataPath>Объект.ВходящиеДанные.ВидНоменклатурыНаименование</DataPath>
<EditMode>EnterOnInput</EditMode>
<ContextMenu name="ВходящиеДанныеВидНоменклатурыНаименованиеКонтекстноеМеню" id="93"/>
<ExtendedTooltip name="ВходящиеДанныеВидНоменклатурыНаименованиеРасширеннаяПодсказка" id="94"/>
</InputField>
<InputField name="ВходящиеДанныеОрганизацияНаименование" id="95">
<DataPath>Объект.ВходящиеДанные.ОрганизацияНаименование</DataPath>
<EditMode>EnterOnInput</EditMode>
<ContextMenu name="ВходящиеДанныеОрганизацияНаименованиеКонтекстноеМеню" id="96"/>
<ExtendedTooltip name="ВходящиеДанныеОрганизацияНаименованиеРасширеннаяПодсказка" id="97"/>
</InputField>
<InputField name="ВходящиеДанныеНоменклатура" id="98">
<DataPath>Объект.ВходящиеДанные.Номенклатура</DataPath>
<EditMode>EnterOnInput</EditMode>
<ContextMenu name="ВходящиеДанныеНоменклатураКонтекстноеМеню" id="99"/>
<ExtendedTooltip name="ВходящиеДанныеНоменклатураРасширеннаяПодсказка" id="100"/>
</InputField>
<InputField name="ВходящиеДанныеСклад" id="101">
<DataPath>Объект.ВходящиеДанные.Склад</DataPath>
<EditMode>EnterOnInput</EditMode>
<ContextMenu name="ВходящиеДанныеСкладКонтекстноеМеню" id="102"/>
<ExtendedTooltip name="ВходящиеДанныеСкладРасширеннаяПодсказка" id="103"/>
</InputField>
<InputField name="ВходящиеДанныеСерияСсылка" id="104">
<DataPath>Объект.ВходящиеДанные.СерияСсылка</DataPath>
<EditMode>EnterOnInput</EditMode>
<ContextMenu name="ВходящиеДанныеСерияСсылкаКонтекстноеМеню" id="105"/>
<ExtendedTooltip name="ВходящиеДанныеСерияСсылкаРасширеннаяПодсказка" id="106"/>
</InputField>
<CheckBoxField name="ВходящиеДанныеСопоставлено" id="107">
<DataPath>Объект.ВходящиеДанные.Сопоставлено</DataPath>
<EditMode>EnterOnInput</EditMode>
<CheckBoxType>Auto</CheckBoxType>
<ContextMenu name="ВходящиеДанныеСопоставленоКонтекстноеМеню" id="108"/>
<ExtendedTooltip name="ВходящиеДанныеСопоставленоРасширеннаяПодсказка" id="109"/>
</CheckBoxField>
<InputField name="ВходящиеДанныеКомментарийСопоставления" id="110">
<DataPath>Объект.ВходящиеДанные.КомментарийСопоставления</DataPath>
<EditMode>EnterOnInput</EditMode>
<ContextMenu name="ВходящиеДанныеКомментарийСопоставленияКонтекстноеМеню" id="111"/>
<ExtendedTooltip name="ВходящиеДанныеКомментарийСопоставленияРасширеннаяПодсказка" id="112"/>
</InputField>
</ChildItems>
</Table>
</ChildItems>
@@ -325,5 +410,35 @@
</ToolTip>
<Action>ПодобратьСклады</Action>
</Command>
<Command name="ЗагрузитьОстатки" id="2">
<Title>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Загрузить остатки</v8:content>
</v8:item>
</Title>
<ToolTip>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Загрузить остатки из исходной базы</v8:content>
</v8:item>
</ToolTip>
<Action>ЗагрузитьОстатки</Action>
</Command>
<Command name="Сопоставить" id="3">
<Title>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Сопоставить</v8:content>
</v8:item>
</Title>
<ToolTip>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Сопоставить НСИ и серии</v8:content>
</v8:item>
</ToolTip>
<Action>Сопоставить</Action>
</Command>
</Commands>
</Form>
@@ -1,10 +1,232 @@
#Область ОбработчикиСобытийФормы
&НаСервере
Процедура ПодобратьСкладыНаСервере()
// Вставить содержимое обработчика.
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если Не ЗначениеЗаполнено(Объект.ПодключениеКСтороннейБазе) Тогда
Попытка
Объект.ПодключениеКСтороннейБазе = Константы.РК_РабочееПодключениеЧерезАПИ.Получить();
Исключение
КонецПопытки;
КонецЕсли;
Если Не ЗначениеЗаполнено(Объект.ДатаОстатков) Тогда
Объект.ДатаОстатков = КонецДня(НачалоМесяца(ТекущаяДатаСеанса()) - 1);
КонецЕсли;
Если Не ЗначениеЗаполнено(Объект.ДатаДокументовВвода) Тогда
Объект.ДатаДокументовВвода = Объект.ДатаОстатков;
КонецЕсли;
КонецПроцедуры
#КонецОбласти
#Область ОбработчикиКомандФормы
&НаКлиенте
Процедура ПодобратьСклады(Команда)
Если Не ЗначениеЗаполнено(Объект.ПодключениеКСтороннейБазе) Тогда
ПоказатьПредупреждение(, "Укажите подключение к сторонней базе");
Возврат;
КонецЕсли;
ПодобратьСкладыНаСервере();
Элементы.ГруппаСтраницы.ТекущаяСтраница = Элементы.ГруппаОтбор;
КонецПроцедуры
&НаКлиенте
Процедура ЗагрузитьОстатки(Команда)
Если Не ЗначениеЗаполнено(Объект.ПодключениеКСтороннейБазе) Тогда
ПоказатьПредупреждение(, "Укажите подключение к сторонней базе");
Возврат;
КонецЕсли;
Если Не ЗначениеЗаполнено(Объект.ДатаОстатков) Тогда
ПоказатьПредупреждение(, "Укажите дату остатков");
Возврат;
КонецЕсли;
Количество = ЗагрузитьОстаткиНаСервере();
Элементы.ГруппаСтраницы.ТекущаяСтраница = Элементы.ГруппаВходящиеДанные;
ПоказатьОповещениеПользователя("Загрузка остатков", , "Загружено строк: " + Формат(Количество, "ЧГ=0"));
КонецПроцедуры
&НаКлиенте
Процедура Сопоставить(Команда)
Если Объект.ВходящиеДанные.Количество() = 0 Тогда
ПоказатьПредупреждение(, "Сначала загрузите остатки");
Возврат;
КонецЕсли;
ДопПараметры = Новый Структура("РежимТест", Истина);
Описание = Новый ОписаниеОповещения("СопоставитьПослеВопроса", ЭтотОбъект, ДопПараметры);
ПоказатьВопрос(Описание,
"Создавать отсутствующие серии и характеристики?" + Символы.ПС
+ "Да — запись; Нет — только поиск (тест); Отмена — прервать.",
РежимДиалогаВопрос.ДаНетОтмена);
КонецПроцедуры
&НаКлиенте
Процедура СопоставитьПослеВопроса(РезультатВопроса, ДопПараметры) Экспорт
Если РезультатВопроса = КодВозвратаДиалога.Отмена Тогда
Возврат;
КонецЕсли;
РежимТест = (РезультатВопроса <> КодВозвратаДиалога.Да);
Текст = СопоставитьНаСервере(РежимТест);
ПоказатьОповещениеПользователя("Сопоставление", , Текст);
КонецПроцедуры
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
&НаСервере
Процедура ПодобратьСкладыНаСервере()
Ответ = РК_ОбменСВнешнейБазой.СписокСкладовВнешнейБазы(Объект.ПодключениеКСтороннейБазе);
Строки = Неопределено;
Если ТипЗнч(Ответ) = Тип("Структура") Или ТипЗнч(Ответ) = Тип("Соответствие") Тогда
Строки = ЗначениеСвойстваОтвета(Ответ, "rows");
КонецЕсли;
Если ТипЗнч(Строки) <> Тип("Массив") Тогда
ВызватьИсключение "Некорректный ответ /v1/stock/warehouses";
КонецЕсли;
БылиОтметки = Новый Соответствие;
Для Каждого Стр Из ОтборСкладовВнешнейБазы Цикл
БылиОтметки.Вставить(Строка(Стр.Идентификатор), Стр.Отметка);
КонецЦикла;
ОтборСкладовВнешнейБазы.Очистить();
Для Каждого СтрокаAPI Из Строки Цикл
ГуидТекст = СокрЛП(Строка(ЗначениеСвойстваОтвета(СтрокаAPI, "guid")));
Если ПустаяСтрока(ГуидТекст) Тогда
Продолжить;
КонецЕсли;
НовСтр = ОтборСкладовВнешнейБазы.Добавить();
НовСтр.Наименование = СокрЛП(Строка(ЗначениеСвойстваОтвета(СтрокаAPI, "name")));
Попытка
НовСтр.Идентификатор = Новый УникальныйИдентификатор(ГуидТекст);
Исключение
Продолжить;
КонецПопытки;
СтараяОтметка = БылиОтметки.Получить(Строка(НовСтр.Идентификатор));
НовСтр.Отметка = ?(СтараяОтметка = Неопределено, Ложь, СтараяОтметка = Истина);
КонецЦикла;
КонецПроцедуры
&НаСервере
Функция ЗагрузитьОстаткиНаСервере()
СкладыGUID = Новый Массив;
Для Каждого Стр Из ОтборСкладовВнешнейБазы Цикл
Если Стр.Отметка Тогда
СкладыGUID.Добавить(Строка(Стр.Идентификатор));
КонецЕсли;
КонецЦикла;
Если СкладыGUID.Количество() = 0 Тогда
ВызватьИсключение "Отметьте хотя бы один склад на закладке «Отбор»";
КонецЕсли;
Фильтры = Новый Структура;
Фильтры.Вставить("warehouses", СкладыGUID);
Фильтры.Вставить("only_nonzero", Истина);
ВсеСтроки = Новый Массив;
Курсор = "";
Лимит = 2000;
МаксСтраниц = 200;
Для НомерСтраницы = 1 По МаксСтраниц Цикл
ПараметрыЗапроса = Новый Структура;
ПараметрыЗапроса.Вставить("mode", "combined");
ПараметрыЗапроса.Вставить("as_of", Формат(Объект.ДатаОстатков, "ДФ=yyyy-MM-ddTHH:mm:ss"));
ПараметрыЗапроса.Вставить("limit", Лимит);
ПараметрыЗапроса.Вставить("include_policy", Истина);
ПараметрыЗапроса.Вставить("filters", Фильтры);
Если Не ПустаяСтрока(Курсор) Тогда
ПараметрыЗапроса.Вставить("cursor", Курсор);
КонецЕсли;
Ответ = РК_ОбменСВнешнейБазой.ОстаткиВнешнейБазы(Объект.ПодключениеКСтороннейБазе, ПараметрыЗапроса);
Строки = ЗначениеСвойстваОтвета(Ответ, "rows");
Если ТипЗнч(Строки) <> Тип("Массив") Тогда
ВызватьИсключение "Некорректный ответ /v1/stock/balances";
КонецЕсли;
Для Каждого СтрAPI Из Строки Цикл
ВсеСтроки.Добавить(СтрAPI);
КонецЦикла;
Мета = ЗначениеСвойстваОтвета(Ответ, "meta");
ЕстьЕще = Ложь;
Если ТипЗнч(Мета) = Тип("Структура") Или ТипЗнч(Мета) = Тип("Соответствие") Тогда
ЗначениеЕстьЕще = ЗначениеСвойстваОтвета(Мета, "has_more");
Если ТипЗнч(ЗначениеЕстьЕще) = Тип("Булево") Тогда
ЕстьЕще = ЗначениеЕстьЕще;
ИначеЕсли ЗначениеЕстьЕще <> Неопределено Тогда
ЕстьЕще = (НРег(СокрЛП(Строка(ЗначениеЕстьЕще))) = "true" Или Строка(ЗначениеЕстьЕще) = "1");
КонецЕсли;
КонецЕсли;
Курсор = СокрЛП(Строка(ЗначениеСвойстваОтвета(Ответ, "cursor")));
Если Не ЕстьЕще Или ПустаяСтрока(Курсор) Или Строки.Количество() = 0 Тогда
Прервать;
КонецЕсли;
КонецЦикла;
Пакет = Новый Структура("rows", ВсеСтроки);
ОбъектОбработки = РеквизитФормыВЗначение("Объект");
ОбъектОбработки.ЗаполнитьВходящиеДанныеИзОтветаОстатков(Пакет, Истина);
ЗначениеВРеквизитФормы(ОбъектОбработки, "Объект");
Возврат Объект.ВходящиеДанные.Количество();
КонецФункции
&НаСервере
Функция СопоставитьНаСервере(РежимТест)
ОбъектОбработки = РеквизитФормыВЗначение("Объект");
Результат = ОбъектОбработки.СопоставитьВходящиеДанные(РежимТест);
ЗначениеВРеквизитФормы(ОбъектОбработки, "Объект");
Возврат СтрШаблон(
"Сопоставлено: %1; не сопоставлено: %2; создано серий: %3; создано характеристик: %4%5",
Формат(Результат.Сопоставлено, "ЧН=0; ЧГ=0"),
Формат(Результат.НеСопоставлено, "ЧН=0; ЧГ=0"),
Формат(Результат.СозданоСерий, "ЧН=0; ЧГ=0"),
Формат(Результат.СозданоХарактеристик, "ЧН=0; ЧГ=0"),
?(РежимТест, " (тест)", ""));
КонецФункции
&НаСервере
Функция ЗначениеСвойстваОтвета(Источник, Имя)
Если Источник = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
Если ТипЗнч(Источник) = Тип("Структура") Или ТипЗнч(Источник) = Тип("ФиксированнаяСтруктура") Тогда
Если Источник.Свойство(Имя) Тогда
Возврат Источник[Имя];
КонецЕсли;
Возврат Неопределено;
КонецЕсли;
Если ТипЗнч(Источник) = Тип("Соответствие") Или ТипЗнч(Источник) = Тип("ФиксированноеСоответствие") Тогда
Возврат Источник.Получить(Имя);
КонецЕсли;
Возврат Неопределено;
КонецФункции
#КонецОбласти