/****************************************************************************** * Product: iDempiere ERP & CRM Smart Business Solution * * Copyright (C) 2012 Carlos Ruiz * * Copyright (C) 2012 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.impexp; import static org.compiere.model.SystemIDs.REFERENCE_DOCUMENTACTION; import static org.compiere.model.SystemIDs.REFERENCE_PAYMENTRULE; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.math.BigDecimal; import java.nio.charset.Charset; import java.sql.SQLException; import java.sql.Timestamp; import java.text.DecimalFormatSymbols; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.logging.Level; import org.adempiere.base.IGridTabImporter; import org.adempiere.exceptions.AdempiereException; import org.adempiere.util.IProcessUI; import org.adempiere.util.ProcessUtil; import org.compiere.model.GridField; import org.compiere.model.GridTab; import org.compiere.model.GridWindow; import org.compiere.model.GridWindowVO; import org.compiere.model.MColumn; import org.compiere.model.MLocation; import org.compiere.model.MProcess; import org.compiere.model.MQuery; import org.compiere.model.MRefList; import org.compiere.model.MRefTable; import org.compiere.model.MTable; import org.compiere.model.PO; import org.compiere.model.Query; import org.compiere.process.ProcessInfo; import org.compiere.tools.FileUtil; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.DisplayType; import org.compiere.util.Env; import org.compiere.util.Language; import org.compiere.util.Msg; import org.compiere.util.Trx; import org.compiere.util.Util; import org.compiere.util.ValueNamePair; import org.compiere.wf.MWFProcess; import org.supercsv.cellprocessor.Optional; import org.supercsv.cellprocessor.ParseBigDecimal; import org.supercsv.cellprocessor.ParseBool; import org.supercsv.cellprocessor.ParseDate; import org.supercsv.cellprocessor.ParseInt; import org.supercsv.cellprocessor.constraint.StrMinMax; import org.supercsv.cellprocessor.ift.CellProcessor; import org.supercsv.exception.SuperCsvCellProcessorException; import org.supercsv.io.CsvMapReader; import org.supercsv.io.ICsvMapReader; import org.supercsv.prefs.CsvPreference; /** * CSV Importer for GridTab * @author Carlos Ruiz * @author Juan David Arboleda */ public class GridTabCSVImporter implements IGridTabImporter { private static final String ERROR_HEADER = "_ERROR_"; private static final String LOG_HEADER = "_LOG_"; private static final String IMPORT_MODE_MERGE = "M"; private static final String IMPORT_MODE_UPDATE = "U"; private static final String IMPORT_MODE_INSERT = "I"; private boolean m_isError = false; private String m_import_mode = null; private List header; private List readProcArray; private List locationFields; private boolean isThereKey; private boolean isThereDocAction; private Map tabMapIndexes; private List> data; private ICsvMapReader mapReader; private TreeMap sortedtTabMapIndexes; private List rawData; private boolean isMasterok = true; private boolean isDetailok = true; private boolean error = false; private List rowsTmpResult; private PO masterRecord; //Files management private File errFile; private File logFile; private PrintWriter errFileW; private PrintWriter logFileW; private String delimiterChar = ","; private String quoteChar = "\""; //Trx private Trx trx; private String trxName; private boolean isSingleTrx = false; /** Logger */ private static final CLogger log = CLogger.getCLogger(GridTabCSVImporter.class); @Override public File fileImport(GridTab gridTab, List childs, InputStream filestream, Charset charset , String importMode) { return fileImport(gridTab, childs, filestream, charset, importMode, null, null, null); }//fileImport @Override public File fileImport(GridTab gridTab, List childs, InputStream filestream, Charset charset, String importMode, IProcessUI processUI) { return fileImport(gridTab, childs, filestream, charset, importMode, null, null, processUI); } @Override public File fileImport(GridTab gridTab, List childs, InputStream filestream, Charset charset, String importMode, String p_delimiterChar, String p_quoteChar, IProcessUI processUI) { if(!gridTab.isInsertRecord() && isInsertMode()) throwAdempiereException("Insert record disabled for Tab"); try { String errFileName = FileUtil.getTempMailName("Import_" + gridTab.getTableName(), "_err.csv"); initValues(); m_import_mode = importMode; errFile = new File(errFileName); errFileW = new PrintWriter(errFile, charset.name()); if (p_delimiterChar != null) delimiterChar = p_delimiterChar; if (p_quoteChar != null) quoteChar = p_quoteChar; CsvPreference csvpref = new CsvPreference.Builder(quoteChar.charAt(0), delimiterChar.charAt(0), "\r\n" /* ignored */).build(); mapReader = new CsvMapReader(new InputStreamReader(filestream, charset), csvpref); header = Arrays.asList(mapReader.getHeader(true)); //Mapping header int indxDetail = mapCSVHeader(gridTab); if(isUpdateOrMergeMode() && !isThereKey) throwAdempiereException(gridTab.getTableName()+": "+Msg.getMsg(Env.getCtx(), "NoKeyFound")); tabMapIndexes.put(gridTab,indxDetail-1); isThereKey = false; locationFields = null; //Mapping details mapCSVDetail(indxDetail, childs); sortedtTabMapIndexes = null; if (childs.size()>0 && !tabMapIndexes.isEmpty()){ ValueComparator bvc = new ValueComparator(tabMapIndexes); sortedtTabMapIndexes = new TreeMap(bvc); sortedtTabMapIndexes.putAll(tabMapIndexes); }else{ Map localMapIndexes = new HashMap(); localMapIndexes.put(gridTab, header.size()-1); ValueComparator bvc = new ValueComparator(localMapIndexes); sortedtTabMapIndexes = new TreeMap(bvc); sortedtTabMapIndexes.putAll(localMapIndexes); } m_isError = false; // write the header String rawHeader = mapReader.getUntokenizedRow(); errFileW.write(rawHeader + delimiterChar + ERROR_HEADER + "\n"); data = new ArrayList>(); rawData = new ArrayList(); // pre-process to check for errors preProcess(processUI, gridTab, indxDetail); if ( !m_isError ) { String logFileName = FileUtil.getTempMailName("Import_" + gridTab.getTableName(), "_log.csv"); logFile = new File(logFileName); logFileW = new PrintWriter(logFile, charset.name()); // write the header logFileW.write(rawHeader + delimiterChar + LOG_HEADER + "\n"); // no errors found - process header and then details isMasterok = true; isDetailok = true; error = false; trx = null; trxName = null; rowsTmpResult = new ArrayList(); long lastOutput = new Date().getTime(); for (int idx = 0; idx < data.size(); idx++) { if( processUI != null && new Date().getTime()-lastOutput > 1000 /* one second */){ processUI.statusUpdate(refreshImportStatus(idx + 1, data.size() + 1)); lastOutput = new Date().getTime(); } String rawLine = rawData.get(idx); StringBuilder rowResult = new StringBuilder(); boolean isDetail = false; if (rawLine.charAt(0)==','){ isDetail=true; //check out if master row comes empty Map rowMap = data.get(idx); for(int i=0; i < indxDetail-1; i++){ if(rowMap.get(header.get(i))!=null){ isDetail=false; break; } } } if (!isMasterok && isDetail){ rawLine = rawLine + delimiterChar + quoteChar + Msg.getMsg(Env.getCtx(),"NotProcessed") + quoteChar + "\n"; rowsTmpResult.add(rawLine); continue; }else if(isMasterok && isDetail && !isDetailok){ rawLine = rawLine + delimiterChar + quoteChar + "Record not processed due to detail record failure" + quoteChar + "\n"; rowsTmpResult.add(rawLine); continue; } if( isSingleTrx() && trx == null ) createTrx(gridTab); if( !isDetail && !isSingleTrx() ){ manageMasterTrx(gridTab, null); createTrx(gridTab); } if (trx != null) trx.setDisplayName(GridTabCSVImporter.class.getName()+"_fileImport_" + gridTab.getTableName()); String recordResult = processRecord(importMode, gridTab, indxDetail, isDetail, idx, rowResult, childs); rowResult.append(recordResult); // write rawLine = rawLine + delimiterChar + quoteChar + rowResult.toString().replaceAll(delimiterChar, "") + quoteChar + "\n"; rowsTmpResult.add(rawLine); if( isSingleTrx() && isError() ) break; } manageMasterTrx(gridTab,childs); } } catch (IOException e) { throw new AdempiereException(e); } catch (Exception ex) { throw new AdempiereException(ex); } finally { try { if (mapReader != null) mapReader.close(); if (errFileW != null) { errFileW.flush(); errFileW.close(); } if (logFileW != null) { logFileW.flush(); logFileW.close(); } } catch (IOException e) { e.printStackTrace(); } gridTab.getTableModel().setImportingMode(false,null); for (GridTab detail : childs) { detail.getTableModel().setImportingMode(false,null); } gridTab.dataRefreshAll(); } if (logFile != null) return logFile; else return errFile; }//fileImport private void initValues(){ mapReader = null; errFile = null; logFile = null; errFileW = null; logFileW = null; masterRecord = null; readProcArray = new ArrayList(); tabMapIndexes = new HashMap(); locationFields = null; isThereKey = false; isThereDocAction = false; } /** * Rollback trx and update the text in log file */ private void rollbackTrx(){ trx.rollback(); for( String row : rowsTmpResult ){ row = row.replaceAll("Updated","RolledBack"); row = row.replaceAll("Inserted","RolledBack"); logFileW.write(row); } } /** * Commit trx and writes in log file * @return error message or null */ private String commitTrx(){ try { trx.commit(true); } catch (SQLException e) { setError(true); return e.getLocalizedMessage(); } for( String row : rowsTmpResult ) logFileW.write(row); return null; } /** * Map the header and returns the index where the detail starts * @param gridTab * @return index where detail start */ private int mapCSVHeader(GridTab gridTab){ int indxDetail = 0; for(int idx = 0; idx < header.size(); idx++) { String headName = header.get(idx); if (headName==null) { throwAdempiereException("Header column cannot be empty, Col: " + (idx + 1)); } if (headName.equals(ERROR_HEADER) || headName.equals(LOG_HEADER)){ header.set(idx, null); readProcArray.add(null); continue; } if (headName.indexOf(">") > 0) { if(idx==0){ throwAdempiereException(Msg.getMsg(Env.getCtx(),"WrongHeader", new Object[] {headName})); }else if (headName.contains(MTable.getTableName(Env.getCtx(), MLocation.Table_ID)) && locationFields==null){ locationFields = getSpecialMColumn(header,MTable.getTableName(Env.getCtx(), MLocation.Table_ID),idx); for(GridField sField:locationFields){ readProcArray.add(getProccesorFromColumn(sField)); indxDetail++; } idx=indxDetail; }else break; }else{ boolean isKeyColumn = headName.indexOf("/") > 0; boolean isForeing = headName.indexOf("[") > 0 && headName.indexOf("]")>0; String columnName = getColumnName (isKeyColumn,isForeing,false,headName); GridField field = gridTab.getField(columnName); if (field == null) throwAdempiereException(Msg.getMsg(Env.getCtx(), "FieldNotFoundInTab" , new Object[] {columnName, gridTab.getName()}) ); else if(isKeyColumn && !isThereKey) isThereKey =true; else if (!isThereDocAction && MColumn.get(Env.getCtx(),field.getAD_Column_ID()).getAD_Reference_Value_ID() == REFERENCE_DOCUMENTACTION ) isThereDocAction= true; readProcArray.add(getProccesorFromColumn(field)); indxDetail++; } } return indxDetail; }//mapCSVHeader /** * Map details fields in the csv file * @param indxDetail * @param childs */ private void mapCSVDetail(int indxDetail, List childs){ String childTableName = null; GridTab currentDetailTab = null; for(int idx = indxDetail; idx < header.size(); idx++) { String detailName = header.get(idx); if(detailName!=null && detailName.indexOf(">") > 0){ childTableName = detailName.substring(0,detailName.indexOf(">")); if (currentDetailTab==null || (currentDetailTab!=null && !childTableName.equals(currentDetailTab.getTableName()))){ if(currentDetailTab!=null){ //check out key per Tab if(isUpdateOrMergeMode() && !isThereKey){ throwAdempiereException(currentDetailTab.getTableName()+": "+Msg.getMsg(Env.getCtx(), "NoKeyFound")); }else{ tabMapIndexes.put(currentDetailTab,idx-1); isThereKey =false; } } for(GridTab detail: childs){ if(detail.getTableName().equals(childTableName)){ currentDetailTab = detail; break; } } } if(currentDetailTab == null) throwAdempiereException(Msg.getMsg(Env.getCtx(),"NoChildTab",new Object[] {childTableName})); String columnName = detailName; if (columnName.contains(MTable.getTableName(Env.getCtx(), MLocation.Table_ID)) && locationFields==null){ locationFields = getSpecialMColumn(header,MTable.getTableName(Env.getCtx(), MLocation.Table_ID),idx); for(GridField sField:locationFields){ readProcArray.add(getProccesorFromColumn(sField)); idx++; } idx--; }else{ boolean isKeyColumn= columnName.indexOf("/") > 0; boolean isForeing = columnName.indexOf("[") > 0 && columnName.indexOf("]")>0; columnName = getColumnName(isKeyColumn,isForeing,true,columnName); GridField field = currentDetailTab.getField(columnName); if(field == null) throwAdempiereException(Msg.getMsg(Env.getCtx(), "FieldNotFound",new Object[] {detailName})); else if(isKeyColumn && !isThereKey) isThereKey =true; readProcArray.add(getProccesorFromColumn(field)); } }else throwAdempiereException(Msg.getMsg(Env.getCtx(),"WrongDetailName",new Object[] {" col("+idx+") ",detailName})); } if(currentDetailTab!=null){ if(isUpdateOrMergeMode() && !isThereKey) throwAdempiereException(currentDetailTab.getTableName()+": "+Msg.getMsg(Env.getCtx(), "NoKeyFound")); tabMapIndexes.put(currentDetailTab,header.size()-1); } }//mapCSVDetail /** * Pre-process the input csv file and look for errors * @param processUI * @param gridTab * @param indxDetail */ private void preProcess(IProcessUI processUI, GridTab gridTab, int indxDetail){ CellProcessor[] processors = readProcArray.toArray(new CellProcessor[readProcArray.size()]); long lastOutput = new Date().getTime(); while (true) { if( processUI != null && new Date().getTime()-lastOutput > 1000 /* one second */){ processUI.statusUpdate(refreshImportStatus(data.size(), 0)); lastOutput = new Date().getTime(); } Map map = null; boolean isLineError = false; StringBuilder errMsg = new StringBuilder(); try { // devCoffee #6141 - IDEMPIERE-3832 String[] hdrs = new String[header.size()]; header.toArray(hdrs); map = mapReader.read( hdrs, processors); } catch (SuperCsvCellProcessorException e) { int idx = e.getCsvContext().getColumnNumber() - 1; errMsg.append(header.get(idx)).append(": ").append(e.getMessage()); isLineError = true; } catch (IOException e) { throw new AdempiereException(e); } String rawLine = mapReader.getUntokenizedRow(); if (! isLineError) { if(map == null) break; //Re-order information coming from map List tmpRow = getOrderedRowFromMap(header,map); //read master and detail int initIndx= 0; for(Map.Entry tabIndex : sortedtTabMapIndexes.entrySet()) { GridTab tmpGrid = tabIndex.getKey(); if(gridTab.equals(tmpGrid) && tmpRow.get(0)==null){ initIndx = indxDetail; continue; } int endindx = tabIndex.getValue(); StringBuilder lineError = preprocessRow (tmpGrid,header,tmpRow,initIndx,endindx); if( lineError!= null && lineError.length() > 0 ){ isLineError = true; if (errMsg.length() > 0) errMsg.append(" / "); errMsg.append(lineError); } initIndx = endindx + 1; } } if (isLineError && ! m_isError) m_isError = true; if (!m_isError) { data.add(map); rawData.add(rawLine); } // write rawLine = rawLine + delimiterChar + quoteChar + errMsg.toString().replaceAll(quoteChar, "") + quoteChar + "\n"; errFileW.write(rawLine); } }//preProcess /** * Manage the trx * if the trx exists - commits when no errors, rollback when errors. * @param gridTab * @param childs */ private void manageMasterTrx(GridTab gridTab, List childs){ if (trx != null) { try { if (isError()) { gridTab.dataDelete(); rollbackTrx(); setError(false); } else { boolean commit = false; if (isThereDocAction) { boolean isError = false; int AD_Process_ID = MColumn.get(Env.getCtx(), gridTab.getField("DocAction").getAD_Column_ID()).getAD_Process_ID(); if (AD_Process_ID > 0){ String docResult = processDocAction(masterRecord, AD_Process_ID); if (docResult.contains("error")) isError = true; rowsTmpResult.set(0,rowsTmpResult.get(0).replace(quoteChar + "\n",docResult + quoteChar + "\n")); } else { throwAdempiereException("No Process found for document action."); } if (isError){ gridTab.dataDelete(); rollbackTrx(); } else { commit = true; } } else { commit = true; } if (commit) { String commitResult = commitTrx(); if (isError()) { rowsTmpResult.set(0,rowsTmpResult.get(0).replace(quoteChar + "\n",commitResult + quoteChar + "\n")); gridTab.dataDelete(); rollbackTrx(); } } } } finally { trx.close(); trx=null; } } }//manageMasterTrx /** * Create a new Trx with a random Name * @param gridTab */ private void createTrx(GridTab gridTab){ trxName = Trx.createTrxName("CSVImport"); gridTab.getTableModel().setImportingMode(true,trxName); trx = Trx.get(trxName,true); masterRecord = null; rowsTmpResult.clear(); isMasterok = true; isDetailok = true; } //createTrx /** * Process the record for each row.
* First insert the master tab - if no errors found proceeds with the details tabs when existing.
* Stops at the first error found in the row. * @param importMode * @param gridTab * @param indxDetail * @param isDetail * @param idx * @param rowResult * @return message or empty string */ private String processRecord(String importMode, GridTab gridTab, int indxDetail, boolean isDetail, int idx, StringBuilder rowResult, List childs){ String logMsg = null; GridTab currentGridTab = null; int currentColumn = 0; try { for( Map.Entry tabIndex : sortedtTabMapIndexes.entrySet() ) { currentGridTab = tabIndex.getKey(); if( isDetail && gridTab.equals(currentGridTab) ){ currentColumn = indxDetail; continue; } //Assign master trx to its children if( !gridTab.equals(currentGridTab) ){ currentGridTab.getTableModel().setImportingMode(true,trxName); isDetail=true; } int j = tabIndex.getValue(); logMsg = areValidKeysAndColumns(currentGridTab,data.get(idx),header,currentColumn,j,masterRecord,trx); if (logMsg == null){ if ( isInsertMode() ){ if( !currentGridTab.getTableModel().isOpen() ) currentGridTab.getTableModel().open(0); //how to read from status since the warning is coming empty ? if ( !currentGridTab.dataNew(false) ){ logMsg = "["+currentGridTab.getName()+"]"+"- Was not able to create a new record!"; }else{ currentGridTab.navigateCurrent(); if (! isDetail) { for (GridTab child : childs) { child.query(false); } } } } if( logMsg==null ) logMsg = proccessRow(currentGridTab,header,data.get(idx),currentColumn,j,masterRecord,trx); currentColumn = j + 1; if( !(logMsg == null) ){ m_import_mode = importMode; //Ignore row since there is no data if("NO_DATA_TO_IMPORT".equals(logMsg)){ logMsg = ""; continue; }else setError(true); } }else { setError(true); currentColumn = j + 1; } if ( !isError() ) { if ( currentGridTab.dataSave(false) ){ PO po = currentGridTab.getTableModel().getPO(currentGridTab.getCurrentRow()); //Keep master record for details validation if(currentGridTab.equals(gridTab)) masterRecord = po; if( isInsertMode() ) { logMsg = Msg.getMsg(Env.getCtx(), "Inserted") + " " + po.toString(); if (!Util.isEmpty(currentGridTab.getKeyColumnName()) && currentGridTab.getKeyColumnName().endsWith("_ID")) { int recordId = currentGridTab.getRecord_ID(); if (recordId > 0) { if (currentGridTab.getTabNo() == 0) Env.setContext(Env.getCtx(), currentGridTab.getWindowNo(), currentGridTab.getKeyColumnName(), recordId); Env.setContext(Env.getCtx(), currentGridTab.getWindowNo(), currentGridTab.getTabNo(), currentGridTab.getKeyColumnName(), Integer.toString(recordId)); } } } else { logMsg = Msg.getMsg(Env.getCtx(), "Updated") + " " + po.toString(); if( currentGridTab.equals(gridTab) && sortedtTabMapIndexes.size()>1 ) currentGridTab.dataRefresh(true); } } else { ValueNamePair ppE = CLogger.retrieveWarning(); if (ppE==null) ppE = CLogger.retrieveError(); String info = null; if ( ppE != null ) info = ppE.getName(); if ( info == null ) info = ""; logMsg = Msg.getMsg(Env.getCtx(), "Error") + " " + Msg.getMsg(Env.getCtx(), "SaveError") + " (" + info + ")"; currentGridTab.dataIgnore(); //Problem in the master record if( currentGridTab.equals(gridTab) && masterRecord == null ){ isMasterok = false; rowResult.append( "<" + currentGridTab.getTableName() + ">: " ); rowResult.append(logMsg); rowResult.append(" / "); break; } //Problem in the detail record if( !currentGridTab.equals(gridTab) && masterRecord != null ){ isDetailok = false; rowResult.append( "<" + currentGridTab.getTableName() + ">: " ); rowResult.append(logMsg); rowResult.append(" / "); break; } } rowResult.append( "<" + currentGridTab.getTableName() + ">: " ); rowResult.append(logMsg); rowResult.append(" / "); } else { //if error true currentGridTab.dataIgnore(); rowResult.append( "<" + currentGridTab.getTableName() + ">: " ); rowResult.append(logMsg); rowResult.append(" / "); //Master Failed, thus details cannot be imported if( currentGridTab.equals(gridTab) && masterRecord == null ){ isMasterok = false; break; } //Detail failed if( !currentGridTab.equals(gridTab) && masterRecord != null ){ isDetailok = false; break; } } m_import_mode = importMode; } } catch (Exception e) { rowResult.append( "<" + currentGridTab.getTableName() + ">: " ); rowResult.append(Msg.getMsg(Env.getCtx(), "Error") + " " + e); rowResult.append(" / "); currentGridTab.dataIgnore(); setError(true); //Master Failed, thus details cannot be imported if( currentGridTab.equals(gridTab) && masterRecord == null ) isMasterok = false; if( !currentGridTab.equals(gridTab) && masterRecord != null ) isDetailok = false; } finally { m_import_mode = importMode; } return rowResult.toString(); }//processRecord private void throwAdempiereException(String msg){ throw new AdempiereException(msg); } /** * @param currentRecord * @param total * @return status text */ private String refreshImportStatus(int currentRecord, int total){ int percent = currentRecord * 100; if (total > 0) percent = percent / total; else percent = 0; if( percent == 0 ){ Object[] args = new Object[] {currentRecord}; return Msg.getMsg(Env.getCtx(), "PreProcessingCVSProgress", args); }else{ Object[] args = new Object[] {currentRecord, total, percent}; return Msg.getMsg(Env.getCtx(), "PercentProcessingProgress", args); } } /** * * @param document * @param AD_Process_ID * @return message */ private String processDocAction(PO document, int AD_Process_ID){ int AD_Workflow_ID = MProcess.get(Env.getCtx(),AD_Process_ID).getAD_Workflow_ID(); if (AD_Workflow_ID > 0){ ProcessInfo wfProcess = new ProcessInfo (document.get_TrxName(),AD_Process_ID,document.get_Table_ID(),document.get_ID()); wfProcess.setTransactionName(document.get_TrxName()); MWFProcess wdPro = ProcessUtil.startWorkFlow(Env.getCtx(),wfProcess, AD_Workflow_ID); if(wdPro == null) return "Document action could not be processed"; else if (wfProcess.isError()) return "Document action error: "+wfProcess.getSummary(); else return "Document action processed ["+wfProcess.getSummary()+"]"; }else { return "No workflow was found"; } } private boolean isInsertMode() { return IMPORT_MODE_INSERT.equals(m_import_mode); } private boolean isUpdateMode() { return IMPORT_MODE_UPDATE.equals(m_import_mode); } private boolean isMergeMode() { return IMPORT_MODE_MERGE.equals(m_import_mode); } private boolean isUpdateOrMergeMode() { return isUpdateMode() || isMergeMode(); } /** * @param isKey * @param isForeing * @param isDetail * @param headName * @return column name */ private String getColumnName(boolean isKey ,boolean isForeing ,boolean isDetail , String headName){ if(isKey){ if(headName.indexOf("/") > 0){ if(headName.endsWith("K")) headName = headName.substring(0,headName.length()-2); else if (headName.endsWith("KT")){ setSingleTrx(true); headName = headName.substring(0,headName.length()-3); } else throw new AdempiereException(Msg.getMsg(Env.getCtx(), "ColumnKey")+" "+headName); } } if(isForeing) headName = headName.substring(0, headName.indexOf("[")); if(isDetail){ headName = headName.substring(headName.indexOf(">")+ 1,headName.length()); if (headName.indexOf(">")>0) headName = headName.substring(headName.indexOf(">")+ 1,headName.length()); } return headName; } /** * Get extra fields for Location display type * @param header * @param tableName * @param idx * @return Extra fields for Location display type */ private List getSpecialMColumn(List header, String tableName, int idx) { List lsField = new ArrayList(); if (tableName.equals(MTable.getTableName(Env.getCtx(), MLocation.Table_ID))){ GridWindowVO gWindowVO = Env.getMWindowVO(0,121,0); GridWindow m_mWindow = new GridWindow (gWindowVO); GridTab m_mTab = m_mWindow.getTab(0); m_mWindow.initTab(0); for(int i = idx;i< header.size();i++){ if (header.get(i).contains(MTable.getTableName(Env.getCtx(), MLocation.Table_ID))) { boolean isKeyColumn = header.get(i).indexOf("/") > 0; boolean isForeing = header.get(i).indexOf("[") > 0 && header.get(i).indexOf("]")>0; String columnName = getColumnName (isKeyColumn,isForeing,true,header.get(i)); GridField field = m_mTab.getField(columnName); if (field == null) throw new AdempiereException(Msg.getMsg(Env.getCtx(), "FieldNotFound", new Object[] {header.get(i)})); lsField.add(field); }else break; } } return lsField; } /** * Get column value from map, order by header * @param header * @param map * @return Value list for row */ private List getOrderedRowFromMap (List header,Map map){ List tmpRow= new ArrayList(); for (int i = 0; i < header.size(); i++) tmpRow.add(null); for(Map.Entry record : map.entrySet()) { String Column =record.getKey(); Object value = record.getValue(); int toIndx= header.indexOf(Column); tmpRow.set(toIndx, value); } return tmpRow; } /** * Pre-process tmpRow and looks for error * @param gridTab * @param header * @param tmpRow * @param startindx * @param endindx * @return error message or null */ private StringBuilder preprocessRow (GridTab gridTab,List header,List tmpRow,int startindx,int endindx){ boolean isEmptyRow = true; boolean isAddressValidated = false ; StringBuilder mandatoryColumns = new StringBuilder(); for(int i = startindx; i < endindx +1; i++){ String columnName = header.get(i); Object value = tmpRow.get(i); //Validate Address if (!"C_Location".equals(gridTab.getTableName())) { //Validate Address if(header.get(i).contains(MTable.getTableName(Env.getCtx(),MLocation.Table_ID)) && !isAddressValidated){ StringBuilder specialColumns = new StringBuilder(); specialColumns = validateSpecialFields(gridTab,header,tmpRow,i,"C_Location_ID"); isAddressValidated = true; if(specialColumns==null) continue; else return specialColumns; }else if (header.get(i).contains(MTable.getTableName(Env.getCtx(),MLocation.Table_ID)) && isAddressValidated){ continue; } } if(value!=null) isEmptyRow=false; if (log.isLoggable(Level.FINE)) log.fine("Setting " + columnName + " to " + value); boolean isKeyColumn = columnName.indexOf("/") > 0; boolean isForeing = columnName.indexOf("[") > 0 && columnName.indexOf("]")>0; boolean isDetail = columnName.indexOf(">") > 0; columnName = getColumnName(isKeyColumn,isForeing,isDetail,columnName); String foreignColumn=null; if(isForeing) foreignColumn = header.get(i).substring(header.get(i).indexOf("[")+1, header.get(i).indexOf("]")); GridField field=gridTab.getField(columnName); if (field == null) return new StringBuilder(Msg.getMsg(Env.getCtx(), "NotAWindowField" , new Object[] {header.get(i)})); if (field.isParentValue()) continue; if (!(field.isDisplayed() || field.isDisplayedGrid())) return new StringBuilder(Msg.getMsg(Env.getCtx(), "FieldNotDisplayed",new Object[] {header.get(i)})); MColumn column = MColumn.get(Env.getCtx(), field.getAD_Column_ID()); boolean isWrongValueForMandatory = false; if (field.isMandatory(false) || column.isMandatory()){ if (isInsertMode() && value == null && field.getDefault()==null){ isWrongValueForMandatory = true; }else if (!isInsertMode() && "(null)".equals(value)){ isWrongValueForMandatory = true; } } if(isWrongValueForMandatory){ mandatoryColumns.append(" / "); mandatoryColumns.append(header.get(i)); } if (isForeing && value != null && !"(null)".equals(value)){ String foreignTable = column.getReferenceTableName(); Object idS = null; if("AD_Ref_List".equals(foreignTable)) idS = resolveForeignList(column,foreignColumn,value,null); else idS = resolveForeign(foreignTable,foreignColumn,value,field,null); if(idS == null){ //it could be that record still doesn't exist if import mode is inserting or merging if(isUpdateMode()) return new StringBuilder(Msg.getMsg(Env.getCtx(),(idS instanceof Integer && (int)idS==-2)?"ForeignMultipleResolved":"ForeignNotResolved",new Object[]{header.get(i),value})); } } else { // TODO: we could validate length of string or min/max } } if(mandatoryColumns.length()>1 && !isEmptyRow) return new StringBuilder(Msg.getMsg(Env.getCtx(), "FillMandatory")+" "+mandatoryColumns); else return null; } /** * Validate extra fields for DisplayType.Location. * @param gridTab * @param header * @param tmpRow * @param i * @param sField * @return error message or null */ private StringBuilder validateSpecialFields(GridTab gridTab,List header,List tmpRow,int i,String sField){ GridField field = gridTab.getField(sField); if(field == null) return new StringBuilder(Msg.getMsg(Env.getCtx(), "NotAWindowField",new Object[] {sField})); if(!(field.isDisplayed() || field.isDisplayedGrid())) return new StringBuilder(Msg.getMsg(Env.getCtx(), "FieldNotDisplayed",new Object[] {field.getColumnName()})); if (header.get(i).contains(MTable.getTableName(Env.getCtx(),MLocation.Table_ID))) { //without Country any address would be invalid boolean thereIsCountry = false ; boolean isEmptyRow = true; Object countryId = null; int regionIndex = -1; for(int j= i;j< header.size();j++){ if(!header.get(j).contains(MTable.getTableName(Env.getCtx(),MLocation.Table_ID))) break; //validate if location contains its parent table if(!header.get(j).contains(gridTab.getTableName())) return new StringBuilder().append("Invalid location column's name, it must contain its parent table: ") .append(gridTab.getTableName().toString()) .append("[").append(header.get(j)).append("]"); String columnName = header.get(j); Object value = tmpRow.get(j); if(value!=null){ if(columnName.contains("C_Country_ID")) { thereIsCountry= true; countryId = value; } }else continue; boolean isKeyColumn = columnName.indexOf("/") > 0; boolean isForeign = columnName.indexOf("[") > 0 && columnName.indexOf("]")>0; boolean isDetail = columnName.indexOf(">") > 0; String foreignColumn = null; columnName = getColumnName(isKeyColumn,isForeign,isDetail,columnName); if(isForeign) foreignColumn = header.get(j).substring(header.get(j).indexOf("[")+1, header.get(j).indexOf("]")); if(isForeign && !"(null)".equals(value)){ String foreignTable = columnName.substring(0,columnName.length()-3); if ("C_Region".equals(foreignTable)) { regionIndex = j; } else { Object id = resolveForeign(foreignTable,foreignColumn,value,field,null); if (id == null || (id instanceof Integer && (int)id < 0)) return new StringBuilder(Msg.getMsg(Env.getCtx(),(id instanceof Integer && (int)id==-2)?"ForeignMultipleResolved":"ForeignNotResolved" ,new Object[]{header.get(j),value})); if (columnName.contains("C_Country_ID")) { countryId = id; } } } isEmptyRow=false; } MColumn column = MColumn.get(Env.getCtx(), field.getAD_Column_ID()); if((field.isMandatory(true) || column.isMandatory()) && !isEmptyRow && !thereIsCountry) return new StringBuilder(Msg.getMsg(Env.getCtx(), "FillMandatory")+" "+field.getColumnName()+"["+"C_Country_ID]"); if (countryId != null && regionIndex != -1) { String columnName = header.get(regionIndex); Object value = tmpRow.get(regionIndex); boolean isKeyColumn = columnName.indexOf("/") > 0; boolean isForeign = columnName.indexOf("[") > 0 && columnName.indexOf("]") > 0; boolean isDetail = columnName.indexOf(">") > 0; String foreignColumn = null; columnName = getColumnName(isKeyColumn, isForeign, isDetail, columnName); if (isForeign) foreignColumn = header.get(regionIndex).substring(header.get(regionIndex).indexOf("[") + 1, header.get(regionIndex).indexOf("]")); if (isForeign && !"(null)".equals(value)) { int id = resolveForeignRegionByCountry(foreignColumn, value, field, null, (Integer) countryId); if (id < 0) return new StringBuilder(Msg.getMsg(Env.getCtx(), id == -2 ? "ForeignMultipleResolved" : "ForeignNotResolved",new Object[] { header.get(regionIndex), value })); } } } return null; } /** * Process row for import * @param gridTab * @param header * @param map * @param startindx * @param endindx * @param masterRecord * @param trx * @return error message or null */ private String proccessRow(GridTab gridTab,List header, Map map,int startindx,int endindx,PO masterRecord,Trx trx){ String logMsg = null; boolean isThereRow = false; MLocation address = null; List parentColumns = new ArrayList(); int regionIndex = -1; for(int i = startindx ; i < endindx + 1 ; i++){ String columnName = header.get(i); Object value = map.get(header.get(i)); boolean isDetail = false; if(value == null) continue; if (columnName.endsWith("_ID") && "0".equals(value) && ! MTable.isZeroIDTable(gridTab.getTableName())) continue; boolean isKeyColumn= columnName.indexOf("/") > 0; boolean isForeign = columnName.indexOf("[") > 0 && columnName.indexOf("]")>0; isDetail = columnName.indexOf(">") > 0; columnName = getColumnName(isKeyColumn,isForeign,isDetail,columnName); String foreignColumn = null; Object setValue = null; if(isForeign) foreignColumn = header.get(i).substring(header.get(i).indexOf("[")+1,header.get(i).indexOf("]")); if(!isForeign && !isKeyColumn && ("AD_Language".equals(columnName) || "EntityType".equals(columnName))) { setValue = value; GridField field = gridTab.getField(columnName); logMsg = gridTab.setValue(field,setValue); if(logMsg!=null && logMsg.equals("")) logMsg= null; }else if(!"C_Location".equals(gridTab.getTableName()) && header.get(i).contains(MTable.getTableName(Env.getCtx(),MLocation.Table_ID))){ if(address == null){ if(isInsertMode()){ address = new MLocation (Env.getCtx(),0,trx.getTrxName()); }else{ Object location = gridTab.getValue("C_Location_ID")==null?0:gridTab.getValue("C_Location_ID").toString(); int C_Location_ID = Integer.parseInt(location.toString()); address = new MLocation (Env.getCtx(),C_Location_ID,trx.getTrxName()); } } GridField field = gridTab.getField(columnName); if(!"(null)".equals(value.toString().trim())){ if(isForeign) { String foreignTable = columnName.substring(0,columnName.length()-3); if("C_Region".equals(foreignTable)) regionIndex = i; else setValue = resolveForeign(foreignTable,foreignColumn,value,field,trx); if("C_City".equals(foreignTable)) address.setCity(value.toString()); }else setValue = value; } address.set_ValueOfColumn(columnName,setValue); }else{ if(isKeyColumn && isUpdateMode()) continue; GridField field = gridTab.getField(columnName); if (field.isParentValue()){ if("(null)".equals(value.toString())){ logMsg = Msg.getMsg(Env.getCtx(),"NoParentDelete", new Object[] {header.get(i)}); break; } if(isForeign && masterRecord!=null){ if (masterRecord.get_Value(foreignColumn).toString().equals(value)){ logMsg = gridTab.setValue(field,masterRecord.get_ID()); if(logMsg.equals("")) logMsg= null; else break; }else{ if(value!=null){ logMsg = header.get(i)+" - "+Msg.getMsg(Env.getCtx(),"DiffParentValue", new Object[] {masterRecord.get_Value(foreignColumn).toString(),value}); break; } } }else if(isForeign && masterRecord==null && gridTab.getTabLevel()>0){ Object master =gridTab.getParentTab().getValue(foreignColumn); if (master!=null && value!=null && !master.toString().equals(value)){ logMsg = header.get(i)+" - "+Msg.getMsg(Env.getCtx(),"DiffParentValue", new Object[] {master.toString(),value}); break; } }else if (masterRecord==null && isDetail){ MColumn column = MColumn.get(Env.getCtx(),field.getAD_Column_ID()); String foreignTable = column.getReferenceTableName(); Object idS = null; if ("AD_Ref_List".equals(foreignTable)) idS = resolveForeignList(column, foreignColumn, value,trx); else idS = resolveForeign(foreignTable,foreignColumn,value, field, trx); if (idS == null || (idS instanceof Integer && (int)idS < 0)) return Msg.getMsg(Env.getCtx(),(idS instanceof Integer && (int)idS==-2)?"ForeignMultipleResolved":"ForeignNotResolved",new Object[]{header.get(i),value}); if (idS instanceof Integer && (int)idS >= 0) logMsg = gridTab.setValue(field,idS); else if (idS != null) logMsg = gridTab.setValue(field,idS); if(logMsg !=null && logMsg.equals("")) logMsg = null; else break; } parentColumns.add(columnName); continue; } //this field should not be inserted or updated if(!field.isDisplayed(true)) continue; if("(null)".equals(value.toString().trim())){ logMsg = gridTab.setValue(field,null); if(logMsg.equals("")) logMsg= null; else break; }else{ MColumn column = MColumn.get(Env.getCtx(),field.getAD_Column_ID()); if (isForeign){ String foreignTable = column.getReferenceTableName(); if ("AD_Ref_List".equals(foreignTable)) { String idS = resolveForeignList(column, foreignColumn, value,trx); if(idS == null) return Msg.getMsg(Env.getCtx(),"ForeignNotResolved",new Object[]{header.get(i),value}); setValue = idS; isThereRow =true; } else { Object id = resolveForeign(foreignTable, foreignColumn, value,field,trx); if (id == null || (id instanceof Integer && (int)id < 0)) return Msg.getMsg(Env.getCtx(),(id instanceof Integer && (int)id==-2)?"ForeignMultipleResolved":"ForeignNotResolved",new Object[]{header.get(i),value}); setValue = id; if (field.isParentValue()) { Object actualId = field.getValue(); if (actualId != null && ! actualId.equals(id)) { logMsg = Msg.getMsg(Env.getCtx(), "ParentCannotChange",new Object[]{header.get(i)}); break; } } isThereRow =true; } }else{ if(value != null) { if(value instanceof java.util.Date) value = new Timestamp(((java.util.Date)value).getTime()); if(DisplayType.Payment == field.getDisplayType()){ String oldValue = value.toString(); for(ValueNamePair pList: MRefList.getList(Env.getCtx(),REFERENCE_PAYMENTRULE,false)){ if(pList.getName().equals(oldValue.toString())){ oldValue = pList.getValue(); break; } } if(!value.toString().equals(oldValue)) value = oldValue; else return Msg.getMsg(Env.getCtx(),"ForeignNotResolved",new Object[]{header.get(i),value}); }else if(DisplayType.Button == field.getDisplayType()){ if(column.getAD_Reference_Value_ID()== REFERENCE_DOCUMENTACTION){ String oldValue = value.toString(); for(ValueNamePair pList: MRefList.getList(Env.getCtx(),REFERENCE_DOCUMENTACTION,false)){ if(pList.getName().equals(oldValue.toString())){ oldValue = pList.getValue(); break; } } if(!value.toString().equals(oldValue)) value = oldValue; else return Msg.getMsg(Env.getCtx(),"ForeignNotResolved",new Object[]{header.get(i),value}); }else{ return Msg.getMsg(Env.getCtx(),"Invalid") + " Column ["+column.getColumnName()+"]"; } } else if (DisplayType.isNumeric(field.getDisplayType()) || DisplayType.isID(field.getDisplayType())) { if (columnName.endsWith("_ID")) { if (!(value instanceof Integer)) { Integer idValue = Integer.valueOf(value.toString()); value = idValue; } } else if (!(value instanceof BigDecimal)) { BigDecimal decValue = new BigDecimal(value.toString()); value = decValue; } } setValue = value; isThereRow =true; } } if(setValue != null) { Object oldValue = gridTab.getValue(field); if (isValueChanged(oldValue, setValue)) { if (!field.isEditable(true)) { return Msg.getMsg(Env.getCtx(), "FieldIsReadOnly",new Object[] {header.get(i)}); } logMsg = gridTab.setValue(field,setValue); } else { logMsg = ""; } } if(logMsg!=null && logMsg.equals("")) logMsg= null; else break; } } } if(address!=null){ if (regionIndex != -1 && address.getC_Country_ID() > 0) { String columnName = header.get(regionIndex); Object value = map.get(header.get(regionIndex)); boolean isDetail = false; boolean isKeyColumn= columnName.indexOf("/") > 0; boolean isForeign = columnName.indexOf("[") > 0 && columnName.indexOf("]")>0; isDetail = columnName.indexOf(">") > 0; columnName = getColumnName(isKeyColumn,isForeign,isDetail,columnName); String foreignColumn = null; Object setValue = null; if(isForeign) foreignColumn = header.get(regionIndex).substring(header.get(regionIndex).indexOf("[")+1,header.get(regionIndex).indexOf("]")); GridField field = gridTab.getField(columnName); if(!"(null)".equals(value.toString().trim())){ if(isForeign) { setValue = resolveForeignRegionByCountry(foreignColumn, value, field, trx, address.getC_Country_ID()); }else setValue = value; } address.set_ValueOfColumn(columnName,setValue); } if (!address.save()){ logMsg = CLogger.retrieveError()+" Address : "+address; }else { logMsg = gridTab.setValue("C_Location_ID",address.getC_Location_ID()); if(logMsg.equals("")) logMsg= null; else return logMsg; isThereRow =true; } } boolean checkParentKey = parentColumns.size()!=gridTab.getParentColumnNames().size(); if(isThereRow && logMsg==null && (masterRecord!=null||gridTab.getParentTab()!=null) && checkParentKey){ for(String linkColumn : gridTab.getParentColumnNames()){ String columnName = linkColumn; Object setValue = masterRecord != null ? masterRecord.get_Value(linkColumn) : gridTab.getParentTab().getValue(linkColumn); //resolve missing key if(setValue==null){ columnName = null; for(int j = startindx;j < endindx + 1;j++){ if(header.get(j).contains(linkColumn)){ columnName = header.get(j); setValue = map.get(columnName); break; } } if( columnName!=null ){ String foreignColumn = null; boolean isForeing = columnName.indexOf("[") > 0 && columnName.indexOf("]")>0; if(isForeing) foreignColumn = columnName.substring(columnName.indexOf("[")+1,columnName.indexOf("]")); columnName = getColumnName(false,isForeing,true,columnName); GridField field = gridTab.getField(columnName); MColumn column = MColumn.get(Env.getCtx(),field.getAD_Column_ID()); if (isForeing){ String foreignTable = column.getReferenceTableName(); if ("AD_Ref_List".equals(foreignTable)) { String idS = resolveForeignList(column,foreignColumn,setValue,trx); if(idS == null) return Msg.getMsg(Env.getCtx(),"ForeignNotResolved",new Object[]{columnName,setValue}); setValue = idS; } else { Object id = resolveForeign(foreignTable, foreignColumn, setValue, field, trx); if (id == null || (id instanceof Integer && (int)id < 0)) return Msg.getMsg(Env.getCtx(),(id instanceof Integer && (int)id==-2)?"ForeignMultipleResolved":"ForeignNotResolved",new Object[]{columnName,setValue}); setValue = id; } } }else{ logMsg = "Key: "+linkColumn+" "+ Msg.getMsg(Env.getCtx(),"NotFound"); break; } } logMsg = gridTab.setValue(linkColumn,setValue); if(logMsg.equals("")) logMsg= null; else continue; } } if(logMsg == null && !isThereRow) logMsg ="NO_DATA_TO_IMPORT"; return logMsg; } /** * @param field * @return CellProcessor */ private CellProcessor getProccesorFromColumn(GridField field) { // TODO: List columns can use RequireSubStr constraint if (DisplayType.Date == field.getDisplayType()) { return (new Optional(new ParseDate(DisplayType.DEFAULT_DATE_FORMAT))); } else if (DisplayType.DateTime == field.getDisplayType()) { return (new Optional(new ParseDate(DisplayType.DEFAULT_TIMESTAMP_FORMAT))); } else if (DisplayType.Time == field.getDisplayType()) { return (new Optional(new ParseDate(DisplayType.DEFAULT_TIME_FORMAT))); } else if (DisplayType.Integer == field.getDisplayType()) { return (new Optional(new ParseInt())); } else if (DisplayType.isNumeric(field.getDisplayType())) { return (new Optional(new ParseBigDecimal(new DecimalFormatSymbols(Language.getLoginLanguage().getLocale())))); } else if (DisplayType.YesNo == field.getDisplayType()) { return (new Optional(new ParseBool("y", "n"))); } else if (DisplayType.TextLong == field.getDisplayType() || DisplayType.JSON == field.getDisplayType()) { return (new Optional(new StrMinMax(1, Long.MAX_VALUE))); } else if (DisplayType.isText(field.getDisplayType())) { return (new Optional(new StrMinMax(1, field.getFieldLength()))); } else { // optional lookups and text return null; } } /** * @param gridTab * @param map * @param header * @param startindx * @param endindx * @param masterRecord * @param trx * @return error message or null */ private String areValidKeysAndColumns(GridTab gridTab, Map map,List header,int startindx,int endindx,PO masterRecord,Trx trx){ MQuery pquery = new MQuery(gridTab.getAD_Table_ID()); String logMsg= null; Object tmpValue=null; String columnwithKey=null; Object setValue = null; List parentColumns = new ArrayList(); //Process columnKeys + Foreign to add restrictions. for (int i = startindx ; i < endindx + 1 ; i++){ boolean isKeyColumn = header.get(i).indexOf("/") > 0 && ( header.get(i).endsWith("K") || header.get(i).endsWith("KT")); if(isKeyColumn && ("C_Location".equals(gridTab.getTableName()) || !header.get(i).contains(MTable.getTableName(Env.getCtx(),MLocation.Table_ID)))){ boolean isForeing = header.get(i).indexOf("[") > 0 && header.get(i).indexOf("]")>0; boolean isDetail = header.get(i).indexOf(">") > 0; columnwithKey = getColumnName(isKeyColumn,isForeing,isDetail,header.get(i)); if(map.get(header.get(i)) instanceof java.util.Date) tmpValue = new Timestamp(((java.util.Date)map.get(header.get(i))).getTime()); else tmpValue = map.get(header.get(i)); if (tmpValue==null) continue; GridField field = gridTab.getField(columnwithKey); MColumn column = MColumn.get(Env.getCtx(), field.getAD_Column_ID()); if(field.isParentValue()){ parentColumns.add(column.getColumnName()); } String foreignColumn = null; if(isForeing){ foreignColumn = header.get(i).substring(header.get(i).indexOf("[")+1,header.get(i).indexOf("]")); String foreignTable = column.getReferenceTableName(); if ("AD_Ref_List".equals(foreignTable)) { String idS = resolveForeignList(column, foreignColumn, tmpValue,trx); setValue = idS; }else { Object id = resolveForeign(foreignTable, foreignColumn, tmpValue,field,trx); setValue = id; } }else{ setValue = tmpValue ; } pquery.addRestriction(columnwithKey,MQuery.EQUAL,setValue); } } if (pquery.getRestrictionCount() > 0){ //check out if parent keys were completed properly if (gridTab.isDetail()){ for(String linkColumn : gridTab.getParentColumnNames()){ if(!pquery.getWhereClause().contains(linkColumn)){ Object value = masterRecord!=null ? masterRecord.get_Value(linkColumn) : gridTab.getParentTab() != null ? gridTab.getParentTab().getValue(linkColumn) : null; //resolve key if(value==null){ String columnName = null; for(int j = startindx;j 0 && columnName.indexOf("]")>0; columnwithKey = getColumnName(false,isForeing,true,columnName); GridField field = gridTab.getField(columnwithKey); MColumn column = MColumn.get(Env.getCtx(), field.getAD_Column_ID()); String foreignColumn = null; if(isForeing){ foreignColumn = columnName.substring(columnName.indexOf("[")+1,columnName.indexOf("]")); String foreignTable = column.getReferenceTableName(); if ("AD_Ref_List".equals(foreignTable)) { String idS = resolveForeignList(column,foreignColumn,value,trx); value = idS; }else { value = resolveForeign(foreignTable,foreignColumn,value,field,trx); } } }else{ //mandatory key not found return Msg.getMsg(Env.getCtx(),"FillMandatory")+" "+linkColumn; } } if(value!=null) pquery.addRestriction(linkColumn,MQuery.EQUAL,value); } } } gridTab.getTableModel().dataRequery(pquery.getWhereClause(), false, 0, false); if (isInsertMode()){ if(gridTab.getTableModel().getRowCount()>=1) logMsg = Msg.getMsg(Env.getCtx(), "AlreadyExists")+" "+pquery; else return null; } if (isUpdateMode()){ if(gridTab.getTableModel().getRowCount()==1){ gridTab.navigateCurrent(); return null; } else if(gridTab.getTableModel().getRowCount()<=0) logMsg = Msg.getMsg(Env.getCtx(), "not.found")+" "+pquery; else if(gridTab.getTableModel().getRowCount()>1) logMsg = Msg.getMsg(Env.getCtx(),"TooManyRows")+" "+pquery; } if (isMergeMode()){ if(gridTab.getTableModel().getRowCount()==1){ gridTab.navigateCurrent(); m_import_mode = IMPORT_MODE_UPDATE; }else if(gridTab.getTableModel().getRowCount()<=0) m_import_mode = IMPORT_MODE_INSERT; else if(gridTab.getTableModel().getRowCount()>1) logMsg = Msg.getMsg(Env.getCtx(),"TooManyRows")+" "+pquery; } } return logMsg; } /** * @param column * @param foreignColumn * @param value * @param trx * @return value from matching ad_ref_list record */ private String resolveForeignList(MColumn column, String foreignColumn, Object value ,Trx trx) { String idS = null; String trxName = (trx!=null?trx.getTrxName():null); StringBuilder select = new StringBuilder("SELECT Value FROM AD_Ref_List WHERE ") .append(foreignColumn).append("=? AND AD_Reference_ID=? AND IsActive='Y'"); idS = DB.getSQLValueStringEx(trxName, select.toString(), value, column.getAD_Reference_Value_ID()); return idS; } /** * @param foreignTable * @param foreignColumn * @param value * @param field * @param trx * @return -3 for not found, -2 for more than 1 match and > 0 for foreign id */ private Object resolveForeign(String foreignTable, String foreignColumn, Object value, GridField field, Trx trx) { boolean systemAccess = false; if (!"AD_Client".equals(foreignTable)) { MTable ft = MTable.get(Env.getCtx(), foreignTable); String accessLevel = ft.getAccessLevel(); if ( MTable.ACCESSLEVEL_All.equals(accessLevel) || MTable.ACCESSLEVEL_SystemOnly.equals(accessLevel) || MTable.ACCESSLEVEL_SystemPlusClient.equals(accessLevel)) { systemAccess = true; } } int thisClientId = Env.getAD_Client_ID(Env.getCtx()); String trxName = (trx!=null?trx.getTrxName():null); StringBuilder postSelect = new StringBuilder(" FROM ") .append(foreignTable).append(" WHERE ") .append(foreignColumn).append("=? AND IsActive='Y' AND AD_Client_ID=?"); if (field != null ) { if (!Util.isEmpty(field.getVO().ValidationCode)) { String dynamicValid = Env.parseContext(Env.getCtx(), field.getWindowNo(), field.getGridTab().getTabNo(), field.getVO().ValidationCode, false); if (Util.isEmpty(dynamicValid)) { return 0;// it's parse error but simple consider like ForeignNotResolved }else { postSelect.append(" AND (").append(dynamicValid).append(")"); } } int ref = field.getVO().displayType; int refval = field.getVO().AD_Reference_Value_ID; if (refval > 0 && (ref == DisplayType.Table || ref == DisplayType.Search)) { final MRefTable refTable = new Query(Env.getCtx(), MRefTable.Table_Name, "AD_Reference_ID=?", trxName) .setParameters(refval) .firstOnly(); String whereClause = refTable.getWhereClause(); if (!Util.isEmpty(whereClause)) { String dynamicValid = Env.parseContext(Env.getCtx(), field.getWindowNo(), field.getGridTab().getTabNo(), whereClause, false); if (Util.isEmpty(dynamicValid)) { return 0;// it's parse error but simple consider like ForeignNotResolved }else { postSelect.append(" AND (").append(dynamicValid).append(")"); } } } } StringBuilder selectCount = new StringBuilder("SELECT COUNT(*)").append(postSelect); MTable forTab = MTable.get(Env.getCtx(), foreignTable); StringBuilder selectId = new StringBuilder("SELECT ").append(forTab.getKeyColumns()[0]).append(postSelect); int count = DB.getSQLValueEx(trxName, selectCount.toString(), value, thisClientId); if (count == 1) { // single value found, OK if (forTab.isUUIDKeyTable()) return DB.getSQLValueStringEx(trxName, selectId.toString(), value, thisClientId); else return DB.getSQLValueEx(trxName, selectId.toString(), value, thisClientId); } else if (count > 1) { // multiple values found, error ForeignMultipleResolved return -2; } else if (count == 0) { // no values found, error ForeignNotResolved if (systemAccess && thisClientId != 0) { // not found in client, try with System count = DB.getSQLValueEx(trxName, selectCount.toString(), value, 0 /* System */); if (count == 1) { // single value found, OK if (forTab.isUUIDKeyTable()) return DB.getSQLValueStringEx(trxName, selectId.toString(), value, 0 /* System */); else return DB.getSQLValueEx(trxName, selectId.toString(), value, 0 /* System */); } else if (count > 1) { // multiple values found, error ForeignMultipleResolved return -2; } } } return -3; // no values found, error ForeignNotResolved } private int resolveForeignRegionByCountry(String foreignColumn, Object regionValue, GridField field, Trx trx, int countryId) { String foreignTable = "C_Region"; boolean systemAccess = true; int thisClientId = Env.getAD_Client_ID(Env.getCtx()); StringBuilder postSelect = new StringBuilder(" FROM ") .append(foreignTable).append(" WHERE ") .append(foreignColumn).append("=? AND C_Country_ID=? AND IsActive='Y' AND AD_Client_ID=?"); StringBuilder selectCount = new StringBuilder("SELECT COUNT(*)").append(postSelect); StringBuilder selectId = new StringBuilder("SELECT ").append(foreignTable).append("_ID").append(postSelect); int count = DB.getSQLValueEx(trxName, selectCount.toString(), regionValue, countryId, thisClientId); if (count == 1) { // single value found, OK return DB.getSQLValueEx(trxName, selectId.toString(), regionValue, countryId, thisClientId); } else if (count > 1) { // multiple values found, error ForeignMultipleResolved return -2; } else if (count == 0) { // no values found, error ForeignNotResolved if (systemAccess && thisClientId != 0) { // not found in client, try with System count = DB.getSQLValueEx(trxName, selectCount.toString(), regionValue, countryId, 0 /* System */); if (count == 1) { // single value found, OK return DB.getSQLValueEx(trxName, selectId.toString(), regionValue, countryId, 0 /* System */); } else if (count > 1) { // multiple values found, error ForeignMultipleResolved return -2; } } } return -3; // no values found, error ForeignNotResolved } //Copy from GridTable @SuppressWarnings("unchecked") private boolean isValueChanged(Object oldValue, Object value) { if ( isNotNullAndIsEmpty(oldValue) ) { oldValue = null; } if ( isNotNullAndIsEmpty(value) ) { value = null; } boolean bChanged = (oldValue == null && value != null) || (oldValue != null && value == null); if (!bChanged && oldValue != null) { if (oldValue.getClass().equals(value.getClass())) { if (oldValue instanceof Comparable) { bChanged = (((Comparable)oldValue).compareTo(value) != 0); } else { bChanged = !oldValue.equals(value); } } else if(value != null) { bChanged = !(oldValue.toString().equals(value.toString())); } } return bChanged; } /** * @param value * @return true if value is not null and not an empty string */ private boolean isNotNullAndIsEmpty (Object value) { if (value != null && (value instanceof String) && value.toString().equals("") ) { return true; } else { return false; } } @Override public String getFileExtension() { return "csv"; } @Override public String getFileExtensionLabel() { return Msg.getMsg(Env.getCtx(), "FileCSV"); } @Override public String getContentType() { return "application/csv"; } @Override public String getSuggestedFileName(GridTab gridTab) { Calendar cal = Calendar.getInstance(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); String dt = sdf.format(cal.getTime()); String localFile = "Import_" + gridTab.getTableName() + "_" + dt + (m_isError ? "_err" : "_log") + "." + getFileExtension(); return localFile; } public boolean isError() { return error; } public boolean isSingleTrx() { return isSingleTrx; } public void setSingleTrx(boolean isSingleTrx) { this.isSingleTrx = isSingleTrx; } public void setError(boolean error) { this.error = error; } static class ValueComparator implements Comparator { Map base; public ValueComparator(Map base) { this.base = base; } // Note: this comparator imposes orderings that are inconsistent with equals. public int compare(GridTab a, GridTab b) { if(base.get(a) >= base.get(b)) return 1; else return -1; } } }