Текущее время: Пн, июн 10 2024, 15:42

Часовой пояс: 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 часа


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

Сейчас этот форум просматривают: Google [Bot]


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

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