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