AX中RunbaseReport类用来运行一个报表,它可以定义一些参数,在弹出对话框的时候设置这些参数,以列出工单为例,先看看不考虑从其他form点击纪录带入参数的时候如何实现这个报表。下图是所用到的几个对象:
菜单项TestProdReport指向类TestProdReport,下面是类TestProdReport的代码:
class TestProdReport extends RunbaseReport{ NoYesId showOnlyOpen; DialogField dfShowOnlyOpen; #define.CurrentVersion(0) #localmacro.CurrentList showOnlyOpen #endmacro}static void main(Args args){ TestProdReport prodReport = new TestProdReport(); ; if (prodReport.prompt()) prodReport.run();}public identifiername lastValueElementName(){ return reportStr(TestProdReport);}public NoYes parmShowOnlyOpen(NoYes _showOnlyOpen = showOnlyOpen){ showOnlyOpen = _showOnlyOpen; return showOnlyOpen;}public Object dialog(DialogRunbase dialog, boolean forceOnClient){ DialogRunbase ret; ; ret = super(dialog, forceOnClient); dfshowOnlyOpen = ret.addFieldValue(typeid(NoYesId),showOnlyOpen,"Show only open orders"); return ret;}public boolean getFromDialog(){ boolean ret; ret = super(); showOnlyOpen = dfShowOnlyOpen.value(); return ret;}public container pack(){ return [#CurrentVersion, #CurrentList] + [super()];}public boolean unpack(container packedClass){ container base; boolean ret; Integer version = RunBase::getVersion(packedClass); boolean dummy; switch (version) { case #CurrentVersion: [version, #CurrentList, base] = packedClass; ret = super(base); break; default: ret = false; } return ret;}
报表TestProdReport的代码:
public class ReportRun extends ObjectRun{ TestProdReport testProdReport;}public void init(){ ; testProdReport = this.args().caller(); super();}public boolean send(Common _cursor, int _level=1, boolean _triggerOffBody=TRUE, boolean _newPageBeforeBody=FALSE){ boolean ret; ProdTable _prodTable; ; if(_cursor.TableId==tablenum(ProdTable)) { _prodTable = _cursor; if(testProdReport.parmShowOnlyOpen() && _prodTable.ProdStatus==ProdStatus::Completed) return true; } ret = super(_cursor, _level, _triggerOffBody, _newPageBeforeBody); return ret;}
这里我们是在报表的Send函数判断工单状态根据条件过滤掉已关闭的工单,这不是最好的办法,因为Query仍会查询出所有的工单纪录,在一定程度上影响性能,更好的办法是重载fetch方法,在fetch方法中获取报表的query,在query中根据条件加入对ProdStatus的过滤条件,再由Query创建QueryRun,运行QueryRun获取ProdTable纪录Send,这里就不再演示了。
现在的问题是如果把TestProdReport菜单项添加到ProdTable form中,在ProdTable form上点击这个菜单只显示当前ProdTable纪录到报表该如何来做呢?
首先在TestProdReport类定义中添加变量来纪录form中的当前纪录:
ProdTable prodTable;
添加parmXXX函数来传入这条ProdTable纪录:
public ProdTable parmProdTable(ProdTable _prodTable = prodTable){ ; prodTable = _prodTable; return prodTable;}
添加函数initFromArgs来从传入的Args初始化这条纪录:
private void initFromArgs(Args args){ ; if ( args && args.dataset()) { switch(args.dataset()) { case(tablenum(ProdTable)) : this.parmProdTable(args.record()); break; default: } this.makeReportRun(); //创建ReportRun对象 this.initQueryRun(); //初始化Report的QueryRun,必须是手工调用,正常运行报表是不会调用这个函数的,它调用后面的initQuery }}
添加initQuery函数来初始化Query,initQuery函数会被initQueryRun()调用,在这里我们根据传入的ProdTable纪录根据主键工单号ProdId来过滤:
public Query initQuery(){ QueryBuildDataSource queryBuildDataSource; QueryBuildRange queryBuildRange; Query query; ; query = super(); queryBuildDataSource = query.dataSourceTable(tablenum(ProdTable)); if (!queryBuildDataSource) queryBuildDataSource = query.addDataSource(Tablenum(ProdTable)); queryBuildRange = queryBuildDataSource.findRange(fieldnum(ProdTable, ProdId)); if (!queryBuildRange) queryBuildRange = queryBuildDataSource.addRange(fieldnum(ProdTable, ProdId)); if (prodTable) queryBuildRange.value(prodTable.ProdId);//根据工单号来筛选 return query;}
最后修改main函数,调用initFromArgs来初始化就可以了:
static void main(Args args){ TestProdReport prodReport = new TestProdReport(); ; prodReport.getLast(); prodReport.initFromArgs(args); if (prodReport.prompt()) prodReport.run();}
注意这里在调用initFromArgs函数之前调用了一次getLast(),如果不在这里调用一次getLast(),后面的prompt()会在内部调用一次getLast(),把上一次pack的query读入,显示出来的对话框中看到的是上一次的内容,也就把initFromArgs()创建的Query的给覆盖了,所以这里先调用一次getLast(),prompt()会看到这个函数已经调用过了就不再调用了,所以这行是必须的。当前纪录的工单号相应的会出现在对话框中的过滤选项内:
以上就是如何在报表中根据form的当前纪录来过滤的实现办法。这里还要说一个runbaseReport的initParmDefault()函数,它是在第一次运行报表时会被调用,一旦有pack/unpack成功之后就不会在调用,在这里可以针对报表的第一次运行做一些初始化的工作,针对我们的这个报表是不需要的。
再把我们的这个例子扩展开,如果说在ProdTable form中选中了多条纪录,要在报表上打印出这多条纪录又该怎么办呢?
我们调用传入纪录的isFormDataSource()来判断是否来自于form的DataSource,如果是则使用common.Datasource()方法获得formDataSource,再使用formDatasource.getFirst(true)、formDatasource.getNext()轮询获取选中纪录的主键,然后设置到Query的Range中就可以了。基本结构是一样的,这里就不再演示了,下面的链接可以下载这个演示的project,从AXAPTA 3.0系统导出: