一、业务背景
有个朋友做ECC 6.0的项目,期初上线时,有一个需求是批量导入货源清单,我问了好几个朋友,加上自己以前的积累,硬是没有找到一个完全能用的程序,下面我来说一下我遇到的问题;
对货源清单不熟悉的小伙伴,可以先看一下我之前写过的一篇文章;
SAP_MM模块-货源清单_sap 货源清单-CSDN博客文章浏览阅读2.3k次,点赞27次,收藏33次。本文详细介绍了SAP系统中的货源清单定义、维护方式,包括手工创建、通过框架协议和信息记录创建,以及自动产生货源的过程。还重点讲解了货源清单的启用方式、优先级确定和在采购申请中的应用,以及与配额协议、框架协议的关系。https://blog.csdn.net/qq_40141758/article/details/136895836
二、遇到的问题;
(一)没有直接能用的程序;
无论是自己手头上的,还是问朋友拿到的,很多都只有S4版本的程序,要知道,S4版本的代码,很多都是用了新语法的,根本就无法直接在ECC版本上使用,要改起来也感觉比较麻烦;
(二)LSMW录屏无法满足效果;
自己用LSMW录屏,存在以下几个问题;
(1)标准的录屏,发现没法区分表头和行项目,导致披导时,同一个物料 + 工厂,只能录入一条数据,这里比较简单,只截一个主要的图,如果对LSMW不熟悉的,可以网上随便搜一下资料看看,这里由于LSMW录入无法达到预期效果,就不重点讲了,找不到资料想了解LSMW的,可以私聊交流。
如上图所示,不同物料 +工厂(不同的表头数据),可以正常录入一条数据,但是不能录入多条,同一个物料 +工厂,维护多条供应商数据时,会被覆盖。
(2)自己手动区分表头和行项目(但是实际发现不可行,不知道如何匹配上,有知道的小朋友欢迎评论或者私信指导一下,不甚感激),下面记录一下我当时的想法:
A、录屏时,如果录入多行数据,由于当时想的是,录入的数据有可能是3行、4行等不固定的行数,所以下面02的部分数据就不要了,只保留01行的。
B、维护源结构:通过手动新增表头和行项目(类别用标准的BOM导入结构,H代表表头,I代表行项目,导入EXCEL时原本想着可以用同样的方法导进去系统)
C、维护源字段,把表头需要的字段和行项目需要的字段分别维护好
D、然而,在维护结构关系时,发现没有办法把源结构和源字段匹配上!!!
如下图所示,只能分配表头,无法分配行项目~~
我们来看一下,LSMW批量导入BOM时,这个地方是如下图所示的,但是这个用的是系统标准的Object-0030 BOM。
目前没有进一步研究,不清楚标准功能,是否可以录屏实现像BOM这种多层结构的数据录入,也就导致这个LSMW录屏的方式,目前无法正常达到我们的目的,有知道的小伙伴可以分享一下,感谢感谢~~
(三)拿到的数据无法直接用:
最终从一个朋友手中,拿到一个ECC版本的披导程序,但是测试发现,也是只能录入一条数据!!
1、源程序;
看一下朋友发过来的程序中,以下BDC录屏部分的代码就知道,只能维护一条数据~~
2、优化后的代码;
最终,经过一边让别的ABAP开发朋友指导,一边改,终于是改完了,以下附上完整的代码记录以及使用方法;
*&---------------------------------------------------------------------*
*& Program ID/Name: Z_TEST_ZGY Date written: 2024-08-18
*& Author's name: 风沙 Last update:
*& Program title: 货源清单创建
*& Project Name:
*& Version: 1.0
*---------------------------------------------------------------------*
* Description: (Incl. Related Function Area and System)
*
*---------------------------------------------------------------------*
* Include:
*
*---------------------------------------------------------------------*
* Calls: (RFC and BPI)
*
*---------------------------------------------------------------------*
* Function Modules:
*
*---------------------------------------------------------------------*
* Table:
*
*---------------------------------------------------------------------*
* Change History
*---------------------------------------------------------------------*
***********************************************************************
REPORT Z_TEST_ZGY MESSAGE-ID zdev NO STANDARD PAGE HEADING.
INCLUDE <icon>.
*--------------------------------------------------------------------*
* Data Declaration
*--------------------------------------------------------------------*
DATA: BEGIN OF ig_file OCCURS 0,
matnr LIKE eord-matnr,"物料号
werks LIKE eord-werks,"工厂
vdatu(8),"有效从
bdatu(8),"有效至
lifnr LIKE eord-lifnr,"供应商
ekorg LIKE eord-ekorg,"采购组织
reswk LIKE eord-reswk,"供货工厂
meins LIKE eord-meins,"单位
feskz LIKE rm06w-feskz,"固定货源
notkz LIKE eord-notkz,"冻结
autet LIKE eord-autet,"MRP
END OF ig_file.
DATA: BEGIN OF ig_out OCCURS 0,
matnr LIKE eord-matnr,
werks LIKE eord-werks,
lifnr LIKE eord-lifnr,
icn(4) TYPE c,
msg TYPE string,
END OF ig_out.
DATA: ig_bdcdata LIKE bdcdata OCCURS 0 WITH HEADER LINE .
DATA: ig_bdcmsg LIKE bdcmsgcoll OCCURS 0 WITH HEADER LINE.
TYPE-POOLS: slis.
DATA: fieldcat TYPE slis_t_fieldcat_alv WITH HEADER LINE.
DEFINE add2fc.
fieldcat-fieldname = &1.
fieldcat-seltext_l = &2.
fieldcat-just = &3.
fieldcat-icon = &4.
append fieldcat.
clear fieldcat.
END-OF-DEFINITION.
DATA: layout TYPE slis_layout_alv .
DATA: trans_mode TYPE c.
*--------------------------------------------------------------------*
* Selection Screen
*--------------------------------------------------------------------*
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE text-001.
PARAMETERS: p_path LIKE rlgrap-filename OBLIGATORY.
SELECTION-SCREEN END OF BLOCK b1.
SELECTION-SCREEN BEGIN OF BLOCK b2 WITH FRAME TITLE text-002.
PARAMETERS: p_n TYPE c DEFAULT 'X' RADIOBUTTON GROUP bdc,
p_e TYPE c RADIOBUTTON GROUP bdc,
p_a TYPE c RADIOBUTTON GROUP bdc.
SELECTION-SCREEN END OF BLOCK b2.
*--------------------------------------------------------------------*
* AT SELECTION-SCREEN
*--------------------------------------------------------------------*
AT SELECTION-SCREEN.
IF p_n = 'X'.
trans_mode = 'N'.
ELSEIF p_e = 'X'.
trans_mode = 'E'.
ELSE.
trans_mode = 'A'.
ENDIF.
*--------------------------------------------------------------------*
* start-of-selection on value-request
*--------------------------------------------------------------------*
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_path.
CALL FUNCTION 'WS_FILENAME_GET'
EXPORTING
def_filename = ''
def_path = 'C:\'
mask = ',TXT FILES(*.TXT),*.TXT,'
mode = 'O'
title = '选择导入文件'
IMPORTING
filename = p_path
EXCEPTIONS
inv_winsys = 1
no_batch = 2
selection_cancel = 3
selection_error = 4
OTHERS = 5.
*--------------------------------------------------------------------*
* start-of-selection
*--------------------------------------------------------------------*
START-OF-SELECTION.
PERFORM frm_upload_txt.
PERFORM frm_delete_data.
PERFORM frm_run_bdc.
PERFORM frm_show_alv.
*&---------------------------------------------------------------------*
*& Form frm_upload_execl
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
* --> p1 text
* <-- p2 text
*----------------------------------------------------------------------*
FORM frm_upload_txt .
* { add by yuanxin at 20130627 将函数WS_UPLOAD替换成GUI_UOLOAD
* CALL FUNCTION 'WS_UPLOAD'
* EXPORTING
* filename = p_path
* filetype = 'DAT'
* TABLES
* data_tab = ig_file
* EXCEPTIONS
* conversion_error = 1
* file_open_error = 2
* file_read_error = 3
* invalid_type = 4
* no_batch = 5
* unknown_error = 6
* invalid_table_width = 7
* gui_refuse_filetransfer = 8
* customer_error = 9
* no_authority = 10
* OTHERS = 11.
DATA L_P_FILE TYPE STRING.
L_P_FILE = p_path.
CALL FUNCTION 'GUI_UPLOAD'
EXPORTING
FILENAME = L_P_FILE
FILETYPE = 'ASC'
HAS_FIELD_SEPARATOR = 'X'
TABLES
DATA_TAB = ig_file
EXCEPTIONS
FILE_OPEN_ERROR = 1
FILE_READ_ERROR = 2
NO_BATCH = 3
GUI_REFUSE_FILETRANSFER = 4
INVALID_TYPE = 5
NO_AUTHORITY = 6
UNKNOWN_ERROR = 7
BAD_DATA_FORMAT = 8
HEADER_NOT_ALLOWED = 9
SEPARATOR_NOT_ALLOWED = 10
HEADER_TOO_LONG = 11
UNKNOWN_DP_ERROR = 12
ACCESS_DENIED = 13
DP_OUT_OF_MEMORY = 14
DISK_FULL = 15
DP_TIMEOUT = 16
OTHERS = 17.
* end }
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
IF ig_file[] IS INITIAL.
MESSAGE s001 WITH '没有读取到数据,程序将停止!'.
STOP.
ENDIF.
SORT ig_file BY matnr werks.
ENDFORM. " frm_upload_execl
*&---------------------------------------------------------------------*
*& Form frm_run_bdc
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
* --> p1 text
* <-- p2 text
*----------------------------------------------------------------------*
FORM frm_run_bdc .
DATA wl_file LIKE LINE OF ig_file.
DATA:BEGIN OF LT_SOURCE OCCURS 0,
matnr LIKE eord-matnr,"物料号
werks LIKE eord-werks,"工厂
END OF LT_SOURCE.
LOOP AT ig_file.
MOVE ig_file-matnr to LT_SOURCE-matnr.
MOVE ig_file-werks to LT_SOURCE-werks.
APPEND LT_SOURCE.
clear ig_file.
ENDLOOP.
DELETE ADJACENT DUPLICATES FROM LT_SOURCE COMPARING matnr werks.
data : lv_item TYPE numc2 VALUE 0."用于修改一个物料 +工厂,有多个供应商的情况,用于传值
data:LV_EORD_VDATU type char50.
data:LV_EORD_BDATU type char50.
data:LV_EORD_LIFNR type char50.
data:LV_EORD_EKORG type char50.
data:LV_EORD_RESWK type char50.
data:LV_EORD_MEINS type char50.
data:LV_RM06W_FESKZ type char50.
data:LV_EORD_NOTKZ type char50.
data:LV_EORD_AUTET type char50.
data:lv_erod_vdatu type char50.
CLEAR: LT_SOURCE , ig_file.
LOOP AT LT_SOURCE.
CLEAR ig_bdcdata.
REFRESH ig_bdcdata.
PERFORM frm_fill_bdc USING:
'X' 'SAPLMEOR' '0200',
'' 'BDC_OKCODE' '/00',
'' 'EORD-MATNR' LT_SOURCE-matnr,
'' 'EORD-WERKS' LT_SOURCE-werks,
'X' 'SAPLMEOR' '0205',
'' 'BDC_OKCODE' '=BU'.
CLEAR :lv_item."不清空的话,会一直累加。
LOOP AT ig_file where matnr = LT_SOURCE-matnr and
werks = LT_SOURCE-werks.
CLEAR wl_file.
MOVE-CORRESPONDING ig_file TO wl_file.
lv_item = lv_item + 1. " 计数器递增
clear :LV_EORD_VDATU,LV_EORD_BDATU, LV_EORD_LIFNR, LV_EORD_EKORG, LV_EORD_RESWK, LV_EORD_MEINS, LV_RM06W_FESKZ, LV_EORD_NOTKZ, LV_EORD_AUTET.
concatenate 'EORD-VDATU' '(' lv_item ')' into LV_EORD_VDATU.
concatenate 'EORD-BDATU' '(' lv_item ')' into LV_EORD_BDATU.
concatenate 'EORD-LIFNR' '(' lv_item ')' into LV_EORD_LIFNR.
concatenate 'EORD-EKORG' '(' lv_item ')' into LV_EORD_EKORG.
concatenate 'EORD-RESWK' '(' lv_item ')' into LV_EORD_RESWK.
concatenate 'EORD-MEINS' '(' lv_item ')' into LV_EORD_MEINS.
concatenate 'RM06W-FESKZ' '(' lv_item ')' into LV_RM06W_FESKZ.
concatenate 'EORD-NOTKZ' '(' lv_item ')' into LV_EORD_NOTKZ.
concatenate 'EORD-AUTET' '(' lv_item ')' into LV_EORD_AUTET.
PERFORM frm_fill_bdc USING:
'' LV_EORD_VDATU wl_file-vdatu,
'' LV_EORD_BDATU wl_file-bdatu,
'' LV_EORD_LIFNR wl_file-lifnr,
'' LV_EORD_EKORG wl_file-ekorg,
'' LV_EORD_RESWK wl_file-reswk,
'' LV_EORD_MEINS wl_file-meins,
'' LV_RM06W_FESKZ wl_file-feskz,
'' LV_EORD_NOTKZ wl_file-notkz,
'' LV_EORD_AUTET wl_file-autet.
clear:ig_file.
endloop.
CALL TRANSACTION 'ME01' USING ig_bdcdata
MODE trans_mode
UPDATE 'S'
MESSAGES INTO ig_bdcmsg.
CLEAR ig_out.
ig_out-matnr = wl_file-matnr.
ig_out-werks = wl_file-werks.
LOOP AT ig_bdcmsg.
CASE ig_bdcmsg-msgtyp.
WHEN 'S'.
ig_out-icn = icon_led_green.
WHEN 'E' OR 'A'.
ig_out-icn = icon_led_red.
WHEN 'I'.
ig_out-icn = icon_led_inactive.
WHEN 'W'.
ig_out-icn = icon_led_yellow.
ENDCASE.
MESSAGE ID ig_bdcmsg-msgid
TYPE ig_bdcmsg-msgtyp
NUMBER ig_bdcmsg-msgnr
WITH ig_bdcmsg-msgv1
ig_bdcmsg-msgv2
ig_bdcmsg-msgv3
ig_bdcmsg-msgv4
INTO ig_out-msg.
APPEND ig_out.
CLEAR: ig_out-icn,ig_out-msg.
ENDLOOP.
CLEAR wl_file.
CLEAR ig_out.
ENDLOOP.
SORT ig_out BY matnr werks lifnr.
DELETE ADJACENT DUPLICATES FROM ig_out.
ENDFORM. " frm_run_bdc
*&---------------------------------------------------------------------*
*& Form frm_fill_bdc
*&---------------------------------------------------------------------*
* BDC 公用
*----------------------------------------------------------------------*
FORM frm_fill_bdc USING v_1 v_2 v_3.
IF v_1 = 'X' .
ig_bdcdata-program = v_2 .
ig_bdcdata-dynpro = v_3 .
ig_bdcdata-dynbegin = 'X' .
ELSE.
ig_bdcdata-fnam = v_2 .
ig_bdcdata-fval = v_3 .
ENDIF .
APPEND ig_bdcdata .
CLEAR ig_bdcdata .
ENDFORM. " frm_fill_bdc
*&---------------------------------------------------------------------*
*& Form frm_show_alv
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
* --> p1 text
* <-- p2 text
*----------------------------------------------------------------------*
FORM frm_show_alv .
REFRESH fieldcat.
add2fc: 'MATNR' '物料号' 'L' '',
'WERKS' '工厂' 'L' '',
'LIFNR' '供应商' 'L' '',
'ICN' '' '' 'X',
'MSG' '' 'L' ''.
layout-colwidth_optimize = 'X'.
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
EXPORTING
i_interface_check = ''
i_callback_program = sy-repid
is_layout = layout
it_fieldcat = fieldcat[]
TABLES
t_outtab = ig_out
EXCEPTIONS
program_error = 1
OTHERS = 2.
ENDFORM. " frm_show_alv
*&---------------------------------------------------------------------*
*& Form frm_delete_data
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
* --> p1 text
* <-- p2 text
*----------------------------------------------------------------------*
FORM frm_delete_data .
DATA: BEGIN OF il_a017 OCCURS 0,
matnr LIKE a017-matnr,
werks LIKE a017-werks,
lifnr LIKE a017-lifnr,
END OF il_a017.
DATA: vl_index LIKE sy-tabix.
SELECT matnr werks lifnr
INTO TABLE il_a017
FROM a017
FOR ALL ENTRIES IN ig_file
WHERE matnr = ig_file-matnr
AND werks = ig_file-werks
AND lifnr = ig_file-lifnr.
SORT il_a017 BY matnr werks lifnr.
LOOP AT ig_file.
vl_index = sy-tabix.
READ TABLE il_a017 WITH KEY matnr = ig_file-matnr
werks = ig_file-werks
lifnr = ig_file-lifnr
BINARY SEARCH.
IF sy-subrc <> 0.
ig_out-matnr = ig_file-matnr.
ig_out-werks = ig_file-werks.
ig_out-lifnr = ig_file-lifnr.
ig_out-icn = icon_led_red.
CONCATENATE '该物料的工厂级别' ig_out-lifnr '采购信息不存在' INTO ig_out-msg.
APPEND ig_out.
DELETE ig_file INDEX vl_index.
ENDIF.
CLEAR: ig_out,ig_file.
ENDLOOP.
IF ig_file[] IS INITIAL.
MESSAGE s000 WITH '没有合适的数据,程序将停止!'.
STOP.
ENDIF.
ENDFORM. " frm_delete_data
*Text elements
*----------------------------------------------------------
* 001 导入文件路径
* 002 模式
*Selection texts
*----------------------------------------------------------
* P_A A 单步处理
* P_E E 出错处理
* P_N N 批量处理
* P_PATH 文件名称
*Messages
*----------------------------------------------------------
*
* Message class: ZDEV
*000 & & & & &
*001 重复:序列号-&在地点&下已经存在!
3、前台操作界面;
这三个不同模式,对应事务码SM35执行批处理的那三个模式
A:单步处理,也就是SM35的处理/前台,执行的时候弹出操作对话框
E:出错处理,也就是SM35的仅显示错误,执行的时候,有错误的才会有弹框显示错误信息
N:批量处理,也就是SM35的不可见,执行的时候,全部后台执行,直到执行完。
该代码注意的地方;
(1)以下的PERFORM frm_delete_data中,会把物料没有维护采购信息记录的数据删除;譬如说,同一个物料 + 工厂下,需要维护三个供应商,但是其中一个供应商没有维护采购信息记录,那么这条数据会单独剥离出来,并且在ALV输出时会提示。
然后只会导入进去两条其他的数据。
4、注意事项;
(1)MRP字段与固定供应商字段的影响
货源清单,不维护MRP字段的时候,是可以不需要维护采购信息记录的;但是如果维护了MRP字段,那么采购信息记录是必须要有的。
注意,如果维护了固定供应商字段,那么也必须要要有采购信息记录;
综上所述,该程序也可以优化成,就算该供应商不维护对应的采购信息记录,也可以导入进去,但是就不能维护MRP字段和固定供应商字段了,否则就会报错,根据实际情况优化。
(2)批导过程注意事项;
A、批导模板要用带分隔符的TXT文档,不能带标题,如下图所示的字段顺序,放到TXT时,需要去掉标题行
B、同一个物料 +工厂的货源清单中,如果有多个供应商时,MRP字段尽量只维护一个供应商,如果维护多个供应商时,会显示警告信息; 提示
记录 被冻结。处理是不可能的
消息号 06705
该问题原因就是同时维护了MRP字段,然后有效期又有重叠部分,就会出现警告消息。
最开始以为是供应商主数据或者物料或者是哪里冻结住了,后面网上看了一下资料,再切换了一下英文环境下操作,发现错误提示如下“Several sources MRP-relevant on one date”,就是说一个日期与多个MRP相关
如果这里不注意这个问题,那么使用这个批导程序录入时,就会提示这个“记录被冻结”的错误消息~~但是实际上也能录入进去,就是会给用户一些错觉而已。
3、S4版本的货源清单批量导入程序,由于是朋友给的,不是自己开发的成品,就不拿出来分享了,有需要的小伙伴可以私信交流。
三、总结:
1、LSMW这个功能有空还是要再研究下,看看多层结构的数据录入,如果标准的LSMW不存在时,是否可以自己单独录入;
2、货源清单录入功能,需要注意维护采购信息记录;
3、同一个物料 +工厂的货源清单,有效期有重叠时,注意MRP字段不要全部都维护;
最后,预告一个批量处理SAP数据的工具,RPA相关的工具,可以批量录入主数据或者MIGO过帐等操作的,但是目前还没仔细研究完,后续研究完会再分享出来~~