SAP EXCEL DOI 详解

发布于:2025-04-03 ⋅ 阅读:(16) ⋅ 点赞:(0)

什么是DOI

DOI是Desktop Office Integration的缩写,是SAP提供的解决与Office集成的开发技术。早期SAP用于解决Office集成,使用的是OLE技术。

OLE有两个比较大的缺点:

一是语法参照VBA,在ABAP中使用各种VBA的方法(method),语法的友好性差;

二是数据写入到Excel中,速度特别慢,慢得难以接受。 
DOI是SAP提供的OLE的替代品,用面向对象的方式实现。很好的解决了上面所说的两个问题。

请参考:Desktop Office Integration官方帮助

DOI开发的要点

涉及的关对象

SAP和Office集成,可以通过代码创建Excel文档,也可以将Excel模板文档放在应用程序服务器上。DOI打开这个文档,然后对文档进行操作。我们先介绍创建Excel文档的方式。

为了操作Excel文档,至少需要四个对象:

  • Container: 存放excel电子表格(spreadsheet)的容器。这个开发人员应该比较容易理解,展示spreadsheet肯定需要一个容器来存放。这个容器一般在dialog screen中定义,也可以直接使用ABAP程序默认的screen(即screen号码为1000的屏幕)

  • container control: 容器中用于创建和管理其他Office集成所需要的对象。container control是一个接口,类型是i_oi_container_control

  • document proxy: 每一个document proxy的实例代表用office application打开的文档,可以是excel,也可以是word。如果想打开多个文档,需要定义多个实例。document proxy是一个接口,类型为i_oi_document_proxy

  • spreadsheet: spreadsheet接口,代表最终要操作的excel文档。spreadhseet的类型是i_oi_spreadsheet

如果读取服务器上的文档模板,需要cl_bds_document_set类:

  • business document set: bds是business document set的缩写。business document set用于管理后续要操作的文档,可以包含一个或多个文档。

DOI操作文档的步骤

  • 获取container
  • 创建container control对象实例并初始化
  • 创建document proxy对象的实例
  • 打开一个服务器上的模板文档或新建一个excel文档
  • 操作excel文档,设置excel的属性
  • 退出时关闭excel文档,释放资源

我们的第一个例子,不使用dialog screen,新建一个excel文档,在屏幕1000中显示。

开始编码

定义公共变量

* desktop office integration interface
data: gr_container type ref to cl_gui_container,
      gr_control type ref to i_oi_container_control,
      gr_document type ref to i_oi_document_proxy,
      gr_spreadsheet type ref to i_oi_spreadsheet.

这些就是我们前面提到的四个必须的对象。

获取container

cl_gui_container类的静态方法screen0获取屏幕1000,并赋值给gr_container。 

form get_container.
  gr_container = gr_container = cl_gui_container=>screen0.
endform.                    "get_container 

 创建container control对象实例并初始化

form create_container_control.
* create container control
  call method c_oi_container_control_creator=>get_container_control
    importing
      control = gr_control.
 
* initialize control
  call method gr_control->init_control
    exporting
      inplace_enabled          = 'X '
      inplace_scroll_documents = 'X'
      register_on_close_event  = 'X'
      register_on_custom_event = 'X'
      r3_application_name      = 'DOI demo by Stone Wang'
      parent                   = gr_container.
endform.   

代码比较直观,不多说。

创建document proxy对象的实例

form create_excel_document.
  call method gr_control->get_document_proxy
    exporting
      document_type  = 'Excel.Sheet'
      no_flush       = 'X'
    importing
      document_proxy = gr_document.
 
  call method gr_document->create_document
    exporting
      document_title = 'DOI test by Stone Wang '
      no_flush       = 'X '
      open_inplace   = 'X'.
endform.                    "create_excel_document

open_inplace参数控制excel文档是独立显示还是在SAP GUI中嵌入显示。如果嵌入显示,gr_control的init_control方法中,inplace_enabled参数要设为X

将以上代码综合在一起:

form main.
  skip 1.
 
  perform get_container.
  perform create_container_control.
  perform create_excel_document.
endform.

注意第一句skip 1必须,否则不能创建屏幕。

完整代码

report  zdoi_hello.
 
type-pools: soi.
 
data: gr_container type ref to cl_gui_container,
      gr_control type ref to i_oi_container_control,
      gr_document type ref to i_oi_document_proxy,
      gr_spreadsheet type ref to i_oi_spreadsheet.
 
start-of-selection.
  perform main.
 
form get_container.
  gr_container = gr_container = cl_gui_container=>screen0.
endform.                    "get_container
form create_container_control.
* create container control
  call method c_oi_container_control_creator=>get_container_control
    importing
      control = gr_control.
* initialize control
  call method gr_control->init_control
    exporting
      inplace_enabled          = 'X '
      inplace_scroll_documents = 'X'
      register_on_close_event  = 'X'
      register_on_custom_event = 'X'
      r3_application_name      = 'DOI demo by Stone Wang'
      parent                   = gr_container.
endform.                    "create_container_control
 
form create_excel_document.
  call method gr_control->get_document_proxy
    exporting
      document_type  = 'Excel.Sheet'
      no_flush       = 'X'
    importing
      document_proxy = gr_document.
 
  call method gr_document->create_document
    exporting
      document_title = 'DOI test by Stone Wang '
      no_flush       = 'X '
      open_inplace   = 'X'.
endform.                    "create_excel_document
form main.
  skip 1.
  perform get_container.
  perform create_container_control.
  perform create_excel_document.
endform.

 应用案例1

*&---------------------------------------------------------------------*
*& Report ZFI1029_D01
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT zfi1029_d01.

TABLES:zsfi0277,faglflext,ztfi1000_01.
INCLUDE zfi_authority_check.

**************************************************************************
*--------------------------------变量定义--------------------------------*
**************************************************************************

TYPES:
  BEGIN OF ty_all,
    bukrs TYPE ztfi0005-hcode,
    butxt TYPE ztfi0005-hname,
    bbzt  TYPE char6,           "保存状态
    itab  TYPE zsfi0319_t,
  END OF ty_all,
  BEGIN OF ty_output_meta,
    sheet_name TYPE char10,
    row        TYPE i,
    column     TYPE i,
    value      TYPE char100,
  END OF ty_output_meta,

  BEGIN OF ty_datum,
    datum TYPE char20,
  END OF ty_datum.

DATA:
  gv_message     TYPE char100,
  gv_bbzt        TYPE char6,
  gv_zpath       TYPE localfile,                "文件路径
  g_fname        TYPE string,
  gv_ok          TYPE i,
  gt_output_meta TYPE TABLE OF ty_output_meta,
  gt_datum       TYPE TABLE OF ty_datum,        "查询期间
  gt_itab        TYPE TABLE OF zsfi0319,        "非重复的往来公司
  gt_all         TYPE TABLE OF ty_all.          "全部公司的报表数据

DATA:
  go_table   TYPE REF TO data. "期末数
**************************************************************************
*--------------------------------选择画面--------------------------------*
**************************************************************************
SELECT-OPTIONS:
  s_bukrs FOR ztfi1000_01-bukrs OBLIGATORY MEMORY ID buk.
PARAMETERS:
  p_gjahr TYPE ztfi1000_01-gjahr OBLIGATORY,
  p_monat TYPE ztfi1000_01-monat OBLIGATORY.
*SELECTION-SCREEN BEGIN OF BLOCK b01 WITH FRAME TITLE TEXT-t01.
*PARAMETERS:
*  p_total AS CHECKBOX ,
*  p_bg    AS CHECKBOX USER-COMMAND uc1,
*  p_mail  TYPE ad_smtpadr,
*  p_subj  TYPE so_obj_des.
*
*SELECTION-SCREEN END OF BLOCK b01.

* 公司代码帮助专用
SELECT-OPTIONS:
  s_accgrp  FOR zsfi0277-parentaccgroup NO-DISPLAY,
  s_acc     FOR faglflext-racct NO-DISPLAY.

INITIALIZATION.
  DATA(l_period) = zcl_fi_utility=>get_default_closing_period( ).
  p_gjahr = l_period+0(4).
  p_monat = l_period+4(2).

AT SELECTION-SCREEN OUTPUT.
*  LOOP AT SCREEN.
*    IF screen-name CP '*P_MAIL*'
*      OR screen-name CP '*P_SUBJ*' .
*      IF p_bg = ''.
*        screen-active = 0.
*      ELSE.
*        screen-active = 1.
*      ENDIF.
*      MODIFY SCREEN.
*    ENDIF.
*  ENDLOOP.

**************************************************************************
*----------------------------------事件----------------------------------*
**************************************************************************
AT SELECTION-SCREEN ON VALUE-REQUEST FOR s_bukrs-low.
  PERFORM f4_bukrs USING 'S_BUKRS-LOW'.

AT SELECTION-SCREEN ON VALUE-REQUEST FOR s_bukrs-high.
  PERFORM f4_bukrs USING 'S_BUKRS-HIGH'.

AT SELECTION-SCREEN.
 “公司代码权限检查

START-OF-SELECTION.
*  IF p_bg IS NOT INITIAL. "后台模式
*    IF p_mail IS INITIAL.
*      MESSAGE TEXT-e01 TYPE 'S' DISPLAY LIKE 'E'.
*      STOP.
*    ENDIF.
*    PERFORM frm_run_as_job.
*    " no need to return the current program, as it's already submit as background job
*    STOP.
*  ENDIF.
* 取得权益占用统计报表
  PERFORM frm_impotr_zfi1000_r01.

* 编辑模板第一列数据
  PERFORM frm_edit_col.

* 编辑模板表内容
  PERFORM frm_edit_data.

* 取得路径
  PERFORM frm_get_oath.

  IF gv_zpath IS INITIAL AND sy-batch IS INITIAL.
    MESSAGE '操作已取消' TYPE 'S'.
    LEAVE LIST-PROCESSING.
  ENDIF.

* 导出excel
*  PERFORM frm_download_meta_data.
*  PERFORM frm_export_excel_xlsm.
   PERFORM frm_export_excel.

**************************************************************************
*---------------------------------主处理---------------------------------*
**************************************************************************

*&---------------------------------------------------------------------*
*&      Form  F4_BUKRS
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM f4_bukrs  USING iv_field.

  DATA: lv_flag    TYPE c,
        lv_fd(132) TYPE c.

  DATA: ls_accgrp LIKE LINE OF s_accgrp,
        ls_acc    LIKE LINE OF s_acc.

  DATA: lt_tree          TYPE zfi_t_account_tree_alv,
        lt_tree_selected LIKE lt_tree,
        ls_tree          LIKE LINE OF lt_tree.

  DATA  ls_bukrs LIKE LINE OF s_bukrs.

  DATA: lv_dyname     TYPE d020s-prog,
        lv_dynumb     TYPE d020s-dnum,
        lt_dynpfields TYPE STANDARD TABLE OF dynpread,
        ls_dynpfields LIKE LINE OF lt_dynpfields.

  CONSTANTS lc_dynnr_sel TYPE sy-dynnr VALUE '1000'.

  CONSTANTS lc_group TYPE ktopl VALUE 'VK00'.

  CONSTANTS: lc_sign   LIKE ls_bukrs-sign VALUE 'I',
             lc_option LIKE ls_bukrs-option VALUE 'EQ'.

* Search help only allowed in standard selection screen
  IF sy-dynnr <> lc_dynnr_sel.
    GET CURSOR FIELD lv_fd.
    CALL FUNCTION 'F4IF_FIELD_VALUE_REQUEST'
      EXPORTING
        tabname     = 'T001'
        fieldname   = 'BUKRS'
        searchhelp  = 'C_T001'
        dynpprog    = sy-repid
        dynpnr      = sy-dynnr
        dynprofield = lv_fd.

    RETURN.

  ENDIF.

* Get old select from selection screen other elements
  ls_tree-finalflag = abap_true.
  LOOP AT s_accgrp INTO ls_accgrp.

    ls_tree-accgroup = ls_accgrp-low.
    APPEND ls_tree TO lt_tree_selected.

  ENDLOOP.

  CLEAR ls_tree-finalflag.
  LOOP AT s_acc INTO ls_acc.

    ls_tree-accgroup = ls_acc-low.
    APPEND ls_tree TO lt_tree_selected.

  ENDLOOP.

  REFRESH: s_accgrp,
           s_acc.

  CALL FUNCTION 'ZFI_BUKRS_TREE_HELP'
    EXPORTING
      iv_group        = lc_group
      iv_sub          = abap_true
      it_selected_acc = lt_tree_selected
    IMPORTING
      ct_acc_tree     = lt_tree
      cv_exit         = lv_flag.
  IF lv_flag IS NOT INITIAL.
    EXIT.
  ELSE.
    REFRESH s_bukrs.
  ENDIF.

  IF NOT lt_tree IS INITIAL.

    ls_bukrs-sign = lc_sign.
    ls_bukrs-option = lc_option.

    LOOP AT lt_tree INTO ls_tree
         WHERE finalflag IS INITIAL.

      ls_bukrs-low = ls_tree-accgroup.
      APPEND ls_bukrs TO s_bukrs.

    ENDLOOP.
*   Update selection screen backup elements
    ls_accgrp-sign = lc_sign.
    ls_accgrp-option = lc_option.

    ls_acc-sign = lc_sign.
    ls_acc-option = lc_option.

    LOOP AT lt_tree INTO ls_tree.

      IF ls_tree-finalflag = abap_true.

        ls_accgrp-low = ls_tree-accgroup.
        APPEND ls_accgrp TO s_accgrp.

      ELSE.

        ls_acc-low = ls_tree-accgroup.
        APPEND ls_acc TO s_acc.

      ENDIF.

    ENDLOOP.

  ENDIF.

* Sychronize S_HKONT-LOW with first account
  lv_dyname = sy-repid.
  lv_dynumb = sy-dynnr.

  ls_dynpfields-fieldname = iv_field.
  READ TABLE s_bukrs INDEX 1 INTO ls_bukrs.
  IF sy-subrc = 0.

    ls_dynpfields-fieldvalue = ls_bukrs-low.

  ENDIF.
  APPEND ls_dynpfields TO lt_dynpfields.

  CALL FUNCTION 'DYNP_VALUES_UPDATE'
    EXPORTING
      dyname               = lv_dyname
      dynumb               = lv_dynumb
    TABLES
      dynpfields           = lt_dynpfields
    EXCEPTIONS
      invalid_abapworkarea = 1
      invalid_dynprofield  = 2
      invalid_dynproname   = 3
      invalid_dynpronummer = 4
      invalid_request      = 5
      no_fielddescription  = 6
      undefind_error       = 7
      OTHERS               = 8.
  IF sy-subrc <> 0.

    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
          WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.

  ENDIF.

ENDFORM.
*&---------------------------------------------------------------------*
*&      Form  FRM_IMPOTR_ZFI1000_R01
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM frm_impotr_zfi1000_r01 .

  DATA:
    lt_t001  TYPE TABLE OF t001,     "所有公司代码
    ls_t001  LIKE LINE  OF lt_t001,
    lt_rspar TYPE TABLE OF rsparams,
    ls_rspar LIKE LINE  OF lt_rspar,
    lt_fcat  TYPE lvc_t_fcat,
    ls_fcat  TYPE lvc_s_fcat,
    lt_itab  TYPE TABLE OF zsfi0319,
    ls_all   LIKE LINE  OF gt_all.

  CLEAR:gt_datum,gt_itab ,gt_all.

  SELECT *
    INTO TABLE lt_t001
    FROM t001
   WHERE bukrs IN s_bukrs.

* 权限检查已经chenk过,不会失败
  CHECK sy-subrc = 0.
  SORT lt_t001 BY bukrs ASCENDING.
  DESCRIBE TABLE lt_t001 LINES gv_ok.

  ls_fcat-fieldname = 'MARK'.       "标识列,区分公司属于哪种往来类型,输出时会删掉
  ls_fcat-datatype  = 'ZE_ZYXT1'.
  ls_fcat-intlen = 100.
  APPEND ls_fcat TO lt_fcat.

  ls_fcat-fieldname = 'NAME'.       "字段ID列,项目名称存在重复的,靠此字段区分,输出时会删掉
  ls_fcat-datatype  = 'ZE_ZYXT1'.
  ls_fcat-intlen = 100.
  APPEND ls_fcat TO lt_fcat.

  ls_fcat-fieldname = 'XM'.         "项目
  ls_fcat-datatype  = 'ZE_ZYXT1'.
  ls_fcat-intlen = 100.
  APPEND ls_fcat TO lt_fcat.

  ls_fcat-fieldname = 'SUM'.        "合计
  ls_fcat-datatype  = 'ZE_ZYXT1'.
  ls_fcat-intlen = 100.
  APPEND ls_fcat TO lt_fcat.

  LOOP AT lt_t001 INTO ls_t001.
    ls_fcat-fieldname = ls_t001-bukrs.  "字段ID为公司代码
    ls_fcat-datatype  = 'ZE_ZYXT1'.
    ls_fcat-intlen = 100.
    APPEND ls_fcat TO lt_fcat.
  ENDLOOP.

* 创建动态内表
  PERFORM frm_create_table USING lt_fcat.

  LOOP AT lt_t001 INTO ls_t001.

    CLEAR:lt_itab[].

*   编辑传入参数
    ls_rspar-selname = 'P_BUKRS'.
    ls_rspar-kind    = 'P'.
    ls_rspar-low     = ls_t001-bukrs.
    APPEND ls_rspar TO lt_rspar.

    CLEAR:ls_rspar.
    ls_rspar-selname = 'P_GJAHR'.
    ls_rspar-kind    = 'P'.
    ls_rspar-low     = p_gjahr.
    APPEND ls_rspar TO lt_rspar.

    CLEAR:ls_rspar.
    ls_rspar-selname = 'P_MONAT'.
    ls_rspar-kind    = 'P'.
    ls_rspar-low     = p_monat.
    APPEND ls_rspar TO lt_rspar.

    CLEAR:ls_rspar.
    ls_rspar-selname = 'P_EXPORT'.
    ls_rspar-kind    = 'P'.
    ls_rspar-low     = 'X'.     "传输数据专用
    APPEND ls_rspar TO lt_rspar.

*   调用程序:权益占用统计报表
    SUBMIT zfi1000_r01 WITH SELECTION-TABLE lt_rspar AND RETURN.

*   来源程序:ZFI1000_R01
    IMPORT p1 = lt_itab             "所得税计算明细表
           p2 = gv_bbzt             "所得税费用表
           FROM MEMORY ID 'ZFI1000_R01'.

    DELETE FROM MEMORY ID 'ZFIR029'.

*   保存所有往来公司
    APPEND LINES OF lt_itab TO gt_itab.

*   存储所有公司数据
    ls_all-bukrs = ls_t001-bukrs.
    ls_all-butxt = ls_t001-butxt.
    ls_all-bbzt  = gv_bbzt.
    ls_all-itab  = lt_itab[].
    APPEND ls_all TO gt_all.

  ENDLOOP.

ENDFORM.
*&---------------------------------------------------------------------*
*&      Form  FRM_CREATE_TABLE
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM frm_create_table  USING    it_fcat TYPE lvc_t_fcat.

  CLEAR:go_table.

* 期末数
  cl_alv_table_create=>create_dynamic_table(
     EXPORTING
       it_fieldcatalog           = it_fcat
     IMPORTING
       ep_table                  = go_table
     EXCEPTIONS
       generate_subpool_dir_full = 1
       OTHERS                    = 2
   ).

ENDFORM.
*&---------------------------------------------------------------------*
*&      Form  FRM_EDIT_COL
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM frm_edit_col .

  DATA:
    lv_mark   TYPE char1,
    ls_all    LIKE LINE  OF gt_all,
    ls_itab   LIKE LINE  OF ls_all-itab,
    ls_itab_g LIKE LINE  OF gt_itab.

  FIELD-SYMBOLS:
    <fs_table> TYPE STANDARD TABLE,
    <fs_st>    TYPE any,
    <fs_value> TYPE any.

* MARK,EDIT都不为空,且fieldname是空的表行,就是往来公司的数据行
  DELETE gt_itab WHERE mark IS INITIAL OR edit IS INITIAL OR fieldname IS NOT INITIAL.

* 排除重复的往来公司
  SORT gt_itab BY mark ASCENDING pname ASCENDING.
  DELETE ADJACENT DUPLICATES FROM gt_itab COMPARING mark pname.

  ASSIGN go_table->* TO <fs_table>.

* 初始化导出动态内表第一列
  APPEND INITIAL LINE TO <fs_table> ASSIGNING <fs_st>.  "第1行
  ASSIGN COMPONENT 'XM' OF STRUCTURE <fs_st> TO <fs_value>.
  IF sy-subrc = 0 AND <fs_value> IS ASSIGNED.
    <fs_value> = '项目'.
  ENDIF.

  ASSIGN COMPONENT 'SUM' OF STRUCTURE <fs_st> TO <fs_value>.
  IF sy-subrc = 0 AND <fs_value> IS ASSIGNED.
    <fs_value> = '合计'.
  ENDIF.

  APPEND INITIAL LINE TO <fs_table> ASSIGNING <fs_st>.  "第2行
  ASSIGN COMPONENT 'XM' OF STRUCTURE <fs_st> TO <fs_value>.
  IF sy-subrc = 0 AND <fs_value> IS ASSIGNED.
    <fs_value> = '项目'.
  ENDIF.

  ASSIGN COMPONENT 'SUM' OF STRUCTURE <fs_st> TO <fs_value>.
  IF sy-subrc = 0 AND <fs_value> IS ASSIGNED.
    <fs_value> = '合计'.
  ENDIF.

  APPEND INITIAL LINE TO <fs_table> ASSIGNING <fs_st>.  "第3行
  ASSIGN COMPONENT 'XM' OF STRUCTURE <fs_st> TO <fs_value>.
  IF sy-subrc = 0 AND <fs_value> IS ASSIGNED.
    <fs_value> = '报表状态'.
  ENDIF.

* 随便取的一家公司数据,编辑模板第一列
  READ TABLE gt_all INTO ls_all INDEX 1.

* 删除这家公司的所有往来公司行,模板的往来公司行使用内表gt_itab编辑
  DELETE ls_all-itab WHERE mark IS NOT INITIAL AND edit IS NOT INITIAL AND fieldname IS INITIAL.
  LOOP AT ls_all-itab INTO ls_itab.

    CLEAR:lv_mark.

*   全角空格转半角
    CALL FUNCTION 'SJIS_DBC_TO_SBC'
      EXPORTING
        special  = 'X'
      CHANGING
        text     = ls_itab-pname
      EXCEPTIONS
        overflow = 1
        OTHERS   = 2.

    CONDENSE ls_itab-pname NO-GAPS.

    APPEND INITIAL LINE TO <fs_table> ASSIGNING <fs_st>.
    ASSIGN COMPONENT 'XM' OF STRUCTURE <fs_st> TO <fs_value>.
    IF sy-subrc = 0 AND <fs_value> IS ASSIGNED.
      <fs_value> = ls_itab-pname.
    ENDIF.

    ASSIGN COMPONENT 'MARK' OF STRUCTURE <fs_st> TO <fs_value>.
    IF sy-subrc = 0 AND <fs_value> IS ASSIGNED.
      <fs_value> = ls_itab-mark.
    ENDIF.

    ASSIGN COMPONENT 'NAME' OF STRUCTURE <fs_st> TO <fs_value>.
    IF sy-subrc = 0 AND <fs_value> IS ASSIGNED.
      <fs_value> = ls_itab-fieldname.
    ENDIF.

    CASE ls_itab-pname.
      WHEN '1、往来'.
        lv_mark = '1'.
      WHEN '2、股东借款'.
        lv_mark = '2'.
      WHEN '3、加计占用'.
        lv_mark = '3'.
      WHEN '合作方往来'.
        lv_mark = '4'.
      WHEN '权益部分代总部投资'.
        lv_mark = '5'.
      WHEN OTHERS.
        CONTINUE.
    ENDCASE.

    LOOP AT gt_itab INTO ls_itab_g WHERE mark = lv_mark.

*     全角空格转半角
      CALL FUNCTION 'SJIS_DBC_TO_SBC'
        EXPORTING
          special  = 'X'
        CHANGING
          text     = ls_itab_g-pname
        EXCEPTIONS
          overflow = 1
          OTHERS   = 2.

      CONDENSE ls_itab_g-pname NO-GAPS.

      APPEND INITIAL LINE TO <fs_table> ASSIGNING <fs_st>.
      ASSIGN COMPONENT 'XM' OF STRUCTURE <fs_st> TO <fs_value>.
      IF sy-subrc = 0 AND <fs_value> IS ASSIGNED.
        <fs_value> = ls_itab_g-pname.
      ENDIF.

      ASSIGN COMPONENT 'MARK' OF STRUCTURE <fs_st> TO <fs_value>.
      IF sy-subrc = 0 AND <fs_value> IS ASSIGNED.
        <fs_value> = ls_itab_g-mark.
      ENDIF.

    ENDLOOP.

  ENDLOOP.

ENDFORM.
*&---------------------------------------------------------------------*
*&      Form  FRM_EDIT_DATA
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM frm_edit_data .

  DATA:
    lv_datum TYPE char20,
    ls_all   LIKE LINE OF gt_all,
    ls_itab  LIKE LINE OF ls_all-itab.

  FIELD-SYMBOLS:
    <fs_table> TYPE STANDARD TABLE,
    <fs_st>    TYPE any,
    <fs_value> TYPE any,
    <fs_sum>   TYPE any.

  ASSIGN go_table->* TO <fs_table>.

  LOOP AT gt_all INTO ls_all.

    READ TABLE <fs_table> ASSIGNING <fs_st> INDEX 1.
    ASSIGN COMPONENT ls_all-bukrs OF STRUCTURE <fs_st> TO <fs_value>.
    IF sy-subrc = 0 AND <fs_value> IS ASSIGNED.
      <fs_value> = ls_all-bukrs.
    ENDIF.

    READ TABLE <fs_table> ASSIGNING <fs_st> INDEX 2.
    ASSIGN COMPONENT ls_all-bukrs OF STRUCTURE <fs_st> TO <fs_value>.
    IF sy-subrc = 0 AND <fs_value> IS ASSIGNED.
      <fs_value> = ls_all-butxt.
    ENDIF.

    READ TABLE <fs_table> ASSIGNING <fs_st> INDEX 3.
    ASSIGN COMPONENT ls_all-bukrs OF STRUCTURE <fs_st> TO <fs_value>.
    IF sy-subrc = 0 AND <fs_value> IS ASSIGNED.
      <fs_value> = ls_all-bbzt.
    ENDIF.

    LOOP AT ls_all-itab INTO ls_itab.

*     全角空格转半角
      CALL FUNCTION 'SJIS_DBC_TO_SBC'
        EXPORTING
          special  = 'X'
        CHANGING
          text     = ls_itab-pname
        EXCEPTIONS
          overflow = 1
          OTHERS   = 2.

      CONDENSE ls_itab-pname NO-GAPS.

      READ TABLE <fs_table> ASSIGNING <fs_st> WITH KEY ('XM') = ls_itab-pname
                                                       ('MARK') = ls_itab-mark
                                                       ('NAME') = ls_itab-fieldname.

      IF sy-subrc = 0.
        ASSIGN COMPONENT ls_all-bukrs OF STRUCTURE <fs_st> TO <fs_value>.
        IF sy-subrc = 0 AND <fs_value> IS ASSIGNED.
          <fs_value> = ls_itab-dmbtr.
          CONDENSE <fs_value> NO-GAPS.
          CALL FUNCTION 'CLOI_PUT_SIGN_IN_FRONT'  "负号提前
            CHANGING
              value = <fs_value>.
        ELSE. "动态表创建逻辑不改的话,这里不会失败
          CONTINUE.
        ENDIF.

*       比例不用合计
        IF ls_itab-pname = '一、权益比例(%)'.
          <fs_value> = <fs_value> / 100.
          CONTINUE.
        ENDIF.

        ASSIGN COMPONENT 'SUM' OF STRUCTURE <fs_st> TO <fs_sum>.
        IF sy-subrc = 0 AND <fs_sum> IS ASSIGNED.
          <fs_sum> = <fs_sum> + ls_itab-dmbtr.
          CONDENSE <fs_sum> NO-GAPS.
          CALL FUNCTION 'CLOI_PUT_SIGN_IN_FRONT'  "负号提前
            CHANGING
              value = <fs_sum>.
        ENDIF.

      ENDIF.

    ENDLOOP.

  ENDLOOP.

* 编辑查询期间
  CONCATENATE '查询期间:' p_gjahr '年' p_monat '月' INTO lv_datum.

  INSERT INITIAL LINE INTO <fs_table> INDEX 1 ASSIGNING <fs_st>.
  ASSIGN COMPONENT 'XM' OF STRUCTURE <fs_st> TO <fs_value>.
  IF sy-subrc = 0 AND <fs_value> IS ASSIGNED.
    <fs_value> = lv_datum.
  ENDIF.

ENDFORM.
*&---------------------------------------------------------------------*
*&      Form  FRM_GET_OATH
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM frm_get_oath .

  DATA:
    lv_bukrs TYPE char100,
    lv_fname TYPE string,
    lv_path  TYPE string,
    lv_temp1 TYPE string,
    lv_temp2 TYPE string,
    lv_fpath TYPE string.

  CONCATENATE '权益占用统计汇总表' '_' p_gjahr p_monat '.XLSX'
         INTO g_fname.

  CLEAR:gv_zpath.
  lv_fname = g_fname.

  CALL METHOD cl_gui_frontend_services=>get_desktop_directory
    CHANGING
      desktop_directory    = lv_path
    EXCEPTIONS
      cntl_error           = 0
      error_no_gui         = 0
      not_supported_by_gui = 0
      OTHERS               = 0.

  CALL METHOD cl_gui_frontend_services=>file_save_dialog
    EXPORTING
      default_file_name         = lv_fname
      file_filter               = cl_gui_frontend_services=>filetype_excel
      initial_directory         = lv_path
      prompt_on_overwrite       = 'X'
    CHANGING
      filename                  = lv_temp1
      path                      = lv_temp2
      fullpath                  = lv_fpath
    EXCEPTIONS
      cntl_error                = 0
      error_no_gui              = 0
      not_supported_by_gui      = 0
      invalid_default_file_name = 0
      OTHERS                    = 0.

  gv_zpath = lv_fpath.

ENDFORM.
*&---------------------------------------------------------------------*
*&      Form  FRM_EXPORT_EXCEL
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM frm_export_excel.

  DATA:
    lo_iref_template    TYPE REF TO cl_bds_document_set,
    lo_oref_container   TYPE REF TO cl_gui_custom_container,
    lo_iref_document    TYPE REF TO i_oi_document_proxy,
    lo_iref_error       TYPE REF TO i_oi_error,
    lo_iref_spreadsheet TYPE REF TO i_oi_spreadsheet,
    lo_iref_control     TYPE REF TO i_oi_container_control.

  DATA:
    lv_message   TYPE char200,
    lv_text      TYPE char50,
    lv_table     TYPE char20,
    lv_last_row  TYPE i,
    lv_last_col  TYPE i,
    lt_fields    TYPE STANDARD TABLE OF rfc_fields,
    lt_signature TYPE sbdst_signature,
    ls_signature TYPE bapisignat,
    lt_uri       TYPE sbdst_uri,
    ls_uri       TYPE bapiuri,
    lt_sheet     TYPE soi_sheets_table,
    ls_sheet     TYPE soi_sheets,
    lv_retcode   TYPE soi_ret_string.

* 批量导出的内表
  FIELD-SYMBOLS:
    <fs_table> TYPE STANDARD TABLE.

* 初始化容器
  CALL METHOD c_oi_container_control_creator=>get_container_control
    IMPORTING
      control = lo_iref_control
      retcode = lv_retcode.

  IF lv_retcode <> c_oi_errors=>ret_ok.
    MESSAGE '容器初始化失败' TYPE 'S' DISPLAY LIKE 'E'.
    LEAVE LIST-PROCESSING.
  ENDIF.

  CALL METHOD lo_iref_control->init_control
    EXPORTING
      r3_application_name      = 'ZFI1029'
      inplace_enabled          = 'X'
      inplace_scroll_documents = 'X'
      parent                   = lo_oref_container
    IMPORTING
      retcode                  = lv_retcode.

  IF lv_retcode <> c_oi_errors=>ret_ok.
    MESSAGE '容器初始化失败' TYPE 'S' DISPLAY LIKE 'E'.
    LEAVE LIST-PROCESSING.
  ENDIF.

  PERFORM frm_progress_indicator USING 10 '正在打开excel...'.

* 下载模板
  CREATE OBJECT lo_iref_template.
  ls_signature-prop_name  = 'BDS_KEYWORD'.
  ls_signature-prop_value = 'ZFI1029'.
  APPEND ls_signature TO lt_signature.

  CLEAR:lt_uri[].
  CALL METHOD lo_iref_template->get_with_url
    EXPORTING
      classname       = 'SOFFICEINTEGRATION'
      classtype       = 'OT'
      object_key      = 'ZFI'
    CHANGING
      uris            = lt_uri
      signature       = lt_signature
    EXCEPTIONS
      nothing_found   = 1
      error_kpro      = 2
      internal_error  = 3
      parameter_error = 4
      not_authorized  = 5
      not_allowed     = 6.

  READ TABLE lt_uri INTO ls_uri INDEX 1.
  IF lv_retcode <> c_oi_errors=>ret_ok OR sy-subrc <> 0.
    MESSAGE '模板下载失败' TYPE 'S' DISPLAY LIKE 'E'.
    LEAVE LIST-PROCESSING.
  ENDIF.

* 打开文件
  CALL METHOD lo_iref_control->get_document_proxy
    EXPORTING
      document_type  = 'Excel.Sheet'
    IMPORTING
      document_proxy = lo_iref_document
      retcode        = lv_retcode.
  IF lv_retcode <> c_oi_errors=>ret_ok.
    MESSAGE '文件对象初始化失败' TYPE 'S' DISPLAY LIKE 'E'.
    LEAVE LIST-PROCESSING.
  ENDIF.

  CALL METHOD lo_iref_document->open_document
    EXPORTING
      document_url = ls_uri-uri
      open_inplace = 'X'
    IMPORTING
      retcode      = lv_retcode.
  IF lv_retcode <> c_oi_errors=>ret_ok.
    MESSAGE '文件打开失败' TYPE 'S' DISPLAY LIKE 'E'.
    LEAVE LIST-PROCESSING.
  ENDIF.

* 获取sheet信息
  FREE:lo_iref_error.
  CALL METHOD lo_iref_document->get_spreadsheet_interface
    IMPORTING
      error           = lo_iref_error
      sheet_interface = lo_iref_spreadsheet.

  CALL METHOD lo_iref_spreadsheet->get_sheets
    IMPORTING
      sheets = lt_sheet
      error  = lo_iref_error.

  READ TABLE lt_sheet INTO ls_sheet INDEX 1.

  IF lo_iref_error->error_code <> c_oi_errors=>ret_ok OR ls_sheet IS INITIAL.
    MESSAGE 'sheet信息取得失败' TYPE 'S' DISPLAY LIKE 'E'.
    LEAVE LIST-PROCESSING.
  ENDIF.

  CONCATENATE '正在填充excel:' ls_sheet-sheet_name INTO lv_text.

  ASSIGN go_table->* TO <fs_table>.
  CHECK <fs_table> IS ASSIGNED.

* 激活sheet
  CALL METHOD lo_iref_spreadsheet->select_sheet
    EXPORTING
      name  = ls_sheet-sheet_name
    IMPORTING
      error = lo_iref_error.

  CHECK lo_iref_error->error_code = c_oi_errors=>ret_ok.

  PERFORM frm_progress_indicator USING 30 lv_text.

* 获取输出内表列项目
  CLEAR:lt_fields[].
  CALL FUNCTION 'DP_GET_FIELDS_FROM_TABLE'
    TABLES
      data   = <fs_table>
      fields = lt_fields.

* 删除标识列:MARK
  DELETE lt_fields INDEX 1.

* 删除标识列:NAME
  DELETE lt_fields INDEX 1.

* 计算输出内容所需要和行数和列数
  lv_last_row = lines( <fs_table> ).
  lv_last_col = lines( lt_fields ).

* 在sheet中选取范围
  CALL METHOD lo_iref_spreadsheet->set_selection
    EXPORTING
      left    = 2
      top     = 2
      rows    = lv_last_row
      columns = lv_last_col.

  CALL METHOD lo_iref_spreadsheet->insert_range
    EXPORTING
      columns = lv_last_col
      rows    = lv_last_row
      name    = ls_sheet-sheet_name.

* 输入内表数据至所选取范围
  CALL METHOD lo_iref_spreadsheet->insert_one_table
    EXPORTING
      data_table   = <fs_table>
      fields_table = lt_fields
      rangename    = ls_sheet-sheet_name.
  PERFORM frm_progress_indicator USING 90 '正在保存excel...'.

* 存储excel至本地
  CALL METHOD lo_iref_document->save_as
    EXPORTING
      file_name = gv_zpath
    IMPORTING
      retcode   = lv_retcode.
  IF lv_retcode <> c_oi_errors=>ret_ok.
    MESSAGE '保存Excel文件出错' TYPE 'S' DISPLAY LIKE 'E'.
    LEAVE LIST-PROCESSING.
  ELSE.
    lv_message = gv_ok && '家公司全部导出成功'.
    MESSAGE lv_message TYPE 'S'.
  ENDIF.

* 释放所有对象
  CALL METHOD lo_iref_document->release_document
    IMPORTING
      retcode = lv_retcode.

  FREE: lo_iref_spreadsheet,
        lo_iref_document.

  CALL METHOD lo_iref_control->release_all_documents.
  CALL METHOD lo_iref_control->destroy_control.
ENDFORM.
FORM frm_export_excel_xlsm .

  DATA:
    lt_ranges           TYPE soi_range_list,
    lt_contents         TYPE soi_generic_table,

    lo_iref_template    TYPE REF TO cl_bds_document_set,
    lo_oref_container   TYPE REF TO cl_gui_custom_container,
    lo_iref_document    TYPE REF TO i_oi_document_proxy,
    lo_iref_error       TYPE REF TO i_oi_error,
    lo_iref_spreadsheet TYPE REF TO i_oi_spreadsheet,
    lo_iref_control     TYPE REF TO i_oi_container_control.

  DATA:
    lv_message    TYPE char200,
    lv_text       TYPE char50,
    lv_table      TYPE char20,
    lv_last_row   TYPE i,
    lv_last_col   TYPE i,
    l_col_from    TYPE i,  " 每一批次需要输出的起始列,以500列为一批次,例如: 1, 501,1001,...
    l_col_to      TYPE i,  " 每一批次需要输出的结束列,以500列为一批次,例如: 500,1000,1500, ...
    l_cols        TYPE i,  " 每一批次需要输出的列数
    l_left        TYPE i,    " 输出区域的起始位置
    l_range_name  TYPE char100,
    lt_fields     TYPE STANDARD TABLE OF rfc_fields,
    lt_fields_tmp LIKE lt_fields,
    lt_signature  TYPE sbdst_signature,
    ls_signature  TYPE bapisignat,
    lt_uri        TYPE sbdst_uri,
    ls_uri        TYPE bapiuri,
    lt_sheet      TYPE soi_sheets_table,
    ls_sheet      TYPE soi_sheets,
    lv_retcode    TYPE soi_ret_string.

* 批量导出的内表
  FIELD-SYMBOLS:
    <fs_table> TYPE STANDARD TABLE.

* 初始化容器
  CALL METHOD c_oi_container_control_creator=>get_container_control
    IMPORTING
      control = lo_iref_control
      retcode = lv_retcode.

  IF lv_retcode <> c_oi_errors=>ret_ok.
    MESSAGE '容器初始化失败' TYPE 'S' DISPLAY LIKE 'E'.
    LEAVE LIST-PROCESSING.
  ENDIF.

  CALL METHOD lo_iref_control->init_control
    EXPORTING
      r3_application_name      = 'ZFI1029'
      inplace_enabled          = 'X'
      inplace_scroll_documents = 'X'
      parent                   = lo_oref_container
    IMPORTING
      retcode                  = lv_retcode.

  IF lv_retcode <> c_oi_errors=>ret_ok.
    MESSAGE '容器初始化失败' TYPE 'S' DISPLAY LIKE 'E'.
    LEAVE LIST-PROCESSING.
  ENDIF.

  PERFORM frm_progress_indicator USING 10 '正在打开excel...'.

* 下载模板
  CREATE OBJECT lo_iref_template.
  ls_signature-prop_name  = 'BDS_KEYWORD'.
*  ls_signature-prop_value = 'ZFI1029'.
  ls_signature-prop_value = 'ZFI1029_XLSM'.
  APPEND ls_signature TO lt_signature.

  CLEAR:lt_uri[].
  CALL METHOD lo_iref_template->get_with_url
    EXPORTING
      classname       = 'SOFFICEINTEGRATION'
      classtype       = 'OT'
      object_key      = 'ZFI'
    CHANGING
      uris            = lt_uri
      signature       = lt_signature
    EXCEPTIONS
      nothing_found   = 1
      error_kpro      = 2
      internal_error  = 3
      parameter_error = 4
      not_authorized  = 5
      not_allowed     = 6.

  READ TABLE lt_uri INTO ls_uri INDEX 1.
  IF lv_retcode <> c_oi_errors=>ret_ok OR sy-subrc <> 0.
    MESSAGE '模板下载失败' TYPE 'S' DISPLAY LIKE 'E'.
    LEAVE LIST-PROCESSING.
  ENDIF.

* 打开文件
  CALL METHOD lo_iref_control->get_document_proxy
    EXPORTING
      document_type  = 'Excel.Sheet'
    IMPORTING
      document_proxy = lo_iref_document
      retcode        = lv_retcode.
  IF lv_retcode <> c_oi_errors=>ret_ok.
    MESSAGE '文件对象初始化失败' TYPE 'S' DISPLAY LIKE 'E'.
    LEAVE LIST-PROCESSING.
  ENDIF.

  CALL METHOD lo_iref_document->open_document
    EXPORTING
      document_url = ls_uri-uri
      open_inplace = 'X'
    IMPORTING
      retcode      = lv_retcode.
  IF lv_retcode <> c_oi_errors=>ret_ok.
    MESSAGE '文件打开失败' TYPE 'S' DISPLAY LIKE 'E'.
    LEAVE LIST-PROCESSING.
  ENDIF.

* 获取sheet信息
  FREE:lo_iref_error.
  CALL METHOD lo_iref_document->get_spreadsheet_interface
    IMPORTING
      error           = lo_iref_error
      sheet_interface = lo_iref_spreadsheet.

  CALL METHOD lo_iref_spreadsheet->get_sheets
    IMPORTING
      sheets = lt_sheet
      error  = lo_iref_error.

  READ TABLE lt_sheet INTO ls_sheet INDEX 1.

  IF lo_iref_error->error_code <> c_oi_errors=>ret_ok OR ls_sheet IS INITIAL.
    MESSAGE 'sheet信息取得失败' TYPE 'S' DISPLAY LIKE 'E'.
    LEAVE LIST-PROCESSING.
  ENDIF.

  CONCATENATE '正在填充excel:' ls_sheet-sheet_name INTO lv_text.

  ASSIGN go_table->* TO <fs_table>.
  CHECK <fs_table> IS ASSIGNED.

* 激活sheet
  CALL METHOD lo_iref_spreadsheet->select_sheet
    EXPORTING
      name  = ls_sheet-sheet_name
    IMPORTING
      error = lo_iref_error.

  CHECK lo_iref_error->error_code = c_oi_errors=>ret_ok.

  PERFORM frm_progress_indicator USING 30 lv_text.

  lo_iref_spreadsheet->insert_range_dim( EXPORTING name = 'INTERFACE' top = 1 left = 1 rows = 1 columns = 1 no_flush = 'X'
  IMPORTING error = lo_iref_error ) .

  lt_ranges = VALUE soi_range_list( ( name = 'INTERFACE' rows = 1 columns = 1 code = 0  ) ).

  APPEND INITIAL LINE TO lt_contents ASSIGNING FIELD-SYMBOL(<fs_content>).
  <fs_content>-row =  1 .<fs_content>-column = '1' . <fs_content>-value = g_fname.

  lo_iref_spreadsheet->set_ranges_data(
  EXPORTING
  ranges    = lt_ranges
  contents  = lt_contents
*    no_flush = 'X'
  IMPORTING error = lo_iref_error
  ).

  CALL METHOD lo_iref_document->execute_macro
    EXPORTING
      macro_string = 'Module1.ExportData'
*     no_flush     = space
    IMPORTING
      error        = lo_iref_error.

  PERFORM frm_progress_indicator USING 90 '正在保存excel...'.

* 存储excel至本地
  CALL METHOD lo_iref_document->save_as
    EXPORTING
      file_name = gv_zpath
    IMPORTING
      retcode   = lv_retcode.
  IF lv_retcode <> c_oi_errors=>ret_ok.
    MESSAGE '保存Excel文件出错' TYPE 'S' DISPLAY LIKE 'E'.
    LEAVE LIST-PROCESSING.
  ELSE.
    lv_message = gv_ok && '家公司全部导出成功'.
    MESSAGE lv_message TYPE 'S'.
  ENDIF.

* 释放所有对象
  CALL METHOD lo_iref_document->release_document
    IMPORTING
      retcode = lv_retcode.

  FREE: lo_iref_spreadsheet,
        lo_iref_document.

  CALL METHOD lo_iref_control->release_all_documents.
  CALL METHOD lo_iref_control->destroy_control.
  zcl_base_utility=>delete_file( g_fname ).

ENDFORM.

*&---------------------------------------------------------------------*
*&      Form  FRM_PROGRESS_INDICATOR
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM frm_progress_indicator  USING    iv_percentage TYPE i
                                      iv_text       TYPE c.

  CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
    EXPORTING
      percentage = iv_percentage
      text       = iv_text.

ENDFORM.
*&---------------------------------------------------------------------*
*&      Form  FRM_DOWNLOAD_META_DATA
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM frm_download_meta_data .
  FIELD-SYMBOLS:
    <fs_table> TYPE STANDARD TABLE.

  DATA :
    ls_output_meta TYPE ty_output_meta,
    lo_data_table  TYPE REF TO cl_abap_tabledescr,
    lo_struc       TYPE REF TO cl_abap_structdescr.

  ASSIGN go_table->* TO <fs_table>.

  lo_data_table ?= cl_abap_typedescr=>describe_by_data( <fs_table> ).
  lo_struc ?= lo_data_table->get_table_line_type( ).

  ls_output_meta-sheet_name  = '1'.
  LOOP AT <fs_table> ASSIGNING FIELD-SYMBOL(<fs_output>).
    ls_output_meta-row = sy-tabix + 1." 动态内表的第一行数据,对应输出到EXCEL第2行
    LOOP AT lo_struc->components ASSIGNING FIELD-SYMBOL(<fs_comp>) FROM 3.
      DATA(l_col) = sy-tabix - 1. " 动态内表的第一个字段,对应输出到EXCEL第2列
      ASSIGN COMPONENT <fs_comp>-name OF STRUCTURE <fs_output> TO FIELD-SYMBOL(<fs_d>).
      IF sy-subrc = 0.
        IF <fs_d> = '' OR <fs_d> = '0.0000'.
          CONTINUE.
        ENDIF.
        ls_output_meta-column = l_col .
        ls_output_meta-value = <fs_d>.
      ENDIF.
      APPEND ls_output_meta TO gt_output_meta.
    ENDLOOP.
  ENDLOOP.
  g_fname = zcl_base_utility=>download_file( CHANGING it_data = gt_output_meta ).

ENDFORM.