diff --git a/CHANGELOG.md b/CHANGELOG.md
index 652297b..57b068d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,15 @@
---
+## 1.3.1.71 (2026-08-17)
+
+### РК_ПроверкаСерий — объединение дублей с учётом остатков (#15)
+
+- Вкладка **Объединение дублей**: поиск групп (вид + номер + срок + дата производства).
+- Остатки из `ДвиженияСерийТоваров.Остатки`; предупреждение при остатках у нескольких серий или у дублей.
+- Группы без остатков отмечаются автоматически; с остатками — только вручную + подтверждение перед записью.
+- Режим тест; объединение через `ОбщегоНазначения.ЗаменитьСсылки`.
+
## 1.3.1.70 (2026-08-17)
### РК_ПроверкаСерий — проверка и исправление серий (#14)
diff --git a/VERSION b/VERSION
index 3e96bb9..f227c14 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.3.1.70
+1.3.1.71
diff --git a/src/ConfigDumpInfo.xml b/src/ConfigDumpInfo.xml
index 611e878..2511448 100644
--- a/src/ConfigDumpInfo.xml
+++ b/src/ConfigDumpInfo.xml
@@ -216,6 +216,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Configuration.xml b/src/Configuration.xml
index e7228f8..1370579 100644
--- a/src/Configuration.xml
+++ b/src/Configuration.xml
@@ -62,7 +62,7 @@
Role.РК_ОсновнаяРоль
- 1.3.1.70
+ 1.3.1.71
Language.Русский
diff --git a/src/DataProcessors/РК_ПроверкаСерий.xml b/src/DataProcessors/РК_ПроверкаСерий.xml
index e34ed80..f974eb9 100644
--- a/src/DataProcessors/РК_ПроверкаСерий.xml
+++ b/src/DataProcessors/РК_ПроверкаСерий.xml
@@ -591,6 +591,592 @@
+
+
+
+ f14e0020-0020-4001-8000-000000000020
+ f14e0020-0020-4002-8000-000000000020
+
+
+ f14e0020-0020-4003-8000-000000000020
+ f14e0020-0020-4004-8000-000000000020
+
+
+
+ ГруппыДублей
+
+
+ ru
+ Группы дублей
+
+
+
+
+ DontCheck
+
+
+
+
+ Отметка
+
+
+ ru
+ Отметка
+
+
+
+ xs:boolean
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+ <
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
+
+
+ ИдентификаторГруппы
+
+
+ ru
+ № группы
+
+
+
+ xs:decimal100Any
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+ <
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
+
+
+ ВидНоменклатуры
+
+
+ ru
+ Вид номенклатуры
+
+
+
+ cfg:CatalogRef.ВидыНоменклатуры
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+ <
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
+
+
+ Номер
+
+
+ ru
+ Номер
+
+
+
+ xs:string200Variable
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+ <
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
+
+
+ ГоденДо
+
+
+ ru
+ Годен до
+
+
+
+ xs:dateTimeDateTime
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+ <
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
+
+
+ ДатаПроизводства
+
+
+ ru
+ Дата производства
+
+
+
+ xs:dateTimeDateTime
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+ <
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
+
+
+ СерияМастер
+
+
+ ru
+ Серия-мастер
+
+
+
+ cfg:CatalogRef.СерииНоменклатуры
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+ <
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
+
+
+ КоличествоСерий
+
+
+ ru
+ Кол-во серий
+
+
+
+ xs:decimal100Any
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+ <
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
+
+
+ КоличествоЗамен
+
+
+ ru
+ К замене
+
+
+
+ xs:decimal100Any
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+ <
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
+
+
+ ОстатокМастер
+
+
+ ru
+ Остаток мастер
+
+
+
+ xs:decimal153Any
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+ <
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
+
+
+ ОстатокДублей
+
+
+ ru
+ Остаток дублей
+
+
+
+ xs:decimal153Any
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+ <
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
+
+
+ СерийСОстатком
+
+
+ ru
+ Серий с остатком
+
+
+
+ xs:decimal100Any
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+ <
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
+
+
+ Предупреждение
+
+
+ ru
+ Предупреждение
+
+
+
+ xs:string200Variable
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+ <
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
+
+
+ ДеталиОстатков
+
+
+ ru
+ Детали остатков
+
+
+
+ xs:string0Variable
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+ <
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
+
+
+ Объединено
+
+
+ ru
+ Объединено
+
+
+
+ xs:boolean
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+ <
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
+
+
+ Комментарий
+
+
+ ru
+ Комментарий
+
+
+
+ xs:string0Variable
+
+ false
+
+
+
+ false
+
+ false
+ false
+
+
+ false
+ <
+ DontCheck
+ Items
+
+
+ Auto
+ Auto
+
+
+ Auto
+
+
+
+
diff --git a/src/DataProcessors/РК_ПроверкаСерий/Forms/Форма/Ext/Form.xml b/src/DataProcessors/РК_ПроверкаСерий/Forms/Форма/Ext/Form.xml
index 48f07dd..a853adf 100644
--- a/src/DataProcessors/РК_ПроверкаСерий/Forms/Форма/Ext/Form.xml
+++ b/src/DataProcessors/РК_ПроверкаСерий/Forms/Форма/Ext/Form.xml
@@ -6,21 +6,7 @@
Проверка и исправление серий номенклатуры
-
-
-
-
-
-
+
ПриСозданииНаСервере
@@ -39,6 +25,24 @@
+
+
+
+ ru
+ Группа страницы
+
+
+
+
+
+
+
+ ru
+ Заполнение реквизитов
+
+
+
+
List
false
@@ -65,6 +69,17 @@
+
+
+
+
+
+
+
+ ru
+ Объединение дублей
+
+
+
+
+
+ List
+ false
+ false
+ false
+ false
+ false
+ Объект.ГруппыДублей
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - ГруппыДублей
+ SearchStringRepresentation
+
+
+
+
+
+
+ - ГруппыДублей
+ ViewStatusRepresentation
+
+
+
+
+
+
+ - ГруппыДублей
+ SearchControl
+
+
+
+
+
+
+ Объект.ГруппыДублей.Отметка
+ EnterOnInput
+ Auto
+
+
+
+
+ Объект.ГруппыДублей.ИдентификаторГруппы
+ true
+ EnterOnInput
+ 4
+
+
+
+
+ Объект.ГруппыДублей.СерияМастер
+ true
+ EnterOnInput
+ true
+
+
+
+
+ Объект.ГруппыДублей.Предупреждение
+ true
+ EnterOnInput
+ style:SpecialTextColor
+
+
+
+
+ Объект.ГруппыДублей.ВидНоменклатуры
+ true
+ EnterOnInput
+
+
+
+
+ Объект.ГруппыДублей.Номер
+ true
+ EnterOnInput
+
+
+
+
+ Объект.ГруппыДублей.ГоденДо
+ true
+ EnterOnInput
+
+
+
+
+ Объект.ГруппыДублей.ДатаПроизводства
+ true
+ EnterOnInput
+
+
+
+
+ Объект.ГруппыДублей.КоличествоСерий
+ true
+ EnterOnInput
+ 6
+
+
+
+
+ Объект.ГруппыДублей.КоличествоЗамен
+ true
+ EnterOnInput
+ 6
+
+
+
+
+ Объект.ГруппыДублей.ОстатокМастер
+ true
+ EnterOnInput
+ 8
+
+
+
+
+ Объект.ГруппыДублей.ОстатокДублей
+ true
+ EnterOnInput
+ 8
+
+
+
+
+ Объект.ГруппыДублей.СерийСОстатком
+ true
+ EnterOnInput
+ 6
+
+
+
+
+ Объект.ГруппыДублей.ДеталиОстатков
+ true
+ EnterOnInput
+ 2
+
+
+
+
+ Объект.ГруппыДублей.Объединено
+ true
+ EnterOnInput
+ Auto
+
+
+
+
+ Объект.ГруппыДублей.Комментарий
+ true
+ EnterOnInput
+
+
+
+
+
+
+
+
+
@@ -269,5 +498,69 @@
СнятьОтметку
+
+
+
+ ru
+ Найти дубли
+
+
+
+
+ ru
+ Найти группы дублей по виду, номеру, сроку и дате производства
+
+
+
+ StdPicture.Find
+ true
+
+ НайтиДубли
+
+
+
+
+ ru
+ Объединить отмеченные
+
+
+
+
+ ru
+ Заменить ссылки дублей на серию-мастер (с учётом остатков)
+
+
+
+ StdPicture.Write
+ true
+
+ ОбъединитьГруппы
+
+
+
+
+ ru
+ Отметить
+
+
+
+ StdPicture.CheckAll
+ true
+
+ ОтметитьГруппыДублей
+
+
+
+
+ ru
+ Снять отметку
+
+
+
+ StdPicture.UncheckAll
+ true
+
+ СнятьОтметкуГруппыДублей
+
diff --git a/src/DataProcessors/РК_ПроверкаСерий/Forms/Форма/Ext/Form/Module.bsl b/src/DataProcessors/РК_ПроверкаСерий/Forms/Форма/Ext/Form/Module.bsl
index 6b53a76..b343814 100644
--- a/src/DataProcessors/РК_ПроверкаСерий/Forms/Форма/Ext/Form/Module.bsl
+++ b/src/DataProcessors/РК_ПроверкаСерий/Forms/Форма/Ext/Form/Module.bsl
@@ -1,4 +1,4 @@
-// Проверка и исправление данных Справочник.СерииНоменклатуры (#14).
+// Проверка и исправление данных Справочник.СерииНоменклатуры (#14, #15).
// Логика заполнения номера/срока из наименования — как docs/transfer/Код_исправления_серий_crm3-26.bsl.
#Область ОбработчикиСобытийФормы
@@ -83,12 +83,94 @@
&НаКлиенте
Процедура Отметить(Команда)
- УстановитьОтметкуВыделенныхСтрок(Истина);
+ УстановитьОтметкуВыделенныхСтрок("ПроблемныеСерии", Истина);
КонецПроцедуры
&НаКлиенте
Процедура СнятьОтметку(Команда)
- УстановитьОтметкуВыделенныхСтрок(Ложь);
+ УстановитьОтметкуВыделенныхСтрок("ПроблемныеСерии", Ложь);
+КонецПроцедуры
+
+&НаКлиенте
+Процедура НайтиДубли(Команда)
+
+ Попытка
+ Количество = НайтиДублиНаСервере();
+ Исключение
+ ПоказатьПредупреждение(, КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
+ Возврат;
+ КонецПопытки;
+ Элементы.ГруппаСтраницы.ТекущаяСтраница = Элементы.СтраницаДубли;
+ ПоказатьОповещениеПользователя("Дубли серий", ,
+ "Найдено групп: " + Формат(Количество, "ЧН=0; ЧГ=0"));
+
+КонецПроцедуры
+
+&НаКлиенте
+Процедура ОбъединитьГруппы(Команда)
+
+ Если Объект.ГруппыДублей.Количество() = 0 Тогда
+ ПоказатьПредупреждение(, "Сначала выполните поиск дублей");
+ Возврат;
+ КонецЕсли;
+
+ Если Не ЕстьОтмеченныеГруппыДублей() Тогда
+ ПоказатьПредупреждение(, "Отметьте группы для объединения");
+ Возврат;
+ КонецЕсли;
+
+ Если Не Объект.РежимТест Тогда
+ ТекстПредупреждения = ТекстПредупрежденияОбОстаткахНаСервере();
+ ТекстВопроса = "Объединить отмеченные группы дублей (замена ссылок на серию-мастер)?";
+ Если ЗначениеЗаполнено(ТекстПредупреждения) Тогда
+ ТекстВопроса = ТекстПредупреждения + Символы.ПС + Символы.ПС + ТекстВопроса;
+ КонецЕсли;
+ Описание = Новый ОписаниеОповещения("ОбъединитьГруппыПослеВопроса", ЭтотОбъект);
+ ПоказатьВопрос(Описание, ТекстВопроса, РежимДиалогаВопрос.ДаНет);
+ Возврат;
+ КонецЕсли;
+
+ ВыполнитьОбъединениеНаКлиенте();
+
+КонецПроцедуры
+
+&НаКлиенте
+Процедура ОбъединитьГруппыПослеВопроса(РезультатВопроса, ДопПараметры) Экспорт
+
+ Если РезультатВопроса <> КодВозвратаДиалога.Да Тогда
+ Возврат;
+ КонецЕсли;
+ ВыполнитьОбъединениеНаКлиенте();
+
+КонецПроцедуры
+
+&НаКлиенте
+Процедура ВыполнитьОбъединениеНаКлиенте()
+
+ Попытка
+ Результат = ОбъединитьОтмеченныеГруппыНаСервере();
+ Исключение
+ ПоказатьПредупреждение(, КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
+ Возврат;
+ КонецПопытки;
+
+ Текст = СтрШаблон("Объединено ссылок: %1; групп: %2; ошибок: %3%4",
+ Формат(Результат.ОбъединеноСсылок, "ЧН=0; ЧГ=0"),
+ Формат(Результат.ОбъединеноГрупп, "ЧН=0; ЧГ=0"),
+ Формат(Результат.Ошибок, "ЧН=0; ЧГ=0"),
+ ?(Объект.РежимТест, " (тест)", ""));
+ ПоказатьОповещениеПользователя("Дубли серий", , Текст);
+
+КонецПроцедуры
+
+&НаКлиенте
+Процедура ОтметитьГруппыДублей(Команда)
+ УстановитьОтметкуВыделенныхСтрок("ГруппыДублей", Истина);
+КонецПроцедуры
+
+&НаКлиенте
+Процедура СнятьОтметкуГруппыДублей(Команда)
+ УстановитьОтметкуВыделенныхСтрок("ГруппыДублей", Ложь);
КонецПроцедуры
#КонецОбласти
@@ -108,9 +190,10 @@
КонецФункции
&НаКлиенте
-Процедура УстановитьОтметкуВыделенныхСтрок(Отметка)
+Процедура УстановитьОтметкуВыделенныхСтрок(ИмяТаблицы, Отметка)
- ТаблицаФормы = Элементы.ПроблемныеСерии;
+ ТаблицаФормы = Элементы[ИмяТаблицы];
+ ДанныеТаблицы = Объект[ИмяТаблицы];
Выделенные = ТаблицаФормы.ВыделенныеСтроки;
Если Выделенные.Количество() = 0 Тогда
ПоказатьПредупреждение(, "Выделите строки в таблице");
@@ -118,7 +201,7 @@
КонецЕсли;
Для Каждого ИдентификаторСтроки Из Выделенные Цикл
- СтрокаТЗ = Объект.ПроблемныеСерии.НайтиПоИдентификатору(ИдентификаторСтроки);
+ СтрокаТЗ = ДанныеТаблицы.НайтиПоИдентификатору(ИдентификаторСтроки);
Если СтрокаТЗ <> Неопределено Тогда
СтрокаТЗ.Отметка = Отметка;
КонецЕсли;
@@ -126,6 +209,18 @@
КонецПроцедуры
+&НаКлиенте
+Функция ЕстьОтмеченныеГруппыДублей()
+
+ Для Каждого Стр Из Объект.ГруппыДублей Цикл
+ Если Стр.Отметка И Не Стр.Объединено Тогда
+ Возврат Истина;
+ КонецЕсли;
+ КонецЦикла;
+ Возврат Ложь;
+
+КонецФункции
+
&НаСервере
Функция НайтиПроблемыНаСервере()
@@ -387,4 +482,293 @@
КонецФункции
+&НаСервере
+Функция НайтиДублиНаСервере()
+
+ Объект.ГруппыДублей.Очистить();
+
+ ЗапросГруппы = Новый Запрос;
+ ЗапросГруппы.Текст =
+ "ВЫБРАТЬ
+ | Сер.ВидНоменклатуры КАК ВидНоменклатуры,
+ | Сер.Номер КАК Номер,
+ | Сер.ГоденДо КАК ГоденДо,
+ | Сер.ДатаПроизводства КАК ДатаПроизводства,
+ | КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Сер.Ссылка) КАК КоличествоСерий
+ |ИЗ
+ | Справочник.СерииНоменклатуры КАК Сер
+ |ГДЕ
+ | НЕ Сер.ПометкаУдаления
+ |СГРУППИРОВАТЬ ПО
+ | Сер.ВидНоменклатуры,
+ | Сер.Номер,
+ | Сер.ГоденДо,
+ | Сер.ДатаПроизводства
+ |ИМЕЮЩИЕ
+ | КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Сер.Ссылка) > 1";
+
+ ВыбГруппы = ЗапросГруппы.Выполнить().Выбрать();
+ НомерГруппы = 0;
+
+ Пока ВыбГруппы.Следующий() Цикл
+
+ Мастер = НайтиСериюМастерГруппы(
+ ВыбГруппы.ВидНоменклатуры,
+ ВыбГруппы.Номер,
+ ВыбГруппы.ГоденДо,
+ ВыбГруппы.ДатаПроизводства);
+ Если Не ЗначениеЗаполнено(Мастер) Тогда
+ Продолжить;
+ КонецЕсли;
+
+ СерииГруппы = СерииГруппыДублей(
+ ВыбГруппы.ВидНоменклатуры,
+ ВыбГруппы.Номер,
+ ВыбГруппы.ГоденДо,
+ ВыбГруппы.ДатаПроизводства);
+ Если СерииГруппы.Количество() < 2 Тогда
+ Продолжить;
+ КонецЕсли;
+
+ Остатки = ОстаткиПоСериям(СерииГруппы);
+ НомерГруппы = НомерГруппы + 1;
+ ДобавитьСтрокуГруппыДублей(НомерГруппы, ВыбГруппы, Мастер, СерииГруппы, Остатки);
+
+ КонецЦикла;
+
+ Возврат Объект.ГруппыДублей.Количество();
+
+КонецФункции
+
+&НаСервере
+Функция НайтиСериюМастерГруппы(Вид, Номер, ГоденДо, ДатаПроизводства)
+
+ Запрос = Новый Запрос;
+ Запрос.Текст =
+ "ВЫБРАТЬ ПЕРВЫЕ 1
+ | Сер.Ссылка КАК Ссылка
+ |ИЗ
+ | Справочник.СерииНоменклатуры КАК Сер
+ | ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ДвиженияСерийТоваров КАК Дв
+ | ПО Дв.Серия = Сер.Ссылка
+ |ГДЕ
+ | Сер.ВидНоменклатуры = &Вид
+ | И Сер.Номер = &Номер
+ | И Сер.ГоденДо = &ГоденДо
+ | И Сер.ДатаПроизводства = &ДатаПроизводства
+ | И НЕ Сер.ПометкаУдаления
+ |СГРУППИРОВАТЬ ПО
+ | Сер.Ссылка
+ |УПОРЯДОЧИТЬ ПО
+ | КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Дв.Регистратор) УБЫВ,
+ | Сер.Ссылка";
+ Запрос.УстановитьПараметр("Вид", Вид);
+ Запрос.УстановитьПараметр("Номер", Номер);
+ Запрос.УстановитьПараметр("ГоденДо", ГоденДо);
+ Запрос.УстановитьПараметр("ДатаПроизводства", ДатаПроизводства);
+
+ Выборка = Запрос.Выполнить().Выбрать();
+ Если Выборка.Следующий() Тогда
+ Возврат Выборка.Ссылка;
+ КонецЕсли;
+ Возврат Справочники.СерииНоменклатуры.ПустаяСсылка();
+
+КонецФункции
+
+&НаСервере
+Функция СерииГруппыДублей(Вид, Номер, ГоденДо, ДатаПроизводства)
+
+ Запрос = Новый Запрос;
+ Запрос.Текст =
+ "ВЫБРАТЬ
+ | Сер.Ссылка КАК Серия
+ |ИЗ
+ | Справочник.СерииНоменклатуры КАК Сер
+ |ГДЕ
+ | Сер.ВидНоменклатуры = &Вид
+ | И Сер.Номер = &Номер
+ | И Сер.ГоденДо = &ГоденДо
+ | И Сер.ДатаПроизводства = &ДатаПроизводства
+ | И НЕ Сер.ПометкаУдаления";
+ Запрос.УстановитьПараметр("Вид", Вид);
+ Запрос.УстановитьПараметр("Номер", Номер);
+ Запрос.УстановитьПараметр("ГоденДо", ГоденДо);
+ Запрос.УстановитьПараметр("ДатаПроизводства", ДатаПроизводства);
+
+ Результат = Новый Массив;
+ Выборка = Запрос.Выполнить().Выбрать();
+ Пока Выборка.Следующий() Цикл
+ Результат.Добавить(Выборка.Серия);
+ КонецЦикла;
+ Возврат Результат;
+
+КонецФункции
+
+&НаСервере
+Функция ОстаткиПоСериям(Серии)
+
+ Результат = Новый Соответствие;
+ Если Серии.Количество() = 0 Тогда
+ Возврат Результат;
+ КонецЕсли;
+
+ Запрос = Новый Запрос;
+ Запрос.Текст =
+ "ВЫБРАТЬ
+ | Ост.Серия КАК Серия,
+ | СУММА(Ост.КоличествоОстаток) КАК Количество
+ |ИЗ
+ | РегистрНакопления.ДвиженияСерийТоваров.Остатки(, Серия В (&Серии)) КАК Ост
+ |СГРУППИРОВАТЬ ПО
+ | Ост.Серия";
+ Запрос.УстановитьПараметр("Серии", Серии);
+
+ Выборка = Запрос.Выполнить().Выбрать();
+ Пока Выборка.Следующий() Цикл
+ Результат.Вставить(Выборка.Серия, Выборка.Количество);
+ КонецЦикла;
+ Возврат Результат;
+
+КонецФункции
+
+&НаСервере
+Процедура ДобавитьСтрокуГруппыДублей(НомерГруппы, ДанныеГруппы, Мастер, СерииГруппы, Остатки)
+
+ ОстатокМастер = ОстатокСерииИзСоответствия(Остатки, Мастер);
+ ОстатокДублей = 0;
+ СерийСОстатком = 0;
+ Детали = Новый Массив;
+ КоличествоЗамен = 0;
+
+ Для Каждого Серия Из СерииГруппы Цикл
+ Остаток = ОстатокСерииИзСоответствия(Остатки, Серия);
+ Если Остаток > 0.000001 Тогда
+ СерийСОстатком = СерийСОстатком + 1;
+ КонецЕсли;
+ Если Серия <> Мастер Тогда
+ КоличествоЗамен = КоличествоЗамен + 1;
+ ОстатокДублей = ОстатокДублей + Остаток;
+ КонецЕсли;
+ Префикс = ?(Серия = Мастер, "Мастер", "Дубль");
+ Детали.Добавить(СтрШаблон("%1 %2: %3",
+ Префикс, Серия, Формат(Остаток, "ЧН=0; ЧГ=0; ЧДЦ=3")));
+ КонецЦикла;
+
+ Предупреждение = "";
+ Если СерийСОстатком > 1 Тогда
+ Предупреждение = "Остатки у " + Формат(СерийСОстатком, "ЧН=0; ЧГ=0") + " серий группы";
+ ИначеЕсли ОстатокДублей > 0.000001 Тогда
+ Предупреждение = "У дублей есть остатки → перенос на мастер";
+ КонецЕсли;
+
+ НовСтр = Объект.ГруппыДублей.Добавить();
+ НовСтр.Отметка = ПустаяСтрока(Предупреждение);
+ НовСтр.ИдентификаторГруппы = НомерГруппы;
+ НовСтр.ВидНоменклатуры = ДанныеГруппы.ВидНоменклатуры;
+ НовСтр.Номер = ДанныеГруппы.Номер;
+ НовСтр.ГоденДо = ДанныеГруппы.ГоденДо;
+ НовСтр.ДатаПроизводства = ДанныеГруппы.ДатаПроизводства;
+ НовСтр.СерияМастер = Мастер;
+ НовСтр.КоличествоСерий = СерииГруппы.Количество();
+ НовСтр.КоличествоЗамен = КоличествоЗамен;
+ НовСтр.ОстатокМастер = ОстатокМастер;
+ НовСтр.ОстатокДублей = ОстатокДублей;
+ НовСтр.СерийСОстатком = СерийСОстатком;
+ НовСтр.Предупреждение = Предупреждение;
+ НовСтр.ДеталиОстатков = СтрСоединить(Детали, "; ");
+
+КонецПроцедуры
+
+&НаСервере
+Функция ОстатокСерииИзСоответствия(Остатки, Серия)
+
+ Остаток = Остатки.Получить(Серия);
+ Если Остаток = Неопределено Тогда
+ Возврат 0;
+ КонецЕсли;
+ Возврат Остаток;
+
+КонецФункции
+
+&НаСервере
+Функция ТекстПредупрежденияОбОстаткахНаСервере()
+
+ Строки = Новый Массив;
+ Для Каждого Стр Из Объект.ГруппыДублей Цикл
+ Если Не Стр.Отметка Или Стр.Объединено Тогда
+ Продолжить;
+ КонецЕсли;
+ Если Не ЗначениеЗаполнено(Стр.Предупреждение) Тогда
+ Продолжить;
+ КонецЕсли;
+ Строки.Добавить(СтрШаблон("Группа %1: %2 (%3)",
+ Формат(Стр.ИдентификаторГруппы, "ЧН=0; ЧГ=0"),
+ Стр.Предупреждение,
+ Стр.ДеталиОстатков));
+ КонецЦикла;
+ Если Строки.Количество() = 0 Тогда
+ Возврат "";
+ КонецЕсли;
+ Возврат "Внимание: у отмеченных групп есть остатки по сериям:" + Символы.ПС
+ + СтрСоединить(Строки, Символы.ПС);
+
+КонецФункции
+
+&НаСервере
+Функция ОбъединитьОтмеченныеГруппыНаСервере()
+
+ Результат = Новый Структура("ОбъединеноСсылок, ОбъединеноГрупп, Ошибок", 0, 0, 0);
+
+ Для Каждого СтрГруппа Из Объект.ГруппыДублей Цикл
+
+ Если Не СтрГруппа.Отметка Или СтрГруппа.Объединено Тогда
+ Продолжить;
+ КонецЕсли;
+
+ СтрГруппа.Комментарий = "";
+ Если Не ЗначениеЗаполнено(СтрГруппа.СерияМастер) Тогда
+ СтрГруппа.Комментарий = "Не определена серия-мастер";
+ Результат.Ошибок = Результат.Ошибок + 1;
+ Продолжить;
+ КонецЕсли;
+
+ Заменяемые = Новый Соответствие;
+ СерииГруппы = СерииГруппыДублей(
+ СтрГруппа.ВидНоменклатуры,
+ СтрГруппа.Номер,
+ СтрГруппа.ГоденДо,
+ СтрГруппа.ДатаПроизводства);
+ Для Каждого Серия Из СерииГруппы Цикл
+ Если Серия <> СтрГруппа.СерияМастер Тогда
+ Заменяемые.Вставить(Серия, СтрГруппа.СерияМастер);
+ КонецЕсли;
+ КонецЦикла;
+
+ Если Заменяемые.Количество() = 0 Тогда
+ СтрГруппа.Комментарий = "Нет серий для замены";
+ Продолжить;
+ КонецЕсли;
+
+ Попытка
+ Если Объект.РежимТест Тогда
+ СтрГруппа.Объединено = Истина;
+ Результат.ОбъединеноСсылок = Результат.ОбъединеноСсылок + Заменяемые.Количество();
+ Результат.ОбъединеноГрупп = Результат.ОбъединеноГрупп + 1;
+ Иначе
+ ОбщегоНазначения.ЗаменитьСсылки(Заменяемые);
+ СтрГруппа.Объединено = Истина;
+ Результат.ОбъединеноСсылок = Результат.ОбъединеноСсылок + Заменяемые.Количество();
+ Результат.ОбъединеноГрупп = Результат.ОбъединеноГрупп + 1;
+ КонецЕсли;
+ Исключение
+ СтрГруппа.Комментарий = КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
+ Результат.Ошибок = Результат.Ошибок + 1;
+ КонецПопытки;
+
+ КонецЦикла;
+
+ Возврат Результат;
+
+КонецФункции
+
#КонецОбласти