透过采购单"仓库"下拉列表框显示过程,分析"TableDirect"类型的reference

看到采购单的仓库列表的列的显示格式,只是设置为了"TableDirect"的reference,这个reference没有什么进一步的设置,比如表明,列明,等等,心生好奇,看我来给大家找出其中缘由!

1.获得下拉列表数据SQL的生成

下面是窗口初始化时生成下拉框时候的调用堆栈,下拉框数据的大部分SQL就是这时候生成的

MLookupFactory.getLookup_TableDir(Properties, Language, int, String) line: 624

MLookupFactory.getLookupInfo(Properties, int, int, int, Language, String, int, boolean, String) line: 192

GridFieldVO.initFinish() line: 504

GridFieldVO.create(Properties, int, int, int, int, boolean, ResultSet) line: 201

GridTabVO.createFields(GridTabVO) line: 272

GridTabVO.getFields() line: 444

GridTab.loadFields() line: 353

GridTab.loadTab() line: 296

GridTab.initTab(boolean) line: 287

GridWindow.initTab(int) line: 195

APanel.initPanel(int, int, MQuery) line: 753

AWindow.initWindow(int, MQuery) line: 102

AMenuStartItem.startWindow(int, int) line: 249

AMenuStartItem.run() line: 147

"TableDirect"类型的reference,下拉列表框数据的SQL生成过程如下:

wps_clip_p_w_picpath-17736

 

1.1根据数据库列名:M_Warehouse_ID, 得到键值列名M_Warehouse_ID和表名M_Warehouse,期间如果此列不是以"_ID"结尾则会报错

  • 列名检察

wps_clip_p_w_picpath-26451

  • 得到列名

wps_clip_p_w_picpath-32126

  • 得到表名

wps_clip_p_w_picpath-2180

getZoomTableName():

ADempiere

对于本例, 键值列名M_Warehouse_ID, 表明M_Warehouse

1.2取出表中"Identifier"的列

  • 表定义如下:

ADempiere

ADempiere

ADempiere

  • 最后根据表的列定义,拼为下面形式的sql:

NVL(%tableName%.col1Name,'-1')||'_'||NVL(%tableName%.col2Name,'-1')||'_'||NVL(%tableName%.col3Name,'-1')

取"Identifier"的SQL

SELECT c.ColumnName,c.IsTranslated,c.AD_Reference_ID,

c.AD_Reference_Value_ID,t.AD_Window_ID,t.PO_Window_ID , c.ColumnSQL

FROM adempiere.AD_Table t

INNER JOIN adempiere.AD_Column c ON (t.AD_Table_ID=c.AD_Table_ID)

WHERE TableName='M_Warehouse' AND c.IsIdentifier='Y' ORDER BY c.SeqNo

对于本例,因为M_Warehouse表只定义了一个Identifier列,就是'name'列,因此结果为NVL(M_Warehouse.name,'-1')

1.3.本例的最后结果为

select

M_Warehouse.M_Warehouse_ID, //tableName.keyColName

NULL,  //固定的,和后面取数据方式有关

NVL(M_Warehouse.name,'-1'), //2步骤得到的SQL

M_Warehouse.IsActive //固定的

from  M_Warehouse //from tableName

2.从数据库根据上面SQL查询数据并填充下拉列表 2.1.有3种情况,会触发:

  • l 窗口第一次初始化

MLookup.loadData(boolean) line: 618

MLookup.getData(boolean, boolean) line: 370

MLookup.getData(boolean, boolean, boolean, boolean) line: 400

MLookup(Lookup).fillComboBox(boolean, boolean, boolean, boolean) line: 282

VLookup.(String, boolean, boolean, boolean, Lookup) line: 281

VEditorFactory.getEditor(GridTab, GridField, boolean) line: 139

GridController.init() line: 416

GridController.initGrid(GridTab, boolean, int, APanel, GridWindow, boolean) line: 376

APanel.initPanel(int, int, MQuery) line: 808

AWindow.initWindow(int, MQuery) line: 102

AMenuStartItem.startWindow(int, int) line: 249

AMenuStartItem.run() line: 147

  • l 刷新(右键->重新查询)

MLookup.loadData(boolean) line: 618

MLookup.getData(boolean, boolean) line: 370

MLookup.getData(boolean, boolean, boolean, boolean) line: 400

MLookup(Lookup).fillComboBox(boolean, boolean, boolean, boolean) line: 282

MLookup.refresh(boolean) line: 598

MLookup.refresh() line: 573

VLookup.actionRefresh() line: 1471

VLookup.actionPerformed(ActionEvent) line: 741

CMenuItem(AbstractButton).fireActionPerformed(ActionEvent) line: 1995

AbstractButton$Handler.actionPerformed(ActionEvent) line: 2318

DefaultButtonModel.fireActionPerformed(ActionEvent) line: 387

  • l 更改采购单上的"组织"字段,并将焦点移动到仓库字段时,会根据当前新的组织去获得仓库列表

MLookup.loadData(boolean) line: 618

MLookup.getData(boolean, boolean) line: 382

MLookup.getData(boolean, boolean, boolean, boolean) line: 400

MLookup(Lookup).fillComboBox(boolean, boolean, boolean, boolean) line: 282

VLookup.focusGained(FocusEvent) line: 1510

2.2.获取数据MLookup$MLoader.run()

l 基于前面获得的SQL,加上一些条件限制,比如组织ID等,本例最后SQL如下:

SELECT M_Warehouse.M_Warehouse_ID,NULL,COALESCE(M_Warehouse.Name,'-1'),M_Warehouse.IsActive

FROM adempiere.M_Warehouse

WHERE M_Warehouse.AD_Client_ID IN(0,11)

AND M_Warehouse.AD_Org_ID IN(50007,0,50004,50005,50006,1000000,50000,50001,50002,11,12)

AND M_Warehouse.M_Warehouse_ID NOT IN (

SELECT Record_ID FROM adempiere.AD_Private_Access WHERE AD_Table_ID = 190 AND AD_User_ID <> 101 AND IsActive = 'Y' )

AND M_Warehouse.AD_Org_ID=50001 ORDER BY 3

上面的"COALESCE"不是前面的"NVL",这应该是我测试的时候用了postgresql的结果,中间进行了转换

l 从上面SQL结果中取出第一列(数字)或者第二列(字符)作为key,第3列作为name,放进一个map中,返回给前面显示下拉列表

代码说明:

boolean isNumber = m_info.KeyColumn.endsWith("_ID");

ADempiere

本例中是取出第一列和第三列,第二列在其他功能中应该会用到,就是key是字符的时候

3.分析结果

从上面可分析出,如果想让reference位tableDirect类型的列能显示出下拉列表的数据,则必须满足下面条件:

  • 列名必须以"_ID"位结尾,且区分大小写,否则报错
  • 从列名去掉后面的"_ID"得到的是存放列表数据的那个表的表名,从AD的命名规则来看,这个应该没问题
  • 这个表必须至少有一列定义为"Identifier"列,否则会报错
  • 这个表必须有"IsActive"列,否则会报SQL错误, 这个按照AD表的规则,应该没问题
  • 最后下拉列表的key是那个表的主键,显示的是"Identifier"拼出来的列的值

好了,通过上面的分析,我们机会看到,为什么AD简单的使用了一个"TableDirect"的reference就能出个下拉列表,这需要建立在上面的一系列规则之上,否则的话就会出错喽!