понедельник, 31 августа 2015 г.

Разработка GUI для SAP с использованием JScript часть 3

Итак с божьей помощью удалось реализовать требуемый интерфейс. Но в тех.задании было указано что несколько полей имеют фиксированный диапазон значений - а конкретно от 0 до 3 с шагом 0.5. Можно конечно попытаться реализовать проверку самостоятельно, но я поддался природной лени и решил поискать как остальные люди справляются с подобными задачами.
В результате поисков я наткнулся на библиотеку jquery-ui.js - все красиво, рисуются нужные элементы управления, осталось прикрутить библиотеку к SAP.
Для начала я скачал все входящие в библиотеку файлы и поправил пути в библиотеке стилей jquery-ui.css и в библиотеке jquery-ui.js. Далее я загрузил все файлы (включая PNG) библиотеки в репозиторий SAP (транзакция SMW0). В процессе загрузки пришлось создать новый MIME тип png/image через ведение mime типов
После этого я попытался выгрузить файлы библиотеки вот таким SAP кодом:
 
Код

   call method creat_html_control->load_mime_object 
            exporting object_url = 'jquery-ui.css' 
                      object_id = 'z_jquery_ui' 
       exceptions others = 1. 

   call method creat_html_control->load_mime_object 
            exporting object_url = 'jquery-ui.structure.css' 
                      object_id = 'Z_jquery_ui_structure_css' 
       exceptions others = 1. 

   call method creat_html_control->load_mime_object 
            exporting object_url = 'jquery-ui.theme.css' 
                      object_id = 'z_jquery_ui_theme_css' 
       exceptions others = 1. 

   call method creat_html_control->load_mime_object 
            exporting object_url = 'jquery-ui.js' 
                      object_id = 'Z_jquery-ui_js' 
       exceptions others = 1. 

   call method creat_html_control->load_mime_object 
            exporting object_url = 'jquery.js' 
                      object_id = 'z_jquery_js' 
       exceptions others = 1. 


   call method creat_html_control->load_mime_object 
            exporting object_url = 'ui-bg_diagonals-thick_18_b81900_40x40.png' 
                      object_id = 'Z_ui-bg_diagonals-thick_18_b81900_40x40' 
       exceptions others = 1. 

   call method creat_html_control->load_mime_object 
            exporting object_url = 'ui-bg_diagonals-thick_20_666666_40x40.png' 
                      object_id = 'Z_ui-bg_diagonals-thick_20_666666_40x40' 
       exceptions others = 1. 

   call method creat_html_control->load_mime_object 
            exporting object_url = 'ui-bg_flat_10_000000_40x100.png' 
                      object_id = 'Z_ui-bg_flat_10_000000_40x100' 
       exceptions others = 1. 

   call method creat_html_control->load_mime_object 
            exporting object_url = 'ui-bg_glass_100_f6f6f6_1x400.png' 
                      object_id = 'Z_ui-bg_glass_100_f6f6f6_1x400' 
       exceptions others = 1. 

   call method creat_html_control->load_mime_object 
            exporting object_url = 'ui-bg_glass_100_fdf5ce_1x400.png' 
                      object_id = 'z_ui-bg_glass_100_fdf5ce_1x400' 
       exceptions others = 1. 

   call method creat_html_control->load_mime_object 
            exporting object_url = 'ui-bg_glass_65_ffffff_1x400.png' 
                      object_id = 'Z_ui-bg_glass_65_ffffff_1x400' 
       exceptions others = 1. 


   call method creat_html_control->load_mime_object 
            exporting object_url = 'ui-bg_gloss-wave_35_f6a828_500x100.png' 
                      object_id = 'Z_ui-bg_gloss-wave_35_f6a828_500x100' 
       exceptions others = 1. 

   call method creat_html_control->load_mime_object 
            exporting object_url = 'ui-bg_highlight-soft_100_eeeeee_1x100.png' 
                      object_id = 'Z_ui-bg_highlight-soft_100_eeeeee_1x100' 
       exceptions others = 1. 

   call method creat_html_control->load_mime_object 
            exporting object_url = 'ui-bg_highlight-soft_75_ffe45c_1x100.png' 
                      object_id = 'z_ui-bg_highlight-soft_75_ffe45c_1x100' 
       exceptions others = 1. 

   call method creat_html_control->load_mime_object 
            exporting object_url = 'ui-icons_222222_256x240.png' 
                      object_id = 'z_ui-icons_222222_256x240' 
       exceptions others = 1. 

   call method creat_html_control->load_mime_object 
            exporting object_url = 'ui-icons_228ef1_256x240.png' 
                      object_id = 'z_ui-icons_228ef1_256x240' 
       exceptions others = 1. 

   call method creat_html_control->load_mime_object 
            exporting object_url = 'ui-icons_ef8c08_256x240.png' 
                      object_id = 'z_ui-icons_ef8c08_256x240' 
       exceptions others = 1. 

   call method creat_html_control->load_mime_object 
            exporting object_url = 'ui-icons_ffd27a_256x240.png' 
                      object_id = 'z_ui-icons_ffd27a_256x240' 
       exceptions others = 1. 

   call method creat_html_control->load_mime_object 
            exporting object_url = 'ui-icons_ffffff_256x240.png' 
                      object_id = 'z_ui-icons_ffffff_256x240' 
       exceptions others = 1.

Вроде все отработало без ошибок, элементы управления создавались как и описано в документации. Но картинки на кнопках управления не появлялись. Так как ссылки на png ресурсы были прописаны в CSS библиотеке, то я предположил что при выгрузке из репозитория они располагаются так что нативный HTML не видит пути к этим файлам. И точно, если перенести ссылки на png в сам шаблон - то все отображается и работает. Вот так выглядит заголовок в шаблоне, на основании которого строится HTML интерфейс:
Код
<html>
  <head>
    <link href="jquery-ui.css" rel="stylesheet">
    <script src="jquery.js"></script>
    <script src="jquery-ui.js"></script>
  <style>
    .ui-icon {
   width: 16px;
   height: 16px;
      }
    .ui-icon,
    .ui-widget-content .ui-icon {
   background-image: url("ui-icons_222222_256x240.png");
      }
    .ui-widget-header .ui-icon {
   background-image: url("ui-icons_ffffff_256x240.png");
      }
    .ui-state-default .ui-icon {
   background-image: url("ui-icons_ef8c08_256x240.png");
      }
    .ui-state-hover .ui-icon,
    .ui-state-focus .ui-icon {
   background-image: url("ui-icons_ef8c08_256x240.png");
      }
    .ui-state-active .ui-icon {
   background-image: url("ui-icons_ef8c08_256x240.png");
      }
    .ui-state-highlight .ui-icon {
   background-image: url("ui-icons_228ef1_256x240.png");
      }
    .ui-state-error .ui-icon,
    .ui-state-error-text .ui-icon {
   background-image: url("ui-icons_ffd27a_256x240.png");
      }
  </style>
  </head> 

воскресенье, 30 августа 2015 г.

Разработка GUI для SAP с использованием JScript часть 2

Для реализации случая с отсутствием события OnChange (пользователь просто вышел из HTML окна), пришлось опробовать несколько вариантов: 

    1. Обработка события OnKeyUp элемента ввода данных - В этом случае при каждом нажатии кнопки значение передается в SAP, но так как обработка событий осуществляется в синхронном режиме, то при быстром вводе текста пропадает половина символов. 
    2. Фиксация введенных данных во внутреннюю переменную при событии onKeyUp и по таймеру (ну например 10 секунд) отправка данных в SAP. В принципе работает, но есть вероятность что в момент отправки данных пользователь начнет опять вводить данные - в этом случае опять пропадет символ. Пропаданий символов гораздо меньше чем в первом случае - но они есть. 
    3. Обработка события onUnload элемента <BODY>. Идея в том чтобы передать данные в момент разрушения HTML объекта. Не работает, событие возникает, но SAP не реагирует на передаваемую ссылку. 
    4. Вызов скрипта в HTML со стороны SAP. На первый взгляд такое невозможно, но в классе cl_gui_html_viewer есть пара хитрых методов: set_script и execute_script, правда они protected, но их можно использовать если реализовать потомка. Как это сделать можно посмотреть в примере - SAPHTML_SCRIPT_DEMO. Этот вариант также не подходит для реализации поставленной задачи - SAP также не реагирует на сгенерированную таким образом ссылку, хотя возможность запускать скрипты из кода SAP довольно интересная. 
    5. Комбинированная обработка событий onKeyUp на элементе ввода и onMouseOut на контейнере, который содержит все элементы. Этот вариант полностью подошел, при этом данные не теряются и передаются в SAP. Вот пример кода в HTML шаблоне: 
    Код

    <table onm ouseout="sendKey();">
    <tr>
    <td><textarea onc hange="sendVal(this)" onk eyup="evntKey(this)" name="FIELD"></td>
    </tr>
    </table> 

а вот пример обработчиков: 

Код

    <script>
    var curr_element;
    function evntKey(e){
    curr_element=e;
    }
    function sendKey()
    {
    var l_str=curr_element.value;
    var la_array=l_str.match(/.{1,250}/g);
    var vl_str='SAPEVENT:KeyData?';
    for(var i=0; i<la_array.length; i++){
      if (i>0) {vl_str=vl_str+'&';}
      vl_str=vl_str+curr_element.name+'_'+i+'='+la_array[i];
      }
    location.href=vl_str;
    }
  


    function sendVal(e)
    {
    var l_str=e.value;
    var la_array=l_str.match(/.{1,250}/g);
    var vl_str='SAPEVENT:KeyData?';
    for(var i=0; i<la_array.length; i++){
      if (i>0) {vl_str=vl_str+'&';}
      vl_str=vl_str+e.name+'_'+i+'='+la_array[i];
      }
    location.href=vl_str;
    } 

Разработка GUI для SAP с использованием JScript

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

data: 
   creat_html_control TYPE REF TO cl_gui_html_viewer, "Непосредственно экземпляр класса 
   creat_container TYPE REF TO cl_gui_custom_container, "Конейнер на форме для размещения 
   doc_url(80). "Имя, которое будет присвоено шаблону после выгрузки. По этому имени внутри HTML можно делать ссылки 

  IF creat_html_control IS INITIAL. 
    CREATE OBJECT creat_container"Создаем контейнер 
        EXPORTING 
            container_name = 'HTML_CONTAINER'."Имя контейнера на форме 
    CREATE OBJECT creat_html_control"Создаем экземпляр 
         EXPORTING 
              parent    = creat_container. 
    IF sy-subrc NE 0. 
    ENDIF. 
  ENDIF. 
  doc_url = 'htm1.htm'. 
  CALL METHOD creat_html_control->load_html_document "Загружаем шаблон 
     EXPORTING 
          document_id  = 'ZPA_TEMPL_9840_ADV' "Имя шаблона в репозитории - транзакция SMW0 
     IMPORTING 
          assigned_url = doc_url 
     EXCEPTIONS 
          OTHERS       = 1. 
  CALL METHOD creat_html_control->show_url 
     EXPORTING 
          url = doc_url. 

  CL_GUI_HTML_VIEWER=>set_focus( creat_html_control ). 

Следующий шаг при работе с HTML - это обработка событий. Событие, связанное с действиями внутри HTML кода у класса всего одно - SAPEVENT. Параметров у события немного: 

  • ACTION 
  • FRAME 
  • GETDATA 
  • POSTDATA 
  • QUERY_TABLE 

Я использовал только ACTION - имя события, которое можно задать в HTML и QUERY_TABLE - представляет из себя таблицу с двумя полями - NAME и VALUE, что позволяет передавать через него несколько значений за раз. Вот пример реализации обработки событий, в примере реализована обработка события с передачей одного значения: 

Код

CLASS cl_creat_handler IMPLEMENTATION.

  METHOD on_sapevent.

  data:
       ls_q_table like line of query_table,
       ls_frontend_val like line of gt_frontend_val,
       edaction(100).
  field-symbols:
      <fs_val> type gty_frontend_values.

    edaction = action. "Имя события
    case edaction.
      when 'GetData' or 'KeyData'.
        loop at query_table into ls_q_table. "Смотрим таблицу с получаемыми значениями
          condense ls_q_table-name.
          ls_frontend_val-name = ls_q_table-name.
          concatenate ls_frontend_val-value ls_q_table-value into ls_frontend_val-value. "Если в таблице значений больше одной записи, то соберем их все в оду переменную
        endloop.
        read table gt_frontend_val assigning <fs_val> with key name = ls_frontend_val-name.
        if sy-subrc <> 0.
          append ls_frontend_val to gt_frontend_val.
        else.
          <fs_val>-value = ls_frontend_val-value.
        endif.


    endcase.
  ENDMETHOD.

ENDCLASS.   " cl_creat_handler   implementation
"Ну и вот так присваивается обработчик:
    DAT A:
      creat_evt_receiver TYPE REF TO cl_creat_handler,
      myevent_tab TYPE cntl_simple_events,
      myevent TYPE cntl_simple_event.

    myevent-eventid = creat_html_control->m_id_sapevent.
    myevent-appl_event = 'x'.
    APPEND myevent TO myevent_tab.

    CALL METHOD creat_html_control->set_registered_events
        EXPORTING
           events = myevent_tab.

    CREATE OBJECT creat_evt_receiver.

    SET HANDLER creat_evt_receiver->on_sapevent
                FOR creat_html_control. 


При первом подходе к реализации интерфейса я не пользовался никакими дополнительными ресурсами - только шаблон в WEB репозитории SAP (транзакция SMW0) и код в SAP. Вот что получилось: 

    Для реализации этого интерфейса пришлось очень много писать в SAP кода оформления, плюс отслеживать события нажатия на вкладки и перерисовывать всю страницу каждый раз при переключении вкладок. 
    Теперь про обещанные грабли: 
    Первое - разметка HTML - все размеры, смещения, отступы надо задавать в пикселях (px), иначе есть вероятность что у какого-нибудь пользователя вся красота расползется. 
    Второе и самое главное - результаты заполнения полей ввода. Так как класс SAP не имеет никакого представления об объектах внутри шаблона HTML, то за передачу правильных данных отвечает исключительно HTML шаблон. Вот здесь и появляется обещанный в заголовке JScript. 
    Итак для полей ввода возможны несколько ситуаций, когда надо передать данные: 





  • ввод значения завершен и пользователь переходит в другое поле для продолжения ввода 
  • ввод значения завершен и пользователь нажимает кнопку "Сохранить" в интерфейсе SAP 


Первое событие отловить очень просто - это onChange тэга <input>. Вот пример привязки обработчика: 

Код

<input type="text" name="Z_OB_1" size=2" onc hange="sendVal(this)"> 


а вот как выглядит скрипт, который обрабатывает событие (указывается в HTML шаблоне в <HEAD>): 
Код

    function sendVal(e)
    {
    var l_str=e.value;
    var la_array=l_str.match(/.{1,250}/g);
    var vl_str='SAPEVENT:KeyData?';
    for(var i=0; i<la_array.length; i++){
      if (i>0) {vl_str=vl_str+'&';}
      vl_str=vl_str+e.name+'_'+i+'='+la_array[i];
      }
    location.href=vl_str;
    } 
В скрипте осуществляется разбиение значения поля ввода на куски по 250 символов (на стороне SAP значение большего размера обрезаются), затем формируется SAP ссылка, которая инициирует событие SAPEVENT на стороне SAP. Ссылка должна иметь следующий вид: SAPEVENT:Имя_события?имя_переменной1 = значение1&имя_переменной2 = значение2 теоретически переменных и значений может быть сколь угодно много. Далее командой location.href передаем полученное значение в SAP.