Загрузка данных


&НаКлиенте
Процедура ПроверитьКоманда(Команда)
	Результат = ВыполнитьНаСервере(Истина);
	Т = Новый ТекстовыйДокумент;
	Т.УстановитьТекст(Результат);
	Т.Показать("Проверка разбора");
КонецПроцедуры

&НаКлиенте
Процедура ВыполнитьКоманда(Команда)
	ПоказатьВопрос(Новый ОписаниеОповещения("ПослеВопроса", ЭтотОбъект),
		"Выполнить разбор номенклатуры?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры

&НаКлиенте
Процедура ПослеВопроса(Ответ, Параметры) Экспорт
	Если Ответ <> КодВозвратаДиалога.Да Тогда
		Возврат;
	КонецЕсли;
	Результат = ВыполнитьНаСервере(Ложь);
	Т = Новый ТекстовыйДокумент;
	Т.УстановитьТекст(Результат);
	Т.Показать("Результат разбора");
КонецПроцедуры

&НаСервереБезКонтекста
Функция ЕстьЦифра(Стр)
	Для К = 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 Тогда
		Итог.Добавить("=== ОШИБКИ ===");
		Итог.Добавить(СтрСоединить(Ошибки, Символы.ПС));
		Итог.Добавить("");
	КонецЕсли;
	Итог.Добавить("=== РАЗБОР ===");
	Итог.Добавить(СтрСоединить(Лог, Символы.ПС));
	Итог.Добавить("");
	Итог.Добавить("=== ПРОПУЩЕНО ===");
	Итог.Добавить(СтрСоединить(Пропущено, Символы.ПС));
	Возврат СтрСоединить(Итог, Символы.ПС);

КонецФункции