[Axapta]带参数的RunbaseReport最佳实践
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系统导出:https://files.cnblogs.com/duanshuiliu/TestProdReport.zip