业务目的:对采购订单做批量修改,同时需要用报表显示导入结果,Excel上传凭证后,录制BDC,调用BDC对采购订单进行修改
批量修改以下3个采购订单的短文本以及采购订单数量
提示Tips:上述提供的3个采购订单涉及的供应商以及采购的物料均不是真实的数据,仅作为测试数据进行操作使用
效果展示
在初始界面上下载导入模板
点击按钮即可下载文件模版
在下载的模板文件填写上述3个采购订单需要进行批量修改的具体内容
保存该文件并在初始界面进行上传
点击执行按钮,程序会读取Excel文件里面的数据,以ALV报表的形式进行采购订单相关信息展示并对其内容进行数据合法性以及存在性检查
在ALV报表上全选所有采购订单行数据
点击导入按钮进行批量修改操作,ALV报表会及时呈现批导的结果
我们再通过事务码ME23N查看上述3个采购订单的短文本以及采购订单数量是否发生了更改
涉及的功能点:
① 批量修改采购订单模板下载
② 批量修改采购订单Excel文件上传
③ 批量修改采购订单Excel文件内容校验(消息灯与消息内容提示)
④ 批量修改采购订单"短文本"和"采购订单数量"字段内容
⑤ 更新修改采购订单执行的结果
完整代码如下所示
主程序(z_pobm_demo1_437)
*&---------------------------------------------------------------------*
*& Report Z_POBM_DEMO1_437
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT z_pobm_demo1_437.INCLUDE z_pobm_demo1_437_top. " 数据定义INCLUDE z_pobm_demo1_437_f01. " 子例程*----------------------------------------------------------------------*
* DESC: INITIALIZATION 事件
*----------------------------------------------------------------------*
INITIALIZATION.sscrfields-functxt_01 = icon_xlv && '模板下载'. " 设置第一个按钮的文本,生成模板下载按钮(含图标)*----------------------------------------------------------------------*
* DESC: AT SELECTION-SCREEN ON VALUE-REQUEST FOR [field] 事件
*----------------------------------------------------------------------*
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_path. " 为选择屏幕的自定义字段提供搜索帮助PERFORM frm_get_path.*----------------------------------------------------------------------*
* DESC: AT SELECTION-SCREEN 屏幕响应事件
*----------------------------------------------------------------------*
AT SELECTION-SCREEN.CASE sy-ucomm.WHEN 'FC01'.PERFORM frm_download. " 模板文件下载并保存到本地ENDCASE.*----------------------------------------------------------------------*
* DESC: START-OF-SELECTION 事件
*----------------------------------------------------------------------*
START-OF-SELECTION.PERFORM frm_upload_excel. " 解析Excel文件中的数据PERFORM frm_get_output. " 将Upload数据放到ALV内表*----------------------------------------------------------------------*
* DESC: END-OF-SELECTION 显示数据用的事件
*----------------------------------------------------------------------*
END-OF-SELECTION.PERFORM frm_display_alv. " ALV呈现数据
INCLUDE程序(z_pobm_demo1_437_top)
*&---------------------------------------------------------------------*
*& 包含 Z_POBM_DEMO1_437_TOP
*&---------------------------------------------------------------------*TABLES: sscrfields.* 选择屏幕
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001.PARAMETERS: p_path TYPE rlgrap-filename. " 上传文件路径
SELECTION-SCREEN END OF BLOCK b1.SELECTION-SCREEN FUNCTION KEY 1. " 激活第一个按钮* 自定义数据类型
TYPES: BEGIN OF ty_upload,ebeln TYPE ekpo-ebeln, " 采购订单编号ebelp TYPE ekpo-ebelp, " 采购订单的行项目编号txz01 TYPE ekpo-txz01, " 短文本menge TYPE string, " 采购订单数量END OF ty_upload,BEGIN OF ty_output,ebeln TYPE ekpo-ebeln, " 采购订单编号ebelp TYPE ekpo-ebelp, " 采购订单的行项目编号txz01 TYPE ekpo-txz01, " 短文本menge TYPE ekpo-menge, " 采购订单数量msg TYPE bapi_msg, " 消息status TYPE icon_d, " 消息灯sel(1) TYPE c, " 选择标志END OF ty_output.TYPES: tt_output TYPE STANDARD TABLE OF ty_output. " 表类型* 定义内表
DATA: gt_upload TYPE STANDARD TABLE OF ty_upload, " gt_upload内表用于存储解析上传的Excel文件数据gt_output TYPE STANDARD TABLE OF ty_output. " gt_output内表用于展示ALV数据* 定义变量用于BDC录屏操作
DATA: bdcdata TYPE STANDARD TABLE OF bdcdata WITH HEADER LINE.* ALV参数定义
DATA: gt_fieldcat TYPE lvc_t_fcat, " 字段目录内表gs_layout TYPE lvc_s_layo. " 用于定义ALV表单的相关格式、属性
INCLUDE程序(z_pobm_demo1_437_f01)
*&---------------------------------------------------------------------*
*& 包含 Z_POBM_DEMO1_437_F01
*&---------------------------------------------------------------------**&---------------------------------------------------------------------*
*& Form frm_download
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& --> p1 text
*& <-- p2 text
*&---------------------------------------------------------------------*
FORM frm_download." 下载批导模板DATA: lv_filename TYPE string, " 文件名lv_path TYPE string, " 文件路径lv_fullpath TYPE string. " 文件全路径DATA: ls_key TYPE wwwdatatab,lv_destination LIKE rlgrap-filename,lv_rc TYPE sy-subrc." ABAP对象模式(调用类的方法)---弹出保存文件对话框CALL METHOD cl_gui_frontend_services=>file_save_dialogEXPORTINGdefault_extension = 'XLSX' " 文件类型(默认)default_file_name = 'ekpo_template.xlsx' " 文件名(默认)CHANGINGfilename = lv_filename " 文件名path = lv_path " 文件路径fullpath = lv_fullpath " 文件全路径EXCEPTIONScntl_error = 1error_no_gui = 2not_supported_by_gui = 3invalid_default_file_name = 4OTHERS = 5.IF sy-subrc = 0.SELECT SINGLE *INTO CORRESPONDING FIELDS OF ls_keyFROM wwwdataWHERE relid = 'MI'AND objid = 'ZABAP_EKPO_437' " SAP WWW网关对象名(在事务码SMW0创建的对象名称)AND srtf2 = '0'.lv_destination = lv_fullpath. " 赋值" 调用函数(从SAP服务器下载对应的文件)CALL FUNCTION 'DOWNLOAD_WEB_OBJECT'EXPORTINGkey = ls_keydestination = lv_destinationIMPORTINGrc = lv_rc.ELSE.MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgnoWITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.ENDIF.ENDFORM.*&---------------------------------------------------------------------*
*& Form frm_get_path
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& --> p1 text
*& <-- p2 text
*&---------------------------------------------------------------------*
FORM frm_get_path .DATA: lt_file_table TYPE filetable,lv_rc TYPE i,lv_initial_path TYPE string VALUE 'C:\Users\HP\Desktop'." ABAP对象模式(调用类的方法)---提供文件选择对话框的方式获取文件的完整路径CALL METHOD cl_gui_frontend_services=>file_open_dialogEXPORTINGinitial_directory = lv_initial_path " 默认上传路径CHANGINGfile_table = lt_file_table " 上传文件的完整路径rc = lv_rcEXCEPTIONSfile_open_dialog_failed = 1cntl_error = 2error_no_gui = 3not_supported_by_gui = 4OTHERS = 5.IF sy-subrc = 0.READ TABLE lt_file_table INTO DATA(ls_filetab) INDEX 1.IF sy-subrc = 0.p_path = ls_filetab-filename. " 文件路径赋值ENDIF.ENDIF.
ENDFORM.*&---------------------------------------------------------------------*
*& Form frm_get_output
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& --> p1 text
*& <-- p2 text
*&---------------------------------------------------------------------*
FORM frm_get_output.DATA: lv_flg_err TYPE boole_d.gt_output = CORRESPONDING #( gt_upload ). " 赋值(同名传递)" 对gt_output内表中的数据进行校验IF gt_output IS NOT INITIAL.SELECT ebeln, " 采购凭证编号ebelp " 采购凭证的项目编号FROM ekpoINTO TABLE @DATA(lt_ekpo)FOR ALL ENTRIES IN @gt_outputWHERE ebeln = @gt_output-ebelnAND ebelp = @gt_output-ebelp.ENDIF.LOOP AT gt_output ASSIGNING FIELD-SYMBOL(<lfs_output>).CLEAR:lv_flg_err.* 1. 检查采购凭证IF <lfs_output>-ebeln IS INITIAL OR <lfs_output>-ebelp IS INITIAL.<lfs_output>-status = icon_led_red.MESSAGE e001(z_msg437) INTO <lfs_output>-msg. " 采购凭证号和行项目必填lv_flg_err = abap_true.ELSE." 检查lt_ekpo内表中是否存在 ebeln 和 ebelp 等于<lfs_output>中对应字段的数据行READ TABLE lt_ekpo TRANSPORTING NO FIELDS WITH KEY ebeln = <lfs_output>-ebeln ebelp = <lfs_output>-ebelp.IF sy-subrc <> 0.<lfs_output>-status = icon_led_red.MESSAGE e002(z_msg437) WITH <lfs_output>-ebeln <lfs_output>-ebelp INTO <lfs_output>-msg. " 采购凭证&1 &2不存在不存在lv_flg_err = abap_true.ENDIF.ENDIF.* 2.校验数量字段IF <lfs_output>-menge < 0.<lfs_output>-status = icon_led_red.MESSAGE e003(z_msg437) INTO DATA(lv_msg).<lfs_output>-msg = |{ <lfs_output>-msg }/{ lv_msg }|.lv_flg_err = abap_true.ENDIF.SHIFT <lfs_output>-msg LEFT DELETING LEADING '/'.ENDLOOP.
ENDFORM.*&---------------------------------------------------------------------*
*& Form frm_upload_excel
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& --> p1 text
*& <-- p2 text
*&---------------------------------------------------------------------*
FORM frm_upload_excel.DATA: lv_filename TYPE rlgrap-filename,lt_data TYPE STANDARD TABLE OF alsmex_tabline,ls_upload TYPE ty_upload.IF p_path IS NOT INITIAL. " 判断文件路径是否不为空lv_filename = p_path. " 赋值" 调用函数,读取Excel文件中的数据存放到内表中CALL FUNCTION 'ALSM_EXCEL_TO_INTERNAL_TABLE'EXPORTINGfilename = lv_filename " 导入的文件名称路径i_begin_col = 1 " 导入单元格的开始列i_begin_row = 2 " 导入单元格的开始行i_end_col = 255 " 导入单元格的结束列i_end_row = 65536 " 导入单元格的结束行TABLESintern = lt_data " 内表,存储单元格中的内容EXCEPTIONSinconsistent_parameters = 1upload_ole = 2OTHERS = 3." 将内表lt_data中的值转换到内表gt_upload中LOOP AT lt_data INTO DATA(ls_data)." <lfv_value>表示指向结构体ls_upload字段的指针(指向第几个字段取决于变量ls_data-col)" ls_data-col可理解为结构体ls_upload的索引值" 定义指针<lfv_value>通过索引动态的访问ls_upload的结构成员ASSIGN COMPONENT ls_data-col OF STRUCTURE ls_upload TO FIELD-SYMBOL(<lfv_value>).SHIFT ls_data-value LEFT DELETING LEADING space. " 去除空格<lfv_value> = ls_data-value. " 赋值AT END OF row.APPEND ls_upload TO gt_upload.CLEAR ls_upload.ENDAT.ENDLOOP.ELSE.MESSAGE e000(z_msg437). " 上传不存在的文件路径会报错ENDIF.
ENDFORM.*&---------------------------------------------------------------------*
*& Form frm_display_alv
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& --> p1 text
*& <-- p2 text
*&---------------------------------------------------------------------*
FORM frm_display_alv .PERFORM frm_build_layout. " 设置ALV样式PERFORM frm_build_fieldcat. " 设置字段属性" 调用函数显示ALV数据CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC'EXPORTINGi_callback_program = sy-cprogi_callback_pf_status_set = 'FRM_SET_PF_STATUS' " 子例程(设置状态栏)i_callback_user_command = 'FRM_USER_COMMAND' " 子例程(用户指令响应)i_bypassing_buffer = abap_trueis_layout_lvc = gs_layout " 设置ALV布局it_fieldcat_lvc = gt_fieldcat " 设置ALV列属性i_save = 'A'TABLESt_outtab = gt_output " 内表数据EXCEPTIONSprogram_error = 1OTHERS = 2.
ENDFORM.*&---------------------------------------------------------------------*
*& Form frm_build_layout
*&---------------------------------------------------------------------*
*& Set layout设置行的属性(ALV界面格式)
*&---------------------------------------------------------------------*
*& --> p1 text
*& <-- p2 text
*&---------------------------------------------------------------------*
FORM frm_build_layout.CLEAR gs_layout.gs_layout-box_fname = 'SEL'. " 选择标识gs_layout-zebra = abap_true. " 斑马条纹显示gs_layout-cwidth_opt = abap_true. " 优化列宽设置gs_layout-col_opt = abap_true.
ENDFORM.*&---------------------------------------------------------------------*
*& Form frm_build_fieldcat
*&---------------------------------------------------------------------*
*& 设置字段属性
*&---------------------------------------------------------------------*
*& --> p1 text
*& <-- p2 text
*&---------------------------------------------------------------------*
FORM frm_build_fieldcat." 订单表头gt_fieldcat = VALUE #(( fieldname = 'STATUS' coltext = '消息灯' no_zero = ' ' edit = ' ' just = 'C' key = abap_true ) " 消息灯( fieldname = 'MSG' coltext = '消息' no_zero = ' ' edit = ' ' key = abap_true ) " 消息( fieldname = 'EBELN' coltext = '采购订单' no_zero = ' ' edit = ' ' key = abap_true ) " 采购订单( fieldname = 'EBELP' coltext = '采购订单行项目' no_zero = ' ' edit = ' ' key = abap_true ) " 采购订单行项目( fieldname = 'TXZ01' coltext = '短文本' no_zero = ' ' edit = ' ' key = abap_true ) " 短文本( fieldname = 'MENGE' coltext = '采购订单数量' no_zero = ' ' edit = ' ' key = abap_true ) " 采购订单数量).
ENDFORM.* 设置状态栏
FORM frm_set_pf_status USING extab TYPE slis_t_extab.SET PF-STATUS 'ZSTANDARD_FULLSCREEN'.
ENDFORM.* 设置用户指令响应
FORM frm_user_command USING pv_ucomm LIKE sy-ucommps_selfield TYPE slis_selfield." 编辑完成保存后强制刷新ALV页面DATA: lr_grid TYPE REF TO cl_gui_alv_grid.DATA: lv_stable TYPE lvc_s_stbl.lv_stable-row = abap_true.lv_stable-col = abap_true.CALL FUNCTION 'GET_GLOBALS_FROM_SLVC_FULLSCR'IMPORTINGe_grid = lr_grid.CALL METHOD lr_grid->check_changed_data." 点击自定义按钮触发批导数据功能CASE pv_ucomm.WHEN 'ZUPLOAD'.PERFORM frm_import_data.ENDCASE.CALL METHOD lr_grid->refresh_table_displayEXPORTINGis_stable = lv_stable.ENDFORM.*&---------------------------------------------------------------------*
*& Form frm_import_data
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& --> p1 text
*& <-- p2 text
*&---------------------------------------------------------------------*
FORM frm_import_data.DATA: lt_output TYPE STANDARD TABLE OF ty_output, " 内表(存储可以进行批导处理的数据)lt_import TYPE STANDARD TABLE OF ty_output. " 内表(存储可以进行BAPI调用处理的数据)DATA: lv_error_flg TYPE boole_d. " 错误标识* ALV获取可以进行批导的数据(消息灯不报红且是用户选择的数据)LOOP AT gt_output INTO DATA(ls_output)WHERE status IS INITIAL AND sel = abap_true.APPEND ls_output TO lt_output.ENDLOOP." 调用BAPI之前需要对数据进行排序SORT lt_output BY ebeln ebelp. " 排序LOOP AT lt_output ASSIGNING FIELD-SYMBOL(<lfs_output>).AT NEW ebeln.REFRESH: lt_import. " 清空ENDAT.APPEND <lfs_output> TO lt_import. " 添加数据AT END OF ebeln.
* 调用BDC录屏更新数据CLEAR:lv_error_flg.PERFORM frm_change_po_bdc USING lt_import CHANGING lv_error_flg.* 更新当前采购订单执行的结果IF lv_error_flg = abap_true.LOOP AT gt_output ASSIGNING FIELD-SYMBOL(<lfs_update>)WHERE ebeln = <lfs_output>-ebeln.<lfs_update>-msg = '更新失败'.<lfs_update>-status = icon_led_red. " 消息灯报红ENDLOOP.ELSE.LOOP AT gt_output ASSIGNING <lfs_update>WHERE ebeln = <lfs_output>-ebeln.<lfs_update>-msg = '更新成功'.<lfs_update>-status = icon_led_green. " 消息灯报绿ENDLOOP.ENDIF.ENDAT.ENDLOOP.
ENDFORM.*&---------------------------------------------------------------------*
*& Form frm_change_po_bdc
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& --> LT_IMPORT
*& <-- LV_ERROR_FLG
*&---------------------------------------------------------------------*
FORM frm_change_po_bdc USING pt_import TYPE tt_outputCHANGING cv_error_flg TYPE boole_d.DATA: ls_import TYPE ty_output,bdcmsg TYPE STANDARD TABLE OF bdcmsgcoll. " 收集SAP系统中的信息DATA: lv_var1 TYPE char5,lv_var2 TYPE char2,lv_field TYPE bdcdata-fnam, " 字段名lv_menge TYPE c LENGTH 16. " 采购订单数量READ TABLE pt_import INTO ls_import INDEX 1.CLEAR bdcdata[]. " 清空bdcdata内表PERFORM bdc_dynpro USING 'SAPMM06E' '0105'.PERFORM bdc_field USING 'BDC_OKCODE' '/00'.PERFORM bdc_field USING 'RM06E-BSTNR' ls_import-ebeln. " ebeln:采购订单编号PERFORM bdc_dynpro USING 'SAPMM06E' '0121'.PERFORM bdc_field USING 'BDC_OKCODE' '=BU'.LOOP AT pt_import INTO ls_import.lv_var1 = ls_import-ebelp. " ebelp:lv_var2 = lv_var1+2(2).lv_field = 'EKPO-TXZ01(' && lv_var2 && ')'.PERFORM bdc_field USING lv_field ls_import-txz01. " txz01:短文本CLEAR:lv_field.lv_field = 'EKPO-MENGE(' && lv_var2 && ')'.CLEAR:lv_menge.WRITE ls_import-menge TO lv_menge.CONDENSE lv_menge.PERFORM bdc_field USING lv_field lv_menge. " menge:采购订单数量ENDLOOP." 调用并执行事务码CALL TRANSACTION 'ME22' USING bdcdataMODE 'N' " A:Debug模式显示所有屏幕 E:错误发生显示屏幕 N:不显示屏幕UPDATE 'A' " A:同步更新 S:异步更新MESSAGES INTO bdcmsg. " 消息IF sy-subrc = 0.LOOP AT bdcmsg INTO DATA(ls_msg)WHERE msgtyp CA 'AEX'.cv_error_flg = abap_true. " 报错EXIT.ENDLOOP.IF cv_error_flg = abap_true.CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'. " 回滚,撤销以上修改ELSE.CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'. " 提交,保存以上修改ENDIF.ELSE.CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'. " 回滚,撤销以上修改ENDIF.ENDFORM.*&---------------------------------------------------------------------*
*& Form bdc_dynpro
*&---------------------------------------------------------------------*
*& 处理屏幕
*&---------------------------------------------------------------------*
*& --> P_
*& --> P_
*&---------------------------------------------------------------------*
FORM bdc_dynpro USING program TYPE bdcdata-programdynpro TYPE bdcdata-dynpro.CLEAR:bdcdata.bdcdata-program = program. " 程序名bdcdata-dynpro = dynpro. " 屏幕编号bdcdata-dynbegin = 'X'. " 开始IDAPPEND bdcdata.ENDFORM.*&---------------------------------------------------------------------*
*& Form bdc_field
*&---------------------------------------------------------------------*
*& 处理字段
*&---------------------------------------------------------------------*
*& --> P_
*& --> P_
*& --> ENDFORM
*&---------------------------------------------------------------------*
FORM bdc_field USING fnam TYPE bdcdata-fnamfval.CLEAR:bdcdata.bdcdata-fnam = fnam. " 字段名称bdcdata-fval = fval. " 字段值APPEND bdcdata.ENDFORM.
上述代码涉及的消息类以及文本元素如下所示
GUI STATUS状态栏
本文涉及到的相关知识点
[SAP ABAP] INCLUDE程序创建
[SAP ABAP] SELECTION-SCREEN
[SAP ABAP] PARAMETERS
[SAP ABAP] 选择屏幕事件
[SAP ABAP] 在选择屏幕上的标准工具栏上增加自定义按钮
[SAP ABAP] SMW0上传模板
[SAP ABAP] 自定义字段提供F4帮助
[SAP ABAP] ASSIGN COMPONENT的用法
[SAP ABAP] CORRESPONDING
[SAP ABAP] VALUE关键字
[OPEN SQL] FOR ALL ENTRIES IN
[SAP ABAP] READ TABLE 关键字 TRANSPORTING NO FIELDS的用法
[SAP ABAP] 使用LOOP AT...ASSIGNING FIELD-SYMBOL 直接更新内表数据
[SAP ABAP] LOOP中的AT NEW 与 AT END OF
[SAP ABAP] ALV状态栏GUI STATUS的快速创建
[SAP ABAP] ALV中的USER_COMMAN用户事件
[SAP ABAP] BDC录屏