Эта статья ориентирована на ABAP-разработчиков в системах SAP ERP. Она содержит много специфических для платформы моментов, которые малоинтересны или даже спорны для разработчиков, использующих другие платформы.

Есть такой эвфемизм: “исторически сложилось”.

Так вот, в моей основной системе исторически так сложилось, что:
  • много пользователей работают через SAP GUI for HTML;
  • практически вся отчетность выгружается в Excel через ZWWW.


А это значит что без правильно настроенной связки Браузер+Java жить непросто.

Java нужна для работы с файлами (выгрузить, загрузить). Принципиально веб-приложение должно работать только внутри своего окна и его нельзя подпускать к файловой системе пользователя даже на пушечный выстрел. С файлами должен работать лично браузер удобным ему способом, но это противоречит подходу SAP GUI, который хочет контролировать всё: показ диалога открытия, заголовок окна диалога, список доступных расширений файлов, разрешение множественного выбора, выбор каталога, чтение каталога, считывание содержимого файла или запись. Так как SAP GUI for HTML должен повторять функциональность большого брата, поэтому они там решили не менять подход, а ввести дополнительную прослойку в виде Java-апплета, который бы выполнял эти действия на стороне клиента. ABAP-часть при таком подходе остаётся практически без изменений.

Кроме этого, ZWWW работает через технологию OLE, без вариантов. А веб-приложение нельзя подпускать к OLE-интерфейсам клиентской машины даже в радиусе поражения ракет класса “земля-воздух”. Следовательно, нужна ещё одна прослойка в виде Java-апплета, которая будет проксировать OLE-вызовы и выполнять сопутствующие махинации.

Так как SAP GUI for HTML и сам является прослойкой между ABAP-инстанцией и ITS-сервером, то всё это сооружение начинает походить на игру Дженга.


Такая игра идёт постоянно. То браузеры начинают отключать старую джаву, то джава-апплеты теряют полномочия, то что-то происходит с проверкой подписи апплета, то появляются какие-то черные/белые списки исключений, то вдруг апплет начинает жутко тормозить на какой-то версии JRE, то выходит новая версия офиса, то обновляют ITS/ABAP, то пользователи в другом конце страны не могут настроить политику безопасности, то вдруг кому-то кажется что проще выставить в браузере низкий уровень безопасности …

Если следить за хронологией, то можно заметить:
Скоро останется только старый и не очень добрый IE. Евошний разработчик хоть и обещал поддерживать IE11 в Windows 10, но мы-то с вами знаем …

Что будет, если у бухгалтера внезапно отобрать возможность выгрузить любой отчет в Excel? Апокалипсис!
И вот Джавапокалипсис прилижается.



Рассмотрим варианты.

* * *
Поменять браузер (SAP GUI for HTML) на полноценный SAP GUI. Отпадает по административным показаниям.

Поменять браузер (SAP GUI for HTML) на NWBC. Но NWBC — это только оболочка вокруг того же IE, поэтому глубинных проблем данный подход не решает. Отпадает.
* * *
Заставить ZWWW работать на сервере и скидывать результат клиенту. Невозможно.
* * *
Если гора не идет к платформа не походит к движку, то можно поменять ZWWW на другой движок:
  1. Формуляры PDF
  2. Отчеты BI (BObj/BW/другое)
  3. Альтернативный движок Abap2xlsx


Все три сценария трудоёмки, но есть показания:

1. PDF генерируется на стороне сервера. Пользователь не может подправить содержимое. Некоторые формы нужны только для печати (“не отходя от кассы”) и не нужны в качестве рабочих документов в файловом виде. Самые простые примеры: приходный кассовый ордер, расходный кассовый ордер, счет к оплате и другие мелкие формы на одну страницу.

2. Некоторые отчеты нужны не только пользователям внутри системы, но и внешним пользователям, причём у этих внешних пользователей есть доступ к BI системе. Отчеты не требуют актуальности в режиме реального времени. Если в отчете есть нетривиальные расчеты, то можно выгружать итоговые расчеты в BI-систему и не заставлять BI-систему повторять эту сложную логику. Снижение нагрузки на ERP-систему. Возможна экономия на количестве лицензий. Рабочие отчеты могут остаться в системе, но без выгрузки в красивый Excel.

3. Всё оставшееся можно переписать с ZWWW на Abap2xlsx. Эта библиотека позволяет сделать генерацию файла Excel непосредственно на сервере. Подходы у этих двух библиотек кардинально разные, тут просто нельзя одни вызовы заменить на другие вызовы. Скорость существенно возрастёт, особенно для сложных документов. Необходимость поддержки старых форматов XLS уже не актуальна.

Это всё предыстория, а ради чего я начал писать-то… Допустим, переписали генерацию файла на Abap2xlsx, но его ещё и как-то скинуть на клиента надо красиво!

Скидывание файла на клиента по браузерному подходу можно выполнить с использованием функционального метода ALEWEB_DOWNLOAD. Дело-то он делает, но вот не очень красиво. Например, в нём нельзя управлять именем файла, а временные имена из случайных букв нравятся только системе, а людей от такого просто тошнит. Если покрутиться в том районе, то можно и подстучать, допилить, скопировать в Z-область. Скукота!

А можно сделать финт через сервис и HTTP-обработчик. Это очень интересно!

Первым делом создадим таблицу, например:


В эту таблицу мы будем складывать бинарные файлы, которые необходимо отдать клиенту. Сам файл хранится в поле XDATA.

HTTP-обработчики в системе реализуются в виде классов. За образец возьмём класс CL_HTTP_EXT_PING.

Создадим класс ZCL_WWW_FILE_DOWNLOAD и реализуем в нём метод HANDLE_REQUEST:

  data lt_query_string type tihttpnvp,
       ls_query_string type ihttpnvp.
  data ls_zwww_content type zwww_content.
  data lv_mimetype type string.
  data lv_filename type string.

  server->request->get_form_fields( changing fields = lt_query_string ).
  read table lt_query_string into ls_query_string 
    with key name = 'file_uid'.

  ls_zwww_content-file_uid = ls_query_string-value.
  select single * from zwww_content into ls_zwww_content 
    where file_uid = ls_zwww_content-file_uid.

  lv_mimetype = ls_zwww_content-mimetype.

  concatenate 'attachment; filename="' 
              ls_zwww_content-filename 
              '"'  into lv_filename.

  server->response->set_header_field( name = 'Content-Type' 
                                              value = lv_mimetype ).
  server->response->set_header_field( name = 'Content-Disposition' 
                                              value = lv_filename  ).

  server->response->set_data( ls_zwww_content-xdata ).

Всё тривиально:
  1. Считываем параметр file_uid из HTTP-запроса
  2. Достаём содержимое из таблицы
  3. Выставляем заголовки HTTP-ответа
  4. Скидываем содержимое файла в тело HTTP-ответа


Затем мы зарегистрируем в транзакции SICF новый сервис, и присвоим ему только что созданный обработчик:


После этого сервис станет доступен по ссылке вида:
http://host:8000/sap/bc/ZFILE_DL?file_uid=5A2D5067C04D1ED5AFB71835DF981A07

Отлично! Осталось дело за малым: сделать выгрузку.

Сначала любым способом получаем бинарный файл XLSX. Например: посредством конвертации из ALV:
  data lo_converter type ref to zcl_excel_converter.

  create object lo_converter.

  lo_converter->convert(
    exporting
      io_alv        = gr_table
      it_table      = gt_report[]
      i_row_int     = 2
      i_column_int  = 2  ).


Затем добавляем файл в таблицу:
  data ls_zwww_content type zwww_content.

  clear ls_zwww_content.

  call function 'GUID_CREATE'
    importing ev_guid_32 = ls_zwww_content-file_uid.

  ls_zwww_content-filename = sy-repid && '.xlsx'.
  ls_zwww_content-mimetype = 'application/msexcel'.
  ls_zwww_content-uname = sy-uname.
  get time stamp field ls_zwww_content-timestamp.

  lo_converter->get_file( importing e_file = ls_zwww_content-xdata ).

  insert into zwww_content values ls_zwww_content.
  commit work  and wait.


Затем пинаем загрузчик:
  data lv_url type string.

  concatenate 'http://host:8000/sap/bc/ZFILE_DL?file_uid='
               ls_zwww_content-file_uid 
              '&sap-client=100&sap-language=RU' into lv_url.

  call function 'ITS_BROWSER_WINDOW_OPEN'
    exporting
      url               = lv_url
      window_name       = '_top'
    exceptions
      its_not_available = 1
      others            = 2.


Пробуем, драфт работает!

Но как можно подход ZWWW применить к Abap2xlsx?

А дальше происходит диалог:
  • А давай переделаем все отчеты…
  • Все?
  • Их сто! А у нас есть на это время?
  • Там есть группировки, динамические столбцы и макросы!
  • Макросы на бейсике, Карл!

То же самое, но в картинках


На этой оптимистичной ноте я откланиваюсь. Спасибо за внимание и до новых встреч!

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


  1. sl4mmer
    19.01.2016 12:41
    +8

    Вся суть энтерпрайза в одном посте =)

    P.S. За отсылку к Dwarf Fortress в названиии — зачет


    1. IRainman
      20.01.2016 12:56

      И даже в одной картинке. Рыдаю до сих пор, хоть мне и очень-очень грустно.


      1. poxu
        20.01.2016 13:12

        Отсылка в Dwarf Fortress в названии? Я не заметил, поясните пожалуйста.


        1. sl4mmer
          20.01.2016 13:27

          Имеется ввиду DF-мем Котострофа/Котопакалипсис


          1. poxu
            20.01.2016 13:32
            +1

            По-моему вы видите отсылки к ДФ там, где на самом деле отсылка к Библии :).


            1. ivanbolhovitinov
              20.01.2016 14:04

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


  1. Beholder
    19.01.2016 16:32

    Вместо апплетов можно использовать Java Web Start, который пока никуда не делся.


    1. ivanbolhovitinov
      19.01.2016 17:12
      +1

      Не, тут всё не так. Java Web Start — это стэндэлоун по сути.
      А у меня тут апплеты-прокладки. Причем и апплеты не мои, и веб-приложение не моё.


  1. OnYourLips
    19.01.2016 17:22

    Как насчёт standalone приложения, в комплекте которого есть JDK и настроенный на работу с ним браузер?
    ИМХО это единственный надёжно работающий вариант, к тому же не требующий сложной установки (достаточно распаковать архив от пользователя).

    Хотя на самом деле проблема в том, что поддержка софта прекращена многие годы назад, но его жизненный цикл ещё не закончен. Поэтому проблемы ожидаемы.


    1. ivanbolhovitinov
      19.01.2016 17:44

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


  1. Jabher
    19.01.2016 17:32
    +4

    так вот из-за кого браузеры не обновляют.


  1. an24
    19.01.2016 22:16
    +2

    Мы тоже столкнулись с похожей проблемой. Решение было найдено очень остроумное. Был создан проксирующий объект(на js), который эмулировал Excel API. Он записывал все команды в специальный лог команд, затем лог отправлялся на сервер. Там он «проигрывался» и получался готовый xls. Ну а дальше все как у вас. Только в БД мы отчеты не сохраняем. Генерится уникальный url на сервлет, который тащит xls из памяти.

    В результате, программы подготовки отчета(которые работают в браузере) даже не почувствовали, что произошли какие-то изменения. Они по прежнему продолжали отправлять все в Excel. Переписали только места, где были вызовы типа Workbook.SaveAs. Их оказалось немного.


  1. poxu
    20.01.2016 14:01

    Хром, кстати, поддерживал джава апплеты только в путь. А потом первым объявил, что перестанет. С него началось всё.