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

Приемы и подходы, обмен опытом

Модераторы: Screw, larin

Ответить
Max_Ural
партнер
Сообщения: 82
Зарегистрирован: Ср, 19/09/2007 08:35
Имя Фамилия: Максим Шагубаков
Откуда: Галактика-Урал
Контактная информация:

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

Сообщение Max_Ural »

Задача состоит в следующем:
Есть ДО на закупку.
Нам необходимо дать возможность пользователю одну из следующих возможностей (в порядке убывания приоритета):
1. Фильтр по аналитике Целевого учета в спецификации ДО.
2. Сортировка по аналитике Целевого учета в спецификации ДО.

3а. Установка фильтра по переменной логической таблицы шапки документа
В частности, в логической таблице описаны переменные vsKau1 и vsKau3, которые содержат строковые значения аналитики Целевого учета. С допущением, что в рамках одного документа будет использоваться один разрез Целевого учета, нас устроил бы вариант установки фильтра по этим полям.
В ходе изысканий пытались найти возможность создания и программной установки пользовательских фильтров, но данного функционала не нашли.

Максимум, чего удалось добиться - использовать событие cmFilterPick. То есть мы конфигуратором вытащили эти переменные в колонки Browse и докомпилировали .

Код: Выделить всё

alter interface vschetB;    //Совместительство

    overload
    end;
      handleEvent
          cmFilterPick:{
            //message('Открылся наш обработчик');
            case (CurField) of
              #VSKAU1:{
                //message('Здесь будет выбор объекта строительства для фильтра');
                var ks_nrec:comp;
                ks_nrec:=0;
                if RunInterface(L_Uks::katstroy,0, true, ks_nrec, false, true)=cmDefault {
                  if getfirst katstroy where ((ks_nrec==katstroy.nrec))=tsOk {

                    FilterPickVar :=katstroy.name;
                  }
                }
              }
              #VSKAU3:{
                //message('Здесь будет выбор сметы для фильтра');
                var nm:string;
                if RunInterface(iSmetaUrRO, nm)=cmDefault {
                  FilterPickVar := nm;
                }
              }
            end;
          }

      end;
end.
Но и тут есть проблемы:
1. Не устанавливается строковый фильтр на сконфигурированную колонку.
2. В обработчике события cmFilterPick мы можем определить только текущее поле интерфейса, в котором был фокус перед вызовом Пользовательских фильтров, а логичнее выглядит в данном случае поле, на которое настроен конкретный пользовательский фильтр, так как в данном интерфейсе планируется использовать фильтры по четырем полям.

3б. Установка фильтра по внешнему атрибуту, привязанному к шапке документа.
К шапке ДО привязан атрибут типа ссылка на Persons.

4. Сортировка списка документов по выбранному полю (опять же, либо по переменной логической таблицы, либо по внешнему атрибуту)

В целом, я изложил все возможные варианты. Нас устроил бы даже один из них, например, только сортировка по полю, на котором находится фокус, либо только удобная фильтрация.
Подскажите пожалуйста, какие есть варианты решения данной задачи
Аватара пользователя
larin
топ-софт
Сообщения: 228
Зарегистрирован: Пн, 10/09/2007 12:13
Имя Фамилия: Михаил Ларин
Откуда: ТопCофт
Контактная информация:

Сообщение larin »

Правильным направлением в решении этой задачи будет, использование всяческих возможностей докомпиляции интерфейсов (т.е. все что может конструкция alter interface). Кроме перекрытия/дополнения обработчиков событий, можно также дополнить логическую таблицу интерфейса (добавить таблицы в запрос, добавить ограничения condition, добавить подцепки bounds и сортировки order).

Вот под рукой оказался готовый пример. В котором иллюстрируется, примем программирования с помощью которого можно добавить в окно "Разноска хозяйственных операций" модуля "Хозяйственные операции" установку специфичного фильтра по клавише [Alt+5].

- Применяется конструкция alter interface SoprHoz;
- дополняется логическая таблица новым фильтром
- добавляется обработчик события cmAlt5

Таким же образом можно доработать и ваш интерфейс.

Код: Выделить всё

#component "F_SoprHoz"

alter interface SoprHoz;

  Create view SoprHozView as
  select
    TTNDoc.*
  from
    TTNDoc
  bounds    Violenti01 = 1109 == TTNDoc.wTable and SoprHoz.cSoprDoc == TTNDoc.cDoc
  condition Violenti02 = (TTNDoc.SPLIST = 'eee');

  HandleEvent
    cmAlt5:
    {
      if( BoundActive(tbViolenti01) )
      {
        if(Message('Снять фильтр от Виолентия?', YesNo) = cmYes)
        {
          SubBounds(tbViolenti01);
          PopCondition(tcViolenti02);
          RereadRecord;
        }
      }
      else
      {
        if(Message('Установить фильтр от Виолентия?', YesNo) = cmYes)
        {
          AddBounds(tbViolenti01);
          PushCondition(tcViolenti02);
          RereadRecord;
        }
      }
    }
  end;
end.
PS: Реализовать сортировку по строковым значения аналитики целевого учета вряд ли конечно получится. Без внешней выгрузки в память это не возможно. Но фильтрацию организовать можно.
Hershy
рег.отделение
Сообщения: 64
Зарегистрирован: Вт, 03/03/2009 07:53
Имя Фамилия: Николай Воронцов
Откуда: Галактика-Урал
Контактная информация:

Сообщение Hershy »

Вот так да
воспользовался примером, написал такое

Код: Выделить всё

alter interface VSCHETB;
    Create view
    var
        par01
            : String;
    condition Cond1 = (VSKAU1 = par1);
    overload
    end;
    handleEvent
        cmFilterSave:{
            case RunMenu('mnuSetFilterType') of
            cmValue1:{
                inherited:: handleEvent (cmFilterSave);
            }
            cmValue2:{
                if(not readmydsk(par01,'054par01',true)){
                    par01 := '';
                }
                if(RunInterface('l_basedoc::setcheck', par01) = cmOK){
                    SaveMyDsk(par01,'054par01');
                }else{
                }
                if(par01 <> ''){
                    if( ConditionActive(tcCond1)){
                            PopCondition(tcCond1);
                    }
                    PushCondition(tcCond1);
                    RereadRecord;
                }
            }
            end;
        }//cmFilterPick
    end;//handleevents
end.
в любом случае при установке фильтра ни один документ не удовлетворяет Cond1, таблица пустая

может дело в том, что нельзя ставить ограничения на поля, которых нет в базе, как например VSKAU1, которая описана в логической таблице к VSCHETB, как GetKAUField(1) ( FieldName = vsKau1 )

такие вот проблемы
Аватара пользователя
larin
топ-софт
Сообщения: 228
Зарегистрирован: Пн, 10/09/2007 12:13
Имя Фамилия: Михаил Ларин
Откуда: ТопCофт
Контактная информация:

Сообщение larin »

А если попробовать вместо

Код: Выделить всё

PushCondition(tcCond1); 
сделать

Код: Выделить всё

PushConditionForLeave(tcCond1, tnSpStep);
Hershy
рег.отделение
Сообщения: 64
Зарегистрирован: Вт, 03/03/2009 07:53
Имя Фамилия: Николай Воронцов
Откуда: Галактика-Урал
Контактная информация:

Сообщение Hershy »

изменил вышеприведенный код вот так

Код: Выделить всё

                if(par01 <> ''){
                    // устанавливаем ограничения на об.стр.
                    if( ConditionActiveInLeave (tcCond1, tnBASEDOC)){
                        PopConditionForLeave(tcCond1, tnBASEDOC);
                    }
                    PushConditionForLeave(tcCond1, tnBASEDOC);
                }else{
                    if( ConditionActiveInLeave (tcCond1, tnBASEDOC)){
                        PopConditionForLeave(tcCond1, tnBASEDOC);
                    }
                }
                RereadRecord;
У меня есть сомнения, что узел нужно ставить SPSTEP, однако сделал так

Код: Выделить всё

                if(par01 <> ''){
                    // устанавливаем ограничения на об.стр.
                    if( ConditionActiveInLeave (tcCond1, tnSPSTEP)){
                        PopConditionForLeave(tcCond1, tnSPSTEP);
                    }
                    PushConditionForLeave(tcCond1, tnSPSTEP);
                }else{
                    if( ConditionActiveInLeave (tcCond1, tnSPSTEP)){
                        PopConditionForLeave(tcCond1, tnSPSTEP);
                    }
                }
                RereadRecord;
В первом случае с tnBASEDOC, отработал также как и обычный opCondition, т.е. показал пустой browse, во втором случае вообще ничего не сделал.
Аватара пользователя
larin
топ-софт
Сообщения: 228
Зарегистрирован: Пн, 10/09/2007 12:13
Имя Фамилия: Михаил Ларин
Откуда: ТопCофт
Контактная информация:

Сообщение larin »

Погодите а какой вам нужно список отфильтровать? список самих ДО или список товарных позиций в ДО?
Hershy
рег.отделение
Сообщения: 64
Зарегистрирован: Вт, 03/03/2009 07:53
Имя Фамилия: Николай Воронцов
Откуда: Галактика-Урал
Контактная информация:

Сообщение Hershy »

В том то и дело, смысл сей задачи в следующем
1. Есть ДО
2. К ДО есть спецификация
3. У всех позиций спецификации одинаковый КАУ1 (объект строительства)
4. У ДО есть вычисляемое поле VSKAU1, которое по неизвестным мне правилам показывает наименование объекта строительства из спецификации

Сама задача:
Поставить фильтр на ДО с ограничениями на определенный (выбранный) объект строительства

Пляски с бубном уже подсказывают мне, что это не возможно

Ну а если совсем просто, то
Можно ли поставить фильтр на документ, по полю из спецификации этого документа?
den
заказчик
Сообщения: 117
Зарегистрирован: Пт, 26/10/2007 14:16
Имя Фамилия: Денис Кучин
Откуда: Геомостпроект НПО

Сообщение den »

Да уж. При большом размере данных долго все это будет работать. Я бы не стал тут с кондишинами развлекаться так. Может можно пойти по простому пути и перекрыть какое-нить событие на локальном меню ? А в нем реализовать выбор из стандарт фейса нужного объекта строит-ва, и затем с помощью директивы dsql получить список таких до :

select distinct b.brec from
basedoc b, stepdoc st, spstep sps, specmtr mtr
where nrec_obj = mtr.COBJ
and nrec_saldtune = mtr.CSALDTUNE
and 1104=mtr.COTABLE
and mtr.cspec = sps.nrec
and sps.cstepdoc = st.nrec
and st.cbasedoc = b.nrec
and b.viddoc=201

ну а далее наложить баундс на этот отобранное множество basedoc.nrec

Ну или тоже самое помощью родных конструкций языка vip ,используя индекс SPECMTR06. Просто в первом варианте "за один присест" Вы их получите....
Последний раз редактировалось den Пт, 24/04/2009 08:25, всего редактировалось 1 раз.
Аватара пользователя
larin
топ-софт
Сообщения: 228
Зарегистрирован: Пн, 10/09/2007 12:13
Имя Фамилия: Михаил Ларин
Откуда: ТопCофт
Контактная информация:

Сообщение larin »

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

Алгоритм примерно такой:
- при установке фильтра просканировать документы и их спецификации,
- признак совпадения документа с условием фильтрации записать в таблицу в памяти
- логическую таблицу интерфейса дополнить жесткой подцепкой BASEDOC на эту таблицу в памяти

По производительности это конечно будет не самое лучшее, что может быть вообще в природе, но работать вроде будет. :shuffle:
cruger
топ-софт
Сообщения: 566
Зарегистрирован: Пт, 21/09/2007 15:19
Имя Фамилия: Фёдор Терсин
Откуда: Галактика Софт
Контактная информация:

Сообщение cruger »

Насколько я понял, цель - отфильтровать ЛТ так, что бы в корне были только те записи, для которых есть хотя бы одна запись из подцепляемой, удовлетворяющая определённым условиям.
Сходу посоветовал бы рассмотреть вариант заведения дополнительного узла подцепленной таблицы с наложенными на неё требуемыми условиями и с жёсткой подцепкой к корневой.
Hershy
рег.отделение
Сообщения: 64
Зарегистрирован: Вт, 03/03/2009 07:53
Имя Фамилия: Николай Воронцов
Откуда: Галактика-Урал
Контактная информация:

Сообщение Hershy »

Здравствуйте всем.

Воспользовался помощью Михаила Ларина и Дениса Кучина

Получилось следующее

Код: Выделить всё

//-----------------------------------------------------------------------------
mnuSetFilterType Menu
{
- 'Стандартные фильтры',      cmValue1, '', , , , sci1Esc;
- 'Пользовательские фильтры', cmValue2, '', , '', , sci1Esc;
}
//-----------------------------------------------------------------------------
TABLE STRUCT LOCAL MTFILTR "ТАБЛИЦА ФиЛЬТРА"(
  NREC     : COMP
, CBASEDOC : COMP
)WITH INDEX(
    MT1 = NREC(surrogate),
    MT2 = CBASEDOC
);
//------------------------------------------------------------------
alter interface VSCHETB;
          
    Create view
    var
        par01
            : COMP;

    as select
       SALDTUNESV.*,
       STEPDOCSV.*,
       SPSTEPSV.*,
       specmtrSV.*,
       katstroySV.*,
       BASEDOCSV.*
    FROM

        SYNONYM BASEDOC  BASEDOCSV,
        SYNONYM SALDTUNE SALDTUNESV,
        SYNONYM STEPDOC  STEPDOCSV,
        SYNONYM SPSTEP   SPSTEPSV,
        SYNONYM specmtr  specmtrSV,
        SYNONYM katstroy katstroySV,
        MTFILTR

    bounds    Boun0541 = BASEDOC.NREC /== MTFILTR.CBASEDOC;

    overload
    end;

    handleEvent
        // для меню L_BASEDOC::MNUVSCHETB2
        cmFilterSave:{
            case RunMenu('mnuSetFilterType') of
            cmValue1:{
                inherited:: handleEvent (cmFilterSave);
            }
            cmValue2:{
                if(RunInterface('l_basedoc::setcheck', par01) = cmOK){
                    SaveMyDsk(par01,'054par01');
                }else{
                }
                delete ALL MTFILTR;
                var sqltime : TIME;
                if(par01 <> 0){
                    if(chbox1 = 0){
                        // устанавливаем ограничения на об.стр.
                        sqltime := Cur_Time;
                        StartNewVisual(vtRotateVisual, vfThread+vfScreenBottom+vfTimer, 'Заполнение фильтра vip', 1);
                        _loop BASEDOCSV {
                            NEXTVISUAL;
                            if(getfirst STEPDOCSV where ((  101 /== BASEDOCSV.VIDDOC
                                                        AND BASEDOCSV.NREC /== STEPDOCSV.CBASEDOC
                                                        AND STEPDOCSV.NREC /== SPSTEPSV.CSTEPDOC
                                                        AND 1104 /== SPECMTRSV.COTABLE
                                                        AND SPSTEPSV.NREC /== SPECMTRSV.CSPEC
                                                        AND par01 /== SPECMTRSV.COBJ)) = tsOK){
                                insert MTFILTR
                                SET
                                CBASEDOC := BASEDOCSV.NREC;
                            }
                        }
                        STOPVISUAL('Заполнение фильтра закончена',0);
                        message('Время построения vip '+TimeToStr (Sub_Time(Cur_Time, sqltime),'MM.SS'));
                    }
                    PUTTABLETODBF(#MTFILTR, 'MTFILTR.dbf',ptfFullTable);
                    if(BoundActive (tbBoun0541)){
                        PopBounds(tbBoun0541);
                    }
                    PushBounds(tbBoun0541);
                }else{
                    if( BoundActive(tbBoun0541)){
                        PopBounds(tbBoun0541);
                    }//par01
                }
                RereadRecord;
            }
            end;
        }//cmFilterPick
    end;//handleevents
end.
Все работает.
Оттестировали, получается примерно 50 секунд на базу SQL, с 40 т. записями в BASEDOC. Не очень быстро, мягко говоря. Плюс сильно затруднено позиционирование (перемещение) между ДО, после установки фильтра (примерно 2-3 сек). Однако, проблема решена и это главное.
Реализовали через обычный vip, сейчас пробуем через dsql, возможно создание таблицы в памяти будет быстрее, но позиционирование, думаю, останется таким же долгим.
Всем большое спасибо.
den
заказчик
Сообщения: 117
Зарегистрирован: Пт, 26/10/2007 14:16
Имя Фамилия: Денис Кучин
Откуда: Геомостпроект НПО

Сообщение den »

Ес-но так долго будет. А Вы что хотели - у вас же перелопачиваются все ДО со спецификацией по цепочке прописанной по вью. А нужно уже взять заранее известные ДО по нужному объекту строит-ва :

даже такой в лоб перебор без оптимизации должен, по идее, работать быстрее :

_loop specmtr where ((
nrec_obj == specmtr.COBJ
and nrec_saldtune == specmtr.CSALDTUNE
and 1104==specmtr.COTABLE
))
{
if getfirst fastfirstrow spstep where ((specmtr.cspec ==spstep.nrec ))=tsok
if getfirst fastfirstrow stepdoc where ((spstep.cstepdoc==stepdoc.nrec))=tsok
if getfirst fastfirstrow basedoc where ((stepdoc.cbasedoc==basedoc.nrec))=tsok
{
if 101 = BASEDOCSV.VIDDOC
{
//здесь проверяем есть ли такой basedoc.nre уже в MTFILTR
}
}
}

Насчет позиционирования(перемещения) по фейсу, тут да - тяжелая ситуевина. Могу ошибаться ,конечно, и пусть подскажут знающие, но может быть убыстрить сможет тока переориентация корневой таблы на MTFILTR и с помощью все тех же баундсов наложение/снятие их.
Т.е. нужно сделать чтобы перекладывалось при активизации такого фильтра что то типа :

MTFILTR.cbasedoc == basedoc.nrec

а не наоборот жесткая подцепка. Хотя может насчет этого я и не прав совсем - может просто напросто вся вьюха слететь в основ логической таблице интерфейса :-)
Ответить