ARTICLE DETAIL

资讯详情

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

SAP ABAP选择屏幕:PARAMETERS与SELECT-OPTIONS实战

SAP ABAP选择屏幕:PARAMETERS与SELECT-OPTIONS实战 SAP ABAP Selection Screen选择屏幕大概是每个 ABAP 人写得最多、也最容易被低估的一块东西。它看起来就是几个 PARAMETERS 加几个 SELECT-OPTIONS跑起来也就那样但真正在项目里用户对这个界面的抱怨往往比报表本身还多字段太多找不到、条件填错了一查就是半小时、明明想查一个月的数据结果把三年的底表全捞出来了。选择屏幕是报表的第一个交互层它决定了用户怎么描述需求也决定了你后面那段 SQL 能不能跑得快。这篇东西我打算把选择屏幕从语法、事件、布局、变式一直到性能按我自己写代码的顺序捋一遍中间夹一些踩过的坑和调试时的小手段。不管你是刚开始写 ABAP 报表还是写了几年但一直用模板复制粘贴这里应该都能捞到点能直接用上的东西。1. 选择屏幕这门手艺到底值不值得单独拿出来讲1.1 从每天早上那几次取数说起我待过的一个项目MM 那边每天早上八点半固定有七八个人跑同一支库存报表运行时间从十几秒到七八分钟不等差别全在选择屏幕上同一支程序有人输入单个工厂加两个物料号三秒出结果有人所有字段留空直接回车程序把 MARD 表从头翻到尾整个对话进程卡住后台的作业队列也跟着抖。这支程序本身写得没毛病OPEN SQL 也没写错问题就是选择屏幕没有做任何约束和引导。所以我的看法是选择屏幕不是一个界面装饰它是你和用户之间关于数据范围的一份契约。用户在选择屏幕上承诺了什么范围你的代码就应该只在这个范围里干活。字段给得太随意用户就不当回事字段给得太多用户反而不知道该填哪个。更现实的一点是SAP 标准事务里几乎所有的入口都是选择屏幕——MM 的 MD07 库存需求清单、SD 里的 STO 库存转储单相关查询、CO 的 KO88 订单结算你每天面对的其实就是各式各样的选择屏幕。想给这些标准事务加字段走的也是增强里的屏幕出口而不是改标准代码。1.2 PARAMETERS 和 SELECT-OPTIONS 的定位差异这两个语句新手最容易混着用我个人的划分标准很简单单值用 PARAMETERS区间或多值用 SELECT-OPTIONS。PARAMETERS 定义的是一个具体类型的变量界面上就是一个输入框SELECT-OPTIONS 定义的是一个所谓选择表selection table底层是一个带 SIGN、OPTION、LOW、HIGH 四个字段的内表结构界面上会渲染成一个带多选按钮和区间切换的控件。这个底层结构非常关键因为它决定了三件事。第一SELECT-OPTIONS 声明的变量可以直接塞进 OPEN SQL 的 WHERE 条件里写成WHERE matnr IN s_matnrSAP 会自动帮你展开成区间集合第二它可以直接作为参数传给 RFC 函数或 BAPI因为标准函数接口里大量使用这种结构第三你可以在代码里手工构造它的内容比如APPEND VALUE #( sign I option BT low 1000 high 2000 ) TO s_matnr这在做权限过滤或者联动过滤时特别有用。反过来 PARAMETERS 没有这个能力如果你拿一个单值字段去做WHERE条件的手工拼接字符串拼 SQL 的做法在 ABAP 里既不安全也不优雅一旦字段里带了单引号就出事。还有一种情况是标准程序里常见的RANGES它声明的是纯内表结构、界面上不显示通常用于内部传参或者给 ALV 做过滤条件别跟 SELECT-OPTIONS 搞混——前者不占屏幕后者占。1.3 选择屏幕背后的执行时序很多人写选择屏幕是凭感觉往事件块里塞代码塞完能跑就行但一旦出现回车后默认值被重置按钮点两次才生效这类怪事就抓瞎了。要避免这个必须记住选择屏幕的事件顺序这是整篇文章里我认为最值钱的一段。一次典型的选择屏幕交互是这样走的程序启动时先执行INITIALIZATION这是你赋默认值的唯一正确位置然后进入AT SELECTION-SCREEN OUTPUT也就是 PBOProcess Before Output每次界面刷新前都会跑一遍用户敲回车或者点了按钮进入AT SELECTION-SCREEN也就是 PAIProcess After Input这里做校验校验通过后界面再一次 PBO然后才轮到START-OF-SELECTION正式的业务逻辑从这里开始最后是END-OF-SELECTION。容易翻车的地方有两处。一是很多人把默认值赋在 AT SELECTION-SCREEN OUTPUT 里结果是每次回车界面刷新都重新赋一遍用户改过的值被冲掉表现出来就是我明明改了条件一按回车又变回去了。二是在 AT SELECTION-SCREEN OUTPUT 里写 SELECT 语句界面每刷新一次查一次数据库用户每按一次回车就多一次无谓的 IO这在大表上非常明显。我的做法是默认值一律放 INITIALIZATION取过一次就不该再变的数据也放 INITIALIZATION 缓存到全局变量AT SELECTION-SCREEN OUTPUT 里只做纯屏幕逻辑绝不碰数据库。2. Parameters 与 Select-Options 的附加项哪些必须记住2.1 PARAMETERS 的附加项逐个拆解PARAMETERS后面可以跟一长串附加项写熟了效率极高但每一个都有它自己的脾气。我按使用频率排一下。TYPE和LIKE是声明类型的实际项目里我更推荐用TYPE直接引用数据元素比如TYPE matnr、TYPE werks_d因为数据元素的转换例程、F4 帮助、字段标签全都带过来了界面上还能自动显示字段描述文本省掉一大堆 TEXT 符号维护。LIKE引用的是字典字段现在基本被 TYPE 取代了。DEFAULT给默认值注意它只接受字面量或者系统字段这类简单表达式DEFAULT sy-datum、DEFAULT X都没问题别指望在里面写函数。OBLIGATORY把字段设成必输界面上会打勾并且自动校验它的好处是不用你写代码坏处是当这个字段被动态隐藏时老版本里照样会弹必须输入的提示所以凡是打算动态隐藏的字段我都不用 OBLIGATORY改成在 AT SELECTION-SCREEN 里手工判断。LOWER CASE允许输入小写字母不加这个附加项时输入会自动转成大写这在处理物料描述、备注这类文本字段、或者和外部系统对接的字段时会出问题——供应商传来的文件名、邮箱地址全被吃成大写谁也认不出来。涉及 Unicode 和特殊字符的输入场景务必检查这个附加项。AS CHECKBOX把字段变成复选框RADIOBUTTON GROUP grp变成单选组同一组里必须有一个 DEFAULT X否则初始状态全是未选中。单选组配USER-COMMAND附加项是个非常实用的组合它让用户在切换单选按钮时立刻触发 PAI你就能在 AT SELECTION-SCREEN OUTPUT 里根据当前选中的选项显示或隐藏后续字段——相当于做了一个简易的分步表单。MEMORY ID关联 SPA/GPA 参数作用是把用户上次输入的值自动带出来用户不用每次都重敲。这个功能在天天跑同一支报表的场景里特别受欢迎但要注意它和变式的优先级问题后面变式那一节会细说。MODIF ID m1是动态控制屏幕的钥匙它给字段打上一个分组标记之后就能在 PBO 里按组去遍历屏幕字段改属性。这个附加项一定要用大写写因为存进 SCREEN-GROUP1 时会被转成大写很多人写小写之后发现过滤不到抓半天。2.2 SELECT-OPTIONS 的附加项与 RANGES 结构SELECT-OPTIONS的附加项和 PARAMETERS 有重叠也有专属。重叠的包括DEFAULT、OBLIGATORY、LOWER CASE、MEMORY ID、MODIF ID专属的有这么几个。NO-EXTENSION去掉右侧的多选按钮NO INTERVALS去掉区间输入注意这个写法历史上有过NO-INTERVALS和NO INTERVALS两种形式建议按当前版本的语法检查提示来写。这两个附加项我一般在用户明确只查单值、或者屏幕空间紧张时用能显著降低误操作。NO DATABASE SELECTION值得单独说。加了它SAP 就不会自动为这个字段生成数据库层的选择帮助F4适合两种情况一是字段对应的表数据量巨大自动生成的 F4 慢到用户骂人二是这个字段根本不属于任何透明表比如是内表字段或者几个字段拼出来的逻辑值。加完之后你自己在AT SELECTION-SCREEN ON VALUE-REQUEST FOR里写 F4用F4IF_INT_TABLE_VALUE_REQUEST函数把候选列表控制在自己手里性能和信息量都好得多。VISIBLE LENGTH n控制输入框的显示宽度纯粹是版式优化但别小看它一个物料描述字段窄到一个字都看不清用户就会来提需求单。SELECT-OPTIONS 底层那个内表结构写代码时一定要有意识地去用。除了前面说的IN条件和构造区间还有几点单值条件可以写成s_matnr-low直接判断要判断用户到底填没填用IF s_matnr IS INITIAL而不是IF s_matnr-low IS INITIAL因为前者检查的是整张表是否为空把一个选择表传递给下游函数时直接传s_matnr[]加上方括号表示传内表本身而不是结构。这几行看着不起眼但我在代码评审里改过太多次了。2.3 屏幕布局语句BLOCK、LINE、COMMENT、PUSHBUTTON字段定义好之后界面还是平铺的一长条用户找一个字段要往下拖三屏。这时候就该用布局语句了。SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE text-001和对应的END OF BLOCK b1是最常用的把相关字段圈进一个带标题的框里。我的习惯是按业务含义分块主数据条件一块、时间范围一块、技术开关一块。标题用 TEXT 符号而不是硬编码字符串这样多语言登录时能自动切换——顺带说一句改了文本符号之后如果用户那边还显示旧文字多半是文本缓存在作怪退出登录重进或者查一下文本池的翻译状态基本能解决。块内部如果要控制行SELECTION-SCREEN SKIP n空 n 行SELECTION-SCREEN ULINE画一条横线分隔SELECTION-SCREEN BEGIN OF LINE和END OF LINE可以让你把几个元素塞在同一行里实现标签 复选框 输入框这种紧凑排布。同一个 LINE 里可以用SELECTION-SCREEN COMMENT 1(20) text-002加一行说明文字用SELECTION-SCREEN POSITION n精确控制起始列。SELECTION-SCREEN PUSHBUTTON定义按钮形如SELECTION-SCREEN PUSHBUTTON /1(15) btn_all USER-COMMAND alls.分号后面的 USER-COMMAND 是按钮被点击时回传的命令码习惯上我控制在四个字符以内早期版本对这个长度卡得比较死用短码最稳妥。按钮上的文字必须在你选定的那个名字的变量里赋值通常在 INITIALIZATION 里写btn_all 全选物料。想给按钮加图标的话可以把图标常量拼到文本里但图标和文字的排布在不同 GUI 版本下表现不一致我这边的经验是内部工具随便给业务用户用的正式报表一律用纯文字省得有人截图来问这是什么符号。3. 那几个必须搞清楚的屏幕事件3.1 INITIALIZATION默认值和按钮文本都在这写INITIALIZATION 在选择屏幕还没显示出来之前执行一次它的定位是初始化。默认值、按钮文本、下拉列表的候选值集合、按用户身份算出来的初始范围全都应该在这个事件里准备好。我特别喜欢在这里做一件小事根据sy-uname查用户的属性给一个合理的默认区间。举个实际做过的例子某支凭证查询报表默认查上月数据但如果用户是第一次登录系统很多业务同事会一时想不起来该查哪天我就去 USR02 表里取用户上次登录日期TRDAT 字段把默认起始日期设成那个日期所在月的第一天结束日期设成当前日期。这样每个人打开报表看到的默认区间都贴合自己的使用节奏接受度非常高。代码大概就是一句SELECT SINGLE trdat FROM usr02 INTO gv_datum WHERE bname sy-uname AND trdat 00000000注意那个00000000的排除条件USR02 里没登录过的用户这个字段是零日期不排除会拿到一个没法用的默认值。还有一点INITIALIZATION 里不要写会跑很久的取数。它虽然只执行一次但它挡在界面显示之前跑的慢用户就盯着一个白屏等体验极差。真正耗时的数据准备放到 START-OF-SELECTION。3.2 AT SELECTION-SCREEN OUTPUT动态控制屏幕的入口这个事件是 PBO每次界面刷新都会跑。动态控制屏幕的代码就写在这里标准写法是遍历 SCREEN 内表AT SELECTION-SCREEN OUTPUT. LOOP AT SCREEN. IF screen-group1 M1 AND p_type X. screen-active 0. MODIFY SCREEN. ENDIF. ENDLOOP.几个必须记住的点。第一MODIFY SCREEN不能省改了结构不写回等于没改。第二SCREEN-ACTIVE和SCREEN-INPUT不是一回事INPUT 0是字段还在但变灰不可输入ACTIVE 0是字段直接隐藏不占位置。想只读显示用 INPUT想藏起来用 ACTIVE。第三SCREEN-REQUIRED控制必输的小勾前面提到 OBLIGATORY 字段在隐藏后仍可能要求输入就是靠这里把 REQUIRED 清掉解决的。第四LOOP AT SCREEN 拿到的是屏幕上所有元素包括按钮和工具栏用SCREEN-GROUP1过滤最稳所以字段定义时那个 MODIF ID 千万别偷懒。一个真实的坑有人在 PBO 里对同一个字段反复设置 INVISIBLE/ACTIVE逻辑写得像开关一样来回切结果在某些前端尤其是 Web GUI 和 Fiori 的某些场景下渲染错位出现字段重叠或者输入框跑到标签下面。我的经验是PBO 里的判断尽量写成一次性算清楚只写一次 MODIFY不要在循环里对同一字段多次赋值。3.3 AT SELECTION-SCREEN 家族校验、F4、F1 分别挂在哪PAI 这块分了几个变体各有各的用途挂错地方是新手最常见的问题。AT SELECTION-SCREEN不带后缀在整个屏幕校验通过后触发一次我一般在这里做跨字段的全局校验比如组合条件是否矛盾、必输字段是否填了、权限检查AUTHORITY-CHECK。这里用MESSAGE ... TYPE E报错会直接把用户拦在选择屏幕上不会进入业务逻辑。注意TYPE W是警告会继续执行但会弹出提示很多老代码用 W 提示数据量大请耐心等待其实挺扰民的。AT SELECTION-SCREEN ON s_matnr挂在单个字段上字段一填完就触发。这里做格式校验最合适比如用 CO 运算符判断是不是纯数字IF p_kunnr CO 0123456789这个运算符的意思是只包含这些字符比调用函数快得多也比正则好读。但这里千万别做数据库查询因为用户每改一次这个字段就查一次输入框里敲十下就查十次用户会怀疑系统卡死。真要查表校验存在性放在不带后缀的 AT SELECTION-SCREEN 里或者干脆不校验、让后续逻辑用空结果兜底。AT SELECTION-SCREEN ON BLOCK b1只在某个块内的字段变化时触发适合做块级别的联动AT SELECTION-SCREEN ON RADIOBUTTON GROUP grp处理单选组切换配 USER-COMMAND 用效果最好。F4 从值帮助挂在AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_matnr里面调用F4IF_INT_TABLE_VALUE_REQUEST把内表、要显示的列、返回值字段名配好即可。这个函数比老的HELP_VALUES_GET_...系列好用太多能返回多列、能回填多个字段、还能做动态列。F1 帮助挂在AT SELECTION-SCREEN ON HELP-REQUEST FOR p_matnr一般用来弹出文档说明或者字段释义用得少但偶尔业务方会点名要。3.4 用 SSCRFIELDS 做工具栏按钮SSCRFIELDS是一个系统自带的结构用来接收选择屏幕上的用户命令。用法有两层。第一层是工具栏按钮写SELECTION-SCREEN FUNCTION KEY 1就能在屏幕的应用工具栏上多出一个按钮最多五个KEY 1 到 KEY 5按钮文字在 INITIALIZATION 里赋给sscrfields-functxt_01点击后在 AT SELECTION-SCREEN 里判断sscrfields-ucomm FC01。这一招我经常用来放导出模板重置条件按上次条件查询这类辅助动作比在屏幕里塞一堆 PUSHBUTTON 干净。第二层是普通 PUSHBUTTON它回传的命令码也是从SSCRFIELDS-UCOMM里读的不是你定义 USER-COMMAND 时写的那个名字本身。举例SELECTION-SCREEN PUSHBUTTON /1(12) btn1 USER-COMMAND alls.点击后你要判断的是sscrfields-ucomm ALLS大写。我见过不止一个同事在这里写成IF sy-ucomm ALLS然后死活不生效——sy-ucomm 是对话屏幕的命令字段选择屏幕上的命令走的是 SSCRFIELDS。顺带提一句PUSHBUTTON 触发的 PAI 会正常执行 AT SELECTION-SCREEN所以如果你在 AT SELECTION-SCREEN 里写了强制校验比如必输检查点了辅助按钮也会被拦下来。正确做法是在校验前面加一句IF sscrfields-ucomm IS INITIAL的判断把按钮命令和普通回车区分开。4. 从零搭一个能直接抄的取数程序4.1 需求和字段选型假设需求是查某个工厂下指定物料范围的库存要能按物料类型、库存地点过滤结果用 ALV 展示还要有一些技术开关。字段选型我会这么定工厂用 PARAMETERS 单选物料号、物料类型、库存地点用 SELECT-OPTIONS查询日期用 PARAMETERS 带默认值。为什么工厂不用 SELECT-OPTIONS因为这支报表的取数逻辑是按工厂走索引的多工厂查询会让执行计划变复杂业务上也没有这个需求。选择屏幕给什么字段本质上是你在替用户约束查询范围范围越收敛SQL 越好写、越快。如果一个报表允许用户把所有条件留空那你必须确保留空时的默认行为是可接受的而不是把整张表扫一遍。4.2 完整代码骨架REPORT zmm_rpt_stock. TABLES: mara, mard. 仅为兼容老的 F4 和字段引用新代码可去掉 SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001. PARAMETERS: p_werks TYPE mard-werks OBLIGATORY MEMORY ID z_wrk, p_date TYPE sy-datum DEFAULT sy-datum MODIF ID m1. SELECT-OPTIONS: s_matnr FOR mara-matnr, s_mtart FOR mara-mtart, s_lgort FOR mard-lgort NO-EXTENSION, s_kunnr FOR mara-matnr NO DISPLAY. SELECTION-SCREEN END OF BLOCK b1. SELECTION-SCREEN BEGIN OF BLOCK b2 WITH FRAME TITLE TEXT-002. SELECTION-SCREEN BEGIN OF LINE. SELECTION-SCREEN COMMENT 1(22) TEXT-003. PARAMETERS p_det AS CHECKBOX DEFAULT X. SELECTION-SCREEN COMMENT 30(22) TEXT-004. PARAMETERS p_top TYPE i DEFAULT 500. SELECTION-SCREEN END OF LINE. SELECTION-SCREEN END OF BLOCK b2. DATA: gt_out TYPE STANDARD TABLE OF ty_out, gv_txt TYPE string. INITIALIZATION. gv_txt 库存查询. 按用户上次登录日期给个合理默认区间示例 DATA: lv_datum TYPE usr02-trdat. SELECT SINGLE trdat FROM usr02 INTO lv_datum WHERE bname sy-uname AND trdat 00000000. IF sy-subrc 0 AND lv_datum IS NOT INITIAL. p_date lv_datum. ENDIF. AT SELECTION-SCREEN OUTPUT. LOOP AT SCREEN. IF screen-group1 M1 AND p_werks IS INITIAL. screen-input 0. MODIFY SCREEN. ENDIF. ENDLOOP. AT SELECTION-SCREEN. IF s_matnr[] IS INITIAL AND s_mtart[] IS INITIAL. MESSAGE 物料号和物料类型至少填一个否则查询范围过大 TYPE E. ENDIF. AUTHORITY-CHECK OBJECT M_MSEG_WMB ID WERKS FIELD p_werks ID ACTVT FIELD 03. IF sy-subrc 0. MESSAGE 没有该工厂的查看权限 TYPE E. ENDIF. AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_werks. PERFORM f4_werks. START-OF-SELECTION. PERFORM get_data. PERFORM show_alv.这段骨架里我故意塞了几个细节都是能省事的地方。MEMORY ID让工厂自动带出上次的值MODIF ID m1配合 PBO 做只读控制NO-EXTENSION让库存地点只能填单值NO DISPLAY的字段界面上不显示但代码里能用适合放隐藏的权限控制条件。最后那条 AUTHORITY-CHECK 用的是 MM 模块库存相关的常用授权对象实际对象名按你们项目的权限设计走。注意授权检查放在 PAI 里而不是 PBO 里理由是 PBO 会被反复执行权限检查每次都跑没意义。4.3 把过滤条件下推到数据库接着写取数部分。原则只有一条凡是在选择屏幕上填了的条件必须在数据库层过滤不要捞到内表再 LOOP 里 CHECK。FORM get_data. 处理空值兜底 IF s_lgort[] IS INITIAL. 不填库存地点就是全部OPEN SQL 的 IN 语句对空表视为无条件 ENDIF. SELECT m~matnr, m~mtart, m~meins, d~werks, d~lgort, d~labst INTO TABLE gt_out FROM mara AS m INNER JOIN mard AS d ON d~matnr m~matnr WHERE m~matnr IN s_matnr AND m~mtart IN s_mtart AND d~werks p_werks AND d~lgort IN s_lgort. SORT gt_out BY matnr werks lgort. ENDFORM.这里有几件事要掰开说。IN s_matnr这种写法是 7.40 以后的语法旧版本要把去掉但更重要的是语义OPEN SQL 里对空的 selection table 使用 IN等价于这个条件不存在也就是全选。这是好事也是坏事好处是用户不填就是查全部坏处是用户以为自己填了、实际因为某个原因没生效结果查到天亮。所以我前面在 PAI 里加了那条至少填一个条件的校验。对比一下FOR ALL ENTRIES它的行为和 IN 完全不同驱动内表为空时会报语法运行时错误或者返回奇怪的结果而且它不去重驱动内表有重复值会导致结果翻倍。如果确实要用 FOR ALL ENTRIES务必先CHECK lt_key IS NOT INITIAL再SORT lt_keyDELETE ADJACENT DUPLICATES COMPARING。这一条我在生产事故复盘里见过不止一次。关于 SORT 和 ORDER BY 的取舍数据库排序有它的代价尤其是大结果集而在应用层 SORT 是在内存里做速度通常更快。我的习惯是数据量小几万行以内就在应用层 SORT几十万行以上就交给数据库 ORDER BY同时配合分页或者 TOP N 限制。p_top TYPE i DEFAULT 500那个字段就是干这个的用UP TO p_top ROWS限制返回条数这在探查数据、给业务做快速预览时特别有用。4.4 与变式结合做成日常工具一支报表真正被业务用起来标志是有人开始问能不能保存成我自己的条件。这就是变式的价值。变式的创建入口在 SE38 的程序界面菜单上或者直接按变式维护的热键。命名规则是以字母开头、长度有限制14 位左右建议用有意义的前缀比如Z_MAT_ALL、Z_MAT_ONE。保存的时候会问你全局变式还是仅用户特定的全局的对所有人可见但需要相应权限用户特定的只对自己可见后者更适合个人习惯。变式能存的东西比大多数人以为的多。除了字段的值它还能记录字段的隐藏/只读状态在变式维护界面里对每个字段设置可见性以及一个动态修改的标志位。这个标志位的含义是加载变式时允许屏幕程序通过 AT SELECTION-SCREEN OUTPUT 再次修改屏幕而不是强行按变式里保存的状态显示。如果你既想在变式里隐藏字段、又想在代码里根据情况显示回来就必须勾上它否则代码里改了也不生效——这个坑我调了半个下午才找到原因。变式相关的基础表是 VARI变式头、VARID变式字段值、VARIT变式描述文本做变式的批量管理或者跨系统搬运时很多时候就是在这几张表或者配套函数上做文章。跨系统搬运我一般用传输请求带过去比手工在目标系统重建靠谱。在事务码里挂变式是在 SE93 维护事务码的时候指定起始变式或者用SUBMIT zmm_rpt_stock USING SELECTION-SCREEN Z_MAT_ALL在别的程序里调用。定时作业里也常用这一招作业里调SUBMIT ... VIA SELECTION-SCREEN加变式名业务改条件只要改变式不用动作业配置这在月结期间能救不少人。有一个冲突要提醒如果字段同时用了MEMORY ID和变式加载变式时变式的值优先但从菜单单独启动程序时 SPA/GPA 的值会带出来。两种机制混用会让用户困惑为什么有时候默认值是上次的、有时候是变式的我的做法是正式报表用变式提供默认值MEMORY ID 只用在临时自查的小工具上两者不共存。5. 常见问题与排查实录5.1 问题速查表现象大概率原因处理办法用户改完条件回车后又变回默认值默认值写在 AT SELECTION-SCREEN OUTPUT 里每次 PBO 都重赋一遍默认值统一挪到 INITIALIZATION每次回车都卡一下PBO 里写了 SELECT 或函数调用把取数移到 INITIALIZATION 缓存PBO 只做屏幕逻辑自定义按钮点了没反应判断用了 sy-ucomm或者 USER-COMMAND 漏写改用 sscrfields-ucomm命令码用大写短码字段被隐藏了却仍提示必输用了 OBLIGATORY 附加项去掉 OBLIGATORY改为代码校验或在 PBO 里清 screen-required所有条件留空时查出全表selection table 为空时 IN 条件被忽略PAI 里强制要求至少填一个范围条件FOR ALL ENTRIES 结果条数对不上驱动内表有重复值或为空先判空再 SORT DELETE ADJACENT DUPLICATESLOOP AT SCREEN 过滤不到字段MODIF ID 的组标记大小写不匹配统一用大写过滤时比对 SCREEN-GROUP1变式里的隐藏设置不生效没勾动态修改标志在变式属性里勾上或改由代码全权控制输入的小写字母被自动转成大写缺 LOWER CASE 附加项对文本类字段补上该附加项变式改了描述文字但用户还看到旧的文本缓存或翻译未同步让用户重新登录检查文本符号的翻译状态勾了复选框切换单选却要按两次回车单选组没配 USER-COMMAND给单选组加 USER-COMMAND触发即时 PAI5.2 几个我踩过的坑第一个坑是关于 OBLIGATORY 和动态隐藏的组合。有个报表有个按日期查的单选按钮选中后才显示日期字段。日期字段加了 OBLIGATORY结果用户在没选该模式时只要一按回车就被提示日期必输因为 OBLIGATORY 是屏幕级的静态约束不管你隐藏没隐藏。最后的解法是去掉 OBLIGATORY在 AT SELECTION-SCREEN 里判断IF r_date X AND p_date IS INITIAL再报错。这个教训我后来固化成了习惯凡是可能被动态隐藏的字段一律不用 OBLIGATORY。第二个坑是选择屏幕里嵌 ALV。有段时间我想做一个边选条件边看预览的效果在 PBO 里创建 ALV 容器结果每次回车都试图重新创建一个容器对象报了一堆运行时错误。这个场景可行但必须在全局加一个标志变量只在第一次 PBO 时创建容器和 ALV 实例后续只刷新数据。而且选择屏幕里的 ALV 涉及自定义容器和屏幕号码的处理复杂度不低不是强需求真没必要做一个单独的展示页面反而更清晰。第三个坑和权限有关。有一支报表给不同地区的同事用我原打算用一个隐藏的 SELECT-OPTIONS 字段在代码里拼权限过滤条件字段加了NO DISPLAY。结果发现用户仍然能用变式把隐藏字段的值带进来或者用 SUBMIT 的时候传参。所以权限过滤绝对不能依赖字段看不见必须用 AUTHORITY-CHECK 在代码里做硬校验隐藏字段只是为了防止误操作。这个思路在给标准事务做增强、加自定义字段时同样适用具体出口在哪个增强点得根据模块去 SMOD/CMOD 里查对应的组件不能想当然。还有一条经验是关于日期范围的。用户非常喜欢把日期区间填得巨大比如从系统上线那天查到现在。这时候与其直接跑不如在 PAI 里算一下区间跨度超过阈值就弹一个确认消息让用户确认知道自己在查大范围。这个提示看着简单但它把程序慢的责任从开发身上转移到了用户的操作选择上用户心里有数就不会来投诉。5.3 调试选择屏幕的实用技巧选择屏幕的调试比普通程序烦因为事件触发的时机不直观我攒了几个手法。最基础的是用/h进入调试但这个办法在选择屏幕上有讲究在命令框输入/h然后敲回车进入调试之后每次回车会在 PAI 处停下你能看到SSCRFIELDS-UCOMM和SY-UCOMM的当前值判断按钮命令是不是按预期传出来的这一招排查按钮问题特别快。如果要在 LOOP 中间停下来看数据不要在循环里干等用 TABIX 做条件断点LOOP AT gt_out ASSIGNING FIELD-SYMBOL(fs). IF sy-tabix 100. BREAK-POINT. ENDIF. ENDLOOP.这个老办法到现在还是最快的方式改一个数字就能定位到想要的迭代次数。新版本调试器里也支持断点跳过 N 次和带条件的语句断点但每次都要在调试器界面里点来点去反而不如直接改代码快而且调试完还要记得删掉。第三个手法是看屏幕事件到底走没走。在选择屏幕相关的事件里加一句给全局变量赋值的语句比如gv_dbg gv_dbg 1然后在 START-OF-SELECTION 里把值打出来能直观看到 PBO 跑了几次、PAI 进了几次。排查为什么这段校验不生效时非常有效——往往你会发现是因为事件压根没进而不是逻辑写错了。最后一个是我个人特别喜欢的给选择屏幕加一个只在开发环境显示的技术块用MODIF ID加sy-sysid判断里面放是否启用详细日志是否禁用缓存这类开关。测试阶段调试起来快得多上生产时这个块因为系统 ID 不匹配自动隐藏不需要改代码或者维护两套版本。6. 一些关于扩展方向的个人想法选择屏幕这块再往下走有几个方向值得花时间。一个是把它和 Fiori 的过滤栏做对照理解两者的数据结构其实很像理解了 selection table 的 SIGN/OPTION/LOW/HIGH看 Fiori 的过滤条件绑定就会觉得眼熟做 ABAP 到 Fiori 的迁移时能省不少力。另一个方向是把常用条件的组合做成动态选择屏幕用FREE_SELECTIONS_INIT和FREE_SELECTIONS_DIALOG让用户自己挑字段适合那种谁都想查不同维度的分析类报表代价是布局和校验都不好控制得权衡。我个人在实际操作中的体会是选择屏幕写得好的报表代码本身通常也写得干净因为逼着你先把数据范围想清楚而反过来选择屏幕随便糊的报表后面大概率是层层嵌套的 LOOP 和一堆 IF改起来像拆炸弹。所以每次开新报表我都会先花二十分钟把选择屏幕的字段、布局、默认值、校验、变式规划好这二十分钟基本能从后面的返工里省回来。还有一个习惯是程序里凡是涉及屏幕动态控制的判断我都写成独立的小 FORM 或者类方法而不是散在 PBO 里下次复用直接抄过去就行——屏幕逻辑越集中后面维护的人越少骂你。
返回列表