业务目的:对采购订单做批量修改,同时需要用报表显示导入结果,Excel上传凭证后,使用BAPI对采购订单进行修改
批量修改以下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数据
* 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_dialog
EXPORTING
default_extension = 'XLSX' " 文件类型(默认)
default_file_name = 'ekpo_template.xlsx' " 文件名(默认)
CHANGING
filename = lv_filename " 文件名
path = lv_path " 文件路径
fullpath = lv_fullpath " 文件全路径
EXCEPTIONS
cntl_error = 1
error_no_gui = 2
not_supported_by_gui = 3
invalid_default_file_name = 4
OTHERS = 5.
IF sy-subrc = 0.
SELECT SINGLE *
INTO CORRESPONDING FIELDS OF ls_key
FROM wwwdata
WHERE relid = 'MI'
AND objid = 'ZABAP_EKPO_437' " SAP WWW网关对象名(在事务码SMW0创建的对象名称)
AND srtf2 = '0'.
lv_destination = lv_fullpath. " 赋值
" 调用函数(从SAP服务器下载对应的文件)
CALL FUNCTION 'DOWNLOAD_WEB_OBJECT'
EXPORTING
key = ls_key
destination = lv_destination
IMPORTING
rc = lv_rc.
ELSE.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH 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_dialog
EXPORTING
initial_directory = lv_initial_path " 默认上传路径
CHANGING
file_table = lt_file_table " 上传文件的完整路径
rc = lv_rc
EXCEPTIONS
file_open_dialog_failed = 1
cntl_error = 2
error_no_gui = 3
not_supported_by_gui = 4
OTHERS = 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 ekpo
INTO TABLE @DATA(lt_ekpo)
FOR ALL ENTRIES IN @gt_output
WHERE ebeln = @gt_output-ebeln
AND 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'
EXPORTING
filename = lv_filename " 导入的文件名称路径
i_begin_col = 1 " 导入单元格的开始列
i_begin_row = 2 " 导入单元格的开始行
i_end_col = 255 " 导入单元格的结束列
i_end_row = 65536 " 导入单元格的结束行
TABLES
intern = lt_data " 内表,存储单元格中的内容
EXCEPTIONS
inconsistent_parameters = 1
upload_ole = 2
OTHERS = 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'
EXPORTING
i_callback_program = sy-cprog
i_callback_pf_status_set = 'FRM_SET_PF_STATUS' " 子例程(设置状态栏)
i_callback_user_command = 'FRM_USER_COMMAND' " 子例程(用户指令响应)
i_bypassing_buffer = abap_true
is_layout_lvc = gs_layout " 设置ALV布局
it_fieldcat_lvc = gt_fieldcat " 设置ALV列属性
i_save = 'A'
TABLES
t_outtab = gt_output " 内表数据
EXCEPTIONS
program_error = 1
OTHERS = 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-ucomm
ps_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'
IMPORTING
e_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_display
EXPORTING
is_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.
* 调用BAPI更新数据
CLEAR:lv_error_flg.
PERFORM frm_change_po_bapi 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_bapi
*&---------------------------------------------------------------------*
*& text
*&---------------------------------------------------------------------*
*& --> LT_IMPORT
*& <-- LV_ERROR_FLG
*&---------------------------------------------------------------------*
FORM frm_change_po_bapi USING pt_import TYPE tt_output
CHANGING cv_error_flg.
DATA: lt_poitem TYPE STANDARD TABLE OF bapimepoitem, " 采购凭证的项目编号
lt_poitemx TYPE STANDARD TABLE OF bapimepoitemx,
lt_return TYPE STANDARD TABLE OF bapiret2. " 返还参数
LOOP AT pt_import INTO DATA(ls_import).
APPEND INITIAL LINE TO lt_poitem ASSIGNING FIELD-SYMBOL(<lfs_poitem>).
<lfs_poitem>-po_item = ls_import-ebelp. " 采购凭证的项目编号
<lfs_poitem>-short_text = ls_import-txz01. " 短文本
<lfs_poitem>-quantity = ls_import-menge. " 采购订单数量
APPEND INITIAL LINE TO lt_poitemx ASSIGNING FIELD-SYMBOL(<lfs_poitemx>).
<lfs_poitemx>-po_item = ls_import-ebelp. " 采购凭证的项目编号
<lfs_poitemx>-short_text = abap_true. " 短文本
<lfs_poitemx>-quantity = abap_true. " 采购订单数量
ENDLOOP.
" 调用修改采购订单的BAPI
CALL FUNCTION 'BAPI_PO_CHANGE'
EXPORTING
purchaseorder = ls_import-ebeln " 采购订单编号
TABLES
return = lt_return
poitem = lt_poitem
poitemx = lt_poitemx.
LOOP AT lt_return INTO DATA(ls_return)
WHERE type CA 'AEX'.
cv_error_flg = abap_true. " 报错
ENDLOOP.
IF cv_error_flg = abap_true.
CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'. " 回滚
ELSE.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'. " 提交
ENDIF.
ENDFORM.
上述代码涉及的消息类以及文本元素如下所示


GUI STATUS状态栏
本文涉及到的相关知识点
[SAP ABAP] 在选择屏幕上的标准工具栏上增加自定义按钮
[SAP ABAP] ASSIGN COMPONENT的用法
[SAP ABAP] READ TABLE 关键字 TRANSPORTING NO FIELDS的用法
[SAP ABAP] 使用LOOP AT...ASSIGNING FIELD-SYMBOL 直接更新内表数据
[SAP ABAP] LOOP中的AT NEW 与 AT END OF