Axapta财务过账分析(一)
本文试图详细介绍Axapta中的财务过账,从战略,战术和代码实现等三个不同的层面看财务过账的实现.
战略层面:
毫无疑问财务是ERP的核心,所有的业务数据最终会在财务管理的某个科目中体现,公司从财务数据分析的情况可以了解到公司的运营状况,做出相应的业务分析和决策.
Axapta的财务过账操作是联系业务和财务的纽带.对于销售,采购,生产,库存管理以及项目管理等模块都有具体的过账动作.
销售和采购模块通过销售订单和采购订单的开发票动作来实现,工单通过工单的cost来实现.
在销售,采购做发(收)货单以及工单完工入库的时候也会写相应的科目,我一直称之为物理过账,本文暂不讨论.
本文以销售订单为例说明,操作路径如下:
Account Receivable->Sales Order->Posting->Invoice.
战术层面:
对于销售订单而言,需要做财务过账的信息有
1.因卖出商品而产生的应收账款
2.卖出商品引起的库存料品价值减少
3.销售税的过账
4.折扣的过账
5.杂项收费的过账
代码实现分析:
做企业软件没有任何技术壁垒,所以代码实现的分析也就没什么技术含量可言.
说白了,企业软件做了什么事情?无非就是从数据库的某张表里捞出数据,在内存里按照一定的业务逻辑判断一下,修改一下,然后放回到数据库里.至于大师们鼓吹的O-R Mapping以及耳熟能详的OO,无非就是让这个过程变得简单一些,以后改代码的时候方便一些(虽然国内大多数软件公司号称OO,但基本上是一个项目一套soure,上线后就闪人,真搞不清楚鼓吹OO对他们有啥子用,呵呵),在本质上也就那么回事,说得那么唬人,实际上没啥东西.而在代码分析中最关键的还是抓住本质的东西,从哪个表里抓的数据,对数据做了怎样的业务判断,写回了哪些表,当然也要顺便提一下类的继承和包含关系.
一.基本思路
Axapta在过账的时候采用了parmTable和ParmLines,这些表中的数据从SalesTable和SalesLine表中取得,这样可以保证在过账的时候不会修改订单的原始数据,
二.过账窗体的生成
过账使用的窗体叫做SalesEditLines,该窗体包含的数据源如下:
1.SalesParmUpdate
2.SalesParmTable
3.SalesParmSubTable
4.SalesParmLine
5.InventDim
6.SalesShippingStat
本小节主要介绍SalesEditLines是如何初始化的以及数据源中的数据是如何生成的.
查看财务过账对应的按钮可知道MenuItem SalesFormLetter_Invoice,调用的类为SalesFormLetter
通过MenuItem传递了EnumTypeParemeter为DocumentStatus,值为Invoice的枚举类型.这里就用到了前面文章中提到的Constructor Controlled Inheritance ,父类SalesFormLetter根据不同的DocumentStatus值创建不同的子类,在创建SalesFormLetter_Invoice对象时,做了如下几件事情:
1.取得了parmId(该变量在FormLetter中定义);
2.根据文档状态构造了SalesQuantity的实例SalesQuantity_Invoice;
3.初始化表SalesParmUpdate中的部分值;
4.根据SalesParmeter中字段ShippingStat设定useShippingStat的值.
这个参数可以通过如下路径设定:
Account Receivable->Setup->Parameter->Shipments->Shipping Specification
该属性的具体用途在后面介绍.
另外args对象还通过DataSource属性传递了当前的销售订单SalesTable,传递该SalesTable的目的如下:
通过SalesTable的dataSource属性得到FormDataSource,从而得到调用SalesFormLetter的窗体的DataSource,赋值给Formletter的callerFormDataSource,在做财务过账后刷新调用SalesFormLetter窗体的数据源.
接下来就是调用Prompt()构造窗体了,本小节的主要任务在这里完成.
构造窗体的任务主要是调用基类RunBase中的PromptPrim()来完成的,在该方法中会调用SalesFormLetter重载的方法dialog.
SalesFormLetter的dialog方法做了如下几件事情:
1.创建salesParmUpdate记录,并插入表中;
2.通过类工厂ClassFactory创建FormRunClass---SalesEditLines
3.调用initLinesQuery()方法,构造dialog上的select按钮对应的query(这段代码中有map使用的示例代码比较经典,在稍后的专项分析中讨论),并初始化表SalesParmTable和SalesParmLines中的记录,当然在本次分析中是用了callerFormDataSource做为限制条件初始化这两张表的.当然这里还做了一件事情根据useShippingStat的值(前面有介绍到),来决定是否在表SalesShippingStat插入相应记录.
4.调用窗体SalesEditLines的init()方法,当然在Init方法中调用super()会调用DataSource们的Init()方法,其中datasource SalesParmTable的init方法调用了cacheAddMethod()方法.另外在Init方法中实例化了SalesEditLinesForm类,并调用了setupSumByControl方法,该方法可以做为通过枚举类型填充FormComboBoxControl的示例代码.接下来用parmId筛选salesParmUpdate和salesParmTable两个数据源的数据.
在调用完dialog()方法后,会调用salesEditLines的Run方法,run方法做了如下几件事情:
1.用salesFormLetter中的几个属性值初始化SaleEditLines窗体的控件值;
2.根据salesEditLinesForm的属性值,设定salesParmUpdate_ds,salesParmTable_ds,salesParmLine_ds中的某些属性以及某些控件的可见性.
然后就是调用wait方法,窗体就生成了,下面就是第二阶段的问题了.
如果点击SaleEditLines的OK,则会调用salesFormLetter的run方法.