AxInternalBase API
创建Ax<Table>类的目的是当创建和更新AX表中的记录时有一个可用的API。AxInternalBase API的设计目标如下:
1.该API易于使用
2.该API必须处理相关字段。当一个字段更新时可以应用默认值。比如,当更改销售订单的客户账户时,将地址字段从客户记录复制到销售订单记录时,地址字段应该可以用默认值
3.该API必须处理字段更新的顺序。比如,发票帐号字段是一个相关字段,当客户账户改变时,该字段应该变成默认值。
4.字段默认值可能不总是提供期望的最终结果。考虑一个例子:首先更新如果发票帐号,其他字段的值是默认的,然后更新客户帐号,其他字段的值是默认的,这样默认值将会重写显式提供的发票帐号字段。
5.该API必须能从编码规则取得编码或者标识符。比如,当创建一个销售订单时,销售订单号必须从销售订单的编码规则中取得。处理这些的逻辑在这些类中实现。
新的Ax<Table>类必须继承自AxInternalBase类。AxInternalBase类追踪为了给表的某个字段设定一个特定的值都执行了哪些方法,可以外部或者内部实现追踪。外部的,比如,可以用一个特定的值调用AxSalesTable类的parmCustAccount方法。内部的,可以调用AxSalesTable的parmInvoiceAccount方法,因为它是个相关字段,当parmCustAccount 方法执行时,它应该变成默认值。通过监测执行的方法,AxinternalBase类确保外部设定的值不被重写。
类AxBC(译注:上下文都没提到过AxBC这个东东,应该就是Ax<Table>类)的声明中必须声明一个与AxBC相关的表类型的记录变量。因此AxSalesTable的类声明如下所示:
class AxSalesTable extends AxInternalBase
{
SalesTable salesTable;
}
在Ax<Table>类中,必须为相关表的每一个字段创建一个实例方法。方法名必须与字段名相同,以parm为前缀,并且必须使用如下模板:{
SalesTable salesTable;
}
public DataType parmFieldName(DataType _fieldName
= literal)
{
if (!prmisdefault(_fieldName))
{
this.setField(fieldNum(TableName,FieldName), _fieldName);
}
return tableName.fieldName;
}
如果该实例方法执行的时候不用参数,会返回字段的值,如果用参数执行该方法,setField方法会被执行,参数为表字段的ID和传入的参数。= literal)
{
if (!prmisdefault(_fieldName))
{
this.setField(fieldNum(TableName,FieldName), _fieldName);
}
return tableName.fieldName;
}
类AxInternalBase的实例方法setField查看该字段是否已经被设定了某个特定的值,如果没有设定会给该字段分配当前值。同时,更新其他已经分配了值的一系列字段。setField方法的逻辑如9-4所示。
图9-4.setField方法的逻辑流程图
在类AxSalesTable的parmCustAccount方法体中可以看一个setField方法的具体实现(译注:原文缩减了该方法,这里将方法体全部列出):
public str parmCustAccount(str _custAccount = '')
{
DictField dictField;
;
if (!prmisdefault(_custAccount))
{
dictField = new DictField(tablenum(SalesTable),fieldnum(SalesTable, CustAccount));
if(this.valueMappingInbound())
{
this.validateInboundString(_custAccount, dictField, this.mapPolicy().xMLMapCustAccount());
_custAccount = this.axCustAccount(_custAccount);
}
else
{
this.validateInboundString(_custAccount, dictField);
}
this.setField(fieldnum(SalesTable, CustAccount), _custAccount);
}
if (this.valueMappingOutbound())
{
return this.axCustAccount(salesTable.CustAccount);
}
else
{
return salesTable.CustAccount;
}
}
如果必须解决内在字段关系,就是当一个字段的值更改时表的另一个字段要设置成某个特定的值,必须为两个字段创建实例方法。方法名必须与字段名相同,用set作为前缀,并采用如下模板,加粗的文本必须用当前的表和字段名修改(译注:其中Table和FieldName为加粗的文本)。{
DictField dictField;
;
if (!prmisdefault(_custAccount))
{
dictField = new DictField(tablenum(SalesTable),fieldnum(SalesTable, CustAccount));
if(this.valueMappingInbound())
{
this.validateInboundString(_custAccount, dictField, this.mapPolicy().xMLMapCustAccount());
_custAccount = this.axCustAccount(_custAccount);
}
else
{
this.validateInboundString(_custAccount, dictField);
}
this.setField(fieldnum(SalesTable, CustAccount), _custAccount);
}
if (this.valueMappingOutbound())
{
return this.axCustAccount(salesTable.CustAccount);
}
else
{
return salesTable.CustAccount;
}
}
protected void setFieldName()
{
if (this.isMethodExecuted(funcName(), fieldNum
(TableName, FieldName)))
{
return;
}
// Additional code goes here.
}
由于这个方法可能会被执行很多次,isMethodExecuted 方法用于探测该方法是否被执行过并且该字段是否被赋了某个值,isMethodExecuted 方法的逻辑如图9-5所示{
if (this.isMethodExecuted(funcName(), fieldNum
(TableName, FieldName)))
{
return;
}
// Additional code goes here.
}
图9-5.方法isMethodExecuted 的逻辑流程
如果依赖于其他字段(译注:原文为Dependencies on other fields ,意思是如果当前字段依赖于其他字段的话,就按如下方式执行)必须按如下方式编程
this.setAnotherFieldName();
if (this.isFieldSet(fieldNum(TableName,
AnotherFieldName)))
{
this.fieldName(newValue);
}
首先,需要执行当前字段所依赖字段的set方法,如果该字段所依赖的字段更改了,该方法给其赋新值。然后需要判断依赖字段是否被赋予了某个新值。如果赋了新值,则给当前字段赋新值。可以看一下类AxSalesTable 的setPaymMode 方法的实现:if (this.isFieldSet(fieldNum(TableName,
AnotherFieldName)))
{
this.fieldName(newValue);
}
protected void setPaymMode()
{
if (this.isMethodExecuted(funcname(), fieldnum(SalesTable, PaymMode)))
{
return;
}
this.setInvoiceAccount();
if (this.isFieldSet(fieldnum(SalesTable, InvoiceAccount)))
{
this.parmPaymMode(this.invoiceAccount_CustTableRecord().PaymMode);
}
}
{
if (this.isMethodExecuted(funcname(), fieldnum(SalesTable, PaymMode)))
{
return;
}
this.setInvoiceAccount();
if (this.isFieldSet(fieldnum(SalesTable, InvoiceAccount)))
{
this.parmPaymMode(this.invoiceAccount_CustTableRecord().PaymMode);
}
}
为了设置和获取当前记录,必须重写类AxSaelsTable的currentRecord 方法。重写必须使用如下模板
protected TableName currentRecord(TableName
_tableName = tableName)
{
if (!prmisdefault(_tableName))
{
super(_tableName);
tableName = _tableName;
}
else
{
super();
}
return tableName;
}
currentRecord调用super执行类AxInternalBase 的实例方法currentRecord,实例方法currentRecord 的逻辑如图9-6所示:_tableName = tableName)
{
if (!prmisdefault(_tableName))
{
super(_tableName);
tableName = _tableName;
}
else
{
super();
}
return tableName;
}
图9-6.currentRecord 方法的逻辑流程
可以看一下类AxSalesTable的currentRecord方法的实现:
protected SalesTable currentRecord(SalesTable
_salesTable = salesTable)
{
if (!prmisdefault(_salesTable))
{
super(_salesTable);
salesTable = _salesTable;
}
else
{
super();
}
return salesTable;
}
要更新一条已存在的记录,表记录必须通过表记录的实例方法传递给Ax<Table>对象,方法名必须与表名相同,并且必须采用如下模板:_salesTable = salesTable)
{
if (!prmisdefault(_salesTable))
{
super(_salesTable);
salesTable = _salesTable;
}
else
{
super();
}
return salesTable;
}
public TableName tableName(TableName _tableName = tableName)
{
if (!prmisdefault(_tableName))
{
this.setCurrentRecordFromExternal(_tableName);
}
return this.currentRecord();
}
setCurrentRecordFromExternal 实例方法执行currentRecord 方法,并清除所有内部变量以便准备一个新的对象来应对新纪录的更改。{
if (!prmisdefault(_tableName))
{
this.setCurrentRecordFromExternal(_tableName);
}
return this.currentRecord();
}
为确保在插入或更新记录之前调用所有的默认方法,必须重写类AxInternalBase 的setTableFields 方法,该方法应包含对所有默认方法的调用。可以看一下类AxSalesTable 的setTableFields 方法的部分实现:
protected void setTableFields()
{
SalesTableLinks salesTableLinks;
;
super();
useMapPolicy = false;
this.setCashDisc();
this.setCommissionGroup();
this.setContactPersonId();
this.setCurrencyCode();
this.setCustAccount();
this.setCustGroup();
this.setDeliveryAddress();
this.setDeliveryCity();
// And so on
{
SalesTableLinks salesTableLinks;
;
super();
useMapPolicy = false;
this.setCashDisc();
this.setCommissionGroup();
this.setContactPersonId();
this.setCurrencyCode();
this.setCustAccount();
this.setCustGroup();
this.setDeliveryAddress();
this.setDeliveryCity();
// And so on