ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SAP Script表单preform实战:从文本元素调用到调试技巧全解析

SAP Script表单preform实战:从文本元素调用到调试技巧全解析 1. 先搞清楚为什么SAP Script表单里要写preform1.1 先理解SAP Script表单的执行模型SAP Script是SAP里很老牌的一套打印表单方案。你在SE71里维护一个窗体窗体里有主数据、段落格式、字符格式、页面和窗口窗口里再挂上文本元素。真正打印的时候后台是靠ABAP程序通过OPEN_FORM、WRITE_FORM、CLOSE_FORM这三个函数把文本元素一行一行解释出来的。这里有个非常关键的认知误区很多新手以为SAP Script就是纯文字排版工具文本元素里只能写写静态文字、放放字段变量号。但实际上在文本元素中是可以嵌入控制命令的比如/: PERFORM xxx、/: IF xxx、/: ENDIF这类的行它们会被表单解释器当指令执行。而这个/: PERFORM xxx调用的子程序业内通常直接叫它preform本质就是ABAP子例程FORM...ENDFORM只是它跑在SAP Script的上下文里完成业务逻辑后再把结果输送到文本元素里展示。明白了这个执行模型你才能解释为什么有时候文本里放字段变量写不全逻辑为什么有时要绕一大圈去做中间值处理。凡是遇到那种“要根据某个表里的值动态拼一段话”“要按条件显示不显示某段文字”“要循环输出多行数据”的需求光靠文本元素里的变量号是不够的必须借助preform把复杂逻辑算好再把结果传回给表单打印。1.2 preform和普通ABAP子例程的关联preform这个词在SAP历练里叫法很多有人叫子程序有人叫打印子例程其实就是FORM ... ENDFORM定义的本地块只不过它被SAP Script的文本控制命令引用。你在SE80里写一个include程序里面放一堆FORM然后把程序名挂到表单上文本元素就可以通过/: PERFORM name去调。它和普通ABAP子例程在三点上是相通的参数传递机制完全一致都支持USING、CHANGING、TABLES引用传递和值传递规则照样适用内部可以正常使用ABAP语句查表、循环、字符串拼接、类型转换、排序想怎么用就怎么用调试方式也一致能打断点、能看调用栈、能改参数值。但preform有一个不太一样的点它的调用入口既不在正常的ABAP程序里也不在函数组里而是由RSTXSCRP这个报表程序在解释文本元素时触发。这导致很多人直接用“从外部调用进入调试”的思维去处理结果发现断点根本不进卡了半天不知道问题出在哪。这也是我把调试单独拉出来写一大节的原因——preform开发中真正的痛点往往不是写FORM而是不知道怎么让它停下来给你看。2. 实战起步搭建一个带preform逻辑的打印表单2.1 前期准备表单框架与程序挂接这里我拿一个非常典型的场景来讲打印采购订单时需要在抬头区域加一行动态备注这行备注不是存在主数据里的而是运行时候要根据供应商主记录、订单类型、当前日期等条件实时拼出来的。假设表单已经存在我们要新增这个动态文本区域。第一步在SE71里打开目标表单。如果是新建表单需要先维护基本页、主窗口这些框架如果只是在已有表单上加功能直接进入Pages页签找到你希望显示动态文本的窗口通常就是MAIN窗口。第二步在文本元素中把光标定位到要出现动态备注的位置。这时你直接写一行文字、放字段变量都没问题但我们要用到preform所以实际要写的不是最终文本而是一个控制命令加参数占位。常见写法是这样/: PERFORM GET_VENDOR_NOTE 供应商备注NOTE这里要注意NOTE是SAP Script的字段显示语法它会在运行时去取ABAP程序中同名变量或字段符号的值。问题在于NOTE要能取到NOTE这个变量这个变量必须存在于当前表单的执行上下文中。对SAP Script来说表单上下文里的变量通常来自驱动程序的全局变量、表单程序里的全局变量以及通过PERFORM...CHANGING传进去的参数。所以我们还得写一个专门的子例程来维护NOTE这个值。第三步把包含这个子例程的ABAP程序挂到表单上。在SE71菜单里选择“程序/表单”页签或叫做“表单程序/包含程序”维护一个程序名。我实际项目中最常用的方式是建一个专门的include程序比如ZINCL_MY_FORM001所有跟这个表单有关的FORM都放里面然后把这个include程序挂上去。这里的坑在于很多新人会把“挂上去”理解成“编译进去”以为程序里写了就行。实际上表单不关心程序在哪见的代码它只认你在“表单程序”维护框里填的那个名字。如果没填或者填的程序名下面找不到对应的FORM调用时就会直接报错。2.2 在文本元素中正确调用PERFORM文本元素里调用preform的标准语法有很多变体我实际用得最多的是这几种/: PERFORM GET_VENDOR_NOTE /: PERFORM CONVERT_NUM USING KUNNR CHANGING NOTE /: PERFORM FILL_ITEMS TABLES IT_ITEMS第一种是无参调用看起来最简单也最值得警惕。因为一旦没有参数传递子例程里就只能依赖全局变量。全局变量在哪来要么来自驱动程序的全局变量区要么在表单程序声明部分用DATA定义的变量要么通过别的PERFORM里面用CHANGING改的。新手最喜欢写无参调用然后发现子例程里读不到任何数据各种莫名其妙。我建议哪怕是简单的逻辑也至少用CHANGING把一个主变量传进去再传出来方便排错。第二种带USING和CHANGING的调用是重点。注意KUNNR这种写法在文本元素里会先被解释成变量值再把值传给FORM。也就是说你写在文本元素里的KUNNR必须事先存在否则会变成空串。想让这个变量存在就得保证它或者来自驱动程序的全局变量或者被之前调用的某个preform设置过。这也是为什么我经常把一连串的preform像一个流水线一样摆在文本元素开头先预处理各种变量再逐行输出到正文。第三种TABLES传内表在我自定义的打印场景中用得不多但标准打印程序里很常见。它的调用把程序的内表直接引用传入子例程子例程内部对表的所有操作都会反映回调用端。如果你不想要这种引用传递效果就要么用CHANGING传要么在子例程内部先复制。这里还有一点要专门提醒文本元素里写/: PERFORM时行的开头必须是斜杠加冒号紧挨着格式上稍微错位都可能导致整行被当成普通文字输出。我见过不止一次有人排版时在斜杠前多打了个空格结果屏幕上直接把代码那行字打印出来了半天没反应过来。2.3 FORM子例程的参数定义与写法跟文本元素对应的ABAP端就要写FORM了。拿上面那个供应商备注的例子来说最简单的版本长这样FORM get_vendor_note CHANGING cv_note TYPE string. DATA: lv_kunnr TYPE kunnr, lv_text TYPE string. IF vendor-maktx IS INITIAL. cv_note 暂无备注信息. ELSE. CONCATENATE 供应商: vendor-name1 备注: vendor-maktx 日期: sy-datum INTO cv_note RESPECTING BLANKS. ENDIF. ENDFORM.这里有几个地方是实战中容易翻车的第一FORM名字的大小写问题。SAP Script解释器对PERFORM后面跟的名字和ABAP里定义的FORM名字匹配时看起来不区分大小写但某些版本和某些场景下非常敏感。我自己的经验是两边统一用小写、中间用下划线别混用驼峰省得在奇奇怪怪的地方踩雷。比如你写/: PERFORM Get_Vendor_Note但在include程序里定义的是FORM GET_VENDOR_NOTE虽然ABAP编译不报错打印预览时就可能说找不到子程序。第二参数个数和顺序必须完全一致。文本元素里写USING KUNNR CHANGING NOTEFORM定义就得是FORM xxx USING p_kunnr CHANGING p_note个数、顺序差一个都会在执行时报“形式参数与实际参数不一致”之类的错误。这个错误在预览时经常不友好提示直接弹一个信封差不多的通用消息需要进调试才看得明白。第三在子例程里给参数赋值要注意类型的隐性转换。CHANGING参数如果定义成字符串传进来的可能是个带前导零的订单号、日期串使用前最好做格式处理。这里和热词里提到的“abap 检查是否为数值类型”正好对上了在处理从表单文本元素传进来的数据时如果你不确定它是字典型还是数值型先做一次IS NUMERIC判断再转换否则拼字符串时会出很多幺蛾子。第四如果你的preform里要处理内表最稳妥的方式是把内表定义为STANDARD TABLE类型然后用TABLES参数传入。但注意TABLES传入的是引用preform里对表的修改会影响调用方的原表所以在打印逻辑里如果你只想临时排序、筛选最好先复制一份避免污染作业数据。这里可以合理用上ABAP的SORT语句处理完再做打印输出又顺手又不会破坏原表结构。3. preform调试技巧从守株待兔到精准命中断点3.1 在SE71预览中进入调试的三种方法前边说了preform跑在RSTXSCRP的上下文里所以你直接在SE71里点“预览”默认情况下你放的BREAK-POINT是不会停下来的。这个坑我踩了很久后来摸出三条路。第一条路也是最省事的在文本元素里临时加一行/: PERFORM DEBUG_STOP然后在这个子例程里放一个BREAK-POINT预览时就能停住。但这条路的麻烦在于你需要额外维护一个调试用的FORM发布前还得记得删掉容易遗漏我一般只在紧急排查时用。第二条路利用SAP的调试触发机制。在SE38编辑那个include程序把光标放在FORM第一行点击“创建断点”。然后回到SE71预览界面在表单的“其他设置”里把“调试”勾选上。不同版本这个菜单位置会变但核心逻辑就是告诉表单解释器我要进入ABAP调试器。勾选后预览时系统会弹出一个消息框问你“是否启动调试”选是然后表单一执行到你打的断点就会停。这个方法稳定、不用改代码是我日常最推荐的。第三条路如果你已经有一个驱动主程序在跑打印逻辑比如ZREPORT调用了OPEN_FORM、WRITE_FORM那你就在主程序里CALL FUNCTION OPEN_FORM之前用/h打开调试模式然后在调试器里设置断点到include程序的FORM。因为主程序是普通ABAP程序所有函数调用都会跟着进去preform自然也会被带进去。这个方法特别适合排查那种“为什么打印任务发不出来”的问题因为你不仅能看到preform内部还能看到驱动程序的整个调用链。3.2 动态条件下的条件断点设置很多时候preform会被循环调用多次比如订单里每一行ITEM都调一次同一个子例程你只在某一行数据异常时才想停下来看。这时候手动打断点然后反复按F8会崩溃正确姿势是条件断点。在SE38的调试器里当你已经进入调试状态后可以在顶部菜单“调试器→断点→创建”里选择条件断点输入条件表达式比如sy-tabix 5、p_kunnr 0000123456或者iv_total 1000。这样只有当条件满足时才会中断。对preform来说有一点和普通ABAP程序不同你要判断的变量很多时候是FORM的形式参数不是全局变量。在断点条件里直接写p_kunnr这种形式参数名有的版本会提示变量不存在。我的办法是在FORM里面第一行先用全局变量或实例变量接一下比如gv_check p_kunnr然后断点条件写gv_check xxx。虽然多写一行代码但调试条件就稳了。另外如果你要调试的目标FORM是在include程序里的SE38的GUI版本里有时候断点不生效请确认这个include程序有明确的编译单元。有的include只是纯粹被主程序或函数组包含没有单独的主程序入口这时候调试器可能不认。解决方法是把断点打在包含它的主程序里然后逐步Step Into到include中。3.3 调用堆栈与参数值检查断点停住之后别急着看变量先看一眼调用堆栈。在ABAP调试器里调用堆栈窗口能完整展示当前调用路径从RSTXSCRP的文本解释器到某个函数模块再到我们的include子例程一层一层非常清晰。这个堆栈对定位问题是杀手级的。我在现场帮同事排过一个怪问题表单打印时备注里出现了一堆乱码但是任何人查数据都觉得源表没问题。进调试后断点停在preform里堆栈显示这个FORM不是从文本元素的PERFORM进来的而是被另一个FORM间接调用的那个父FORM在调用前把某个全局变量改掉了。如果不看堆栈光看子例程内部永远找不到变量被谁污染。参数值检查这一块建议重点看两类变量一是CHANGING出参在ENDFORM之前的值二是文本元素里变量号对应的字段值。尤其是后者如果你发现子例程算出来的lv_note明明是“供应商张三”但打印出来却是空的十有八九是你文本元素里写的变量名和子例程里实际赋值给CHANGING的参数名对不上。用调试器把表单上下文里的字段值列出来一眼就能看出来。关于调试时的参数修改也顺手说一句SAP调试器允许你在调试中直接改preform局部变量的值这在测试不同分支时非常节省时间。比如你有一个价格判断逻辑正常数据永远是正数你想验证负数和零的场景直接在调试器里把传入的金额改成负数再按F8就能模拟不需要跑一遍业务去造数据。唯一的坑是对值传递参数改了只在子例程内部有效你要验证的是调用方拿到的结果时得改CHANGING参数并在ENDFORM之后去看调用方的变量。4. 高频问题排查实录与避坑清单4.1 preform常见错误速查表实战中我整理过一张表格凡是SAP Script表单开发里遇到preform的问题基本都能在里面找到对应项。现象可能原因处理方案预览直接报“找不到子程序”PERFORM名字与FORM名不匹配或程序未正确挂到表单核对大小写与下划线命名检查SE71“程序/表单”页签是否维护了include程序打印时文本元素里出现了“/: PERFORM”整行文字控制命令行的斜杠前缀前有空格或格式错误删掉行首多余空格确保该行以/:严格开头子例程内变量全为空没有传参且全局变量未在表单上下文中就绪改用USING/CHANGING明确传参或在子例程里重新查表赋值参数不匹配报错USING/CHANGING参数个数、顺序不对调整文本元素调用行与FORM定义的参数保持一致条件判断在文本元素里不生效文本元素里的/: IF和/: ENDIF配对缺失或条件变量未定义检查控制命令配对提前用调试器确认变量是否存在断点不停没在SE71预览设置里开启调试或断点打在未加载的程序用3.1节的三条路之一重新触发调试同样的FORM一个表单能调一个不能调不同表单挂了不同的include程序两边的“程序/表单”配置需要分别维护CHANGING参数在调用方没变文本元素传参的是值传递CHANGING用的是形参副本确认传参类型为引用传递或改用TABLES传内表这张表我贴在公司内部Wiki上很多次了每次带新人都先过一遍能省掉一大半的低级提问。4.2 两个让人抓狂的隐晦坑第一个坑和全局变量作用域有关。SAP Script的表单上下文里并不是所有ABAP全局变量都能直接显示为变量名。很多人以为既然驱动程序的全局变量和include程序的全局变量都在同一次运行上下文中那我在文本元素里直接GT_ITEMS显示内表总可以吧结果输出一堆空白。原因是SAP Script字段引用能访问的变量范围有严格限制并不等价于“ABAP程序中的所有全局数据”。它通常只能访问表单驱动当前实例已导出的、且在表单上下文中被明确维护过的字段。而preform改写的全局变量因为是通过引用传递进来的很多时候是能显示的但如果你依赖的是程序内部一个纯粹的局部全局变量就可能什么都输出不了。解决办法很简单也符合良好实践preform里算好的值务必通过CHANGING参数明确传出去不要依赖“我在子例程里改了全局变量表单就该自动认”。我在做迁移和重构时见过太多原来能跑的老表单换个新驱动主程序后全局变量上下文变了打印值就全没了最后推倒重写一堆FORM才救回来。第二个坑是文本元素里的行数与窗口大小不对应导致的“内容被截断”假象。这个不属于preform本身但它经常和preform一起出现你调用了preform生成了足够长的动态文本但窗口里只留了固定两行的高度结果文本被截断看起来像逻辑没执行对。排错时先别急着怀疑FORM先看一下窗口的高度和文本行的字号是否足以容纳超长内容。这里调试器帮不上忙因为它显示的是变量值而不是排版结果所以逻辑对了、输出截了的情况很误导人。我一般会在预览界面用“显示行结构”这种辅助功能或者临时把窗口高度调大再预览确认到底是不是布局问题。4.3 判断什么时候不该用preform最后再补一个方向性的经验preform不是万能的别什么都往里面塞。SAP Script表单里能直接通过字段变量号输出的值就尽量不要绕一层FORM去算只有涉及需要条件判断、循环拼接、查表汇总的时候才把逻辑下沉到preform里。如果企业项目里有条件用SAP Smart Forms或Adobe Form那又另当别论——新项目往往直接上更现代的打印方案preform这种写法更多是在存量SAP Script表单维护中高频出现。我经历过不少次“明明能显示字段值但非要用PERFORM包一层结果把自己绕晕”的案例。比如一个订单行项目金额明明数据库字段就有文本元素里直接VBAP-NETWR就行偏要写个FORM查一遍再赋给CHANGING参数。这不仅没有带来任何灵活性反而增加了调试成本。所以在动手写preform之前先问自己三个问题这个值能不能直接在文本里引用这个逻辑是不是必须要用ABAP的流程控制这个子例程会不会被多处复用如果都不是那就别写。5. 经验沉淀让preform更好用的几个建议5.1 命名规范一眼看出这是表单子程序我见过太多名字起得像随机数的FORM比如FORM zform001过两个月再看根本想不起来它是干嘛的。建议在命名上统一前缀比如F_加业务对象加动作F_GET_VENDOR_NOTE、F_CONVERT_PRICE、F_FILL_ITEMS。这样在文本元素里看到/: PERFORM F_GET_VENDOR_NOTE文本编辑器里看到对应FORM上下文一下就能对上。还有个实用技巧把文本元素里所有PERFORM调用集中放在窗口文本的最前部当作“预计算区”下面再放真正要输出的排版文本。这样阅读表单时逻辑前置、输出后置结构非常清爽。如果你把PERFORM散落在一大段文本中间维护时容易看漏而且调试时也不容易定位。5.2 参数设计尽量用CHANGING而不是全局变量前面提到了好几次“通过CHANGING明确传参”这里我再强调下背后的原因。SAP Script表单这种东西最难维护的点在于它隐式依赖上下文。如果你在preform里大量使用全局变量别人接手时根本不知道这个变量在哪个程序、哪段逻辑里被改过。但如果你把每个FORM的输入输出都挂在参数上表单文本元素的PERFORM行就变成了一份最简单的文档USING A CHANGING B清晰告诉阅读者这个子例程吃进什么、吐出什么。传递参数时还要注意内表和大字符串的性能问题。值传递会在每次调用时复制数据如果循环里频繁调用很浪费资源。这时候我建议要么用引用传递在FORM参数上使用TYPE REF TO要么把数据先整理好再一次性传给FORM。SAP Script本来就适合输出排版量不大的表单别让preform把性能拖垮了。5.3 最后分享一点小技巧我个人在实际操作中的体会是凡是涉及preform的功能改造交付前一定要把“正常数据”“边界数据”“空数据”三种场景各打印一遍预览。这听起来像废话但preform最容易出的问题就是平时数据都正常一遇到供应商主记录没有维护备注、日期字段为初始值、内表为空这类边界就会直接打出空行或者把字段名原样打出来。所以我在写FORM时开头第一行往往先把参数判个空能还给调用方一个默认值就还给默认值宁可在表单里显示一句“暂无”也不要让客户看到空白区域。再有一个小技巧就是善用SY-SUBRC和消息拼接来做运行日志。如果你在preform里查表失败、读不到数据可以临时在ENDIF之前写一句CONCATENATE D sy-subrc INTO gv_debug之类的代码把调试信息传给页面某个隐藏的文本区域。等系统上线后让用户把那个区域的文字截图发过来比远程反复问“显示的什么”高效得多。这是我多次被一线用户反馈“就是这个界面不对”之后才摸索出来的土办法但真的管用。preform这种技术形态在SAP Script表单里还会存在很久它不新潮但在存量系统维护中非常实用。掌握好它的开发套路和调试手段能让你在这类老牌打印表单面前做到心里有数上手不慌。
返回列表