Текущее время: Чт, мар 28 2024, 18:16

Часовой пояс: UTC + 3 часа


Правила форума


ВНИМАНИЕ!

Вопросы по SAP Query и Quick View - сюда



Начать новую тему Ответить на тему  [ Сообщений: 10 ] 
Автор Сообщение
 Заголовок сообщения: Генерация DOCX ABAPом.
СообщениеДобавлено: Вт, май 28 2013, 10:27 
Почетный гуру
Почетный гуру
Аватара пользователя

Зарегистрирован:
Ср, июн 01 2005, 09:40
Сообщения: 536
Откуда: Belgorod
Пол: Мужской
Всем привет!
Никак не доходили руки довести до рабочей версии свою поделку, но вроде как получилось.

Ниже класс генерации отчета в формате DOCX средствами АБАП кода.
Плюсы и минусы такого подхода описывать не имеет смысла, всем и так понятно.
Если будут ошибки пишите, оперативно не исправлю, как будет возможность сделаю.
Если вы нашли и поправили баг, буду вам благодарен.

Сделал сразу с программой примером ее использования.
Здесь после кода программы идет файл 1.docx, который надо скопировать в 'c:\temp'.
Готовый файл упадет туда же с именем 3.docx.
Code:
*&---------------------------------------------------------------------*
*& Report  ZHCM_TEST
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*

REPORT zhcm_test.

*----------------------------------------------------------------------*
*       CLASS lcl_docx DEFINITION
*----------------------------------------------------------------------*
* Поделка: Братковский Михаил можно просто Брат Мигель
* версия один.и все нули
*
* Класс генерации отчетов в формате DOCX
* при создании класса на вход передается строка xstring, содержащая
* шаблон файла docx.
* формат объявления полей в шаблоне следующий:
* 1. если это не табличное поле, то оно должно начинаться на #0
* 2. если это поле таблицы, то оно должно начинантсья на #1 - #9,
* где 1-9 значения параметра iv_table.
* 3. После №0-№9 идет наименование поля.
* 4. После наименования поля идет #.
* Например: поле для простой замены #0DATE#
* ВАЖНО! Все поле надо вписывать за один раз (без редактирования),
* Если редактировать пол частично,
* то почему-то текст развивается на разные теги
*
* 5. строка таблицы 1-9 может состоять из множества строк в шаблоне,
* 6. перед внесением данных для новой строки табличной части, необходимо
* вызывать метод new_line (для полей простой замены это не требуется).
* 7. методы внесения данных (методы подходят для внесения любого поля):
* 8. add_field - добавляет для заполнения одно поле.
* 9. add_structure - добавляет все поля структуры
* 10. add_table - добавляет все строки по всей структуре
* 11. процесс заполнения осуществляется методом fill_data
* 12. получения результирующего отчета - get_document
*
* что надо доделать: причесать код программы, добавить кучу проверок,
* добавить преобразование полей согласно типа
*----------------------------------------------------------------------*
CLASS lcl_docx DEFINITION.
  PUBLIC SECTION.
    METHODS new_line.
    METHODS constructor
      IMPORTING
         string TYPE xstring.
    METHODS add_field
      IMPORTING
         iv_table TYPE string DEFAULT '0'
         iv_field TYPE string
         iv_value TYPE any.
    METHODS get_document
      RETURNING
        value(rv_string) TYPE xstring.
    METHODS clear_field_value.
    METHODS fill_data.
    METHODS clear_used_blocks.
    METHODS add_structure
      IMPORTING
         iv_table TYPE string
         is_data TYPE any.
    METHODS add_table
      IMPORTING
         iv_table TYPE string
         it_data TYPE any.

  PRIVATE SECTION.
    TYPE-POOLS: ixml, abap.
    CLASS cl_ixml DEFINITION LOAD.

    TYPES: BEGIN OF ts_vfields,
             number TYPE i,
             table TYPE num1,
             fieldname TYPE string,
             fieldrep TYPE string,
             value TYPE string,
           END OF ts_vfields.

    TYPES: tt_vfields TYPE TABLE OF ts_vfields WITH NON-UNIQUE KEY number table fieldname.
    TYPES: BEGIN OF ts_nodes_tables,
             table TYPE num1,
             gid TYPE i,
           END OF ts_nodes_tables.
    TYPES: tt_nodes_tables TYPE TABLE OF ts_nodes_tables WITH NON-UNIQUE KEY table.

    TYPES: BEGIN OF ts_nodes_fields,
             table TYPE num1,
             gid TYPE i,
             index TYPE i,
           END OF ts_nodes_fields.
    TYPES: tt_nodes_fields TYPE TABLE OF ts_nodes_fields WITH NON-UNIQUE KEY table.
    TYPES: BEGIN OF ts_number_tables,
             number TYPE i,
             table TYPE num1,
           END OF ts_number_tables.
    TYPES: tt_number_tables TYPE TABLE OF ts_number_tables WITH NON-UNIQUE KEY number table.

    TYPES: BEGIN OF ts_table_fields,
             table TYPE num1,
             fieldname TYPE string,
             fieldtype TYPE char1,
           END OF ts_table_fields.
    TYPES: tt_table_fields TYPE TABLE OF ts_table_fields WITH NON-UNIQUE KEY table fieldname.

    CLASS-DATA:  gr_document       TYPE REF TO if_ixml_document
                ,gr_istream        TYPE REF TO if_ixml_istream
                ,gr_ixml           TYPE REF TO if_ixml
                ,gr_ostream        TYPE REF TO if_ixml_ostream
                ,gr_parser         TYPE REF TO if_ixml_parser
                ,gr_renderer       TYPE REF TO if_ixml_renderer
                ,gr_stream_factory TYPE REF TO if_ixml_stream_factory
                ,gr_zipper         TYPE REF TO cl_abap_zip
                ,gt_fields_val     TYPE tt_vfields
                ,gt_nodes_fields   TYPE tt_nodes_fields
                ,gt_nodes_tables   TYPE tt_nodes_tables
                ,gt_number_tables  TYPE tt_number_tables
                ,gt_table_fields   TYPE tt_table_fields
                ,gv_document       TYPE xstring
                ,gv_number         TYPE i.
    METHODS set_field_value
      IMPORTING
         ir_node TYPE REF TO if_ixml_node
         iv_number TYPE i.
    METHODS check_fields.
    METHODS check_tables.
    METHODS check_report.
ENDCLASS.                    "lcl_docx DEFINITION

*----------------------------------------------------------------------*
*       CLASS lcl_docx IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_docx IMPLEMENTATION.
  METHOD new_line.
    gv_number = gv_number + 1.
  ENDMETHOD.                    "NEW_LINE

  METHOD constructor.

    CREATE OBJECT gr_zipper.
    gr_zipper->load( string ).
    gr_zipper->get( EXPORTING name = 'word/document.xml' IMPORTING content = gv_document ).
    gr_ixml = cl_ixml=>create( ).
    gr_stream_factory = gr_ixml->create_stream_factory( ).
    gr_istream = gr_stream_factory->create_istream_xstring( gv_document ).
    gr_document = gr_ixml->create_document( ).
    gr_parser = gr_ixml->create_parser( stream_factory = gr_stream_factory
                                        istream        = gr_istream
                                        document       = gr_document ).
    gr_parser->parse( ).
    gv_number = 1.
  ENDMETHOD.                    "constructor

  METHOD add_field.
    DATA: ls_fields_val TYPE ts_vfields,
          ls_number_tables TYPE ts_number_tables.
    IF iv_table = '0'.
      ls_number_tables-number = ls_fields_val-number = 1.
    ELSE.
      ls_number_tables-number = ls_fields_val-number = gv_number.
    ENDIF.
    ls_number_tables-table = ls_fields_val-table = iv_table.
    READ TABLE gt_number_tables WITH KEY number = gv_number table = iv_table BINARY SEARCH TRANSPORTING NO FIELDS.
    IF sy-subrc IS NOT INITIAL.
      APPEND ls_number_tables TO gt_number_tables.
    ENDIF.
    ls_fields_val-fieldname = iv_field.
    CONCATENATE '#' iv_table iv_field '#' INTO ls_fields_val-fieldrep.
    ls_fields_val-value = iv_value.
    APPEND ls_fields_val TO gt_fields_val.
  ENDMETHOD.                    "add_field

  METHOD get_document.
    DATA: lv_document TYPE xstring.
    gr_ostream = gr_stream_factory->create_ostream_xstring( string = lv_document ).
    gr_renderer = gr_ixml->create_renderer( ostream  = gr_ostream
                                            document = gr_document ).
    gr_renderer->render( ).
    gr_zipper->delete( EXPORTING name = 'word/document.xml' ).
    gr_zipper->add( EXPORTING name = 'word/document.xml' content = lv_document ).
    rv_string = gr_zipper->save( ).
  ENDMETHOD.                    "get_document

  METHOD clear_field_value.
    CLEAR: gt_fields_val[].
    gv_number = 1.
  ENDMETHOD.                    "CLEAR_FIELD_VALUE

  METHOD fill_data.
    DATA: lr_cnode TYPE REF TO if_ixml_node.
    DATA: lr_nnode TYPE REF TO if_ixml_node.
    DATA: lr_pnode TYPE REF TO if_ixml_node.
    DATA: lr_fnode TYPE REF TO if_ixml_node,
          lr_ffilter TYPE REF TO if_ixml_node_filter,
          lr_fiterator TYPE REF TO if_ixml_node_iterator.
    DATA: ls_field_val TYPE ts_vfields,
          ls_nodes_tables TYPE ts_nodes_tables,
          ls_number_tables TYPE ts_number_tables.
    DATA: lv_number TYPE i,
          lv_gid TYPE i,
          lv_index TYPE i,
          lv_tabix TYPE i.

    me->check_report( ).

    SORT gt_fields_val BY number.
    DO gv_number TIMES.
      lv_number = sy-index.
      READ TABLE gt_number_tables WITH KEY number = lv_number BINARY SEARCH TRANSPORTING NO FIELDS.
      IF sy-subrc IS INITIAL.
        lv_tabix = sy-tabix.
      ELSE.
        lv_tabix = 1.
      ENDIF.
      LOOP AT gt_number_tables FROM lv_tabix INTO ls_number_tables.
        IF ls_number_tables-number NE lv_number. EXIT. ENDIF.
        LOOP AT gt_nodes_tables INTO ls_nodes_tables WHERE table = ls_number_tables-table.
          IF ls_nodes_tables-table > '0'.
            lr_cnode = gr_document->find_from_gid( ls_nodes_tables-gid ).
            lr_nnode = lr_cnode->clone( ).
            lr_pnode = lr_cnode->get_parent( ).
            lr_pnode->append_child( lr_nnode ).
            lv_gid = lr_nnode->get_gid( ).
            lr_ffilter = lr_nnode->create_filter_name( name = '#text' ).
            lr_fiterator = lr_nnode->create_iterator_filtered( lr_ffilter ).
            lr_fnode = lr_fiterator->get_next( ).
            WHILE lr_fnode IS NOT INITIAL.
              lv_index = sy-index.
              READ TABLE gt_nodes_fields WITH KEY table = ls_nodes_tables-table gid = ls_nodes_tables-gid index = lv_index TRANSPORTING NO FIELDS.
              IF sy-subrc IS INITIAL.
                me->set_field_value( ir_node = lr_fnode iv_number = lv_number ).
              ENDIF.
              lr_fnode = lr_fiterator->get_next( ).
            ENDWHILE.
            FREE: lr_ffilter, lr_fiterator.
          ELSE.
            lr_fnode = gr_document->find_from_gid( ls_nodes_tables-gid ).
            me->set_field_value( ir_node = lr_fnode iv_number = lv_number ).
          ENDIF.
        ENDLOOP.
      ENDLOOP.
    ENDDO.
    clear_used_blocks( ).
    clear_field_value( ).
  ENDMETHOD.                    "fill_data

  METHOD clear_used_blocks.
    DATA: lr_node TYPE REF TO if_ixml_node.
    DATA: ls_nodes_tables TYPE ts_nodes_tables.

    LOOP AT gt_nodes_tables INTO ls_nodes_tables WHERE table > '0'.
      lr_node = gr_document->find_from_gid( ls_nodes_tables-gid ).
      IF lr_node IS NOT INITIAL.
        lr_node->remove_node( ).
      ENDIF.
    ENDLOOP.
  ENDMETHOD.                    "clear_used_blocks

  METHOD add_structure.
    DATA: lr_struc_descr TYPE REF TO cl_abap_structdescr,
          lt_fields TYPE ddfields,
          ls_fields TYPE LINE OF ddfields.
    DATA: lt_components TYPE abap_compdescr_tab,
          ls_components TYPE LINE OF abap_compdescr_tab.

    DATA: lv_field TYPE string,
          lv_value TYPE string.

    FIELD-SYMBOLS: <value> TYPE any.
    new_line( ).
    lr_struc_descr ?= cl_abap_structdescr=>describe_by_data( is_data ).
    LOOP AT lr_struc_descr->components INTO ls_components.
      ASSIGN COMPONENT sy-tabix OF STRUCTURE is_data TO <value>.
      lv_field = ls_components-name."ls_fields-fieldname.
      lv_value = <value>.
      add_field( iv_table = iv_table iv_field = lv_field  iv_value = lv_value ).
    ENDLOOP.
  ENDMETHOD.                    "add_structure

  METHOD add_table.
    FIELD-SYMBOLS: <table> TYPE ANY TABLE,
                   <structure> TYPE any.
    ASSIGN it_data TO <table>.
    LOOP AT <table> ASSIGNING <structure>.
      add_structure( iv_table = iv_table is_data = <structure> ).
    ENDLOOP.
  ENDMETHOD.                    "add_table

  METHOD set_field_value.
    DATA: ls_fields_val TYPE ts_vfields.
    DATA: lv_value TYPE string,
          lv_count TYPE i,
          lv_tabix TYPE i.

    lv_value = ir_node->get_value( ).
*< loop optimize в таком виде loop работает на порядок быстрее, но таблица должна быть отсортирована по ключу
    READ TABLE gt_fields_val WITH KEY number = iv_number BINARY SEARCH TRANSPORTING NO FIELDS.
    IF sy-subrc IS INITIAL.
      lv_tabix = sy-tabix.
    ELSE.
      lv_tabix = 1.
    ENDIF.
    LOOP AT gt_fields_val FROM lv_tabix INTO ls_fields_val.
      IF ls_fields_val-number NE iv_number.
        EXIT.
      ENDIF.
      REPLACE ls_fields_val-fieldrep IN lv_value WITH ls_fields_val-value.
      IF sy-subrc = 0.
        lv_count = 1.
      ENDIF.
    ENDLOOP.
*> loop optimize
    IF lv_count = 1.
      ir_node->set_value( lv_value ).
    ENDIF.
  ENDMETHOD.                    "set_field_value


  METHOD check_fields.
    DATA: lr_cnode TYPE REF TO if_ixml_node.
    DATA: lr_filter TYPE REF TO if_ixml_node_filter,
          lr_iterator TYPE REF TO if_ixml_node_iterator.
    DATA: ls_nodes_fields TYPE ts_nodes_fields,
          ls_nodes_tables TYPE ts_nodes_tables.
    DATA: lv_gid TYPE i,
          lv_num TYPE num1,
          lv_count TYPE i,
          lv_value TYPE string,
          lv_tname TYPE text2 VALUE '#0'.
    lr_filter = gr_document->create_filter_name( name = '#text').
    lr_iterator = gr_document->create_iterator_filtered( lr_filter ).
    lr_cnode = lr_iterator->get_next( ).
    WHILE lr_cnode IS NOT INITIAL.
      lv_gid = lr_cnode->get_gid( ).
      lv_value = lr_cnode->get_value( ).
      FIND lv_tname IN lv_value MATCH COUNT lv_count.
      IF lv_count > 0.
        READ TABLE gt_nodes_tables WITH KEY table = 0 gid = lv_gid TRANSPORTING NO FIELDS.
        IF sy-subrc IS NOT INITIAL.
          ls_nodes_tables-table = 0.
          ls_nodes_tables-gid = lv_gid.
          APPEND ls_nodes_tables TO gt_nodes_tables.
        ENDIF.
      ENDIF.
      lr_cnode = lr_iterator->get_next( ).
    ENDWHILE.
  ENDMETHOD.                    "check_fields

  METHOD check_tables.
    DATA: lr_tnode TYPE REF TO if_ixml_node,
          lr_fnode TYPE REF TO if_ixml_node,
          lr_tfilter TYPE REF TO if_ixml_node_filter,
          lr_ffilter TYPE REF TO if_ixml_node_filter,
          lr_titerator TYPE REF TO if_ixml_node_iterator,
          lr_fiterator TYPE REF TO if_ixml_node_iterator.
    DATA: ls_nodes_fields TYPE ts_nodes_fields,
          ls_nodes_tables TYPE ts_nodes_tables.
    DATA: lv_gid TYPE i,
          lv_index TYPE i,
          lv_num TYPE num1,
          lv_count TYPE i,
          lv_value TYPE string,
          lv_tname TYPE text2.
    lr_tfilter = gr_document->create_filter_name( name = 'tr' namespace = 'w' ).
    lr_titerator = gr_document->create_iterator_filtered( lr_tfilter ).
    lr_tnode = lr_titerator->get_next( ).
    WHILE lr_tnode IS NOT INITIAL.
      lv_gid = lr_tnode->get_gid( ).
*      me->check_fields( lv_gid ).
      lr_ffilter = lr_tnode->create_filter_name( name = '#text' ).
      lr_fiterator = lr_tnode->create_iterator_filtered( lr_ffilter ).
      lr_fnode = lr_fiterator->get_next( ).
      WHILE lr_fnode IS NOT INITIAL.
        CLEAR lv_count.
        lv_num = 1.
        ls_nodes_fields-index = sy-index.
        ls_nodes_fields-gid = lv_gid.
        lv_value = lr_fnode->get_value( ).
        DO 10 TIMES.
          CONCATENATE '#' lv_num INTO lv_tname.
          FIND lv_tname IN lv_value MATCH COUNT lv_count.
          IF lv_count > 0.
            ls_nodes_fields-table = lv_num.
            APPEND ls_nodes_fields TO gt_nodes_fields.
            EXIT.
          ENDIF.
          lv_num = lv_num + 1.
        ENDDO.
        IF lv_count > 0.
          READ TABLE gt_nodes_tables WITH KEY table = lv_num gid = lv_gid TRANSPORTING NO FIELDS.
          IF sy-subrc IS NOT INITIAL.
            ls_nodes_tables-table = lv_num.
            ls_nodes_tables-gid = lv_gid.
            APPEND ls_nodes_tables TO gt_nodes_tables.
          ENDIF.
        ENDIF.
        lr_fnode = lr_fiterator->get_next( ).
      ENDWHILE.
      FREE: lr_ffilter, lr_fiterator, lr_fnode.
      lr_tnode = lr_titerator->get_next( ).
    ENDWHILE.
  ENDMETHOD.                    "check_fields

  METHOD check_report.
    me->check_tables( ).
    me->check_fields( ).
  ENDMETHOD.                    "check_tables
ENDCLASS.                    "lcl_docx IMPLEMENTATION


PARAMETERS p_file TYPE text250 DEFAULT 'c:\temp\1.docx'.
DATA: l_data_xml TYPE xstring.
DATA: l_filename TYPE string.

START-OF-SELECTION.
  l_filename = p_file.
  TRY.
      l_data_xml = cl_openxml_helper=>load_local_file( l_filename ).
    CATCH
      cx_openxml_not_found.
  ENDTRY.
  DATA: gr_docx TYPE REF TO lcl_docx.
  CREATE OBJECT gr_docx
    EXPORTING
      string = l_data_xml.
  gr_docx->add_field( iv_field = 'DATE1' iv_value = '01.01.2013').
  gr_docx->add_field( iv_field = 'FIO' iv_value = 'Иванов Иван Иванович').
  DO 1000 TIMES.
    gr_docx->new_line( ).
    gr_docx->add_field( iv_table = '1' iv_field = 'DATA' iv_value = 'text1 table1').
    gr_docx->add_field( iv_table = '1' iv_field = 'NUM' iv_value = sy-index ).
    gr_docx->new_line( ).
    gr_docx->add_field( iv_table = '1' iv_field = 'DATA' iv_value = 'text2 table1').
    gr_docx->add_field( iv_table = '1' iv_field = 'NUM' iv_value = '1.2.').

    gr_docx->new_line( ).
    gr_docx->add_field( iv_table = '2' iv_field = 'DATA' iv_value = 'text1 table2').
    gr_docx->add_field( iv_table = '2' iv_field = 'NUM' iv_value = '2.1.').
    gr_docx->new_line( ).
    gr_docx->add_field( iv_table = '2' iv_field = 'DATA' iv_value = 'text2 table2').
    gr_docx->add_field( iv_table = '2' iv_field = 'NUM' iv_value = '2.2.').

    gr_docx->new_line( ).
    gr_docx->add_field( iv_table = '1' iv_field = 'DATA' iv_value = 'text3 table1').
    gr_docx->add_field( iv_table = '1' iv_field = 'NUM' iv_value = '1.3.').
  ENDDO.
  gr_docx->fill_data( ).
  l_data_xml = gr_docx->get_document( ).
  cl_openxml_helper=>store_local_file( im_file_name = 'c:\temp\3.docx' im_data = l_data_xml ).

_________________
Новый этап на проекте - устранение доработок :).


Принять этот ответ
Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Генерация DOCX ABAPом.
СообщениеДобавлено: Вт, май 28 2013, 10:58 
Специалист
Специалист

Зарегистрирован:
Вт, ноя 28 2006, 16:02
Сообщения: 114
На каких объемах данных тестировали?
Классы IXML весьма тормознутые.


Принять этот ответ
Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Генерация DOCX ABAPом.
СообщениеДобавлено: Вт, май 28 2013, 11:03 
Почетный гуру
Почетный гуру
Аватара пользователя

Зарегистрирован:
Ср, июн 01 2005, 09:40
Сообщения: 536
Откуда: Belgorod
Пол: Мужской
Fugitive написал(а):
На каких объемах данных тестировали?
Классы IXML весьма тормознутые.


В примере генерируется 230 страничный документ.
Время генерации 8 секунд.

_________________
Новый этап на проекте - устранение доработок :).


Принять этот ответ
Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Генерация DOCX ABAPом.
СообщениеДобавлено: Вт, май 28 2013, 11:19 
Старший специалист
Старший специалист

Зарегистрирован:
Вт, мар 30 2010, 09:04
Сообщения: 258
Брат Мигель написал:
Fugitive написал(а):
На каких объемах данных тестировали?
Классы IXML весьма тормознутые.


В примере генерируется 230 страничный документ.
Время генерации 8 секунд.
Брат Мигель, почему с ворда-то начали? :)


Принять этот ответ
Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Генерация DOCX ABAPом.
СообщениеДобавлено: Вт, май 28 2013, 11:24 
Старший специалист
Старший специалист

Зарегистрирован:
Чт, окт 22 2009, 12:41
Сообщения: 473
Классно, очень благодарен! Обязательно попробую использовать.
Кстати, вы не пробовали abap2docx, какие самые принципиальные отличия от него?


Принять этот ответ
Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Генерация DOCX ABAPом.
СообщениеДобавлено: Вт, май 28 2013, 11:27 
Почетный гуру
Почетный гуру
Аватара пользователя

Зарегистрирован:
Ср, июн 01 2005, 09:40
Сообщения: 536
Откуда: Belgorod
Пол: Мужской
homoSAPience написал(а):
Брат Мигель, почему с ворда-то начали? :)

Ну мысль была такая, что генерировать документы и приказы можно было бы: в фоне, сохранять журнал отчетов вместе с самим файлом ну и т.д.
Если говорить об экселе, у него посложнее структура файла.
Да и для экселя наверняка есть уже что-то подобное.

_________________
Новый этап на проекте - устранение доработок :).


Принять этот ответ
Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Генерация DOCX ABAPом.
СообщениеДобавлено: Вт, май 28 2013, 13:44 
Почетный гуру
Почетный гуру
Аватара пользователя

Зарегистрирован:
Ср, июн 01 2005, 09:40
Сообщения: 536
Откуда: Belgorod
Пол: Мужской
weise написал(а):
Кстати, вы не пробовали abap2docx, какие самые принципиальные отличия от него?

Честно говоря не видел, может и зря наколхозил :).

_________________
Новый этап на проекте - устранение доработок :).


Принять этот ответ
Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Генерация DOCX ABAPом.
СообщениеДобавлено: Вт, май 28 2013, 16:58 
Старший специалист
Старший специалист

Зарегистрирован:
Чт, окт 22 2009, 12:41
Сообщения: 473
Нет, точно не зря. Тут все довольно компактно и понятно, а там не самый дружелюбный интерфейс, хотя развиваются давно.


Принять этот ответ
Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Генерация DOCX ABAPом.
СообщениеДобавлено: Чт, ноя 11 2021, 15:30 
Младший специалист
Младший специалист

Зарегистрирован:
Пн, мар 19 2012, 15:00
Сообщения: 76
Это задублированное сообщение, админы, удалите, пожалуйста :D


Последний раз редактировалось Benoni Чт, ноя 11 2021, 15:46, всего редактировалось 1 раз.

Принять этот ответ
Вернуться к началу
 Профиль Отправить email  
 
 Заголовок сообщения: Re: Генерация DOCX ABAPом.
СообщениеДобавлено: Чт, ноя 11 2021, 15:31 
Младший специалист
Младший специалист

Зарегистрирован:
Пн, мар 19 2012, 15:00
Сообщения: 76
Привет, Брат Мигель!
А можешь перезалить файлик с 1.docx, хотел поизучать твою программу?

Спасибо!

Брат Мигель написал:
Всем привет!
Никак не доходили руки довести до рабочей версии свою поделку, но вроде как получилось.

Ниже класс генерации отчета в формате DOCX средствами АБАП кода.
Плюсы и минусы такого подхода описывать не имеет смысла, всем и так понятно.
Если будут ошибки пишите, оперативно не исправлю, как будет возможность сделаю.
Если вы нашли и поправили баг, буду вам благодарен.

Сделал сразу с программой примером ее использования.
Здесь после кода программы идет файл 1.docx, который надо скопировать в 'c:\temp'.
Готовый файл упадет туда же с именем 3.docx.
Code:
*&---------------------------------------------------------------------*
*& Report  ZHCM_TEST
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*

REPORT zhcm_test.

*----------------------------------------------------------------------*
*       CLASS lcl_docx DEFINITION
*----------------------------------------------------------------------*
* Поделка: Братковский Михаил можно просто Брат Мигель
* версия один.и все нули
*
* Класс генерации отчетов в формате DOCX
* при создании класса на вход передается строка xstring, содержащая
* шаблон файла docx.
* формат объявления полей в шаблоне следующий:
* 1. если это не табличное поле, то оно должно начинаться на #0
* 2. если это поле таблицы, то оно должно начинантсья на #1 - #9,
* где 1-9 значения параметра iv_table.
* 3. После №0-№9 идет наименование поля.
* 4. После наименования поля идет #.
* Например: поле для простой замены #0DATE#
* ВАЖНО! Все поле надо вписывать за один раз (без редактирования),
* Если редактировать пол частично,
* то почему-то текст развивается на разные теги
*
* 5. строка таблицы 1-9 может состоять из множества строк в шаблоне,
* 6. перед внесением данных для новой строки табличной части, необходимо
* вызывать метод new_line (для полей простой замены это не требуется).
* 7. методы внесения данных (методы подходят для внесения любого поля):
* 8. add_field - добавляет для заполнения одно поле.
* 9. add_structure - добавляет все поля структуры
* 10. add_table - добавляет все строки по всей структуре
* 11. процесс заполнения осуществляется методом fill_data
* 12. получения результирующего отчета - get_document
*
* что надо доделать: причесать код программы, добавить кучу проверок,
* добавить преобразование полей согласно типа
*----------------------------------------------------------------------*
CLASS lcl_docx DEFINITION.
  PUBLIC SECTION.
    METHODS new_line.
    METHODS constructor
      IMPORTING
         string TYPE xstring.
    METHODS add_field
      IMPORTING
         iv_table TYPE string DEFAULT '0'
         iv_field TYPE string
         iv_value TYPE any.
    METHODS get_document
      RETURNING
        value(rv_string) TYPE xstring.
    METHODS clear_field_value.
    METHODS fill_data.
    METHODS clear_used_blocks.
    METHODS add_structure
      IMPORTING
         iv_table TYPE string
         is_data TYPE any.
    METHODS add_table
      IMPORTING
         iv_table TYPE string
         it_data TYPE any.

  PRIVATE SECTION.
    TYPE-POOLS: ixml, abap.
    CLASS cl_ixml DEFINITION LOAD.

    TYPES: BEGIN OF ts_vfields,
             number TYPE i,
             table TYPE num1,
             fieldname TYPE string,
             fieldrep TYPE string,
             value TYPE string,
           END OF ts_vfields.

    TYPES: tt_vfields TYPE TABLE OF ts_vfields WITH NON-UNIQUE KEY number table fieldname.
    TYPES: BEGIN OF ts_nodes_tables,
             table TYPE num1,
             gid TYPE i,
           END OF ts_nodes_tables.
    TYPES: tt_nodes_tables TYPE TABLE OF ts_nodes_tables WITH NON-UNIQUE KEY table.

    TYPES: BEGIN OF ts_nodes_fields,
             table TYPE num1,
             gid TYPE i,
             index TYPE i,
           END OF ts_nodes_fields.
    TYPES: tt_nodes_fields TYPE TABLE OF ts_nodes_fields WITH NON-UNIQUE KEY table.
    TYPES: BEGIN OF ts_number_tables,
             number TYPE i,
             table TYPE num1,
           END OF ts_number_tables.
    TYPES: tt_number_tables TYPE TABLE OF ts_number_tables WITH NON-UNIQUE KEY number table.

    TYPES: BEGIN OF ts_table_fields,
             table TYPE num1,
             fieldname TYPE string,
             fieldtype TYPE char1,
           END OF ts_table_fields.
    TYPES: tt_table_fields TYPE TABLE OF ts_table_fields WITH NON-UNIQUE KEY table fieldname.

    CLASS-DATA:  gr_document       TYPE REF TO if_ixml_document
                ,gr_istream        TYPE REF TO if_ixml_istream
                ,gr_ixml           TYPE REF TO if_ixml
                ,gr_ostream        TYPE REF TO if_ixml_ostream
                ,gr_parser         TYPE REF TO if_ixml_parser
                ,gr_renderer       TYPE REF TO if_ixml_renderer
                ,gr_stream_factory TYPE REF TO if_ixml_stream_factory
                ,gr_zipper         TYPE REF TO cl_abap_zip
                ,gt_fields_val     TYPE tt_vfields
                ,gt_nodes_fields   TYPE tt_nodes_fields
                ,gt_nodes_tables   TYPE tt_nodes_tables
                ,gt_number_tables  TYPE tt_number_tables
                ,gt_table_fields   TYPE tt_table_fields
                ,gv_document       TYPE xstring
                ,gv_number         TYPE i.
    METHODS set_field_value
      IMPORTING
         ir_node TYPE REF TO if_ixml_node
         iv_number TYPE i.
    METHODS check_fields.
    METHODS check_tables.
    METHODS check_report.
ENDCLASS.                    "lcl_docx DEFINITION

*----------------------------------------------------------------------*
*       CLASS lcl_docx IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_docx IMPLEMENTATION.
  METHOD new_line.
    gv_number = gv_number + 1.
  ENDMETHOD.                    "NEW_LINE

  METHOD constructor.

    CREATE OBJECT gr_zipper.
    gr_zipper->load( string ).
    gr_zipper->get( EXPORTING name = 'word/document.xml' IMPORTING content = gv_document ).
    gr_ixml = cl_ixml=>create( ).
    gr_stream_factory = gr_ixml->create_stream_factory( ).
    gr_istream = gr_stream_factory->create_istream_xstring( gv_document ).
    gr_document = gr_ixml->create_document( ).
    gr_parser = gr_ixml->create_parser( stream_factory = gr_stream_factory
                                        istream        = gr_istream
                                        document       = gr_document ).
    gr_parser->parse( ).
    gv_number = 1.
  ENDMETHOD.                    "constructor

  METHOD add_field.
    DATA: ls_fields_val TYPE ts_vfields,
          ls_number_tables TYPE ts_number_tables.
    IF iv_table = '0'.
      ls_number_tables-number = ls_fields_val-number = 1.
    ELSE.
      ls_number_tables-number = ls_fields_val-number = gv_number.
    ENDIF.
    ls_number_tables-table = ls_fields_val-table = iv_table.
    READ TABLE gt_number_tables WITH KEY number = gv_number table = iv_table BINARY SEARCH TRANSPORTING NO FIELDS.
    IF sy-subrc IS NOT INITIAL.
      APPEND ls_number_tables TO gt_number_tables.
    ENDIF.
    ls_fields_val-fieldname = iv_field.
    CONCATENATE '#' iv_table iv_field '#' INTO ls_fields_val-fieldrep.
    ls_fields_val-value = iv_value.
    APPEND ls_fields_val TO gt_fields_val.
  ENDMETHOD.                    "add_field

  METHOD get_document.
    DATA: lv_document TYPE xstring.
    gr_ostream = gr_stream_factory->create_ostream_xstring( string = lv_document ).
    gr_renderer = gr_ixml->create_renderer( ostream  = gr_ostream
                                            document = gr_document ).
    gr_renderer->render( ).
    gr_zipper->delete( EXPORTING name = 'word/document.xml' ).
    gr_zipper->add( EXPORTING name = 'word/document.xml' content = lv_document ).
    rv_string = gr_zipper->save( ).
  ENDMETHOD.                    "get_document

  METHOD clear_field_value.
    CLEAR: gt_fields_val[].
    gv_number = 1.
  ENDMETHOD.                    "CLEAR_FIELD_VALUE

  METHOD fill_data.
    DATA: lr_cnode TYPE REF TO if_ixml_node.
    DATA: lr_nnode TYPE REF TO if_ixml_node.
    DATA: lr_pnode TYPE REF TO if_ixml_node.
    DATA: lr_fnode TYPE REF TO if_ixml_node,
          lr_ffilter TYPE REF TO if_ixml_node_filter,
          lr_fiterator TYPE REF TO if_ixml_node_iterator.
    DATA: ls_field_val TYPE ts_vfields,
          ls_nodes_tables TYPE ts_nodes_tables,
          ls_number_tables TYPE ts_number_tables.
    DATA: lv_number TYPE i,
          lv_gid TYPE i,
          lv_index TYPE i,
          lv_tabix TYPE i.

    me->check_report( ).

    SORT gt_fields_val BY number.
    DO gv_number TIMES.
      lv_number = sy-index.
      READ TABLE gt_number_tables WITH KEY number = lv_number BINARY SEARCH TRANSPORTING NO FIELDS.
      IF sy-subrc IS INITIAL.
        lv_tabix = sy-tabix.
      ELSE.
        lv_tabix = 1.
      ENDIF.
      LOOP AT gt_number_tables FROM lv_tabix INTO ls_number_tables.
        IF ls_number_tables-number NE lv_number. EXIT. ENDIF.
        LOOP AT gt_nodes_tables INTO ls_nodes_tables WHERE table = ls_number_tables-table.
          IF ls_nodes_tables-table > '0'.
            lr_cnode = gr_document->find_from_gid( ls_nodes_tables-gid ).
            lr_nnode = lr_cnode->clone( ).
            lr_pnode = lr_cnode->get_parent( ).
            lr_pnode->append_child( lr_nnode ).
            lv_gid = lr_nnode->get_gid( ).
            lr_ffilter = lr_nnode->create_filter_name( name = '#text' ).
            lr_fiterator = lr_nnode->create_iterator_filtered( lr_ffilter ).
            lr_fnode = lr_fiterator->get_next( ).
            WHILE lr_fnode IS NOT INITIAL.
              lv_index = sy-index.
              READ TABLE gt_nodes_fields WITH KEY table = ls_nodes_tables-table gid = ls_nodes_tables-gid index = lv_index TRANSPORTING NO FIELDS.
              IF sy-subrc IS INITIAL.
                me->set_field_value( ir_node = lr_fnode iv_number = lv_number ).
              ENDIF.
              lr_fnode = lr_fiterator->get_next( ).
            ENDWHILE.
            FREE: lr_ffilter, lr_fiterator.
          ELSE.
            lr_fnode = gr_document->find_from_gid( ls_nodes_tables-gid ).
            me->set_field_value( ir_node = lr_fnode iv_number = lv_number ).
          ENDIF.
        ENDLOOP.
      ENDLOOP.
    ENDDO.
    clear_used_blocks( ).
    clear_field_value( ).
  ENDMETHOD.                    "fill_data

  METHOD clear_used_blocks.
    DATA: lr_node TYPE REF TO if_ixml_node.
    DATA: ls_nodes_tables TYPE ts_nodes_tables.

    LOOP AT gt_nodes_tables INTO ls_nodes_tables WHERE table > '0'.
      lr_node = gr_document->find_from_gid( ls_nodes_tables-gid ).
      IF lr_node IS NOT INITIAL.
        lr_node->remove_node( ).
      ENDIF.
    ENDLOOP.
  ENDMETHOD.                    "clear_used_blocks

  METHOD add_structure.
    DATA: lr_struc_descr TYPE REF TO cl_abap_structdescr,
          lt_fields TYPE ddfields,
          ls_fields TYPE LINE OF ddfields.
    DATA: lt_components TYPE abap_compdescr_tab,
          ls_components TYPE LINE OF abap_compdescr_tab.

    DATA: lv_field TYPE string,
          lv_value TYPE string.

    FIELD-SYMBOLS: <value> TYPE any.
    new_line( ).
    lr_struc_descr ?= cl_abap_structdescr=>describe_by_data( is_data ).
    LOOP AT lr_struc_descr->components INTO ls_components.
      ASSIGN COMPONENT sy-tabix OF STRUCTURE is_data TO <value>.
      lv_field = ls_components-name."ls_fields-fieldname.
      lv_value = <value>.
      add_field( iv_table = iv_table iv_field = lv_field  iv_value = lv_value ).
    ENDLOOP.
  ENDMETHOD.                    "add_structure

  METHOD add_table.
    FIELD-SYMBOLS: <table> TYPE ANY TABLE,
                   <structure> TYPE any.
    ASSIGN it_data TO <table>.
    LOOP AT <table> ASSIGNING <structure>.
      add_structure( iv_table = iv_table is_data = <structure> ).
    ENDLOOP.
  ENDMETHOD.                    "add_table

  METHOD set_field_value.
    DATA: ls_fields_val TYPE ts_vfields.
    DATA: lv_value TYPE string,
          lv_count TYPE i,
          lv_tabix TYPE i.

    lv_value = ir_node->get_value( ).
*< loop optimize в таком виде loop работает на порядок быстрее, но таблица должна быть отсортирована по ключу
    READ TABLE gt_fields_val WITH KEY number = iv_number BINARY SEARCH TRANSPORTING NO FIELDS.
    IF sy-subrc IS INITIAL.
      lv_tabix = sy-tabix.
    ELSE.
      lv_tabix = 1.
    ENDIF.
    LOOP AT gt_fields_val FROM lv_tabix INTO ls_fields_val.
      IF ls_fields_val-number NE iv_number.
        EXIT.
      ENDIF.
      REPLACE ls_fields_val-fieldrep IN lv_value WITH ls_fields_val-value.
      IF sy-subrc = 0.
        lv_count = 1.
      ENDIF.
    ENDLOOP.
*> loop optimize
    IF lv_count = 1.
      ir_node->set_value( lv_value ).
    ENDIF.
  ENDMETHOD.                    "set_field_value


  METHOD check_fields.
    DATA: lr_cnode TYPE REF TO if_ixml_node.
    DATA: lr_filter TYPE REF TO if_ixml_node_filter,
          lr_iterator TYPE REF TO if_ixml_node_iterator.
    DATA: ls_nodes_fields TYPE ts_nodes_fields,
          ls_nodes_tables TYPE ts_nodes_tables.
    DATA: lv_gid TYPE i,
          lv_num TYPE num1,
          lv_count TYPE i,
          lv_value TYPE string,
          lv_tname TYPE text2 VALUE '#0'.
    lr_filter = gr_document->create_filter_name( name = '#text').
    lr_iterator = gr_document->create_iterator_filtered( lr_filter ).
    lr_cnode = lr_iterator->get_next( ).
    WHILE lr_cnode IS NOT INITIAL.
      lv_gid = lr_cnode->get_gid( ).
      lv_value = lr_cnode->get_value( ).
      FIND lv_tname IN lv_value MATCH COUNT lv_count.
      IF lv_count > 0.
        READ TABLE gt_nodes_tables WITH KEY table = 0 gid = lv_gid TRANSPORTING NO FIELDS.
        IF sy-subrc IS NOT INITIAL.
          ls_nodes_tables-table = 0.
          ls_nodes_tables-gid = lv_gid.
          APPEND ls_nodes_tables TO gt_nodes_tables.
        ENDIF.
      ENDIF.
      lr_cnode = lr_iterator->get_next( ).
    ENDWHILE.
  ENDMETHOD.                    "check_fields

  METHOD check_tables.
    DATA: lr_tnode TYPE REF TO if_ixml_node,
          lr_fnode TYPE REF TO if_ixml_node,
          lr_tfilter TYPE REF TO if_ixml_node_filter,
          lr_ffilter TYPE REF TO if_ixml_node_filter,
          lr_titerator TYPE REF TO if_ixml_node_iterator,
          lr_fiterator TYPE REF TO if_ixml_node_iterator.
    DATA: ls_nodes_fields TYPE ts_nodes_fields,
          ls_nodes_tables TYPE ts_nodes_tables.
    DATA: lv_gid TYPE i,
          lv_index TYPE i,
          lv_num TYPE num1,
          lv_count TYPE i,
          lv_value TYPE string,
          lv_tname TYPE text2.
    lr_tfilter = gr_document->create_filter_name( name = 'tr' namespace = 'w' ).
    lr_titerator = gr_document->create_iterator_filtered( lr_tfilter ).
    lr_tnode = lr_titerator->get_next( ).
    WHILE lr_tnode IS NOT INITIAL.
      lv_gid = lr_tnode->get_gid( ).
*      me->check_fields( lv_gid ).
      lr_ffilter = lr_tnode->create_filter_name( name = '#text' ).
      lr_fiterator = lr_tnode->create_iterator_filtered( lr_ffilter ).
      lr_fnode = lr_fiterator->get_next( ).
      WHILE lr_fnode IS NOT INITIAL.
        CLEAR lv_count.
        lv_num = 1.
        ls_nodes_fields-index = sy-index.
        ls_nodes_fields-gid = lv_gid.
        lv_value = lr_fnode->get_value( ).
        DO 10 TIMES.
          CONCATENATE '#' lv_num INTO lv_tname.
          FIND lv_tname IN lv_value MATCH COUNT lv_count.
          IF lv_count > 0.
            ls_nodes_fields-table = lv_num.
            APPEND ls_nodes_fields TO gt_nodes_fields.
            EXIT.
          ENDIF.
          lv_num = lv_num + 1.
        ENDDO.
        IF lv_count > 0.
          READ TABLE gt_nodes_tables WITH KEY table = lv_num gid = lv_gid TRANSPORTING NO FIELDS.
          IF sy-subrc IS NOT INITIAL.
            ls_nodes_tables-table = lv_num.
            ls_nodes_tables-gid = lv_gid.
            APPEND ls_nodes_tables TO gt_nodes_tables.
          ENDIF.
        ENDIF.
        lr_fnode = lr_fiterator->get_next( ).
      ENDWHILE.
      FREE: lr_ffilter, lr_fiterator, lr_fnode.
      lr_tnode = lr_titerator->get_next( ).
    ENDWHILE.
  ENDMETHOD.                    "check_fields

  METHOD check_report.
    me->check_tables( ).
    me->check_fields( ).
  ENDMETHOD.                    "check_tables
ENDCLASS.                    "lcl_docx IMPLEMENTATION


PARAMETERS p_file TYPE text250 DEFAULT 'c:\temp\1.docx'.
DATA: l_data_xml TYPE xstring.
DATA: l_filename TYPE string.

START-OF-SELECTION.
  l_filename = p_file.
  TRY.
      l_data_xml = cl_openxml_helper=>load_local_file( l_filename ).
    CATCH
      cx_openxml_not_found.
  ENDTRY.
  DATA: gr_docx TYPE REF TO lcl_docx.
  CREATE OBJECT gr_docx
    EXPORTING
      string = l_data_xml.
  gr_docx->add_field( iv_field = 'DATE1' iv_value = '01.01.2013').
  gr_docx->add_field( iv_field = 'FIO' iv_value = 'Иванов Иван Иванович').
  DO 1000 TIMES.
    gr_docx->new_line( ).
    gr_docx->add_field( iv_table = '1' iv_field = 'DATA' iv_value = 'text1 table1').
    gr_docx->add_field( iv_table = '1' iv_field = 'NUM' iv_value = sy-index ).
    gr_docx->new_line( ).
    gr_docx->add_field( iv_table = '1' iv_field = 'DATA' iv_value = 'text2 table1').
    gr_docx->add_field( iv_table = '1' iv_field = 'NUM' iv_value = '1.2.').

    gr_docx->new_line( ).
    gr_docx->add_field( iv_table = '2' iv_field = 'DATA' iv_value = 'text1 table2').
    gr_docx->add_field( iv_table = '2' iv_field = 'NUM' iv_value = '2.1.').
    gr_docx->new_line( ).
    gr_docx->add_field( iv_table = '2' iv_field = 'DATA' iv_value = 'text2 table2').
    gr_docx->add_field( iv_table = '2' iv_field = 'NUM' iv_value = '2.2.').

    gr_docx->new_line( ).
    gr_docx->add_field( iv_table = '1' iv_field = 'DATA' iv_value = 'text3 table1').
    gr_docx->add_field( iv_table = '1' iv_field = 'NUM' iv_value = '1.3.').
  ENDDO.
  gr_docx->fill_data( ).
  l_data_xml = gr_docx->get_document( ).
  cl_openxml_helper=>store_local_file( im_file_name = 'c:\temp\3.docx' im_data = l_data_xml ).


Принять этот ответ
Вернуться к началу
 Профиль Отправить email  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 10 ] 

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Русская поддержка phpBB