Загрузка данных
Ниже полный код без лишней процедуры и без лимита: «Выполнить» обработает все найденные позиции.
Код модуля формы
bsl
&НаКлиенте
Процедура ПроверитьКоманда(Команда)
Результат = ВыполнитьНаСервере(Истина);
Т = Новый ТекстовыйДокумент;
Т.УстановитьТекст(Результат);
Т.Показать("Проверка разбора");
КонецПроцедуры
&НаКлиенте
Процедура ВыполнитьКоманда(Команда)
ПоказатьВопрос(Новый ОписаниеОповещения("ПослеВопроса", ЭтотОбъект),
"Выполнить разбор номенклатуры?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВопроса(Ответ, Параметры) Экспорт
Если Ответ <> КодВозвратаДиалога.Да Тогда
Возврат;
КонецЕсли;
Результат = ВыполнитьНаСервере(Ложь);
Т = Новый ТекстовыйДокумент;
Т.УстановитьТекст(Результат);
Т.Показать("Результат разбора");
КонецПроцедуры
&НаСервереБезКонтекста
Функция ЕстьЦифра(Стр)
Для К = 1 По СтрДлина(Стр) Цикл
КодСим = КодСимвола(Сред(Стр, К, 1));
Если КодСим >= 48 И КодСим <= 57 Тогда
Возврат Истина;
КонецЕсли;
КонецЦикла;
Возврат Ложь;
КонецФункции
&НаСервереБезКонтекста
Функция РазобратьНаименование(Знач Наим, Причина)
Стр = СокрЛП(Наим);
Стр = СтрЗаменить(Стр, "\", "/");
Пока СтрНайти(Стр, "/ ") > 0 ИЛИ СтрНайти(Стр, " /") > 0 Цикл
Стр = СтрЗаменить(Стр, "/ ", "/");
Стр = СтрЗаменить(Стр, " /", "/");
КонецЦикла;
Токены = СтрРазделить(Стр, " ", Ложь);
Если Токены.Количество() < 2 Тогда
Причина = "нет префикса перед номерами";
Возврат Неопределено;
КонецЕсли;
Последний = Токены[Токены.ВГраница()];
Если СтрНайти(Последний, "/") = 0 Тогда
Причина = "в последнем слове нет «/», разделять нечего";
Возврат Неопределено;
КонецЕсли;
// Неоднозначность: ещё одна группа номеров через "/" не в конце названия
Для Н = 0 По Токены.ВГраница() - 1 Цикл
Токен = Токены[Н];
Если СтрНайти(Токен, "/") > 0 И СтрНайти(Токен, "*") = 0 Тогда
Части = СтрРазделить(Токен, "/", Ложь);
Если Части.Количество() >= 2 Тогда
ВсеНомера = Истина;
Для Каждого Часть Из Части Цикл
Если СтрДлина(Часть) < 7 ИЛИ НЕ ЕстьЦифра(Часть) Тогда
ВсеНомера = Ложь;
КонецЕсли;
КонецЦикла;
Если ВсеНомера Тогда
Причина = "неоднозначно: ещё одна группа номеров «" + Токен + "» не в конце названия";
Возврат Неопределено;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Номера = Новый Массив;
Уникальные = Новый Соответствие;
Для Каждого Элемент Из СтрРазделить(Последний, "/", Ложь) Цикл
Номер = СокрЛП(Элемент);
Если Номер = "" Тогда
Продолжить;
КонецЕсли;
Если НЕ ЕстьЦифра(Номер) Тогда
Причина = "часть «" + Номер + "» без цифр, похоже это не номер";
Возврат Неопределено;
КонецЕсли;
Если Уникальные[ВРег(Номер)] = Неопределено Тогда
Уникальные[ВРег(Номер)] = Истина;
Номера.Добавить(Номер);
КонецЕсли;
КонецЦикла;
Если Номера.Количество() < 2 Тогда
Причина = "меньше двух номеров";
Возврат Неопределено;
КонецЕсли;
ЧастиПрефикса = Новый Массив;
Для Н = 0 По Токены.ВГраница() - 1 Цикл
ЧастиПрефикса.Добавить(Токены[Н]);
КонецЦикла;
Результат = Новый Структура;
Результат.Вставить("Префикс", СтрСоединить(ЧастиПрефикса, " "));
Результат.Вставить("Номера", Номера);
Возврат Результат;
КонецФункции
&НаСервереБезКонтекста
Функция ЕстьОстатки(Номенклатура)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| О.Номенклатура КАК Номенклатура
|ИЗ
| РегистрНакопления.Запасы.Остатки(, Номенклатура = &Н) КАК О
|ГДЕ
| О.КоличествоОстаток <> 0";
Запрос.УстановитьПараметр("Н", Номенклатура);
Возврат НЕ Запрос.Выполнить().Пустой();
КонецФункции
&НаСервереБезКонтекста
Функция ЕстьШтрихкод(Номенклатура)
Запрос = Новый Запрос(
"ВЫБРАТЬ ПЕРВЫЕ 1 Ш.Штрихкод ИЗ РегистрСведений.Штрихкоды КАК Ш ГДЕ Ш.Владелец = &Н");
Запрос.УстановитьПараметр("Н", Номенклатура);
Возврат НЕ Запрос.Выполнить().Пустой();
КонецФункции
&НаСервереБезКонтекста
Процедура СоздатьШтрихкод(Номенклатура)
Запрос = Новый Запрос(
"ВЫБРАТЬ МАКСИМУМ(Ш.Штрихкод) КАК Макс ИЗ РегистрСведений.Штрихкоды КАК Ш ГДЕ Ш.Штрихкод ПОДОБНО ""2____________""");
Выб = Запрос.Выполнить().Выбрать();
Тело = 200000000001;
Если Выб.Следующий() И ЗначениеЗаполнено(Выб.Макс) Тогда
Попытка
Тело = Число(Лев(Выб.Макс, 12)) + 1;
Исключение
КонецПопытки;
КонецЕсли;
Стр = Формат(Тело, "ЧГ=0");
Сумма = 0;
Для К = 1 По 12 Цикл
Сумма = Сумма + Число(Сред(Стр, К, 1)) * ?(К % 2 = 0, 3, 1);
КонецЦикла;
ШК = Стр + Строка((10 - Сумма % 10) % 10);
Запись = РегистрыСведений.Штрихкоды.СоздатьМенеджерЗаписи();
Запись.Штрихкод = ШК;
Запись.Владелец = Номенклатура;
Запись.Записать();
КонецПроцедуры
&НаСервереБезКонтекста
Функция НайтиРегистрАналогов()
Для Каждого Рег Из Метаданные.РегистрыСведений Цикл
Если СтрНайти(НРег(Рег.Имя), "аналог") > 0
И Рег.Измерения.Найти("Номенклатура") <> Неопределено
И Рег.Измерения.Найти("Аналог") <> Неопределено Тогда
Возврат Рег.Имя;
КонецЕсли;
КонецЦикла;
Возврат Неопределено;
КонецФункции
&НаСервере
Функция ВыполнитьНаСервере(ТолькоПроверка)
МД = Метаданные.Справочники.Номенклатура;
Лог = Новый Массив;
Пропущено = Новый Массив;
Ошибки = Новый Массив;
ИмяРегАналогов = НайтиРегистрАналогов();
Если ИмяРегАналогов = Неопределено Тогда
Возврат "СТОП: не найден регистр сведений аналогов с измерениями Номенклатура и Аналог. "
+ "Пришлите список РегистрыСведений с «Аналог» в имени.";
КонецЕсли;
Если Метаданные.РегистрыНакопления.Найти("Запасы") = Неопределено Тогда
Возврат "СТОП: не найден регистр накопления «Запасы».";
КонецЕсли;
Если Метаданные.РегистрыСведений.Найти("Штрихкоды") = Неопределено Тогда
Возврат "СТОП: не найден регистр сведений «Штрихкоды».";
КонецЕсли;
Если МД.Реквизиты.Найти("Описание") = Неопределено ИЛИ МД.Реквизиты.Найти("Артикул") = Неопределено Тогда
Возврат "СТОП: у номенклатуры нет реквизита «Описание» или «Артикул».";
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Н.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Н
|ГДЕ
| НЕ Н.ЭтоГруппа
| И НЕ Н.ПометкаУдаления
| И (Н.Наименование ПОДОБНО ""%/%"" ИЛИ Н.Наименование ПОДОБНО ""%\%"")
|УПОРЯДОЧИТЬ ПО
| Н.Наименование";
Выборка = Запрос.Выполнить().Выбрать();
Обработано = 0;
Пока Выборка.Следующий() Цикл
Объект = Выборка.Ссылка.ПолучитьОбъект();
Исх = СокрЛП(Объект.Наименование);
Если СтрНайти(Объект.Описание, "[РАЗОБРАНО]") > 0 Тогда
Продолжить;
КонецЕсли;
Причина = "";
Р = РазобратьНаименование(Исх, Причина);
Если Р = Неопределено Тогда
Пропущено.Добавить(Исх + " <-- " + Причина);
Продолжить;
КонецЕсли;
НовыеНаим = Новый Массив;
Для Каждого Номер Из Р.Номера Цикл
НовыеНаим.Добавить(Р.Префикс + " " + Номер);
КонецЦикла;
Остатки = ЕстьОстатки(Выборка.Ссылка);
Лог.Добавить(Исх + Символы.ПС + " -> " + СтрСоединить(НовыеНаим, "; ") + Символы.ПС
+ " исходная: " + ?(Остатки, "ОСТАЁТСЯ (есть остаток)", "помечается на удаление"));
Если ТолькоПроверка Тогда
Продолжить;
КонецЕсли;
НачатьТранзакцию();
Попытка
Новые = Новый Массив;
Для Каждого Номер Из Р.Номера Цикл
НовНаим = Р.Префикс + " " + Номер;
Если СтрДлина(НовНаим) > МД.ДлинаНаименования Тогда
ВызватьИсключение "Название длиннее допустимых " + МД.ДлинаНаименования + " символов: " + НовНаим;
КонецЕсли;
Нов = Объект.Скопировать();
Нов.Код = "";
Нов.УстановитьНовыйКод();
Нов.ПометкаУдаления = Ложь;
Нов.Наименование = НовНаим;
Нов.Артикул = Номер;
Нов.Описание = Исх;
Для Каждого Рекв Из МД.Реквизиты Цикл
Син = НРег(Рекв.Синоним);
Если Рекв.Имя = "НаименованиеПолное" ИЛИ СтрНайти(Син, "для печати") > 0 ИЛИ СтрНайти(Син, "для сайт") > 0 Тогда
Если Рекв.Тип.СодержитТип(Тип("Строка")) Тогда
Нов[Рекв.Имя] = НовНаим;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Нов.Записать();
Новые.Добавить(Нов.Ссылка);
Если НЕ ЕстьШтрихкод(Нов.Ссылка) Тогда
СоздатьШтрихкод(Нов.Ссылка);
КонецЕсли;
КонецЦикла;
// Взаимные аналоги
Для Каждого Позиция Из Новые Цикл
Набор = РегистрыСведений[ИмяРегАналогов].СоздатьНаборЗаписей();
Набор.Отбор.Номенклатура.Установить(Позиция);
Для Каждого Аналог Из Новые Цикл
Если Аналог <> Позиция Тогда
З = Набор.Добавить();
З.Номенклатура = Позиция;
З.Аналог = Аналог;
КонецЕсли;
КонецЦикла;
Набор.Записать();
КонецЦикла;
// Исходная позиция
Если Остатки Тогда
Объект.Описание = СокрЛП(Объект.Описание + Символы.ПС + "[РАЗОБРАНО] есть остаток, разнесено на отдельные позиции");
Объект.Записать();
Иначе
Объект.УстановитьПометкуУдаления(Истина);
КонецЕсли;
ЗафиксироватьТранзакцию();
Обработано = Обработано + 1;
Исключение
ОтменитьТранзакцию();
Ошибки.Добавить(Исх + Символы.ПС + " ОШИБКА: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецЦикла;
Итог = Новый Массив;
Итог.Добавить(?(ТолькоПроверка, "РЕЖИМ ПРОВЕРКИ (ничего не записано)", "ВЫПОЛНЕНО"));
Итог.Добавить("Регистр аналогов: " + ИмяРегАналогов);
Итог.Добавить("Будет разобрано / разобрано позиций: " + ?(ТолькоПроверка, Лог.Количество(), Обработано));
Итог.Добавить("Пропущено (нужно вручную): " + Пропущено.Количество());
Итог.Добавить("Ошибок: " + Ошибки.Количество());
Итог.Добавить("");
Если Ошибки.Количество() > 0 Тогда
Итог.Добавить("=== ОШИБКИ ===");
Итог.Добавить(СтрСоединить(Ошибки, Символы.ПС));
Итог.Добавить("");
КонецЕсли;
Итог.Добавить("=== РАЗБОР ===");
Итог.Добавить(СтрСоединить(Лог, Символы.ПС));
Итог.Добавить("");
Итог.Добавить("=== ПРОПУЩЕНО ===");
Итог.Добавить(СтрСоединить(Пропущено, Символы.ПС));
Возврат СтрСоединить(Итог, Символы.ПС);
КонецФункции
Что делать
Создать обработку. В конфигураторе: Файл → Новый → Внешняя обработка, имя РазборНоменклатуры.
Форма. Вкладка «Формы» → Добавить → Готово.
Команды. В редакторе формы на вкладке «Команды» добавьте две команды: ПроверитьКоманда (действие ПроверитьКоманда) и ВыполнитьКоманда (действие ВыполнитьКоманда). Перетащите обе в список элементов формы слева сверху, чтобы они появились кнопками.
Код. Вкладка «Модуль» формы: удалите всё, что там есть, и вставьте код выше целиком. Сохраните как РазборНоменклатуры.epf.
Запуск на копии базы. Откройте копию под пользователем с полными правами: ≡ → Файл → Открыть → выберите epf.
Проверить. Нажмите «Проверить». Откроется текст «было → станет», решение по каждой исходной позиции (остаётся или на удаление) и список пропущенных. Ничего не записывается.
Выполнить. Нажмите «Выполнить». По завершении откроется отчёт: сколько разобрано, ошибки, пропущенные.
Проверить результат. Откройте одну из групп: карточки новых позиций, штрихкод, описание, вкладка «Аналоги». Найдите любой из номеров в списке номенклатуры и в подборе заказа.
Позиции из блока «Пропущено» (например, фильтр трансмиссии с номерами через пробел) нужно поправить вручную. Помеченные на удаление исходные позиции удаляются через «Администрирование → Удаление помеченных объектов».
Если на копии выдаст «СТОП» или «ОШИБКА», или поиск по одному номеру не найдёт остальные позиции группы, пришлите текст отчёта или скриншот.