Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
0 Kudos

Some of us have experience with SapScript, SmartForms and PDF based forms. Nevertheless Customers sometime wish to get an MS Office document as soon as it is ‘more familiar’.

We have some options provided by SAP or third party developers. These are:

OLE, DOI both are standard and couple versions of MS Office tools ZWWW as an example or the tool to work with RTF documents.

But let’s compare these technologies:

OLE becomes incredible slow when operating huge data, but you can do everything with your document. But use of OLE makes you hardcode types of documents. So this is a reason why it’s not flexible.

DOI works fine with huge data due to optimization of flushes. For example, you can made OLE document very fast by using queue of OLE commands and common FLUSH. But DOI provides you with this ‘fast option’ from the box. Furthermore you can still operate with OLE objects getting correspondent reference. But where are disadvantages? Well you need to create additional objects to link your GUI-session with document on a frontend such as container. Then you should know how to be with all those interfaces like I_OI_DOCUMENT_PROXY.

ZWWW is a great tool to get almost everything fast and good. You can create a template either at SMW0 or at OAOR transactions. Then you specifies logic of data mapping. This tool works fast.  Isn’t it a dream? I guess no. Because this tool operates with special files with VBA-macroses and runs it without user’s authorization. So it’s a potential harm to user’s computer.

Tool to operate RTF document works also fine but you need to create a whole document in you program. So there’s a huge question how to provide user with possibility to influence the document?

Why not to have a tool which separates design and content parts like does SmartForms or PDF-based forms?

We have documents repository (t-code OAOR) and we have web-repository (t-code SMW0). Both have possibility to store MS Office documents and templates.

We have DOI and OLE tools to manipulate document.

We want to separate development and design stages to make documents flexible.

Desired result is when developer creates a data-extraction report which includes simple call of MS Office document template. This template filled with data extracted by rules described simply and placed into the document. So a functional consultant without developer-key can manipulate design of this document at any time.

Imagine you’ve created a report that gets FI document with its positions. It’s very simple: get the BKPF record and correspondent positions from BSEG. Here are two types of data: structure (BKPF single line) and table type (BSEG positions). Then report calls document via some tool and this FI document is represented with some layout. On the other side a consultant can instantly change any parameter of the document: field’s order of described BKPF structure and their presence, column order of table content and their presence. So, while you have the simple report to extract data you can have different results with possibility to change layout even at production system directly.

Source code listed below will let you use a simple code to fill complex documents. Example of use is:

DATA: BEGIN OF interface,
bkpf  
TYPE bkpf,
bseg_t
TYPE TABLE OF bseg,
END OF interface,
lref_doi
TYPE REF TO i_oi_document_proxy.

*  Some data extraction
lref_doi
= zcl_bas_utilities=>get_doi_doc_from_so( 'FI_DOC_TEMPLATE' ).
zcl_bas_utilities
=>put_data_by_intf_into_names( ir_doi       = lref_doi
is_interface
= interface ).
message 'Save document before press `OK`!' TYPE 'I'.

Fields mapping defined by range names (for MS Excel) or bookmarks (for MS Word). For example: ‘BKPF_1’ to output the first field of structure BKPF or ‘BKPF_STBLG’ to output value of field STBLG of structure BKPF. To create column order for BSEG table we should name range like this: ‘BSEG_2_3_BETRG’. This range name will describe filling table with 3 columns of second, third and BETRG fields of table.

  Here is my code to get this tool

Class ZCL_BAS_UTILITIES.

Common part:

class ZCL_BAS_UTILITIES definition
public
final
create public .

public section.
*"* public components of class ZCL_BAS_UTILITIES
*"* do not include other source files here!!!
type-pools CNTL .
type-pools SBDST .
type-pools SLIS .

constants TRUE type CHAR01 value 'X'. "#EC NOTEXT
constants FALSE type CHAR01 value ' '. "#EC NOTEXT
constants ON type CHAR01 value '1'. "#EC NOTEXT
constants OFF type CHAR01 value '0'. "#EC NOTEXT

class-methods GET_DOI_DOC_FROM_SO
importing
!IV_DOCUMENT_NAME
type CLIKE
!IV_HIDE
type CHAR01 optional
returning
value(RR_DOI_DOC) type ref to I_OI_DOCUMENT_PROXY .
class-methods INSERT_TABLE_INTO_NAMED_RANGE
importing
!IR_DOCUMENT
type ref to I_OI_DOCUMENT_PROXY
!IT_TABLE
type STANDARD TABLE
!IV_DICTYPE
type CLIKE optional
!IV_RANGENAME
type CLIKE .
class-methods FILL_VALUE_BY_RANGE_NAME
importing
!IV_RANGE_NAME
type CLIKE
!IS_FIELD
type ZHR_NAMEDESCR_WA optional
!IR_DOCUMENT
type ref to I_OI_DOCUMENT_PROXY
!IV_NEW_VALUE
type ANY
!IV_CONCATENATE
type CHAR01 default 'X' .

  class-methods GET_NAMES_FROM_DOI
importing
!IR_DOI
type ref to I_OI_DOCUMENT_PROXY
!IV_STOR_OLE_OBJ
type CHAR01 default FALSE
returning
value(RT_NAMES) type ZHR_NAMEDESCR_ITAB .
class-methods OLE_FLUSH .
class-methods PUT_DATA_BY_INTF_INTO_NAMES
importing
!IR_DOI
type ref to I_OI_DOCUMENT_PROXY
!IS_INTERFACE
type ANY .
class-methods SAVE_DOI
importing
!IR_DOI
type ref to I_OI_DOCUMENT_PROXY
!IV_FILEPATH
type STRING optional
returning
value(RV_FILEPATH) type STRING .
class-methods OPEN_DOCUMENT
importing
!IV_FILEPATH
type STRING .
class-methods GET_PAGES
importing
!IR_DOI
type ref to I_OI_DOCUMENT_PROXY
returning
value(RV_RESULT) type INT4 .

Private part:

private section.
*"* private components of class ZCL_BAS_UTILITIES
*"* do not include other source files here!!!

class-data FIELDS_TO_UPDATE type SOI_FORM_LIST .
class-data SEPARATOR type CHAR04 .
class-data GO_DOC type CNTL_HANDLE .
class-data GO_RANGE type OBJ_RECORD .
class-data GO_BR_CELL type OBJ_RECORD .
class-data GO_LT_CELL type OBJ_RECORD .
class-data GO_APL type OBJ_RECORD .
class-data GO_NAMES type OBJ_RECORD .
class-data GO_ACTIVESHEET type OBJ_RECORD .
class-data GO_SELECTION type OBJ_RECORD .

class-methods ADD_FIELD_VALUE
importing
!IS_FIELD
type ANY
!IV_VALUE
type ANY
!IV_CONCT
type CHAR01 default 'X' .
class-methods INITIALIZE_FIELD_TO_UPDATE .
class-methods UPDATE_FIELDS
importing
!IR_DOI
type ref to I_OI_DOCUMENT_PROXY
!IT_FIELDS
type ZHR_NAMEDESCR_ITAB .
class-methods HIDE_DOI
importing
!IR_DOI
type ref to I_OI_DOCUMENT_PROXY .
class-methods FILL_VALUE_BY_BOOKMARK
importing
!IO_BOOKMARK
type OBJ_RECORD
!IV_NEW_VALUE
type ANY .
class-methods GET_WORD_TABLES
importing
!IO_DOCUMENT
type OBJ_RECORD
returning
value(RT_TABLES) type ZHR_WORDTABLE_ITAB .
class-methods INSERT_TAB_INTO_WORD_TAB
importing
!IT_TABLE
type STANDARD TABLE
!IR_WORD
type ref to I_OI_WORD_PROCESSOR_DOCUMENT
!IV_TAB_INDX
type ANY .
class-methods PUT_STR_FIELD_INT_BKMRK
importing
!IO_BOOKMARK
type OBJ_RECORD
!IT_FIELDS
type STANDARD TABLE
!IS_SOURCE
type ANY .
class-methods PUT_TABLE_INTO_BKMRK
importing
!IR_DOI
type ref to I_OI_DOCUMENT_PROXY
!IT_FIELDS
type STANDARD TABLE
!IT_SOURCE
type STANDARD TABLE
!IV_TAB_INDX
type ANY .
class-methods PUT_THE_STRUCTURE
importing
!IR_DOI
type ref to I_OI_DOCUMENT_PROXY
!IS_FIELD
type ZHR_NAMEDESCR_WA optional
!IT_FIELDS
type STANDARD TABLE
!IS_SOURCE
type ANY
!IV_FULL_NAME
type CLIKE .
class-methods PUT_THE_TABLE
importing
!IR_DOI
type ref to I_OI_DOCUMENT_PROXY
!IT_FIELDS
type STANDARD TABLE
!IT_SOURCE
type STANDARD TABLE
!IV_FULL_NAME
type CLIKE .
class-methods GET_FORM_FIELDS
importing
!IR_FORM
type ref to I_OI_FORM
changing
!CT_NAMES
type ZHR_NAMEDESCR_ITAB .
class-methods GET_WORD_BOOKMARKS
importing
!IR_DOI
type ref to I_OI_DOCUMENT_PROXY
changing
!CT_NAMES
type ZHR_NAMEDESCR_ITAB .
class-methods GET_XLS_FIELDS
importing
!IR_XLS
type ref to I_OI_SPREADSHEET
changing
!CT_NAMES
type ZHR_NAMEDESCR_ITAB .

Method’s code:

method GET_DOI_DOC_FROM_SO.
DATA: ls_bds_conn       TYPE bds_conn00,
lr_container_cont
TYPE REF TO i_oi_container_control,
lr_container     
TYPE REF TO cl_gui_custom_container,
lt_content       
TYPE sbdst_content,
lv_size          
TYPE i.

SELECT SINGLE classname classtype log_system object_key
INTO CORRESPONDING FIELDS OF ls_bds_conn
FROM bds_conn06  "Actually here can be any of bds_connNN DB tables
WHERE object_key = iv_document_name.
CHECK ls_bds_conn IS NOT INITIAL.
* Document exists

* Get document's content
cl_bds_document_set
=>get_with_table(
EXPORTING
logical_system
= ls_bds_conn-log_system
classname     
= ls_bds_conn-classname
classtype     
= ls_bds_conn-classtype
object_key    
= ls_bds_conn-object_key
CHANGING
content       
= lt_content ).

* Prepare control
c_oi_container_control_creator
=>get_container_control(
IMPORTING
control = lr_container_cont ).

CHECK lr_container_cont IS BOUND.

* Create GUI- connection
CREATE OBJECT lr_container
EXPORTING
container_name
= 'TECH_CONTAINER'
EXCEPTIONS
others         = 6.

CHECK lr_container IS BOUND.

*Initialize control
lr_container_cont
->init_control(
r3_application_name
= 'SAP'
parent             
= lr_container ).

* Initialize proxy
CALL METHOD lr_container_cont->get_document_proxy
EXPORTING
document_type 
= ''
IMPORTING
document_proxy
= rr_doi_doc.

*Fill document with data
rr_doi_doc
->open_document_from_table(
document_size
= lv_size
document_table
= lt_content ).

CHECK iv_hide is NOT INITIAL.
hide_doi
( rr_doi_doc ).
endmethod.

METHOD insert_table_into_named_range.
* Last change at 23 March 2012 by Guryanov Alexander
* Optimize rows insert to reduce dependence execution
* time from a size of the input table.
*  I've tried to reduce FLUSH commands but here is
*  the minimum.
DATA: lr_doi TYPE REF TO i_oi_document_proxy,
lr_xls
TYPE REF TO i_oi_spreadsheet,
lv_new_range
TYPE char80,
lt_fields
TYPE TABLE OF rfc_fields,
lv_lines 
TYPE i,
lv_address
TYPE char40,
lv_end_row
TYPE char20,
lv_end_column
TYPE char20,
lv_columns
TYPE i,
lv_cur_row
TYPE char20,
lv_cur_column
TYPE char20.
FIELD-SYMBOLS: <line> TYPE any.

CHECK it_table IS NOT INITIAL.

lr_doi
= ir_document.
lr_doi
->get_spreadsheet_interface( IMPORTING
sheet_interface
= lr_xls ).

CHECK lr_xls IS BOUND.
lr_doi
->get_document_handle( "EXPORTING NO_FLUSH = 'X'
IMPORTING handle = go_doc ).
GET PROPERTY OF go_doc-obj 'Application' = go_apl NO FLUSH"Get an Application object
* Calculate position of the named range
GET PROPERTY OF go_apl 'Range' = go_range
NO FLUSH
EXPORTING #1 = iv_rangename.
CALL METHOD OF go_apl 'GoTo' NO FLUSH  "
EXPORTING
#1    
= iv_rangename.
"Clear the range
GET PROPERTY OF go_range 'Cells' = go_names  "Get the first cell
NO FLUSH                                   "of the named range
EXPORTING
#1
= 1
#2
= 1.

GET PROPERTY OF go_names 'Address' = lv_address   "get its address
EXPORTING
#1
= 'True'              "absolute row address
#2
= 'True'              "absolute column address
#3
= -4150.              "RC format
CHECK lv_address IS NOT INITIAL.
SHIFT lv_address."Remove letter 'R'
SPLIT lv_address AT 'C' INTO lv_cur_row lv_cur_column.

* Calculate if some lines should be added
lv_lines
= lines( it_table ) - 1.
IF lv_lines > 0. "If we have some rows to add
*  Add number of table lines without one
lv_lines
= lv_lines + lv_cur_row - 1.

*  Get the right bottom cell of desired region
GET PROPERTY OF go_apl 'Rows' = go_br_cell
NO FLUSH
EXPORTING
#1
= lv_lines.

*  Calculate the left top cell of desired region
GET PROPERTY OF go_apl 'Rows' = go_lt_cell
NO FLUSH
EXPORTING
#1
= lv_cur_row.

CALL METHOD OF go_lt_cell 'Copy' NO FLUSH. "
*  Here is a range between left top and bottom right cells
GET PROPERTY OF go_apl 'Range' = go_range
NO FLUSH
EXPORTING
#1
= go_lt_cell
#2
= go_br_cell.

*  Insert correspondent lines
CALL METHOD OF go_range 'Insert' "
NO FLUSH
EXPORTING
#1      
= -4121
#2      
= '0'.
SET PROPERTY OF go_apl 'CutCopyMode' = 'False' NO FLUSH.
ENDIF.
GET PROPERTY OF go_apl 'Names' = go_names NO FLUSH.
CONCATENATE '=R' lv_cur_row 'C' lv_cur_column INTO lv_address.

GET TIME.
CONCATENATE iv_rangename sy-timlo
INTO lv_new_range.
CALL METHOD OF go_names 'Add'  "Create a new technical named range
NO FLUSH
EXPORTING
#1      
= lv_new_range    "generated name
#2      
= lv_address      "go_range        "For specified range
#3      
= 'False'.         "User will not see this name
*  Remove an "old" named range
GET PROPERTY OF go_names 'Item' = go_names NO FLUSH "Get named range
EXPORTING
#1
= iv_rangename.
CALL METHOD OF go_names 'Delete'. "Remove named range

CALL FUNCTION 'DP_GET_FIELDS_FROM_TABLE'
TABLES
data             = it_table
fields           = lt_fields
EXCEPTIONS
dp_invalid_table
= 1
OTHERS           = 2.

CALL METHOD lr_xls->insert_one_table
EXPORTING
data_table  
= it_table
fields_table
= lt_fields
rangename   
= lv_new_range
wholetable  
= '1'.

FREE OBJECT: go_selection,
go_names
,
go_apl
,
go_doc
-obj,
go_range
,
go_br_cell
,
go_lt_cell
,
go_activesheet
.
ENDMETHOD.

METHOD fill_value_by_range_name.
DATA: lr_doi TYPE REF TO i_oi_document_proxy,
lr_xls
TYPE REF TO i_oi_spreadsheet,
lv_new_range
TYPE char80,
lv_oldval   
TYPE text452,
lv_newval   
TYPE text452.
FIELD-SYMBOLS: <line> TYPE any.

*  IF is_field IS NOT INITIAL.
*    add_field_value( is_field = is_field
*                     iv_value = iv_new_value
*                     iv_conct = iv_concatenate ).
*    RETURN.
*  ENDIF.

lr_doi
= ir_document.
lr_doi
->get_spreadsheet_interface( IMPORTING
sheet_interface
= lr_xls ).

CHECK lr_xls IS BOUND.
lr_doi
->get_document_handle( "EXPORTING NO_FLUSH = 'X'
IMPORTING handle = go_doc ).
GET PROPERTY OF go_doc-obj 'Application' = go_apl NO FLUSH.
GET PROPERTY OF go_apl 'Range' = go_range
NO FLUSH
EXPORTING
#1
= iv_range_name .
IF iv_concatenate = 'X'.
lv_newval
= iv_new_value.
GET PROPERTY OF go_range 'Value' = lv_oldval.
IF lv_oldval IS NOT INITIAL.
CONCATENATE lv_oldval lv_newval
INTO
lv_oldval
SEPARATED BY space.
ELSE.
lv_oldval
= lv_newval.
ENDIF.

SET PROPERTY OF go_range 'Value' = lv_oldval.
ELSE.
SET PROPERTY OF go_range 'Value' = iv_new_value.
ENDIF.

FREE OBJECT:
go_range
,
go_apl
,
go_doc
-obj.
ENDMETHOD.

method GET_NAMES_FROM_DOI.
DATA: lr_form   TYPE REF TO i_oi_form,
lr_xls   
TYPE REF TO i_oi_spreadsheet,
lr_word  
TYPE REF TO I_OI_WORD_PROCESSOR_DOCUMENT.
*        lt_fields TYPE soi_form_list.
FIELD-SYMBOLS: <field> TYPE LINE OF soi_form_list,
<name> 
LIKE LINE OF rt_names.

CHECK ir_doi IS BOUND.
*  Get form interface (common interface)
ir_doi
->get_form_interface( IMPORTING f_interface = lr_form ).
*  Fill names for common interface
get_form_fields
( EXPORTING ir_form = lr_form
CHANGING ct_names = rt_names ).
*  Get spreadsheet interface
ir_doi
->get_spreadsheet_interface( IMPORTING sheet_interface = lr_xls ).
*  Fill names for Excel document
get_xls_fields
( EXPORTING ir_xls   = lr_xls
CHANGING  ct_names = rt_names ).
*  Get wordprocessor interface
*  ir_doi->get_wordprocessor_interface( IMPORTING wp_interface = lr_word ).
get_word_bookmarks
( EXPORTING ir_doi  = ir_doi
CHANGING  ct_names = rt_names ).

endmethod.

method OLE_FLUSH.
CALL FUNCTION 'FLUSH'
EXCEPTIONS
cntl_system_error
= 1
cntl_error       
= 2
OTHERS            = 3.
IF sy-subrc <> 0.
* Implement suitable error handling here
ENDIF.
endmethod.

METHOD put_data_by_intf_into_names.
DATA: lt_names      TYPE zhr_namedescr_itab,
lr_typedescr 
TYPE REF TO cl_abap_typedescr,
lr_tabledescr
TYPE REF TO cl_abap_tabledescr,
lr_linedescr 
TYPE REF TO cl_abap_typedescr,
lt_fields    
TYPE TABLE OF string,
ls_field     
type LINE OF zhr_namedescr_itab.

FIELD-SYMBOLS: <name>  LIKE LINE OF lt_names,
<field>
TYPE string,
<any1> 
TYPE any,
<any2> 
TYPE any.
lt_names
= get_names_from_doi( ir_doi ).
CHECK lt_names IS NOT INITIAL.
initialize_field_to_update
( ).
LOOP AT lt_names ASSIGNING <name>
WHERE kind <> 'FLD'.
CLEAR lt_fields.
READ TABLE lt_names
with key kind = 'FLD'
name
= <name>-name
INTO
ls_field
.
IF sy-subrc <> 0.
clear ls_field.
ENDIF.
*    Check if such field exists in interface
ASSIGN COMPONENT <name>-name OF STRUCTURE is_interface TO <any1>.
IF sy-subrc = 0.
APPEND INITIAL LINE TO lt_fields ASSIGNING <field>.
<field>
= <name>-name.
ELSE.
*  Get named range's name for ex. TAB1_1_3_SOMEFIELD
*  And split it into table: TAB1/ 1/ 3/ SOMEFIELD
SPLIT <name>-name AT '_' INTO TABLE lt_fields.
ENDIF.
*  So the first line of this table should be a field of the interface is_interface
READ TABLE lt_fields ASSIGNING <field> INDEX 1.
CHECK <field> is ASSIGNED.
ASSIGN COMPONENT <field> OF STRUCTURE is_interface TO <any1>.
CHECK sy-subrc = 0.
lr_typedescr
= cl_abap_typedescr=>describe_by_data( <any1> ).
DELETE lt_fields INDEX 1.      "Remove structure/table/element name
CASE lr_typedescr->kind.
WHEN cl_abap_typedescr=>kind_elem. "Simple field
IF <name>-kind = 'RNGE'. "Excel Range name
fill_value_by_range_name
( iv_range_name = <name>-name
ir_document  
= ir_doi
is_field     
= ls_field
iv_new_value 
= <any1> ).
ELSEIF <name>-kind = 'BKMRK'"Word bookmark
fill_value_by_bookmark
( io_bookmark  = <name>-ole_obj
iv_new_value
= <any1> ).
ENDIF.

WHEN cl_abap_typedescr=>kind_struct.
IF <name>-kind = 'RNGE'. "Excel Range name
put_the_structure
( ir_doi    = ir_doi
it_fields
= lt_fields
is_source
= <any1>
iv_full_name
= <name>-name ).
ELSEIF <name>-kind = 'BKMRK'"Word bookmark
put_str_field_int_bkmrk
( io_bookmark = <name>-ole_obj
it_fields  
= lt_fields
is_source  
= <any1> ).
ENDIF.

WHEN cl_abap_typedescr=>kind_table.
IF <name>-kind = 'RNGE'. "Excel Range name
put_the_table
( ir_doi    = ir_doi
it_fields
= lt_fields
it_source
= <any1>
iv_full_name
= <name>-name ).
ELSEIF <name>-kind = 'WRDTAB'"Word table
put_table_into_bkmrk
( ir_doi      = ir_doi          "Reference to a document proxy
it_fields  
= lt_fields       "Desired fields from table
it_source  
= <any1>          "A table to transfer into a word table
iv_tab_indx
= <name>-value ). "Table index
ENDIF.
WHEN OTHERS.
ENDCASE.
CLEAR lt_fields.
ENDLOOP.

update_fields
( ir_doi    = ir_doi
it_fields
= lt_names ).
ENDMETHOD.

METHOD save_doi.
DATA: lv_filepath TYPE char255,
lv_sapdir  
TYPE string.
CHECK ir_doi IS BOUND.
IF iv_filepath IS NOT INITIAL.
lv_filepath
= iv_filepath.
ELSE.
cl_gui_frontend_services
=>get_sapgui_workdir( CHANGING sapworkdir lv_sapdir
EXCEPTIONS OTHERS = 4 ).
CALL FUNCTION 'FLUSH'
EXCEPTIONS
OTHERS = 0.

*    CHECK sy-subrc = 0.
IF lv_sapdir IS INITIAL OR sy-subrc <> 0.
cl_gui_frontend_services
=>get_desktop_directory(
CHANGING desktop_directory lv_sapdir ).
*      lv_sapdir = 'C:'.
CALL FUNCTION 'FLUSH'
EXCEPTIONS
OTHERS = 0.
ENDIF.
GET TIME.
ir_doi
->get_document_type( IMPORTING document_type = lv_filepath ).
IF lv_filepath(5) = 'Excel'.
lv_filepath
= lv_sapdir && '\' && sy-uname && sy-datum && sy-timlo && '.xls'.
ELSE.
lv_filepath
= lv_sapdir && '\' && sy-uname && sy-datum && sy-timlo && '.doc'.
ENDIF.
ENDIF.
CHECK lv_filepath IS NOT INITIAL.
ir_doi
->save_as( file_name = lv_filepath ).
rv_filepath
= lv_filepath.
CALL FUNCTION 'FLUSH'
EXCEPTIONS
OTHERS = 0.

ENDMETHOD.

METHOD open_document.
CHECK iv_filepath IS NOT INITIAL.
cl_gui_frontend_services
=>execute(
document 
= iv_filepath
maximized
= 'X' ).
ENDMETHOD.

METHOD get_pages.
DATA: lv_hanler TYPE cntl_handle.
ir_doi
->get_document_handle( EXPORTING no_flush = 'X'
IMPORTING handle   = lv_hanler ).
GET PROPERTY OF lv_hanler-obj 'ActiveSheet' = go_activesheet NO FLUSH.
GET PROPERTY OF go_activesheet 'PageSetup' = go_apl NO FLUSH.
GET PROPERTY OF go_apl 'Pages' = go_range NO FLUSH.
GET PROPERTY OF go_range 'Count' = rv_result.
FREE OBJECT: go_range, go_apl, go_activesheet.
ADD 1 TO rv_result.
ENDMETHOD.

METHOD add_field_value.
DATA: ls_field TYPE LINE OF soi_form_list,
lv_field
TYPE char200.
FIELD-SYMBOLS: <field> TYPE LINE OF soi_form_list,
<clike>
TYPE c.

MOVE-CORRESPONDING is_field TO ls_field.
ls_field
-type   = 'T'.
ls_field
-number = '1'.
ls_field
-code   = '0'.
IF iv_conct IS INITIAL.
write iv_value to ls_field-value LEFT-JUSTIFIED.
*    ls_field-value = iv_value.
ELSE.
WRITE iv_value to lv_field LEFT-JUSTIFIED.
*    lv_field = iv_value.
CONCATENATE ls_field-value lv_field INTO ls_field-value SEPARATED BY space.
*    TRY .
*        ASSIGN iv_value TO <clike> CASTING.
*        CONCATENATE ls_field-value <clike> INTO ls_field-value SEPARATED BY space.
*      CATCH cx_sy_assign_cast_illegal_cast .
*        WRITE iv_value TO lv_field LEFT-JUSTIFIED.
*        CONDENSE lv_field.
*        CONCATENATE ls_field-value lv_field INTO ls_field-value SEPARATED BY space.
*    ENDTRY.

ENDIF.
APPEND ls_field TO fields_to_update.
*  MODIFY TABLE fields_to_update FROM ls_field.
ENDMETHOD.

METHOD initialize_field_to_update.
CLEAR fields_to_update.
ENDMETHOD.

METHOD update_fields.
DATA: lr_form TYPE REF TO i_oi_form,
lt_fields
TYPE soi_form_list.
FIELD-SYMBOLS: <field>  TYPE LINE OF soi_form_list,
<update>
LIKE LINE OF fields_to_update.
CHECK fields_to_update IS NOT INITIAL.
*  ir_doi->get_form_interface( EXPORTING no_flush = 'X'
*                              IMPORTING f_interface = lr_form ).
*  lr_form->synchronize_fields( no_flush = 'X' ).
*  lr_form->get_data( IMPORTING fields = lt_fields ).
*  LOOP AT lt_fields ASSIGNING <field>.
*    READ TABLE fields_to_update WITH KEY name = <field>-name ASSIGNING <update>.
*    IF sy-subrc <> 0.
*      APPEND INITIAL LINE TO fields_to_update ASSIGNING <update>.
*      MOVE-CORRESPONDING <field> TO <update>.
*      <update>-type   = 'T'.
*      <update>-number = '1'.
*      <update>-code   = '0'.
*    ENDIF.
*  ENDLOOP.

ir_doi
->get_form_interface( EXPORTING no_flush = 'X'
IMPORTING f_interface = lr_form ).
lr_form
->set_data( fields = fields_to_update
CLEAR = ' ' ).
*  lr_form->synchronize_fields( ).
CLEAR fields_to_update.
ENDMETHOD.

METHOD hide_doi.
DATA: lv_hanler TYPE cntl_handle.
ir_doi
->get_document_handle( EXPORTING no_flush = 'X'
IMPORTING handle   = lv_hanler ).
GET PROPERTY OF lv_hanler-obj 'Application' = go_apl NO FLUSH.
SET PROPERTY OF go_apl 'Visible' = 'False' NO FLUSH.
SET PROPERTY OF go_apl 'DisplayAlerts' = 'False'.
FREE OBJECT go_apl.
ENDMETHOD.

METHOD fill_value_by_bookmark.
GET PROPERTY OF io_bookmark 'Range' = go_range NO FLUSH.
SET PROPERTY OF go_range 'Text' = iv_new_value.

FREE OBJECT: go_range.
ENDMETHOD.

METHOD get_word_tables.
DATA: lv_count TYPE i,
lo_table
TYPE obj_record.
FIELD-SYMBOLS: <table> LIKE LINE OF rt_tables.
GET PROPERTY OF io_document 'Tables' = go_activesheet NO FLUSH.
GET PROPERTY OF go_activesheet 'Count' = lv_count.
DO lv_count TIMES.
APPEND INITIAL LINE TO rt_tables ASSIGNING <table>.
<table>
-indx = sy-tabix.
GET PROPERTY OF go_activesheet 'Item' = lo_table
EXPORTING
#1
= sy-index.
CHECK sy-subrc = 0.
<table>
-ole_obj = lo_table.
ENDDO.
FREE OBJECT: lo_table,
go_activesheet
.
ENDMETHOD.

METHOD insert_tab_into_word_tab.
DATA: lr_str_descr  TYPE REF TO cl_abap_structdescr,
lt_str_fields
TYPE ddfields,
lt_columns   
TYPE soi_colnames_table,
lv_doctab_no 
TYPE i.
FIELD-SYMBOLS: <field> TYPE dfies,
<line> 
TYPE any,
<table_line>
TYPE any.
READ TABLE it_table ASSIGNING <table_line> INDEX 1.
CHECK <table_line> IS ASSIGNED.
lr_str_descr ?= cl_abap_structdescr
=>describe_by_data( <table_line> ).
CHECK lr_str_descr IS BOUND.
lt_str_fields
= lr_str_descr->get_ddic_field_list( ). "Here can be dump due to exceptions section omitted
LOOP AT lt_str_fields ASSIGNING <field>.
APPEND INITIAL LINE TO lt_columns ASSIGNING <line>.
<line>
= <field>-fieldname.
ENDLOOP.
lv_doctab_no
= iv_tab_indx.
ir_word
->insert_table2(
data_table
= it_table
info_col_table 
= lt_columns
lowerbound     
= "Whole table
upperbound     
= " whole table
doctable_number
= lv_doctab_no
clearoption    
= "Clear content
startrow       
= "From the first row
varsize        
= 'X' ). "Any rows number
ENDMETHOD.

method PUT_STR_FIELD_INT_BKMRK.
*  This method will put just one field of a structure
*  into the bookmark
FIELD-SYMBOLS: <field>  type any,
<source>
type any.
LOOP AT it_fields ASSIGNING <field>.
ASSIGN COMPONENT <field> OF STRUCTURE is_source to <source>.
IF <source> is ASSIGNED.
exit. "Here we've found soume field from a list of fields at the source structure
ENDIF.
ENDLOOP.
CHECK <source> is ASSIGNED.
fill_value_by_bookmark
( io_bookmark  = io_bookmark
iv_new_value
= <source> ).
endmethod.

METHOD put_table_into_bkmrk.
*  This method should create a local version of the table if some fields specified
*  Otherwise it should put a whole table into the document
DATA: lr_str_descr TYPE REF TO cl_abap_structdescr,
lt_str_fields
TYPE ddfields,
lv_index     
TYPE i,
lt_target_struct
TYPE zhr_typedescr_itab,
ld_target       
TYPE REF TO data,
lr_word         
TYPE REF TO i_oi_word_processor_document.
FIELD-SYMBOLS: <table_line> TYPE any,
<field>     
TYPE any,
<any>       
TYPE any,
<any1>      
TYPE any,
<any_tab>   
TYPE STANDARD TABLE,
<str_field> 
TYPE dfies,
<target_str>
LIKE LINE OF lt_target_struct.

READ TABLE it_source ASSIGNING <table_line> INDEX 1.
CHECK <table_line> IS ASSIGNED. "No sense to continue if no data to put

ir_doi
->get_wordprocessor_interface( IMPORTING wp_interface = lr_word ). "Get word interface
CHECK lr_word IS BOUND. "No sense to continue without the Word

IF it_fields IS INITIAL. "No fields specified
insert_tab_into_word_tab
( it_table = it_source
ir_word 
= lr_word
iv_tab_indx
= iv_tab_indx ).
ELSE.
*  Create fields list
lr_str_descr ?= cl_abap_structdescr
=>describe_by_data( <table_line> ).
CHECK lr_str_descr IS BOUND.
lt_str_fields
= lr_str_descr->get_ddic_field_list( ). "Here can be dump due to exceptions section omitted
LOOP AT it_fields ASSIGNING <field>.
CHECK <field> IS NOT INITIAL.
READ TABLE lt_str_fields WITH KEY fieldname = <field> ASSIGNING <str_field>.
IF sy-subrc <> 0.
TRY.
MOVE <field> TO lv_index.
CATCH cx_sy_conversion_no_number .
CONTINUE.
ENDTRY.
READ TABLE lt_str_fields ASSIGNING <str_field> INDEX lv_index.
CHECK sy-subrc = 0.
ENDIF.
APPEND INITIAL LINE TO lt_target_struct ASSIGNING <target_str>.
<target_str>
-field_name = <str_field>-fieldname.
CONCATENATE <str_field>-tabname '-' <str_field>-fieldname
INTO <target_str>-field_type.

ENDLOOP.
ld_target
= create_dyn_table( lt_target_struct ).
CHECK ld_target IS BOUND.
ASSIGN ld_target->* TO <any_tab>.
CHECK sy-subrc = 0.
LOOP AT it_source ASSIGNING <any>.
APPEND INITIAL LINE TO <any_tab> ASSIGNING <any1>.
MOVE-CORRESPONDING <any> TO <any1>.
ENDLOOP.
insert_tab_into_word_tab
( it_table = <any_tab>
ir_word 
= lr_word
iv_tab_indx
= iv_tab_indx ).
ENDIF.
ENDMETHOD.

method PUT_THE_STRUCTURE.
*  This method will put just one field of a structure
*  into the named range
FIELD-SYMBOLS: <field>  type any,
<source>
type any.
LOOP AT it_fields ASSIGNING <field>.
ASSIGN COMPONENT <field> OF STRUCTURE is_source to <source>.
IF <source> is ASSIGNED.
exit. "Here we've found some field from a list of fields at the source structure
ENDIF.
ENDLOOP.
CHECK <source> is ASSIGNED.
fill_value_by_range_name
( iv_range_name = iv_full_name
ir_document  
= ir_doi
iv_new_value 
= <source> ).
endmethod.

METHOD put_the_table.
*  This method should create a local version of the table if some fields specified
*  Otherwise it should put a whole table into the document
DATA: lr_str_descr TYPE REF TO cl_abap_structdescr,
lt_str_fields
TYPE ddfields,
lv_index     
TYPE i,
lt_target_struct
TYPE zhr_typedescr_itab,
ld_target       
TYPE REF TO data,
lt_fcat         
TYPE lvc_t_fcat.
FIELD-SYMBOLS: <table_line> TYPE any,
<field>     
TYPE any,
<any>       
TYPE any,
<any1>      
TYPE any,
<any_tab>   
TYPE STANDARD TABLE,
<str_field> 
TYPE dfies,
<target_str>
LIKE LINE OF lt_target_struct,
<fline>     
TYPE LINE OF lvc_t_fcat,
<component> 
TYPE abap_compdescr.

READ TABLE it_source ASSIGNING <table_line> INDEX 1.
CHECK <table_line> IS ASSIGNED. "No sense to continue if no data to put

IF it_fields IS INITIAL. "No fields specified
insert_table_into_named_range
( ir_document = ir_doi
it_table   
= it_source
iv_rangename
= iv_full_name ).
ELSE.
*  Create fields list
lr_str_descr ?= cl_abap_structdescr
=>describe_by_data( <table_line> ).
CHECK lr_str_descr IS BOUND.
LOOP AT it_fields ASSIGNING <field>.
CHECK <field> IS NOT INITIAL.
READ TABLE lr_str_descr->components WITH KEY name = <field> ASSIGNING <component>.
IF sy-subrc <> 0.
TRY.
MOVE <field> TO lv_index.
CATCH cx_sy_conversion_no_number .
CONTINUE.
ENDTRY.
READ TABLE lr_str_descr->components ASSIGNING <component> INDEX lv_index.
CHECK sy-subrc = 0.
ENDIF.
APPEND INITIAL LINE TO lt_fcat ASSIGNING <fline>.
<fline>
-fieldname = <component>-name.
<fline>
-datatype  = <component>-type_kind.
<fline>
-inttype   = <component>-type_kind.
<fline>
-intlen    = <component>-length.
<fline>
-decimals  = <component>-decimals.
ENDLOOP.
cl_alv_table_create
=>create_dynamic_table(
EXPORTING
it_fieldcatalog 
= lt_fcat
i_length_in_byte
= 'X'
IMPORTING
ep_table
= ld_target ).
CHECK ld_target IS BOUND.
ASSIGN ld_target->* TO <any_tab>.
CHECK sy-subrc = 0.
LOOP AT it_source ASSIGNING <any>.
APPEND INITIAL LINE TO <any_tab> ASSIGNING <any1>.
MOVE-CORRESPONDING <any> TO <any1>.
ENDLOOP.
insert_table_into_named_range
( ir_document = ir_doi
it_table   
= <any_tab>
iv_rangename
= iv_full_name ).
ENDIF.
ENDMETHOD.

method GET_FORM_FIELDS.
data: lt_fields TYPE soi_form_list.
FIELD-SYMBOLS: <field> type line of SOI_FORM_LIST,
<name> 
like LINE OF ct_names.

CHECK ir_form IS BOUND.
ir_form
->get_form_fields( IMPORTING fields = lt_fields ).
LOOP AT lt_fields ASSIGNING <field>.
APPEND INITIAL LINE TO ct_names ASSIGNING <name>.
MOVE-CORRESPONDING <field> to <name>. "Fill name and value
<name>
-kind = 'FLD'.                  "Domain value could be a reason of dumps if other values ranges determined

ENDLOOP.
endmethod.

METHOD get_word_bookmarks.
* Here's nothing to do actually due to no tools to get bookmarks from a word interface
* A Document ole reference could be used to get this info by Bookmarks collection

DATA: lv_count TYPE i,
lv_name 
TYPE text128,
lt_word_tables
TYPE zhr_wordtable_itab,
lv_cur_indx   
TYPE i.
FIELD-SYMBOLS: <name> LIKE LINE OF ct_names,
<wrdtab>
LIKE LINE OF lt_word_tables.
ir_doi
->get_document_handle( EXPORTING no_flush = 'X'
IMPORTING handle = go_doc ).
GET PROPERTY OF go_doc-obj 'Bookmarks' = go_names NO FLUSH.
GET PROPERTY OF go_names 'Count' = lv_count.
DO lv_count TIMES.
GET PROPERTY OF go_names 'Item' = go_range
NO FLUSH
EXPORTING
#1
= sy-index.
APPEND INITIAL LINE TO ct_names ASSIGNING <name>.
GET PROPERTY OF go_range 'Name' = <name>-name.
<name>
-kind = 'BKMRK'.   "Word bookmark
<name>
-ole_obj = go_range.
ENDDO.
FREE OBJECT: go_selection, go_br_cell, go_lt_cell, go_range, go_names.
CLEAR lv_count.
GET PROPERTY OF go_doc-obj 'Tables' = go_names NO FLUSH.
GET PROPERTY OF go_names 'Count' = lv_count.
DO lv_count TIMES.
*    ActiveDocument.Tables(i).Range.Bookmarks(1) = TableName
lv_cur_indx
= sy-index.
GET PROPERTY OF go_names 'Item' = go_selection
NO FLUSH
EXPORTING
#1
= lv_cur_indx.
GET PROPERTY OF go_selection 'Range' = go_range NO FLUSH.
GET PROPERTY OF go_range 'Bookmarks' = go_br_cell NO FLUSH.
GET PROPERTY OF go_br_cell 'Item' = go_lt_cell
NO FLUSH
EXPORTING
#1
= 1.
GET PROPERTY OF go_lt_cell 'Name' = lv_name.
CHECK sy-subrc = 0"Bookmark for table exists
APPEND INITIAL LINE TO ct_names ASSIGNING <name>.
<name>
-name = lv_name.
<name>
-kind = 'WRDTAB'.   "Word bookmark
<name>
-value = lv_cur_indx.
ENDDO.
*  FREE OBJECT: go_selection, go_br_cell, go_lt_cell, go_range, go_names.
FREE OBJECT: go_selection,
go_names
,
go_apl
,
go_doc
-obj,
go_range
,
go_br_cell
,
go_lt_cell
,
go_activesheet
.
ENDMETHOD.

method GET_XLS_FIELDS.
DATA: lt_ranges TYPE soi_range_list.
FIELD-SYMBOLS: <field> TYPE LINE OF soi_range_list,
<name> 
LIKE LINE OF ct_names.

CHECK ir_xls IS BOUND.
ir_xls
->get_ranges_names( IMPORTING ranges = lt_ranges ).
LOOP AT lt_ranges ASSIGNING <field>.
APPEND INITIAL LINE TO ct_names ASSIGNING <name>.
MOVE-CORRESPONDING <field> TO <name>. "Fill name
<name>
-kind = 'RNGE'.                 "Domain value could be a reason of dumps if other values ranges determined
*   Will not fill object value due to interface bounds
ENDLOOP.
endmethod.

Also you’ll need to create types:

ZHR_NAMEDESCR_ITAB (table type of ZHR_NAMEDESCR_WA)
ZHR_NAMEDESCR_WA (structure):
NAME                  CHAR128

KIND                     CHAR6

VALUE                  CHAR255

OLE_OBJ             OBJ_RECORD

ZHR_TYPEDESCR_ITAB (table type of ZHR_TYPEDESCR_WA)

ZHR_TYPEDESCR_WA (structure):

FIELD_NAME                     CHAR40

FIELD_TYPE                       CHAR100

ZHR_WORDTABLE_ITAB (table type of ZHR_WORDTABLE_WA)

ZHR_WORDTABLE_WA (structure):

INDX                     INT4

OLE_OBJ             OBJ_RECORD

6 Comments