![[SAP ABAP开发技术总结]数据输入输出转换、小数位/单位/货币格式化 [SAP ABAP开发技术总结]数据输入输出转换、小数位/单位/货币格式化](https://image.miaokee.com:8440/aHR0cDovL2ltYWdlcy5jbmJsb2dzLmNvbS9jbmJsb2dzX2NvbS9qaWFuZ3poZW5nanVuLzY1NTYxNC9vXyVFNyVCQiVCRjIuZ2lm.gif?w=700&webp=1)
15.1.3. 通过转换规则输入输出函数手动转换... 124
15.3. 单位换算:UNIT_CONVERSION_SIMPLE. 128
15. 数据格式化、转换
15.1. 数据输入输出转换
15.1.1. 输出时自动转换
如果某个变量参照的数据元素所对应的Domain具有转换规则,那么在输出时(如Write输出、ALV展示、文本框中显示),最后显示的结果会自动发生转换,如参照 ekpo-meins 表字段的变量赋值时就会发生转换,因为 ekpo-meins 所对应的元素Doamin设置了转换规则:
所以,在显示输出这样的数据时要注意,如果要显示原始数据,则不能参照该表字段来定义变量,而是自己定义。
DATA: i_meins3 LIKE ekpo-meins.
"注:这里只能是内部单位ST,而不是PC,因为Write时是输出转换(即内->外的转换)
i_meins3 = 'ST'.
"只要是参考过 ekpo-meins 的变量,Write输出时自动转换
WRITE:/ i_meins3.
在调试过程中发现都是原始数据,自动转换发生在Write输出时:
15.1.2. 输入时自动转换
输出时会发生自动转换,那么,在输入时,如从选择屏幕上录入的数据是参照带有规则转换的Domain的数据元素创建的选择屏幕字段时,从界面录入到ABAP程序中时,会自动按照转换规则进行转换,如下面从界面上输入的是 PC (外部格式的单位),但录入到ABAP程序中时,自动转换为ST(内部格式的部位),但再次Write输出时,又将 ST转换为PC输出(从内部转换为外部格式):
15.1.3. 通过转换规则输入输出函数手动转换
除了上面通过借助于参照带有转换规则的表字段进行自动转换外,实质上可以通过转换规则对应的输入输出函数进行手动转换,如VBAK-vbeln的转换规则:
CONVERSION_EXIT_ALPHA_INPUT:输入转换,前面补齐零
此函数将字符类型的变量转换成SAP数据库中内部格式数据,如定单号vbeln的类型为 Char 10,如果输入的vbeln为6位,则会在前面补4个零(注:该函数的转换规则为:如果含有其他非数字,则不会补零,只有全部是数字时才补,这可以通过VBELN查看到),Number类型的不需要,因为在ABAP程序中N类型不足时长度时默认就会在前面补零(如 POSNR),而且Number类型的默认值就是全为零,而C类型不足时会以后面全补空格
CONVERSION_EXIT_ALPHA_OUTPUT:输出转换,去掉前导零
DATA: vbeln TYPE vbak-vbeln.
DATA: str TYPE string VALUE '600000'.
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
EXPORTING input = str
IMPORTING output = vbeln.
"自动输出转换,输出最初始数据,但程序内部已发生变化
WRITE: / vbeln."600000
15.2. 数量小位数格式化
该语句根据Unit <u>来设置<f>的小数位数(即保留小数点多少位,或精确到小数点后多少位),<u>为<f>的单位。<u>必须要在T006中进行过配置,并且<u>的值(单位KEY值)就是T006-MSEHI字段值,而T006-DECAN字段值决定<f>显示的小数位数,如果<u>在表T006中没有找到,将会忽略该UNIT选项
该选项的使用限制如下:
? <f>必须是P类型的
? 如果<f>本身的小数位比<u>所配置的小数位少时,系统会忽略该选项
? 如果<f>本身的小数位比<u>所配置的要多时,并且多余的小数位全部是零时,会被截断;如果多余的小数部分不是零时,也会直接忽略该选项
从上面的限制条件来看,该格式化输出只针对<f>的小数位超过了其单位<u>设置的小数位,且超过的小数要全是零才会起作用(去掉多余的零),如果<f>的小数位短于<u>设置的小数位,也不会再补后输出
.
p2 = '1.1000'.
"多余的小数位全部是零时,会被截断
WRITE:/ p2 UNIT 'D10'."1.100
p2 = '1.1001'.
"多余的小数部分不是零时,也会直接忽略该选项
WRITE:/ p2 UNIT 'D10'."1.1001
DATA: i_menge LIKE ekpo-menge VALUE '1.000'.
"注:UNIT选项后面一定要是内部单位ST,而不是外部单位PC,因为这里是WRITE输出,
"即内部转换外部,将数据库表存储的原数据格式化输出显示
WRITE: / i_menge UNIT 'ST'."1
WRITE: / i_menge."1.000
15.2.1. 案例
问:通过se11 我们可以看到ekpo中menge的数据元素是BSTMG,BSTMG的域是长度13小数位3位。在程序中我参照ekpo-menge定义的变量显示的时候后面都有3位小数,而我希望输出时与me23n一样,即去掉小数点后面多余的零,请问大侠们有没有比较好的办法。为什么me23n中“PO数量”显示的时候没有多余的零,而他们的数据元素是一样的。
答:MENGE实际上是个存储度量衡值的字段,他的基本数据类型是QUAN,他的小数位数并不是你看到的3,而是由这个字段关联的度量衡单位决定的,以MENGE为例,你可以在SE11的最右边一个Tab页,Currency/Quantity Fields里看到,他关联的单位是EKPO-MEINS
. "没有参照表字段ekpo-meins,所以Write输出时不会自动输出转换
SELECT menge meins meins FROM ekpo INTO(i_menge,i_meins,i_meins2) WHERE ebeln = '4500012164'.
"带单位的数量需要根据单位进行格式化输出,这样才与ME23N 中显示的数据一样
WRITE: / i_menge UNIT i_meins,i_meins, i_menge,i_meins2.
ENDSELECT.
在ALV中显示时,如果是金额或数量时,需通过Fieldcat设置cfieldname 、ctabname ;qfieldname、qtabname这样在显示时才会正确
也可直接使用Domain所配置的转换规则所对应的输入输出转换函数CONVERSION_EXIT_CUNIT_INPUT、 CONVERSION_EXIT_CUNIT_OUTPUT来手动对单位进行转换:
15.3. 单位换算:UNIT_CONVERSION_SIMPLE
.
CALL FUNCTION 'UNIT_CONVERSION_SIMPLE'
EXPORTING
input = p_in
round_sign = round"舍入方式(+ up, - down, X comm, SPACE.)
unit_in = unit_in
unit_out = unit_out
IMPORTING
output = result.
WRITE: 'Result: ',result.
15.4. 货币格式化
WRITE <f> CURRENCY <c>.
输出金额<f>时,会根据该语句设置的货币代码<C>来决定其小数位置,如果货币代码<c>在表TCURX(CURRKEY)表中存在,则系统将根据TCURX-CURRDEC字段的值来设置<f>的小数点的位置,否则将<f>转换成具有2位小数的数字。这就意味着除非<f>本身就是类型为P(.2)(即货币的最大单位与最小单位换算为100时,如CNY人民币、USD美元)的金额字段,否则需要在TCURX表中配置所对应币种的小数位(因为不配置时会采用默认的2位)。
注意:这里的<f>一般是从数据库里读取出来的金额数据才需要这样格式化输出的,如果<f>本身存储的就是真实的金额,则不需要格式再输出,而是直接输出;另外,这里的格式化只是简单机械的根据TCURX-CURRDEC所配置的小数位置来设置金额的小数点位置(而并不是乘以或除以某个转换率),并与金额变量<f>类型本身的具有多少小数位有关:如果<f>的类型为P(6.5),值为<f> = 1.234时,且TCURX表里配置的小数位为2时,最后输出的是 1234.00 ,而不是12.34(如果是根据转换率来除,则结果会正确),因为在格式化前,会将小数末的0(1.23400)也参与处理,并不理会<f>本身原来的小位数,而是将所有的数字位(抛开小数点,但包括末尾的0)看作是待格式会的数字字符串:
.
p = '1.234'.
WRITE: p CURRENCY 'aa'."1,234.00
TCURX:货币小数位表
TCURC:货币代码表
TCURR:汇率表
SAP表里存储的并不是货币的最小单位,一般是以货币最大单位(也是常用计量单元)来存储,不过在存储之前会使用经过转换:比如存储的金额是 100,则存储到表之前会除以一个转换因子后再存入数据表中(该转换因子是通过CURRENCY_CONVERTING_FACTOR函数获得的,如比CNY的转换因子为1,JPY为100),所以如果要读取出来自已进行展示,则需要再次乘以这个因子才能得到真正的金额数。另外,数据库中存储的虽然不是最小单位,但取出来后都是放在P类型的变量中的,所以取出来在内存中统计是不会有精度丢失的(P类型相当于Java中的BigDecimal类类型)。
TCURX-CURRDEC中存储的小数位实质上是根据同种币种的最大单位与最小的换算率= 10X来计算得到的,式中的X即TCURX-CURRDEC表字段中的小数位,如CNY中的最大单位元与最小单位分相差100倍,所以100 = 10X,X就为2,最后TCURX-CURRDEC存储的就是2(是可以不需要在TCURX表中配置的,所以查不到CNY的配置数据,因为不配置时默认值也是2);另外,JPY日元没有最小单位,所以最大单位与最小单位的换算率就是1(1 = 10X),所以X就为0,所以TCURX-CURRDEC存储的就是0。而转换因子计算式为:转换因子 = 100/10X,(CNY人民币:100/10X=100/102 =1,JPY日元:100/10X=100/100 =100),即转换因子 = 100/货币的最大单位与最小单位换算率,金额入库时需要除以这个转换因子,读取出来展示前需要乘以这个转换因子
数据库中用来存储金额的字段的类型都是P(.2),即带两位小数,因为转换因子最大也就是100(除以100后,即为小数点后2位),有的是零点几(在存入之前会将真实金额除以这个转换因子后再存入),所以存储类型为两位小数的数字类型即可。ABAP程序中用来存储从表中读取出来的内部金额的变量类型一定要具有两位类型的,否则在使用诸如CONVERT_TO_LOCAL_CURRENCY、CONVERT_TO_FOREIGN_CURRENCY转换函数或者是格式化输出时,都会有问题,所以在ABAP程序中定义这些用来存储数据库表中所存内部金额变量时,最好参照相应词典类型。
15.4.1. 从表中读取日元并正确的格式化输出
倍,所以要还原操作界面上输入的日元金额,则需要使用后面的格式化输出
倍
倍,因为在存入表中时缩小了100倍
倍(因为多了一位小数位)。所以格式化正确输出的前提是要用来接收从表中读取的金额变量的类型要与数据表相应金额字段类型相同,否则格式化输出会出错
netpr1 = netpr.
WRITE: / netpr1, netpr1 CURRENCY waers."格式化的结果是错误的
15.4.2. SAP 货币转换因子
一般而言,币种的小数位为2,所以系统默认的位数也是2,但是有一些特殊币种如日元JPY,没有小数位。只要小数位不等于2,需要在系统中特殊处理(通过转换因子进行转换,具体请参看后面SAP提供的函数 currency_converting_factor 实现过程)。在编程中
l List中,当输出CURR字段时,记得指定对应的货币:
如:WRITE: vbap-netwr CURRENCY vbap-waerk.
l Screen中,对于CURR字段,需要设置对应的货币字段:
l ALV中,需要对FIELD CATALOG进行设置
如:ls_cfieldname = 'WAERS'. "这里的WAERS是内表中的另一货币字段,里面存储了相应金额的货币代码
货币的是:fieldcat-cfieldname、fieldcat-ctabname(内表名,可以不设置)
顺便数量也是相似的方法来处理的:
数量的是:fieldcat-qfieldname、fieldcat-qtabname(内表名,可以不设置)
下面是SAP转换因子函数,在金额存储与在ALV展示时都会自动除以与乘以这个转换因子:
就报错
。如果表tcurx中的currdec = 0就默认转换比率为100
cur_factor 的情况下循环currdec次,每次将转换比率除以10
.
*- factor 0 not allowed; check data definition of factor
*- entry in tcurx with more than 5 decimals not allowed
RAISE too_many_decimals.
ENDIF.
factor = cur_factor.
ENDFUNCTION.
简单的使用Function CURRENCY_CONVERTING_FACTOR,输入币种,就可以得到相应的转换比率了。我们在SE16中看到的货币金额基本上都经过了这个转换,如日元,都是除以100后存入数据库的。所以当我们从数据库中读取日元金额时也应该作相应的转换,乘以100 。
1、如果某货币的小数位不是2位,则需要通过OY04设置其小数位数,即需在TCURX表中进行维护
2、系统中的数据表存放的日元JPY、俄卢布RUR等货币比前台输入的金额小100倍,因为它们没有小数位,所以转换因子为100,存入表之前SAP会先将金额除以这个因子后再存入
3、系统根据转换因子将原金额转换成含小位小数的金额后存储(据说根据ISO的什么标准),如日元为0位小数,转换因子为100,120日元除以因子100后转换后变成1.20,缩小100倍。如为USDN为5位小数,其转换因子为100/10/10/10/10/10=0.001,12.01230除以0.001后则转换成12012.30,扩大1000倍。SAP在金额数据存储时会自动的转换,其实SAP是有external及internal的数据格式,可以调用以下函数实现相互转换。BAPI_CURRENCY_CONV_TO_INTERNAL:转换成数据库中内部存储金额,BAPI_CURRENCY_CONV_TO_external:转换成外部实际金额
4、每次币别的汇率更改在正式生产系统中新创建一条记录,利用函数CONVERT_TO_LOCAL_CURRENCY自动会把当前最近的时间的汇率作为转化的汇率,而不是直接在原纪录上更改
5、OB07、OB08,维护各币种之间的汇率。
6、碰到比较变态的货币,例如日元,它们是没有小数点的,系统内存储的和你看到的不同,有个BAPI可以使用:BAPI_CURRENCY_CONV_TO_INTERNAL
7、还有两个不同币种之间的转换FM:CONVERT_TO_FOREIGN_CURRENCY,和CONVERT_TO_LOCAL_CURRENCY基本没有区别,功能都是一样的,只是转换的源与目标相反而已:CONVERT_TO_FOREIGN_CURRENCY是将外币转换为本位币,而CONVERT_TO_LOCAL_CURRENCY是将本位币转换为其他外币
15.4.3. 货币内外格式转换
foreign_currency 日元
,否则实质金额不准确,这里正是
,所以这里的金额实质上为0.01美元,而不是1美元
日元
即可
IMPORTING
amount_internal = jpy "转换后的内部存储金额
return = ret.
CALL FUNCTION 'CONVERT_TO_LOCAL_CURRENCY'
EXPORTING
date = sy-datum
foreign_amount = jpy "源货币金额(内部格式)
foreign_currency = jpy_k"源货币类型
local_currency = usd_k"目标货币类型
IMPORTING
local_amount = usd."目标货币金额(内部格式)
WRITE: jpy, jpy_k,usd, usd_k.
WRITE: / jpy CURRENCY jpy_k, jpy_k,
usd CURRENCY usd_k, usd_k.