diff --git a/CHANGELOG.md b/CHANGELOG.md
index d01babd..fec9ec4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,15 @@
---
+## 1.3.1.133 (2026-10-13)
+
+### #36: окно серий сворачивается суммой перед записью ввода остатков
+
+- Проведение склада для статусов серий 4, 6, 8 и 10 берёт максимум количества, а не сумму повторов одной серии.
+- Перед записью КБ строки окна серий с одной номенклатурой, характеристикой, назначением, серией и упаковкой складываются.
+- [#81](https://git.p7net.ru/rhana/crm3-migration/issues/81): строки товаров с одной номенклатурой, характеристикой, серией и ГТД суммируются, в том числе с заполненной характеристикой. Разный ГТД остаётся разными строками.
+- Загрузка остатков не создаёт элементы номеров ГТД. Если у найденной карточки пуст код, в него пишется номер из остатка. Пустая страна заполняется по коду страны из исходной базы.
+
## 1.3.1.132 (2026-10-13)
### #79: сумма без НДС при выключенном «Цена включает НДС»
diff --git a/VERSION b/VERSION
index a79b069..f59737d 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.3.1.132
+1.3.1.133
diff --git a/src/ConfigDumpInfo.xml b/src/ConfigDumpInfo.xml
index 1ecd54e..8381460 100644
--- a/src/ConfigDumpInfo.xml
+++ b/src/ConfigDumpInfo.xml
@@ -83,7 +83,7 @@
-
+
@@ -161,6 +161,7 @@
+
@@ -204,6 +205,7 @@
+
diff --git a/src/Configuration.xml b/src/Configuration.xml
index e5ee4d1..bd3a478 100644
--- a/src/Configuration.xml
+++ b/src/Configuration.xml
@@ -62,7 +62,7 @@
Role.РК_ОсновнаяРоль
- 1.3.1.132
+ 1.3.1.133
Language.Русский
diff --git a/src/DataProcessors/РК_ЗагрузкаОстатков.xml b/src/DataProcessors/РК_ЗагрузкаОстатков.xml
index c0a7b37..7cd2b3e 100644
--- a/src/DataProcessors/РК_ЗагрузкаОстатков.xml
+++ b/src/DataProcessors/РК_ЗагрузкаОстатков.xml
@@ -1032,6 +1032,46 @@
Auto
+
+
+ ГТДСтранаКод
+
+
+ ru
+ Код страны ГТД
+
+
+
+
+ xs:string
+
+ 10
+ Variable
+
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
ГТДИдентификатор
@@ -2558,6 +2598,46 @@
Auto
+
+
+ ГТДСтранаКод
+
+
+ ru
+ Код страны ГТД
+
+
+
+
+ xs:string
+
+ 10
+ Variable
+
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
ГТДИдентификатор
diff --git a/src/DataProcessors/РК_ЗагрузкаОстатков/Ext/ObjectModule.bsl b/src/DataProcessors/РК_ЗагрузкаОстатков/Ext/ObjectModule.bsl
index e00e9ba..37f0721 100644
--- a/src/DataProcessors/РК_ЗагрузкаОстатков/Ext/ObjectModule.bsl
+++ b/src/DataProcessors/РК_ЗагрузкаОстатков/Ext/ObjectModule.bsl
@@ -176,7 +176,7 @@
КонецЕсли;
КонецЕсли;
- ЗаполнитьНомерГТДСтроки(СтрТ);
+ ЗаполнитьНомерГТДСтроки(СтрТ, РежимТест);
СтрТ.Сопоставлено = Истина;
Если ПустаяСтрока(СтрТ.КомментарийСопоставления) Тогда
@@ -289,7 +289,7 @@
Продолжить;
КонецЕсли;
- ЗаполнитьНомерГТДСтроки(СтрТ);
+ ЗаполнитьНомерГТДСтроки(СтрТ, РежимТест);
СтрТ.Сопоставлено = Истина;
Если ПустаяСтрока(СтрТ.КомментарийСопоставления) Тогда
@@ -1170,7 +1170,7 @@
Исключение
КонецПопытки;
- СтрокиСклада = СвернутьСтрокиБезХарактеристики(СтрокиСклада);
+ СтрокиСклада = СвернутьСтрокиОднойАналитики(СтрокиСклада);
Для Каждого СтрТ Из СтрокиСклада Цикл
ДобавитьСтрокуТоваровДокументаВвода(Док, СтрТ);
КонецЦикла;
@@ -1187,6 +1187,7 @@
НоменклатураСервер.ЗаполнитьСтатусыУказанияСерий(Док, ПараметрыУказанияСерий);
Исключение
КонецПопытки;
+ СвернутьСерииДокументаВвода(Док);
Док.ОбменДанными.Загрузка = Ложь;
Док.Записать(РежимЗаписиДокумента.Запись);
@@ -1245,7 +1246,7 @@
Исключение
КонецПопытки;
- СтрокиГруппы = СвернутьСтрокиБезХарактеристики(СтрокиГруппы);
+ СтрокиГруппы = СвернутьСтрокиОднойАналитики(СтрокиГруппы);
Для Каждого СтрТ Из СтрокиГруппы Цикл
ДобавитьСтрокуТоваровДокументаВвода(Док, СтрТ);
КонецЦикла;
@@ -1262,6 +1263,7 @@
НоменклатураСервер.ЗаполнитьСтатусыУказанияСерий(Док, ПараметрыУказанияСерий);
Исключение
КонецПопытки;
+ СвернутьСерииДокументаВвода(Док);
Док.ОбменДанными.Загрузка = Ложь;
Док.Записать(РежимЗаписиДокумента.Запись);
@@ -1269,6 +1271,56 @@
КонецФункции
+// Проведение ТоварыНаСкладах для статусов 4, 6, 8, 10 берёт МАКСИМУМ количества
+// окна серий в разрезе номенклатуры, характеристики, назначения, серии и упаковки (#36).
+// Повторы одной серии из разных строк товаров схлопываются суммой до записи.
+//
+Процедура СвернутьСерииДокументаВвода(Док)
+
+ Если Док.Серии.Количество() < 2 Тогда
+ Возврат;
+ КонецЕсли;
+
+ Индекс = Новый Соответствие;
+ КУдалению = Новый Массив;
+ Для Каждого СтрСер Из Док.Серии Цикл
+ КлючХар = "";
+ Если ЗначениеЗаполнено(СтрСер.Характеристика) Тогда
+ КлючХар = Строка(СтрСер.Характеристика.УникальныйИдентификатор());
+ КонецЕсли;
+ КлючНазначения = "";
+ Если ЗначениеЗаполнено(СтрСер.Назначение) Тогда
+ КлючНазначения = Строка(СтрСер.Назначение.УникальныйИдентификатор());
+ КонецЕсли;
+ КлючУпаковки = "";
+ Если ЗначениеЗаполнено(СтрСер.Упаковка) Тогда
+ КлючУпаковки = Строка(СтрСер.Упаковка.УникальныйИдентификатор());
+ КонецЕсли;
+ КлючСерии = "";
+ Если ЗначениеЗаполнено(СтрСер.Серия) Тогда
+ КлючСерии = Строка(СтрСер.Серия.УникальныйИдентификатор());
+ КонецЕсли;
+ Ключ = Строка(СтрСер.Номенклатура.УникальныйИдентификатор())
+ + "|" + КлючХар
+ + "|" + КлючНазначения
+ + "|" + КлючСерии
+ + "|" + КлючУпаковки;
+ Уже = Индекс.Получить(Ключ);
+ Если Уже = Неопределено Тогда
+ Индекс.Вставить(Ключ, СтрСер);
+ Иначе
+ Уже.Количество = Уже.Количество + СтрСер.Количество;
+ Уже.КоличествоУпаковок = Уже.КоличествоУпаковок + СтрСер.КоличествоУпаковок;
+ КУдалению.Добавить(СтрСер);
+ КонецЕсли;
+ КонецЦикла;
+
+ Для Каждого СтрСер Из КУдалению Цикл
+ Док.Серии.Удалить(СтрСер);
+ КонецЦикла;
+
+КонецПроцедуры
+
Процедура ДобавитьСтрокуТоваровДокументаВвода(Док, СтрТ)
КолДок = СтрТ.КоличествоОрганизации;
@@ -1375,22 +1427,39 @@
КонецФункции
-Функция СвернутьСтрокиБезХарактеристики(Строки)
+// #81: одна аналитика — номенклатура, характеристика, серия и ГТД.
+// Разный ГТД не схлопывается. Без серии строка остаётся отдельной:
+// пустая колонка серии не отличает разные карточки окна.
+//
+Функция СвернутьСтрокиОднойАналитики(Строки)
Результат = Новый Массив;
Индекс = Новый Соответствие;
Для Каждого СтрТ Из Строки Цикл
- Если ЗначениеЗаполнено(СтрТ.Характеристика) Или Не ЗначениеЗаполнено(СтрТ.СерияСсылка) Тогда
+ Если Не ЗначениеЗаполнено(СтрТ.СерияСсылка) Тогда
Результат.Добавить(СтрТ);
Продолжить;
КонецЕсли;
+ КлючХар = "";
+ Если ЗначениеЗаполнено(СтрТ.Характеристика) Тогда
+ КлючХар = Строка(СтрТ.Характеристика.УникальныйИдентификатор());
+ КонецЕсли;
КлючГТД = "";
Если ЗначениеЗаполнено(СтрТ.НомерГТДСсылка) Тогда
КлючГТД = Строка(СтрТ.НомерГТДСсылка.УникальныйИдентификатор());
КонецЕсли;
+ КлючНазначения = "";
+ Попытка
+ Если ЗначениеЗаполнено(СтрТ.НазначениеИдентификатор) Тогда
+ КлючНазначения = Строка(СтрТ.НазначениеИдентификатор);
+ КонецЕсли;
+ Исключение
+ КонецПопытки;
Ключ = Строка(СтрТ.Номенклатура.УникальныйИдентификатор())
+ + "|" + КлючХар
+ "|" + Строка(СтрТ.СерияСсылка.УникальныйИдентификатор())
- + "|" + КлючГТД;
+ + "|" + КлючГТД
+ + "|" + КлючНазначения;
Уже = Индекс.Получить(Ключ);
Если Уже = Неопределено Тогда
Индекс.Вставить(Ключ, СтрТ);
@@ -1524,6 +1593,7 @@
ГТДТекст = СокрЛП(Строка(ЗначениеСвойства(СтрокаAPI, "gtd_number", "")));
КонецЕсли;
НовСтр.ГТД = ГТДТекст;
+ НовСтр.ГТДСтранаКод = СокрЛП(Строка(ЗначениеСвойства(СтрокаAPI, "gtd_country_code", "")));
НовСтр.ОрганизацияИдентификатор = UUIDИзСтроки(ЗначениеСвойства(СтрокаAPI, "organization_guid", ""));
НовСтр.ОрганизацияНаименование = СокрЛП(Строка(ЗначениеСвойства(СтрокаAPI, "organization_name", "")));
@@ -1680,28 +1750,117 @@
КонецФункции
-Процедура ЗаполнитьНомерГТДСтроки(СтрТ)
+// Элементы НомераГТД обработка не создаёт. Пустой код даёт в документе «<>»:
+// представление справочника — код, а в списке виден регистрационный номер.
+// Из нескольких карточек одного номера берётся заполненная. Если код пуст,
+// в него пишется номер из остатка. Пустая страна берётся из кода страны исходной базы.
+//
+Процедура ЗаполнитьНомерГТДСтроки(СтрТ, РежимТест)
Если Не ЗначениеЗаполнено(СтрТ.ГТД) Тогда
Возврат;
КонецЕсли;
- КодГТД = СокрЛП(СтрТ.ГТД);
- СтрТ.НомерГТДСсылка = Справочники.НомераГТД.НайтиПоКоду(КодГТД);
- Если Не ЗначениеЗаполнено(СтрТ.НомерГТДСсылка) Тогда
- СтрТ.НомерГТДСсылка = Справочники.НомераГТД.НайтиПоРеквизиту("РегистрационныйНомер", КодГТД);
- КонецЕсли;
- Если Не ЗначениеЗаполнено(СтрТ.НомерГТДСсылка) Тогда
- СтрТ.НомерГТДСсылка = Справочники.НомераГТД.НайтиПоНаименованию(КодГТД, Истина);
- КонецЕсли;
- Если Не ЗначениеЗаполнено(СтрТ.НомерГТДСсылка) Тогда
- СсылкаГТД = СсылкаПоUUID(Справочники.НомераГТД, СтрТ.ГТДИдентификатор);
- Если ЗначениеЗаполнено(СсылкаГТД) И ОбщегоНазначения.СсылкаСуществует(СсылкаГТД) Тогда
- СтрТ.НомерГТДСсылка = СсылкаГТД;
- КонецЕсли;
- КонецЕсли;
+ СтрТ.НомерГТДСсылка = ПодобратьКарточкуГТД(СокрЛП(СтрТ.ГТД), СтрТ.ГТДИдентификатор,
+ СтрТ.ГТДСтранаКод, РежимТест);
КонецПроцедуры
+Функция ПодобратьКарточкуГТД(КодГТД, Идентификатор, КодСтраныИсточника, РежимТест)
+
+ СсылкаПоГуид = СсылкаПоUUID(Справочники.НомераГТД, Идентификатор);
+ Запрос = Новый Запрос;
+ Запрос.УстановитьПараметр("Номер", КодГТД);
+ Запрос.УстановитьПараметр("Ссылка", СсылкаПоГуид);
+ Запрос.Текст =
+ "ВЫБРАТЬ
+ | ГТД.Ссылка КАК Ссылка,
+ | ГТД.Код КАК Код,
+ | ГТД.РегистрационныйНомер КАК РегНомер,
+ | ГТД.СтранаПроисхождения КАК Страна
+ |ИЗ
+ | Справочник.НомераГТД КАК ГТД
+ |ГДЕ
+ | НЕ ГТД.ПометкаУдаления
+ | И (СОКРЛП(ГТД.Код) = &Номер
+ | ИЛИ ГТД.РегистрационныйНомер = &Номер
+ | ИЛИ ГТД.Ссылка = &Ссылка)";
+
+ Лучшая = Справочники.НомераГТД.ПустаяСсылка();
+ ЛучшийБалл = -1;
+ СтранаПолная = Справочники.СтраныМира.ПустаяСсылка();
+ Выборка = Запрос.Выполнить().Выбрать();
+ Пока Выборка.Следующий() Цикл
+ НомерСовпадает = СокрЛП(Выборка.Код) = КодГТД Или СокрЛП(Выборка.РегНомер) = КодГТД;
+ Если Не НомерСовпадает Тогда
+ Продолжить;
+ КонецЕсли;
+ Балл = 0;
+ Если ЗначениеЗаполнено(СокрЛП(Выборка.Код)) Тогда
+ Балл = Балл + 2;
+ КонецЕсли;
+ Если ЗначениеЗаполнено(Выборка.Страна) Тогда
+ Балл = Балл + 1;
+ Если Не ЗначениеЗаполнено(СтранаПолная) Тогда
+ СтранаПолная = Выборка.Страна;
+ КонецЕсли;
+ КонецЕсли;
+ Если Балл > ЛучшийБалл Тогда
+ ЛучшийБалл = Балл;
+ Лучшая = Выборка.Ссылка;
+ КонецЕсли;
+ КонецЦикла;
+
+ Если Не ЗначениеЗаполнено(Лучшая) Тогда
+ Возврат Справочники.НомераГТД.ПустаяСсылка();
+ КонецЕсли;
+
+ СтранаИсточника = СтранаМираПоКоду(КодСтраныИсточника);
+ Если ЗначениеЗаполнено(СтранаИсточника) Тогда
+ СтранаПолная = СтранаИсточника;
+ КонецЕсли;
+
+ Возврат ДополнитьПустуюКарточкуГТД(Лучшая, КодГТД, СтранаПолная, РежимТест);
+
+КонецФункции
+
+Функция СтранаМираПоКоду(КодСтраны)
+
+ Код = СокрЛП(КодСтраны);
+ Если ПустаяСтрока(Код) Тогда
+ Возврат Справочники.СтраныМира.ПустаяСсылка();
+ КонецЕсли;
+ Возврат Справочники.СтраныМира.НайтиПоКоду(Код);
+
+КонецФункции
+
+Функция ДополнитьПустуюКарточкуГТД(СсылкаГТД, КодГТД, СтранаПолная, РежимТест)
+
+ КодКарточки = СокрЛП(ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаГТД, "Код"));
+ СтранаКарточки = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаГТД, "СтранаПроисхождения");
+ НуженКод = Не ЗначениеЗаполнено(КодКарточки);
+ НужнаСтрана = Не ЗначениеЗаполнено(СтранаКарточки) И ЗначениеЗаполнено(СтранаПолная);
+ Если РежимТест Или (Не НуженКод И Не НужнаСтрана) Тогда
+ Возврат СсылкаГТД;
+ КонецЕсли;
+
+ ОбъектГТД = СсылкаГТД.ПолучитьОбъект();
+ Если НуженКод Тогда
+ ОбъектГТД.Код = КодГТД;
+ Если Не ЗначениеЗаполнено(ОбъектГТД.РегистрационныйНомер) Тогда
+ ОбъектГТД.РегистрационныйНомер = КодГТД;
+ КонецЕсли;
+ Если Не ЗначениеЗаполнено(ОбъектГТД.ТипНомераГТД) Тогда
+ ОбъектГТД.ТипНомераГТД = Перечисления.ТипыНомеровГТД.НомерГТД;
+ КонецЕсли;
+ КонецЕсли;
+ Если НужнаСтрана Тогда
+ ОбъектГТД.СтранаПроисхождения = СтранаПолная;
+ КонецЕсли;
+ ОбъектГТД.Записать();
+ Возврат ОбъектГТД.Ссылка;
+
+КонецФункции
+
// A4a (#24 comment-893/897): plain dev «Основное соглашение по договорам» (продажи) →
// индивидуальное соглашение комиссии Поварова в target (не типовое 1448bf7f:
// ввод на основании типового соглашения в Заказ/Реализация запрещён).