在上线前的测试阶段,要频繁测试某些参数,随着基础数据和测试数据的增加,需要反复地初始化系统,能够灵活快速地将数据恢复到某个时点就比较重要了,让人记得每天都做一件事情是比较费劲的,当然吃饭睡觉除外。
AX有数据导入导出的功能,并且这些类都是继承自RunbaseBatch的,也就是说可以做定期备份的,但是有一个问题是,文件名的问题,如果不处理一下,由于只有一个文件名,它每次都会把上次的备份给覆盖掉,这当然不是我们所希望看到的。本文描述一下如何修改代码使其每天可自动备份一个文件,只针对初学者,高手就不必看了。
一.修改代码处理文件名
找到类SysDataExpImp,把unpack方法修改如下:

Code
public boolean unpack(container packedClass)
{
version version = RunBase::getVersion(packedClass);
container c;
#DataBackup
boolean ret;
switch (version)
{
case #CurrentVersion:
[version,#CurrentList,c] = packedClass;
ret = super(c);
//Farseer Begin 2009.03
switch(this.parmGroupId())
{
case "All":
fileName = #AllData + Date2str(systemDateGet(),321,2,3,2,3,4) + " "+time2str(timenow(),3,3);
break;
case "Main":
fileName = #MainData + Date2str(systemDateGet(),321,2,3,2,3,4) + " "+time2str(timenow(),3,3);
break;
}
return ret;
//Farseer End 2009.03
default:
return false;
}
return true;
}
其中#DataBackup是一个宏,在里面定义了两个路径,分别用来存放全部数据的备份#AllData和只包含主数据的备份#MainData。
二.定义组
我的做法一般都是备份两份数据,1.包含全部数据,2.只包含主数据,不包含交易。组的名字分别为All和Main。
系统管理->期间->数据导入/导出->定义组
新建两个组,名字分别为All和Main,在包括表组选项卡中,All这个组选中所有的表组,而Main只选择前四项,既杂项,参数,组和主要。如下图所示:
All:

Main

三.定义导出数据的批处理和及其频率
选中刚建好的组,点击导出

设置选择批处理

选择 重复执行,设置执行备份数据的频率,如果想每天晚上执行一次,则可以按照如下设置方式,当然这个东西就看每个人的想法了。

四.执行批处理
基本设置->期间->批处理->处理,点击确定,让它运行就OK了。
参与过AX项目,特别是做开发的可能都会遇到一个问题,AX不是能开发吗?于是用户搬出一大堆文档,Word,Excel的,按照这些格式给我改吧!做这事绝对是出力不讨好,这些格式绝对是不固定的,三天两头要变的,虽然AX开发比较灵活,但应对起这些华丽花哨的Word和Excel也只能疲于应付,Word和Excel的格式总会比AX的报表灵活的多。
AX的文档管理里可以通过预先定义的模板来生成Word和Excel文件,这样格式就由用户自己通过模板定义了,AX只负责在用户定义的这些模板的基础上通过COM把AX的存储的数据写到模板里形成文件,这样格式的修改就扔给客户了,想怎么改就怎么改吧,不管AX的事。
本文以销售订单为例,介绍一下文档模板的使用,本文面向AX初学者,高手就不用看了,另外,AX4.0SP2有bug,这个模板功能不能用,AX2009是OK的。
一.定义文档类型
基本管理->设置->文档设置->单据类型
新建一个文档类型,类描述 选择 通过 COM 创建 Word 文档,如下图所示:

2.设置AX字段与模板中书签的对应关系
点击 选项 按钮
在 概览 中 添加一条记录,选择 SalesTable,然后选择字段添加 数据表,字段以及书签。如下图所示:


三.设计销售合同模板
插入书签的名字要跟上面图片中的书签名称相同,插入书签的操作,会用Word的都会,这里就不赘述了,如下图所示。

将上述文件保存为SalesContract.dot
四.选择模板
在第二张图所示的模板文件处,选择步骤三中保存的模板。

五.使用
应收账款->销售订单详细信息

生成的文件如下图所示:

怎么样?还凑合吧?限于我的审美情趣,也只能弄出这么难看的样子了,可根据每个人的喜好,酌情修改样子。数据由AX提供,打印的模板由用户自己去做,这个的确是不错的主意,呵呵。
由于我对财务一窍不通,如果本文有贻笑大方的地方,还望各位AX财务高手不吝赐教。
业务背景
到年底了,也该清清财务账了,年结说复杂可以非常复杂,说简单也很简单,本文只涉及最简单的部分。
会计科目按其类型分类可以分为资产,负债,所有者权益,成本和损益类(2007年新的会计制度增加了 共同类,我暂时不知道这类科目是做什么用的,对于不懂的东西就不乱写了,等搞懂了再说)。
在年结的时候,逻辑其实很简单:
1.损益类科目的余额清零,余额转到本年利润科目,当年还有传说中的月结,什么表结法和账结法之类,国内习惯用账结法,国外的软件一般采用表结法,所谓的表结法就是月末什么都不做,账结法在每个月的月底把损益类科目的余额转到本年利润科目,其实在本质上是一回事。
2.其余类型的科目,资产,负债,所有者权益,成本把年底的余额转到下一年做为下一年的年初额。
综上所述,自然地把会计科目分为两类,损益类把余额放到本年利润,其余类型的科目余额直接转为自己的期初。
操作步骤:
下面介绍一下在AX里应该怎么做年结的动作:
1.设置总账参数
操作路径:
总账->设置->参数设置

选择 在结转期间删除年末结转交易记录 和 必须填写凭证号。这里暂时不介绍这几个参数是做什么用的,本文后面在介绍代码的时候会一一介绍这几个参数的作用。国内实施的时候一般是这么选择的,所以一般情况下这么选就OK了。
2.运行年结
当然年结是个很复杂的过程,要处理一些账面上的东西,这里只是简单的介绍在AX里的操作步骤而已,在操作这个之前先做好准备工作。当然AX的年结动作相当简单,并且是可以反复运行的,所以也没什么大碍。
总账->期间->会计年度关帐->期初交易记录

结束日期 选择 会计期间的最后一天,如果不是最后一天,系统会提示的。资产负债科目选择 结算->未结算,年结的结转科目 选择 本年利润,输入一个凭证号。如果想在完成后显示生成的交易,则选中 打印创建的交易记录。
选择完成后点击确定即可。
代码分析
接下来我们看一看,AX处理年结的代码。
由于业务本身的逻辑很简单,AX处理年结的代码也极其简单,只用了一个继承自RunbaseBatch的类LedgerTransferOpening来处理所有的事情。先介绍一下AX中跟年结有关的基本概念:
1.科目类型
会计科目对应的表LedgerTable的字段AccountPlType来表示科目类型,正如前面业务背景部分提到的,科目类型在年结的时候分成两种情况来处理,AX在实现的时候必然也要考虑这一点。AccountPlType的类型是枚举类型LedgerAccountType,该枚举类型共有如下几个值:
AccountOperations 损益
AccountRevenue 收入
AccountCost 成本
AccountStatus 所有者权益
AccountActive 资产
AccountLiable 负债
Common 共同
当然后面还有标题,页面标题,还有合计 等,这些科目由于并不发生交易,所以在年结的时候不会处理它们。在前面介绍会计科目类型的时候,并没有 收入 类型的科目,所以这个应该也不需要处理。对于损益类科目,前面介绍的,这些科目的余额应该过账到 本年利润。剩下的几个 成本,所有者权益,资产和负债应该直接转到下一年的年初。
2.期间代码
为了区别年结时生成的交易和正常发生的交易,AX用了一个枚举类型PeriodCode来不同的交易类型。这个枚举类型有如下值:
Opening 开始日期
这个翻译有些问题,不管怎么样翻译成开始日期总还是有些别扭,无所谓了,我们只要知道在什么情况下会生成这种类型的交易就可以了。前面介绍业务背景的时候提到过,要将余额转到下一年,这势必要生成一个期初交易凭证,OK,这个期初交易凭证的类型就是Opening的,只有在年结的时候才会生成这种类型的交易。
Regular 正常
这个就不用解释了,在年中生成的所有的交易,类型都是 正常 的。
Closing 结束日期
我们前面介绍参数设定的时候,有一个参数我们是没有选择的--在结转期间创建期末交易记录。这个参数在国内一般不使用。它会将本年的余额借贷相反冲掉,如果我们没有选择那个选项,当然也就不会有Closing类型的交易出现了。
3.转账方法
前面介绍年结的操作步骤的时候,其中有一个参数 转账方法 资产负债类科目,我们选择了 结算->未结算,对应一个枚举类型OpeningMethod,有两个选项:
Reset 重置
ClosingToOpening 结算->未结算
这两个选项怎么解释那?通过看代码,我们可以看出来端倪:
Reset实际上就是要把自己的余额清零,然后把余额转移到其他科目上去,怎么算清零?很明显,对于某个科目,如果不把这个科目的余额结转到自己身上,而是转移到其他科目上,这就是清零了,因为在这个科目本身上没有生成Opening类型的交易,在下一年查询该科目的余额时,就查不到该科目上一年度的余额了,这个恰好就是损益类科目的逻辑。
ClosingToOpening的意思是把余额还转移到自己身上,对于非损益类科目,按照前面说的逻辑,应该选择这个选项。
对于损益类科目,AX是没有提供选项的,也就是说它都会过账到 本年利润科目。对于资产类科目,它提供了一个选择,可以把余额转到自己身上,也可以转到本年利润科目。但是很明显,我们一般也不会这么选择。
4.期初科目
会计科目表->常规(页签)有一个 期初科目的选项。

这个选项实际上提供了一种灵活性,虽然这种灵活性在大多数情况下并不使用。
我们在上一节介绍了Reset和ClosingToOpening,也介绍了它的使用方法,Reset把余额转移到其他科目,ClosingToOpening把余额转移到自己身上。这种分类是按照科目类型来的,损益类采用Reset,资产负债所有者权益成本类采用ClosingToOpening,如果某个科目想单独定义将余额过账到某个科目的话,就采用这个期初科目的选项。
OK,看代码吧。主要是LedgerTransferOpening的run方法:

Code
/* Find where to place the opening amount. */
if (ledgerTable.AccountPlType <= LedgerAccountType::AccountCost)
{
currentAccount = ledgerTable.Openingaccount ? ledgerTable.Openingaccount:
methodOperations == OpeningMethod::Reset ? transferResult : ledgerTable.AccountNum;
}
else
{
currentAccount = ledgerTable.Openingaccount ? ledgerTable.Openingaccount:
methodStatus == OpeningMethod::Reset ? transferResult : ledgerTable.AccountNum;
}
}
这段代码就是决定要把余额过账到哪个科目的。
需要注意的是它把AX的科目分成了两类,小于或等于成本类科目,根据之前的介绍,包括 损益,收入和成本。但是我们成本类科目的余额是要过账到自己身上的,而这里把它等同于损益类科目了,也就是说它把成本类科目的余额转移到本年利润了。
有三个方法可以更正:
1.把成本类科目的类型改成资产类。成本类科目本来就是核算在制品的,本质上属于存货科目,所以改成资产类也无可厚非;
2.把成本类科目的期初科目都设置成自己,这样不管是Reset还是ClosingToOpening都转移到自己身上了;
3.该代码。这个有修改的代码可能就比较多了,AX的逻辑把损益,收入和成本都看成ProfitLoss科目了,所以可能要伤筋动骨。
综上所述,第一种方式应该是最合理的,我感觉AX的科目类型成本跟我们的会计科目类型 成本 应该是不同的。
从代码上看,如果没有选择 创建期末交易记录,1和2的代码路径是完全一样的,只是在创建期末交易记录的时候有所差异,国内一般不会创建期末交易记录,所以1和2两种方式也就没什么不同了,当然这只是一个财务门外汉的个人之见,不见得合理。
OK,就说写这么多了,更详细的内容,看一下run方法就可以了。
所谓的汇兑损益,就是由汇率变化引起的损失或者收益。如果公司没有外币业务,就不必关心这个功能了。关于国内会计对汇兑损益的处理规定在CPA2008教程的第12章外币业务有详细的论述,看等有时间把相关内容抄到这篇文章中来。
我们先来看一下AX中是怎么来处理汇兑损益的,由于本人财务知识有限,错误在所难免,还望高人指点。
概述
在AX财务教程II的第三章MutilCurrency有关于汇兑损益操作的介绍,这里就不详细介绍了,只是简单过一下,还是将重点放到代码的分析上。
在AX中汇兑损益的调整分成两种:
1.总账模块的调整
2.应收应付模块的调整
总账模块的汇兑损益调整逻辑极其简单,只用了一个继承自RunbaseBatch的类LedgerExchAdj,几个方法搞定。它的思路也很简单,找到用户指定时间范围(从组里的时间和到组里的时间)的LedgerTrans记录,用一个类KeySum把这些记录按照科目和维度分组汇总外币金额和主货币金额(也就是MST),用到组里的时间的汇率*外币金额得到新的主货币金额,如果有差异就生成一笔调整分录,简单吧?嗯。
这样不就OK了吗?干吗费劲折腾要在应收应付模块里弄一把,用刑捕头的话说,这真让人费解啊,confused......我能想到的理由:
1.应收应付模块可能是最多涉及到外币业务的模块;
2.应收应付模块还牵扯到应收应付款和已收已付款的冲销;
3.应收应付模块需要在客户交易记录表中记录每次汇率调整的记录。
应收模块的汇兑损益逻辑
应收和应付模块汇兑损益的逻辑类似,这里只介绍应收模块,应付double一下就可以了。
我想AX对汇兑损益的处理与国内有所不同的地方就是在应收应付处理的时候分为了已实现损益和未实现损益两个来处理。其实AX的观点处理方式很明确也很直接,应收和已收冲销的时候产生的汇兑损益是已实现损益,未冲销前的汇率调整产生的损益是未实现损益。
客户交易可以分为两类应收和已收,对于应收和已收又分为已结和未结,所谓的已结和未结也就是应收和收款之间有没有做过冲销(Settle),我们知道应收的会计分录是:
借:应收账款
贷:主营业务收入
收款的是
借:现金(银行存款)之类
贷:应收账款
实际上收款是将应收账款从贷方转走,在完全冲销后借贷相等,应收账款为零(当然这是理想状况)。这里就有个问题,收款对应哪些笔应收款的问题,反之亦然,所以要有个settle的功能。AX在settle的时候会进行汇兑损益的处理,它的逻辑是借贷反向冲回前面已经做的未实现损益,然后用收款交易的汇率和应收款交易汇率之间的差额产生已实现损益。
简单介绍一下AX应收和收款之间Settle的表和类结构.在表方面,有三张表CustTrans,CustSettlement和CustTransOpen.从表的名字上可以看出,AX在设计的时候是把记录分成已结和未结的,已结的放在CustSettlement,未结的放在CustTransOpen,已结和未结的都在CustTrans,记录起初在CustTransOpen,随着核销的进行慢慢转移到了CustSettlement里.这种设计很直观。
至于类结构,也很直观,CustVendSettle,CustVendSettle_Cust,最主要的逻辑在CustVendSettle的SettleNow方法中进行,这个方法有将近1000行之多。。。顺着看一遍这个方法,它所处理的逻辑也相对简单,主要的逻辑是将客户的借方交易记录(应收)和贷方交易记录(收款)进行冲销,还有就是处理现金折扣,特殊增值税,收款和应收过账模板不同时的处理以及如果记录完全Settle,并且对应的过账模板指定了关闭模板的科目转移,当然还有本文所关注的重点---汇兑损益的处理逻辑,把代码摘录如下:

Code
/* set off unrealised exchange rate loss/gain */
if (custVendTransDebet.ExchAdjustmentUnrealized)
{
this.reverseUnrealisedExchAdj(custVendTransDebet, custVendTransOpenDebet, settleFactorDebetCur);
this.updateTransactionReverseExchAdj(custVendTransDebet, custVendTransOpenDebet);
}
if (custVendTransCredit.ExchAdjustmentUnrealized)
{
this.reverseUnrealisedExchAdj(custVendTransCredit, custVendTransOpenCredit, settleFactorCreditCur);
this.updateTransactionReverseExchAdj(custVendTransCredit, custVendTransOpenCredit);
}
if (Currency::amount(settleAmountMSTDebet + usedCashDiscMSTDebet + creditNoteUsedCashDiscMSTDebet +
settleAmountMSTCredit + usedCashDiscMSTCredit + creditNoteUsedCashDiscMSTCredit) != 0)
{
this.postExchRateDiff(-(settleAmountMSTDebet + usedCashDiscMSTDebet + creditNoteUsedCashDiscMSTDebet +
settleAmountMSTCredit + usedCashDiscMSTCredit + creditNoteUsedCashDiscMSTCredit),
custVendTransDebet,
false,
custVendTransCredit.PostingProfile);
}
可以很简单地看到AX在Settle的时候对汇兑损益的处理逻辑。前两个if,分别反向冲回未实现汇兑损益,后面一个if过账已实现汇兑损益。
OK,已实现汇兑损益就这么多了,那么未实现汇兑损益是怎么弄的那?
实在 应收模块的汇率调整在 应收账款->期间->汇兑损益调整 窗体中进行,我们首先介绍一下处理未实现汇兑损益涉及到的类。
首先是继承自RunbaseBatch的类,CustVendExchAdj及其子类CustExchAdj,我们知道继承自RunbaseBatch的类一般就是提供给用户一个输入信息的界面,如果处理逻辑比较复杂,一般会再写一些类用来处理具体的业务逻辑,放在上述类的run方法里调用,汇率调整这里也不能免俗,它用了CustVendExchAdjTrans及其子类CustExchAdjTrans来处理汇率的调整。主要的逻辑代码在CustVendExchAdjTrans的update方法里,这个方法的逻辑分成两部分处理已结交易和未结交易,分别通过exchAdjustSettlement和exchAdjustTrans进行。exchAdjustTrans的处理很直观,把之前的未实现的汇兑损益冲回,在生成新的未实现汇兑损益。对于调整已结交易,AX的方法exchAdjustSettlement处理方式有些让人费解,可能我还没有理解汇兑损益的真谛的缘故,也许有一天对这块业务大彻大悟了就能明白它的处理方式了,我发现看AX代码确实有这么一个过程,代码的理解是随着对业务的理解而逐渐加深的,如果不了解业务,能看到的只是读写表的过程,随着对业务的理解,代码也就有了生命.当对业务大彻大悟的时候,代码也就被复原成生龙活虎的人了。现在我只能把AX的处理逻辑复述一遍,至于为什么这么做还需要继续修炼...
1.调整数据的范围

Code
while select forupdate custVendSettlement
where custVendSettlement.TransDate >= postingDate
&& custVendSettlement.SettleAmountCur
&& custVendSettlement.CanBeReversed
join custVendTrans
where custVendTrans.RecId == custVendSettlement.TransRecId
&& custVendTrans.TransDate < postingDate
我们从类CustVendExchAdj的update方法的上述代码中可以看到,AX要处理的交易记录是在 考虑日期 之后(包含考虑日期)有过核销记录,并且交易日期在考虑日期之前的。这可真有点绕的。。。
2.调整的逻辑
在CustVendExchAdj的exchAdjustSettlement方法。我感觉它的逻辑应该是分成两类 之前有过未实现汇兑损益调整记录的和没有调整过的。
我的想法是:既然是已经核销过的记录,已经产生了已实现汇兑损益,理论上不应该再进行未实现的汇兑损益调整了,因为已经结转到现金和银行类科目了,再调整就没完没了了.那AX干吗还要这么折腾?I have no idea...
我猜想应该是AX想补回一些未结的汇兑损益记录,举个例子:
发票 2008年8月1日
应收账款:100 USD 汇率:720
2008年9月1日 汇率:700
收款 2008年10月4日 汇率:690
应收账款: 100USD
第一种情况:在核销时没有进行未结的汇兑损益调整,那么在核销时产生的已结汇兑损益调整凭证应该是:
借:已实现汇兑损失 30
贷:应收账款 30
损失了30大洋。如果这个时候再运行期间里的汇兑损益调整,考虑日期选择2008年9月8日,AX会怎么处理那?
很明显根据 调整数据的范围 里的介绍,2008年8月1日的发票对应的记录在调整之列,AX会生成啥凭证那?
2008-10-4
借:未实现汇兑收益 20
贷:应收账款 20
2008-09-08
借:应收账款 20
贷:未实现汇兑损失 20
从表面的意思看,它是补了一对借贷相反的未实现汇兑损益凭证,当然各个科目的金额都没什么变化,因为本来也不应该调整,只是给相应的科目增加了两笔记录。
第二种情况是在核销时已经进行了未结的汇兑损益调整,然后在核销后用核销日期前的某个日期做为考虑日期再进行一次期间的汇兑损益,这个过程会产生N多凭证,不过正过来反过去,到最后只是多了一些交易,但都借贷相反冲掉了,科目的余额还是按照核销时产生的已实现汇兑损益计算。
接着瞎侃。
上一篇说了资金流,这一篇就扯一下信息流,在软件设计的时候信息流可能有很多想法,每个人对信息流的理解也不一样,所以也没有统一的标准。我这里所分成的信息流,现金流和物流仅仅是为了分析销售开票这个过程,人为地按照我自己的理解把代码分割了一下,便于整理,如此而已。
我这里提到的信息流是指在销售开票的过程中用哪些表和代码记录了与客户相关的信息。企业折腾的过程最终是为了赚钱,所以最后总会落脚到Money,也就是上文提到的那些财务凭证。在从物料转化成Money的过程中,需要记录一系列的信息,要不然将来客户说凭啥给你钱?当然这只是一方面了,还有很多情况,比如一个销售订单可能要多次发货和开票,需要记录每次开票的信息。
AX如何记录这些信息那?
客户开票信息
CustInvoiceJour和CustInvoiceTrans这两张表就是用来记录每次销售开票过账的信息的。AX用来写这两个表的代码也很简单明了,就是在用for循环处理每个销售订单行的时候,顺便通过writeJournalLine方法写CustInvoiceTrans,在处理完一整张销售订单后通过writeJournal方法写CustInvoiceJour,这段代码在SalesFormLetter_Invoice的updateNow方法里,具体每个字段写入的值可以参考相应的方法。
付款计划
在信息流里面还有个问题就是付款计划,关于付款计划的在财务教程I的第9章有详细的介绍,所以关于它的计算逻辑这里也就不再赘述了。AX用了CustPaymSched和CustPaymSchedLine这两张表来存储相关信息,当然还有两张表来存储付款计划的规则,就是PaymSched和PaymSchedLine。由于付款计划对于应收应付来说是通用的,所以对应采购的付款计划也有相应的表VendPaymSched和VendPaymSchedLine,这两套表的处理逻辑大部分都是相同的,所以AX用了Map,CustVendPaymentSched和CustVendPaymentSchedLine来处理,从这里的处理来看也就很能明白Map的实际作用了,就是为了把公用的代码放到一个地方,免得采购和销售各写一套。上面说的是数据层面,在逻辑处理方面,当然要搞几个类来处理一下计算的逻辑,AX弄了PaymSchedCalc及其子类,还有CustVendPaymSched及其子类来处理。
对于付款计划,我不太清楚国内用的是不是有很多应用,对于做项目一般会采用分期付款的方式,比如ERP项目的时候,一般分成几个阶段付款,在合同里都有约定,在word等非结构化文档里显然不适合查询和追踪,把这些条款翻译成AX里对应的付款计划,放到关系数据库表里,显然会方便查询和追款,这或许就是付款计划存在的现实意义吧。
这里只简单介绍一下AX在开销售发票的时候是在哪里处理付款计划的,其他的逻辑方面的东西就不展开讲了,感觉用的不是太多,这年头欠钱的都是老大,计划赶不上变化。
AX是在SalesFormLetter_Invoice的updateNow方法通过调用this.createPaymentSched();来创建付款计划的,当然在这个方法里,AX又会调用前面介绍的关于付款计划的类和表去做相应的处理,具体的处理逻辑先看财务I的第9章然后翻翻代码就OK了。
客户交易记录
万涓成水终究汇流成河,AX处理所有的业务到最终都落脚到一张表上,比如库存到最后是InventTrans,财务是LedgerTrans,客户就是CustTrans了。这种处理方式简介而直观,一如库存模块,不可能在让我库存关帐的时候,再去照顾所有业务对应的表,SalesLine,PurchLine,InventJournalLine之类,如果将来你再来几个新的模块,多开几个新的表,我库存关帐的逻辑都要改,那是要死人的,LedgerTrans和CustTrans也是这个道理。关于客户的交易都放在CustTrans里,在将来处理的时候就简单了,所以最后要介绍的内容就是关于CustTrans的了。上一篇文章中我们介绍了说AX在应收应付模块吧LedgerVoucher封装了一把,变成了CustVendVoucher及其子类,一方面简化了对LedgerVoucher的操作,更重要的是在应收应付模块要处理更多的事情。其中一件很重要的就是要生成客户交易记录。我们通过浏览代码的方式来介绍一下在这部分AX到底做了些什么。
SalesFormLetter_invoice-》updateNow->postJournal
这个方法里的其他方法都很简洁,我们很容易就可以看出它做了什么,我们需要了解的是其中的createCustTrans方法做了什么,顾名思义,这个方法就是创建客户交易记录的。主要的内容在类CustVendVoucher的post方法中:
1.向LedgerVoucher对象中添加交易
客户应收账款额对应的记录,要在这里添加到这个对象中,要不然就瘸了。
2.创建CustTrans记录
创建CustTrans记录,这些信息大部分来自于CustInvoiceJour。
3.创建CustTransOpen记录
之所以需要这个表是因为应收账款和收款以及预付款之间需要核销(Settle),也就是弄明白哪几笔收款付了哪几笔应付款。创建这个表的好处是,可能核销很多次,一笔记录可能要被拆成几笔,如果放在CustTrans里面,CustTrans最后就面目全非了,为了尽可能地保留CustTrans的原貌,未结的交易都放在CustTransOpen这里面,折腾就折腾这个表了。如果用来付款计划,对一笔CustTrans会根据付款计划插入多条记录进CustTransOpen。
4.处理收款和核销
如果采用的付款期限是货到付款(C.O.D)采用现金结算,并指定了现金科目,那么AX在开票的时候就直接生成收款凭证,并且在应收和已收款之间做核销。收款的凭证很简单,就是 贷:应收账款 借:在付款期限处设定的现金科目。至于核销就是另一段故事了,里面牵扯的内容也相对复杂,比如汇兑损益,比如不同过账模板,关闭模板,林林总总,不一而足,在后面专门介绍汇兑损益的时候再把收款和汇兑损益以及核销的逻辑介绍一下。
OK,信息流就说这么多了,物流在介绍库存代码的时候介绍过,实际上销售开票的过程也就是在调用那一段逻辑而已,没什么新鲜的内容,也就不再赘述了。
销售开票就到这里吧。