abap2xlsx/src/not_cloud/zcl_excel_ole.clas.abap
Abo 66a45b840a
remove CHAR255 ( WIP #692 ) (#847)
DTEL, "STRU" and CLAS

Co-authored-by: sandraros <34005250+sandraros@users.noreply.github.com>
2021-10-09 20:04:05 +02:00

2096 lines
60 KiB
ABAP

CLASS zcl_excel_ole DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
TYPES ty_doc_url TYPE c LENGTH 255.
CLASS-METHODS bind_alv_ole2
IMPORTING
!i_document_url TYPE ty_doc_url DEFAULT space
!i_xls TYPE c DEFAULT space
!i_save_path TYPE string
!io_alv TYPE REF TO cl_gui_alv_grid
!it_listheader TYPE slis_t_listheader OPTIONAL
!i_top TYPE i DEFAULT 1
!i_left TYPE i DEFAULT 1
!i_columns_header TYPE c DEFAULT 'X'
!i_columns_autofit TYPE c DEFAULT 'X'
!i_format_col_header TYPE soi_format_item OPTIONAL
!i_format_subtotal TYPE soi_format_item OPTIONAL
!i_format_total TYPE soi_format_item OPTIONAL
EXCEPTIONS
miss_guide
ex_transfer_kkblo_error
fatal_error
inv_data_range
dim_mismatch_vkey
dim_mismatch_sema
error_in_sema .
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_excel_ole IMPLEMENTATION.
METHOD bind_alv_ole2.
*--------------------------------------------------------------------*
* Method description:
* Method use to export a CL_GUI_ALV_GRID object to xlsx/xls file
* with list header and characteristics of ALV field catalog such as:
* + Total, group's subtotal
* + Quantity fields, amount fields (dependent fields)
* + No_out, no_zero, ...
* Technique use in method:
* SAP Desktop Office Integration (DOI)
*--------------------------------------------------------------------*
* Data for session 0: DOI constructor
* ------------------------------------------
DATA: lo_control TYPE REF TO i_oi_container_control.
DATA: lo_proxy TYPE REF TO i_oi_document_proxy.
DATA: lo_spreadsheet TYPE REF TO i_oi_spreadsheet.
DATA: lo_error TYPE REF TO i_oi_error.
DATA: lc_retcode TYPE soi_ret_string.
DATA: li_has TYPE i. "Proxy has spreadsheet interface?
DATA: l_is_closed TYPE i.
* Data for session 1: Get LVC data from ALV object
* ------------------------------------------
DATA: l_has_activex,
l_doctype_excel_sheet(11) TYPE c.
* LVC
DATA: lt_fieldcat_lvc TYPE lvc_t_fcat.
DATA: wa_fieldcat_lvc TYPE lvc_s_fcat.
DATA: lt_sort_lvc TYPE lvc_t_sort.
DATA: lt_filter_idx_lvc TYPE lvc_t_fidx.
DATA: lt_grouplevels_lvc TYPE lvc_t_grpl.
* KKBLO
DATA: lt_fieldcat_kkblo TYPE kkblo_t_fieldcat.
DATA: lt_sort_kkblo TYPE kkblo_t_sortinfo.
DATA: lt_grouplevels_kkblo TYPE kkblo_t_grouplevels.
DATA: lt_filter_idx_kkblo TYPE kkblo_t_sfinfo.
DATA: wa_listheader LIKE LINE OF it_listheader.
* Subtotal
DATA: lt_collect00 TYPE REF TO data.
DATA: lt_collect01 TYPE REF TO data.
DATA: lt_collect02 TYPE REF TO data.
DATA: lt_collect03 TYPE REF TO data.
DATA: lt_collect04 TYPE REF TO data.
DATA: lt_collect05 TYPE REF TO data.
DATA: lt_collect06 TYPE REF TO data.
DATA: lt_collect07 TYPE REF TO data.
DATA: lt_collect08 TYPE REF TO data.
DATA: lt_collect09 TYPE REF TO data.
* data table name
DATA: l_tabname TYPE kkblo_tabname.
* local object
DATA: lo_grid TYPE REF TO lcl_gui_alv_grid.
* data table get from ALV
DATA: lt_alv TYPE REF TO data.
* total / subtotal data
FIELD-SYMBOLS: <f_collect00> TYPE STANDARD TABLE.
FIELD-SYMBOLS: <f_collect01> TYPE STANDARD TABLE.
FIELD-SYMBOLS: <f_collect02> TYPE STANDARD TABLE.
FIELD-SYMBOLS: <f_collect03> TYPE STANDARD TABLE.
FIELD-SYMBOLS: <f_collect04> TYPE STANDARD TABLE.
FIELD-SYMBOLS: <f_collect05> TYPE STANDARD TABLE.
FIELD-SYMBOLS: <f_collect06> TYPE STANDARD TABLE.
FIELD-SYMBOLS: <f_collect07> TYPE STANDARD TABLE.
FIELD-SYMBOLS: <f_collect08> TYPE STANDARD TABLE.
FIELD-SYMBOLS: <f_collect09> TYPE STANDARD TABLE.
* table before append subtotal lines
FIELD-SYMBOLS: <f_alv_tab> TYPE STANDARD TABLE.
* data for session 2: sort, filter and calculate total/subtotal
* ------------------------------------------
* table to save index of subotal / total line in excel tanle
* this ideal to control index of subtotal / total line later
* for ex, when get subtotal / total line to format
TYPES: BEGIN OF st_subtot_indexs,
index TYPE i,
END OF st_subtot_indexs.
DATA: lt_subtot_indexs TYPE TABLE OF st_subtot_indexs.
DATA: wa_subtot_indexs LIKE LINE OF lt_subtot_indexs.
* data table after append subtotal
DATA: lt_excel TYPE REF TO data.
DATA: l_tabix TYPE i.
DATA: l_save_index TYPE i.
* dyn subtotal table name
DATA: l_collect TYPE string.
* subtotal range, to format subtotal (and total)
DATA: subranges TYPE soi_range_list.
DATA: subrangeitem TYPE soi_range_item.
DATA: l_sub_index TYPE i.
* table after append subtotal lines
FIELD-SYMBOLS: <f_excel_tab> TYPE STANDARD TABLE.
FIELD-SYMBOLS: <f_excel_line> TYPE any.
* dyn subtotal tables
FIELD-SYMBOLS: <f_collect_tab> TYPE STANDARD TABLE.
FIELD-SYMBOLS: <f_collect_line> TYPE any.
FIELD-SYMBOLS: <f_filter_idx_line> LIKE LINE OF lt_filter_idx_kkblo.
FIELD-SYMBOLS: <f_fieldcat_line> LIKE LINE OF lt_fieldcat_kkblo.
FIELD-SYMBOLS: <f_grouplevels_line> LIKE LINE OF lt_grouplevels_kkblo.
FIELD-SYMBOLS: <f_line> TYPE any.
* Data for session 3: map data to semantic table
* ------------------------------------------
TYPES: BEGIN OF st_column_index,
fieldname TYPE kkblo_fieldname,
tabname TYPE kkblo_tabname,
col LIKE sy-index,
END OF st_column_index.
* columns index
DATA: lt_column_index TYPE TABLE OF st_column_index.
DATA: wa_column_index LIKE LINE OF lt_column_index.
* table of dependent field ( currency and quantity unit field)
DATA: lt_fieldcat_depf TYPE kkblo_t_fieldcat.
DATA: wa_fieldcat_depf TYPE kkblo_fieldcat.
* XXL interface:
* -XXL: contain exporting columns characteristic
DATA: lt_sema TYPE TABLE OF gxxlt_s INITIAL SIZE 0.
DATA: wa_sema LIKE LINE OF lt_sema.
* -XXL interface: header
DATA: lt_hkey TYPE TABLE OF gxxlt_h INITIAL SIZE 0.
DATA: wa_hkey LIKE LINE OF lt_hkey.
* -XXL interface: header keys
DATA: lt_vkey TYPE TABLE OF gxxlt_v INITIAL SIZE 0.
DATA: wa_vkey LIKE LINE OF lt_vkey.
* Number of H Keys: number of key columns
DATA: l_n_hrz_keys TYPE i.
* Number of data columns in the list object: non-key columns no
DATA: l_n_att_cols TYPE i.
* Number of V Keys: number of header row
DATA: l_n_vrt_keys TYPE i.
* curency to format amount
DATA: lt_tcurx TYPE TABLE OF tcurx.
DATA: wa_tcurx LIKE LINE OF lt_tcurx.
DATA: l_def TYPE flag. " currency / quantity flag
DATA: wa_t006 TYPE t006. " decimal place of unit
DATA: l_num TYPE i. " table columns number
DATA: l_typ TYPE c. " table type
DATA: wa TYPE REF TO data.
DATA: l_int TYPE i.
DATA: l_counter TYPE i.
FIELD-SYMBOLS: <f_excel_column> TYPE any.
FIELD-SYMBOLS: <f_fcat_column> TYPE any.
* Data for session 4: write to excel
* ------------------------------------------
DATA: sema_type TYPE c.
DATA l_error TYPE REF TO c_oi_proxy_error.
DATA count TYPE i.
DATA datac TYPE i.
DATA datareal TYPE i. " exporting column number
DATA vkeycount TYPE i.
DATA all TYPE i.
DATA mit TYPE i VALUE 1. " index of recent row?
DATA li_col_pos TYPE i VALUE 1. " column position
DATA li_col_num TYPE i. " table columns number
FIELD-SYMBOLS: <line> TYPE any.
FIELD-SYMBOLS: <item> TYPE any.
DATA td TYPE sydes_desc.
DATA: typ.
DATA: ranges TYPE soi_range_list.
DATA: rangeitem TYPE soi_range_item.
DATA: contents TYPE soi_generic_table.
DATA: contentsitem TYPE soi_generic_item.
DATA: semaitem TYPE gxxlt_s.
DATA: hkeyitem TYPE gxxlt_h.
DATA: vkeyitem TYPE gxxlt_v.
DATA: li_commentary_rows TYPE i. "row number of title lines + 1
DATA: lo_error_w TYPE REF TO i_oi_error.
DATA: l_retcode TYPE soi_ret_string.
DATA: no_flush TYPE c VALUE 'X'.
DATA: li_head_top TYPE i. "header rows position
* Data for session 5: Save and clode document
* ------------------------------------------
DATA: li_document_size TYPE i.
DATA: ls_path TYPE rlgrap-filename.
* MACRO: Close_document
*-------------------------------------------
DEFINE close_document.
CLEAR: l_is_closed.
IF lo_proxy IS NOT INITIAL.
* check proxy detroyed adi
CALL METHOD lo_proxy->is_destroyed
IMPORTING
ret_value = l_is_closed.
* if dun detroyed yet: close -> release proxy
IF l_is_closed IS INITIAL.
CALL METHOD lo_proxy->close_document
* EXPORTING
* do_save = do_save
IMPORTING
error = lo_error
retcode = lc_retcode.
ENDIF.
CALL METHOD lo_proxy->release_document
IMPORTING
error = lo_error
retcode = lc_retcode.
ELSE.
lc_retcode = c_oi_errors=>ret_document_not_open.
ENDIF.
* Detroy control container
IF lo_control IS NOT INITIAL.
CALL METHOD lo_control->destroy_control.
ENDIF.
CLEAR:
lo_spreadsheet,
lo_proxy,
lo_control.
* free local
CLEAR: l_is_closed.
END-OF-DEFINITION.
* Macro to catch DOI error
*-------------------------------------------
DEFINE error_doi.
IF lc_retcode NE c_oi_errors=>ret_ok.
close_document.
CALL METHOD lo_error->raise_message
EXPORTING
type = 'E'.
CLEAR: lo_error.
ENDIF.
END-OF-DEFINITION.
*--------------------------------------------------------------------*
* SESSION 0: DOI CONSTRUCTOR
*--------------------------------------------------------------------*
* check active windown
CALL FUNCTION 'GUI_HAS_ACTIVEX'
IMPORTING
return = l_has_activex.
IF l_has_activex IS INITIAL.
RAISE miss_guide.
ENDIF.
* Get Container Object of Screen
CALL METHOD c_oi_container_control_creator=>get_container_control
IMPORTING
control = lo_control
retcode = lc_retcode.
error_doi.
* Initialize Container control
CALL METHOD lo_control->init_control
EXPORTING
parent = cl_gui_container=>default_screen
r3_application_name = ''
inplace_enabled = 'X'
no_flush = 'X'
register_on_close_event = 'X'
register_on_custom_event = 'X'
IMPORTING
error = lo_error
retcode = lc_retcode.
error_doi.
* Get Proxy Document:
* check exist of document proxy, if exist -> close first
IF NOT lo_proxy IS INITIAL.
close_document.
ENDIF.
IF i_xls IS NOT INITIAL.
* xls format, doctype = soi_doctype_excel97_sheet
l_doctype_excel_sheet = 'Excel.Sheet.8'.
ELSE.
* xlsx format, doctype = soi_doctype_excel_sheet
l_doctype_excel_sheet = 'Excel.Sheet'.
ENDIF.
CALL METHOD lo_control->get_document_proxy
EXPORTING
document_type = l_doctype_excel_sheet
register_container = 'X'
IMPORTING
document_proxy = lo_proxy
error = lo_error
retcode = lc_retcode.
error_doi.
IF i_document_url IS INITIAL.
* create new excel document
CALL METHOD lo_proxy->create_document
EXPORTING
create_view_data = 'X'
open_inplace = 'X'
no_flush = 'X'
IMPORTING
error = lo_error
retcode = lc_retcode.
error_doi.
ELSE.
* Read excel template for i_DOCUMENT_URL
* this excel template can be store in local or server
CALL METHOD lo_proxy->open_document
EXPORTING
document_url = i_document_url
open_inplace = 'X'
no_flush = 'X'
IMPORTING
error = lo_error
retcode = lc_retcode.
error_doi.
ENDIF.
* Check Spreadsheet Interface of Document Proxy
CALL METHOD lo_proxy->has_spreadsheet_interface
IMPORTING
is_available = li_has
error = lo_error
retcode = lc_retcode.
error_doi.
* create Spreadsheet object
CHECK li_has IS NOT INITIAL.
CALL METHOD lo_proxy->get_spreadsheet_interface
IMPORTING
sheet_interface = lo_spreadsheet
error = lo_error
retcode = lc_retcode.
error_doi.
*--------------------------------------------------------------------*
* SESSION 1: GET LVC DATA FROM ALV OBJECT
*--------------------------------------------------------------------*
* data table
CREATE OBJECT lo_grid
EXPORTING
i_parent = cl_gui_container=>screen0.
CALL METHOD lo_grid->get_alv_attributes
EXPORTING
io_grid = io_alv
IMPORTING
et_table = lt_alv.
ASSIGN lt_alv->* TO <f_alv_tab>.
* fieldcat
CALL METHOD io_alv->get_frontend_fieldcatalog
IMPORTING
et_fieldcatalog = lt_fieldcat_lvc.
* table name
LOOP AT lt_fieldcat_lvc INTO wa_fieldcat_lvc
WHERE NOT tabname IS INITIAL.
l_tabname = wa_fieldcat_lvc-tabname.
EXIT.
ENDLOOP.
IF sy-subrc NE 0.
l_tabname = '1'.
ENDIF.
CLEAR: wa_fieldcat_lvc.
* sort table
CALL METHOD io_alv->get_sort_criteria
IMPORTING
et_sort = lt_sort_lvc.
* filter index
CALL METHOD io_alv->get_filtered_entries
IMPORTING
et_filtered_entries = lt_filter_idx_lvc.
* group level + subtotal
CALL METHOD io_alv->get_subtotals
IMPORTING
ep_collect00 = lt_collect00
ep_collect01 = lt_collect01
ep_collect02 = lt_collect02
ep_collect03 = lt_collect03
ep_collect04 = lt_collect04
ep_collect05 = lt_collect05
ep_collect06 = lt_collect06
ep_collect07 = lt_collect07
ep_collect08 = lt_collect08
ep_collect09 = lt_collect09
et_grouplevels = lt_grouplevels_lvc.
ASSIGN lt_collect00->* TO <f_collect00>.
ASSIGN lt_collect01->* TO <f_collect01>.
ASSIGN lt_collect02->* TO <f_collect02>.
ASSIGN lt_collect03->* TO <f_collect03>.
ASSIGN lt_collect04->* TO <f_collect04>.
ASSIGN lt_collect05->* TO <f_collect05>.
ASSIGN lt_collect06->* TO <f_collect06>.
ASSIGN lt_collect07->* TO <f_collect07>.
ASSIGN lt_collect08->* TO <f_collect08>.
ASSIGN lt_collect09->* TO <f_collect09>.
* transfer to KKBLO struct
CALL FUNCTION 'LVC_TRANSFER_TO_KKBLO'
EXPORTING
it_fieldcat_lvc = lt_fieldcat_lvc
it_sort_lvc = lt_sort_lvc
it_filter_index_lvc = lt_filter_idx_lvc
it_grouplevels_lvc = lt_grouplevels_lvc
IMPORTING
et_fieldcat_kkblo = lt_fieldcat_kkblo
et_sort_kkblo = lt_sort_kkblo
et_filtered_entries_kkblo = lt_filter_idx_kkblo
et_grouplevels_kkblo = lt_grouplevels_kkblo
TABLES
it_data = <f_alv_tab>
EXCEPTIONS
it_data_missing = 1
it_fieldcat_lvc_missing = 2
OTHERS = 3.
IF sy-subrc <> 0.
RAISE ex_transfer_kkblo_error.
ENDIF.
CLEAR:
wa_fieldcat_lvc,
lt_fieldcat_lvc,
lt_sort_lvc,
lt_filter_idx_lvc,
lt_grouplevels_lvc.
CLEAR:
lo_grid.
*--------------------------------------------------------------------*
* SESSION 2: SORT, FILTER AND CALCULATE TOTAL / SUBTOTAL
*--------------------------------------------------------------------*
* append subtotal & total line
CREATE DATA lt_excel LIKE <f_alv_tab>.
ASSIGN lt_excel->* TO <f_excel_tab>.
LOOP AT <f_alv_tab> ASSIGNING <f_line>.
l_save_index = sy-tabix.
* filter base on filter index table
READ TABLE lt_filter_idx_kkblo ASSIGNING <f_filter_idx_line>
WITH KEY index = l_save_index
BINARY SEARCH.
IF sy-subrc NE 0.
APPEND <f_line> TO <f_excel_tab>.
ENDIF.
* append subtotal lines
READ TABLE lt_grouplevels_kkblo ASSIGNING <f_grouplevels_line>
WITH KEY index_to = l_save_index
BINARY SEARCH.
IF sy-subrc = 0.
l_tabix = sy-tabix.
DO.
IF <f_grouplevels_line>-subtot EQ 'X' AND
<f_grouplevels_line>-hide_level IS INITIAL AND
<f_grouplevels_line>-cindex_from NE 0.
* dynamic append subtotal line to excel table base on grouplevel table
* ex <f_GROUPLEVELS_line>-level = 1
* then <f_collect_tab> = '<F_COLLECT01>'
l_collect = <f_grouplevels_line>-level.
CONDENSE l_collect.
CONCATENATE '<F_COLLECT0'
l_collect '>'
* '->*'
INTO l_collect.
ASSIGN (l_collect) TO <f_collect_tab>.
* incase there're more than 1 total line of group, at the same level
* for example: subtotal of multi currency
LOOP AT <f_collect_tab> ASSIGNING <f_collect_line>.
IF sy-tabix BETWEEN <f_grouplevels_line>-cindex_from
AND <f_grouplevels_line>-cindex_to.
APPEND <f_collect_line> TO <f_excel_tab>.
* save subtotal lines index
wa_subtot_indexs-index = sy-tabix.
APPEND wa_subtot_indexs TO lt_subtot_indexs.
* append sub total ranges table for format later
ADD 1 TO l_sub_index.
subrangeitem-name = l_sub_index.
CONDENSE subrangeitem-name.
CONCATENATE 'SUBTOT'
subrangeitem-name
INTO subrangeitem-name.
subrangeitem-rows = wa_subtot_indexs-index.
subrangeitem-columns = 1. " start col
APPEND subrangeitem TO subranges.
CLEAR: subrangeitem.
ENDIF.
ENDLOOP.
UNASSIGN: <f_collect_tab>.
UNASSIGN: <f_collect_line>.
CLEAR: l_collect.
ENDIF.
* check next subtotal level of group
UNASSIGN: <f_grouplevels_line>.
ADD 1 TO l_tabix.
READ TABLE lt_grouplevels_kkblo ASSIGNING <f_grouplevels_line>
INDEX l_tabix.
IF sy-subrc NE 0
OR <f_grouplevels_line>-index_to NE l_save_index.
EXIT.
ENDIF.
UNASSIGN:
<f_collect_tab>,
<f_collect_line>.
ENDDO.
ENDIF.
CLEAR:
l_tabix,
l_save_index.
UNASSIGN:
<f_filter_idx_line>,
<f_grouplevels_line>.
ENDLOOP.
* free local data
UNASSIGN:
<f_line>,
<f_collect_tab>,
<f_collect_line>,
<f_fieldcat_line>.
* append grand total line
IF <f_collect00> IS ASSIGNED.
ASSIGN <f_collect00> TO <f_collect_tab>.
IF <f_collect_tab> IS NOT INITIAL.
LOOP AT <f_collect_tab> ASSIGNING <f_collect_line>.
APPEND <f_collect_line> TO <f_excel_tab>.
* save total line index
wa_subtot_indexs-index = sy-tabix.
APPEND wa_subtot_indexs TO lt_subtot_indexs.
* append grand total range (to format)
ADD 1 TO l_sub_index.
subrangeitem-name = l_sub_index.
CONDENSE subrangeitem-name.
CONCATENATE 'TOTAL'
subrangeitem-name
INTO subrangeitem-name.
subrangeitem-rows = wa_subtot_indexs-index.
subrangeitem-columns = 1. " start col
APPEND subrangeitem TO subranges.
ENDLOOP.
ENDIF.
ENDIF.
CLEAR:
subrangeitem,
lt_sort_kkblo,
<f_collect00>,
<f_collect01>,
<f_collect02>,
<f_collect03>,
<f_collect04>,
<f_collect05>,
<f_collect06>,
<f_collect07>,
<f_collect08>,
<f_collect09>.
UNASSIGN:
<f_collect00>,
<f_collect01>,
<f_collect02>,
<f_collect03>,
<f_collect04>,
<f_collect05>,
<f_collect06>,
<f_collect07>,
<f_collect08>,
<f_collect09>,
<f_collect_tab>,
<f_collect_line>.
*--------------------------------------------------------------------*
* SESSION 3: MAP DATA TO SEMANTIC TABLE
*--------------------------------------------------------------------*
* get dependent field field: currency and quantity
CREATE DATA wa LIKE LINE OF <f_excel_tab>.
ASSIGN wa->* TO <f_excel_line>.
DESCRIBE FIELD <f_excel_line> TYPE l_typ COMPONENTS l_num.
DO l_num TIMES.
l_save_index = sy-index.
ASSIGN COMPONENT l_save_index OF STRUCTURE <f_excel_line>
TO <f_excel_column>.
IF sy-subrc NE 0.
MESSAGE e801(zabap2xlsx) WITH 'FATAL ERROR' RAISING fatal_error.
ENDIF.
LOOP AT lt_fieldcat_kkblo ASSIGNING <f_fieldcat_line>
WHERE tabname = l_tabname.
ASSIGN COMPONENT <f_fieldcat_line>-fieldname
OF STRUCTURE <f_excel_line> TO <f_fcat_column>.
DESCRIBE DISTANCE BETWEEN <f_excel_column> AND <f_fcat_column>
INTO l_int IN BYTE MODE.
* append column index
* this columns index is of table, not fieldcat
IF l_int = 0.
wa_column_index-fieldname = <f_fieldcat_line>-fieldname.
wa_column_index-tabname = <f_fieldcat_line>-tabname.
wa_column_index-col = l_save_index.
APPEND wa_column_index TO lt_column_index.
ENDIF.
* append dependent fields (currency and quantity unit)
IF <f_fieldcat_line>-cfieldname IS NOT INITIAL.
CLEAR wa_fieldcat_depf.
wa_fieldcat_depf-fieldname = <f_fieldcat_line>-cfieldname.
wa_fieldcat_depf-tabname = <f_fieldcat_line>-ctabname.
COLLECT wa_fieldcat_depf INTO lt_fieldcat_depf.
ENDIF.
IF <f_fieldcat_line>-qfieldname IS NOT INITIAL.
CLEAR wa_fieldcat_depf.
wa_fieldcat_depf-fieldname = <f_fieldcat_line>-qfieldname.
wa_fieldcat_depf-tabname = <f_fieldcat_line>-qtabname.
COLLECT wa_fieldcat_depf INTO lt_fieldcat_depf.
ENDIF.
* rewrite field data type
IF <f_fieldcat_line>-inttype = 'X'
AND <f_fieldcat_line>-datatype(3) = 'INT'.
<f_fieldcat_line>-inttype = 'I'.
ENDIF.
ENDLOOP.
CLEAR: l_save_index.
UNASSIGN: <f_fieldcat_line>.
ENDDO.
* build semantic tables
l_n_hrz_keys = 1.
* Get keyfigures
LOOP AT lt_fieldcat_kkblo ASSIGNING <f_fieldcat_line>
WHERE tabname = l_tabname
AND tech NE 'X'
AND no_out NE 'X'.
CLEAR wa_sema.
CLEAR wa_hkey.
* Units belong to keyfigures -> display as str
READ TABLE lt_fieldcat_depf INTO wa_fieldcat_depf WITH KEY
fieldname = <f_fieldcat_line>-fieldname
tabname = <f_fieldcat_line>-tabname.
IF sy-subrc = 0.
wa_sema-col_typ = 'STR'.
wa_sema-col_ops = 'DFT'.
* Keyfigures
ELSE.
CASE <f_fieldcat_line>-datatype.
WHEN 'QUAN'.
wa_sema-col_typ = 'N03'.
IF <f_fieldcat_line>-no_sum NE 'X'.
wa_sema-col_ops = 'ADD'.
ELSE.
wa_sema-col_ops = 'NOP'. " no dependent field
ENDIF.
WHEN 'DATS'.
wa_sema-col_typ = 'DAT'.
wa_sema-col_ops = 'NOP'.
WHEN 'CHAR' OR 'UNIT' OR 'CUKY'. " Added fieldformats UNIT and CUKY - dd. 26-10-2012 Wouter Heuvelmans
wa_sema-col_typ = 'STR'.
wa_sema-col_ops = 'DFT'. " dependent field
* incase numeric, ex '00120' -> display as '12'
WHEN 'NUMC'.
wa_sema-col_typ = 'STR'.
wa_sema-col_ops = 'DFT'.
WHEN OTHERS.
wa_sema-col_typ = 'NUM'.
IF <f_fieldcat_line>-no_sum NE 'X'.
wa_sema-col_ops = 'ADD'.
ELSE.
wa_sema-col_ops = 'NOP'.
ENDIF.
ENDCASE.
ENDIF.
l_counter = l_counter + 1.
l_n_att_cols = l_n_att_cols + 1.
wa_sema-col_no = l_counter.
READ TABLE lt_column_index INTO wa_column_index WITH KEY
fieldname = <f_fieldcat_line>-fieldname
tabname = <f_fieldcat_line>-tabname.
IF sy-subrc = 0.
wa_sema-col_src = wa_column_index-col.
ELSE.
RAISE fatal_error.
ENDIF.
* columns index of ref currency field in table
IF NOT <f_fieldcat_line>-cfieldname IS INITIAL.
READ TABLE lt_column_index INTO wa_column_index WITH KEY
fieldname = <f_fieldcat_line>-cfieldname
tabname = <f_fieldcat_line>-ctabname.
IF sy-subrc = 0.
wa_sema-col_cur = wa_column_index-col.
ENDIF.
* quantities fields
* treat as currency when display on excel
ELSEIF NOT <f_fieldcat_line>-qfieldname IS INITIAL.
READ TABLE lt_column_index INTO wa_column_index WITH KEY
fieldname = <f_fieldcat_line>-qfieldname
tabname = <f_fieldcat_line>-qtabname.
IF sy-subrc = 0.
wa_sema-col_cur = wa_column_index-col.
ENDIF.
ENDIF.
* Treat of fixed currency in the fieldcatalog for column
DATA: l_num_help(2) TYPE n.
IF NOT <f_fieldcat_line>-currency IS INITIAL.
SELECT * FROM tcurx INTO TABLE lt_tcurx.
SORT lt_tcurx.
READ TABLE lt_tcurx INTO wa_tcurx
WITH KEY currkey = <f_fieldcat_line>-currency.
IF sy-subrc = 0.
l_num_help = wa_tcurx-currdec.
CONCATENATE 'N' l_num_help INTO wa_sema-col_typ.
wa_sema-col_cur = sy-tabix * ( -1 ).
ENDIF.
ENDIF.
wa_hkey-col_no = l_n_att_cols.
wa_hkey-row_no = l_n_hrz_keys.
wa_hkey-col_name = <f_fieldcat_line>-reptext.
APPEND wa_hkey TO lt_hkey.
APPEND wa_sema TO lt_sema.
ENDLOOP.
* free local data
CLEAR:
lt_column_index,
wa_column_index,
lt_fieldcat_depf,
wa_fieldcat_depf,
lt_tcurx,
wa_tcurx,
l_num,
l_typ,
wa,
l_int,
l_counter.
UNASSIGN:
<f_fieldcat_line>,
<f_excel_line>,
<f_excel_column>,
<f_fcat_column>.
*--------------------------------------------------------------------*
* SESSION 4: WRITE TO EXCEL
*--------------------------------------------------------------------*
CLEAR: wa_tcurx.
REFRESH: lt_tcurx.
* if spreadsheet dun have proxy yet
IF li_has IS INITIAL.
l_retcode = c_oi_errors=>ret_interface_not_supported.
CALL METHOD c_oi_errors=>create_error_for_retcode
EXPORTING
retcode = l_retcode
no_flush = no_flush
IMPORTING
error = lo_error_w.
RETURN.
ENDIF.
CREATE OBJECT l_error
EXPORTING
object_name = 'OLE_DOCUMENT_PROXY'
method_name = 'get_ranges_names'.
CALL METHOD c_oi_errors=>add_error
EXPORTING
error = l_error.
DESCRIBE TABLE lt_sema LINES datareal.
DESCRIBE TABLE <f_excel_tab> LINES datac.
DESCRIBE TABLE lt_vkey LINES vkeycount.
IF datac = 0.
RAISE inv_data_range.
ENDIF.
IF vkeycount NE l_n_vrt_keys.
RAISE dim_mismatch_vkey.
ENDIF.
all = l_n_vrt_keys + l_n_att_cols.
IF datareal NE all.
RAISE dim_mismatch_sema.
ENDIF.
DATA: decimal TYPE c.
* get decimal separator format ('.', ',', ...) in Office config
CALL METHOD lo_proxy->get_application_property
EXPORTING
property_name = 'INTERNATIONAL'
subproperty_name = 'DECIMAL_SEPARATOR'
CHANGING
retvalue = decimal.
DATA date_format TYPE usr01-datfm.
SELECT SINGLE datfm FROM usr01 INTO date_format WHERE bname = sy-uname.
DATA: comma_elim(4) TYPE c.
FIELD-SYMBOLS <g> TYPE any.
DATA search_item(4) VALUE ' #'.
CONCATENATE ',' decimal '.' decimal INTO comma_elim.
DATA help TYPE i. " table (with subtotal) line number
help = datac.
DATA: rowmax TYPE i VALUE 1. " header row number
DATA: columnmax TYPE i VALUE 0. " header columns number
LOOP AT lt_hkey INTO hkeyitem.
IF hkeyitem-col_no > columnmax.
columnmax = hkeyitem-col_no.
ENDIF.
IF hkeyitem-row_no > rowmax.
rowmax = hkeyitem-row_no.
ENDIF.
ENDLOOP.
DATA: hkeycolumns TYPE i. " header columns no
hkeycolumns = columnmax.
IF hkeycolumns < l_n_att_cols.
hkeycolumns = l_n_att_cols.
ENDIF.
columnmax = 0.
LOOP AT lt_vkey INTO vkeyitem.
IF vkeyitem-col_no > columnmax.
columnmax = vkeyitem-col_no.
ENDIF.
ENDLOOP.
DATA overflow TYPE i VALUE 1.
DATA testname(10) TYPE c.
DATA temp2 TYPE i. " 1st item row position in excel
DATA realmit TYPE i VALUE 1.
DATA realoverflow TYPE i VALUE 1. " row index in content
CALL METHOD lo_spreadsheet->screen_update
EXPORTING
updating = ''.
CALL METHOD lo_spreadsheet->load_lib.
DATA: str(40) TYPE c. " range names of columns range (w/o col header)
DATA: rows TYPE i. " row postion of 1st item line in ecxel
* calculate row position of data table
DESCRIBE TABLE it_listheader LINES li_commentary_rows.
* if grid had title, add 1 empy line between title and table
IF li_commentary_rows NE 0.
ADD 1 TO li_commentary_rows.
ENDIF.
* add top position of block data
li_commentary_rows = li_commentary_rows + i_top - 1.
* write header (commentary rows)
DATA: li_commentary_row_index TYPE i VALUE 1.
DATA: li_content_index TYPE i VALUE 1.
DATA: ls_index(10) TYPE c.
DATA ls_commentary_range(40) TYPE c VALUE 'TITLE'.
DATA: li_font_bold TYPE i.
DATA: li_font_italic TYPE i.
DATA: li_font_size TYPE i.
LOOP AT it_listheader INTO wa_listheader.
li_commentary_row_index = i_top + li_content_index - 1.
ls_index = li_content_index.
CONDENSE ls_index.
CONCATENATE ls_commentary_range(5) ls_index
INTO ls_commentary_range.
CONDENSE ls_commentary_range.
* insert title range
CALL METHOD lo_spreadsheet->insert_range_dim
EXPORTING
name = ls_commentary_range
top = li_commentary_row_index
left = i_left
rows = 1
columns = 1
no_flush = no_flush.
* format range
CASE wa_listheader-typ.
WHEN 'H'. "title
li_font_size = 16.
li_font_bold = 1.
li_font_italic = -1.
WHEN 'S'. "subtile
li_font_size = -1.
li_font_bold = 1.
li_font_italic = -1.
WHEN OTHERS. "'A' comment
li_font_size = -1.
li_font_bold = -1.
li_font_italic = 1.
ENDCASE.
CALL METHOD lo_spreadsheet->set_font
EXPORTING
rangename = ls_commentary_range
family = ''
size = li_font_size
bold = li_font_bold
italic = li_font_italic
align = 0
no_flush = no_flush.
* title: range content
rangeitem-name = ls_commentary_range.
rangeitem-columns = 1.
rangeitem-rows = 1.
APPEND rangeitem TO ranges.
contentsitem-row = li_content_index.
contentsitem-column = 1.
CONCATENATE wa_listheader-key
wa_listheader-info
INTO contentsitem-value
SEPARATED BY space.
CONDENSE contentsitem-value.
APPEND contentsitem TO contents.
ADD 1 TO li_content_index.
CLEAR:
rangeitem,
contentsitem,
ls_index.
ENDLOOP.
* set range data title
CALL METHOD lo_spreadsheet->set_ranges_data
EXPORTING
ranges = ranges
contents = contents
no_flush = no_flush.
REFRESH:
ranges,
contents.
rows = rowmax + li_commentary_rows + 1.
all = date_format.
all = all + 3.
LOOP AT lt_sema INTO semaitem.
IF semaitem-col_typ = 'DAT' OR semaitem-col_typ = 'MON' OR
semaitem-col_typ = 'N00' OR semaitem-col_typ = 'N01' OR
semaitem-col_typ = 'N02' OR
semaitem-col_typ = 'N03' OR semaitem-col_typ = 'PCT' OR
semaitem-col_typ = 'STR' OR semaitem-col_typ = 'NUM'.
CLEAR str.
str = semaitem-col_no.
CONDENSE str.
CONCATENATE 'DATA' str INTO str.
mit = semaitem-col_no.
li_col_pos = semaitem-col_no + i_left - 1.
* range from data1 to data(n), for each columns of table
CALL METHOD lo_spreadsheet->insert_range_dim
EXPORTING
name = str
top = rows
left = li_col_pos
rows = help
columns = 1
no_flush = no_flush.
DATA dec TYPE i VALUE -1.
DATA typeinfo TYPE sydes_typeinfo.
LOOP AT <f_excel_tab> ASSIGNING <line>.
ASSIGN COMPONENT semaitem-col_no OF STRUCTURE <line> TO <item>.
DESCRIBE FIELD <item> INTO td.
READ TABLE td-types INDEX 1 INTO typeinfo.
IF typeinfo-type = 'P'.
dec = typeinfo-decimals.
ELSEIF typeinfo-type = 'I'.
dec = 0.
ENDIF.
DESCRIBE FIELD <line> TYPE typ COMPONENTS count.
mit = 1.
DO count TIMES.
IF mit = semaitem-col_src.
ASSIGN COMPONENT sy-index OF STRUCTURE <line> TO <item>.
DESCRIBE FIELD <item> INTO td.
READ TABLE td-types INDEX 1 INTO typeinfo.
IF typeinfo-type = 'P'.
dec = typeinfo-decimals.
ENDIF.
EXIT.
ENDIF.
mit = mit + 1.
ENDDO.
EXIT.
ENDLOOP.
* format for each columns of table (w/o columns headers)
IF semaitem-col_typ = 'DAT'.
IF semaitem-col_no > vkeycount.
CALL METHOD lo_spreadsheet->set_format
EXPORTING
rangename = str
currency = ''
typ = all
no_flush = no_flush.
ELSE.
CALL METHOD lo_spreadsheet->set_format
EXPORTING
rangename = str
currency = ''
typ = 0
no_flush = no_flush.
ENDIF.
ELSEIF semaitem-col_typ = 'STR'.
CALL METHOD lo_spreadsheet->set_format
EXPORTING
rangename = str
currency = ''
typ = 0
no_flush = no_flush.
ELSEIF semaitem-col_typ = 'MON'.
CALL METHOD lo_spreadsheet->set_format
EXPORTING
rangename = str
currency = ''
typ = 10
no_flush = no_flush.
ELSEIF semaitem-col_typ = 'N00'.
CALL METHOD lo_spreadsheet->set_format
EXPORTING
rangename = str
currency = ''
typ = 1
decimals = 0
no_flush = no_flush.
ELSEIF semaitem-col_typ = 'N01'.
CALL METHOD lo_spreadsheet->set_format
EXPORTING
rangename = str
currency = ''
typ = 1
decimals = 1
no_flush = no_flush.
ELSEIF semaitem-col_typ = 'N02'.
CALL METHOD lo_spreadsheet->set_format
EXPORTING
rangename = str
currency = ''
typ = 1
decimals = 2
no_flush = no_flush.
ELSEIF semaitem-col_typ = 'N03'.
CALL METHOD lo_spreadsheet->set_format
EXPORTING
rangename = str
currency = ''
typ = 1
decimals = 3
no_flush = no_flush.
ELSEIF semaitem-col_typ = 'N04'.
CALL METHOD lo_spreadsheet->set_format
EXPORTING
rangename = str
currency = ''
typ = 1
decimals = 4
no_flush = no_flush.
ELSEIF semaitem-col_typ = 'NUM'.
IF dec EQ -1.
CALL METHOD lo_spreadsheet->set_format
EXPORTING
rangename = str
currency = ''
typ = 1
decimals = 2
no_flush = no_flush.
ELSE.
CALL METHOD lo_spreadsheet->set_format
EXPORTING
rangename = str
currency = ''
typ = 1
decimals = dec
no_flush = no_flush.
ENDIF.
ELSEIF semaitem-col_typ = 'PCT'.
CALL METHOD lo_spreadsheet->set_format
EXPORTING
rangename = str
currency = ''
typ = 3
decimals = 0
no_flush = no_flush.
ENDIF.
ENDIF.
ENDLOOP.
* get item contents for set_range_data method
* get currency cell also
mit = 1.
DATA: currcells TYPE soi_cell_table.
DATA: curritem TYPE soi_cell_item.
curritem-rows = 1.
curritem-columns = 1.
curritem-front = -1.
curritem-back = -1.
curritem-font = ''.
curritem-size = -1.
curritem-bold = -1.
curritem-italic = -1.
curritem-align = -1.
curritem-frametyp = -1.
curritem-framecolor = -1.
curritem-currency = ''.
curritem-number = 1.
curritem-input = -1.
DATA: const TYPE i.
* Change for Correction request
* Initial 10000 lines are missing in Excel Export
* if there are only 2 columns in exported List object.
IF datareal GT 2.
const = 20000 / datareal.
ELSE.
const = 20000 / ( datareal + 2 ).
ENDIF.
DATA: lines TYPE i.
DATA: innerlines TYPE i.
DATA: counter TYPE i.
DATA: curritem2 LIKE curritem.
DATA: curritem3 LIKE curritem.
DATA: length TYPE i.
DATA: found.
* append content table (for method set_range_content)
LOOP AT <f_excel_tab> ASSIGNING <line>.
* save line index to compare with lt_subtot_indexs,
* to discover line is a subtotal / totale line or not
* ex use to set 'dun display zero in subtotal / total line'
l_save_index = sy-tabix.
DO datareal TIMES.
READ TABLE lt_sema INTO semaitem WITH KEY col_no = sy-index.
IF semaitem-col_src NE 0.
ASSIGN COMPONENT semaitem-col_src
OF STRUCTURE <line> TO <item>.
ELSE.
ASSIGN COMPONENT sy-index
OF STRUCTURE <line> TO <item>.
ENDIF.
contentsitem-row = realoverflow.
IF sy-subrc = 0.
MOVE semaitem-col_ops TO search_item(3).
SEARCH 'ADD#CNT#MIN#MAX#AVG#NOP#DFT#'
FOR search_item.
IF sy-subrc NE 0.
RAISE error_in_sema.
ENDIF.
MOVE semaitem-col_typ TO search_item(3).
SEARCH 'NUM#N00#N01#N02#N03#N04#PCT#DAT#MON#STR#'
FOR search_item.
IF sy-subrc NE 0.
RAISE error_in_sema.
ENDIF.
contentsitem-column = sy-index.
IF semaitem-col_typ EQ 'DAT' OR semaitem-col_typ EQ 'MON'.
IF semaitem-col_no > vkeycount.
" Hinweis 512418
" EXCEL bezieht Datumsangaben
" auf den 31.12.1899, behandelt
" aber 1900 als ein Schaltjahr
" d.h. ab 1.3.1900 korrekt
" 1.3.1900 als Zahl = 61
DATA: genesis TYPE d VALUE '18991230'.
DATA: number_of_days TYPE p.
* change for date in char format & sema_type = X
DATA: temp_date TYPE d.
IF NOT <item> IS INITIAL AND NOT <item> CO ' ' AND NOT
<item> CO '0'.
* change for date in char format & sema_type = X starts
IF sema_type = 'X'.
DESCRIBE FIELD <item> TYPE typ.
IF typ = 'C'.
temp_date = <item>.
number_of_days = temp_date - genesis.
ELSE.
number_of_days = <item> - genesis.
ENDIF.
ELSE.
number_of_days = <item> - genesis.
ENDIF.
* change for date in char format & sema_type = X ends
IF number_of_days < 61.
number_of_days = number_of_days - 1.
ENDIF.
SET COUNTRY 'DE'.
WRITE number_of_days TO contentsitem-value
NO-GROUPING
LEFT-JUSTIFIED.
SET COUNTRY space.
TRANSLATE contentsitem-value USING comma_elim.
ELSE.
CLEAR contentsitem-value.
ENDIF.
ELSE.
MOVE <item> TO contentsitem-value.
ENDIF.
ELSEIF semaitem-col_typ EQ 'NUM' OR
semaitem-col_typ EQ 'N00' OR
semaitem-col_typ EQ 'N01' OR
semaitem-col_typ EQ 'N02' OR
semaitem-col_typ EQ 'N03' OR
semaitem-col_typ EQ 'N04' OR
semaitem-col_typ EQ 'PCT'.
SET COUNTRY 'DE'.
DESCRIBE FIELD <item> TYPE typ.
IF semaitem-col_cur IS INITIAL.
IF typ NE 'F'.
WRITE <item> TO contentsitem-value NO-GROUPING
NO-SIGN DECIMALS 14.
ELSE.
WRITE <item> TO contentsitem-value NO-GROUPING
NO-SIGN.
ENDIF.
ELSE.
* Treat of fixed curreny for column >>Y9CK007319
IF semaitem-col_cur < 0.
semaitem-col_cur = semaitem-col_cur * ( -1 ).
SELECT * FROM tcurx INTO TABLE lt_tcurx.
SORT lt_tcurx.
READ TABLE lt_tcurx INTO
wa_tcurx INDEX semaitem-col_cur.
IF sy-subrc = 0.
IF typ NE 'F'.
WRITE <item> TO contentsitem-value NO-GROUPING
CURRENCY wa_tcurx-currkey NO-SIGN DECIMALS 14.
ELSE.
WRITE <item> TO contentsitem-value NO-GROUPING
CURRENCY wa_tcurx-currkey NO-SIGN.
ENDIF.
ENDIF.
ELSE.
ASSIGN COMPONENT semaitem-col_cur
OF STRUCTURE <line> TO <g>.
* mit = index of recent row
curritem-top = rowmax + mit + li_commentary_rows.
li_col_pos = sy-index + i_left - 1.
curritem-left = li_col_pos.
* if filed is quantity field (qfieldname ne space)
* or amount field (cfieldname ne space), then format decimal place
* corresponding with config
CLEAR: l_def.
READ TABLE lt_fieldcat_kkblo ASSIGNING <f_fieldcat_line>
WITH KEY tabname = l_tabname
tech = space
no_out = space
col_pos = semaitem-col_no.
IF sy-subrc = 0.
IF <f_fieldcat_line>-cfieldname IS NOT INITIAL.
l_def = 'C'.
ELSE."if <f_fieldcat_line>-qfieldname is not initial.
l_def = 'Q'.
ENDIF.
ENDIF.
* if field is amount field
* exporting of amount field base on currency decimal table: TCURX
IF l_def = 'C'. "field is amount field
SELECT SINGLE * FROM tcurx INTO wa_tcurx
WHERE currkey = <g>.
* if amount ref to un-know currency -> default decimal = 2
IF sy-subrc EQ 0.
curritem-decimals = wa_tcurx-currdec.
ELSE.
curritem-decimals = 2.
ENDIF.
APPEND curritem TO currcells.
IF typ NE 'F'.
WRITE <item> TO contentsitem-value
CURRENCY <g>
NO-SIGN NO-GROUPING.
ELSE.
WRITE <item> TO contentsitem-value
DECIMALS 14 CURRENCY <g>
NO-SIGN NO-GROUPING.
ENDIF.
* if field is quantity field
* exporting of quantity field base on quantity decimal table: T006
ELSE."if l_def = 'Q'. " field is quantity field
CLEAR: wa_t006.
SELECT SINGLE * FROM t006 INTO wa_t006
WHERE msehi = <g>.
* if quantity ref to un-know unit-> default decimal = 2
IF sy-subrc EQ 0.
curritem-decimals = wa_t006-decan.
ELSE.
curritem-decimals = 2.
ENDIF.
APPEND curritem TO currcells.
WRITE <item> TO contentsitem-value
UNIT <g>
NO-SIGN NO-GROUPING.
CONDENSE contentsitem-value.
ENDIF.
ENDIF. "Y9CK007319
ENDIF.
CONDENSE contentsitem-value.
* add function fieldcat-no zero display
LOOP AT lt_fieldcat_kkblo ASSIGNING <f_fieldcat_line>
WHERE tabname = l_tabname
AND tech NE 'X'
AND no_out NE 'X'.
IF <f_fieldcat_line>-col_pos = semaitem-col_no.
IF <f_fieldcat_line>-no_zero = 'X'.
IF <item> = '0'.
CLEAR: contentsitem-value.
ENDIF.
* dun display zero in total/subtotal line too
ELSE.
CLEAR: wa_subtot_indexs.
READ TABLE lt_subtot_indexs INTO wa_subtot_indexs
WITH KEY index = l_save_index.
IF sy-subrc = 0 AND <item> = '0'.
CLEAR: contentsitem-value.
ENDIF.
ENDIF.
ENDIF.
ENDLOOP.
UNASSIGN: <f_fieldcat_line>.
IF <item> LT 0.
SEARCH contentsitem-value FOR 'E'.
IF sy-fdpos EQ 0.
* use prefix notation for signed numbers
TRANSLATE contentsitem-value USING '- '.
CONDENSE contentsitem-value NO-GAPS.
CONCATENATE '-' contentsitem-value
INTO contentsitem-value.
ELSE.
CONCATENATE '-' contentsitem-value
INTO contentsitem-value.
ENDIF.
ENDIF.
SET COUNTRY space.
* Hier wird nur die korrekte Kommaseparatierung gemacht, wenn die
* Zeichen einer
* Zahl enthalten sind. Das ist für Timestamps, die auch ":" enthalten.
* Für die
* darf keine Kommaseparierung stattfinden.
* Changing for correction request - Y6BK041073
IF contentsitem-value CO '0123456789.,-+E '.
TRANSLATE contentsitem-value USING comma_elim.
ENDIF.
ELSE.
CLEAR contentsitem-value.
* if type is not numeric -> dun display with zero
WRITE <item> TO contentsitem-value NO-ZERO.
SHIFT contentsitem-value LEFT DELETING LEADING space.
ENDIF.
APPEND contentsitem TO contents.
ENDIF.
ENDDO.
realmit = realmit + 1.
realoverflow = realoverflow + 1.
mit = mit + 1.
* overflow = current row index in content table
overflow = overflow + 1.
ENDLOOP.
UNASSIGN: <f_fieldcat_line>.
* set item range for set_range_data method
testname = mit / const.
CONDENSE testname.
CONCATENATE 'TEST' testname INTO testname.
realoverflow = realoverflow - 1.
realmit = realmit - 1.
help = realoverflow.
rangeitem-name = testname.
rangeitem-columns = datareal.
rangeitem-rows = help.
APPEND rangeitem TO ranges.
* insert item range dim
temp2 = rowmax + 1 + li_commentary_rows + realmit - realoverflow.
* items data
CALL METHOD lo_spreadsheet->insert_range_dim
EXPORTING
name = testname
top = temp2
left = i_left
rows = help
columns = datareal
no_flush = no_flush.
* get columns header contents for set_range_data method
* export columns header only if no columns header option = space
DATA: rowcount TYPE i.
DATA: columncount TYPE i.
IF i_columns_header = 'X'.
* append columns header to contents: hkey
rowcount = 1.
DO rowmax TIMES.
columncount = 1.
DO hkeycolumns TIMES.
LOOP AT lt_hkey INTO hkeyitem WHERE col_no = columncount
AND row_no = rowcount.
ENDLOOP.
IF sy-subrc = 0.
str = hkeyitem-col_name.
contentsitem-value = hkeyitem-col_name.
ELSE.
contentsitem-value = str.
ENDIF.
contentsitem-column = columncount.
contentsitem-row = rowcount.
APPEND contentsitem TO contents.
columncount = columncount + 1.
ENDDO.
rowcount = rowcount + 1.
ENDDO.
* incase columns header in multiline
DATA: rowmaxtemp TYPE i.
IF rowmax > 1.
rowmaxtemp = rowmax - 1.
rowcount = 1.
DO rowmaxtemp TIMES.
columncount = 1.
DO columnmax TIMES.
contentsitem-column = columncount.
contentsitem-row = rowcount.
contentsitem-value = ''.
APPEND contentsitem TO contents.
columncount = columncount + 1.
ENDDO.
rowcount = rowcount + 1.
ENDDO.
ENDIF.
* append columns header to contents: vkey
columncount = 1.
DO columnmax TIMES.
LOOP AT lt_vkey INTO vkeyitem WHERE col_no = columncount.
ENDLOOP.
contentsitem-value = vkeyitem-col_name.
contentsitem-row = rowmax.
contentsitem-column = columncount.
APPEND contentsitem TO contents.
columncount = columncount + 1.
ENDDO.
*--------------------------------------------------------------------*
* set header range for method set_range_data
* insert header keys range dim
li_head_top = li_commentary_rows + 1.
li_col_pos = i_left.
* insert range headers
IF hkeycolumns NE 0.
rangeitem-name = 'TESTHKEY'.
rangeitem-rows = rowmax.
rangeitem-columns = hkeycolumns.
APPEND rangeitem TO ranges.
CLEAR: rangeitem.
CALL METHOD lo_spreadsheet->insert_range_dim
EXPORTING
name = 'TESTHKEY'
top = li_head_top
left = li_col_pos
rows = rowmax
columns = hkeycolumns
no_flush = no_flush.
ENDIF.
ENDIF.
* format for columns header + total + subtotal
* ------------------------------------------
help = rowmax + realmit. " table + header lines
DATA: lt_format TYPE soi_format_table.
DATA: wa_format LIKE LINE OF lt_format.
DATA: wa_format_temp LIKE LINE OF lt_format.
FIELD-SYMBOLS: <f_source> TYPE any.
FIELD-SYMBOLS: <f_des> TYPE any.
* columns header format
wa_format-front = -1.
wa_format-back = 15. "grey
wa_format-font = space.
wa_format-size = -1.
wa_format-bold = 1.
wa_format-align = 0.
wa_format-frametyp = -1.
wa_format-framecolor = -1.
* get column header format from input record
* -> map input format
IF i_columns_header = 'X'.
wa_format-name = 'TESTHKEY'.
IF i_format_col_header IS NOT INITIAL.
DESCRIBE FIELD i_format_col_header TYPE l_typ COMPONENTS
li_col_num.
DO li_col_num TIMES.
IF sy-index NE 1. " dun map range name
ASSIGN COMPONENT sy-index OF STRUCTURE i_format_col_header
TO <f_source>.
IF <f_source> IS NOT INITIAL.
ASSIGN COMPONENT sy-index OF STRUCTURE wa_format TO <f_des>.
<f_des> = <f_source>.
UNASSIGN: <f_des>.
ENDIF.
UNASSIGN: <f_source>.
ENDIF.
ENDDO.
CLEAR: li_col_num.
ENDIF.
APPEND wa_format TO lt_format.
ENDIF.
* Zusammenfassen der Spalten mit gleicher Nachkommastellenzahl
* collect vertical cells (col) with the same number of decimal places
* to increase perfomance in currency cell format
DESCRIBE TABLE currcells LINES lines.
lines = lines - 1.
DO lines TIMES.
DESCRIBE TABLE currcells LINES innerlines.
innerlines = innerlines - 1.
SORT currcells BY left top.
CLEAR found.
DO innerlines TIMES.
READ TABLE currcells INDEX sy-index INTO curritem.
counter = sy-index + 1.
READ TABLE currcells INDEX counter INTO curritem2.
IF curritem-left EQ curritem2-left.
length = curritem-top + curritem-rows.
IF length EQ curritem2-top AND curritem-decimals EQ curritem2-decimals.
MOVE curritem TO curritem3.
curritem3-rows = curritem3-rows + curritem2-rows.
curritem-left = -1.
MODIFY currcells INDEX sy-index FROM curritem.
curritem2-left = -1.
MODIFY currcells INDEX counter FROM curritem2.
APPEND curritem3 TO currcells.
found = 'X'.
ENDIF.
ENDIF.
ENDDO.
IF found IS INITIAL.
EXIT.
ENDIF.
DELETE currcells WHERE left = -1.
ENDDO.
* Zusammenfassen der Zeilen mit gleicher Nachkommastellenzahl
* collect horizontal cells (row) with the same number of decimal places
* to increase perfomance in currency cell format
DESCRIBE TABLE currcells LINES lines.
lines = lines - 1.
DO lines TIMES.
DESCRIBE TABLE currcells LINES innerlines.
innerlines = innerlines - 1.
SORT currcells BY top left.
CLEAR found.
DO innerlines TIMES.
READ TABLE currcells INDEX sy-index INTO curritem.
counter = sy-index + 1.
READ TABLE currcells INDEX counter INTO curritem2.
IF curritem-top EQ curritem2-top AND curritem-rows EQ
curritem2-rows.
length = curritem-left + curritem-columns.
IF length EQ curritem2-left AND curritem-decimals EQ curritem2-decimals.
MOVE curritem TO curritem3.
curritem3-columns = curritem3-columns + curritem2-columns.
curritem-left = -1.
MODIFY currcells INDEX sy-index FROM curritem.
curritem2-left = -1.
MODIFY currcells INDEX counter FROM curritem2.
APPEND curritem3 TO currcells.
found = 'X'.
ENDIF.
ENDIF.
ENDDO.
IF found IS INITIAL.
EXIT.
ENDIF.
DELETE currcells WHERE left = -1.
ENDDO.
* Ende der Zusammenfassung
* item data: format for currency cell, corresponding with currency
CALL METHOD lo_spreadsheet->cell_format
EXPORTING
cells = currcells
no_flush = no_flush.
* item data: write item table content
CALL METHOD lo_spreadsheet->set_ranges_data
EXPORTING
ranges = ranges
contents = contents
no_flush = no_flush.
* whole table range to format all table
IF i_columns_header = 'X'.
li_head_top = li_commentary_rows + 1.
ELSE.
li_head_top = li_commentary_rows + 2.
help = help - 1.
ENDIF.
CALL METHOD lo_spreadsheet->insert_range_dim
EXPORTING
name = 'WHOLE_TABLE'
top = li_head_top
left = i_left
rows = help
columns = datareal
no_flush = no_flush.
* columns width auto fix
* this parameter = space in case use with exist template
IF i_columns_autofit = 'X'.
CALL METHOD lo_spreadsheet->fit_widest
EXPORTING
name = 'WHOLE_TABLE'
no_flush = no_flush.
ENDIF.
* frame
* The parameter has 8 bits
*0 Left margin
*1 Top marginT
*2 Bottom margin
*3 Right margin
*4 Horizontal line
*5 Vertical line
*6 Thinness
*7 Thickness
* here 127 = 1111111 6-5-4-3-2-1 mean Thin-ver-hor-right-bot-top-left
* ( final DOI method call, set no_flush = space
* equal to call method CL_GUI_CFW=>FLUSH )
CALL METHOD lo_spreadsheet->set_frame
EXPORTING
rangename = 'WHOLE_TABLE'
typ = 127
color = 1
no_flush = space
IMPORTING
error = lo_error
retcode = lc_retcode.
error_doi.
* reformat subtotal / total line after format wholw table
LOOP AT subranges INTO subrangeitem.
l_sub_index = subrangeitem-rows + li_commentary_rows + rowmax.
CALL METHOD lo_spreadsheet->insert_range_dim
EXPORTING
name = subrangeitem-name
left = i_left
top = l_sub_index
rows = 1
columns = datareal
no_flush = no_flush.
wa_format-name = subrangeitem-name.
* default format:
* - clolor: subtotal = light yellow, subtotal = yellow
* - frame: box
IF subrangeitem-name(3) = 'SUB'.
wa_format-back = 36. "subtotal line
wa_format_temp = i_format_subtotal.
ELSE.
wa_format-back = 27. "total line
wa_format_temp = i_format_total.
ENDIF.
wa_format-frametyp = 79.
wa_format-framecolor = 1.
wa_format-number = -1.
wa_format-align = -1.
* get subtoal + total format from intput parameter
* overwrite default format
IF wa_format_temp IS NOT INITIAL.
DESCRIBE FIELD wa_format_temp TYPE l_typ COMPONENTS li_col_num.
DO li_col_num TIMES.
IF sy-index NE 1. " dun map range name
ASSIGN COMPONENT sy-index OF STRUCTURE wa_format_temp
TO <f_source>.
IF <f_source> IS NOT INITIAL.
ASSIGN COMPONENT sy-index OF STRUCTURE wa_format TO <f_des>.
<f_des> = <f_source>.
UNASSIGN: <f_des>.
ENDIF.
UNASSIGN: <f_source>.
ENDIF.
ENDDO.
CLEAR: li_col_num.
ENDIF.
APPEND wa_format TO lt_format.
CLEAR: wa_format-name.
CLEAR: l_sub_index.
CLEAR: wa_format_temp.
ENDLOOP.
IF lt_format[] IS NOT INITIAL.
CALL METHOD lo_spreadsheet->set_ranges_format
EXPORTING
formattable = lt_format
no_flush = no_flush.
REFRESH: lt_format.
ENDIF.
*--------------------------------------------------------------------*
CALL METHOD lo_spreadsheet->screen_update
EXPORTING
updating = 'X'.
CALL METHOD c_oi_errors=>flush_errors.
lo_error_w = l_error.
lc_retcode = lo_error_w->error_code.
** catch no_flush -> led to dump ( optional )
* go_error = l_error.
* gc_retcode = go_error->error_code.
* error_doi.
CLEAR:
lt_sema,
wa_sema,
lt_hkey,
wa_hkey,
lt_vkey,
wa_vkey,
l_n_hrz_keys,
l_n_att_cols,
l_n_vrt_keys,
count,
datac,
datareal,
vkeycount,
all,
mit,
li_col_pos,
li_col_num,
ranges,
rangeitem,
contents,
contentsitem,
semaitem,
hkeyitem,
vkeyitem,
li_commentary_rows,
l_retcode,
li_head_top,
<f_excel_tab>.
CLEAR:
lo_error_w.
UNASSIGN:
<line>,
<item>,
<f_excel_tab>.
*--------------------------------------------------------------------*
* SESSION 5: SAVE AND CLOSE FILE
*--------------------------------------------------------------------*
* ex of save path: 'FILE://C:\temp\test.xlsx'
CONCATENATE 'FILE://' i_save_path
INTO ls_path.
CALL METHOD lo_proxy->save_document_to_url
EXPORTING
no_flush = 'X'
url = ls_path
IMPORTING
error = lo_error
retcode = lc_retcode
CHANGING
document_size = li_document_size.
error_doi.
* if save successfully -> raise successful message
MESSAGE i400(zabap2xlsx).
CLEAR:
ls_path,
li_document_size.
close_document.
ENDMETHOD. "BIND_ALV_OLE2
ENDCLASS.