AX5.0 Table Template
AX系统只允许属于Main和Group组的table使用模板,而一个表是否可以有空模板是通过两种方式来设置的,一是在table的method下加一个静态方法canAllowBlankRecordTemplate,返回true时在第一次创建模板时会设置允许使用空模板。二是直接编辑模板来决定是否使用空模板,点击Basic->Setup->Record Template,弹出form 1,勾选Allow bland可以设置该table(reference table ID是该table的ID)可以使用空模板。
1 |
下边我们将使用一个例子来演示如何创建和使用模板,以及系统在代码级别是怎么做的。
一. 使用模板
1.1 首先新建一个表davTempalte,并设置该表的tableGroup为Main,然后添加Itemid,ItemName,InventDimId三个field,如图1-1-1
1-1-1 |
1.2 新建一个form davTemplate,添加davTemplate表为数据源,在form的design中新建Grid,添加davTemplate数据源的字段Itemid,ItemName,InventDimId,如图1-2-1。
1-2-1 |
1.3 打开davTempalte form,新建一条记录并保存,如图1-3-1.
1-3-1 |
1.4 使用新建的记录创建一个模板。首先在刚建立的记录上右键单击,在弹出的快捷菜单中选record info,弹出form 1-4-1. 本例我们只讲解company template,所以请点击company accounts template, 弹出form1-4-2,在form1-4-2中,Name为模板的名字,Description是该模板的描述。点击Ok就可以新建一个company template,这里我添加的模板名是davidTest
1-4-1
|
1-4-2 |
1.5 使用模板创建记录。在图1-3-1的form上,点击左上角的新建按钮,弹出模板选择对话框如图1-5-1,选择模板davidTest,点击OK就会根据模板上的数据来创建一条新的记录,你会发现使用模板建立的新记录与我们用于建立模板的记录拥有相同的数据。
注意form1-5-1左下角的Do not ask Again,如果你勾选了该选框,下次再新建记录时会默认使用davidTest模板并且不再弹出此对话框。如果你想在你勾选Do not ask again以后,在新建记录时重新弹出此对话框,请在任意一条已经建立好的记录上右键单击,在弹出的快捷菜单中选Record info,弹出form1-5-2,单击右下角的Show template selection就可以了。
1-5-1
|
1-5-2 |
二. 模板是如何保存的
2.1使用用于浏览container的类查看我们刚才建立的模板可以得到form 2-1-1.
2-1-1 |
2.2 用来保存模板数据的container的结构
该结构用图表表示如图2-2-1,模板一般都是在form上建立的,它会有一组Datasource,其中有一个表会是主datasource,其余表都连接到这个表,如InventTable form上的主Datasource是inventtable。对于这样的一个主table,所有以它为主datasource的template都会放到一个容器里面,我们暂且称之为以某table位主datasource的一组datasource的模板被放在一个容器里,这个容器对应于我们图2-2-1中的caontainer 1.
如果以某table位主datasource的一组datasource有多个模板,那么它们对应于图2-2-1中的 container template1 ~ container template n,注意中间的容器我没有画出。
现在我们把焦点移到这组datasource的第一个模板上,也就是container template 1, 它有4个元素,第一个是template name,也就是模板的名字;第二个是is default,指该模板是否为默认使用的模板;第三个是一个容器container tables,它包含了这组datasource中每个表的模板数据;第四个是description,该模板的描述信息。
然后我们来看container tables, 如果这组datasource 有N 个table,那么会有N个容器来分别存放每一个table的模板数据,这些容器对应于2-2-1中的container table 1~ container table n,我没有画出中间的n-2个容器。如InventTable form 有4个Datasource,那么对应的就有4个container。
现在我们把注意力集中到存储这组datasource中某一个table的模板数据的容器上,如container table 1,它有2个元素,第一个是tableid,第二个是容器,用来存储该table所有不属于系统字段的字段的fieldId 和值,这个容器会包含很多小的容器如container field 1,这些容器只有两个元素,第一个是字段的field id,第二个是该字段对于的模板值。
到这里,您可以参照图2-2-1,2-1-1及步骤一中我们如何创建使用模板的理解模板究竟是怎么保存的。
2-2-1 |
2.3 代码是如何运行的
这里我不打算把代码拷过来,我只写明路径和关键的变量。
当我们打开form1-4-1时,是在打开Forms/SysRecordInfo, 在SysRecordInfo/methods/init里你可以看到变量mainTables(list),通过调用SysRecordTemplate的静态方法mainTables赋值。
mainTables = SysRecordTemplate::mainTables(callerForm, common.TableId)
callerform是调用form 1-4-1的父form,common 是我们右键点击时所在的记录,在该语句执行以后maintables就保存了所有Join到该common的table的活动记录,同时也包含了common在里面。如在Inventtable form上打开form1-4-1,maintables就会含有4条记录,分别是每个datasource的当前活动记录。
当form1-4-2关闭时,会返回新模板所要使用的Name和description.
当你点击form 1-4-1的company accounts template 时,首先会调用Forms/ SysRecordInfo/methods/ setAsCompanyDefault方法,该方法首先声明并且实例化变量sysRecordTemplateStorage ,该变量实际上是class SysRecordTemplateStorageCompany的一个实例,然后调用sysRecordTemplateStorage.set方法,该方法会弹出form1-4-2, form1-4-2关闭以后,会根据该组Datasource是否有可用的模板来分两类来给变量sysRecordTemplateTable赋值。该变量是table sysRecordTemplateTable的实例,它有3个字段,Data字段指对应于图2-2-1中的container 1,table字段指该组datasource的主datasource的tableid,allowBlank字段指该组datasource所在的form在新建记录时是否可以使用空模板。在set方法里你可以看到container 1被加入的第一个元素就是版本号。当退出set方法时,我们的新模板就已经被写入数据库了。
在set方法里,给新模板添加数据是由下面这条语句执行的:
sysRecordTemplateSystemTable.Data += [[description, sysRecordTemplateSystemTable?false:true, this.packList(),details]];
packList方法的路径是classes/ SysRecordTemplateStorage/packList,该方法会遍历我们前边提到的mainTables,把每一条记录的没一个字段的每一个元素都加到容器中,但只添加非系统字段。将记录的字段加入容器是由下面这条语句执行的:
packedCommon = SysRecordTemplateStorage::packCommon(tmpCommon);
在理解了图2-2-1以后,这些代码是比较容易看懂的。
三. 模板的使用的代码实现
当你新建记录是,系统会按图3-1调用方法。
3-1 |
我们把焦点放到Classes/sysRecordTemplate/createRecord方法上。
一进入该方法,注意storageCompany变量是使用common变量实例化的,也就是说所有的datasource的都会使用它当前记录来实例化变量storageCompany,每一个datasource都会调用createRecord方法。当进入该方法时,下面的代码:
If(this.isCached())
是不成立的,因为还没有向cache添加数据,所以走else语句块。下面这句
companyData = storageCompany.get();
在执行之后companyData对应于图2-2-1中的container 1.下面这句:
recordValues = this.promptSelect(userData, condel(companyData,1,1), copyData, allowBlank);
会弹出form1-5-1, form1-5-1关闭时会返回一个container,对应于图2-2-1中的container tables。
RecordValues就是我们要使用的模板。那么是如何根据模板来初始各表的数据的呢?
根据FORM2-2-1可以看到一个container tables有很多个container table,而最关键的代码就在if(this.iscached()) 和 if (recordValues) 下的语句块了。代码如下:
this.initValue(this.getFirstValues(recordValues), forceCompanyTemplate != '', onlyEditableFieldsOnCreate);
recordValues = condel(recordValues,1,1);
if (recordvalues)
this.setCache(recordValues);
首先,取container tables 的第一个容器container 1(对应form2-2-1), 然后使用方法getFirstValues 方法得到container fields容器(对应于form2-2-1),在InitValue方法里面遍历container field里边的 container field容器,并根据fieldId 和 Values 来给datasource调用createRecord时传入的common赋值(这样说是不准确的,common并不是直接传递过来的)。然后把用过的这个container table 从recordValues(对应2-2-1中的container talbes)中删除,并将剩余的container table也即recordValues放入cache。当子datasourece调用是,会按照同样的步骤来给其common赋值,但会走If(this.isCached())的语句块。等createRecord方法退出,数据就都被使用模板赋值了。