ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

SAP ABAP单位内外码转换:原理、函数与实战应用详解

SAP ABAP单位内外码转换:原理、函数与实战应用详解 1. 项目概述SAP单位内外码转换的来龙去脉在SAP的ABAP开发世界里有一个看似不起眼却无处不在的“小”问题单位内外码转换。如果你做过物料主数据、采购订单、生产订单或者任何涉及数量、重量、长度等带单位的数据处理几乎百分百会碰到它。简单来说SAP为了在数据库里高效存储和保证数据一致性单位比如“PC”、“KG”、“M”在数据库里存的并不是我们看到的“PC”这三个字母而是一个内部的、通常是数字形式的代码。这个内部代码就是“内码”而我们业务人员熟悉的“PC”、“KG”就是“外码”或“显示码”。我刚接触ABAP时就曾在这个问题上栽过跟头。有一次写一个报表直接从MSEG物料凭证表里把MENGE数量和MEINS单位拖出来显示结果用户一看就懵了“为什么单位栏是空的或者显示一堆数字” 这就是典型的只取了内码没有转换成外码。反过来如果你要往数据库里写数据比如通过BAPI或BDC创建一张采购订单你传一个“PC”进去系统很可能报错因为它期待的是内码。这个内外码转换的机制是SAP数据一致性的基石之一理解它是ABAPer从“写代码的”迈向“懂业务的”关键一步。2. 核心原理为什么SAP需要内外码转换要彻底搞懂转换得先明白SAP这么设计的原因。这绝不是脱裤子放屁而是有深刻的考量。2.1 设计初衷效率与一致性的权衡想象一下如果SAP在所有表中都直接存储“KG”、“M”、“EA”这样的字符串会怎样首先存储空间浪费。一个3字节的字符串用一个2字节的数字代码T006A-MSEHI就能唯一标识在动辄几十亿条记录的生产系统里这个节省是巨大的。其次也是更重要的数据一致性。如果允许直接存字符串那么“KG”、“kg”、“Kg”算不算同一个单位用户在界面上手误输了一个“KGS”怎么办通过强制使用内码SAP确保了在整个系统范围内同一个物理单位只有一个唯一的标识符。所有对单位的引用都指向这个标识符从根源上杜绝了歧义。这个内码就存储在T006A这个核心配置表中。T006A表里的MSEHI字段就是单位的内码一个数字或短字符串而KURZTEXT字段就是对应的外码我们看到的单位描述。T006表则存储了更详细的技术信息。2.2 转换发生的关键场景转换无处不在主要发生在两个方向输出OUTPUT从数据库读取数据到界面ALV、报表、屏幕或输出文档Smart Forms, Adobe Forms时需要将内码转为外码。这是为了让人能看懂。输入INPUT从界面、外部接口IDoc, BAPI, RFC或批导程序向数据库写入数据时需要将外码转为内码。这是为了让系统能正确处理。几乎所有SAP标准表里涉及单位的字段存的都是内码。比如MARA-MEINS物料基本单位、EKPO-MEINS采购订单单位、MSEG-MEINS物料凭证单位。3. 核心函数深度解析CONVERSION_EXIT_CUNITABAP提供了专门处理单位转换的函数组Function GroupCUNIT。其中最常用的两个函数模块就是CONVERSION_EXIT_CUNIT_INPUT和CONVERSION_EXIT_CUNIT_OUTPUT。很多人只知道用却不清楚里面的门道。3.1 CONVERSION_EXIT_CUNIT_OUTPUT内码转外码这个函数负责把内部单位代码转换成外部显示单位。函数签名与参数解析CALL FUNCTION CONVERSION_EXIT_CUNIT_OUTPUT EXPORTING input lv_internal_unit 输入内部单位代码如 EA language sy-langu 可选语言默认当前登录语言 IMPORTING output lv_external_unit 输出外部单位如 PC EXCEPTIONS unit_not_found 1 others 2.input参数这里有个最常见的误区虽然参数名是input文档也写“internal unit”但它期望的输入格式是已经符合SAP内部格式的代码。对于从数据库表直接取出的字段如MARA-MEINS它本身就是内部格式可以直接传入。但如果你有一个字符串变量里面是ST你需要先确保它是有效的内码。language参数这个参数非常关键却常被忽略。单位的外码描述是依赖于语言的。sy-langu是当前用户的登录语言。如果你的报表是给多语言用户使用的或者需要在后台作业中固定输出某种语言如英语‘EN’就必须显式指定这个参数。不指定则默认用sy-langu。异常处理unit_not_found异常非常重要。如果你传入了一个系统中不存在的单位内码函数会抛出这个异常。在健壮性要求高的程序里必须捕获并处理它否则会造成程序转储DUMP。常见的处理方式是给一个默认值如空字符串或记录错误日志。实操心得在ALV报表中我们通常不会对每个单位字段单独调用这个函数。更高效的做法是在字段目录Field Catalog中为单位的字段设置CONVEXIT属性。例如ls_fcat-fieldname ‘MEINS‘. ls_fcat-convexit ‘CUNIT‘. “ 关键告诉ALV这个字段需要用CUNIT转换出口 APPEND ls_fcat TO lt_fcat.这样ALV在渲染时会自动调用转换出口无需在数据准备阶段手动转换每一行数据性能更好代码也更简洁。3.2 CONVERSION_EXIT_CUNIT_INPUT外码转内码这个函数负责把用户输入或外部系统传入的单位字符串转换成SAP内部代码。函数签名与参数解析CALL FUNCTION CONVERSION_EXIT_CUNIT_INPUT EXPORTING input lv_external_unit 输入外部单位字符串如 ‘PC‘, ‘PCS‘ language sy-langu 可选语言用于解析语言相关的描述 IMPORTING output lv_internal_unit 输出内部单位代码 EXCEPTIONS unit_not_found 1 others 2.input参数这里接受的是外码。但它的“聪明”程度超乎你的想象。它不仅能处理标准的单位代码‘PC’还能处理单位描述‘Piece’甚至是常见缩写‘PCS’。其内部逻辑会去查询T006A等表进行匹配。模糊匹配与风险正是因为它支持模糊匹配所以带来了风险。例如用户输入“M”它可能匹配到“M”米还是“MIN”分钟这取决于系统配置和语言。在关键业务场景如创建生产订单建议在转换前增加一道校验提供一个F4帮助值帮助让用户选择而不是完全依赖自由输入后的自动转换。异常处理同样必须处理unit_not_found。在BDC或接口程序中如果传入了一个非法单位通过捕获此异常可以给用户一个友好的错误提示而不是让整个事务中断。一个常见的坑在BAPI调用中比如BAPI_PO_CREATE1其结构POITEM中的PO_UNIT字段要求的就是外码。很多新手从数据库里取出内码直接赋值给这个字段然后调用BAPI结果BAPI内部又去调用CONVERSION_EXIT_CUNIT_INPUT转换因为内码如‘EA’很可能不是一个有效的外码字符串导致转换失败BAPI报错。正确的做法是如果你手头是内码先用CONVERSION_EXIT_CUNIT_OUTPUT转成外码再传给BAPI。4. 实战应用场景与代码示例光说不练假把式我们来看几个实际开发中一定会遇到的场景。4.1 场景一自定义报表中的单位显示假设我们要开发一个物料库存清单报表ZMM_STOCK_REPORT从MARD库存地点库存和MARA中取数。DATA: lt_data TYPE TABLE OF ty_data, ls_data TYPE ty_data. TYPES: BEGIN OF ty_data, matnr TYPE mara-matnr, “ 物料号 maktx TYPE makt-maktx, “ 物料描述 labst TYPE mard-labst, “ 非限制库存 meins TYPE mara-meins, “ 单位内码 meins_ext TYPE string, “ 单位外码-备用 END OF ty_data. SELECT a~matnr, b~maktx, c~labst, a~meins FROM mara AS a INNER JOIN makt AS b ON a~matnr b~matnr AND b~spras sy-langu INNER JOIN mard AS c ON a~matnr c~matnr INTO TABLE DATA(lt_raw_data) WHERE c~werks ‘1000‘ AND c~lgort ‘0001‘. LOOP AT lt_raw_data ASSIGNING FIELD-SYMBOL(fs_raw). MOVE-CORRESPONDING fs_raw TO ls_data. “ 方法1直接为ALV字段设置转换出口推荐 “ 这里不需要手动转换ls_data-meins保持内码即可。 “ 方法2手动转换如需在代码逻辑中使用外码 CALL FUNCTION ‘CONVERSION_EXIT_CUNIT_OUTPUT‘ EXPORTING input fs_raw-meins IMPORTING output ls_data-meins_ext EXCEPTIONS unit_not_found 1 OTHERS 2. IF sy-subrc 0. ls_data-meins_ext ‘‘. “ 转换失败置空 ENDIF. APPEND ls_data TO lt_data. ENDLOOP. “ 在ALV字段目录中为MEINS字段设置属性 ls_fcat-fieldname ‘MEINS‘. ls_fcat-ref_table ‘MARA‘. ls_fcat-ref_field ‘MEINS‘. ls_fcat-convexit ‘CUNIT‘. “ 核心设置 APPEND ls_fcat TO lt_fcat.注意在简单的显示报表中强烈推荐使用方法1设置convexit。它不仅性能更优而且能自动遵循用户的语言设置。方法2适用于需要在代码中判断单位内容进行后续逻辑处理的场景。4.2 场景二通过BAPI创建采购订单行项目当我们需要用ABAP程序自动创建采购订单时处理单位是关键一步。DATA: ls_poitem TYPE bapimepoitem, lv_unit_ext TYPE bapimepoitem-po_unit. “ 假设我们从某个自定义配置表或界面获取了物料和单位 DATA(lv_matnr) ‘MAT-001‘. DATA(lv_unit_input) ‘PCS‘. “ 用户输入或外部系统传入的是‘PCS’ “ 1. 将外码转换为内码用于可能的其他检查或存储 CALL FUNCTION ‘CONVERSION_EXIT_CUNIT_INPUT‘ EXPORTING input lv_unit_input IMPORTING output DATA(lv_unit_int) EXCEPTIONS unit_not_found 1 OTHERS 2. IF sy-subrc 0. MESSAGE e398(00) WITH ‘单位‘ lv_unit_input ‘不存在‘. RETURN. ENDIF. “ 2. 验证该物料的基本单位是否匹配可选但重要 SELECT SINGLE meins FROM mara INTO DATA(lv_base_unit) WHERE matnr lv_matnr. IF lv_base_unit IS NOT INITIAL AND lv_unit_int lv_base_unit. “ 这里可能需要调用额外的转换函数如MD_CONVERT_MATERIAL_UNIT检查单位换算可行性 “ 或者直接报错要求使用基本单位 ENDIF. “ 3. 为BAPI结构赋值。注意BAPI_PO_CREATE1的PO_UNIT字段需要外码 ls_poitem-material lv_matnr. ls_poitem-po_unit lv_unit_input. “ 这里直接使用原始外码或转换后的外码 “ 如果上一步转换得到的内码是‘EA’而BAPI需要‘PC’则需要再转一次 CALL FUNCTION ‘CONVERSION_EXIT_CUNIT_OUTPUT‘ EXPORTING input lv_unit_int IMPORTING output lv_unit_ext. ls_poitem-po_unit lv_unit_ext. “ 4. 填充其他必要字段然后调用BAPI ls_poitem-pur_quan 100. ls_poitem-po_price ‘10.50‘. “ ... 调用 BAPI_PO_CREATE1 ...这个例子清晰地展示了在数据流转过程中单位代码可能需要在内外码之间多次转换取决于你操作的API或数据库表的具体要求。4.3 场景三增强与校验中的单位处理在用户出口User Exit、BADI或增强点Enhancement Point中我们经常需要校验用户输入的单位。例如在物料主数据MM01的保存前校验增强中METHOD if_ex_material_check~check_data. DATA: lv_unit_int TYPE t006a-msehi. “ cs_mara 是检查时传入的物料数据包含用户输入的单位MEINS此时可能还是外码格式 IF cs_mara-meins IS NOT INITIAL. “ 尝试将输入的单位转换为内码 CALL FUNCTION ‘CONVERSION_EXIT_CUNIT_INPUT‘ EXPORTING input cs_mara-meins IMPORTING output lv_unit_int EXCEPTIONS unit_not_found 1 OTHERS 2. IF sy-subrc 0. “ 单位不存在报错 MESSAGE e603(fi) WITH cs_mara-meins. “ 使用标准消息 ELSE. “ 转换成功将内码存回结构确保后续处理一致 cs_mara-meins lv_unit_int. “ 这里可以添加自定义校验比如禁止使用某些单位 IF lv_unit_int ‘MIN‘ AND cs_mara-mtart ‘FERT‘. “ 成品物料不允许用‘分钟’为单位 MESSAGE e398(00) WITH ‘成品物料不能以分钟为单位‘. ENDIF. ENDIF. ENDIF. ENDMETHOD.在增强中主动进行转换和校验可以提前拦截错误数据保证业务数据的纯洁性。5. 高级话题与性能优化当数据量巨大时单位转换也可能成为性能瓶颈。我们需要更高效的策略。5.1 批量转换与缓存机制在LOOP中逐行调用转换函数是性能杀手。对于大数据量报表应该使用批量读取和缓存。DATA: lt_mara TYPE TABLE OF mara, lt_t006a TYPE TABLE OF t006a, lv_unit_int TYPE t006a-msehi. “ 1. 一次性读取所有需要的物料数据 SELECT matnr, meins FROM mara INTO TABLE lt_mara UP TO 10000 ROWS WHERE matnr IN s_matnr. IF lt_mara IS NOT INITIAL. “ 2. 收集所有不重复的单位内码 DATA(lt_distinct_meins) VALUE string_table( FOR GROUPS grp OF ls IN lt_mara GROUP BY ( meins ls-meins ) ( grp-meins ) ). “ 3. 批量读取单位主数据建立内码-外码的映射表 SELECT msehi, kurztext FROM t006a INTO TABLE lt_t006a FOR ALL ENTRIES IN lt_distinct_meins WHERE msehi lt_distinct_meins-table_line AND spras sy-langu. SORT lt_t006a BY msehi. ENDIF. “ 4. 在循环中使用缓存避免重复调用函数 LOOP AT lt_mara ASSIGNING FIELD-SYMBOL(fs_mara). READ TABLE lt_t006a WITH KEY msehi fs_mara-meins BINARY SEARCH ASSIGNING FIELD-SYMBOL(fs_t006a). IF sy-subrc 0. “ 使用 fs_t006a-kurztext 作为外码 ELSE. “ 单位未找到按空处理 ENDIF. ENDLOOP.这种方式将N次函数调用每次都有开销转换为1次数据库查询和N次内存表查找性能提升是指数级的。5.2 自定义单位的转换出口有时业务会要求使用SAP标准之外的单位比如“箱”、“包”。虽然SAP建议通过维护单位换算关系来实现但在某些简单场景下开发人员可能会想创建自定义的转换出口。强烈不建议这么做自定义转换出口会破坏SAP的标准逻辑导致与后续标准功能如物料需求计划、成本核算不兼容维护起来是噩梦。正确的做法是使用标准单位尽可能映射到标准单位如‘EA’。维护换算关系通过事务代码CUNI为物料维护特定单位的换算公式如 1 箱 12 个。使用替代单位在采购或销售中使用订单单位PO_UNIT/SALES_UNIT与基本单位BASE_UOM的换算。5.3 与ALV、Smart Forms等输出技术的集成除了前面提到的ALV字段目录CONVEXIT属性在其他输出技术中Smart Forms / SAPscript在文本元素的“格式”选项中可以直接为字段指定转换例程CUNIT。Adobe Forms在数据字典中定义字段时可以分配转换出口。在Form Builder中绑定字段后转换会自动生效。OLEExcel输出如果你用OLE2或ALSM_EXCEL_TO_INTERNAL_TABLE这类方式直接写ExcelSAP的转换出口是不会自动调用的。你必须在ABAP端手动调用CONVERSION_EXIT_CUNIT_OUTPUT将转换好的外码字符串写入Excel。6. 常见问题排查与调试技巧即使理解了原理实战中还是会遇到各种怪问题。下面是一些常见问题的排查清单。问题现象可能原因排查步骤与解决方案报表中单位显示为空白或数字代码1. 未设置转换出口。2. 单位内码在T006A中无对应当前语言的外码。1. 检查ALV字段目录的CONVEXIT属性是否设置为CUNIT。2. 用SE16N查看T006A表用该内码和登录语言sy-langu查询看KURZTEXT是否存在。BAPI或BDC报错“单位XX无效”1. 传入的单位是内码而非外码。2. 单位在系统中未维护。1. 用CONVERSION_EXIT_CUNIT_OUTPUT将内码转成外码再传入。2. 用CONVERSION_EXIT_CUNIT_INPUT测试该字符串是否能被系统识别或用SU3检查单位是否在用户参数中维护了默认值。转换函数CONVERSION_EXIT_CUNIT_INPUT抛出UNIT_NOT_FOUND1. 输入字符串完全错误。2. 输入的是描述但语言不匹配。3. 单位被标记为删除。1. 使用事务代码CUNI(维护单位)检查该单位是否存在且有效。2. 尝试用英语‘EN’等语言测试。3. 检查T006A表中该条目的LOEVM删除标志是否为空。同一单位在不同事务中显示不同1. 物料特定单位与通用单位混淆。2. 输出时语言上下文不同。1. 明确区分物料基本单位(MARA-MEINS)和订单单位(EKPO-MEINS)。它们可能不同但通过换算关联。2. 检查转换函数调用时是否显式指定了language参数确保一致性。性能问题大批量数据转换慢在循环中逐行调用转换函数。采用第5.1节所述的批量读取与缓存方案将数据库查询次数降至最低。调试技巧当转换逻辑复杂或出错时最有效的调试方法是使用ABAP调试器。在调用CONVERSION_EXIT_CUNIT_INPUT/OUTPUT的地方设置断点。进入函数内部观察其是如何调用子函数如UNIT_OF_MEASUREMENT_STR_TO_INT以及查询哪些表通常是T006A,T006的。检查传入参数的值和类型确保与函数期望的完全一致。很多时候问题就出在一个肉眼难以察觉的前导或尾部空格上。7. 总结与最佳实践单位内外码转换是SAP ABAP开发中的基础必修课。处理得好代码稳健流畅处理不好处处是坑。回顾一下最重要的几点时刻明确“内外”上下文在从数据库读数据时你拿到的是内码在向用户展示或调用某些BAPI时你需要外码在接收用户输入或处理接口数据时你很可能拿到的是外码需要转内码。在写每一行相关代码前先想清楚数据处于哪个阶段。善用标准机制避免重复造轮子ALV的CONVEXIT、Smart Forms的格式设置都是SAP提供的自动转换机制。在合适的场景使用它们能简化代码并提升性能。异常处理必不可少永远不要假设转换一定会成功。UNIT_NOT_FOUND异常必须被捕获和处理给出清晰的错误信息这是程序健壮性的体现。性能考量面对大数据量忘掉在循环里调函数的方式。采用批量预读取和建立内存缓存映射表是专业开发者的标志。尊重标准谨慎自定义除非有压倒性的理由并获得架构师批准否则不要动自定义转换出口的念头。通过维护标准的单位换算关系来满足业务需求是唯一可持续的道路。最后我个人习惯在项目的公共工具类ZCL_XXX_UTILITY中封装一个单位转换的方法统一处理异常和日志。这样所有开发人员都通过这个统一入口进行转换保证了行为的一致性也便于日后维护和优化。例如可以在这个方法里加入对单位缓存的逻辑无形中提升了整个系统的性能。把这些细节做到位你的ABAP程序在稳定性和专业性上自然会高出一个档次。
返回列表