一切开发都来自于需求,需求并不决定开发。当接到需求的时候,一定要进行可行性分析,是否可以进行开发,否则将会严重影响工作效率
1.SAP报表
- List:原始技术,直接输出数据
- ALV:简单到复杂,也可以面向对象
- MS Office Excel:调用excel输出数据
SAP提供多种方式开发报表,这里只介绍常用的ALV报表,其他的大家有兴趣也可以去网上找资料研究一下
2.需求
1)假设有一个客户提出需求开发一个销售订单报表,报表应该包含的字段有:销售订单(so)、行项目、工厂、物料号、库存地点、需求数量、订单数量、创建日期、交货日期等等
2)接到需求进行可行性分析,如果SAP存在可满足需求的报表则不用进行开发或则进行修改;如果不存在则考虑报表开发是否可行,如果能做就准备开发,否则再和客户进行讨论。
3)假设可行性分析OK!
3、如何下手开发
1)从需求入手,分析需求
该报表主要是关于SO,这就是我们需要重点关注的地方
2)熟悉表
报表中的数据是怎么来的?当然并不是凭空产生的,需求中的销售订单、物料号等数据都将取自于数据库。所以得分析需要哪些表。当然你有可能不熟悉SAP中的表,你可以向有经验的同事请教。通过积累,让这些也成为你的东西
T-CODE:se16n可以查询透明表
以下是需要用到的一些表和一些重点关注字段,当然在实际中可能会涉及到更多的表和字段
1.【VBAK:销售凭证】
字段:vbeln(销售凭证)、erdat(创建日期)、ername(创建者)、auart(销售类型)、bstnk(采购订单)、bsark(采购订单类型)、bstdk(采购日期)
2.【VBAP:销售凭证行项目】
字段:vbeln(销售凭证)、posnr(项目)、matnr(物料)、werks(工厂)、lgort(库存地点)、erdat(销售订单创建日期)、PRCTR(利润中心)、aufnr(订单)
3.【VBPA:销售凭证合作伙伴】
字段: vbeln(销售凭证)、posnr(项目)、kunnr(客户)、lifnr(供应商)
3.【MAKT:物料描述】
字段:matnr(物料号)、spras(语言)、maktx(描述)
4.【LIPS:SD凭证交货项目数据】
字段:vbeln(交货)、posnr(项目)、werks(工厂)、lgort(地点)、lfimg(交货数量)
5.【VBKD:销售凭证业务数据】
字段: vbeln(销售凭证)、posnr(项目)、bstkd(PO)、inco1(国际贸易条件)、
6.【VBEP:销售凭证计划行数据】
字段: vbeln(销售凭证)、posnr(项目)、ETENR(计划行)、edatu(交货日期)、ezit(到达时间)、wmeng(订单数量)、lmeng(需求数量)、bmeng(确认的数量)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
4、报表开发
1)se38创建程序,创建完会有一行代码
2)自定义输出表结构
REPORT zbsdr_025.
TABLES:vbak,mara,t001w,vbap,vbep.
*--------------类型池定义
TYPE-POOLS: slis.
DATA:t_fieldcat TYPE slis_t_fieldcat_alv,
w_fieldcat TYPE LINE OF slis_t_fieldcat_alv,
t_layout TYPE slis_layout_alv.
*------------------定义输出表结构
TYPES:BEGIN OF ty_result,
vbeln TYPE vbak-vbeln, "so
auart TYPE vbak-auart, "销售类 型
posnr TYPE vbap-posnr, "项目
bstkd TYPE vbkd-bstkd, "po
aufnr TYPE vbap-aufnr,
werks TYPE vbap-werks, "工厂
lgort TYPE vbap-lgort, "库存地点
matnr TYPE vbap-matnr, "物料
maktx TYPE makt-maktx,
kunnr TYPE vbpa-kunnr, "客户
lifnr TYPE vbpa-lifnr, "供应商
* lfimg TYPE lips-lfimg, "交货数量
edatu TYPE vbep-edatu, "计划交货日期
erdat TYPE vbap-erdat, "创建日期
lmeng TYPE vbep-lmeng, "需求数量
wmeng TYPE vbep-wmeng, "订单数量
END OF ty_result.
*-----------------------------定义输出表和工作区域
DATA:t_result TYPE TABLE OF ty_result.
DATA:w_result TYPE ty_result.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
(1)类型池:slis 。类型池顾名思义就是个池子,池子里面有很多的类型。开发ALV报表得用到slis里面的类型,所以要引用slis这个类型池
(2)DATA:主要用于声明变量,【TYPE】表示改变量参考的类型。在报表里一般都定义两个东西:内表和工作区。
(3)内表和工作区。内表可以理解为临时表,对内表操作不影响数据库中的表。如果频繁地从数据库中取数会导致效率降低,所以可以用内表临时存放数据;工作区只能存放一条数据,对数据进行处理,一般用于循环中。
DATA:t_fieldcat TYPE slis_t_fieldcat_alv,
w_fieldcat TYPE LINE OF slis_t_fieldcat_alv,
t_layout TYPE slis_layout_alv.
(4) se11查看类型池,如果不知道变量参考的类型可以在这里查找,比如ALV报表字段(我们取名t_fieldcat)
(5)TYPES自定义结构,可以的字段可以来自于不同的表,一般先定义输出表结构(把报表需要展示的字段都放在一个结构里),当然为了后面的操作也要定义内表和工作区,参考的类型就是该结构
DATA:t_result TYPE TABLE OF ty_result.
DATA:w_result TYPE ty_result.
2)声明所需表
TYPES:BEGIN OF ty_vbakp,
vbeln TYPE vbak-vbeln, "so
auart TYPE vbak-auart, "销售类型
posnr TYPE vbap-posnr, "项目
werks TYPE vbap-werks, "工厂
lgort TYPE vbap-lgort, "库存地点
matnr TYPE vbap-matnr, "物料
erdat TYPE vbap-erdat, "创建日期
bstkd TYPE vbkd-bstkd, "po
END OF ty_vbakp.
DATA:t_vbakp TYPE TABLE OF ty_vbakp,
w_vbakp TYPE ty_vbakp.
*----------------------------------------------
"引入makt主要是为了获取物料描述信息
DATA:t_makt TYPE STANDARD TABLE OF makt,
w_makt TYPE makt.
DATA:rt_extab TYPE slis_t_extab.
*----------------------------------------------
"引入vbpa主要是为了获取客户和供应商
DATA:t_vbpa TYPE STANDARD TABLE OF vbpa,
w_vbpa TYPE vbpa.
*-------------------------------------------
"为获取计划交货时间,需求数量,订单数量
data:t_vbep type STANDARD TABLE OF vbep,
w_vbep type vbep.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
需要什么,就声明什么。比如需要获取物料描述信息就定义一个内表参考类型为物料描述表(makt),如果需要的多个表有联系,也可以定义结构,把所需字段放在结构里。如上面的结构ty_vbakp.(表vbak,vbap,vbkd都有销售凭证字段)
3)选择屏幕
*
SELECTION-SCREEN:BEGIN OF BLOCK a1 WITH FRAME TITLE text-001.
SELECT-OPTIONS:s_vbeln FOR vbak-vbeln,
s_matnr FOR mara-matnr,
s_werks FOR t001w-werks,
s_erdat FOR vbap-erdat, "创建日期
s_edatu FOR vbep-edatu. "交货日期
SELECTION-SCREEN:END OF BLOCK a1.
(1)选择屏幕就是SAP的前台,一般是一些查询条件
(2)select-options 可以进行多值查询(有范围的),如上图会有两个输入框。比如用户想查询1000到1010工厂的销售订单。点击输入框后面的搜索按钮,会从数据库里查询,关键字【FOR】后面就是的数据源。要注意的是,select-options里的数据源表都要在TABLES里声明下不然会出错。
TABLES:vbak,mara,t001w,vbap,vbep.
SELECT-OPTIONS:s_vbeln FOR vbak-vbeln.
(3)parameters 用于单值查询
PARAMETERS p_matnr like mara-matnr.
(4)设置选择屏幕的文本值
4)获取数据
START-OF-SELECTION.
*
PERFORM frm_get_data.
*
PERFORM frm_handle_data.
*&
*& Form FRM_GET_DATA
*&
* text
*
*
* <
*
IF t_result IS NOT INITIAL.
PERFORM frm_alv_show.
ELSE.
MESSAGE '没有符合的数据,请重新查询' TYPE 'S' DISPLAY LIKE 'E'.
ENDIF.
END-OF-SELECTION.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
(1)START-OF-SELECTION.程序的开始,PERFORM frm_get_data可以创建子程序,点击子程序名系统自动创建,选第二个,子程序会包含在当前程序里面,如下
FORM frm_get_data .
SELECT * INTO CORRESPONDING FIELDS OF TABLE t_vbakp
FROM vbak
JOIN vbap ON vbak~vbeln = vbap~vbeln
JOIN vbkd ON vbap~vbeln = vbkd~vbeln AND vbap~posnr = vbkd~posnr
WHERE vbak~vbeln IN s_vbeln
AND vbap~werks IN s_werks
AND vbap~matnr IN s_matnr
AND vbap~erdat IN s_erdat.
IF t_vbakp[] IS NOT INITIAL.
*物料描述
SELECT * INTO TABLE t_makt
FROM makt
FOR ALL ENTRIES IN t_vbakp
WHERE matnr = t_vbakp-matnr.
* 客户供应商
SELECT * INTO CORRESPONDING FIELDS OF TABLE t_vbpa
FROM vbpa
FOR ALL ENTRIES IN t_vbakp
WHERE vbpa~vbeln = t_vbakp-vbeln.
* AND vbpa~posnr = t_vbakp-posnr.
*计划交货时间
SELECT * INTO CORRESPONDING FIELDS OF TABLE t_vbep
from vbep
FOR ALL ENTRIES IN t_vbakp
where vbeln = t_vbakp-vbeln
and posnr = t_vbakp-posnr
and edatu in s_edatu.
ENDIF.
ENDFORM. " FRM_GET_DATA
*&---------------------------------------------------------------------*
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
(2)INTO CORRESPONDING FIELDS OF TABLE XXX:一般会把查询到的数据存放到内表对应的字段里
(3) JOIN…ON…:可以把两张表进行关联,ON后面的就是条件,但是不建议关联太多张表,会导致效率下降
(4)IS NOT INITIAL 判断不为空
(5) FOR ALL ENTRIES IN:相当于一个子查询。假设上面代码通过第一个select语句后,t_vbakp有值了。我们还想获取物料描述,当然可以直接从物料描述表(makt)里取,但是好多不是我们需要的,所以要加个条件,条件就是内表t_vbakp里的物料(matnr)即获取这些物料的物料描述。最后再把数据存放在内表t_makt里,所以t_makt就有了我们需要的物料描述
*物料描述
SELECT * INTO TABLE t_makt
FROM makt
FOR ALL ENTRIES IN t_vbakp
WHERE matnr = t_vbakp-matnr.
5)处理数据
(1)和之前一样创建一个子程序
FORM frm_handle_data .
CLEAR w_vbakp.
LOOP AT t_vbakp INTO w_vbakp.
w_result-auart = w_vbakp-auart.
w_result-vbeln = w_vbakp-vbeln.
w_result-werks = w_vbakp-werks.
w_result-lgort = w_vbakp-lgort.
w_result-posnr = w_vbakp-posnr.
w_result-matnr = w_vbakp-matnr.
w_result-erdat = w_vbakp-erdat.
w_result-bstkd = w_vbakp-bstkd.
READ TABLE t_makt INTO w_makt
WITH KEY matnr = w_vbakp-matnr.
IF sy-subrc = 0.
w_result-maktx = w_makt-maktx.
ENDIF.
READ TABLE t_vbep INTO w_vbep
WITH KEY vbeln = w_vbakp-vbeln
posnr = w_vbakp-posnr.
IF sy-subrc = 0.
w_result-lmeng = w_vbep-lmeng.
w_result-wmeng = w_vbep-wmeng.
w_result-edatu = w_vbep-edatu.
ENDIF.
READ TABLE t_vbpa INTO w_vbpa
WITH KEY vbeln = w_vbakp-vbeln.
* posnr = w_vbakp-posnr.
IF sy-subrc = 0.
w_result-kunnr = w_vbpa-kunnr.
w_result-lifnr = w_vbpa-lifnr.
ENDIF.
APPEND w_result TO t_result.
ENDLOOP.
CLEAR w_result.
ENDFORM. " FRM_HANDLE_DATA
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
(2)一般建议使用工作区前先清空工作区
(3)LOOP AT … INTO…. :主要用于循环内表,把内表数据一条一条取出来into到工作区里(马上要工作了^_^)
w_result-auart = w_vbakp-auart.
w_result-vbeln = w_vbakp-vbeln.
w_result-werks = w_vbakp-werks.
w_result-lgort = w_vbakp-lgort.
w_result-posnr = w_vbakp-posnr.
w_result-matnr = w_vbakp-matnr.
w_result-erdat = w_vbakp-erdat.
w_result-bstkd = w_vbakp-bstkd.
(4)上面循环的内表有很多需要的数据直接赋值给工作区w_result就可以
(5)对于循环内表t_vbakp没有的,比如物料描述信息等就要从其他地方取。前面我们已经获取了物料描述信息放在了内表t_makt里, 在这一步要做的是去取出物料对应的物料描述,就用到了READ TABLE …INTO…
(6)READ TABLE …INTO…WITH KEY…:主要用于读取内表数据into到工作区间,条件就是with key后面的。假设内表t_vbakp里有个物料112,当循环到这条记录时,就以这个物料号为条件去t_makt中查,有的话就把数据取出来放在工作区w_makt里。
READ TABLE t_makt INTO w_makt
WITH KEY matnr = w_vbakp-matnr.
IF sy-subrc = 0.
w_result-maktx = w_makt-maktx.
ENDIF.
(7)sy-subrc = 0表示语句运行成功。如果成功就给工作区w_result的物料描述赋值,但是read table只取一条数据,什么意思呢?比如说物料112有两个物料描述,但是w_makt工作区的物料描述就取第一条,因为工作区只能放一条数据。
(8)APPEND… to…:一般把工作区的数据添加到内表里。每一次循环都是给工作区w_result里的字段赋值,赋值完再把工作区添加到内表t_result里。即每次循环都往内表存数据。
6)展示ALV
创建子程序frm_alv_show,子程序里再创建以下几个程序。子程序可以让报表可读性增强,双击程序名可以快速定位到对应的位置
FORM frm_alv_show.
PERFORM frm_init_layout.
PERFORM frm_set_fieldset.
PERFORM frm_output_alv.
PERFORM f_status USING rt_extab .
ENDFORM. "frm_alv_show
*&---------------------------------------------------------------------*
(1)PERFORM frm_init_layout.
初始化ALV布局,可以自动根据数据长度调整列宽
FORM frm_init_layout .
t_layout-colwidth_optimize = 'X'.
t_layout-zebra = 'X'.
*t_layout-box_fieldname = 'BOX'.
ENDFORM.
(2)PERFORM frm_set_fieldset:
该子程序主要用于设置ALV字段
FORM frm_set_fieldset .
PERFORM frm_init_fieldcat USING 'VBELN' '销售凭证'.
PERFORM frm_init_fieldcat USING 'AUART' '销售类型'.
PERFORM frm_init_fieldcat USING 'WERKS' '工厂'.
PERFORM frm_init_fieldcat USING 'LGORT' '库存地点'.
PERFORM frm_init_fieldcat USING 'POSNR' '项目'.
PERFORM frm_init_fieldcat USING 'MATNR' '物料'.
PERFORM frm_init_fieldcat USING 'MAKTX' '物料描述'.
PERFORM frm_init_fieldcat USING 'ERDAT' '创建日期'.
PERFORM frm_init_fieldcat USING 'EDATU' '计划交货日期'.
PERFORM frm_init_fieldcat USING 'KUNNR' '客户'.
PERFORM frm_init_fieldcat USING 'LIFNR' '供应商'.
PERFORM frm_init_fieldcat USING 'BSTKD' 'PO'.
PERFORM frm_init_fieldcat USING 'LMENG' '订单数量'.
PERFORM frm_init_fieldcat USING 'WMENG' '需求数量'.
ENDFORM. " FRM_SET_FIELDSET
*&---------------------------------------------------------------------*
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
这里的字段就是ALV报表即将显示的字段,注意这里的字段名都必须大写否则报错
可以看到该程序里面又有一个子程序frm_init_fieldcat,SAP通过USING传递参数,对应子程序的参数。
PERFORM frm_init_fieldcat USING 'VBELN' '销售凭证'.
FORM frm_init_fieldcat USING fieldcatname LIKE w_fieldcat-fieldname
seltext LIKE w_fieldcat-seltext_l.
CLEAR w_fieldcat.
w_fieldcat-fieldname = fieldcatname .
w_fieldcat-seltext_m = seltext.
w_fieldcat-seltext_l = seltext.
w_fieldcat-seltext_s = seltext.
APPEND w_fieldcat TO t_fieldcat.
ENDFORM.
frm_init_fieldcat 通过using 接收参数,这里有两个参数:字段和字段描述。
(3)PERFORM frm_output_alv.
这里需要调用函数[‘REUSE_ALV_GRID_DISPLAY’]输出ALV
FORM frm_output_alv .
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
EXPORTING
* I_INTERFACE_CHECK = ' '
* I_BYPASSING_BUFFER = ' '
* I_BUFFER_ACTIVE = ' '
i_callback_program = sy-repid
i_callback_pf_status_set = 'F_STATUS '
* I_CALLBACK_USER_COMMAND = ' '
* I_CALLBACK_TOP_OF_PAGE = ' '
* I_CALLBACK_HTML_TOP_OF_PAGE = ' '
* I_CALLBACK_HTML_END_OF_LIST = ' '
* I_STRUCTURE_NAME =
* I_BACKGROUND_ID = ' '
* I_GRID_TITLE =
* I_GRID_SETTINGS =
is_layout = t_layout
it_fieldcat = t_fieldcat
* IT_EXCLUDING =
* IT_SPECIAL_GROUPS =
* IT_SORT =
* IT_FILTER =
* IS_SEL_HIDE =
* I_DEFAULT = 'X'
* I_SAVE = ' '
* IS_VARIANT =
* IT_EVENTS =
* IT_EVENT_EXIT =
* IS_PRINT =
* IS_REPREP_ID =
* I_SCREEN_START_COLUMN = 0
* I_SCREEN_START_LINE = 0
* I_SCREEN_END_COLUMN = 0
* I_SCREEN_END_LINE = 0
* I_HTML_HEIGHT_TOP = 0
* I_HTML_HEIGHT_END = 0
* IT_ALV_GRAPHICS =
* IT_HYPERLINK =
* IT_ADD_FIELDCAT =
* IT_EXCEPT_QINFO =
* IR_SALV_FULLSCREEN_ADAPTER =
* IMPORTING
* E_EXIT_CAUSED_BY_CALLER =
* ES_EXIT_CAUSED_BY_USER =
TABLES
t_outtab = t_result
* EXCEPTIONS
* PROGRAM_ERROR = 1
* OTHERS = 2
.
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. " FRM_OUTPUT_ALV
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
上面的t_outtab就是要输出的ALV表即t_result表。
(4)创建状态
没有状态ALV报表状态栏上的按钮都不能用
看看状态里有什么,直接双击状态名
FORM f_status USING rt_extab TYPE slis_t_extab.
SET PF-STATUS 'STANDARD_FULLSCREEN' EXCLUDING rt_extab.
ENDFORM. "FRM_PLATFORM_O
5.效果