关于数量和金额的小数位数
在AX中数量和金额的EDT类型的基类型都是Real,虽然创建EDT类型的时候,选择的是real,其实叫做Decimal更符合实际情况,因为这种类型的EDT类型在SQL Server里都翻译成numbric类型,因为处理数量和金额的时候要使用精确的计算方式,而不能采用float和real之类的近似数据类型。MSDN关于这些类型的可以参照如下链接:
http://msdn.microsoft.com/zh-cn/library/ms187912.aspx
关于数量和金额在AX的处理主要是基于三个基础EDT类型,数量是RealBase,金额是Money和MoneyMST。其中RealBase我们可以在AOT树中找到,Money和MoneyMST就找不到了,不过它们两个的直接下线Amount和AmountMST在AOT中可以找到。
在界面中显示多少位小数是通过EDT类型的NoOfDecimals来定义的,这个属性跟基类型是string的EDT类型的StringSize不同,NoOfDecimals这个属性只是用来定义在用户的界面上显示多少位小数的,而不会影响SQL Server数据库里使用该EDT类型的字段的小数位数,看了一下SQL Server里对应的数量和金额的字段类型都是Numbric(28,12),我在AX里也没找到更改这个长度的地方,OK,我们可以设想数据存储层最多只能存储12位小数,那么在表现层即使把值定义成可以显示多于12位也没太大意义,实际的ERP应用中也不需要精确到这么大的精度,当然如果这个字段不是直接存储到数据库里,而是做为中间变量值去做运算,在一定程度上倒是可以提高运算的精度,测试了一下,NoOfDecimals可以设定成任意整数值,但是实际上大于16位的值时,它已经当作默认值2去处理了,比如AmoutMST这个EDT类型,NoOfDecimals的值被设置成了20,实际上拖到界面上后,只显示两位小数。
到这里我们可以的出一个结论,NoOfDecimals只是用于限定界面显示的,如果在程序中用EDT类型去定义一个变量,可以赋值为任意长度的小数位数(当然Decimal最大也就(38,20)),在把计算结果存储到SQL Server里时会舍为为12位小数。
这样,数据层和表现层小数位数的限定就清楚了,数据层的小数位数固定为12位(当然可能有地方可以修改这个设定,只是我没找到而已),表现层通过EDT类型的NoOfDecimals去限定。实际上,程序的设计应该在业务逻辑层根据业务的需要在业务层去做一些限定,不能根据数据层和表现层这哥俩一商量就完事了。AX在业务逻辑层对小数的位数也有规定。
联系到业务层面,我们提到数量,当然要跟单位联系起来,要不然只说一个数字,没任何意义,五吨和五千克显然没什么可比性,提到金额,一定要跟币种联系起来,发5W日元和5W美元给你的感觉铁定不一样。AX也是根据这个思路来做的,对数量的小数位数的限定体现在单位上,而对金额的舍入位数的限定写在了币种上。
先来看看数量,我们以销售订单的销售数量为例说明,该字段对应的扩展数据类型是SalesOrderedQty,一路追上去,最终可以追到最顶层的EDT类型是我们前面提到的RealBase,看实际的需求情况了,如果要求所有的数量的显示位数都改成4位或者更高的位数(默认为2位),就直接修改RealBase,这样大家就都继承了,如果只想修改销售数量,可以只修改SalesOrderQty,我们这里只以修改SalesOrderedQty这个EDT类型为例说明,将EDT类型的NoOfDecimals的值设置为4
从上图可以看出数量确实显示为4位了,但是如果输入值就知道了,单位Kg的小数位数我设置为了1位,如下图所示:
无论输入1.2345,还是其他的什么值,都会给你舍到1.2或者1.3(四舍五入),如果想真的能够输入四位值,需要把物料对应的单位的值也设置成4位,这样表现层和业务上也就一致了,如果不设置单位的小数位数,数量字段显示一串零,也没什么意义,单位最多可以设置8位小数,一般情况下足够了,当然可以去修改代码,把这个限定放开,但最多也就12位吧,数据层限定了,还没遇到需要保存这么多位的需求。
再看看金额,还是以销售订单为例吧,EDT类型的SalesLineAmount的最顶级的父类型是Amount,与AmoutnMST相对应,表示的是用原币表示的金额,而AmountMST表示的是用本位币表示的金额。我们知道,在系统管理->设置->系统->修改数据类型的界面有一个修改金额这个数据类型的选项,翻了一下代码,这个地方的设置只会影响AmoutMST这个EDT类型,所以继承自AmountMST的EDT类型会受到影响,但是像销售金额这种用原币表示的金额都是继承自Amount的,所以在这里的修改并不会影响销售订单的行金额显示位数,这里修改EDT类型SalesLineAmount为4位,修改后得到如下值:
从图中可以看出,值虽然显示为了四位小数,但是数量和价格的乘积应该是34.991728,如果舍入到四位应该是34.9917。在币种设置了化整处理,在总账->设置->汇率->化整(选项卡),如下图所示:
可以看到上面一共有四个关于金额的设置,其中第一个是用于常规化整的,我们上面的销售订单行的LineAmount就是受这个字段控制的,其余三个,其中两个订单化整实在开发票的时候把金额取整,而舍入价格是针对价格的舍入设定,不过很让我纳闷的是,这个常规设置只有三种选择,不设置,0.1和0.01,如果这里不设置,AX也会取默认值0.01,所以相当于这里只能是设置为取一位小数0.1,如果想保留三位小数,或者更多,就没办法设置了,这确实是挺奇怪的事情,或许有地方设置我没有找到吧,我翻了半天也没找到在哪里可以设置为更多小数位数。。。我能想到的解决方案也只有把常规化整这个字段对应的EDT类型的NoOfDecimals的长度改得长一些,这其实是我不想采用的方法,因为这样业务人员根本没办法做到,总不能要求业务人员跑到AOT里面去修改这东西,所以我觉得把这个EDT类型暴露在管理里面的修改数据类型里面是不是更合理?或者干脆发布的时候就把这个EDT类型的NoOfDecimals弄得长一些?或者MS认为金额就应该最多保留2位?我暂时把常规化整对应的EDT类型RoundOff的属性NoOfDecimals改成4,然后把常规化整改为0.0001,然后再看销售订单的行金额,如下图所示:
另外采购销售价格之类价格的也是继承自Amount这个基础类型的,所以通过系统管理里修改数据类型的功能并不会更改价格的小数显示位数,只能去AOT里找到相应的EDT类型做修改了,另外价格不像数量和金额,有业务逻辑层的控制,只修改相应的EDT类型就可以,但是价格实际上是跟货币相关的,所以这样处理给人一种不是很统一的感觉。
OK,就说这么多了,由于没看到正式的文档,所有的内容都来自于我自己的测试和猜测,所以不见得正确,还望高手指点。