From b9655b79f785e40d77497eba84bea2278c629fb8 Mon Sep 17 00:00:00 2001 From: mihailkudravcev Date: Tue, 13 Oct 2026 14:41:35 +0300 Subject: [PATCH] =?UTF-8?q?1.3.1.133:=20=D0=BE=D0=BA=D0=BD=D0=BE=20=D1=81?= =?UTF-8?q?=D0=B5=D1=80=D0=B8=D0=B9=20=D0=B8=20=D1=81=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D0=BA=D0=B8=20=D0=BE=D0=B4=D0=BD=D0=BE=D0=B9=20=D0=B0=D0=BD?= =?UTF-8?q?=D0=B0=D0=BB=D0=B8=D1=82=D0=B8=D0=BA=D0=B8=20=D1=81=D1=83=D0=BC?= =?UTF-8?q?=D0=BC=D0=B8=D1=80=D1=83=D1=8E=D1=82=D1=81=D1=8F,=20=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=B0=D0=BD=D0=B0=20=D0=93=D0=A2=D0=94=20=D0=B8?= =?UTF-8?q?=D0=B7=20=D0=BE=D1=81=D1=82=D0=B0=D1=82=D0=BA=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проведение склада берёт максимум строки серии (#36). Строки с одной номенклатурой, характеристикой, серией и ГТД складываются до записи (#81). Пустая карточка ГТД получает код и страну из данных загрузки (#80). Co-authored-by: Cursor --- CHANGELOG.md | 9 + VERSION | 2 +- src/ConfigDumpInfo.xml | 4 +- src/Configuration.xml | 2 +- src/DataProcessors/РК_ЗагрузкаОстатков.xml | 80 +++++++ .../РК_ЗагрузкаОстатков/Ext/ObjectModule.bsl | 203 ++++++++++++++++-- 6 files changed, 275 insertions(+), 25 deletions(-) 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: // ввод на основании типового соглашения в Заказ/Реализация запрещён).