Эта публикация предназначена ABAP-разработчикам в SAP ERP и всем им сочувствующим.

Немногие знают, что в fullscreen ALV можно подключать HTML-заголовки. Еще больше не знают, что можно сделать красивый стандартный выпадающий список, он же select-box, только для такой по сути стандартной фичи, потребуется много вашего Z-кода.

Выглядит примерно так:



Добро пожаловать под кат.

Поехали! Определим глобальные переменные:

— Выходная таблица нашего отчета, пусть она будет на основе всем известной таблицы MARA;
— Переменная, в которой мы будем хранить текущее значение выбранной в селект-боксе;
— константу с подпрограммой для HTML-header;
— класс-handler, который будет срабатывать при выборе данных, и объект handler.

*----------------------------------------------------------------------*
*  Определение глобальных переменных
*----------------------------------------------------------------------*
TYPE-POOLS: slis.
TABLES:  mara.

DATA:
   gt_data  TYPE TABLE OF mara WITH HEADER LINE,
   gv_matnr TYPE mara-matnr.

CONSTANTS:
   gc_form_top  TYPE slis_formname VALUE 'DO_HTML_TOP_OF_PAGE'. 

*----------------------------------------------------------------------*
*       CLASS cl_my_event_handler DEFINITION
*----------------------------------------------------------------------*

CLASS cl_my_event_handler DEFINITION.
  PUBLIC SECTION.
    METHODS:
      handle_selections FOR EVENT selected OF cl_dd_select_element
                                    IMPORTING sender.
ENDCLASS. 

DATA:
    go_hand1  TYPE REF TO cl_my_event_handler.

Напишем главную программу отчета и подпрограммы по инициализации, получении данных и выводе отчета. Здесь особо отмечу, что нужно создать объект handler и подключить HTML-заголовок: i_callback_html_top_of_page = gc_form_top.

*----------------------------------------------------------------------*
*  Отчет
*----------------------------------------------------------------------*
SELECT-OPTIONS:
    so_matnr FOR mara-matnr.

INITIALIZATION.

  PERFORM init.

START-OF-SELECTION.

  PERFORM get_data.

END-OF-SELECTION.

  PERFORM reuse_alv.

*&---------------------------------------------------------------------*
*&      Form  init
*&---------------------------------------------------------------------*
FORM init.
  " -------------------------------------------------- "
  " Создадим объект хендлера
  " -------------------------------------------------- "
  CREATE OBJECT go_hand1.
ENDFORM.

*&---------------------------------------------------------------------*
*&      Form  get_data
*&---------------------------------------------------------------------*
FORM get_data.

  " -------------------------------------------------- "
  " Получим данные
  " -------------------------------------------------- "
  SELECT *
    FROM mara
    INTO TABLE gt_data
   WHERE matnr    IN so_matnr.
  CHECK: sy-subrc IS INITIAL.
  SORT:  gt_data  BY matnr.

ENDFORM.                    "get_data

*&---------------------------------------------------------------------*
*&      Form  reuse_alv
*&---------------------------------------------------------------------*
FORM reuse_alv.

  DATA: lt_fieldcat TYPE TABLE OF lvc_s_fcat.
  CHECK gt_data[] IS NOT INITIAL.

  " -------------------------------------------------- "
  " Вывод ALV
  " -------------------------------------------------- "
  CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
    EXPORTING
      i_structure_name       = 'MARA'
    CHANGING
      ct_fieldcat            = lt_fieldcat[]
    EXCEPTIONS
      inconsistent_interface = 1
      program_error          = 2
      OTHERS                 = 3.

  CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC'
    EXPORTING
      i_callback_program          = sy-repid
      i_callback_html_top_of_page = gc_form_top
      it_fieldcat_lvc             = lt_fieldcat[]
      i_save                      = 'A'
    TABLES
      t_outtab                    = gt_data[].

ENDFORM.                    "reuse_alv

Для HTML-хедера создаем подпрограмму с именем, указанным в глобальном константе. Вызовем из нее подпрограмму, которая создаст нашу красоту:

*&---------------------------------------------------------------------*
*&      Form  do_html_top_of_page
*&---------------------------------------------------------------------*
FORM do_html_top_of_page USING p_doc TYPE REF TO cl_dd_document.

  DATA: lo_form  TYPE REF TO cl_dd_form_area,
              lo_sele  TYPE REF TO cl_dd_select_element.

  " -------------------------------------------------- "
  " Создадим область
  " -------------------------------------------------- "
  CALL METHOD p_doc->add_form
    IMPORTING
      formarea = lo_form.

  " -------------------------------------------------- "
  " Создадим селект-бокс
  " -------------------------------------------------- "
  PERFORM add_casebox
     TABLES
       gt_data
     CHANGING
       gv_matnr
       lo_form
       lo_sele.

ENDFORM.                    "do_html_top_of_page

Посмотрим подпрограмму по созданию селект-бокса. Здесь отмечу, что если в отчете была команда, снять фильтры, зачищаем нашу глобальную переменную (gv_matnr) со значением. Потом создаем линию, в которую добавляем заголовок фильтра, вызываем подпрограмму, которая заполнит нам значения в нем, и сам фильтр на форму, закрываем линию:

*&---------------------------------------------------------------------*
*&      Form  add_casebox
*&---------------------------------------------------------------------*
FORM add_casebox
*&---------------------------------------------------------------------*
     TABLES
        it_data    STRUCTURE mara
     CHANGING
        iv_matnr   TYPE mara-matnr
        lo_form    TYPE REF TO cl_dd_form_area
        lo_selec   TYPE REF TO cl_dd_select_element.
*&---------------------------------------------------------------------*

  DATA: lt_opt_tab TYPE sdydo_option_tab,
        lv_text    TYPE sdydo_text_element.

*----------------------------------------------------------------------*

  DO.
    CASE sy-index.
      WHEN 1.

        " -------------------------------------------------- "
        " Отмена фильтров
        " -------------------------------------------------- "
        CHECK sy-ucomm  EQ '&ILD'.
        CLEAR: iv_matnr.

      WHEN 2.

        " -------------------------------------------------- "
        " Начало линии
        " -------------------------------------------------- "
        CALL METHOD lo_form->line_with_layout
          EXPORTING
            start = 'X'.

      WHEN 3.

        " -------------------------------------------------- "
        " Заголовок селект-бокса
        " -------------------------------------------------- "
        lv_text = 'Материал:'.
        CALL METHOD lo_form->add_text
          EXPORTING
            text = lv_text.

      WHEN 4.

        " -------------------------------------------------- "
        " Разделитель
        " -------------------------------------------------- "
        CALL METHOD lo_form->add_gap
          EXPORTING
            width = 2.

      WHEN 5.

        " -------------------------------------------------- "
        " Заполним таблицу значений
        " -------------------------------------------------- "
        PERFORM fill_mat_tab
            TABLES it_data
          CHANGING iv_matnr
                   lt_opt_tab.

      WHEN 6.

        " -------------------------------------------------- "
        " Добавим сам селект-бокс
        " -------------------------------------------------- "
        CALL METHOD lo_form->add_select_element
          EXPORTING
            OPTIONS        = lt_opt_tab
            value          = 'P'
          IMPORTING
            select_element = lo_selec.

      WHEN 7.

        " -------------------------------------------------- "
        " Подключим хендлер
       " -------------------------------------------------- "
        SET HANDLER go_hand1->handle_selections
                FOR lo_selec.


      WHEN 8.

        " -------------------------------------------------- "
        " Окончание линии
        " -------------------------------------------------- "
        CALL METHOD lo_form->line_with_layout
          EXPORTING
            end = 'X'.


      WHEN OTHERS.
        EXIT.
    ENDCASE.
  ENDDO.

ENDFORM. 

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

*&---------------------------------------------------------------------*
*&      Form  fill_mat_tab
*&---------------------------------------------------------------------*
FORM fill_mat_tab
*&---------------------------------------------------------------------*
          TABLES
             it_data   STRUCTURE mara
        CHANGING
             iv_matnr  TYPE matnr
             it_optab  TYPE sdydo_option_tab.
*&---------------------------------------------------------------------*

  DATA: ls_opt   TYPE sdydo_option.
  REFRESH: it_optab.

*----------------------------------------------------------------------*

  DO.
    CASE sy-index.
      WHEN 1.

        " -------------------------------------------------- "
        " Сохраним значение если оно выбрано
        " -------------------------------------------------- "
        CHECK iv_matnr IS NOT INITIAL.
        ls_opt-value = iv_matnr.
        ls_opt-text    = iv_matnr.
        APPEND ls_opt TO it_optab.

      WHEN 2.

        " -------------------------------------------------- "
        " Если значение не одно, добавим строчку - Все
        " -------------------------------------------------- "
        READ TABLE it_data INDEX 1.
        LOOP AT it_data TRANSPORTING NO FIELDS WHERE matnr NE it_data-matnr.
          ls_opt-value = '*'.
          ls_opt-text  = 'Все'.
          APPEND ls_opt TO it_optab.
          EXIT.
        ENDLOOP.

      WHEN 3.

        " -------------------------------------------------- "
        " Добавим все значения
        " -------------------------------------------------- "
        LOOP AT it_data WHERE matnr IS NOT INITIAL.
          ls_opt-value = it_data-matnr.
          ls_opt-text  = it_data-matnr.
          COLLECT ls_opt INTO it_optab.
        ENDLOOP.

      WHEN OTHERS.
        EXIT.
    ENDCASE.
  ENDDO.


ENDFORM. 

Внедрим наш handler. Здесь в sender->value значение, выбранное пользователем. Запишем его сразу в нашу глобальную переменную gv_matnr. В подпрограмме set_filter по обработке стандартной фильтрации:

1) Получим глобальный grid в локальный объект;
2) Получим уже установленные параметры фильтрации, и снимем уже установленный ранее фильтр, по полю, которое мы фильтруем, через селект-бокс;
3) Добавим новые параметры фильтрации;
4) Сохраним фильтр;
5) Обновим отчет.

*----------------------------------------------------------------------*
*       CLASS cl_my_event_handler IMPLEMENTATION
*----------------------------------------------------------------------*

CLASS cl_my_event_handler IMPLEMENTATION.


  METHOD handle_selections.

    DATA text_buff TYPE sdydo_text_element.

    text_buff = sender->value.
    gv_matnr  = text_buff.

    " -------------------------------------------------- "
    " Установим фильтр в отчете
    " -------------------------------------------------- "

    PERFORM set_filter
        TABLES
          gt_data
        USING
          gv_matnr
          .
  ENDMETHOD.                    "handle_selections

ENDCLASS.                    "cl_my_event_handler IMPLEMENTATION

*&---------------------------------------------------------------------*
*&      Form  set_filter
*&---------------------------------------------------------------------*
FORM set_filter
*&---------------------------------------------------------------------*
          TABLES
              it_data   STRUCTURE mara
          USING
              iv_value  TYPE mara-matnr.
*&---------------------------------------------------------------------*

  DATA: lo_ref1      TYPE REF TO cl_gui_alv_grid,
        lt_filtered  TYPE lvc_t_filt,
        lv_field     TYPE char10 VALUE 'MATNR',
        ls_filter    LIKE LINE OF lt_filtered.

*----------------------------------------------------------------------*

  DO.
    CASE sy-index.
      WHEN 1.

        " -------------------------------------------------- "
        " Получим объект ALV
        " -------------------------------------------------- "
        CALL FUNCTION 'GET_GLOBALS_FROM_SLVC_FULLSCR'
          IMPORTING
            e_grid = lo_ref1.

      WHEN 2.

        " -------------------------------------------------- "
        " Получим итоги и уберем фильтр уст. в прошлый раз
        " -------------------------------------------------- "
        CALL METHOD lo_ref1->get_filter_criteria
            IMPORTING
               et_filter = lt_filtered.
        DELETE lt_filtered WHERE fieldname = lv_field.

      WHEN 3.

        " -------------------------------------------------- "
        " Добавим новый фильтр
        " -------------------------------------------------- "
        CHECK iv_value NE ''
          AND iv_value NE '*'.
        ls_filter-fieldname = lv_field.
        ls_filter-sign      = 'I'.
        ls_filter-option    = 'EQ'.
        ls_filter-low       = iv_value.
        APPEND ls_filter TO lt_filtered.

      WHEN 4.

        " -------------------------------------------------- "
        " Сохраним фильтр
        " -------------------------------------------------- "
        CALL METHOD lo_ref1->set_filter_criteria
          EXPORTING
            it_filter                 = lt_filtered
          EXCEPTIONS
            no_fieldcatalog_available = 1
            OTHERS                    = 2.

        IF sy-subrc <> 0.
          MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                     WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
        ENDIF.

      WHEN 5.

        " -------------------------------------------------- "
        " Обновим ALV
        " -------------------------------------------------- "
        lo_ref1->refresh_table_display( ).

      WHEN OTHERS.
        EXIT.
    ENDCASE.
  ENDDO.

ENDFORM.  

Запускаем! Любуемся на итог страданий:





UPDATE: можно довести до:

Вроде простая штука, а в SAPе нужно постараться еще. Всем спасибо.

Комментарии (25)


  1. pavel_pimenov
    22.01.2016 11:48
    +1

    Можно добавить для сравнения скрин оригинального «не красивого» ComboBox-а
    и табличку сравнения в чем минусы-плюсы обоих решений?


    1. Endrews
      22.01.2016 12:05

      Оригинальный не возможен в ALV в принципе, Абап-такой-Абап )


      1. ivanbolhovitinov
        22.01.2016 20:06

        Как это??? ALV же не обязательно full-screen…
        Можно спокойно нарисовать экран с любыми элементами, включая лист-бокс!


        1. Endrews
          25.01.2016 08:37

          Вру конечно, может быть оригинальный не full-screen, если отдельно в контейнере нарисовать


    1. impwx
      22.01.2016 18:24
      +4

      Я тоже, прочитав фразу "красивый стандартный выпадающий список", очень захотел узнать, как в таком случае выглядит некрасивый.


  1. Rupper
    22.01.2016 12:01

    еще при первом знакомстве с абаперами понял, что гвозди бы делать из этих людей.
    Такие простыни кода для такого результата вообще никого не удивляют и не расстраивают.
    Пользователи, кстати, тоже оказываются в условиях безысходности способны на чудеса запоминания невнятных аббревиатур и кодов.


    1. Endrews
      22.01.2016 12:07

      Да уж. меня это тоже порадовало.
      Но некоторые вещи в САП действительно удобно и хорошо реализованы. НЕКОТОРЫЕ.


  1. kstep
    22.01.2016 18:19
    +2

    Вроде у нас 2016 год на дворе. Почему, боже, ну почему подавляющее большинство ERP/CRM систем выглядят как говно из 80-х?


    1. artemlight
      22.01.2016 18:38

      Потому что это никому не нужно. Интуитивность в этих системах может создать иллюзию простоты.
      Наша компания вот прям сейчас по этим граблям забег устроила — раньше была «сложная» DMS, а теперь «простая» 1С. Уже полгода расхлёбываем, и края не видно.


      1. Zveroloff
        22.01.2016 21:32
        +3

        Да бог с ней, с интуитивностью. Почему они хотя бы не могут выглядеть аккуратно? Что за страшный курсив в заголовке (он постоянно в сапе попадается)?


        1. Fragster
          23.01.2016 10:41

          Лучшее от 1с :) unf.demo.1c.ru/unf/ru_RU


    1. occam
      25.01.2016 07:49
      +1

      Полностью разделяю наблюдению о 80-х и сам регулярно задаюсь тем же вопросом. Есть, конечно, SalesForce и Wave (для управленческого учета), но из них невозможно «вынести» данные с правилами обработки. А все что в формате «сервер-клиент» заставляет задуматься о полной остановке эволюции или конкуренции. SAP, Oracle, 1C, AX и иже с ними — ужасные, громоздкие, разочаровающие тайм-киллеры, не в последнюю очередь из-за своих интерфейсов. Не терпится получить заказ на исследование о том, сколько потерь несет компания, использующая одну из вышеперечисленных erp.


    1. Endrews
      25.01.2016 08:38

      Вообщем конечно ERP-системы не для красоты.
      Интерфейс SAP убийственен, но как я слышал, сейчас уже пол Европы работает на SAP UI5, это HTML-версия интерфейса, на JavaScript


      1. ivanbolhovitinov
        26.01.2016 12:27
        +1

        Маркетинговая разводка, имхо. На этом SAP UI5 можно например водить пальцем по айпаду, смотреть красивые дашборды, можно заапрувить заказ на поставку. Увидел, нажал галочку, победил.
        А интерфейсов для *создания* заказа на поставку как не было, так и нет. Хотите плодить заказы на поставку в этой красоте неописуемой — пишите сами новый приклад.


      1. occam
        26.01.2016 20:46

        Готов с Вами спорить! Мне кажется, что «не для красоты» могут быть узкоспециализированные (отдельные — от слова «отдел») системы: учетная, финансовая, ТОиР etc. А что касается erp, которая охватывает обычно 70-80% автоматизированных рабочих мест… Извините, но это уже сопоставимо с общим для предприятия регламентом, его тоже можно написать, например, на двух страницах, а можно на 15 с длиной императивных предложений на половину A4. Практика показывает, что предприятия с последним подходом намного чаще «дохнут».


        1. occam
          26.01.2016 20:52

          И насчет пол-европы — это может быть (под вопросом?), а вот в большинстве штатовских бизнесов сап весьма даже гоним и ненужен. Поверьте, как бы смешно это не казалось, через какое-то время erp станет таким же гигееническим фактором оснащенности рабочего места, как кондиционирование или освещенность


  1. xonix
    23.01.2016 05:25

    Синтаксисом больно на Кобол похож.


    1. Rupper
      23.01.2016 18:54

      Так он из тех же готов и среды


    1. Endrews
      25.01.2016 08:40

      По сути это тот же Кобол. ABAP впитал всю мощь языков 70-х, ну и конечно, он не стоит на месте, синтаксис обновляется. Прикрутили ООП — ABAP Objects.


  1. Fragster
    25.01.2016 14:36

    А как выглядит «некрасивый» выпадающий список?


  1. ivanbolhovitinov
    25.01.2016 15:12

    Да обычный он, если просто на экране элемент рисовать


    1. Endrews
      26.01.2016 11:39

      Ок, сделайте так:
      Фишка, что это hTML, с ним можно еще по извращаться. В статье набросок.


      1. ivanbolhovitinov
        26.01.2016 12:08

        Ага, это понятно, вся красота ШТМЛ в ваших руках.
        Кстати, в тему…

        недавно наткнулся на ABAP-версию игры 2048


        Исходники есть в стандартной справке:
        help.sap.com/abapdocu_740/en/index.htm?file=abengame_2048_abexa.htm


        1. Endrews
          26.01.2016 12:55

          Looking good!


  1. avz
    26.01.2016 17:34

    А нафига везде DO?