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