主要实现了以下步骤:
1、让用户下载模板。
2、根据模板填写数据选择文件进行上传。
3、根据用户数据进行存在性判断,存在则可以改,不存在不可以修改。
4、通过BAPI或者BDC实现程序自动批量修改。
5、将修改结果显示给用户,失败给出失败信息,消息灯变红,成功显示成功,消息灯为绿。
具体的实现代码的解释请看注释,需要进行修改的地方也在注释的地方进行了标注。
博客中的代码是用BAPI对PO的短文本进行批量修改,在进行批量操作时能用BAPI就尽量用BAPI。可以先查一下有没有系统给的,没有的话再用BDC或者LSMW。
一、主程序
主要是进行初始化和子例程调用。
*&---------------------------------------------------------------------*
*& Report ZVIA_ME22_PRO
*&---------------------------------------------------------------------*
REPORT zvia_me22_pro MESSAGE-ID zvia_msg.
* 变量定义 *
INCLUDE zvia_me22_pro_d01.
* 屏幕定义 *
INCLUDE zvia_me22_pro_s01.
* 功能定义 *
INCLUDE zvia_me22_pro_f01.
* ALV相关功能定义 *
INCLUDE zvia_me22_pro_f02.
* 初始化 *
INITIALIZATION.
sscrfields-functxt_01 = icon_xlv && TEXT-000."下载模板按钮
* 响应事件 *
AT SELECTION-SCREEN.
CASE sy-ucomm."存储用户在 SAP GUI 界面上触发的事件或操作命令
WHEN 'FC01'."不知道是什么可以调试点按钮看SY-UCOMM的值是什么
PERFORM frm_download."下载导入模板
ENDCASE.
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_path.
* 给文件路径添加搜索帮助,让用户可以选择自己的电脑文件而不是需要复制地址 *
* 选择上传文件路径 *
PERFORM frm_get_path.
START-OF-SELECTION.
IF p_path IS INITIAL.
MESSAGE s008 DISPLAY LIKE 'E'.
LEAVE LIST-PROCESSING."终止start-of-selection的执行
ELSE.
PERFORM frm_upload_excel.
IF gt_upload IS NOT INITIAL.
* 将excel数据转换到ALV输出内表 *
PERFORM frm_get_output."upload->output
ELSE.
MESSAGE s009 DISPLAY LIKE 'E'."上传文件内容为空
LEAVE LIST-PROCESSING."终止START-OF-SELECTION
ENDIF.
ENDIF.
END-OF-SELECTION."显示数据用的事件
PERFORM frm_display_alv.
二、变量的定义
主要定义了模板的名字(自己上传的模板的名字),上传数据的格式,以及在ALV显示的时候行项目的格式以及fieldcat。
*&---------------------------------------------------------------------*
*& 包含 ZVIA_ME22_PRO_D01
*&---------------------------------------------------------------------*
TYPE-POOLS : icon.
TABLES : sscrfields.
* 需要修改,这里的VALUE是上传到系统的模板的名字 *
CONSTANTS : "常量
gc_objid TYPE wwwdatatab-objid VALUE 'ZVIA_PO_DBC'.
TYPES :
* 需要进行修改,对应自己的字段 *
BEGIN OF ty_upload, "接收excel上传数据的格式
ebeln TYPE ekpo-ebeln,
ebelp TYPE ekpo-ebelp,
txz01 TYPE ekpo-txz01,
menge TYPE ekpo-menge,
END OF ty_upload,
* 需要进行修改,对应自己的字段,其中消息灯、消息文本、选择框不要动 *
BEGIN OF ty_output,
status TYPE icon_d, "消息灯
ebeln TYPE ekpo-ebeln, "分组时用到的key,需要在前几个
ebelp TYPE ekpo-ebelp,
txz01 TYPE ekpo-txz01,
menge TYPE ekpo-menge,
msg TYPE bapi_msg, "消息文本
sel(1) TYPE c, "选择?
END OF ty_output.
"ty_t_output TYPE STANDARD TABLE OF ty_output.
DATA :
gt_upload TYPE TABLE OF ty_upload, "excel上传数据存放的内表
"gt_output TYPE ty_t_output."ALV输出数据
gt_output TYPE TABLE OF ty_output. "ALV输出数据
DATA :"ALV FIELDCATLOG
gt_fieldcat TYPE lvc_t_fcat,
gs_layout TYPE lvc_s_layo,
gv_import TYPE boole_d.
三、屏幕定义
导入模板的屏幕较为简单。
*&---------------------------------------------------------------------*
*& 包含 ZVIA_ME22_PRO_S01
*&---------------------------------------------------------------------*
SELECTION-SCREEN BEGIN OF BLOCK BLK WITH FRAME TITLE text-001.
PARAMETERS :
p_path TYPE rlgrap-filename MODIF ID fl."上传文件路径
SELECTION-SCREEN END OF BLOCK BLK.
SELECTION-SCREEN FUNCTION KEY 1."添加一个特定的功能键 KEY 1 按下KEY1后会触发特定操作
四、功能定义
定义了一些功能,主要是对下载模板,上传文件以及行项目分析和修改逻辑的实现。
*&---------------------------------------------------------------------*
*& 包含 ZVIA_ME22_PRO_F01
*&---------------------------------------------------------------------*
*&---------------------------------------------------------------------*
*& Form frm_download
*&---------------------------------------------------------------------*
*& 用户下载模板
*&---------------------------------------------------------------------*
FORM frm_download.
DATA : lv_fullpath TYPE string,
lv_filename TYPE string,
lv_path TYPE string,
ls_objnam TYPE string ##NEEDED,
lo_objdata TYPE wwwdatatab,
ls_destination LIKE rlgrap-filename,
lv_rc TYPE i.
* 下载模板 需要对模板所用到的格式进行修改,代码中的.xls部分,与自己的模板保持一致 *
CONCATENATE gc_objid '.xls' INTO ls_objnam."gc_objid在D01里面进行定义 concatenate用于拼接字符串
CONDENSE ls_objnam NO-GAPS.
lv_filename = ls_objnam.
CALL METHOD cl_gui_frontend_services=>file_save_dialog "弹出选择文件保存地址的对话框
EXPORTING
default_extension = 'XLS'
default_file_name = lv_filename
CHANGING
filename = lv_filename "文件名
path = lv_path "文件地址
fullpath = lv_fullpath.
IF lv_fullpath IS INITIAL.
RETURN.
ENDIF.
IF sy-subrc = 0.
SELECT SINGLE relid objid
INTO CORRESPONDING FIELDS OF lo_objdata
FROM wwwdata
WHERE srtf2 = 0
AND relid = 'FL'
AND objid = gc_objid.
IF sy-subrc <> 0 OR lo_objdata-objid IS INITIAL.
RETURN.
ENDIF.
ls_destination = lv_fullpath.
* template download *
CALL FUNCTION 'DOWNLOAD_WEB_OBJECT' "调用FUNCTION进行下载
EXPORTING
key = lo_objdata
destination = ls_destination
IMPORTING
rc = lv_rc.
IF lv_rc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
RETURN.
ENDIF.
ENDIF.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_get_path
*&---------------------------------------------------------------------*
*& 让用户选择文件路径
*&---------------------------------------------------------------------*
FORM frm_get_path .
DATA : lt_filetable TYPE filetable,
ls_filetable TYPE file_table,
lv_rc TYPE sy-subrc,
lv_filename TYPE string,
lv_initial_path TYPE string VALUE 'C:\'. "默认打开c盘
CLEAR lv_filename.
lv_filename = p_path."文件路径
CALL METHOD cl_gui_frontend_services=>file_open_dialog
EXPORTING
default_filename = lv_filename
initial_directory = lv_initial_path
CHANGING
file_table = lt_filetable
rc = lv_rc
EXCEPTIONS
file_open_dialog_failed = 1
cntl_error = 2
error_no_gui = 3
not_supported_by_gui = 4.
IF sy-subrc NE 0 OR lv_rc LT 0.
ELSE.
IF lv_rc = 1.
READ TABLE lt_filetable INTO ls_filetable INDEX 1.
p_path = ls_filetable-filename.
ELSE.
EXIT.
ENDIF.
ENDIF.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_upload_excel
*&---------------------------------------------------------------------*
*& 读取用户上传的excel 需要修改,主要是读取的列按照自己的习惯,看是读取文本行再删除还是直接不读
*&---------------------------------------------------------------------*
FORM frm_upload_excel .
DATA : lt_excel_data TYPE STANDARD TABLE OF alsmex_tabline." row col value 三个组件
DATA : ls_upload TYPE ty_upload."自己定义的列
DATA : lv_index TYPE i.
FIELD-SYMBOLS : <lfs_value> TYPE any.
IF p_path IS NOT INITIAL.
CALL FUNCTION 'ALSM_EXCEL_TO_INTERNAL_TABLE'
EXPORTING
filename = p_path "本地文件
"从第1行第1列
i_begin_col = 1
i_begin_row = 1
"读到第65536行第255列
i_end_col = 255
i_end_row = 65536
TABLES
intern = lt_excel_data. "将excel的数据读到lt_excel_data中
IF sy-subrc = 0.
DELETE lt_excel_data WHERE row = 1."删除字段名行 即删除第一行 也可在上面的时候直接不读非数据行
LOOP AT lt_excel_data INTO DATA(ls_excel_data).
lv_index = ls_excel_data-col.
ASSIGN COMPONENT lv_index OF STRUCTURE ls_upload TO <lfs_value>."给ls_upload的第lv_index个字段的数值和<lfs_value>关联
SHIFT ls_excel_data-value LEFT DELETING LEADING space."去除空格
<lfs_value> = ls_excel_data-value."给<lfs_value>赋值就会修改响应的结构体类似于 filed-symbol
AT END OF row."一行读完之后插入内表
APPEND ls_upload TO gt_upload.
CLEAR : ls_upload.
ENDAT.
ENDLOOP.
ELSE.
PERFORM frm_disp_sys_msg.
RETURN.
ENDIF.
ENDIF.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_get_output
*&---------------------------------------------------------------------*
*& 1、对用户的数据进行检查 2、转换成ALV显示的格式 需要修改,判断数据存在的逻辑按照自己的表进行
*&---------------------------------------------------------------------*
FORM frm_get_output .
DATA : ls_output TYPE ty_output.
LOOP AT gt_upload INTO DATA(ls_upload).
MOVE-CORRESPONDING ls_upload TO ls_output.
* 1、检查数据 看采购凭证是否是存在的 存在的才可以修改 *
SELECT SINGLE ebeln,ebelp
FROM ekpo"采购凭证表
WHERE ebeln = @ls_upload-ebeln
AND ebelp = @ls_upload-ebelp
INTO @DATA(lw_ekpo).
IF sy-subrc <> 0.
ls_output-status = icon_led_red."行项目标注红灯
MESSAGE s010
WITH ls_output-ebeln ls_output-ebelp
INTO ls_output-msg."消息显示在行项目MSG列上
ELSE."存在采购凭证记录 可以进行修改
ls_output-status = icon_led_green."行项目标注绿灯
ENDIF.
APPEND ls_output TO gt_output.
ENDLOOP.
ENDFORM.
FORM frm_disp_sys_msg.
MESSAGE ID sy-msgid TYPE 'S' NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 DISPLAY LIKE 'E'.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_import_data
*&---------------------------------------------------------------------*
*& 导入数据 修改PO的数据
*& 根据gt_output即显示在ALV里面的数据进行修改 消息灯是红的不改 绿的改
*& 需要修改,这里调用BAPI的子例程填自己的
*&---------------------------------------------------------------------*
FORM frm_import_data .
* 存在消息灯是红灯的数据 不允许进行修改,只有全部绿灯才可以进行修改 *
LOOP AT gt_output INTO DATA(ls_output) WHERE status = icon_led_red.
MESSAGE e011.
ENDLOOP.
* 如果没有错误,则进行修改 *
LOOP AT gt_output ASSIGNING FIELD-SYMBOL(<fs_output>).
PERFORM frm_change_po_by_bapi CHANGING <fs_output>.
ENDLOOP.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_change_po_by_bapi
*&---------------------------------------------------------------------*
*& 根据传入的数据用BAPI对PO进行批量修改
*& 需要修改 调用BAPI、BDC写自己的逻辑
*&---------------------------------------------------------------------*
FORM frm_change_po_by_bapi CHANGING is_output TYPE ty_output.
DATA : lt_return TYPE STANDARD TABLE OF bapiret2,
ls_item TYPE bapimepoitem,
lt_item TYPE STANDARD TABLE OF bapimepoitem, "TYPE 从函数BAPI_PO_CHANGE里面得来
ls_itemx TYPE bapimepoitemx,
lt_itemx TYPE STANDARD TABLE OF bapimepoitemx.
* 和SE37测试函数时需要给的数据保持一致 *
ls_item-po_item = is_output-ebelp.
ls_item-short_text = is_output-txz01.
APPEND ls_item TO lt_item."因为参数类型是表类型,所以这里必须用表来传数据
ls_itemx-po_item = is_output-ebelp.
ls_itemx-short_text = 'X'.
APPEND ls_itemx TO lt_itemx.
CALL FUNCTION 'BAPI_PO_CHANGE'
EXPORTING
purchaseorder = is_output-ebeln
TABLES
return = lt_return
poitem = lt_item
poitemx = lt_itemx.
* 检查BAPI的调用是否正确 le_return中不能有TYPE是A/X/E的 *
LOOP AT lt_return INTO DATA(ls_return).
IF ls_return-type = 'A' OR ls_return-type = 'E' OR ls_return-type = 'X'.
"本条数据没有更新成功
is_output-status = icon_led_red.
is_output-msg = ls_return-message.
ENDIF.
IF is_output-status = icon_led_red.
CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'."出现错误回滚
ELSE.
is_output-status = icon_led_green.
is_output-msg = '更新成功'.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'."没有错误提交
ENDIF.
ENDLOOP.
ENDFORM.
五、ALV相关
几乎不用改,主要是对显示的列进行修改,改成自己的和屏幕进行创建并且需要添加一个更新PO按钮。
*&---------------------------------------------------------------------*
*& 包含 ZVIA_ME22_PRO_F02
*&---------------------------------------------------------------------*
*&---------------------------------------------------------------------*
*& Form frm_display_alv
*&---------------------------------------------------------------------*
*& ALV显示读取到的excel数据
*&---------------------------------------------------------------------*
*&---------------------------------------------------------------------*
FORM frm_display_alv .
DATA : lv_cnt_str TYPE string.
CLEAR lv_cnt_str.
lv_cnt_str = lines( gt_upload ).
* 上传条目记录 *
* MESSAGE s304 with lv_cnt_str *
PERFORM frm_build_layout.
PERFORM frm_build_fieldcat.
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC'
EXPORTING
i_bypassing_buffer = abap_true
i_callback_program = sy-cprog
i_callback_pf_status_set = 'FRM_SET_PF_STATUS'
i_callback_user_command = 'FRM_USER_COMMAND'
is_layout_lvc = gs_layout
it_fieldcat_lvc = gt_fieldcat
i_save = 'A'
TABLES
t_outtab = gt_output
EXCEPTIONS
program_error = 1
OTHERS = 2.
IF sy-subrc <> 0.
* Implement suitable error handling here
ENDIF.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_build_layout
*&---------------------------------------------------------------------*
FORM frm_build_layout .
CLEAR gs_layout.
gs_layout-zebra = abap_true.
gs_layout-col_opt = abap_true.
gs_layout-cwidth_opt = abap_true.
gs_layout-box_fname = 'SEL'.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_build_fieldcat
*&---------------------------------------------------------------------*
*& 需要修改,改成自己的列 消息灯、消息不用改
*&---------------------------------------------------------------------*
FORM frm_build_fieldcat .
DATA : lv_struct LIKE dd02l-tabname,
ls_fieldcat TYPE lvc_s_fcat.
REFRESH gt_fieldcat.
* 宏
gt_fieldcat = VALUE #(
( fieldname = 'STATUS' coltext = TEXT-002 no_zero = ' ' edit = ' ' just = 'C' key = abap_true )"消息灯
( fieldname = 'MSG' coltext = TEXT-003 no_zero = ' ' edit = ' ' just = 'C' key = abap_true )"消息
( fieldname = 'EBELN' coltext = TEXT-004 no_zero = ' ' edit = ' ' just = 'C' key = abap_true )"采购订单
( fieldname = 'EBELP' coltext = TEXT-005 no_zero = ' ' edit = ' ' just = 'C' key = abap_true )"采购订单行项目
( fieldname = 'TXZ01' coltext = TEXT-006 no_zero = ' ' edit = ' ' just = 'C' key = abap_true )"短文本
( fieldname = 'MENGE' coltext = TEXT-007 no_zero = ' ' edit = ' ' just = 'C' key = abap_true )"采购订单数量
).
ENDFORM.
* 双击进行屏幕创建,需要添加一个更新PO按钮 *
##CALLED
FORM frm_set_pf_status USING extab TYPE slis_t_extab.
SET PF-STATUS 'ZSTANDARD_FULLSCREEN'.
ENDFORM.
* 需要修改,检查自己的UPDATE按钮名是否和屏幕创建的按钮是一致的 *
FORM frm_user_command USING r_ucomm LIKE sy-ucomm rs_selfield TYPE slis_selfield ##called ##NEEDED.
DATA : lr_grid TYPE REF TO cl_gui_alv_grid.
DATA : ls_output LIKE LINE OF gt_output.
DATA : lv_flg_sel TYPE boole_d,
lv_flg_red TYPE boole_d.
CALL FUNCTION 'GET_GLOBALS_FROM_SLVC_FULLSCR'
IMPORTING
e_grid = lr_grid.
CALL METHOD lr_grid->check_changed_data.
rs_selfield-refresh = abap_true.
rs_selfield-col_stable = abap_true.
rs_selfield-row_stable = abap_true.
CASE r_ucomm.
WHEN '&UPDATE'."和PF里面的UPDATE按钮名称保持一致
PERFORM frm_import_data.
PERFORM frm_refresh_alv.
ENDCASE.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form frm_refresh_data
*&---------------------------------------------------------------------*
*& 每次有改变刷新ALV表单
*&---------------------------------------------------------------------*
FORM frm_refresh_alv .
DATA : lr_grid TYPE REF TO cl_gui_alv_grid,
ls_stable TYPE lvc_s_stbl,
ls_layout TYPE lvc_s_layo.
CALL FUNCTION 'GET_GLOBALS_FROM_SLVC_FULLSCR'
IMPORTING
e_grid = lr_grid.
lr_grid->get_frontend_layout(
IMPORTING
es_layout = ls_layout ).
ls_layout-cwidth_opt = 'X'.
lr_grid->set_frontend_layout( is_layout = ls_layout ).
ls_stable-row = abap_true.
ls_stable-col = abap_true.
CALL METHOD lr_grid->refresh_table_display
EXPORTING
is_stable = ls_stable
i_soft_refresh = abap_true
EXCEPTIONS
finished = 1
OTHERS = 2.
IF sy-subrc ne 0.
PERFORM frm_disp_sys_msg.
ENDIF.
CALL METHOD cl_gui_cfw=>flush
EXCEPTIONS
cntl_system_error = 1
cntl_error = 2.
IF sy-subrc ne 0.
PERFORM frm_disp_sys_msg.
ENDIF.
ENDFORM.
双击 ZSTANDARD_FULLSCREEN后创建屏幕、添加按钮过程。
这里取什么名字,在 frm_user_command子例程里面的按钮就要叫什么,不然点击触发不了后续子例程。