Создаем универсальный механизм отбора и сортировки для управляемых форм





Причиной этого труда стало: отсутствие привычной кнопки "Отбор по значению в текущей колонке", некоторые проблемы с динамическими списками, отсутствие истории отборов, авто восстановления настроек отборов при перезапуске формы (динамических списков), а так же стремление увеличить функциональность отборов и сортировок с учетом возможностей управляемых форм.

Осноные возможноси:

1. Отбор имеет полный функционал как и в 1С Предприятие 8.1;

2. Отсутсвует ошибка при использовании истории отборов (в 1С Предприятии 8.1 если установлен отбор и воспользоватся отбором из истории текущий отбор не отменяется);

3. Возможность использования отборов и сортировки для всех возможных реквизитов, которые присутствуют в таблице и тех которые добавлены через команду формы «Изменить форму» (Например, Номенклатура.Ссылка.Ссылка.ВидНоменклатуры.Код);

4. Сортировка по любому реквизиту в таблице;

5. История отборов размером в 20 элементов;

6. Все построенно на СКД с выводом в таблицу значений, что дает возможность использовать дополнительные поля которые есть в запросе;

7. Минимум серверных вызовов;

8. Для использования в своих разработках достаточно изменить запрос в наборе данных, структуру полей компоновщика (ПриСозданииНаСервере) и подкоректировать таблицу значений для вывода.




Код обработки, работоспособен в конфигурации УТ 11:

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
   
//1. Создаем схему компоновки данных
   
СхемаКомпоновкиДанных = Новый СхемаКомпоновкиДанных;
   
//1.1 определяем источник данных для схемы
   
ИсточникДанных = СхемаКомпоновкиДанных.ИсточникиДанных.Добавить();
   
ИсточникДанных.Имя                = «ИсточникДанных»;
   
ИсточникДанных.СтрокаСоединения   = «»;
   
ИсточникДанных.ТипИсточникаДанных = «Local»;
   
//1.2 определяем набор данных
   
НаборДанных = СхемаКомпоновкиДанных.НаборыДанных.Добавить(Тип(«НаборДанныхЗапросСхемыКомпоновкиДанных»));
   
НаборДанных.Имя            = «НаборДанных»;
   
НаборДанных.ИсточникДанных = «ИсточникДанных»;
   
НаборДанных.АвтоЗаполнениеДоступныхПолей = Истина;
   
НаборДанных.Запрос = «
        |ВЫБРАТЬ
        |   СпрНоменклатура.Ссылка КАК Номенклатура,
        |   СвободныеОстатки.ВНаличииОстаток КАК ВНаличии,
        |   СвободныеОстатки.ВРезервеОстаток КАК ВРезерве
        |
        |ИЗ
        |   Справочник.Номенклатура КАК СпрНоменклатура
        |
        |ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СвободныеОстатки.Остатки КАК СвободныеОстатки
        |ПО СпрНоменклатура.Ссылка = СвободныеОстатки.Номенклатура
        |
        |ГДЕ
        |НЕ СпрНоменклатура.ЭтоГруппа
        |И  СпрНоменклатура.Родитель = &ГруппаНоменклатуры»
;

    //1.2.1 добавляем поля
   
ПолеНоменклатуры = НаборДанных.Поля.Добавить(Тип(«ПолеНабораДанныхСхемыКомпоновкиДанных»));
   
ПолеНоменклатуры.ПутьКДанным = «Номенклатура»;
   
ПолеНоменклатуры.Заголовок   = «Номенклатура»;
   
ПолеНоменклатуры.Поле        = «Номенклатура»;

    //1.3 определяем ресурсы
    //ПолеРесурса = СхемаКомпоновкиДанных.ПоляИтога.Добавить();
    //ПолеРесурса.Выражение = «»;
    //ПолеРесурса.ПутьКДанным = «»;

    //2. создаем настройки для схемы
    //НастройкиКомпоновкиДанных = СхемаКомпоновкиДанных.НастройкиПоУмолчанию;
    АдресСхемыКомпоновкиДанных = ПоместитьВоВременноеХранилище(СхемаКомпоновкиДанных, УникальныйИдентификатор);
   
КомпоновщикНастроек.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(АдресСхемыКомпоновкиДанных));

    //2.1 определяем структуру
    //2.1.1 добавляем группировку «Номенклатура»
   
ГруппировкаНоменклатуры = КомпоновщикНастроек.Настройки.Структура.Добавить(Тип(«ГруппировкаКомпоновкиДанных»));
   
ГруппировкаНоменклатуры.Использование = Истина;

    //2.2 определим выбранные поля
   
ВыбранноеПоле = ГруппировкаНоменклатуры.Выбор.Элементы.Добавить(Тип(«ВыбранноеПолеКомпоновкиДанных»));
   
ВыбранноеПоле.Заголовок     = «Номенклатура»;
   
ВыбранноеПоле.Использование = Истина;
   
ВыбранноеПоле.Поле          = Новый ПолеКомпоновкиДанных(«Номенклатура»);

    ВыбранноеПоле = ГруппировкаНоменклатуры.Выбор.Элементы.Добавить(Тип(«ВыбранноеПолеКомпоновкиДанных»));
   
ВыбранноеПоле.Заголовок     = «ВНаличии»;
   
ВыбранноеПоле.Использование = Истина;
   
ВыбранноеПоле.Поле          = Новый ПолеКомпоновкиДанных(«ВНаличии»);

    ВыбранноеПоле = ГруппировкаНоменклатуры.Выбор.Элементы.Добавить(Тип(«ВыбранноеПолеКомпоновкиДанных»));
   
ВыбранноеПоле.Заголовок     = «ВРезерве»;
   
ВыбранноеПоле.Использование = Истина;
   
ВыбранноеПоле.Поле          = Новый ПолеКомпоновкиДанных(«ВРезерве»);

    ГруппировкаНоменклатуры.Порядок.Элементы.Добавить(Тип(«АвтоЭлементПорядкаКомпоновкиДанных»));
КонецПроцедуры

&НаКлиенте
Процедура ПриОткрытии(Отказ)
   
//Проводим инициализацию отборов из загруженных данных при последнем сохранении
   
ИнициализироватьОтборы();
   
//Обновляем пометки кнопок для визуального восприятия отборов
   
ОбновитьДанныеОтбора();
КонецПроцедуры

&НаСервере
Процедура ИнициализироватьОтборы()
   
//Список значений «ИсторияОтборов» хранит историю отборов, максимум 20 элементов
   
Для і=0 По ИсторияОтборов.Количество()-1 Цикл
       
//Получаем имя команды которая соответствует отбору
       
ИмяКоманды = ИсторияОтборов[і].Значение.ИмяКоманды;
       
//Получаем представление отбора, которое является аналогичным 1С 8.1
       
Представление = ПолучитьПредставлениеОтбора(ИсторияОтборов[і].Значение.МассивОтборов);
       
//Динамически добавляем команду формы и добавляем элемент формы, который привязываем к команде формы
       
ДобавитьКомандуКФорме(ИмяКоманды, Представление);
    КонецЦикла;
КонецПроцедуры

&НаКлиенте
Процедура ИерархияНоменклатурыПередНачаломИзменения(Элемент, Отказ)
   
//Запрет изменения элементов пользователю
   
Отказ = Истина;
КонецПроцедуры

&НаКлиенте
Процедура ИерархияНоменклатурыПередУдалением(Элемент, Отказ)
   
//Запрет удаления элементов пользователю
   
Отказ = Истина;
КонецПроцедуры

&НаКлиенте
Процедура ИерархияНоменклатурыПередНачаломДобавления(Элемент, Отказ, Копирование, Родитель, Группа)
   
//Запрет добавления элементов пользователю
   
Отказ = Истина;
КонецПроцедуры

&НаКлиенте
Процедура ИерархияНоменклатурыПриАктивизацииСтроки(Элемент)
   
//Так как это пример, решил упростить задачу — не обрабатывать пустую ссылку
   
Если Элемент.ТекущаяСтрока.Пустая() Тогда
       
ТаблицаНоменклатуры.Очистить();
    Иначе
       
СформироватьКлиент(Элемент.ТекущаяСтрока);
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура СформироватьКлиент(ГруппаНоменклатуры, Отбор = Ложь, Сортировка = Ложь, Идентификатор = Неопределено, ИмяЭлемента = Неопределено)
    Перем
ПараметрыОтбора;
   
//Запомним данные, возможно нам понадобится востановить курсор пользователя
   
ТекущиеДанные = Элементы.ТаблицаНоменклатуры.ТекущиеДанные;
    Если НЕ
ТекущиеДанные = Неопределено Тогда
        
ПараметрыОтбора = Новый Структура(«Номенклатура», ТекущиеДанные.Номенклатура);
    КонецЕсли;

    СформироватьСервер(ГруппаНоменклатуры, Отбор, Сортировка, Идентификатор, ИмяЭлемента);
   
//Обновляем пометки кнопок для визуального восприятия отборов
   
ОбновитьДанныеОтбора();

    //Востанавливаем курсор на позицию, если она не пропала из выгрузки
   
Если НЕ ПараметрыОтбора = Неопределено Тогда
       
Массив = ТаблицаНоменклатуры.НайтиСтроки(ПараметрыОтбора);
        Если
Массив.Количество() Тогда Элементы.ТаблицаНоменклатуры.ТекущаяСтрока = Массив[0].ПолучитьИдентификатор(); КонецЕсли;
    КонецЕсли;
КонецПроцедуры

&НаСервере
Процедура СформироватьСервер(ГруппаНоменклатуры, Отбор = Ложь, Сортировка = Ложь, Идентификатор = Неопределено, ИмяЭлемента = Неопределено)
    Перем
Значение, ВремОбъект;
   
//Получаем СКД из временного хранилища
   
СхемаКомпоновкиДанных   = ПолучитьИзВременногоХранилища(АдресСхемыКомпоновкиДанных);
   
КомпоновщикНастроек.Настройки.ПараметрыДанных.УстановитьЗначениеПараметра(«ГруппаНоменклатуры», ГруппаНоменклатуры);
   
//У пользователя есть возможность изменять форму, нужно проверить имя реквизита — возможно реквизит был добавлен через «Изменить форму…»
   
Если Отбор ИЛИ НЕ Сортировка = Ложь Тогда
       
ТекущиеДанные = ТаблицаНоменклатуры.НайтиПоИдентификатору(Идентификатор);
       
//Проверяем есть ли реквизит
       
РеквизитИзТаб = ТекущиеДанные.Свойство(ИмяЭлемента, Значение);
        Если
РеквизитИзТаб Тогда
            Если
Отбор Тогда
               
ДобавитьОтборПоЗначению(КомпоновщикНастроек, ИмяЭлемента, Значение);
               
ОбновитьИсториюОтборов(КомпоновщикНастроек, ИсторияОтборов);
            Иначе
               
ДобавитьСортировку(КомпоновщикНастроек, ИмяЭлемента, Сортировка);
            КонецЕсли;
        Иначе
           
//Реквизита нет в таблице, разбиваем ИмяЭлемента на слова
           
МассивИмен = ПолучитьМассивИмен(ИмяЭлемента);
           
Колич_Имен = МассивИмен.Количество() — 1;

            //Данный цикл предназначен для поиска реквизита ссылочного типа, который является родителем реквизита по которому
            //выполняется отбор или сортировка
           
Для i = 0 По Колич_Имен Цикл
                Если
ТекущиеДанные.Свойство(МассивИмен[i], ВремОбъект) Тогда
                    Если
Найти(XMLТипЗнч(ВремОбъект).ИмяТипа, «Ref») = 0 Тогда
                       
//Добавлять дополнительные поля возможно только из реквизитов ссылочного типа
                       
ВызватьИсключение «Возникла неизвестная ошибка. Реквизит имеет не ссылочный тип: » + ИмяЭлемента;
                    КонецЕсли;
                   
ИмяЭлемента = МассивИмен[i];
                   
i=i+1;
                    Прервать;
                ИначеЕсли
i+1 <= Колич_Имен Тогда
                   
МассивИмен[i+1] = МассивИмен[i] + «_» + МассивИмен[i+1];
                Иначе
                    ВызватьИсключение
«Возникла неизвестная ошибка. В таблице нет данных о имени: » + ИмяЭлемента;
                КонецЕсли;
            КонецЦикла;

            //Выполняем построение адреса реквизита по которому нужно выполнить отбор или сортировку
           
АдресРеквизита = «»;
           
МетаВремЗначение = ВремОбъект.Метаданные();
            Пока
i <= Колич_Имен Цикл
               
ВремЗначение = МетаВремЗначение.Реквизиты.Найти(МассивИмен[i]);
                Если НЕ
ВремЗначение = Неопределено Тогда
                   
АдресРеквизита = АдресРеквизита + МассивИмен[i];
                   
формироватьАдресРеквизита(i, Колич_Имен, МетаВремЗначение, ВремЗначение, АдресРеквизита);
                Иначе
                    Попытка
                       
ВремЗначение = МетаВремЗначение.СтандартныеРеквизиты[МассивИмен[i]];
                       
АдресРеквизита = АдресРеквизита + МассивИмен[i];
                       
формироватьАдресРеквизита(i, Колич_Имен, МетаВремЗначение, ВремЗначение, АдресРеквизита);
                    Исключение
                       
МассивИмен[i+1] = МассивИмен[i] + «_» + МассивИмен[i+1];
                    КонецПопытки;
                КонецЕсли;
               
i=i+1;
            КонецЦикла;

            Если Отбор Тогда
               
ДобавитьОтборПоЗначению(КомпоновщикНастроек, ИмяЭлемента + ?(ПустаяСтрока(АдресРеквизита), «», «.» + АдресРеквизита), ПолучитьЗначениеРеквизита(АдресРеквизита, ВремОбъект));
               
ОбновитьИсториюОтборов(КомпоновщикНастроек, ИсторияОтборов);
            Иначе
               
ДобавитьСортировку(КомпоновщикНастроек, ИмяЭлемента + ?(ПустаяСтрока(АдресРеквизита), «», «.» + АдресРеквизита), Сортировка);
            КонецЕсли;
        КонецЕсли;
    КонецЕсли;

    //3. готовим макет
   
КомпоновщикМакетаДанных = Новый КомпоновщикМакетаКомпоновкиДанных;
   
МакетКомпоновкиДанных = КомпоновщикМакетаДанных.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.Настройки, , , Тип(«ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений»));

    //4. исполняем макет
   
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
   
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновкиДанных);

    //5. выводим результат
   
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
   
ТаблицаНоменклатуры.Загрузить(ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных));
КонецПроцедуры

&НаСервере
Процедура ДобавитьСортировку(КомпоновщикНастроек, ИмяЭлемента, Направление)
   
ЭлементыПорядка = КомпоновщикНастроек.Настройки.Порядок.Элементы;
   
ЭлементыПорядка.Очистить();
   
ЭлементПорядка = ЭлементыПорядка.Добавить(Тип(«ЭлементПорядкаКомпоновкиДанных»));
   
ЭлементПорядка.Использование = Истина;
   
ЭлементПорядка.ТипУпорядочивания = Направление;
   
ЭлементПорядка.Поле = Новый ПолеКомпоновкиДанных(ИмяЭлемента);
КонецПроцедуры

&НаСервере
Процедура ДобавитьОтборПоЗначению(КомпоновщикНастроек, ИмяЭлемента, Значение)
    Перем
ЭлементОтбораНайден;
   
ЭлементыОтбора = КомпоновщикНастроек.Настройки.Отбор.Элементы;
    Для Каждого
ЭлементОтбора Из ЭлементыОтбора Цикл
       
ЭлементОтбора.Представление = ЭлементОтбора.ЛевоеЗначение;
        Если
ЭлементОтбора.Представление = ИмяЭлемента И ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно Тогда
           
ЭлементОтбора.Использование = НЕ ЭлементОтбора.Использование;
           
ЭлементОтбора.ПравоеЗначение = Значение;
           
ЭлементОтбораНайден = ЭлементОтбора;
        КонецЕсли;
    КонецЦикла;

    Если ЭлементОтбораНайден = Неопределено Тогда
       
ЭлементОтбора = ЭлементыОтбора.Добавить(Тип(«ЭлементОтбораКомпоновкиДанных»));
       
ЭлементОтбора.Использование = Истина;
       
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных(ИмяЭлемента);
       
ЭлементОтбора.Представление = ЭлементОтбора.ЛевоеЗначение;
       
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
       
ЭлементОтбора.ПравоеЗначение = Значение;
    КонецЕсли;
КонецПроцедуры

&НаСервере
Процедура ОбновитьИсториюОтборов(КомпоновщикНастроек, ИсторияОтборов)
   
//Создаем уникальное имя команды, УникальныйИдентификатор не всегда начинается с буквы, добавляем вначале «n»
   
ИмяКоманды = «n» + СтрЗаменить(Новый УникальныйИдентификатор, «-«, «»);
   
МассивОтборов = Новый Массив;
   
ЭлементыОтбора = КомпоновщикНастроек.Настройки.Отбор.Элементы;
    Для Каждого
ЭлементОтбора Из ЭлементыОтбора Цикл
        Если
ЭлементОтбора.Использование Тогда
           
МассивОтборов.Добавить(Новый Структура(«ЛевоеЗначение, ВидСравнения, ПравоеЗначение», ЭлементОтбора.ЛевоеЗначение, ЭлементОтбора.ВидСравнения, ЭлементОтбора.ПравоеЗначение));
        КонецЕсли;
    КонецЦикла;

    Если МассивОтборов.Количество() Тогда
       
ПредставлениеОтбора = ПолучитьПредставлениеОтбора(МассивОтборов);
       
КоличествоЭлементов = ИсторияОтборов.Количество()-1;
        Для
i=0 По КоличествоЭлементов Цикл
           
//Если отбор найден перемещаем команду в начало списка, так же в списке значений изменяем положение отбора
           
Если ПредставлениеОтбора = ИсторияОтборов[i].Представление Тогда
               
ПереместитьКомандуФормы(ИсторияОтборов[i].Значение.ИмяКоманды, ИсторияОтборов[0].Значение.ИмяКоманды);
               
ИсторияОтборов.Сдвинуть(i, —i);
                Прервать;
           
//Это новый элемент создаем вначале списка значений, так же команду устанавливаем вначало списка
           
ИначеЕсли i = КоличествоЭлементов Тогда
               
ДобавитьКомандуКФорме(ИмяКоманды, ПредставлениеОтбора, ИсторияОтборов[0].Значение.ИмяКоманды);
               
ИсторияОтборов.Вставить(0, Новый Структура(«ИмяКоманды, МассивОтборов», ИмяКоманды, МассивОтборов), ПредставлениеОтбора);
            КонецЕсли;
        КонецЦикла;
       
//Элементов еще нет, создаем первые элементы
       
Если КоличествоЭлементов < 0 Тогда
           
ДобавитьКомандуКФорме(ИмяКоманды, ПредставлениеОтбора);
           
ИсторияОтборов.Вставить(0, Новый Структура(«ИмяКоманды, МассивОтборов», ИмяКоманды, МассивОтборов), ПредставлениеОтбора);
        КонецЕсли;
    КонецЕсли;

    //Элементов больше 20, удаляем лишнее
   
Если ИсторияОтборов.Количество() = 21 Тогда
       
УдалитьКомандуФормы(ИсторияОтборов[20].Значение.ИмяКоманды);
       
ИсторияОтборов.Удалить(20);
    КонецЕсли;
КонецПроцедуры

&НаСервере
Функция ПолучитьПредставлениеОтбора(МассивОтборов)
   
ПредставлениеОтбора = «»;
   
КоличествоЭлементов = МассивОтборов.Количество()-1;
    Для
i=0 По КоличествоЭлементов Цикл
       
ПредставлениеОтбора = ПредставлениеОтбора + МассивОтборов[i].ЛевоеЗначение  + » «
                                                 
+ МассивОтборов[i].ВидСравнения   + » «
                                                 
+ МассивОтборов[i].ПравоеЗначение + ?(i<КоличествоЭлементов, «, «, «»);
    КонецЦикла;
    Возврат
ПредставлениеОтбора;
КонецФункции

&НаСервере
Процедура ДобавитьКомандуКФорме(ИмяКоманды, Представление, ИмяПозицииВставки = Неопределено)
   
Команда = Команды.Добавить(ИмяКоманды);
   
Команда.Действие = «ОтборИстория»;
   
Команда.Заголовок = Представление;
   
КомандаФормы = Элементы.Вставить(Команда.Имя, Тип(«КнопкаФормы»), Элементы.ИсторияОтборов, Элементы.Найти(ИмяПозицииВставки));
   
КомандаФормы.ИмяКоманды = Команда.Имя;
   
КомандаФормы.Заголовок  = Представление;
   
КомандаФормы.Вид  = ВидКнопкиФормы.КнопкаКоманднойПанели;
КонецПроцедуры

&НаСервере
Процедура ПереместитьКомандуФормы(ИмяКоманды, ИмяПозицииПеремещения)
   
Элементы.Переместить(Элементы.Найти(ИмяКоманды), Элементы.ИсторияОтборов, Элементы.Найти(ИмяПозицииПеремещения));
КонецПроцедуры

&НаСервере
Процедура УдалитьКомандуФормы(ИмяКоманды)
   
Элементы.Удалить(Элементы.Найти(ИмяКоманды));
   
Команды.Удалить(Команды.Найти(ИмяКоманды));
КонецПроцедуры

&НаСервере
Функция ПолучитьЗначениеРеквизита(АдресРеквизита, Объект)
    Перем
ЗначениеРеквизита;
    Выполнить(
«ЗначениеРеквизита = Объект.» + АдресРеквизита + «;»);
    Возврат
ЗначениеРеквизита;
КонецФункции

&НаСервере
Функция ПолучитьМассивИмен(ИмяЭлемента)
   
МассивСлов = Новый Массив;
   
ЧислоВхождений = СтрЧислоВхождений(ИмяЭлемента, «_»);
    Если
ЧислоВхождений = 0 Тогда
       
МассивСлов.Добавить(ИмяЭлемента);
    Иначе
        Для
Итератор = 0 По ЧислоВхождений — 1 Цикл
           
Разделитель = Найти(ИмяЭлемента, «_»);
           
МассивСлов.Добавить(Лев(ИмяЭлемента, Разделитель — 1));
           
ИмяЭлемента = Прав(ИмяЭлемента, СтрДлина(ИмяЭлемента) — Разделитель);
        КонецЦикла;
       
МассивСлов.Добавить(ИмяЭлемента);
    КонецЕсли;
    Возврат
МассивСлов;
КонецФункции

&НаСервере
Процедура формироватьАдресРеквизита(i, Колич_Имен, МетаВремЗначение, ВремЗначение, АдресРеквизита)
    Если
i + 1 <= Колич_Имен Тогда
       
//Из описания типа получаем метаданные
       
МетаВремЗначение = ВремЗначение.Тип.ПривестиЗначение(Неопределено).Метаданные();
       
АдресРеквизита = АдресРеквизита + «.»;
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ОбновитьДанныеОтбора(ОтключитьОтбор = Ложь)
   
Элементы.ОтборПоЗначению.Пометка = Ложь;
   
Элементы.ОтборИСортировка.Пометка = Ложь;
    Для Каждого
Элемент Из КомпоновщикНастроек.Настройки.Отбор.Элементы Цикл
        Если
ОтключитьОтбор Тогда Элемент.Использование = Ложь; КонецЕсли;
        Если
Элемент.Использование И Элемент.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно Тогда
           
Элементы.ОтборПоЗначению.Пометка = Истина;
        ИначеЕсли
Элемент.Использование Тогда
           
Элементы.ОтборИСортировка.Пометка = Истина;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

&НаКлиенте
Процедура ОтборИСортировка(Команда)
   
Результат = ОткрытьФормуМодально(«ВнешняяОбработка.УниверсальныйОтбор.Форма.ФормаНастройки», Новый Структура(«КомпоновщикНастроек», КомпоновщикНастроек), ЭтаФорма);
    Если
ТипЗнч(Результат) = Тип(«КомпоновщикНастроекКомпоновкиДанных») Тогда
       
КомпоновщикНастроек = Результат;
       
ОбновитьДанныеОтбора();
       
ОбновитьИсториюОтборов(КомпоновщикНастроек, ИсторияОтборов);
       
ТСтрока = Элементы.ИерархияНоменклатуры.ТекущаяСтрока;
        Если НЕ
ТСтрока.Пустая() Тогда СформироватьКлиент(ТСтрока); КонецЕсли;
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ОтборПоЗначению(Команда)
   
ТСтрока  = Элементы.ИерархияНоменклатуры.ТекущаяСтрока;
   
ТДанные  = Элементы.ТаблицаНоменклатуры.ТекущиеДанные;
   
ТЭлемент = Элементы.ТаблицаНоменклатуры.ТекущийЭлемент;
    Если НЕ
ТСтрока.Пустая() И НЕ Элементы.ТаблицаНоменклатуры.ТекущиеДанные = Неопределено Тогда
       
ИмяЭлемента = ТЭлемент.Имя;
       
СформироватьКлиент(ТСтрока, Истина, , ТДанные.ПолучитьИдентификатор(), ИмяЭлемента);
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ОтключитьОтбор(Команда)
   
ОбновитьДанныеОтбора(Истина);
   
ТСтрока = Элементы.ИерархияНоменклатуры.ТекущаяСтрока;
    Если НЕ
ТСтрока.Пустая() Тогда СформироватьКлиент(ТСтрока); КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ОтборИстория(Команда)
   
//Ищем в истории отборов отбор соответствующий имени команды
   
Перем МассивОтборов;
    Для
i=0 По ИсторияОтборов.Количество()-1 Цикл
        Если
ИсторияОтборов[i].Значение.ИмяКоманды = Команда.Имя Тогда
           
МассивОтборов = ИсторияОтборов[i].Значение.МассивОтборов;
           
ПереместитьКомандуФормы(Команда.Имя, ИсторияОтборов[0].Значение.ИмяКоманды);
            Если
ИсторияОтборов.Количество() > 1 Тогда ИсторияОтборов.Сдвинуть(i, —i); КонецЕсли;
            Прервать;
        КонецЕсли;
    КонецЦикла;

    //Обнуляем все отборы, перед установкой отбора из истории отборов
   
ЭлементыОтбора = КомпоновщикНастроек.Настройки.Отбор.Элементы;
    Для Каждого
Элемент Из ЭлементыОтбора Цикл Элемент.Использование = Ложь; КонецЦикла;
   
//Устанавливаем отборы
   
Для i=0 По МассивОтборов.Количество()-1 Цикл
       
ОтборИзМассиваОпределен = Неопределено;
        Для Каждого
Элемент Из ЭлементыОтбора Цикл
            Если
Элемент.ЛевоеЗначение  = МассивОтборов[i].ЛевоеЗначение
              И  Элемент.ВидСравнения   = МассивОтборов[i].ВидСравнения Тогда
               
Элемент.ПравоеЗначение  = МассивОтборов[i].ПравоеЗначение;
               
Элемент.Использование   = Истина;
               
ОтборИзМассиваОпределен = Истина;
                Прервать;
            КонецЕсли;
        КонецЦикла;
       
//Возможно была очистка отборов вручную, значит создаем нужный отбор
       
Если ОтборИзМассиваОпределен = Неопределено Тогда
           
ЭлементОтбора = ЭлементыОтбора.Добавить(Тип(«ЭлементОтбораКомпоновкиДанных»));
           
ЭлементОтбора.Использование = Истина;
           
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных(МассивОтборов[i].ЛевоеЗначение);
           
ЭлементОтбора.Представление = МассивОтборов[i].ЛевоеЗначение;
           
ЭлементОтбора.ВидСравнения  = МассивОтборов[i].ВидСравнения;
           
ЭлементОтбора.ПравоеЗначение = МассивОтборов[i].ПравоеЗначение;
        КонецЕсли;
    КонецЦикла;

    //Выполняем запрос
   
ТСтрока = Элементы.ИерархияНоменклатуры.ТекущаяСтрока;
    Если НЕ
ТСтрока.Пустая() Тогда
       
СформироватьКлиент(ТСтрока);
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура СортироватьПоВозрастанию(Команда)
   
ТСтрока  = Элементы.ИерархияНоменклатуры.ТекущаяСтрока;
   
ТДанные  = Элементы.ТаблицаНоменклатуры.ТекущиеДанные;
   
ТЭлемент = Элементы.ТаблицаНоменклатуры.ТекущийЭлемент;
    Если НЕ
ТСтрока.Пустая() И НЕ Элементы.ТаблицаНоменклатуры.ТекущиеДанные = Неопределено Тогда
       
ИмяЭлемента = ТЭлемент.Имя;
       
СформироватьКлиент(ТСтрока, , НаправлениеСортировкиКомпоновкиДанных.Возр, ТДанные.ПолучитьИдентификатор(), ИмяЭлемента);
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура СортироватьПоУбыванию(Команда)
   
ТСтрока  = Элементы.ИерархияНоменклатуры.ТекущаяСтрока;
   
ТДанные  = Элементы.ТаблицаНоменклатуры.ТекущиеДанные;
   
ТЭлемент = Элементы.ТаблицаНоменклатуры.ТекущийЭлемент;
    Если НЕ
ТСтрока.Пустая() И НЕ Элементы.ТаблицаНоменклатуры.ТекущиеДанные = Неопределено Тогда
       
ИмяЭлемента = ТЭлемент.Имя;
       
СформироватьКлиент(ТСтрока, , НаправлениеСортировкиКомпоновкиДанных.Убыв, ТДанные.ПолучитьИдентификатор(), ИмяЭлемента);
    КонецЕсли;
КонецПроцедуры

27 Comments

  1. pbazeliuk

    Причиной этого труда стало: отсутствие привычной кнопки «Отбор по значению в текущей колонке», некоторые проблемы с динамическими списками, отсутствие истории отборов, авто восстановления настроек отборов при перезапуске формы (динамических списков), а так же стремление увеличить функциональность отборов и сортировок с учетом возможностей управляемых форм.

    Перейти к публикации

    Reply ↓
  2. zavedeev

    Отлично. Спасибо!!!

    Reply ↓
  3. afanasko

    Отличная вещь! Побежал прикручивать к своим конфам.

    Reply ↓
  4. dandrontiy

    Очень интересно. Получается не только чисто отбор можно использовать, но еще и дополнительные запросы.

    Функционал явно превосходит механизм отборов в 1с 8.1

    Так можно подумать и добавить спец запросы (сделанные не пользователем, а разрабочиком и прикрученные к механизму отбора) а пользователь будет задавать только конечные параметры. Например отбор по реквизитам подчиненного документа.

    Плюс однозначно!

    Но с кодом еще придется поразбираться 😉 не тривиально всё.

    Reply ↓
  5. Saipl

    Работа проделана конечно большая и это +, надеюсь что данный сервис появится на уровне платформы (то что до сих пор не появился это -)

    Reply ↓
  6. khaoos

    Хорошая серьезная разработка. Автор молодец. Однозначно плюс, к тому же еще и код выложил!

    Reply ↓
  7. gavrikprog

    делете

    Reply ↓
  8. xzorkiix
    2. Отсутсвует ошибка при использовании истории отборов (в 1С Предприятии 8.1 если установлен отбор и воспользоватся отбором из истории текущий отбор не отменяется);

    Не знал даже этого.

    Reply ↓
  9. aviaye

    Очень этого не хватало, спасибо автору) И пошла пробовать-экспериментировать

    Reply ↓
  10. Den_D

    Вот это штукенция очень надо. Спасибо огромное!

    Reply ↓
  11. An-Aleksey

    То что доктор прописал 🙂 Однозначно +

    Reply ↓
  12. nizar

    Офигенно

    Reply ↓
  13. Bober777

    МОЛОДЦА

    Reply ↓
  14. Yashazz

    Красиво, но для добавляемых колонок нежизнеспособно, ибо сделать такой универсал очень трудно.

    Вот есть у меня динам.список по выборке, а выборка строится с использованием механизма характеристик. И вот хочу я отобрать по колонке, принудительно выведенной на интерфейс. И тут, подозреваю, нифига не выйдет.

    Да, и если в именах полей, сами по себе, есть знаки подчёркивания — тоже сомневаюсь в безошибочности.

    Reply ↓
  15. pbazeliuk

    (13) Yashazz, Подчеркивания обрабатываются вот этим куском, если это добавленая колонка, тогда поиск идет по метаданным на совпадение (В случае, когда в метаданных есть «Полное_Наименование» оно обработается правильно). Проблема присутствует, если в выборке множество реквизитов ссылочного типа, а не один.

    Пока i <= Колич_Имен Цикл

    ВремЗначение = МетаВремЗначение.Реквизиты.Найти(МассивИмен[i]);

    Если НЕ ВремЗначение = Неопределено Тогда

    АдресРеквизита = АдресРеквизита + МассивИмен[i];

    формироватьАдресРеквизита(i, Колич_Имен, МетаВремЗначение, ВремЗначение, АдресРеквизита);

    Иначе

    Попытка

    ВремЗначение = МетаВремЗначение.СтандартныеРеквизиты[МассивИмен[i]];

    АдресРеквизита = АдресРеквизита + МассивИмен[i];

    формироватьАдресРеквизита(i, Колич_Имен, МетаВремЗначение, ВремЗначение, АдресРеквизита);

    Исключение

    МассивИмен[i+1] = МассивИмен[i] + «_» + МассивИмен[i+1];

    КонецПопытки;

    КонецЕсли;

    i=i+1;

    КонецЦикла;

    Почти все нюансы эти уже исправлены, но руки не доходят обновить публикацию.

    Reply ↓
  16. m1kll

    спасибо, автор молодец

    Reply ↓
  17. tommyknocker

    Отличная идея. Пользователям жутко не хватает этих кнопок. Вроде как в УФ это и есть, но где раньше было одно нажатие, теперь нужно 2-3 в лучшем случае, а при составных отборах и того больше.

    Еще бы как-то это в общую команду оформить…

    Reply ↓
  18. prodines

    Да как этим пользоваться, объясните… (для тех, кто управляемые формы видит в первый раз)

    У меня управляемая форма списка реализаций товаров и услуг. Основной реквизит — Список с типом ДинамическийСписок. Чекбокс Произвольный запрос установлен.

    В скаченной обработке среди реквизитов есть таблица значений. Значит, работаем вместо динамического списка с таблицей значений, что ли?

    И окна «Иерархия номенклатуры» у меня нет = значит, из программного кода придётся вырезать куски кода под него?

    Reply ↓
  19. pbazeliuk

    (17) prodines, это как пример. Для динамического списка эта публикация не подойдет. Для динамического списка все совсем иначе и в описании еще написано:

    отсутствие привычной кнопки «Отбор по значению в текущей колонке», некоторые проблемы с динамическими списками, отсутствие истории отборов, авто восстановления настроек отборов при перезапуске формы (динамических списков)

    .

    Reply ↓
  20. validat

    Скачал. Опыта маловато. Буду пробовать. Сортировка нужна в платформе, однозначно.

    Reply ↓
  21. lesenoklenok

    Отличная статья! Очень помогла! Спасибо автору!

    Reply ↓
  22. IDija

    Подскажите а как бы этот функционал использовать в форме выбора? Чтобы возвращало значение?

    Reply ↓
  23. pbazeliuk

    (21) IDija, можете детально описать проблему, не понятно зачем это использовать в форме выбора?

    Reply ↓
  24. IDija

    (22) pbazeliuk, В документе при выборе в ТЧ Номенклатуры нет истории предыдущего выбора. Что бы при написании наименования предлагалась предыдущие варианты поиска.

    Может есть и вариант попроще, но не шел?

    Reply ↓
  25. pbazeliuk

    (23) IDija, историю выбора номенклатуры нужно реализовывать самому. Задача по своей сути очень простая, если не нужно использовать переопределение вызовов и динамическое подключение функциональности.

    Reply ↓
  26. Manticor

    Подскажите пожалуйста, возможно ли данный механизм реализовать под обычные формы для табличного поля с типом таблица значений??

    Reply ↓
  27. pbazeliuk

    (25) Manticor, для обычных форм можно использовать только идею, но не сам код что тут представлен, нужны множественные доработки.

    Reply ↓

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *