Предыстория вопроса: На предприятии есть прайс-лист, разбитый на подразделы. Последовательность позиций выстраивается не по алфавиту, а по присвоенному номеру (реквизит справочника). Позиции могут перемещаться по последовательности в рамках группы свободно (нужно для выделения или группировки тех или иных позиции в зависимости от настроения %) ). Изначально перемещение позиций было сделано по образцу таблиц значения: на одну позицию вверх, на одну позицию вниз стрелочками. По мере разрастания каталога продукции передвижение позиций становилось все более трудоемким, в итоге после жалоб ответственных за прайс я принял решение сделать возможность перетаскивать позиции стандартным драгунддпропом.
Возможно я изобретаю велосипед и не умею в поиск, но подходящих мне вариантов не нашел. Публикую свой вариант решения.
Итак, задача: переместить произвольные выделенные позиции в списке на новое место в нумерации.
Размещать позиции считаю правильным перед элементом (корень, k) на который мы "сбрасываем" выделенные элементы , и алгоритм я буду строить для такого варианта.
Что нужно определить в первую очередь? Диапазон, который затрагивает перемещение, поскольку при переносе элементов затрагиваются только позиции лежащие между минимальным номером min(nk)в списке перемещения K (корень тоже учитываем) и максимальным max(nk). Для остальных позиций нумерация не меняется.
Вторым шагом необходимо выстроить по возрастанию список выделенных для переноса элементов, поскольку выделение может происходить не по порядку следования элементов. Делается это через объединение таблицы переносимых элементов с самой собой и подсчетом количества позиций лежащих ниже вычисляемой sum(nk<n). Для больших списков квадратичная трудоемкость алгоритма нумерации будет влиять сильно, но вряд ли кто в здравом уме будет одновременно переносить больше сотни выделенных элементов, поэтому трудоемкостью пренебрегаем.
Третий шаг — вычисление смещения переносимых элементов относительно корня sum(nk<K)(например если корень=11, а переносим 8,9, 12, 13 и 15, смещение будет 2 или количество элементов стоящих по порядку ниже корня). Смещение укажет нижний диапазон, с которого начнется новая нумерация перенесенных элементов. В объединенном запросе эти шаги будут идти не по порядку, я показываю ход мысли при решении задачи.
Четвертый шаг — вычисление множества "неподвижных" элементов Е ={min(nk)<=n<max(nk)}K и смещение их на количество позиций, появившихся в результате перемещения . Решается с помощью соединения с таблицей перемещаемых элементов. При соединении подсчитывается сколько перемещаемых позиций стоят ниже элементов которые сами ниже корня sum(nk<ne|ne<k) и соответственно количество стоящих выше для элементов выше корня sum(nk>ne|ne>k). Это будет индивидуальное смещение для каждого "неподвижного" элемента в диапазоне.
Запрос готов:
ТекстЗапроса=
//выберем элементы справочника (как альтернатива можно создать таблицу значений из массива и загрузить в запрос)
ВЫБРАТЬ
ПрайсЛист.НомерВГруппе КАК СтарыйНомер,
ПрайсЛист.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_СписокСтрок
ИЗ
Справочник.ПрайсЛист КАК ПрайсЛист
ГДЕ
ПрайсЛист.Ссылка В(&МассивСсылок)
;
//вычисление смещения для перемещаемых элементов и границ изменяемого диапазона
ВЫБРАТЬ
СУММА(ВЫБОР
КОГДА ВТ_СписокСтрок.СтарыйНомер < (ВЫРАЗИТЬ(&Корень КАК ЧИСЛО))
ТОГДА 1
ИНАЧЕ 0
КОНЕЦ) КАК Смещение,
МИНИМУМ(ВЫБОР
КОГДА ВТ_СписокСтрок.СтарыйНомер < (ВЫРАЗИТЬ(&Корень КАК ЧИСЛО))
ТОГДА ВТ_СписокСтрок.СтарыйНомер
ИНАЧЕ ВЫРАЗИТЬ(&Корень КАК ЧИСЛО)
КОНЕЦ) КАК ПервыйИндекс,
МАКСИМУМ(ВЫБОР
КОГДА ВТ_СписокСтрок.СтарыйНомер > (ВЫРАЗИТЬ(&Корень КАК ЧИСЛО))
ТОГДА ВТ_СписокСтрок.СтарыйНомер
ИНАЧЕ ВЫРАЗИТЬ(&Корень КАК ЧИСЛО)
КОНЕЦ) КАК ПоследнийИндекс
ПОМЕСТИТЬ ВТ_Смещение
ИЗ
ВТ_СписокСтрок КАК ВТ_СписокСтрок
;
//упорядочивание перемещаемых элементов
ВЫБРАТЬ
КОЛИЧЕСТВО(ВТ_СписокМладших.СтарыйНомер) КАК НомерПоПорядку,
ВТ_СписокСтрок.СтарыйНомер КАК СтарыйНомер,
ВТ_СписокСтрок.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_ПронумерованныеСтроки
ИЗ
ВТ_СписокСтрок КАК ВТ_СписокСтрок
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_СписокСтрок КАК ВТ_СписокМладших
ПО ВТ_СписокСтрок.СтарыйНомер > ВТ_СписокМладших.СтарыйНомер
СГРУППИРОВАТЬ ПО
ВТ_СписокСтрок.СтарыйНомер,
ВТ_СписокСтрок.Ссылка
;
//подготовка таблицы для соединения
ВЫБРАТЬ
ВТ_ПронумерованныеСтроки.НомерПоПорядку + ((ВЫРАЗИТЬ(&Корень КАК ЧИСЛО)) - ВТ_Смещение.Смещение) КАК НовыйНомер,
ВТ_ПронумерованныеСтроки.СтарыйНомер КАК СтарыйНомер,
ВТ_ПронумерованныеСтроки.Ссылка КАК Ссылка,
ВТ_Смещение.ПервыйИндекс КАК ПервыйИндекс,
ВТ_Смещение.ПоследнийИндекс КАК ПоследнийИндекс
ПОМЕСТИТЬ ВТ_Перемещенные
ИЗ
ВТ_ПронумерованныеСтроки КАК ВТ_ПронумерованныеСтроки,
ВТ_Смещение КАК ВТ_Смещение
;
//вычисление смещения для "неподвижных" элементов диапазона
ВЫБРАТЬ
ПрайсЛист.НомерВГруппе КАК СтарыйНомер,
ПрайсЛист.Ссылка КАК Ссылка,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВТ_Перемещенные.СтарыйНомер) КАК НовыйНомер
ПОМЕСТИТЬ ВТ_ПолнаяТаблица
ИЗ
Справочник.ПрайсЛист КАК ПрайсЛист
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Перемещенные КАК ВТ_Перемещенные
ПО (ВЫБОР
КОГДА ПрайсЛист.НомерВГруппе >= &Корень
ТОГДА ВТ_Перемещенные.СтарыйНомер > ПрайсЛист.НомерВГруппе
КОГДА ПрайсЛист.НомерВГруппе < &Корень
ТОГДА ВТ_Перемещенные.СтарыйНомер < ПрайсЛист.НомерВГруппе
КОНЕЦ)
ГДЕ
ПрайсЛист.Родитель = &Родитель
И ПрайсЛист.НомерВГруппе < ВТ_Перемещенные.ПоследнийИндекс
И ПрайсЛист.НомерВГруппе >= ВТ_Перемещенные.ПервыйИндекс
И НЕ ПрайсЛист.ПометкаУдаления
И НЕ ПрайсЛист.Ссылка В (&МассивСсылок)
СГРУППИРОВАТЬ ПО
ПрайсЛист.НомерВГруппе,
ПрайсЛист.Ссылка
;
//объединение таблицы перемещаемых и "неподвижных"
ВЫБРАТЬ
ВТ_ПолнаяТаблица.СтарыйНомер КАК СтарыйНомер,
ВТ_ПолнаяТаблица.Ссылка КАК Ссылка,
ВЫБОР
КОГДА ВТ_ПолнаяТаблица.СтарыйНомер < &Корень
ТОГДА ВТ_ПолнаяТаблица.СтарыйНомер - ВТ_ПолнаяТаблица.НовыйНомер
ИНАЧЕ ВТ_ПолнаяТаблица.СтарыйНомер + ВТ_ПолнаяТаблица.НовыйНомер
КОНЕЦ КАК НовыйНомер
ИЗ
ВТ_ПолнаяТаблица КАК ВТ_ПолнаяТаблица
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ВТ_Перемещенные.СтарыйНомер,
ВТ_Перемещенные.Ссылка,
ВТ_Перемещенные.НовыйНомер
ИЗ
ВТ_Перемещенные КАК ВТ_Перемещенные
Теперь в списке нужно прописать процедуры перетаскивания:
&НаКлиенте
Процедура СписокПрайсНачалоПеретаскивания(Элемент, ПараметрыПеретаскивания, Выполнение)
ПараметрыПеретаскивания.ДопустимыеДействия=ДопустимыеДействияПеретаскивания.Перемещение;
КонецПроцедуры
&НаКлиенте
Процедура СписокПрайсОкончаниеПеретаскивания(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка)
СтандартнаяОбработка=Ложь;
КонецПроцедуры
&НаКлиенте
Процедура СписокПрайсПроверкаПеретаскивания(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Строка, Поле)
СтандартнаяОбработка=Ложь;
Если ТипЗнч(ПараметрыПеретаскивания.Значение)=Тип("Массив") Тогда
Для Каждого ЭлементМассива из ПараметрыПеретаскивания.Значение Цикл
Если НЕ ТипЗнч(ЭлементМассива)=Тип("СправочникСсылка.ПрайсЛист") Тогда
ПараметрыПеретаскивания.Действие=ДействиеПеретаскивания.Отмена;
Прервать;
КонецЕсли;
КонецЦикла;
ИначеЕсли НЕ ТипЗнч(ПараметрыПеретаскивания.Значение)=Тип("СправочникСсылка.ПрайсЛист") Тогда
ПараметрыПеретаскивания.Действие=ДействиеПеретаскивания.Отмена;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура СписокПрайсПеретаскивание(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Строка, Поле)
СтандартнаяОбработка=Ложь;
Родитель=РаботаСДинамическимиСписками.УстановленныйОтбор(СписокПрайс,"Родитель");
Если Родитель = Неопределено Тогда
ПоказатьПредупреждение(,"Не установлен отбор по группе прайса, перенос невозможен",5);
Возврат;
КонецЕсли;
ПеретащитьЭлементыПрайса(Родитель,Строка,ПараметрыПеретаскивания.Значение);
ОповеститьОбИзменении(Строка);
КонецПроцедуры
&НаСервере
Процедура ПеретащитьЭлементыПрайса(Родитель,Корень,СписокЭлементов)
Запрос=Новый Запрос;
Запрос.УстановитьПараметр("МассивСсылок",СписокЭлементов);
Запрос.УстановитьПараметр("Корень",Корень.НомерВгруппе);
Запрос.УстановитьПараметр("Родитель",Родитель);
Запрос.Текст= ТекстЗапроса;
ТаблицаСмещений=Запрос.Выполнить().Выгрузить();
Для Каждого СтрокаТаблицы из ТаблицаСмещений Цикл
Если НЕ СтрокаТаблицы.СтарыйНомер=СтрокаТаблицы.НовыйНомер Тогда
ОбъектСсылка=СтрокаТаблицы.Ссылка.ПолучитьОбъект();
ОбъектСсылка.НомерВГруппе=СтрокаТаблицы.НовыйНомер;
ОбъектСсылка.ОбменДанными.Загрузка=Истина;
ОбъектСсылка.Записать();
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Готово. Можно перетаскивать выделенные элементы в любом количестве с любой позиции.