diff --git a/org.adempiere.base.callout/META-INF/MANIFEST.MF b/org.adempiere.base.callout/META-INF/MANIFEST.MF index 580c187484..9785d4bf91 100644 --- a/org.adempiere.base.callout/META-INF/MANIFEST.MF +++ b/org.adempiere.base.callout/META-INF/MANIFEST.MF @@ -13,3 +13,4 @@ Export-Package: org.compiere.model Bundle-ClassPath: . Automatic-Module-Name: org.adempiere.base.callout Bundle-Vendor: iDempiere Community +Import-Package: org.osgi.service.component.annotations;version="1.3.0" diff --git a/org.adempiere.base.callout/OSGI-INF/costadjustmentcalloutfactory.xml b/org.adempiere.base.callout/OSGI-INF/costadjustmentcalloutfactory.xml deleted file mode 100644 index 1638494ee1..0000000000 --- a/org.adempiere.base.callout/OSGI-INF/costadjustmentcalloutfactory.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/org.adempiere.base.callout/OSGI-INF/org.adempiere.base.callout.factory.AnnotationBasedColumnCalloutFactoryImpl.xml b/org.adempiere.base.callout/OSGI-INF/org.adempiere.base.callout.factory.AnnotationBasedColumnCalloutFactoryImpl.xml new file mode 100644 index 0000000000..e7391656db --- /dev/null +++ b/org.adempiere.base.callout/OSGI-INF/org.adempiere.base.callout.factory.AnnotationBasedColumnCalloutFactoryImpl.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.base.callout/build.properties b/org.adempiere.base.callout/build.properties index b5151e0f23..24af2f8751 100644 --- a/org.adempiere.base.callout/build.properties +++ b/org.adempiere.base.callout/build.properties @@ -1,5 +1,4 @@ bin.includes = META-INF/,\ - OSGI-INF/costadjustmentcalloutfactory.xml,\ OSGI-INF/,\ . output.. = target/classes/ diff --git a/org.adempiere.base.callout/src/org/adempiere/base/callout/BPartnerCalloutFactory.java b/org.adempiere.base.callout/src/org/adempiere/base/callout/BPartnerCalloutFactory.java deleted file mode 100644 index e49e88f217..0000000000 --- a/org.adempiere.base.callout/src/org/adempiere/base/callout/BPartnerCalloutFactory.java +++ /dev/null @@ -1,30 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2013 Diego Ruiz * - * Copyright (C) 2013 Trek Global * - * This program is free software; you can redistribute it and/or modify it * - * under the terms version 2 of the GNU General Public License as published * - * by the Free Software Foundation. This program is distributed in the hope * - * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * See the GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, write to the Free Software Foundation, Inc., * - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * - *****************************************************************************/ -package org.adempiere.base.callout; - -import org.adempiere.base.IColumnCallout; -import org.adempiere.base.IColumnCalloutFactory; -import org.adempiere.model.CalloutBPartnerQuickEntry; -import org.compiere.model.MBPartner; - -public class BPartnerCalloutFactory implements IColumnCalloutFactory { - - @Override - public IColumnCallout[] getColumnCallouts(String tableName, String columnName) { - if (tableName.equalsIgnoreCase(MBPartner.Table_Name)) { - return new IColumnCallout[]{new CalloutBPartnerQuickEntry()}; - } - return null; - } -} diff --git a/org.adempiere.base.callout/src/org/adempiere/base/callout/CostAdjustmentCalloutFactory.java b/org.adempiere.base.callout/src/org/adempiere/base/callout/CostAdjustmentCalloutFactory.java deleted file mode 100644 index 37f4daf7e2..0000000000 --- a/org.adempiere.base.callout/src/org/adempiere/base/callout/CostAdjustmentCalloutFactory.java +++ /dev/null @@ -1,186 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2013 Heng Sin Low * - * Copyright (C) 2013 Trek Global * - * This program is free software; you can redistribute it and/or modify it * - * under the terms version 2 of the GNU General Public License as published * - * by the Free Software Foundation. This program is distributed in the hope * - * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * See the GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, write to the Free Software Foundation, Inc., * - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * - *****************************************************************************/ -package org.adempiere.base.callout; - -import java.math.BigDecimal; -import java.util.Properties; - -import org.adempiere.base.IColumnCallout; -import org.adempiere.base.IColumnCalloutFactory; -import org.compiere.model.GridField; -import org.compiere.model.GridTab; -import org.compiere.model.GridTable; -import org.compiere.model.I_M_InventoryLine; -import org.compiere.model.MAcctSchema; -import org.compiere.model.MClient; -import org.compiere.model.MCost; -import org.compiere.model.MCostElement; -import org.compiere.model.MDocType; -import org.compiere.model.MInventory; -import org.compiere.model.MProduct; -import org.compiere.util.Env; -import org.compiere.util.Msg; - -/** - * @author hengsin - * - */ -public class CostAdjustmentCalloutFactory implements IColumnCalloutFactory { - - /** - * - */ - public CostAdjustmentCalloutFactory() { - } - - /* (non-Javadoc) - * @see org.adempiere.base.IColumnCalloutFactory#getColumnCallouts(java.lang.String, java.lang.String) - */ - @Override - public IColumnCallout[] getColumnCallouts(String tableName, - String columnName) { - if (tableName.equalsIgnoreCase(I_M_InventoryLine.Table_Name)) { - if (columnName.equalsIgnoreCase(I_M_InventoryLine.COLUMNNAME_M_Product_ID)) - return new IColumnCallout[]{new CostAdjustmentLineProduct()}; - else if (columnName.equalsIgnoreCase(I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID)) - return new IColumnCallout[]{new CostAdjustmentLineASI()}; - } - - return null; - } - - /** - * callout for m_product_id - */ - private static class CostAdjustmentLineProduct implements IColumnCallout { - @Override - public String start(Properties ctx, int WindowNo, GridTab mTab, - GridField mField, Object value, Object oldValue) { - String trxName = null; - if ( mTab != null - && mTab.getTableModel() != null) { - GridTable gt = mTab.getTableModel(); - if (gt.isImporting()) { - trxName = gt.get_TrxName(); - } - } - MInventory inventory = new MInventory(ctx, (Integer) mTab.getValue("M_Inventory_ID"), trxName); - if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(inventory.getC_DocType().getDocSubTypeInv())) { - String costingMethod = inventory.getCostingMethod(); - if (value == null) { - mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, BigDecimal.ZERO); - mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, BigDecimal.ZERO); - } else { - MProduct product = MProduct.get(ctx, (Integer) value); - MClient client = MClient.get(ctx); - MAcctSchema as = client.getAcctSchema(); - - String costingLevel = product.getCostingLevel(as); - if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(costingLevel)) { - mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, BigDecimal.ZERO); - mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, BigDecimal.ZERO); - }else { - Object asiValue = mTab.getValue(I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID); - int M_ASI_ID = asiValue != null ? (Integer)asiValue : 0; - int AD_Org_ID = inventory.getAD_Org_ID(); - int C_Currency_ID = inventory.getC_Currency_ID(); - - if (as.getC_Currency_ID() != C_Currency_ID) - { - MAcctSchema[] ass = MAcctSchema.getClientAcctSchema(ctx, client.get_ID()); - for (int i = 0; i < ass.length ; i ++) - { - MAcctSchema a = ass[i]; - if (a.getC_Currency_ID() == C_Currency_ID) - as = a ; - } - } - MCost cost = product.getCostingRecord(as, AD_Org_ID, M_ASI_ID, costingMethod); - if (cost == null) { - if (!MCostElement.COSTINGMETHOD_StandardCosting.equals(costingMethod)) { - mTab.setValue(mField, null); - return Msg.getMsg(Env.getCtx(), "NoCostingRecord"); - } - } - - mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, cost.getCurrentCostPrice()); - mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, cost.getCurrentCostPrice()); - } - } - } - return null; - } - } - - /** - * callout for m_attributesetinstance_id - */ - private class CostAdjustmentLineASI implements IColumnCallout { - @Override - public String start(Properties ctx, int WindowNo, GridTab mTab, - GridField mField, Object value, Object oldValue) { - String trxName = null; - if ( mTab != null - && mTab.getTableModel() != null) { - GridTable gt = mTab.getTableModel(); - if (gt.isImporting()) { - trxName = gt.get_TrxName(); - } - } - MInventory inventory = new MInventory(ctx, (Integer) mTab.getValue("M_Inventory_ID"), trxName); - if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(inventory.getC_DocType().getDocSubTypeInv())) { - String costingMethod = inventory.getCostingMethod(); - Object productValue = mTab.getValue(I_M_InventoryLine.COLUMNNAME_M_Product_ID); - if (productValue == null || ((Integer)productValue).intValue() == 0) return null; - - MProduct product = MProduct.get(ctx, (Integer)productValue); - int M_ASI_ID = value != null ? (Integer)value : 0; - int AD_Org_ID = inventory.getAD_Org_ID(); - int C_Currency_ID = inventory.getC_Currency_ID(); - - MClient client = MClient.get(ctx); - MAcctSchema as = client.getAcctSchema(); - - if (as.getC_Currency_ID() != C_Currency_ID) - { - MAcctSchema[] ass = MAcctSchema.getClientAcctSchema(ctx, client.get_ID()); - for (int i = 0; i < ass.length ; i ++) - { - MAcctSchema a = ass[i]; - if (a.getC_Currency_ID() == C_Currency_ID) - as = a ; - } - } - MCost cost = product.getCostingRecord(as, AD_Org_ID, M_ASI_ID, costingMethod); - if (cost == null) { - if (!MCostElement.COSTINGMETHOD_StandardCosting.equals(costingMethod)) { - mTab.setValue(mField, null); - return Msg.getMsg(Env.getCtx(), "NoCostingRecord"); - } - } - - if (cost != null) { - BigDecimal currentCost = (BigDecimal) mTab.getValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice); - if (currentCost == null || currentCost.compareTo(cost.getCurrentCostPrice())==0) return null; - - mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, cost.getCurrentCostPrice()); - mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, cost.getCurrentCostPrice()); - } - - } - return null; - } - - } -} diff --git a/org.adempiere.base.callout/src/org/adempiere/base/callout/CostAdjustmentLineASI.java b/org.adempiere.base.callout/src/org/adempiere/base/callout/CostAdjustmentLineASI.java new file mode 100644 index 0000000000..9fa44b07f8 --- /dev/null +++ b/org.adempiere.base.callout/src/org/adempiere/base/callout/CostAdjustmentLineASI.java @@ -0,0 +1,108 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.base.callout; + +import java.math.BigDecimal; +import java.util.Properties; + +import org.adempiere.base.IColumnCallout; +import org.adempiere.base.annotation.Callout; +import org.compiere.model.GridField; +import org.compiere.model.GridTab; +import org.compiere.model.GridTable; +import org.compiere.model.I_M_InventoryLine; +import org.compiere.model.MAcctSchema; +import org.compiere.model.MClient; +import org.compiere.model.MCost; +import org.compiere.model.MCostElement; +import org.compiere.model.MDocType; +import org.compiere.model.MInventory; +import org.compiere.model.MProduct; +import org.compiere.util.Env; +import org.compiere.util.Msg; + +/** + * + * @author hengsin + * + */ +@Callout(tableName = I_M_InventoryLine.Table_Name, columnName = I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID) +public class CostAdjustmentLineASI implements IColumnCallout { + @Override + public String start(Properties ctx, int WindowNo, GridTab mTab, + GridField mField, Object value, Object oldValue) { + String trxName = null; + if ( mTab != null + && mTab.getTableModel() != null) { + GridTable gt = mTab.getTableModel(); + if (gt.isImporting()) { + trxName = gt.get_TrxName(); + } + } + MInventory inventory = new MInventory(ctx, (Integer) mTab.getValue("M_Inventory_ID"), trxName); + if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(inventory.getC_DocType().getDocSubTypeInv())) { + String costingMethod = inventory.getCostingMethod(); + Object productValue = mTab.getValue(I_M_InventoryLine.COLUMNNAME_M_Product_ID); + if (productValue == null || ((Integer)productValue).intValue() == 0) return null; + + MProduct product = MProduct.get(ctx, (Integer)productValue); + int M_ASI_ID = value != null ? (Integer)value : 0; + int AD_Org_ID = inventory.getAD_Org_ID(); + int C_Currency_ID = inventory.getC_Currency_ID(); + + MClient client = MClient.get(ctx); + MAcctSchema as = client.getAcctSchema(); + + if (as.getC_Currency_ID() != C_Currency_ID) + { + MAcctSchema[] ass = MAcctSchema.getClientAcctSchema(ctx, client.get_ID()); + for (int i = 0; i < ass.length ; i ++) + { + MAcctSchema a = ass[i]; + if (a.getC_Currency_ID() == C_Currency_ID) + as = a ; + } + } + MCost cost = product.getCostingRecord(as, AD_Org_ID, M_ASI_ID, costingMethod); + if (cost == null) { + if (!MCostElement.COSTINGMETHOD_StandardCosting.equals(costingMethod)) { + mTab.setValue(mField, null); + return Msg.getMsg(Env.getCtx(), "NoCostingRecord"); + } + } + + if (cost != null) { + BigDecimal currentCost = (BigDecimal) mTab.getValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice); + if (currentCost == null || currentCost.compareTo(cost.getCurrentCostPrice())==0) return null; + + mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, cost.getCurrentCostPrice()); + mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, cost.getCurrentCostPrice()); + } + + } + return null; + } + +} diff --git a/org.adempiere.base.callout/src/org/adempiere/base/callout/CostAdjustmentLineProduct.java b/org.adempiere.base.callout/src/org/adempiere/base/callout/CostAdjustmentLineProduct.java new file mode 100644 index 0000000000..7be1b4111d --- /dev/null +++ b/org.adempiere.base.callout/src/org/adempiere/base/callout/CostAdjustmentLineProduct.java @@ -0,0 +1,110 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.base.callout; + +import java.math.BigDecimal; +import java.util.Properties; + +import org.adempiere.base.IColumnCallout; +import org.adempiere.base.annotation.Callout; +import org.compiere.model.GridField; +import org.compiere.model.GridTab; +import org.compiere.model.GridTable; +import org.compiere.model.I_M_InventoryLine; +import org.compiere.model.MAcctSchema; +import org.compiere.model.MClient; +import org.compiere.model.MCost; +import org.compiere.model.MCostElement; +import org.compiere.model.MDocType; +import org.compiere.model.MInventory; +import org.compiere.model.MProduct; +import org.compiere.util.Env; +import org.compiere.util.Msg; + +/** + * + * @author hengsin + * + */ +@Callout(tableName = I_M_InventoryLine.Table_Name, columnName = I_M_InventoryLine.COLUMNNAME_M_Product_ID) +public class CostAdjustmentLineProduct implements IColumnCallout { + @Override + public String start(Properties ctx, int WindowNo, GridTab mTab, + GridField mField, Object value, Object oldValue) { + String trxName = null; + if ( mTab != null + && mTab.getTableModel() != null) { + GridTable gt = mTab.getTableModel(); + if (gt.isImporting()) { + trxName = gt.get_TrxName(); + } + } + MInventory inventory = new MInventory(ctx, (Integer) mTab.getValue("M_Inventory_ID"), trxName); + if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(inventory.getC_DocType().getDocSubTypeInv())) { + String costingMethod = inventory.getCostingMethod(); + if (value == null) { + mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, BigDecimal.ZERO); + mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, BigDecimal.ZERO); + } else { + MProduct product = MProduct.get(ctx, (Integer) value); + MClient client = MClient.get(ctx); + MAcctSchema as = client.getAcctSchema(); + + String costingLevel = product.getCostingLevel(as); + if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(costingLevel)) { + mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, BigDecimal.ZERO); + mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, BigDecimal.ZERO); + }else { + Object asiValue = mTab.getValue(I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID); + int M_ASI_ID = asiValue != null ? (Integer)asiValue : 0; + int AD_Org_ID = inventory.getAD_Org_ID(); + int C_Currency_ID = inventory.getC_Currency_ID(); + + if (as.getC_Currency_ID() != C_Currency_ID) + { + MAcctSchema[] ass = MAcctSchema.getClientAcctSchema(ctx, client.get_ID()); + for (int i = 0; i < ass.length ; i ++) + { + MAcctSchema a = ass[i]; + if (a.getC_Currency_ID() == C_Currency_ID) + as = a ; + } + } + MCost cost = product.getCostingRecord(as, AD_Org_ID, M_ASI_ID, costingMethod); + if (cost == null) { + if (!MCostElement.COSTINGMETHOD_StandardCosting.equals(costingMethod)) { + mTab.setValue(mField, null); + return Msg.getMsg(Env.getCtx(), "NoCostingRecord"); + } + } + + mTab.setValue(I_M_InventoryLine.COLUMNNAME_CurrentCostPrice, cost.getCurrentCostPrice()); + mTab.setValue(I_M_InventoryLine.COLUMNNAME_NewCostPrice, cost.getCurrentCostPrice()); + } + } + } + return null; + } +} \ No newline at end of file diff --git a/org.adempiere.base.callout/src/org/adempiere/base/callout/factory/AnnotationBasedColumnCalloutFactoryImpl.java b/org.adempiere.base.callout/src/org/adempiere/base/callout/factory/AnnotationBasedColumnCalloutFactoryImpl.java new file mode 100644 index 0000000000..aecf6112ae --- /dev/null +++ b/org.adempiere.base.callout/src/org/adempiere/base/callout/factory/AnnotationBasedColumnCalloutFactoryImpl.java @@ -0,0 +1,47 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.base.callout.factory; + +import org.adempiere.base.AnnotationBasedColumnCalloutFactory; +import org.adempiere.base.IColumnCalloutFactory; +import org.osgi.service.component.annotations.Component; + +/** + * + * @author hengsin + * + */ +@Component(immediate = true, service = IColumnCalloutFactory.class) +public class AnnotationBasedColumnCalloutFactoryImpl extends AnnotationBasedColumnCalloutFactory { + + public AnnotationBasedColumnCalloutFactoryImpl() { + } + + @Override + protected String[] getPackages() { + return new String[] {"org.adempiere.base.callout","org.adempiere.model"}; + } + +} diff --git a/org.adempiere.base.callout/src/org/adempiere/model/CalloutBPartnerQuickEntry.java b/org.adempiere.base.callout/src/org/adempiere/model/CalloutBPartnerQuickEntry.java index 7e990cb629..61ba170096 100644 --- a/org.adempiere.base.callout/src/org/adempiere/model/CalloutBPartnerQuickEntry.java +++ b/org.adempiere.base.callout/src/org/adempiere/model/CalloutBPartnerQuickEntry.java @@ -16,12 +16,14 @@ package org.adempiere.model; import java.util.Properties; import org.adempiere.base.IColumnCallout; +import org.adempiere.base.annotation.Callout; import org.compiere.model.GridField; import org.compiere.model.GridTab; import org.compiere.model.MBPartner; import org.compiere.util.Env; import org.compiere.util.Util; +@Callout(tableName = MBPartner.Table_Name, columnName = "*") public class CalloutBPartnerQuickEntry implements IColumnCallout { @Override diff --git a/org.adempiere.base.callout/OSGI-INF/bpartnerquickentrycalloutFactory.xml b/org.adempiere.base/OSGI-INF/org.adempiere.base.DefaultAnnotationBasedColumnCalloutFactory.xml similarity index 50% rename from org.adempiere.base.callout/OSGI-INF/bpartnerquickentrycalloutFactory.xml rename to org.adempiere.base/OSGI-INF/org.adempiere.base.DefaultAnnotationBasedColumnCalloutFactory.xml index 91c9d02b1b..55c93c2086 100644 --- a/org.adempiere.base.callout/OSGI-INF/bpartnerquickentrycalloutFactory.xml +++ b/org.adempiere.base/OSGI-INF/org.adempiere.base.DefaultAnnotationBasedColumnCalloutFactory.xml @@ -1,7 +1,7 @@ - - + - + + \ No newline at end of file diff --git a/org.adempiere.base/plugin.xml b/org.adempiere.base/plugin.xml index 91943b4fc5..51cab1bbee 100644 --- a/org.adempiere.base/plugin.xml +++ b/org.adempiere.base/plugin.xml @@ -64,36 +64,6 @@ - - - - - - - - - - - - >> tableNameMap = new HashMap<>(); + + private final Map[]> constructorCache = new ConcurrentHashMap<>(); + + + public AnnotationBasedColumnCalloutFactory() { + } + + @Override + public IColumnCallout[] getColumnCallouts(String tableName, String columnName) { + List callouts = new ArrayList(); + ClassLoader classLoader = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader(); + Map> columnNameMap = tableNameMap.get(tableName); + if (columnNameMap != null) { + List calloutClassNames = columnNameMap.get(columnName); + if (calloutClassNames != null) { + newCalloutInstance(callouts, classLoader, calloutClassNames); + } + calloutClassNames = columnNameMap.get("*"); + if (calloutClassNames != null) { + newCalloutInstance(callouts, classLoader, calloutClassNames); + } + } + + columnNameMap = tableNameMap.get("*"); + if (columnNameMap != null) { + List calloutClassNames = columnNameMap.get(columnName); + if (calloutClassNames != null) { + newCalloutInstance(callouts, classLoader, calloutClassNames); + } + calloutClassNames = columnNameMap.get("*"); + if (calloutClassNames != null) { + newCalloutInstance(callouts, classLoader, calloutClassNames); + } + } + + + return callouts.toArray(new IColumnCallout[0]); + } + + private void newCalloutInstance(List callouts, ClassLoader classLoader, + List calloutClassNames) { + for(String calloutClass : calloutClassNames) { + Constructor[] constructors = constructorCache.get(calloutClass); + if (constructors == null) { + try { + Class clazz = classLoader.loadClass(calloutClass); + Constructor constructor = clazz.getDeclaredConstructor(); + IColumnCallout columnCallout = (IColumnCallout) constructor.newInstance(); + callouts.add(columnCallout); + constructors = new Constructor[] {constructor}; + constructorCache.put(calloutClass, constructors); + } catch (Exception e) { + s_log.log(Level.WARNING, e.getMessage(), e); + constructors = new Constructor[0]; + constructorCache.put(calloutClass, constructors); + } + } else if (constructors.length == 1){ + try { + IColumnCallout columnCallout = (IColumnCallout) constructors[0].newInstance(); + callouts.add(columnCallout); + } catch (Exception e) { + s_log.log(Level.WARNING, e.getMessage(), e); + constructors = new Constructor[0]; + constructorCache.put(calloutClass, constructors); + } + } + } + } + + /** + * Subclasses must override this method in order to provide packages to + * scan, discover and register {@link IColumnCallout} classes + * @return array of packages to be accepted during class scanning + * @see ClassGraph#acceptPackagesNonRecursive(String...) + */ + protected abstract String[] getPackages(); + + @Activate + public void activate(ComponentContext context) throws ClassNotFoundException { + long start = System.currentTimeMillis(); + bundleContext = context.getBundleContext(); + ClassLoader classLoader = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader(); + + ClassGraph graph = new ClassGraph() + .enableAnnotationInfo() + .overrideClassLoaders(classLoader) + .disableNestedJarScanning() + .disableModuleScanning() + .acceptPackagesNonRecursive(getPackages()); + + try (ScanResult scanResult = graph.scan()) { + List processed = new ArrayList(); + for (ClassInfo classInfo : scanResult.getClassesWithAnnotation(Callouts.class)) { + if (classInfo.isAbstract()) + continue; + String className = classInfo.getName(); + AnnotationInfoList annotationInfos = classInfo.getAnnotationInfoRepeatable(Callout.class); + for(AnnotationInfo annotationInfo : annotationInfos) { + processAnnotation(className, annotationInfo); + } + processed.add(className); + } + for (ClassInfo classInfo : scanResult.getClassesWithAnnotation(Callout.class)) { + if (classInfo.isAbstract()) + continue; + String className = classInfo.getName(); + if (processed.contains(className)) + continue; + AnnotationInfo annotationInfo = classInfo.getAnnotationInfo(Callout.class); + processAnnotation(className, annotationInfo); + } + } + long end = System.currentTimeMillis(); + if (s_log.isLoggable(Level.INFO)) + s_log.info(this.getClass().getSimpleName() + " loaded "+tableNameMap.size() +" classes in " + +((end-start)/1000f) + "s"); + } + + private void processAnnotation(String className, AnnotationInfo annotationInfo) { + //not sure why but sometime ClassGraph return Object[] instead of the expected String[] + Object[] tableNames = (Object[]) annotationInfo.getParameterValues().getValue("tableName"); + Object[] columnNames = (Object[]) annotationInfo.getParameterValues().getValue("columnName"); + + boolean matchAllTables = false; + for(Object tableName : tableNames) { + if ("*".equals(tableName) ) { + matchAllTables = true; + break; + } + } + + boolean matchAllColumns = false; + for(Object columnName : columnNames) { + if ("*".equals(columnName)) { + matchAllColumns = true; + break; + } + } + + //not allow to match everything + if (matchAllColumns && matchAllTables) + return; + + Map> columnNameMap = null; + if (matchAllTables) { + columnNameMap = tableNameMap.get("*"); + if (columnNameMap == null) { + columnNameMap = new HashMap>(); + tableNameMap.put("*", columnNameMap); + } + if (matchAllColumns) { + addCallout(className, columnNameMap); + } else { + addCallout(className, columnNames, columnNameMap); + } + } else { + for(Object tableName : tableNames) { + columnNameMap = tableNameMap.get(tableName); + if (columnNameMap == null) { + columnNameMap = new HashMap>(); + tableNameMap.put((String)tableName, columnNameMap); + } + if (matchAllColumns) { + addCallout(className, columnNameMap); + } else { + addCallout(className, columnNames, columnNameMap); + } + } + } + } + + private void addCallout(String className, Object[] columnNames, Map> columnNameMap) { + for (Object columnName : columnNames) { + List callouts = columnNameMap.get(columnName); + if (callouts == null ) { + callouts = new ArrayList(); + columnNameMap.put((String)columnName, callouts); + } + callouts.add(className); + } + } + + private void addCallout(String className, Map> columnNameMap) { + List callouts = columnNameMap.get("*"); + if (callouts == null ) { + callouts = new ArrayList(); + columnNameMap.put("*", callouts); + } + callouts.add(className); + } +} diff --git a/org.adempiere.base/src/org/adempiere/base/DefaultAnnotationBasedColumnCalloutFactory.java b/org.adempiere.base/src/org/adempiere/base/DefaultAnnotationBasedColumnCalloutFactory.java new file mode 100644 index 0000000000..dd85d0a133 --- /dev/null +++ b/org.adempiere.base/src/org/adempiere/base/DefaultAnnotationBasedColumnCalloutFactory.java @@ -0,0 +1,45 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.base; + +import org.osgi.service.component.annotations.Component; + +/** + * + * @author hengsin + * + */ +@Component(immediate = true, service = IColumnCalloutFactory.class) +public class DefaultAnnotationBasedColumnCalloutFactory extends AnnotationBasedColumnCalloutFactory { + + public DefaultAnnotationBasedColumnCalloutFactory() { + } + + @Override + protected String[] getPackages() { + return new String[] {"org.adempiere.model"}; + } + +} diff --git a/org.adempiere.base/src/org/adempiere/base/IMappedColumnCalloutFactory.java b/org.adempiere.base/src/org/adempiere/base/IMappedColumnCalloutFactory.java index eb88ad01ba..0d73e29902 100644 --- a/org.adempiere.base/src/org/adempiere/base/IMappedColumnCalloutFactory.java +++ b/org.adempiere.base/src/org/adempiere/base/IMappedColumnCalloutFactory.java @@ -26,6 +26,8 @@ package org.adempiere.base; import java.util.function.Supplier; +import org.osgi.framework.BundleContext; + /** * * @author hengsin @@ -49,4 +51,10 @@ public interface IMappedColumnCalloutFactory { */ public void removeMapping(String tableName, String columnName, Supplier supplier); + /** + * scan, discover and register classes with Callout annotation + * @param context + * @param packages + */ + public void scan(BundleContext context, String... packages); } \ No newline at end of file diff --git a/org.adempiere.base/src/org/adempiere/base/MappedColumnCalloutFactory.java b/org.adempiere.base/src/org/adempiere/base/MappedColumnCalloutFactory.java index 5d52a60000..b39479a962 100644 --- a/org.adempiere.base/src/org/adempiere/base/MappedColumnCalloutFactory.java +++ b/org.adempiere.base/src/org/adempiere/base/MappedColumnCalloutFactory.java @@ -24,13 +24,26 @@ **********************************************************************/ package org.adempiere.base; +import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.function.Supplier; +import java.util.logging.Level; +import org.adempiere.base.annotation.Callout; +import org.adempiere.base.annotation.Callouts; +import org.compiere.util.CLogger; +import org.osgi.framework.BundleContext; +import org.osgi.framework.wiring.BundleWiring; import org.osgi.service.component.annotations.Component; +import io.github.classgraph.AnnotationInfo; +import io.github.classgraph.AnnotationInfoList; +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ScanResult; + /** * * @author hengsin @@ -42,6 +55,8 @@ import org.osgi.service.component.annotations.Component; property = {"service.ranking:Integer=1"}) public class MappedColumnCalloutFactory implements IColumnCalloutFactory, IMappedColumnCalloutFactory { + private final static CLogger s_log = CLogger.getCLogger(MappedColumnCalloutFactory.class); + private final HashMap>> calloutMap = new HashMap<>(); /** @@ -101,4 +116,114 @@ public class MappedColumnCalloutFactory implements IColumnCalloutFactory, IMappe } } } + + @Override + public void scan(BundleContext context, String... packages) { + ClassLoader classLoader = context.getBundle().adapt(BundleWiring.class).getClassLoader(); + + ClassGraph graph = new ClassGraph() + .enableAnnotationInfo() + .overrideClassLoaders(classLoader) + .disableNestedJarScanning() + .disableModuleScanning() + .acceptPackagesNonRecursive(packages); + + try (ScanResult scanResult = graph.scan()) { + List processed = new ArrayList(); + for (ClassInfo classInfo : scanResult.getClassesWithAnnotation(Callouts.class)) { + if (classInfo.isAbstract()) + continue; + String className = classInfo.getName(); + AnnotationInfoList annotationInfos = classInfo.getAnnotationInfoRepeatable(Callout.class); + for(AnnotationInfo annotationInfo : annotationInfos) { + processAnnotation(className, annotationInfo, classLoader); + } + processed.add(className); + } + for (ClassInfo classInfo : scanResult.getClassesWithAnnotation(Callout.class)) { + if (classInfo.isAbstract()) + continue; + String className = classInfo.getName(); + if (processed.contains(className)) + continue; + AnnotationInfo annotationInfo = classInfo.getAnnotationInfo(Callout.class); + processAnnotation(className, annotationInfo, classLoader); + } + } + } + + private void processAnnotation(String className, AnnotationInfo annotationInfo, ClassLoader classLoader) { + Object[] tableNames = (Object[]) annotationInfo.getParameterValues().getValue("tableName"); + Object[] columnNames = (Object[]) annotationInfo.getParameterValues().getValue("columnName"); + + boolean matchAllTables = false; + for(Object tableName : tableNames) { + if ("*".equals(tableName) ) { + matchAllTables = true; + break; + } + } + + boolean matchAllColumns = false; + for(Object columnName : columnNames) { + if ("*".equals(columnName)) { + matchAllColumns = true; + break; + } + } + + //not allow to match everything + if (matchAllColumns && matchAllTables) + return; + + try { + Class clazz = classLoader.loadClass(className); + Constructor constructor = clazz.getDeclaredConstructor(); + CalloutSupplier supplier = new CalloutSupplier(constructor); + if (matchAllTables) { + for(Object columnName : columnNames) { + addMapping("*", (String)columnName, supplier); + } + } else { + for(Object tableName : tableNames) { + if (matchAllColumns) { + addMapping((String)tableName, "*", supplier); + } else { + for(Object columnName : columnNames) { + addMapping((String)tableName, (String)columnName, supplier); + } + } + } + } + + } catch (Exception e) { + if (s_log.isLoggable(Level.INFO)) + s_log.log(Level.INFO, e.getMessage(), e); + } + + } + + private static class CalloutSupplier implements Supplier { + + private Constructor constructor; + + private CalloutSupplier(Constructor constructor) { + this.constructor = constructor; + } + + @Override + public IColumnCallout get() { + IColumnCallout callout = null; + if (constructor != null) { + try { + callout = (IColumnCallout) constructor.newInstance(); + } catch (Exception e) { + constructor = null; + s_log.log(Level.WARNING, e.getMessage(), e); + } + } + return callout; + } + + } } diff --git a/org.adempiere.base/src/org/adempiere/base/annotation/Callout.java b/org.adempiere.base/src/org/adempiere/base/annotation/Callout.java new file mode 100644 index 0000000000..a3120dad41 --- /dev/null +++ b/org.adempiere.base/src/org/adempiere/base/annotation/Callout.java @@ -0,0 +1,56 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.base.annotation; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Annotation for Column Callout. This should only be used with class that implements the IColumnCallout interface. + * You can repeat the annotation multiple time for different table and column name combination + * Note that you can't use * for both tableName and columnName attribute. + * @author hengsin + * + */ +@Retention(RUNTIME) +@Target(ElementType.TYPE) +@Repeatable(Callouts.class) +public @interface Callout { + /** + * + * @return table names or * to match all tables + */ + String[] tableName(); + + /** + * + * @return column names or * to match all columns + */ + String[] columnName(); +} diff --git a/org.adempiere.base/src/org/adempiere/base/annotation/Callouts.java b/org.adempiere.base/src/org/adempiere/base/annotation/Callouts.java new file mode 100644 index 0000000000..a1a71d002a --- /dev/null +++ b/org.adempiere.base/src/org/adempiere/base/annotation/Callouts.java @@ -0,0 +1,42 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.base.annotation; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * + * @author hengsin + * + */ +@Retention(RUNTIME) +@Target(ElementType.TYPE) +public @interface Callouts { + Callout[] value() default {}; +} diff --git a/org.adempiere.base/src/org/adempiere/model/CalloutInfoWindow.java b/org.adempiere.base/src/org/adempiere/model/CalloutInfoWindow.java index dfafcb60e4..b8eedc1069 100644 --- a/org.adempiere.base/src/org/adempiere/model/CalloutInfoWindow.java +++ b/org.adempiere.base/src/org/adempiere/model/CalloutInfoWindow.java @@ -18,6 +18,7 @@ import java.util.Map; import java.util.Properties; import org.adempiere.base.IColumnCallout; +import org.adempiere.base.annotation.Callout; import org.compiere.model.AccessSqlParser; import org.compiere.model.AccessSqlParser.TableInfo; import org.compiere.model.GridField; @@ -35,6 +36,8 @@ import org.compiere.util.Env; * @author hengsin * */ +@Callout(tableName = "AD_InfoWindow", columnName = "AD_Table_ID") +@Callout(tableName = "AD_InfoColumn", columnName = {"AD_Element_ID","AD_Reference_ID"}) public class CalloutInfoWindow implements IColumnCallout { /** diff --git a/org.idempiere.test/src/org/idempiere/test/model/CalloutTest.java b/org.idempiere.test/src/org/idempiere/test/model/CalloutTest.java new file mode 100644 index 0000000000..76a033c002 --- /dev/null +++ b/org.idempiere.test/src/org/idempiere/test/model/CalloutTest.java @@ -0,0 +1,188 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.idempiere.test.model; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.List; +import java.util.StringTokenizer; + +import org.adempiere.base.ColumnCalloutManager; +import org.adempiere.base.Core; +import org.adempiere.base.IColumnCallout; +import org.adempiere.model.CalloutInfoWindow; +import org.compiere.Adempiere; +import org.compiere.model.Callout; +import org.compiere.model.I_M_InventoryLine; +import org.compiere.model.MBPartner; +import org.compiere.model.MColumn; +import org.compiere.model.MRule; +import org.compiere.model.Query; +import org.compiere.util.Env; +import org.idempiere.test.AbstractTestCase; +import org.junit.jupiter.api.Test; + +/** + * + * @author hengsin + * + */ +public class CalloutTest extends AbstractTestCase { + + public CalloutTest() { + } + + @Test + public void testAnnotatedCallout() { + List callouts = ColumnCalloutManager.findCallout("AD_InfoWindow", "AD_Table_ID"); + assertNotNull(callouts, "Null column callouts for AD_InfoWindow.AD_Table_ID"); + assertTrue(callouts.size() > 0, "Empty column callouts for AD_InfoWindow.AD_Table_ID"); + int found = 0; + for(IColumnCallout callout : callouts) { + if (callout instanceof CalloutInfoWindow) { + found++; + } + } + assertTrue(found==1, CalloutInfoWindow.class.getName() + " not found for AD_InfoWindow.AD_Table_ID"); + + callouts = ColumnCalloutManager.findCallout("AD_InfoColumn", "AD_Element_ID"); + assertNotNull(callouts, "Null column callouts for AD_InfoColumn.AD_Element_ID"); + assertTrue(callouts.size() > 0, "Empty column callouts for AD_InfoColumn.AD_Element_ID"); + found = 0; + for(IColumnCallout callout : callouts) { + if (callout instanceof CalloutInfoWindow) { + found++; + } + } + assertTrue(found==1, CalloutInfoWindow.class.getName() + " not found for AD_InfoColumn.AD_Element_ID"); + + callouts = ColumnCalloutManager.findCallout("AD_InfoColumn", "AD_Reference_ID"); + assertNotNull(callouts, "Null column callouts for AD_InfoColumn.AD_Reference_ID"); + assertTrue(callouts.size() > 0, "Empty column callouts for AD_InfoColumn.AD_Reference_ID"); + found = 0; + for(IColumnCallout callout : callouts) { + if (callout instanceof CalloutInfoWindow) { + found++; + } + } + assertTrue(found==1, CalloutInfoWindow.class.getName() + " not found for AD_InfoColumn.AD_Reference_ID"); + + String calloutClass = "org.adempiere.base.callout.CostAdjustmentLineASI"; + callouts = ColumnCalloutManager.findCallout(I_M_InventoryLine.Table_Name, I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID); + assertNotNull(callouts, "Null column callouts for "+I_M_InventoryLine.Table_Name+"."+I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID); + assertTrue(callouts.size() > 0, "Empty column callouts for "+I_M_InventoryLine.Table_Name+"."+I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID); + found = 0; + for(IColumnCallout callout : callouts) { + if (callout.getClass().getName().equals(calloutClass)) { + found++; + } + } + assertTrue(found==1, calloutClass+" not found for "+I_M_InventoryLine.Table_Name+"." + +I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID); + + calloutClass = "org.adempiere.base.callout.CostAdjustmentLineProduct"; + callouts = ColumnCalloutManager.findCallout(I_M_InventoryLine.Table_Name, I_M_InventoryLine.COLUMNNAME_M_Product_ID); + assertNotNull(callouts, "Null column callouts for "+I_M_InventoryLine.Table_Name+"."+I_M_InventoryLine.COLUMNNAME_M_Product_ID); + assertTrue(callouts.size() > 0, "Empty column callouts for "+I_M_InventoryLine.Table_Name+"."+I_M_InventoryLine.COLUMNNAME_M_Product_ID); + found = 0; + for(IColumnCallout callout : callouts) { + if (callout.getClass().getName().equals(calloutClass)) { + found++; + } + } + assertTrue(found==1, calloutClass+" not found for "+I_M_InventoryLine.Table_Name+"." + +I_M_InventoryLine.COLUMNNAME_M_Product_ID); + + calloutClass = "org.adempiere.base.callout.CostAdjustmentLineProduct"; + callouts = ColumnCalloutManager.findCallout(I_M_InventoryLine.Table_Name, I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID); + assertNotNull(callouts, "Null column callouts for "+I_M_InventoryLine.Table_Name+"."+I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID); + assertTrue(callouts.size() > 0, "Empty column callouts for "+I_M_InventoryLine.Table_Name+"."+I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID); + found = 0; + for(IColumnCallout callout : callouts) { + if (callout.getClass().getName().equals(calloutClass)) { + found++; + } + } + assertTrue(found==0, calloutClass+" found for "+I_M_InventoryLine.Table_Name+"." + +I_M_InventoryLine.COLUMNNAME_M_AttributeSetInstance_ID); + + calloutClass = "org.adempiere.model.CalloutBPartnerQuickEntry"; + callouts = ColumnCalloutManager.findCallout(MBPartner.Table_Name, MBPartner.COLUMNNAME_Name); + assertNotNull(callouts, "Null column callouts for "+MBPartner.Table_Name+"."+MBPartner.COLUMNNAME_Name); + assertTrue(callouts.size() > 0, "Empty column callouts for "+MBPartner.Table_Name+"."+MBPartner.COLUMNNAME_Name); + found = 0; + for(IColumnCallout callout : callouts) { + if (callout.getClass().getName().equals(calloutClass)) { + found++; + } + } + assertTrue(found==1, calloutClass+" not found for "+MBPartner.Table_Name+"." + +MBPartner.COLUMNNAME_Name); + } + + @Test + public void testCoreCalloutMapping() { + Query query = new Query(Env.getCtx(), MColumn.Table_Name, "AD_Column.IsActive='Y' AND AD_Column.AD_Column_ID < 1000000 " + + "AND AD_Table.IsActive='Y' AND AD_Table.AD_Table_ID < 1000000 " + + "AND AD_Column.Callout IS NOT NULL ", getTrxName()); + query.addJoinClause("JOIN AD_Table ON (AD_Column.AD_Table_ID=AD_Table.AD_Table_ID)"); + query.addJoinClause("JOIN AD_Tab ON (AD_Table.AD_Table_ID=AD_Tab.AD_Table_ID AND AD_Tab.IsActive='Y' AND AD_Tab.AD_Tab_ID<1000000)"); + query.addJoinClause("JOIN AD_Window ON (AD_Tab.AD_Window_ID=AD_Window.AD_Window_ID AND AD_Window.IsActive='Y' AND AD_Window.AD_Window_ID<1000000)"); + query.addJoinClause("JOIN AD_Menu ON (AD_Window.AD_Window_ID=AD_Menu.AD_Window_ID AND AD_Menu.IsActive='Y' AND AD_Menu.AD_Menu_ID<1000000)"); + List columns = query.list(); + ClassLoader baseClassLoader = Adempiere.class.getClassLoader(); + int calloutCount = 0; + for(MColumn column : columns) { + StringTokenizer st = new StringTokenizer(column.getCallout(), ";,", false); + while (st.hasMoreTokens()) { + String cmd = st.nextToken().trim(); + + if (cmd.toLowerCase().startsWith(MRule.SCRIPT_PREFIX)) + continue; + int methodStart = cmd.lastIndexOf('.'); + try { + if (methodStart != -1) // no class + { + String className = cmd.substring(0,methodStart); + String method = cmd.substring(methodStart+1); + Callout callout = Core.getCallout(className, method); + if (callout == null) { + //no match from factory, check java classpath + Class cClass = baseClassLoader.loadClass(className); + callout = (Callout)cClass.getDeclaredConstructor().newInstance(); + } + assertNotNull(callout, "Can't get callout "+cmd+" for "+column.getAD_Table()+" "+column); + calloutCount++; + } + } catch (Exception e) { + fail("Can't get callout "+cmd+" for "+column.getAD_Table()+" "+column, e); + } + } + } + assertTrue(calloutCount > 0, "Zero callout loaded"); + } +} diff --git a/org.idempiere.test/src/org/idempiere/test/model/MappedColumnCalloutFactoryTest.java b/org.idempiere.test/src/org/idempiere/test/model/MappedColumnCalloutFactoryTest.java index 033ad87402..0ccf1be8d5 100644 --- a/org.idempiere.test/src/org/idempiere/test/model/MappedColumnCalloutFactoryTest.java +++ b/org.idempiere.test/src/org/idempiere/test/model/MappedColumnCalloutFactoryTest.java @@ -39,6 +39,7 @@ import org.compiere.model.GridTab; import org.compiere.model.MTest; import org.idempiere.test.AbstractTestCase; import org.idempiere.test.TestActivator; +import org.idempiere.test.model.annotated.MyAnnotatedTestQtyCallout; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; @@ -67,6 +68,11 @@ public class MappedColumnCalloutFactoryTest extends AbstractTestCase { var list = Core.findCallout(MTest.Table_Name, MTest.COLUMNNAME_T_Amount); var optional = list.stream().filter(e -> e instanceof MyTestAmountCallout).findFirst(); assertTrue(optional.isPresent(), "Can't find MyTestAmountCallout column callout for " + MTest.Table_Name + "." + MTest.COLUMNNAME_T_Amount); + + factory.scan(TestActivator.context, "org.idempiere.test.model.annotated"); + list = Core.findCallout(MTest.Table_Name, MTest.COLUMNNAME_T_Qty); + optional = list.stream().filter(e -> e instanceof MyAnnotatedTestQtyCallout).findFirst(); + assertTrue(optional.isPresent(), "Can't find MyAnnotatedTestQtyCallout column callout for " + MTest.Table_Name + "." + MTest.COLUMNNAME_T_Qty); } @Test diff --git a/org.idempiere.test/src/org/idempiere/test/model/annotated/MyAnnotatedTestQtyCallout.java b/org.idempiere.test/src/org/idempiere/test/model/annotated/MyAnnotatedTestQtyCallout.java new file mode 100644 index 0000000000..1b7cec5827 --- /dev/null +++ b/org.idempiere.test/src/org/idempiere/test/model/annotated/MyAnnotatedTestQtyCallout.java @@ -0,0 +1,51 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.idempiere.test.model.annotated; + +import java.util.Properties; + +import org.adempiere.base.IColumnCallout; +import org.adempiere.base.annotation.Callout; +import org.compiere.model.GridField; +import org.compiere.model.GridTab; +import org.compiere.model.MTest; + +/** + * + * @author hengsin + * + */ +@Callout(tableName = MTest.Table_Name, columnName = MTest.COLUMNNAME_T_Qty) +public class MyAnnotatedTestQtyCallout implements IColumnCallout { + + public MyAnnotatedTestQtyCallout() { + } + + @Override + public String start(Properties ctx, int WindowNo, GridTab mTab, GridField mField, Object value, Object oldValue) { + return null; + } + +}